Guest User

Untitled

a guest
Jul 22nd, 2018
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.24 KB | None | 0 0
  1. ;;;; Vigenere Cipher Encoder & Decoder program
  2.  
  3. (load "unit-test")
  4. (defpackage :com.izak-halseide.vigenere-cipher
  5. (:use :common-lisp
  6. :com.izak-halseide.unit-test)
  7. (:export :encode
  8. :decode))
  9. (in-package :com.izak-halseide.vigenere-cipher)
  10.  
  11. (defparameter *alphabet* "abcdefghijklmnopqrstuvwxyz")
  12. (defparameter *alpha-size* (length *alphabet*))
  13.  
  14. ;; Portable args fetching
  15. (defun my-command-line ()
  16. "Return the command line args list (portable)"
  17. (or
  18. #+CLISP ext:*args*
  19. #+SBCL *posix-argv*
  20. #+LISPWORKS system:*line-arguments-list*
  21. #+CMU extensions:*command-line-words*
  22. nil))
  23.  
  24. (defun letter->num (letter)
  25. (if (stringp letter)
  26. (position (elt letter 0) *alphabet* :test 'char-equal)
  27. (position letter *alphabet* :test 'char-equal)))
  28.  
  29. (deftest test-letter->num ()
  30. (check
  31. (= (letter->num #\a) 0)
  32. (= (letter->num #\z) 25)
  33. (= (letter->num #\n) 13)))
  34.  
  35. (defun num->letter (num)
  36. (elt *alphabet* num))
  37.  
  38. (deftest test-num->letter ()
  39. (check
  40. (char= #\a (num->letter 0))
  41. (char= #\z (num->letter 25))
  42. (char= #\m (num->letter 12))))
  43.  
  44. (defun shift-letter (letter num)
  45. "Apply a Caesar cipher letter shift by num"
  46. ;; The LET and IF is necessary to make the result match the case of the input
  47. (let ((result (num->letter (mod (+ (letter->num letter) num) *alpha-size*))))
  48. (if (upper-case-p letter) (char-upcase result) result)))
  49.  
  50. (deftest test-shift-letter ()
  51. (check
  52. (char= #\b (shift-letter #\a 1))
  53. (char= #\a (shift-letter #\z 1))
  54. (char= #\z (shift-letter #\a -1))
  55. (char= #\a (shift-letter #\b -1))
  56. (char= #\P (shift-letter #\N 2))
  57. (char= #\P (shift-letter #\P 0))
  58. (char= #\s (shift-letter #\s 0))))
  59.  
  60. (defun encode (message key)
  61. (let ((output (make-array (length message) :element-type 'character :fill-pointer 0))) ; Create an appendable string
  62. (loop
  63. with key-len = (length key)
  64. with key-index = 0
  65. for letter across message do
  66. (if (alpha-char-p letter)
  67. ; then
  68. (let*
  69. ((key-value (letter->num (elt key key-index)))
  70. (new-letter (shift-letter letter key-value)))
  71. (vector-push new-letter output)
  72. ;; Is there a way to do this more concisely with LOOP?:
  73. (incf key-index)
  74. (if (>= key-index key-len) (setf key-index 0)))
  75. ; else
  76. (vector-push letter output)))
  77. output))
  78.  
  79. (deftest test-encode ()
  80. (check
  81. ;; Check example and that 'a' changes nothing
  82. (string= (encode "messages" "keys") "wiqkkkck")
  83. (string= (encode "Secret" "a") "Secret")
  84. ;; Only the message case should affect it
  85. (string= (encode "a" "a") "a")
  86. (string= (encode "A" "a") "A")
  87. (string= (encode "a" "A") "a")
  88. (string= (encode "A" "A") "A")))
  89.  
  90. (defun decode (message key)
  91. (let ((output (make-array (length message) :element-type 'character :fill-pointer 0))) ; Create an appendable string
  92. (loop
  93. with key-len = (length key)
  94. with key-index = 0
  95. for letter across message do
  96. (if (alpha-char-p letter)
  97. ; then
  98. (let*
  99. ((key-value (letter->num (elt key key-index)))
  100. (new-letter (shift-letter letter (- key-value))))
  101. (vector-push new-letter output)
  102. ;; Is there a way to do this more concisely with LOOP?:
  103. (incf key-index)
  104. (if (>= key-index key-len) (setf key-index 0)))
  105. ; else
  106. (vector-push letter output)))
  107. output))
  108.  
  109. (deftest test-decode ()
  110. (check
  111. (string= (decode "T" "a") "T")
  112. (string= (decode "Hflmo" "ab") "Hello"))
  113. (string= (decode "DARG" "bacon") "CAPS"))
  114.  
  115. (deftest test-encode-and-decode ()
  116. (check
  117. ;; Decoding an encoding should be the original message
  118. (string= (decode (encode "message" "ace") "ace") "message")))
  119.  
  120. (deftest test-everything ()
  121. (write-line "Testing...")
  122. (check
  123. (test-num->letter)
  124. (test-letter->num)
  125. (test-shift-letter)
  126. (test-encode)
  127. (test-decode)
  128. (test-encode-and-decode)))
  129.  
  130. (defun good-key-p (key-string)
  131. "A key must be only alphabetical letters"
  132. (= 0 (count-if-not #'alpha-char-p key-string)))
  133.  
  134. (defun split-args-for-cipher (args)
  135. "Takes the args and splits them into the expected message and key"
  136. (let ((message nil) (key (elt args (1- (length args)))))
  137. ;; If the first word is an argument (beginning with "-"), then we skip that
  138. ;; by subsequencing from a start of 1 instead of 0
  139. (setf message (subseq args (if (char= #\- (elt (first args) 0)) 1 0) (1- (length args))))
  140. (setf message (format nil "~{~A~^ ~}" message)) ; This joins the list into a string
  141. (list message key)))
  142.  
  143. (defun encode-args (args)
  144. (let ((arg-discovery (split-args-for-cipher args)))
  145. (format t "~a" (encode (elt arg-discovery 0) (elt arg-discovery 1)))))
  146.  
  147. (defun decode-args (args)
  148. (let ((arg-discovery (split-args-for-cipher args)))
  149. (format t "~a" (decode (elt arg-discovery 0) (elt arg-discovery 1)))))
  150.  
  151. (defun show-help ()
  152. (write-line "Showing help for vigenere-cipher...")
  153. (terpri)
  154. (write-line "Description:")
  155. (write-line "Encode or decode a message with the vigenere cipher, which means you use a word that is repeated across the length of the message and each letter shifts a letter of the plaintext by a certain amount. Special note: do not enter a message that begins with a dash (\"-\").")
  156. (terpri)
  157. (write-line "Usage: \"vigenere-cipher (-decode) message key\".")
  158. (terpri)
  159. (write-line "Options:")
  160. (write-line "Note that only the first option given will be used, and that each option can be abbreviated to one letter.")
  161. (write-line " -test tests the program, and does not take any additional input.")
  162. (write-line " -help shows this help.")
  163. (write-line " -? is the same as \"-help\"")
  164. (write-line " -decode decodes a message instead of encoding a message.")
  165. (terpri))
  166.  
  167. (defun main ()
  168. (let ((args (my-command-line)))
  169. (cond
  170. ((= 0 (length args)) (show-help))
  171. ((string-equal "-h" (first args)) (show-help))
  172. ((string-equal "-?" (first args)) (show-help))
  173. ((string-equal "-help" (first args)) (show-help))
  174. ((string-equal "-t" (first args)) (test-everything))
  175. ((string-equal "-test" (first args)) (test-everything))
  176. ((string-equal "-d" (first args)) (decode-args args))
  177. ((string-equal "-decode" (first args)) (decode-args args))
  178. (t (encode-args args)))))
  179.  
  180. (main)
Add Comment
Please, Sign In to add comment