From f26f6086ca85adddf1a92a88c55394ce7f6f77ff Mon Sep 17 00:00:00 2001 From: adrien Date: Thu, 23 Apr 2026 00:14:57 +0200 Subject: [PATCH] Added Imperial unit scales --- src/Scalar.zig | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Scales.zig | 39 ++++++++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/Scalar.zig b/src/Scalar.zig index 12a10f7..ecc35f6 100644 --- a/src/Scalar.zig +++ b/src/Scalar.zig @@ -749,6 +749,61 @@ test "add/sub bare number on dimensionless scalar" { try std.testing.expectEqual(7, c.value); } +test "Imperial length scales" { + const Foot = Scalar(f64, .{ .L = 1 }, .{ .L = .ft }); + const Meter = Scalar(f64, .{ .L = 1 }, .{}); + const Inch = Scalar(f64, .{ .L = 1 }, .{ .L = .inch }); + const CentiMeter = Scalar(f64, .{ .L = 1 }, .{ .L = .c }); + const Mile = Scalar(f64, .{ .L = 1 }, .{ .L = .mi }); + const KiloMeter = Scalar(f64, .{ .L = 1 }, .{ .L = .k }); + const Yard = Scalar(f64, .{ .L = 1 }, .{ .L = .yd }); + + // 1 ft → 0.3048 m + const one_ft = Foot{ .value = 1.0 }; + try std.testing.expectApproxEqAbs(0.3048, one_ft.to(Meter).value, 1e-9); + + // 12 in → 1 ft + const twelve_in = Inch{ .value = 12.0 }; + try std.testing.expectApproxEqAbs(1.0, twelve_in.to(Foot).value, 1e-9); + + // 1 in → 2.54 cm + const one_in = Inch{ .value = 1.0 }; + try std.testing.expectApproxEqAbs(2.54, one_in.to(CentiMeter).value, 1e-9); + + // 1 mi → 1.609344 km + const one_mi = Mile{ .value = 1.0 }; + try std.testing.expectApproxEqAbs(1.609344, one_mi.to(KiloMeter).value, 1e-9); + + // 3 ft → 1 yd + const three_ft = Foot{ .value = 3.0 }; + try std.testing.expectApproxEqAbs(1.0, three_ft.to(Yard).value, 1e-9); +} + +test "Imperial mass scales" { + const Pound = Scalar(f64, .{ .M = 1 }, .{ .M = .lb }); + const KiloGram = Scalar(f64, .{ .M = 1 }, .{ .M = .k }); + const Ounce = Scalar(f64, .{ .M = 1 }, .{ .M = .oz }); + const Stone = Scalar(f64, .{ .M = 1 }, .{ .M = .st }); + + // 1 lb → ~0.453592 kg + const one_lb = Pound{ .value = 1.0 }; + try std.testing.expectApproxEqAbs(0.45359237, one_lb.to(KiloGram).value, 1e-6); + + // 16 oz → 1 lb + const sixteen_oz = Ounce{ .value = 16.0 }; + try std.testing.expectApproxEqAbs(1.0, sixteen_oz.to(Pound).value, 1e-6); + + // 1 stone → 14 lb + const one_st = Stone{ .value = 1.0 }; + try std.testing.expectApproxEqAbs(14.0, one_st.to(Pound).value, 1e-4); + + // 2 lb + 8 oz → 2.5 lb + const two_lb = Pound{ .value = 2.0 }; + const eight_oz = Ounce{ .value = 8.0 }; + const total = two_lb.add(eight_oz).to(Pound); + try std.testing.expectApproxEqAbs(2.5, total.value, 1e-6); +} + test "comparisons with comptime_int on dimensionless scalar" { const DimLess = Scalar(i128, .{}, .{}); const x = DimLess{ .value = 42 }; diff --git a/src/Scales.zig b/src/Scales.zig index 0732f93..246565f 100644 --- a/src/Scales.zig +++ b/src/Scales.zig @@ -41,6 +41,18 @@ pub const UnitScale = enum(isize) { hour = 3_600, year = 31_536_000, + // Imperial Length (Literal factors in meters) + // 1 inch = 0.0254 meters. Since enum backing is isize, + // we use a unique tag and handle the float in getFactor. + inch = -1001, + ft = -1002, + yd = -1003, + mi = -1004, + + oz = -1005, // 1 oz = 28.3495231 g + lb = -1006, // 1 lb = 453.59237 g (= 16 oz) + st = -1007, // 1 stone = 6350.29318 g (= 14 lb) + // Undefined _, @@ -48,24 +60,31 @@ pub const UnitScale = enum(isize) { var buf: [16]u8 = undefined; return switch (self) { inline .none => "", - inline .P, .T, .G, .M, .k, .h, .da, .d, .c, .m, .u, .n, .p, .f, .min, .hour, .year => @tagName(self), + inline .P, .T, .G, .M, .k, .h, .da, .d, .c, .m, .u, .n, .p, .f, .min, .hour, .year, .inch, .ft, .yd, .mi, .oz, .lb, .st => @tagName(self), else => std.fmt.bufPrint(&buf, "[{d}]", .{@intFromEnum(self)}) catch "[]", // This cannot be inline because of non exhaustive enum, but that's ok, it is just str, not calculation }; } - /// Helper to get the actual scaling factor pub inline fn getFactor(self: @This()) comptime_float { return comptime switch (self) { + // Standard SI Exponents inline .P, .T, .G, .M, .k, .h, .da, .none, .d, .c, .m, .u, .n, .p, .f => std.math.pow(f64, 10.0, @floatFromInt(@intFromEnum(self))), - inline else => @floatFromInt(@intFromEnum(self)), - }; - } - /// Helper to get the actual scaling factor in i32 - pub fn getFactorInt(self: @This()) comptime_int { - return comptime switch (self) { - inline .P, .T, .G, .M, .k, .h, .da, .none, .d, .c, .m, .u, .n, .p, .f => std.math.powi(i32, 10.0, @intFromEnum(self)) catch 0, - inline else => @intFromEnum(self), + // Time Factors + inline .min, .hour, .year => @floatFromInt(@intFromEnum(self)), + + // Imperial Length (metres) + inline .inch => 0.0254, + inline .ft => 0.3048, + inline .yd => 0.9144, + inline .mi => 1609.344, + + // Imperial Mass (grams — base unit for M is gram, i.e. .none = 1 g) + inline .oz => 28.3495231, + inline .lb => 453.59237, + inline .st => 6350.29318, + + inline else => @floatFromInt(@intFromEnum(self)), }; } };