Merge pull request #29 from Smaug123/bug-monoalphabetic

Type-error bug fix for monoalphabetic (fixes #28)
This commit is contained in:
Jake Ireland
2021-03-10 11:42:28 +13:00
committed by GitHub
4 changed files with 62 additions and 61 deletions

View File

@@ -24,13 +24,6 @@ jobs:
- windows-latest
arch:
- x64
- x86
exclude:
# Test 32-bit only on Linux
- os: macOS-latest
arch: x86
- os: windows-latest
arch: x86
include:
# Add a 1.3 job because that's what Invenia actually uses
- os: ubuntu-latest

View File

@@ -1,6 +1,6 @@
name = "ClassicalCiphers"
uuid = "ecf26e93-935c-5e64-9b21-bc8ac81b4723"
version = "2.1.0"
version = "2.1.1"
[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

View File

@@ -2,6 +2,7 @@ _keystr_to_dict(keystr::AbstractString) =
Dict{Char, Char}(Char(i + 64) => c for (i, c) in enumerate(uppercase(keystr)))
_keystr_to_dict(A::AbstractString, B::AbstractString) =
Dict{eltype(A), eltype(B)}(a => b for (a, b) in zip(uppercase(A), uppercase(B))) # policy decision: all dictionaries are uppercase
Base.reverse(D::Dict{T, S}) where {T, S} =
Dict{S, T}(reverse(p) for p in D)
_monoalphabetic_substitution(text, sub_dict::Dict{T, S}) where {T, S} =
@@ -125,7 +126,7 @@ function swap_two(str)
indices = rand(1:length(str), 2)
end
return join(Integer[i == indices[1] ? str[indices[2]] : (i == indices[2] ? str[indices[1]] : str[i]) for i in 1:length(str)])
return join(Char[i == indices[1] ? str[indices[2]] : (i == indices[2] ? str[indices[1]] : str[i]) for i in 1:length(str)])
end
"""
@@ -171,10 +172,10 @@ julia> crack_monoalphabetic(str, chatty=0, rounds=10)
"""
function crack_monoalphabetic(
ciphertext;
starting_key::AbstractString = "",
min_temp::AbstractFloat = 0.0001,
temp_factor::AbstractFloat = 0.97,
acceptance_prob::AbstractFloat = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
starting_key::AbstractString = string(),
min_temp::Union{AbstractFloat, Integer} = 0.0001,
temp_factor::Union{AbstractFloat, Integer} = 0.97,
acceptance_prob::Function = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
chatty::Integer = 0,
rounds::Integer = 1
)
@@ -198,64 +199,64 @@ function crack_monoalphabetic(
key = join(start_key)
else
key = starting_key
end
end
if chatty > 1
println("Starting key: $(key)")
end
if chatty > 1
println("Starting key: $(key)")
end
stripped_ciphertext = letters_only(ciphertext)
fitness = string_fitness(decrypt_monoalphabetic(stripped_ciphertext, key))
total_best_fitness, total_best_key = fitness, key
total_best_decrypt = decrypt_monoalphabetic(ciphertext, key)
stripped_ciphertext = letters_only(ciphertext)
fitness = string_fitness(decrypt_monoalphabetic(stripped_ciphertext, key))
total_best_fitness, total_best_key = fitness, key
total_best_decrypt = decrypt_monoalphabetic(ciphertext, key)
for roundcount in 1:rounds
temp = 10^((roundcount - 1) / rounds)
while temp > min_temp
for i in 1:round(Int, min(ceil(1 / temp), 10))
neighbour = swap_two(key)
new_fitness = string_fitness(decrypt_monoalphabetic(stripped_ciphertext, neighbour), alreadystripped = true)
if new_fitness > total_best_fitness
total_best_fitness = new_fitness
total_best_key = neighbour
total_best_decrypt = decrypt_monoalphabetic(ciphertext, total_best_key)
end
threshold = rand()
if chatty >= 2
println("Current fitness: $(fitness)")
println("New fitness: $(new_fitness)")
println("Acceptance probability: $(acceptance_prob(fitness, new_fitness, temp))")
println("Threshold: $(threshold)")
end
if acceptance_prob(fitness, new_fitness, temp) >= threshold
if chatty >= 1
println("$(key) -> $(neighbour), threshold $(threshold), temperature $(temp), fitness $(new_fitness), prob $(acceptance_prob(fitness, new_fitness, temp))")
end
fitness = new_fitness
key = neighbour
end
for roundcount in 1:rounds
temp = 10^((roundcount - 1) / rounds)
while temp > min_temp
for i in 1:round(Int, min(ceil(1 / temp), 10))
neighbour = swap_two(key)
new_fitness = string_fitness(decrypt_monoalphabetic(stripped_ciphertext, neighbour), alreadystripped = true)
if new_fitness > total_best_fitness
total_best_fitness = new_fitness
total_best_key = neighbour
total_best_decrypt = decrypt_monoalphabetic(ciphertext, total_best_key)
end
temp = temp * temp_factor
threshold = rand()
if chatty >= 2
println("----")
println("Current fitness: $(fitness)")
println("New fitness: $(new_fitness)")
println("Acceptance probability: $(acceptance_prob(fitness, new_fitness, temp))")
println("Threshold: $(threshold)")
end
if acceptance_prob(fitness, new_fitness, temp) >= threshold
if chatty >= 1
println("$(key) -> $(neighbour), threshold $(threshold), temperature $(temp), fitness $(new_fitness), prob $(acceptance_prob(fitness, new_fitness, temp))")
end
fitness = new_fitness
key = neighbour
end
end
key, fitness = total_best_key, total_best_fitness
temp = 1
temp = temp * temp_factor
if chatty >= 2
println("----")
end
end
if chatty >= 1
println("Best was $(total_best_key) at $(total_best_fitness)")
println(total_best_decrypt)
end
return decrypt_monoalphabetic(ciphertext, key), key
key, fitness = total_best_key, total_best_fitness
temp = 1
end
if chatty >= 1
println("Best was $(total_best_key) at $(total_best_fitness)")
println(total_best_decrypt)
end
return decrypt_monoalphabetic(ciphertext, key), key
end
"""

View File

@@ -31,3 +31,10 @@
@test decrypt_monoalphabetic("PCSSI EPHL HL JSKHYECUE", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = false) == "jvllh cjps ps qlrpfcvgc"
@test decrypt_monoalphabetic("PCSSI EPHL HL JSKHYECUE", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = true) == "hello this is plaintext"
@test decrypt_monoalphabetic("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = false) != decrypt_monoalphabetic("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = true)
@test (
RID = encrypt_monoalphabetic("hello there", "mnbvcxzlkjhgfdsapoiuytrewq");
crack_monoalphabetic(RID) == ("gessi ngere", "INZYCDLPSFVKAUMJHOGRBTEQXW");
crack_monoalphabetic("93ee90299") == ("93ee90299", "XQJFEPHRBLTNKDCWVGAZMSIYOU");
crack_monoalphabetic("93ee90299"; chatty = 2) == ("93ee90299", "XQJFEPHRBLTNKDCWVGAZMSIYOU"); # for code coverage reasons!
)