diff --git a/src/cipher_analytical_machine/ciphers/simple_substitution.clj b/src/cipher_analytical_machine/ciphers/simple_substitution.clj new file mode 100644 index 0000000..9d21c98 --- /dev/null +++ b/src/cipher_analytical_machine/ciphers/simple_substitution.clj @@ -0,0 +1,47 @@ +(ns cipher-analytical-machine.ciphers.simple-substitution + (:require [clojure.string :as cs] + [clojure.set :as set] + [cipher-analytical-machine.ciphers.caesar :as caesar]) + (:gen-class)) + +(defn shuffled-numbers + "Generate the shuffled order for integer list." + [size] + (-> size + (take (range)) + (shuffle))) + +(defn generate-substitution-table + "Generate the map (char, int) for the substittion." + [symbols] + (->> (count symbols) + (shuffled-numbers) + (zipmap symbols))) + +(defn find-value-in-table + "It uses the substitution table to find the value of a char or a number." + [char substitution-table symbols] + (get substitution-table char)) + +(defn encrypt-message + "Encrypt a message using the simple substitution cipher. The function is case-insensitive. If a symbol isn't in the symbols list, then it will be removed." + [message key substitution-table symbols] + (let [max-index (count symbols)] + (->> message + (cs/lower-case) + (caesar/encrypt-message key symbols) + (map (fn [char] (find-value-in-table char substitution-table symbols))) + (cs/join \,)))) + +(defn decrypt-message + "Decrypt a message using the simple substitution cipher. The function is case-insensitive." + [message key substitution-table symbols] + (let [substitution-table (set/map-invert substitution-table) + max-index (count symbols) + message (cs/split message #",")] + (->> message + (map #(Integer/parseInt %)) + (map (fn [char] (find-value-in-table char substitution-table symbols))) + (caesar/encrypt-message key symbols) + (cs/join)))) + diff --git a/test/cipher_analytical_machine/ciphers/simple_substitution_test.clj b/test/cipher_analytical_machine/ciphers/simple_substitution_test.clj new file mode 100644 index 0000000..131de20 --- /dev/null +++ b/test/cipher_analytical_machine/ciphers/simple_substitution_test.clj @@ -0,0 +1,44 @@ +(ns cipher-analytical-machine.ciphers.simple-substitution + (:require + [clojure.test :refer :all] + [cipher-analytical-machine.ciphers.simple-substitution :refer :all])) + +(deftest find-value-in-table-test + (let [table {\a 1 \b 2 \c 3} + rtable {1 \a 2 \b 3 \c}] + (testing "If the symbol is in the table as a key, then the result won't nil." + (are [key expected] + (= expected (find-value-in-table key table)) + \a 1 + \d nil + 1 nil + 5 nil)) + + (testing "If the digit is in the reversed table as a key, then the result won't nil." + (are [key expected] + (= expected (find-value-in-table key rtable)) + \a nil + \d nil + 1 \a + 5 nil)))) + +(deftest encrypt-message-text + (let [symbols "abc" + table {\a 1 \b 2 \c 3}] + (testing "The function must encrypt the message and remove unknown symbols." + (are [message key expected] + (= expected (encrypt-message message key table symbols)) + "abc" 0 "1,2,3" + "abc" 1 "2,3,1" + "aDbdc" 0 "1,2,3")))) + +(deftest decrypt-message-text + (let [symbols "abc" + table {\a 1 \b 2 \c 3}] + (testing "The function must decrypt the message and remove unknown numbers." + (are [message key expected] + (= expected (decrypt-message message key table symbols)) + "1,2,3" 0 "abc" + "2,3,1" 1 "abc" + "1,12,2,3" 0 "abc")))) +