Compare commits

...
8 Commits
Author SHA1 Message Date
KKlochko 4750242a1d Add the frequences of symbols as defaults. 2023-09-14 20:50:28 +03:00
KKlochko 174abd8072 Add a simple language analyzers.
continuous-integration/drone/push Build is failing
2023-09-13 21:37:53 +03:00
KKlochko 617dd34a23 Add an option to read a message from a file.
continuous-integration/drone/push Build is passing
2023-09-12 21:55:48 +03:00
KKlochko 63f55bbc50 Add the CI/CD configuration. 2023-09-11 22:23:17 +03:00
KKlochko 4b50b9289b Add tests for factories. 2023-09-11 22:20:27 +03:00
KKlochko 0b368c144e Add factories for symbols. 2023-09-11 22:09:23 +03:00
KKlochko 453b90d537 Add a simple cli interface. 2023-09-10 20:53:30 +03:00
KKlochko f32ae4a9d3 Add a implementation of Caesar cipher. 2023-09-09 21:26:38 +03:00
13 changed files with 453 additions and 6 deletions
+10
View File
@@ -0,0 +1,10 @@
kind: pipeline
type: docker
name: default
steps:
- name: test
image: clojure:temurin-11-lein-2.10.0-alpine
commands:
- lein test
+4 -1
View File
@@ -3,7 +3,10 @@
:url "https://gitlab.com/KKlochko/cipher-analytical-machine" :url "https://gitlab.com/KKlochko/cipher-analytical-machine"
:license {:name "LGPL" :license {:name "LGPL"
:url "http://www.gnu.org/licenses/lgpl-3.0.txt"} :url "http://www.gnu.org/licenses/lgpl-3.0.txt"}
:dependencies [[org.clojure/clojure "1.11.1"]] :dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/tools.cli "1.0.219"]
[org.apache.tika/tika-core "1.28.5"]
[org.apache.tika/tika-langdetect "1.28.5"]]
:main ^:skip-aot cipher-analytical-machine.core :main ^:skip-aot cipher-analytical-machine.core
:target-path "target/%s" :target-path "target/%s"
:profiles {:uberjar {:aot :all :profiles {:uberjar {:aot :all
+37
View File
@@ -0,0 +1,37 @@
(ns cipher-analytical-machine.caesar
(:require [clojure.string :as cs])
(:gen-class))
(defn calculate-char-index
"Calculate the index of a char."
[char symbols]
(cs/index-of symbols char))
(defn encrypt-index
"Calculates new index of a character. Uses its index, the key and max-index (exclusive)."
[index key max-index]
(-> index
(+ key)
(mod max-index)))
(defn encrypt-char
"Calculates new index of a character. Uses a char from symbols, the key and max-index (exclusive)."
[char key symbols max-index]
(get symbols
(-> char
(calculate-char-index symbols)
(encrypt-index key max-index))))
(defn encrypt-message
"Encrypt a message using the Caesar cipher."
[message key symbols]
(let [max-index (count symbols)]
(cs/join
(map (fn [char] (encrypt-char char key symbols max-index))
message))))
(defn decrypt-message
"Decrypt the ciphtext using the Caesar cipher."
[ciphertext key symbols]
(encrypt-message ciphertext (- key) symbols))
+98
View File
@@ -0,0 +1,98 @@
(ns cipher-analytical-machine.cli
(:require
[cipher-analytical-machine.file :as file]
[cipher-analytical-machine.caesar :as caesar]
[clojure.string :as cs]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cipher-options
#{"Caesar"})
(def cli-options
[["-m" "--message MESSAGE" "The message will be encrypted or decrypted."]
["-M" "--message-file MESSAGE-FILE" "The file contains the message that will be encrypted or decrypted."]
["-k" "--key KEY" "The key will be used to encrypt or decrypt."]
["-c" "--cipher CIPHER" "The cipher will be used to encrypt or decrypt a message."
:default "Caesar"]
["-e" "--encrypt" "Encrypt the message."]
["-d" "--decrypt" "Decrypt the message."]
["-h" "--help"]])
(defn usage [options-summary]
(->> ["This is cipher-analytical-machine. It can help you to learn how ciphers works."
""
"Usage: cipher-analytical-machine [options]"
""
"Options:"
options-summary
""]
(cs/join \newline)))
(defn error-msg [errors]
(str "The following errors occurred while parsing your command:\n\n"
(cs/join \newline errors)))
(defn map-has-keys?
"Return true if all keys in the map."
[map keys]
(every? #(contains? map %) keys))
(defn validate-args
"Validate command line arguments. Either return a map indicating the program
should exit (with an error message, and optional ok status), or a map
indicating the action the program should take and the options provided."
[args]
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)]
(cond
(:help options)
{:exit-message (usage summary) :ok? true}
errors
{:exit-message (error-msg errors)}
(map-has-keys? options [:encrypt, :decrypt])
{:exit-message (error-msg ["You can't use enctypt and decrypt mode at the same time!!!"])}
(map-has-keys? options [:message, :message-file])
{:exit-message (error-msg ["You can't use message and message-file options at the same time!!!"])}
(and (map-has-keys? options [:cipher, :key])
(or (contains? options :message)
(contains? options :message-file))
(or (contains? options :encrypt)
(contains? options :decrypt)))
{:options options :arguments arguments}
:else
{:exit-message (usage summary)})))
(defn exit
[exit-message status]
(println exit-message)
(System/exit (if status 0 1)))
(defn get-message
"Return the message"
[options arguments]
(cond
(contains? options :message)
(:message options)
(contains? options :message-file)
(file/read-file (:message-file options))))
(defn actions
[options arguments]
(println (str options arguments))
(let [message (get-message options arguments)
key (Integer/parseInt (:key options))
symbols "abc"]
(println
(cond
(contains? options :encrypt)
(caesar/encrypt-message message key symbols)
(contains? options :decrypt)
(caesar/decrypt-message message key symbols)))))
+7 -2
View File
@@ -1,7 +1,12 @@
(ns cipher-analytical-machine.core (ns cipher-analytical-machine.core
(:require
[cipher-analytical-machine.cli :as cli])
(:gen-class)) (:gen-class))
(defn -main (defn -main
"I don't do a whole lot ... yet."
[& args] [& args]
(println "Hello, World!")) (let [{:keys [options arguments exit-message ok?]} (cli/validate-args args)]
(if exit-message
(cli/exit exit-message ok?)
(cli/actions options arguments))))
+18
View File
@@ -0,0 +1,18 @@
(ns cipher-analytical-machine.file
(:require [clojure.java.io :as io])
(:gen-class))
(defn is-file-exists?
"Checks that a file is exists"
[file-path]
(-> (io/file file-path)
(.exists)))
(defn is-file-not-exists?
"Checks that a file is not exists"
[file-path]
(not (is-file-exists? file-path)))
(defn read-file [file-path]
(slurp file-path))
@@ -0,0 +1,21 @@
(ns cipher-analytical-machine.language_analyzer
(:import [org.apache.tika.language LanguageIdentifier])
(:gen-class))
(defn detect-language
"Return a identified language."
[text]
(-> (new LanguageIdentifier text)
.getLanguage))
(defn is-language?
"Check if the text written a language."
[text language-id]
(= (detect-language text)
language-id))
(defn is-nonsense?
"Check if the text written a language is not a language."
[text language-id]
(not (is-language? text language-id)))
@@ -0,0 +1,34 @@
(ns cipher-analytical-machine.symbol_factories
(:require [clojure.string :as cs])
(:gen-class))
(defn lower-english-alphabet-factory
"Return the English alphabet with only lowercase letters"
[]
(reduce
(fn [acc val] (str acc (char val)))
""
(range (int \a) (inc (int \z)))))
(defn english-alphabet-factory
"Return the English alphabet with lower and upper case letters"
[]
(let [letters (lower-english-alphabet-factory)]
(str letters (cs/upper-case letters))))
(defn lower-ukrainian-alphabet-factory
"Return the Ukrainian alphabet with only lowercase letters"
[]
"абвгґдеєжзиіїйклмнопрстуфхцчшщьюя")
(defn ukrainian-alphabet-factory
"Return the Ukrainian alphabet with lower and upper case letters"
[]
(let [letters (lower-ukrainian-alphabet-factory)]
(str letters (cs/upper-case letters))))
(defn digit-set-factory
"Return digits"
[]
"0123456789")
@@ -0,0 +1,69 @@
(ns cipher-analytical-machine.symbol_frequences
(:gen-class))
(def english-letter-frequences {
\a 0.0804
\b 0.0154
\c 0.0306
\d 0.0399
\e 0.1251
\f 0.0230
\g 0.0196
\h 0.0549
\i 0.0726
\j 0.0016
\k 0.0067
\l 0.0414
\m 0.0253
\n 0.0709
\o 0.0760
\p 0.0200
\q 0.0011
\r 0.0612
\s 0.0654
\t 0.0925
\u 0.0271
\v 0.0099
\w 0.0192
\x 0.0019
\y 0.0173
\z 0.0009
})
(def ukrainian-letter-frequences {
\а 0.064
\б 0.013
0.046
\г 0.013
0.0001
0.027
\е 0.042
0.005
0.007
0.020
0.055
\і 0.044
0.010
0.009
0.033
0.027
0.029
0.068
\о 0.086
\п 0.025
\р 0.043
\с 0.037
0.045
\у 0.027
0.003
\х 0.011
0.010
0.011
0.005
0.004
0.016
0.008
0.019
\ 0.138
})
@@ -0,0 +1,64 @@
(ns cipher-analytical-machine.caesar-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.caesar :refer :all]
))
(deftest calculate-char-index-test
(let [symbols "abc123%$"]
(testing "Letter 'b' in symbols"
(is (= 1 (calculate-char-index \b symbols))))
(testing "Letter 'd' does not in symbols"
(is (= nil (calculate-char-index \d symbols))))
(testing "Digit '1' in symbols"
(is (= 3 (calculate-char-index \1 symbols))))
(testing "Digit '0' does not in symbols"
(is (= nil (calculate-char-index \0 symbols))))
(testing "Symbol '?' does not in symbols"
(is (= nil (calculate-char-index \? symbols))))))
(deftest encrypt-index-test
(testing "The new index is in the range."
(is (= 2 (encrypt-index 0 2 33))))
(testing "The new index that equal to max-index."
(is (= 0 (encrypt-index 30 3 33))))
(testing "The new index that greater than max-index."
(is (= 1 (encrypt-index 30 4 33))))
(testing "The new index that lower than 0."
(is (= 32 (encrypt-index 0 -1 33)))))
(deftest encrypt-char-test
(let [symbols "abc"
max-index (count symbols)]
(testing "Letter 'a' is encrypted to 'b'"
(is (= \b (encrypt-char \a 1 symbols max-index))))
(testing "Letter 'b' is encrypted to 'a'"
(is (= \a (encrypt-char \b 2 symbols max-index))))
(testing "Letter 'b' is decryped to 'a'"
(is (= \a (encrypt-char \b -1 symbols max-index))))))
(deftest encrypt-message-text
(let [symbols "abc"]
(testing "The message 'abc' is encrypted to 'bca'"
(is (= "bca" (encrypt-message "abc" 1 symbols))))
(testing "The message 'bca' is decrypted to 'abc'"
(is (= "abc" (encrypt-message "bca" -1 symbols))))))
(deftest decrypt-message-text
(let [symbols "abc"]
(testing "The message 'bca' is decrypted to 'abc'"
(is (= "abc" (decrypt-message "bca" 1 symbols)))
(testing "The message 'abc' is encrypted to 'bca'"
(is (= "bca" (decrypt-message "abc" -1 symbols)))))))
@@ -2,6 +2,3 @@
(:require [clojure.test :refer :all] (:require [clojure.test :refer :all]
[cipher-analytical-machine.core :refer :all])) [cipher-analytical-machine.core :refer :all]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 1))))
@@ -0,0 +1,45 @@
(ns cipher-analytical-machine.language_analyzer_test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.language_analyzer :refer :all]
))
(deftest detect-language-test
(testing "Checking the English text"
(is (= "en" (detect-language "This is a sentence that uses English."))))
(testing "Checking the Ukrainian text"
(is (= "uk" (detect-language "Текст, що написаний українською."))))
(testing "Checking a gibberish that uses English letters"
(is (= "lt" (detect-language "dfgjldfjgdfl gjdfg idfjig hdgesr khs e."))))
(testing "Checking a gibberish that uses Ukrainian letters"
(is (= "uk" (detect-language "іаврпшшцді врадів аргів аріл варї йцґ.")))))
(deftest is-language?-test
(testing "Checking the English text"
(is (is-language? "This is a sentence that uses English." "en")))
(testing "Checking the Ukrainian text"
(is (is-language? "Текст, що написаний українською." "uk")))
(testing "Checking a gibberish that uses English letters"
(is (is-language? "dfgjldfjgdfl gjdfg idfjig hdgesr khs e." "lt")))
(testing "Checking a gibberish that uses Ukrainian letters"
(is (is-language? "іаврпшшцді врадів аргів аріл варї йцґ." "uk"))))
(deftest is-nonsense?-test
(testing "Checking the English text"
(is (not (is-nonsense? "This is a sentence that uses English." "en"))))
(testing "Checking the Ukrainian text"
(is (not (is-nonsense? "Текст, що написаний українською." "uk"))))
(testing "Checking a gibberish that uses English letters"
(is (is-nonsense? "dfgjldfjgdfl gjdfg idfjig hdgesr khs e." "en")))
(testing "Checking a gibberish that uses Ukrainian letters"
(is (is-nonsense? "іаврпшшцді врадів аргів аріл варї йцґ." "uk"))))
@@ -0,0 +1,46 @@
(ns cipher-analytical-machine.symbol-factories-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.symbol_factories :refer :all]
))
(deftest lower-english-alphabet-factory-test
(let [symbols (lower-english-alphabet-factory)]
(testing "It must have same order"
(is (= "abcdefghijklmnopqrstuvwxyz" symbols)))
(testing "It must have 26 letters"
(is (= 26 (count symbols))))))
(deftest english-alphabet-factory-test
(let [symbols (english-alphabet-factory)]
(testing "It must have same order"
(is (= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" symbols)))
(testing "It must have 2*26 letters"
(is (= (* 2 26) (count symbols))))))
(deftest lower-ukrainian-alphabet-factory-test
(let [symbols (lower-ukrainian-alphabet-factory)]
(testing "It must have same order"
(is (= "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" symbols)))
(testing "It must have 33 letters"
(is (= 33 (count symbols))))))
(deftest ukrainian-alphabet-factory-test
(let [symbols (ukrainian-alphabet-factory)]
(testing "It must have same order"
(is (= "абвгґдеєжзиіїйклмнопрстуфхцчшщьюяАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ" symbols)))
(testing "It must have 2*33 letters"
(is (= (* 2 33) (count symbols))))))
(deftest digit-set-factory-test
(let [symbols (digit-set-factory)]
(testing "It must have same order"
(is (= "0123456789" symbols)))
(testing "It must have 10 digits"
(is (= 10 (count symbols))))))