Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
Caesar Cipher originally deals only with letters (ASCII "A" to "Z", without diacritics/accents), so I'm not sure why you included ۤ$%& in your answer. And using indexOf (as you did) is not ver...
Answer
#1: Initial revision
Caesar Cipher originally deals only with letters (ASCII "A" to "Z", without diacritics/accents), so I'm not sure why you included `ۤ$%&` in [your answer](https://software.codidact.com/posts/286080/286081#answer-286081). And using `indexOf` (as you did) is not very efficient, because it always makes a *loop* through the string, until the character is found (which means you're looping the alphabet string lots of times, [which might not be a good idea](https://www.joelonsoftware.com/2001/12/11/back-to-basics/)). Of course for small strings this won't make much difference, but anyway, to implement this cipher (shifting only ASCII letters) you don't need a string with the whole alphabet. Just use the char's ASCII code and do some calculations with it. To eliminate the problem of the `%` operator with negative numbers, use [`Math.floorMod`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Math.html#floorMod(int,int)), which guarantees that the result is positive (**actually**, `Math.floorMod(x, y)` guarantees that the result has the same sign of `y`, and we're going to use `y` equals 26, so this will do the trick)[^1]: [^1]: Of course you could also use your `mod` function, I'm just showing a native "*batteries included*" solution. ```java public static String caesarCipher(String s, int shift) { StringBuilder result = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); char base; // check if it's an ASCII letter if ('A' <= c && c <= 'Z') { base = 'A'; } else if ('a' <= c && c <= 'z') { base = 'a'; } else { // not a letter, char remains the same in the new string result.append(c); continue; // go to next char } // it only gets here if it's an ASCII letter result.append((char) (Math.floorMod((c - base) + shift, 26) + base)); } return result.toString(); } ``` First I check if the character to be shifted is a lowercase or uppercase ASCII letter, and set the base letter accordingly (for non-letters, I'll just add them to the result without any modification, but you can also throw an exception if those are not to be accepted). Then I "normalize" the character relative to the base letter (so "A"/"a" becomes zero, "B"/"b" becomes 1 and so on). Then I add the shift and get the `floorMod` of that by 26. After that, I'll just add the base again, so 0 becomes "A" (or "a", if the base is lowercase), 1 becomes "B"/"b", etc. Testing: ```java String s = "aA bB yY zZ 123"; String ciphered = caesarCipher(s, 3); System.out.println(ciphered); String deciphered = caesarCipher(ciphered, -3); System.out.println(deciphered); ``` Output: ```none dD eE bB cC 123 aA bB yY zZ 123 ``` As I said, I'm ignoring (keeping unchanged) all the characters that aren't letters, but you can also throw an exception if those are not allowed in the input string. The [description in Wikipedia](https://en.wikipedia.org/wiki/Caesar_cipher) doesn't say what to do with non-letters, but in the examples we can see that at least spaces are allowed (and kept unchanged). You could include those specific checks if you want, but I guess that's beside the point <sup><sub>and left as an exercise for the reader</sub></sup>. --- # "Generic" alphabet If you want a more generic alphabet (as you did in your answer, by including `ۤ$%&`), the calculations won't work anymore, and the `indexOf` approach could be used instead. But if this will be used in lots of strings, `indexOf` won't be very efficient (as already said above), and perhaps it'd be worth to do some pre-processing in the alphabet: ```java public class CaesarCipher { private int encriptionShift; private String alphabet; private Map<Character, Integer> positions; // map each char to its position in the alphabet // receives the alphabet and the shift for encription public CaesarCipher(String alphabet, int encriptionShift) { this.encriptionShift = encriptionShift; this.alphabet = alphabet; this.positions = new HashMap<>(alphabet.length()); for (int i = 0; i < alphabet.length(); i++) { this.positions.put(alphabet.charAt(i), i); } } private String shiftString(String s, int shift) { StringBuilder result = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (this.positions.containsKey(c)) { int pos = this.positions.get(c); int newPosition = Math.floorMod(pos + shift, this.alphabet.length()); result.append(this.alphabet.charAt(newPosition)); } else { // char not in the alphabet, keeping unchanged // (or throw exception if you don't want to allow those) result.append(c); } } return result.toString(); } public String cipher(String s) { return this.shiftString(s, this.encriptionShift); } public String decipher(String s) { return this.shiftString(s, -this.encriptionShift); } } ``` Now each character is mapped to its position in the alphabet (only once, when the instance is created), and I use this map instead of `indexOf` (it's a classic trade-off: the map uses extra memory, but I avoid all the loops caused by `indexOf`). Of course you need to analyze if performance is an issue and if it's worth doing it, but anyway... Testing: ```java String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzۤ$%&"; CaesarCipher c = new CaesarCipher(alphabet, 3); String plaintext = "ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; String ciphertext = c.cipher(plaintext); String decrypted = c.decipher(ciphertext); System.out.println(plaintext); System.out.println(ciphertext); System.out.println(decrypted); ``` Output: ```none ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ %&ABC3456789abcdefghijklmnopqrstuvwxyzۤ$DEFGHIJKLMNOPQRSTUVWXYZ012 ۤ$%&0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ```