Skip to content

Instantly share code, notes, and snippets.

@avorobyov
Last active November 26, 2023 21:54
Show Gist options
  • Save avorobyov/087ad2646737dfb7c20c6717e51a6c8b to your computer and use it in GitHub Desktop.
Save avorobyov/087ad2646737dfb7c20c6717e51a6c8b to your computer and use it in GitHub Desktop.
package com.example.demo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
// Encode (and decode) any string to a string only with a-z characters
public final class Base26 {
private static final BigInteger BASE = BigInteger.valueOf(26);
private static final char smallA = 'a';
private static final char[] letters = "abcdefghijklmnopqrstuvwxyz".toCharArray();
private static final String EMPTY_STRING = "";
private Base26() {
// empty
}
public static String encode(String input) {
Objects.requireNonNull(input, "parameter 'input' cannot be null");
if (input.isEmpty()) {
return EMPTY_STRING;
}
StringBuilder buffer = new StringBuilder();
BigInteger number = new BigInteger(input.getBytes(StandardCharsets.UTF_8));
BigInteger[] resultAndReminder;
do {
resultAndReminder = number.divideAndRemainder(BASE);
BigInteger reminder = resultAndReminder[1];
BigInteger result = resultAndReminder[0];
char c = toCharacter(reminder.intValue());
buffer.append(c);
number = result;
} while (isGreaterThan26(number));
buffer.append(toCharacter(number.intValue()));
return buffer.reverse().toString();
}
public static String decode(String input) {
Objects.requireNonNull(input, "parameter 'input' cannot be null");
if (input.isEmpty()) {
return EMPTY_STRING;
}
// let's do a quick validation before doing expensive long multiplication
requireBase26(input);
int c = input.charAt(0) - smallA;
BigInteger result = BigInteger.valueOf(c);
for (int i = 1; i < input.length(); i++) {
c = input.charAt(i) - smallA;
result = result.multiply(BASE);
result = result.add(BigInteger.valueOf(c));
}
return new String(result.toByteArray());
}
private static void requireBase26(String s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c < 'a' || c > 'z') {
throw new IllegalArgumentException("string is not in Base26 format");
}
}
}
private static char toCharacter(int position) {
return letters[position];
}
private static boolean isGreaterThan26(BigInteger number) {
return number.min(BASE).equals(BASE);
}
}
class Base26Tests {
@ParameterizedTest
@ValueSource(strings = {
"Let’s go to the zoo 384 kдфлоадып 我们一起去动物园吧!فارسی \uD83D\uDC7D",
"" // corner case
})
void encodedContainsOnlyCharsFromAtoZ(String input) {
System.out.println(input);
String output = Base26.encode(input);
System.out.println(output);
for (char c: output.toCharArray()) {
Assertions.assertTrue(c >= 'a' && c <='z');
}
}
@ParameterizedTest
@ValueSource(strings = {
"Let’s go to the zoo 384 kдфлоадып 我们一起去动物园吧!فارسی \uD83D\uDC7D",
"" // corner case
})
void decode(String input) {
System.out.println(input);
String output = Base26.encode(input);
System.out.println(output);
Assertions.assertEquals(input, Base26.decode(output));
}
@ParameterizedTest
@ValueSource(strings = {"A", "sld8", "sd o", "ш"})
void throwExceptionIfInputIsNotBase26(String input) {
IllegalArgumentException thrown = Assertions.assertThrows(IllegalArgumentException.class, () -> {
Base26.decode(input);
});
Assertions.assertEquals("string is not in Base26 format", thrown.getMessage());
}
@Test
void encodeThrowsNpe() {
NullPointerException thrown = Assertions.assertThrows(NullPointerException.class, () -> {
Base26.encode(null);
});
Assertions.assertEquals("parameter 'input' cannot be null", thrown.getMessage());
}
@Test
void decodeThrowsNpe() {
NullPointerException thrown = Assertions.assertThrows(NullPointerException.class, () -> {
Base26.decode(null);
});
Assertions.assertEquals("parameter 'input' cannot be null", thrown.getMessage());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment