Added Rail Fence Cipher implementation (addresses #23)

This is only my attempt at an implementation.  @r0cketr1kky can still have another attempt, or refine my solution, but it is here now for people to use.

TODO: add better docstrings.
This commit is contained in:
Jake W. Ireland
2021-01-08 04:48:00 +13:00
parent 09f71792f3
commit 9cb4f1306c
4 changed files with 63 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ include("affine.jl")
include("hill.jl")
include("playfair.jl")
include("enigma.jl")
include("railfence.jl")
export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic,
encrypt_caesar, decrypt_caesar, crack_caesar,
@@ -22,6 +23,7 @@ export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic,
encrypt_hill, decrypt_hill,
encrypt_playfair, decrypt_playfair,
encrypt_enigma, decrypt_enigma,
string_fitness, index_of_coincidence
string_fitness, index_of_coincidence,
construct_railfence, encrypt_railfence, decrypt_railfence
end # module

49
src/railfence.jl Normal file
View File

@@ -0,0 +1,49 @@
function construct_railfence(input, fence::AbstractArray, n_rails::Integer)
rails = vcat(1:n_rails, (n_rails - 1):-1:2)
for (i, letter) in enumerate(input)
fence[rails[mod1(i, length(rails))], i] = letter
end
return fence
end
"""
```julia
construct_railfence(input::AbstractString, n_rails::Integer)
construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T <: Number}
```
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
"""
function construct_railfence(input::AbstractString, n_rails::Integer)
input = uppercase(replace(input, " " => ""))
# the square is my `UndefInitializer()` for `Char`.
return construct_railfence(input, fill('□', n_rails, length(input)), n_rails)
end
function construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T <: Number}
return construct_railfence(input, zeros(T, n_rails, length(input)), n_rails)
end
"""
```julia
encrypt_railfence(input::AbstractString, n_rails::Integer)
```
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
"""
function encrypt_railfence(input::AbstractString, n_rails::Integer)
return join(Char[c for rail in eachrow(construct_railfence(input, n_rails)) for c in rail if c != '□'])
end
"""
```julia
encrypt_railfence(input::AbstractString, n_rails::Integer)
```
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
"""
function decrypt_railfence(input::AbstractString, n_rails::Integer)
char_positions = Int[n for row in eachrow(construct_railfence(1:length(input), n_rails)) for n in row if n != 0]
return lowercase(join(Char[input[findfirst(==(i), char_positions)] for i in 1:length(input)]))
end

10
test/railfence.jl Normal file
View File

@@ -0,0 +1,10 @@
# using ClassicalCiphers
# using Test
@test encrypt_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3) == "WECRFACERDSOEE.LETNEAIVDEO"
@test decrypt_railfence("WECRFACERDSOEE.LETNEAIVDEO", 3) == "wearediscovered.fleeatonce"
@test encrypt_railfence("Julia is strong", 10) == "JULIAISGSNTOR"
@test decrypt_railfence("JULIAISGSNTOR", 10) == "juliaisstrong"
@test encrypt_railfence("This is not a very strong cipher, but indeed it is classical", 2) == "TIINTVRSRNCPE,UIDEIICASCLHSSOAEYTOGIHRBTNEDTSLSIA"
@test decrypt_railfence("TIINTVRSRNCPE,UIDEIICASCLHSSOAEYTOGIHRBTNEDTSLSIA", 2) == "thisisnotaverystrongcipher,butindeeditisclassical"
@test construct_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3) == ['W' '□' '□' '□' 'E' '□' '□' '□' 'C' '□' '□' '□' 'R' '□' '□' '□' 'F' '□' '□' '□' 'A' '□' '□' '□' 'C' '□'; '□' 'E' '□' 'R' '□' 'D' '□' 'S' '□' 'O' '□' 'E' '□' 'E' '□' '.' '□' 'L' '□' 'E' '□' 'T' '□' 'N' '□' 'E'; '□' '□' 'A' '□' '□' '□' 'I' '□' '□' '□' 'V' '□' '□' '□' 'D' '□' '□' '□' 'E' '□' '□' '□' 'O' '□' '□' '□']

View File

@@ -11,6 +11,7 @@ tests = [
"enigma",
"hill",
"solitaire",
"railfence"
]
println("Running tests:")