From ec05b60fc3580fb13b517fb2f16b7307c59881c1 Mon Sep 17 00:00:00 2001 From: adrien Date: Wed, 22 Apr 2026 10:31:04 +0200 Subject: [PATCH] Replaced Scales.min to helper.finerScales --- src/Base.zig | 4 ++-- src/Dimensions.zig | 2 -- src/Scalar.zig | 44 +++++++++++++++++++++++++--------- src/Scales.zig | 18 ++++---------- src/Vector.zig | 60 ++++++++++++++++++++++++++++++---------------- src/helper.zig | 27 +++++++++++++++++++++ 6 files changed, 106 insertions(+), 49 deletions(-) diff --git a/src/Base.zig b/src/Base.zig index 8bf563c..9928819 100644 --- a/src/Base.zig +++ b/src/Base.zig @@ -134,13 +134,13 @@ test "BaseQuantities - Dynamics (Force and Work)" { // Force = mass * acceleration const f = m.mulBy(a); - try std.testing.expectEqual(98000, f.value); + try std.testing.expectEqual(98, f.value); try std.testing.expect(Force.dims.eql(@TypeOf(f).dims)); // Energy (Work) = Force * distance const distance = Meter.Of(f32){ .value = 5.0 }; const energy = f.mulBy(distance); - try std.testing.expectEqual(490000, energy.value); + try std.testing.expectEqual(490, energy.value); try std.testing.expect(Energy.dims.eql(@TypeOf(energy).dims)); } diff --git a/src/Dimensions.zig b/src/Dimensions.zig index 886f06c..d691a79 100644 --- a/src/Dimensions.zig +++ b/src/Dimensions.zig @@ -29,8 +29,6 @@ pub const Dimension = enum { } }; -// --------- Dimensions struct --------- - const Self = @This(); data: std.EnumArray(Dimension, comptime_int), diff --git a/src/Scalar.zig b/src/Scalar.zig index bf490a5..56b950d 100644 --- a/src/Scalar.zig +++ b/src/Scalar.zig @@ -7,6 +7,17 @@ const UnitScale = Scales.UnitScale; const Dimensions = @import("Dimensions.zig"); const Dimension = Dimensions.Dimension; +// TODO: Add those operation: +// - eq: Equal +// - ne: Not equal +// - gt: Greather than +// - gte: Greather than or equal +// - lt: Less than +// - lte Less than or equal +// - abs: Absolut value +// - pow: Scalar power another +// - log: Scalar log another + pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type { @setEvalBranchQuota(10_000_000); return struct { @@ -22,14 +33,14 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type pub inline fn add(self: Self, rhs: anytype) Scalar( T, dims, - scales.min(@TypeOf(rhs).scales), + hlp.finerScales(Self, @TypeOf(rhs)), ) { if (comptime !dims.eql(@TypeOf(rhs).dims)) @compileError("Dimension mismatch in add: " ++ dims.str() ++ " vs " ++ @TypeOf(rhs).dims.str()); if (comptime @TypeOf(rhs) == Self) return .{ .value = self.value + rhs.value }; - const TargetType = Scalar(T, dims, scales.min(@TypeOf(rhs).scales)); + const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; @@ -39,14 +50,14 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type pub inline fn sub(self: Self, rhs: anytype) Scalar( T, dims, - scales.min(@TypeOf(rhs).scales), + hlp.finerScales(Self, @TypeOf(rhs)), ) { if (comptime !dims.eql(@TypeOf(rhs).dims)) @compileError("Dimension mismatch in sub: " ++ dims.str() ++ " vs " ++ @TypeOf(rhs).dims.str()); if (comptime @TypeOf(rhs) == Self) return .{ .value = self.value - rhs.value }; - const TargetType = Scalar(T, dims, scales.min(@TypeOf(rhs).scales)); + const TargetType = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); const lhs_val = if (comptime @TypeOf(self) == TargetType) self.value else self.to(TargetType).value; const rhs_val = if (comptime @TypeOf(rhs) == TargetType) rhs.value else rhs.to(TargetType).value; @@ -56,11 +67,11 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type pub inline fn mulBy(self: Self, rhs: anytype) Scalar( T, dims.add(@TypeOf(rhs).dims), - scales.min(@TypeOf(rhs).scales), + hlp.finerScales(Self, @TypeOf(rhs)), ) { const RhsType = @TypeOf(rhs); - const SelfNorm = Scalar(T, dims, scales.min(RhsType.scales)); - const RhsNorm = Scalar(T, RhsType.dims, scales.min(RhsType.scales)); + const SelfNorm = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); + const RhsNorm = Scalar(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs))); if (comptime Self == SelfNorm and RhsType == RhsNorm) return .{ .value = self.value * rhs.value }; @@ -72,11 +83,11 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type pub inline fn divBy(self: Self, rhs: anytype) Scalar( T, dims.sub(@TypeOf(rhs).dims), - scales.min(@TypeOf(rhs).scales), + hlp.finerScales(Self, @TypeOf(rhs)), ) { const RhsType = @TypeOf(rhs); - const SelfNorm = Scalar(T, dims, scales.min(RhsType.scales)); - const RhsNorm = Scalar(T, RhsType.dims, scales.min(RhsType.scales)); + const SelfNorm = Scalar(T, dims, hlp.finerScales(Self, @TypeOf(rhs))); + const RhsNorm = Scalar(T, RhsType.dims, hlp.finerScales(Self, @TypeOf(rhs))); const lhs_val = if (comptime Self == SelfNorm) self.value else self.to(SelfNorm).value; const rhs_val = if (comptime RhsType == RhsNorm) rhs.value else rhs.to(RhsNorm).value; if (comptime @typeInfo(T) == .int) { @@ -104,7 +115,6 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type const div: DestT = comptime @intFromFloat(1.0 / ratio); const val = @as(DestT, @intCast(self.value)); const half = comptime div / 2; - // Native round-to-nearest const rounded = if (val >= 0) @divTrunc(val + half, div) else @divTrunc(val - half, div); return .{ .value = rounded }; } @@ -329,6 +339,18 @@ test "DivBy integer exact" { try std.testing.expectEqual(-1, @TypeOf(vel).dims.get(.T)); } +test "Finer scales skip dim 0" { + const Dimless = Scalar(i128, Dimensions.init(.{}), Scales.init(.{})); + const KiloMetre = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); + + const r = Dimless{ .value = 30 }; + const time = KiloMetre{ .value = 4 }; + const vel = r.mulBy(time); + + try std.testing.expectEqual(120, vel.value); + try std.testing.expectEqual(Scales.UnitScale.k, @TypeOf(vel).scales.get(.L)); +} + test "Conversion chain: km -> m -> cm" { const KiloMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k })); const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{})); diff --git a/src/Scales.zig b/src/Scales.zig index a491a11..3799ee3 100644 --- a/src/Scales.zig +++ b/src/Scales.zig @@ -81,25 +81,17 @@ pub fn set(comptime self: *Scales, comptime key: Dimension, comptime val: UnitSc comptime self.data.set(key, val); } -pub fn min(comptime s1: Scales, comptime s2: Scales) Scales { - comptime var out = Scales.initFill(.none); - inline for (std.enums.values(Dimension)) |dim| - out.set(dim, if (s1.get(dim).getFactorInt() > s2.get(dim).getFactorInt()) s2.get(dim) else s1.get(dim)); - - return out; -} - pub inline fn getFactor(comptime s: Scales, comptime d: Dimensions) comptime_float { comptime var factor: f64 = 1.0; inline for (std.enums.values(Dimension)) |dim| { - const power = d.get(dim); - if (power == 0) continue; + const power = comptime d.get(dim); + if (comptime power == 0) continue; - const base = s.get(dim).getFactor(); + const base = comptime s.get(dim).getFactor(); - var i: i32 = 0; + var i: comptime_int = 0; const abs_power = if (power < 0) -power else power; - while (i < abs_power) : (i += 1) { + inline while (i < abs_power) : (i += 1) { if (power > 0) factor *= base else diff --git a/src/Vector.zig b/src/Vector.zig index f64fbde..727f922 100644 --- a/src/Vector.zig +++ b/src/Vector.zig @@ -30,9 +30,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { return .{ .data = data }; } - pub inline fn add(self: Self, rhs: anytype) Vector(len, Scalar(T, d, s.min(@TypeOf(rhs).scales))) { + pub inline fn add(self: Self, rhs: anytype) Vector(len, Scalar( + T, + dims, + hlp.finerScales(Self, @TypeOf(rhs)), + )) { const Tr = @TypeOf(rhs); - var res: Vector(len, Scalar(T, d, s.min(Tr.scales))) = undefined; + var res: Vector(len, Scalar(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; inline for (self.data, 0..) |v, i| { const q = (Q{ .value = v }).add(Tr.ScalarType{ .value = rhs.data[i] }); res.data[i] = q.value; @@ -40,9 +44,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { return res; } - pub inline fn sub(self: Self, rhs: anytype) Vector(len, Scalar(T, d, s.min(@TypeOf(rhs).scales))) { + pub inline fn sub(self: Self, rhs: anytype) Vector(len, Scalar( + T, + dims, + hlp.finerScales(Self, @TypeOf(rhs)), + )) { const Tr = @TypeOf(rhs); - var res: Vector(len, Scalar(T, d, s.min(Tr.scales))) = undefined; + var res: Vector(len, Scalar(T, d, hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; inline for (self.data, 0..) |v, i| { const q = (Q{ .value = v }).sub(Tr.ScalarType{ .value = rhs.data[i] }); res.data[i] = q.value; @@ -53,9 +61,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { pub inline fn divBy( self: Self, rhs: anytype, - ) Vector(len, Scalar(T, d.sub(@TypeOf(rhs).dims), s.min(@TypeOf(rhs).scales))) { + ) Vector(len, Scalar( + T, + dims.sub(@TypeOf(rhs).dims), + hlp.finerScales(Self, @TypeOf(rhs)), + )) { const Tr = @TypeOf(rhs); - var res: Vector(len, Scalar(T, d.sub(Tr.dims), s.min(Tr.scales))) = undefined; + var res: Vector(len, Scalar(T, d.sub(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; inline for (self.data, 0..) |v, i| { const q = (Q{ .value = v }).divBy(Tr.ScalarType{ .value = rhs.data[i] }); res.data[i] = q.value; @@ -66,9 +78,13 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { pub inline fn mulBy( self: Self, rhs: anytype, - ) Vector(len, Scalar(T, d.add(@TypeOf(rhs).dims), s.min(@TypeOf(rhs).scales))) { + ) Vector(len, Scalar( + T, + dims.add(@TypeOf(rhs).dims), + hlp.finerScales(Self, @TypeOf(rhs)), + )) { const Tr = @TypeOf(rhs); - var res: Vector(len, Scalar(T, d.add(Tr.dims), s.min(Tr.scales))) = undefined; + var res: Vector(len, Scalar(T, d.add(Tr.dims), hlp.finerScales(Self, @TypeOf(rhs)))) = undefined; inline for (self.data, 0..) |v, i| { const q = (Q{ .value = v }).mulBy(Tr.ScalarType{ .value = rhs.data[i] }); res.data[i] = q.value; @@ -79,8 +95,12 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { pub inline fn divByScalar( self: Self, scalar: anytype, - ) Vector(len, Scalar(T, d.sub(@TypeOf(scalar).dims), s.min(@TypeOf(scalar).scales))) { - var res: Vector(len, Scalar(T, d.sub(@TypeOf(scalar).dims), s.min(@TypeOf(scalar).scales))) = undefined; + ) Vector(len, Scalar( + T, + dims.sub(@TypeOf(scalar).dims), + hlp.finerScales(Self, @TypeOf(scalar)), + )) { + var res: Vector(len, Scalar(T, d.sub(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined; inline for (self.data, 0..) |v, i| { const q = Q{ .value = v }; res.data[i] = q.divBy(scalar).value; @@ -91,8 +111,12 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { pub inline fn mulByScalar( self: Self, scalar: anytype, - ) Vector(len, Scalar(T, d.add(@TypeOf(scalar).dims), s.min(@TypeOf(scalar).scales))) { - var res: Vector(len, Scalar(T, d.add(@TypeOf(scalar).dims), s.min(@TypeOf(scalar).scales))) = undefined; + ) Vector(len, Scalar( + T, + dims.add(@TypeOf(scalar).dims), + hlp.finerScales(Self, @TypeOf(scalar)), + )) { + var res: Vector(len, Scalar(T, d.add(@TypeOf(scalar).dims), hlp.finerScales(Self, @TypeOf(scalar)))) = undefined; inline for (self.data, 0..) |v, i| { const q = Q{ .value = v }; res.data[i] = q.mulBy(scalar).value; @@ -102,25 +126,22 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { pub fn negate(self: Self) Self { var res: Self = undefined; - inline for (self.data, 0..) |v, i| { + inline for (self.data, 0..) |v, i| res.data[i] = -v; - } return res; } pub inline fn to(self: Self, comptime DestQ: type) Vector(len, DestQ) { var res: Vector(len, DestQ) = undefined; - inline for (self.data, 0..) |v, i| { + inline for (self.data, 0..) |v, i| res.data[i] = (Q{ .value = v }).to(DestQ).value; - } return res; } pub inline fn lengthSqr(self: Self) T { var sum: T = 0; - inline for (self.data) |v| { + inline for (self.data) |v| sum += v * v; - } return sum; } @@ -128,10 +149,7 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { const len_sq = self.lengthSqr(); if (comptime @typeInfo(T) == .int) { - // Construct the unsigned equivalent of T at comptime (e.g., i32 -> u32) const UnsignedT = @Int(.unsigned, @typeInfo(T).int.bits); - - // len_sq is always positive, so @intCast is perfectly safe const u_len_sq = @as(UnsignedT, @intCast(len_sq)); return @as(T, @intCast(std.math.sqrt(u_len_sq))); } else { diff --git a/src/helper.zig b/src/helper.zig index 7e6cb06..71a0372 100644 --- a/src/helper.zig +++ b/src/helper.zig @@ -30,3 +30,30 @@ pub fn printSuperscript(writer: *std.Io.Writer, n: i32) !void { try writer.writeAll(s); } } + +const Scales = @import("Scales.zig"); +const Dimensions = @import("Dimensions.zig"); +const Dimension = @import("Dimensions.zig").Dimension; + +pub fn finerScales(comptime T1: type, comptime T2: type) Scales { + const d1: Dimensions = T1.dims; + const d2: Dimensions = T2.dims; + const s1: Scales = T1.scales; + const s2: Scales = T2.scales; + comptime var out = Scales.initFill(.none); + inline for (std.enums.values(Dimension)) |dim| { + const scale1 = comptime s1.get(dim); + const scale2 = comptime s2.get(dim); + out.set(dim, if (comptime d1.get(dim) == 0 and d2.get(dim) == 0) + .none + else if (comptime d1.get(dim) == 0) + scale2 + else if (comptime d2.get(dim) == 0) + scale1 + else if (comptime scale1.getFactor() > scale2.getFactor()) + scale2 + else + scale1); + } + comptime return out; +}