RosettaCodeData/Task/Verhoeff-algorithm/Zig/verhoeff-algorithm.zig

113 lines
3.8 KiB
Zig

const std = @import("std");
const print = std.debug.print;
const MULTIPLICATION_TABLE: [10][10]i32 = [_][10]i32{
[_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
[_]i32{ 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 },
[_]i32{ 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 },
[_]i32{ 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 },
[_]i32{ 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 },
[_]i32{ 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 },
[_]i32{ 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 },
[_]i32{ 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 },
[_]i32{ 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 },
[_]i32{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
};
const INVERSE: [10]i32 = [_]i32{ 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 };
const PERMUTATION_TABLE: [8][10]i32 = [_][10]i32{
[_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
[_]i32{ 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 },
[_]i32{ 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 },
[_]i32{ 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 },
[_]i32{ 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 },
[_]i32{ 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 },
[_]i32{ 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 },
[_]i32{ 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 },
};
fn verhoeffChecksum(allocator: std.mem.Allocator, number: []const u8, do_validation: bool, do_display: bool) !i32 {
if (do_display) {
const calculation_type = if (do_validation) "Validation" else "Check digit";
print("{s} calculations for {s}\n\n", .{ calculation_type, number });
print(" i ni p[i, ni] c\n", .{});
print("-------------------\n", .{});
}
var working_number = std.ArrayList(u8).init(allocator);
defer working_number.deinit();
try working_number.appendSlice(number);
if (!do_validation) {
try working_number.append('0');
}
var c: i32 = 0;
const le = working_number.items.len - 1;
var i: usize = le + 1;
while (i > 0) {
i -= 1;
const ni = working_number.items[i] - '0';
const pos = (le - i) % 8;
const pi = PERMUTATION_TABLE[pos][ni];
c = MULTIPLICATION_TABLE[@intCast(c)][@as(usize, @intCast(pi))];
if (do_display) {
print("{:2} {:2} {:2} {:2}\n\n", .{ le - i, ni, pi, c });
}
}
if (do_display and !do_validation) {
print("inverse[{}] = {}\n\n", .{ c, INVERSE[@intCast(c)] });
}
if (do_validation) {
return if (c == 0) 1 else 0;
} else {
return INVERSE[@intCast(c)];
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const Test = struct {
test_num: []const u8,
display: bool,
};
const tests = [_]Test{
.{ .test_num = "123", .display = true },
.{ .test_num = "12345", .display = true },
.{ .test_num = "123456789012", .display = false },
};
for (tests) |my_test| {
const digit = try verhoeffChecksum(allocator, my_test.test_num, false, my_test.display);
print("The check digit for {s} is {}\n\n", .{ my_test.test_num, digit });
// Create test numbers with check digit and with '9'
var correct_number = std.ArrayList(u8).init(allocator);
defer correct_number.deinit();
try correct_number.appendSlice(my_test.test_num);
try correct_number.append('0' + @as(u8, @intCast(digit)));
var incorrect_number = std.ArrayList(u8).init(allocator);
defer incorrect_number.deinit();
try incorrect_number.appendSlice(my_test.test_num);
try incorrect_number.append('9');
const numbers = [_][]const u8{ correct_number.items, incorrect_number.items };
for (numbers) |number| {
const validation_result = try verhoeffChecksum(allocator, number, true, my_test.display);
const result = if (validation_result == 1) "correct" else "incorrect";
print("The validation for \"{s}\" is \"{s}\". \n", .{ number, result });
}
}
}