dupirefr/dupire.francois+pro@gmail.com [BAEL-2452] Caesar Cipher in Java (#8209)
* Added benchmarking on larger matrices * [BAEL-3452] Added cipher algorithm * [BAEL-3452] Added deciphering * [BAEL-3452] Added break algorithm * [BAEL-3452] Finalizing break algorithm * Revert "Added benchmarking on larger matrices" This reverts commit 4ea87c0aea44bd73be721691a3c427b03854b442.
This commit is contained in:
		
							parent
							
								
									7d52a39289
								
							
						
					
					
						commit
						6b9f2c74ee
					
				| @ -17,6 +17,11 @@ | ||||
|             <artifactId>commons-codec</artifactId> | ||||
|             <version>${commons-codec.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.apache.commons</groupId> | ||||
|             <artifactId>commons-math3</artifactId> | ||||
|             <version>${commons-math3.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.projectlombok</groupId> | ||||
|             <artifactId>lombok</artifactId> | ||||
| @ -28,6 +33,7 @@ | ||||
|             <artifactId>tradukisto</artifactId> | ||||
|             <version>${tradukisto.version}</version> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.assertj</groupId> | ||||
|             <artifactId>assertj-core</artifactId> | ||||
| @ -52,6 +58,7 @@ | ||||
|         <tradukisto.version>1.0.1</tradukisto.version> | ||||
|         <org.assertj.core.version>3.9.0</org.assertj.core.version> | ||||
|         <commons-codec.version>1.11</commons-codec.version> | ||||
|         <commons-math3.version>3.6.1</commons-math3.version> | ||||
|     </properties> | ||||
| 
 | ||||
| </project> | ||||
| @ -0,0 +1,83 @@ | ||||
| package com.baeldung.algorithms.caesarcipher; | ||||
| 
 | ||||
| import org.apache.commons.math3.stat.inference.ChiSquareTest; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.stream.IntStream; | ||||
| 
 | ||||
| public class CaesarCipher { | ||||
|     private static final char LETTER_A = 'a'; | ||||
|     private static final char LETTER_Z = 'z'; | ||||
|     private static final int ALPHABET_SIZE = LETTER_Z - LETTER_A + 1; | ||||
|     private static final double[] ENGLISH_LETTERS_PROBABILITIES = {0.073, 0.009, 0.030, 0.044, 0.130, 0.028, 0.016, 0.035, 0.074, 0.002, 0.003, 0.035, 0.025, 0.078, 0.074, 0.027, 0.003, 0.077, 0.063, 0.093, 0.027, 0.013, 0.016, 0.005, 0.019, 0.001}; | ||||
| 
 | ||||
|     public String cipher(String message, int offset) { | ||||
|         StringBuilder result = new StringBuilder(); | ||||
| 
 | ||||
|         for (char character : message.toCharArray()) { | ||||
|             if (character != ' ') { | ||||
|                 int originalAlphabetPosition = character - LETTER_A; | ||||
|                 int newAlphabetPosition = (originalAlphabetPosition + offset) % ALPHABET_SIZE; | ||||
|                 char newCharacter = (char) (LETTER_A + newAlphabetPosition); | ||||
|                 result.append(newCharacter); | ||||
|             } else { | ||||
|                 result.append(character); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return result.toString(); | ||||
|     } | ||||
| 
 | ||||
|     public String decipher(String message, int offset) { | ||||
|         return cipher(message, ALPHABET_SIZE - (offset % ALPHABET_SIZE)); | ||||
|     } | ||||
| 
 | ||||
|     public int breakCipher(String message) { | ||||
|         return probableOffset(chiSquares(message)); | ||||
|     } | ||||
| 
 | ||||
|     private double[] chiSquares(String message) { | ||||
|         double[] expectedLettersFrequencies = expectedLettersFrequencies(message.length()); | ||||
| 
 | ||||
|         double[] chiSquares = new double[ALPHABET_SIZE]; | ||||
| 
 | ||||
|         for (int offset = 0; offset < chiSquares.length; offset++) { | ||||
|             String decipheredMessage = decipher(message, offset); | ||||
|             long[] lettersFrequencies = observedLettersFrequencies(decipheredMessage); | ||||
|             double chiSquare = new ChiSquareTest().chiSquare(expectedLettersFrequencies, lettersFrequencies); | ||||
|             chiSquares[offset] = chiSquare; | ||||
|         } | ||||
| 
 | ||||
|         return chiSquares; | ||||
|     } | ||||
| 
 | ||||
|     private long[] observedLettersFrequencies(String message) { | ||||
|         return IntStream.rangeClosed(LETTER_A, LETTER_Z) | ||||
|           .mapToLong(letter -> countLetter((char) letter, message)) | ||||
|           .toArray(); | ||||
|     } | ||||
| 
 | ||||
|     private long countLetter(char letter, String message) { | ||||
|         return message.chars() | ||||
|           .filter(character -> character == letter) | ||||
|           .count(); | ||||
|     } | ||||
| 
 | ||||
|     private double[] expectedLettersFrequencies(int messageLength) { | ||||
|         return Arrays.stream(ENGLISH_LETTERS_PROBABILITIES) | ||||
|           .map(probability -> probability * messageLength) | ||||
|           .toArray(); | ||||
|     } | ||||
| 
 | ||||
|     private int probableOffset(double[] chiSquares) { | ||||
|         int probableOffset = 0; | ||||
|         for (int offset = 0; offset < chiSquares.length; offset++) { | ||||
|             System.out.println(String.format("Chi-Square for offset %d: %.2f", offset, chiSquares[offset])); | ||||
|             if (chiSquares[offset] < chiSquares[probableOffset]) { | ||||
|                 probableOffset = offset; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return probableOffset; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,83 @@ | ||||
| package com.baeldung.algorithms.caesarcipher; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| 
 | ||||
| class CaesarCipherUnitTest { | ||||
|     private static final String SENTENCE = "he told me i could never teach a llama to drive"; | ||||
|     private static final String SENTENCE_SHIFTED_THREE = "kh wrog ph l frxog qhyhu whdfk d oodpd wr gulyh"; | ||||
|     private static final String SENTENCE_SHIFTED_TEN = "ro dyvn wo s myevn xofob dokmr k vvkwk dy nbsfo"; | ||||
| 
 | ||||
|     private CaesarCipher algorithm = new CaesarCipher(); | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceAndShiftThree_whenCipher_thenCipheredMessageWithoutOverflow() { | ||||
|         String cipheredSentence = algorithm.cipher(SENTENCE, 3); | ||||
| 
 | ||||
|         assertThat(cipheredSentence) | ||||
|           .isEqualTo(SENTENCE_SHIFTED_THREE); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceAndShiftTen_whenCipher_thenCipheredMessageWithOverflow() { | ||||
|         String cipheredSentence = algorithm.cipher(SENTENCE, 10); | ||||
| 
 | ||||
|         assertThat(cipheredSentence) | ||||
|           .isEqualTo(SENTENCE_SHIFTED_TEN); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceAndShiftThirtySix_whenCipher_thenCipheredLikeTenMessageWithOverflow() { | ||||
|         String cipheredSentence = algorithm.cipher(SENTENCE, 36); | ||||
| 
 | ||||
|         assertThat(cipheredSentence) | ||||
|           .isEqualTo(SENTENCE_SHIFTED_TEN); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceShiftedThreeAndShiftThree_whenDecipher_thenOriginalSentenceWithoutOverflow() { | ||||
|         String decipheredSentence = algorithm.decipher(SENTENCE_SHIFTED_THREE, 3); | ||||
| 
 | ||||
|         assertThat(decipheredSentence) | ||||
|           .isEqualTo(SENTENCE); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceShiftedTenAndShiftTen_whenDecipher_thenOriginalSentenceWithOverflow() { | ||||
|         String decipheredSentence = algorithm.decipher(SENTENCE_SHIFTED_TEN, 10); | ||||
| 
 | ||||
|         assertThat(decipheredSentence) | ||||
|           .isEqualTo(SENTENCE); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceShiftedTenAndShiftThirtySix_whenDecipher_thenOriginalSentenceWithOverflow() { | ||||
|         String decipheredSentence = algorithm.decipher(SENTENCE_SHIFTED_TEN, 36); | ||||
| 
 | ||||
|         assertThat(decipheredSentence) | ||||
|           .isEqualTo(SENTENCE); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceShiftedThree_whenBreakCipher_thenOriginalSentence() { | ||||
|         int offset = algorithm.breakCipher(SENTENCE_SHIFTED_THREE); | ||||
| 
 | ||||
|         assertThat(offset) | ||||
|           .isEqualTo(3); | ||||
| 
 | ||||
|         assertThat(algorithm.decipher(SENTENCE_SHIFTED_THREE, offset)) | ||||
|           .isEqualTo(SENTENCE); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenSentenceShiftedTen_whenBreakCipher_thenOriginalSentence() { | ||||
|         int offset = algorithm.breakCipher(SENTENCE_SHIFTED_TEN); | ||||
| 
 | ||||
|         assertThat(offset) | ||||
|           .isEqualTo(10); | ||||
| 
 | ||||
|         assertThat(algorithm.decipher(SENTENCE_SHIFTED_TEN, offset)) | ||||
|           .isEqualTo(SENTENCE); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user