mirror of
https://github.com/Smaug123/ClassicalCiphers.jl
synced 2025-10-06 09:58:41 +00:00
Add source files this time
This commit is contained in:
11
src/caesar.jl
Normal file
11
src/caesar.jl
Normal file
@@ -0,0 +1,11 @@
|
||||
function encrypt_caesar(plaintext, key::Integer)
|
||||
# plaintext: string; key: integer offset, so k=1 sends "a" to "b"
|
||||
key = ((key-1) % 26) + 1
|
||||
encrypt_monoalphabetic(plaintext, join([collect(Char(97+key):'z'); collect('a':Char(97+key-1))]))
|
||||
end
|
||||
|
||||
function decrypt_caesar(ciphertext, key::Integer)
|
||||
# ciphertext: string; key: integer offset, so k=1 decrypts "B" as "A"
|
||||
key = ((key-1) % 26) + 1
|
||||
encrypt_caesar(ciphertext, 26-key)
|
||||
end
|
42
src/common.jl
Normal file
42
src/common.jl
Normal file
@@ -0,0 +1,42 @@
|
||||
function letters_only(text)
|
||||
# text: string; removes all non-alphabetic characters
|
||||
filter(x -> ('A' <= x <= 'Z' || 'a' <= x <= 'z'), text)
|
||||
end
|
||||
|
||||
function rotateRight(arr, n)
|
||||
# implementation of the Mathematica function RotateRight
|
||||
ans = copy(arr)
|
||||
for i in 1:length(arr)
|
||||
ans[i] = arr[((2*length(ans)+i-n-1) % length(ans)) + 1]
|
||||
end
|
||||
ans
|
||||
end
|
||||
|
||||
function rotateLeft(arr, n)
|
||||
# implementation of the Mathematica function RotateLeft
|
||||
ans = copy(arr)
|
||||
for i in 1:length(arr)
|
||||
ans[i] = arr[((i+n-1) % length(ans)) + 1]
|
||||
end
|
||||
ans
|
||||
end
|
||||
|
||||
flatten{T}(a::Array{T,1}) = any(map(x->isa(x,Array),a))? flatten(vcat(map(flatten,a)...)): a
|
||||
flatten{T}(a::Array{T}) = reshape(a,prod(size(a)))
|
||||
flatten(a)=a
|
||||
|
||||
function splitBy(arr, func)
|
||||
# implementation of the Mathematica function SplitBy
|
||||
# splits the array into sublists so that each list has the same value of func
|
||||
# on its elements
|
||||
arrinternal = map(func, arr)
|
||||
currans = Vector{Integer}[[arr[1]]]
|
||||
for i in 2:length(arr)
|
||||
if arrinternal[i] != arrinternal[i-1]
|
||||
append!(currans, Vector{Integer}[[arr[i]]])
|
||||
else
|
||||
append!(currans[end], [arr[i]])
|
||||
end
|
||||
end
|
||||
currans
|
||||
end
|
28
src/monoalphabetic.jl
Normal file
28
src/monoalphabetic.jl
Normal file
@@ -0,0 +1,28 @@
|
||||
function encrypt_monoalphabetic(plaintext, key::Dict)
|
||||
# plaintext: string; key: dictionary of {'a' => 'b'}, etc, for replacing 'a' with 'b'
|
||||
join([(i in keys(key) ? key[i] : i) for i in plaintext], "")
|
||||
end
|
||||
|
||||
function decrypt_monoalphabetic(ciphertext, key::Dict)
|
||||
# ciphertext: string; key: dictionary of {'a' => 'b'}, etc, where the plaintext 'a' was
|
||||
# replaced by ciphertext 'b'. No character should appear more than once
|
||||
# as a value in {key}.
|
||||
encrypt_monoalphabetic(ciphertext, Dict{Char, Char}([reverse(a) for a in key]))
|
||||
end
|
||||
|
||||
function encrypt_monoalphabetic(plaintext, key::AbstractString)
|
||||
# plaintext: string; key: string of length 26, first character is the image of 'a', etc
|
||||
# working in lowercase; key is assumed only to have each element appearing once
|
||||
# and to be in lowercase.
|
||||
dict = Dict{Char, Char}(map(x -> (x[1]+96, x[2]), enumerate(key)))
|
||||
encrypt_monoalphabetic(lowercase(plaintext), dict)
|
||||
end
|
||||
|
||||
function decrypt_monoalphabetic(ciphertext, key::AbstractString)
|
||||
# ciphertext: string; key: string of length 26, first character is the image of 'a', etc
|
||||
# working in lowercase; key is assumed only to have each element appearing once
|
||||
# and to be in lowercase
|
||||
# so decrypt_monoalphabetic("cb", "cbade…") is "ab"
|
||||
dict = [(a => Char(96 + search(lowercase(key), a))) for a in lowercase(key)]
|
||||
encrypt_monoalphabetic(lowercase(ciphertext), dict)
|
||||
end
|
132
src/solitaire.jl
Normal file
132
src/solitaire.jl
Normal file
@@ -0,0 +1,132 @@
|
||||
include("common.jl")
|
||||
|
||||
function next_solitaire(deckIn)
|
||||
# performs one round of Solitaire on the given deck
|
||||
# first joker
|
||||
deck = deckIn
|
||||
jokerPos = findin(deck, 53)[1]
|
||||
if jokerPos != length(deck)
|
||||
inter = deck[jokerPos+1]
|
||||
deck[jokerPos+1] = deck[jokerPos]
|
||||
deck[jokerPos] = inter
|
||||
else
|
||||
inter = deck[end]
|
||||
deck[end] = deck[1]
|
||||
deck[1] = inter
|
||||
deck = rotateRight(deck)
|
||||
end
|
||||
# second joker
|
||||
jokerPos = findin(deck, 54)[1]
|
||||
if jokerPos <= length(deck) - 2
|
||||
inter = deck[jokerPos]
|
||||
deck[jokerPos] = deck[jokerPos+1]
|
||||
deck[jokerPos + 1] = deck[jokerPos + 2]
|
||||
deck[jokerPos + 2] = inter
|
||||
elseif jokerPos == length(deck) - 1
|
||||
inter = deck[length(deck)-1]
|
||||
inter1 = deck[length(deck)]
|
||||
deck[end] = deck[1]
|
||||
deck[length(deck)-1] = inter1
|
||||
deck[1] = inter
|
||||
deck = rotateRight(deck, 1)
|
||||
elseif jokerPos == length(deck)
|
||||
inter = deck[1]
|
||||
inter1 = deck[end]
|
||||
deck[1] = deck[2]
|
||||
deck[2] = inter1
|
||||
deck[end] = inter
|
||||
deck = rotateRight(deck,1)
|
||||
end
|
||||
# triple-cut
|
||||
split = splitBy(deck, x -> x > 52)
|
||||
if deck[1] > 52 && deck[end] > 52
|
||||
1
|
||||
# do nothing
|
||||
elseif deck[1] > 52
|
||||
split = rotateRight(split, 1)
|
||||
elseif deck[end] > 52
|
||||
split = rotateLeft(split, 1)
|
||||
else
|
||||
inter = split[1]
|
||||
split[1] = split[end]
|
||||
split[end] = inter
|
||||
end
|
||||
deck = flatten(split)
|
||||
# take bottom of deck and put it just above last card
|
||||
cardsToTake = (deck[end] > 52) ? 0 : deck[end]
|
||||
|
||||
intermediate = rotateLeft(deck[1:length(deck)-1], cardsToTake)
|
||||
append!(intermediate, [deck[end]])
|
||||
deck = intermediate
|
||||
|
||||
return deck
|
||||
end
|
||||
|
||||
function keychar_from_deck(deck::Vector)
|
||||
# given a deck, returns an integer which is the Solitaire key value
|
||||
# output by that deck
|
||||
deck[((deck[1]==54 ? 53 : deck[1]) % length(deck)) + 1]
|
||||
end
|
||||
|
||||
type SolitaireKeyStream
|
||||
deck::Vector
|
||||
end
|
||||
|
||||
Base.start(b::SolitaireKeyStream) = (next_solitaire(b.deck))
|
||||
function Base.next(b::SolitaireKeyStream, state)
|
||||
curState = state
|
||||
while keychar_from_deck(curState) > 52
|
||||
curState = next_solitaire(curState)
|
||||
end
|
||||
(keychar_from_deck(curState), next_solitaire(curState))
|
||||
end
|
||||
Base.done(b::SolitaireKeyStream, state) = false
|
||||
|
||||
function encrypt_solitaire(string, initialDeck::Vector)
|
||||
inp = uppercase(letters_only(string))
|
||||
ans = ""
|
||||
i = 0
|
||||
for keyval in SolitaireKeyStream(initialDeck)
|
||||
i += 1
|
||||
if i > length(inp)
|
||||
break
|
||||
end
|
||||
ans *= encrypt_caesar(inp[i], keyval)
|
||||
end
|
||||
return ans
|
||||
end
|
||||
|
||||
function decrypt_solitaire(string, initialDeck::Vector)
|
||||
inp = uppercase(letters_only(string))
|
||||
ans = ""
|
||||
i = 0
|
||||
for keyval in SolitaireKeyStream(initialDeck)
|
||||
i += 1
|
||||
if i > length(inp)
|
||||
break
|
||||
end
|
||||
ans *= decrypt_caesar(inp[i], keyval)
|
||||
end
|
||||
return ans
|
||||
end
|
||||
|
||||
function key_deck(key::AbstractString)
|
||||
# returns the Solitaire deck after it has been keyed with the given string
|
||||
deck = collect(1:54)
|
||||
for keyval in uppercase(letters_only(key))
|
||||
deck = next_solitaire(deck)
|
||||
cardsToTake = Int(keyval)-64
|
||||
intermediate = rotateLeft(deck[1:length(deck)-1], cardsToTake)
|
||||
append!(intermediate, [deck[end]])
|
||||
deck = intermediate
|
||||
end
|
||||
deck
|
||||
end
|
||||
|
||||
function encrypt_solitaire(string, key::AbstractString)
|
||||
encrypt_solitaire(string, key_deck(key))
|
||||
end
|
||||
|
||||
function decrypt_solitaire(string, key::AbstractString)
|
||||
decrypt_solitaire(string, key_deck(key))
|
||||
end
|
39
src/vigenere.jl
Normal file
39
src/vigenere.jl
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Encrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
||||
For example, encrypt_vigenere("ab", [0, 1]) returns "ac".
|
||||
"""
|
||||
function encrypt_vigenere(plaintext, key::Array)
|
||||
# plaintext: string; key: vector of integer offsets, so [0, 1] encrypts "ab" as "ac"
|
||||
|
||||
ans = [encrypt_caesar(char, key[(i-1) % length(key)+1]) for (i, char) in enumerate(letters_only(plaintext))]
|
||||
join(ans, "")
|
||||
|
||||
end
|
||||
|
||||
"""
|
||||
Decrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
||||
For example, decrypt_vigenere("ac", [0, 1]) returns "ab".
|
||||
"""
|
||||
function decrypt_vigenere(ciphertext, key::Array)
|
||||
# ciphertext: string; key: vector of integer offsets, so [0, 1] decrypts "ac" as "ab"
|
||||
encrypt_vigenere(ciphertext, 26-key)
|
||||
end
|
||||
|
||||
"""
|
||||
Encrypts the given string using the Vigenere cipher according to the given keystring.
|
||||
For example, encrypt_vigenere("ab", "ab") returns "ac".
|
||||
"""
|
||||
function encrypt_vigenere(ciphertext, key::AbstractString)
|
||||
# ciphertext: string; key: string, so "ab" encrypts "ab" as "ac"
|
||||
|
||||
encrypt_vigenere(ciphertext, [Int(i)-97 for i in lowercase(letters_only(key))])
|
||||
end
|
||||
|
||||
"""
|
||||
Decrypts the given string using the Vigenere cipher according to the given keystring.
|
||||
For example, decrypt_vigenere("ab", "ac") returns "ab".
|
||||
"""
|
||||
function decrypt_vigenere(plaintext, key::AbstractString)
|
||||
# plaintext: string; key: string, so "ab" decrypts "ac" as "ab"
|
||||
decrypt_vigenere(plaintext, [Int(i)-97 for i in lowercase(letters_only(key))])
|
||||
end
|
15
test/monoalphabetic.jl
Normal file
15
test/monoalphabetic.jl
Normal file
@@ -0,0 +1,15 @@
|
||||
using ClassicalCiphers
|
||||
using Base.Test
|
||||
|
||||
@test encrypt_monoalphabetic("aBcbD", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o', 'D' => 'D')) == "5@coD"
|
||||
|
||||
# when given a string second argument, it lowercases everything
|
||||
@test encrypt_monoalphabetic("THIS CODE WAS INVENTED BY JULIUS CAESAR", "DEFGHIJKLMNOPQRSTUVWXYZABC") == "WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU"
|
||||
|
||||
@test decrypt_monoalphabetic("5@coD", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o', 'D' => 'D')) == "aBcbD"
|
||||
|
||||
@test decrypt_monoalphabetic("WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU", "DEFGHIJKLMNOPQRSTUVWXYZABC") == lowercase("THIS CODE WAS INVENTED BY JULIUS CAESAR")
|
||||
|
||||
@test encrypt_caesar("THIS CODE WAS INVENTED BY JULIUS CAESAR", 3) == lowercase("WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU")
|
||||
|
||||
@test decrypt_caesar("WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU", 3) == lowercase("THIS CODE WAS INVENTED BY JULIUS CAESAR")
|
28
test/solitaire.jl
Normal file
28
test/solitaire.jl
Normal file
@@ -0,0 +1,28 @@
|
||||
using ClassicalCiphers
|
||||
using Base.Test
|
||||
|
||||
@test encrypt_solitaire("aaaaaaaaaaaaaaa", "") == lowercase("EXKYIZSGEHUNTIQ")
|
||||
@test encrypt_solitaire("aaaaaaaaaaaaaaa", "f") == lowercase("XYIUQBMHKKJBEGY")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "fo") == lowercase("TUJYMBERLGXNDIW")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "foo") == lowercase("ITHZUJIWGRFARMW")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "a") == lowercase("XODALGSCULIQNSC")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "aa") == lowercase("OHGWMXXCAIMCIQP")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "aaa") == lowercase("DCSQYHBQZNGDRUT")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "b") == lowercase("XQEEMOITLZVDSQS")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "bc") == lowercase("QNGRKQIHCLGWSCE")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAA", "bcd") == lowercase("FMUBYBMAXHNQXCJ")
|
||||
@test encrypt_solitaire("AAAAAAAAAAAAAAAAAAAAAAAAA", "cryptonomicon") == lowercase("SUGSRSXSWQRMXOHIPBFPXARYQ")
|
||||
@test encrypt_solitaire("SOLITAIREX", "cryptonomicon") == lowercase("KIRAKSFJAN")
|
||||
|
||||
@test decrypt_solitaire("EXKYI ZSGEH UNTIQ", "") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("XYIUQ BMHKK JBEGY ", "f") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("TUJYM BERLG XNDIW", "fo") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("ITHZU JIWGR FARMW ", "foo") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("XODAL GSCUL IQNSC ", "a") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("OHGWM XXCAI MCIQP ", "aa") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("DCSQY HBQZN GDRUT ", "aaa") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("XQEEM OITLZ VDSQS ", "b") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("QNGRK QIHCL GWSCE ", "bc") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("FMUBY BMAXH NQXCJ", "bcd") == "aaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("SUGSR SXSWQ RMXOH IPBFP XARYQ", "cryptonomicon") == "aaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
@test decrypt_solitaire("KIRAK SFJAN", "cryptonomicon") == "solitairex"
|
17
test/vigenere.jl
Normal file
17
test/vigenere.jl
Normal file
@@ -0,0 +1,17 @@
|
||||
using ClassicalCiphers
|
||||
using Base.Test
|
||||
|
||||
# doc examples
|
||||
@test encrypt_vigenere("ab", [0, 1]) == "ac"
|
||||
@test decrypt_vigenere("ac", [0, 1]) == "ab"
|
||||
@test encrypt_vigenere("ab", "ab") == "ac"
|
||||
@test decrypt_vigenere("ac", "ab") == "ab"
|
||||
|
||||
# others
|
||||
|
||||
@test decrypt_vigenere("DYIMXMESTEZDPNFVVAMJ", [11, 18, 5, 13, 12, 9, 14]-1) == lowercase("THEAMERICANSHAVEROBB")
|
||||
|
||||
@test decrypt_vigenere("DYIMXMESTEZDPNFVVAMJ", "kremlin") == lowercase("THEAMERICANSHAVEROBB")
|
||||
|
||||
@test encrypt_vigenere("THEAMERICANSHAVEROBB", "kremlin") ==
|
||||
lowercase("DYIMXMESTEZDPNFVVAMJ")
|
Reference in New Issue
Block a user