46 lines
1.4 KiB
Crystal
46 lines
1.4 KiB
Crystal
enum BifidOp
|
|
Encode
|
|
Decode
|
|
end
|
|
|
|
def bifid_encode (message, alphabet, op : BifidOp = :encode)
|
|
square = alphabet.chars
|
|
side = Math.isqrt(square.size)
|
|
raise "alphabet not a square" unless side*side == square.size
|
|
chars = message.chars
|
|
raise "incomplete alphabet" unless chars.all?(&.in? square)
|
|
|
|
coords = chars.flat_map {|ch| [*square.index!(ch).divmod(side)] }
|
|
|
|
coords = if op.encode?
|
|
(0...coords.size).step(2).map {|i| coords[i] }.to_a +
|
|
(1...coords.size).step(2).map {|i| coords[i] }.to_a
|
|
else
|
|
half = coords.size//2
|
|
coords[...half].zip(coords[half..]).map {|c1, c2|
|
|
[c1, c2]
|
|
}.flatten
|
|
end
|
|
coords.each_slice(2).map {|(row, col)|
|
|
square[row * side + col]
|
|
}.join
|
|
end
|
|
|
|
poly1 = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
|
|
poly2 = "BGWKZQPNDSIOAXEFCLUMTHYVR"
|
|
superpoly = (' '..'~').join + "¡éßñø"
|
|
|
|
attack = "ATTACKATDAWN"
|
|
flee = "FLEEATONCE"
|
|
invasion = "The invasion will start on the first of January".upcase.tr("J", "I").delete("^A-Z")
|
|
ataque = "¡Attack on 1/1 at 23:59, señor!"
|
|
|
|
[{attack, poly1}, {flee, poly2}, {attack, poly2},
|
|
{invasion, poly1}, {ataque, superpoly}].each do |message, alphabet|
|
|
encoded = bifid_encode(message, alphabet)
|
|
decoded = bifid_encode(encoded, alphabet, :decode)
|
|
puts "Message: #{message} #{alphabet.size > 80 ? "\n" : " "}- Alphabet: #{alphabet}"
|
|
puts "Encoded: #{encoded}"
|
|
puts "Decoded: #{decoded}"
|
|
puts
|
|
end
|