const std = @import("std"); const assert = std.debug.assert; const expect = std.testing.expect; /// Similiar to std.meta.eql but pointers are followed. /// Also Unions and ErrorUnions are excluded. pub fn eq(a: anytype, b: @TypeOf(a)) bool { const T = @TypeOf(a); switch (@typeInfo(T)) { .Int, .ComptimeInt, .Float, .ComptimeFloat => { return a == b; }, .Struct => { if (!@hasDecl(T, "eq")) { @compileError("An 'eq' comparison method has to implemented for Type '" ++ @typeName(T) ++ "'"); } return T.eq(a, b); }, .Array => { for (a) |_, i| if (!eq(a[i], b[i])) return false; return true; }, .Vector => |info| { var i: usize = 0; while (i < info.len) : (i += 1) { if (!eq(a[i], b[i])) return false; } return true; }, .Pointer => |info| { switch (info.size) { .One => return eq(a.*, b.*), .Slice => { if (a.len != b.len) return false; for (a) |_, i| if (!eq(a[i], b[i])) return false; return true; }, .Many => { if (info.sentinel) { if (std.mem.len(a) != std.mem.len(b)) return false; var i: usize = 0; while (i < std.mem.len(a)) : (i += 1) if (!eq(a[i], b[i])) return false; return true; } @compileError("Cannot compare many-item pointer to unknown number of items without sentinel value"); }, .C => @compileError("Cannot compare C pointers"), } }, .Optional => { if (a == null and b == null) return true; if (a == null or b == null) return false; return eq(a.?, b.?); }, else => { @compileError("Cannot compare type '" ++ @typeName(T) ++ "'"); }, } } pub fn lt(a: anytype, b: @TypeOf(a)) bool { const T = @TypeOf(a); switch (@typeInfo(T)) { .Int, .ComptimeInt, .Float, .ComptimeFloat => { return a < b; }, .Struct => { if (!@hasDecl(T, "lt")) { @compileError("A 'lt' comparison method has to implemented for Type '" ++ @typeName(T) ++ "'"); } return T.lt(a, b); }, .Array => { for (a) |_, i| { if (lt(a[i], b[i])) { return true; } else if (eq(a[i], b[i])) { continue; } else { return false; } } return false; }, .Vector => |info| { var i: usize = 0; while (i < info.len) : (i += 1) { if (lt(a[i], b[i])) { return true; } else if (eq(a[i], b[i])) { continue; } else { return false; } } return false; }, .Pointer => |info| { switch (info.size) { .One => return lt(a.*, b.*), .Slice => { const n = std.math.min(a.len, b.len); for (a[0..n]) |_, i| { if (lt(a[i], b[i])) { return true; } else if (eq(a[i], b[i])) { continue; } else { return false; } } return lt(a.len, b.len); }, .Many => { if (info.sentinel) { const n = std.math.min(std.mem.len(a), std.mem.len(b)); var i: usize = 0; while (i < n) : (i += 1) { if (lt(a[i], b[i])) { return true; } else if (eq(a[i], b[i])) { continue; } else { return false; } } return lt(std.mem.len(a), std.mem.len(b)); } @compileError("Cannot compare many-item pointer to unknown number of items without sentinel value"); }, .C => @compileError("Cannot compare C pointers"), } }, .Optional => { if (a == null or b == null) return false; return lt(a.?, b.?); }, else => { @compileError("Cannot compare type '" ++ @typeName(T) ++ "'"); }, } } pub fn le(a: anytype, b: @TypeOf(a)) bool { return lt(a, b) or eq(a, b); } pub fn gt(a: anytype, b: @TypeOf(a)) bool { return !lt(a, b) and !eq(a, b); } pub fn ge(a: anytype, b: @TypeOf(a)) bool { return !lt(a, b); } test "numerals" { try expect(eq(1.0, 1.0)); try expect(!eq(1.0, 1.1)); try expect(lt(1.0, 2.0)); try expect(!lt(1, 1)); try expect(!lt(2, 1)); } test "Arrays" { try expect(eq("abc", "abc")); try expect(!eq("abc", "abb")); try expect(lt("ab", "ba")); try expect(lt("aaa", "aab")); try expect(!lt("aaa", "aaa")); try expect(!lt("aab", "aaa")); } test "structs" { const Car = struct { power: i32, pub fn lt(a: @This(), b: @This()) bool { return a.power < b.power; } pub fn eq(a: @This(), b: @This()) bool { return a.power == b.power; } }; var car1 = Car{ .power = 100 }; var car2 = Car{ .power = 200 }; try expect(eq(car1, car1)); try expect(!eq(car1, car2)); try expect(lt(car1, car2)); try expect(!lt(car1, car1)); } test "Slices" { var o: usize = 0; assert(@TypeOf("abc"[o..]) == [:0]const u8); try expect(eq("abc"[o..], "abc")); try expect(!eq("abc"[o..], "abb")); try expect(lt("aba"[o..], "ba")); try expect(lt("aaa"[o..], "bb")); try expect(!lt("aba"[o..], "aa")); try expect(!lt("aaa"[o..], "aaa")); try expect(lt("aaa"[o..], "aaaa")); try expect(!lt("aaa"[o..], "aa")); try expect(lt("aab"[o..], "aaba")); } test "Optionals" { var x: ?i32 = 1; var y: ?i32 = 2; try expect(lt(x, y)); } test "sentinel terminated pointers" { // TODO } test "Vectors" { // TODO }