import java.math.BigInteger;
class AffineCipher {
private static final int M = 26; // size of alphabet
private static final int A = (int) 'a'; // zero-index of 'a' (ASCII code point 97)
String encode(String input, int a, int b) {
if (greatestCommonDivisor(a, M) != 1) {
throw new IllegalArgumentException("Error: keyA and alphabet size must be coprime.");
}
StringBuilder output = new StringBuilder();
for (int codePoint :
input.replaceAll("\\W", "") // nix punctuation and whitespace
.toLowerCase()
.toCharArray()
) {
output.append( Character.isDigit(codePoint)
? (char) codePoint
: (char) (((a * (codePoint - A) + b) % M) + A));
}
return String.join(" ", splitString(output.toString(), 5));
}
…
String decode(String input, int a, int b) {
if (greatestCommonDivisor(a, M) != 1) {
throw new IllegalArgumentException("Error: keyA and alphabet size must be coprime.");
}
int mmiOfa = modularMultiplicativeInverse(a, M);
return input.replaceAll("\\s", "") // nix whitespace
.chars()
.map(codePoint ->
Character.isDigit(codePoint)
? codePoint
: mmiOfa * (codePoint - A + M*b - b) % M + A)
.collect(StringBuilder::new,
(sb, i) -> sb.append((char) i),
StringBuilder::append)
.toString();
}
private int modularMultiplicativeInverse(int a, int m) {
return BigInteger.valueOf(a)
.modInverse(BigInteger.valueOf(m))
.intValue();
}
private String[] splitString(String inputString, int segmentSize) {
String re = "(?<=\\G.{" + segmentSize + "})";
return inputString.split(re);
}
private static int greatestCommonDivisor(int a, int m) {
return (m == 0) ? a // base case
: greatestCommonDivisor(m, a % m);
}
}