RosettaCodeData/Task/Bifid-cipher/Crystal/bifid-cipher.cr
2026-04-30 12:34:36 -04:00

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