RosettaCodeData/Task/Bifid-cipher/Zig/bifid-cipher.zig

165 lines
4.8 KiB
Zig

const std = @import("std");
const Point = struct {
row: i32,
col: i32,
};
const Bifid = struct {
grid: [][]u8,
coordinates: std.AutoHashMap(u8, Point),
n: i32,
allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator, n: i32, text: []const u8) !*Bifid {
if (text.len != @as(usize, @intCast(n * n))) {
return error.IncorrectLength;
}
var self = try allocator.create(Bifid);
self.* = .{
.grid = try allocator.alloc([]u8, @intCast(n)),
.coordinates = std.AutoHashMap(u8, Point).init(allocator),
.n = n,
.allocator = allocator,
};
for (0..@intCast(n)) |i| {
self.grid[i] = try allocator.alloc(u8, @intCast(n));
}
var row: i32 = 0;
var col: i32 = 0;
for (text) |ch| {
self.grid[@intCast(row)][@intCast(col)] = ch;
try self.coordinates.put(ch, .{ .row = row, .col = col });
col += 1;
if (col == n) {
col = 0;
row += 1;
}
}
if (n == 5) {
// Handle I/J as the same letter in traditional 5x5 grid
if (self.coordinates.get('I')) |i_coords| {
try self.coordinates.put('J', i_coords);
}
}
return self;
}
pub fn deinit(self: *Bifid) void {
for (self.grid) |row| {
self.allocator.free(row);
}
self.allocator.free(self.grid);
self.coordinates.deinit();
self.allocator.destroy(self);
}
pub fn encrypt(self: *const Bifid, text: []const u8, allocator: std.mem.Allocator) ![]u8 {
var row_one = std.ArrayList(i32).init(allocator);
defer row_one.deinit();
var row_two = std.ArrayList(i32).init(allocator);
defer row_two.deinit();
for (text) |ch| {
if (self.coordinates.get(ch)) |coordinate| {
try row_one.append(coordinate.row);
try row_two.append(coordinate.col);
}
}
// Extend row_one with all elements from row_two
for (row_two.items) |col| {
try row_one.append(col);
}
var result = std.ArrayList(u8).init(allocator);
var i: usize = 0;
while (i < row_one.items.len - 1) : (i += 2) {
const r = @as(usize, @intCast(row_one.items[i]));
const c = @as(usize, @intCast(row_one.items[i + 1]));
try result.append(self.grid[r][c]);
}
return result.toOwnedSlice();
}
pub fn decrypt(self: *const Bifid, text: []const u8, allocator: std.mem.Allocator) ![]u8 {
var row = std.ArrayList(i32).init(allocator);
defer row.deinit();
for (text) |ch| {
if (self.coordinates.get(ch)) |coordinate| {
try row.append(coordinate.row);
try row.append(coordinate.col);
}
}
const middle = row.items.len / 2;
var result = std.ArrayList(u8).init(allocator);
for (0..middle) |i| {
const r = @as(usize, @intCast(row.items[i]));
const c = @as(usize, @intCast(row.items[i + middle]));
try result.append(self.grid[r][c]);
}
return result.toOwnedSlice();
}
pub fn display(self: *const Bifid) void {
for (self.grid) |row| {
for (row) |ch| {
std.debug.print("{c} ", .{ch});
}
std.debug.print("\n", .{});
}
}
};
fn runTest(bifid: *const Bifid, message: []const u8, allocator: std.mem.Allocator) !void {
std.debug.print("Using Polybius square:\n", .{});
bifid.display();
std.debug.print("Message: {s}\n", .{message});
const encrypted = try bifid.encrypt(message, allocator);
defer allocator.free(encrypted);
std.debug.print("Encrypted: {s}\n", .{encrypted});
const decrypted = try bifid.decrypt(encrypted, allocator);
defer allocator.free(decrypted);
std.debug.print("Decrypted: {s}\n\n", .{decrypted});
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const message1 = "ATTACKATDAWN";
const message2 = "FLEEATONCE";
const message3 = "THEINVASIONWILLSTARTONTHEFIRSTOFJANUARY";
const bifid1 = try Bifid.init(allocator, 5, "ABCDEFGHIKLMNOPQRSTUVWXYZ");
defer bifid1.deinit();
const bifid2 = try Bifid.init(allocator, 5, "BGWKZQPNDSIOAXEFCLUMTHYVR");
defer bifid2.deinit();
try runTest(bifid1, message1, allocator);
try runTest(bifid2, message2, allocator);
try runTest(bifid2, message1, allocator);
try runTest(bifid1, message2, allocator);
const bifid3 = try Bifid.init(allocator, 6, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
defer bifid3.deinit();
try runTest(bifid3, message3, allocator);
}