113 lines
3.8 KiB
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 });
|
|
}
|
|
}
|
|
}
|