diff --git a/src/Base.zig b/src/Base.zig index 0f9714b..90da1ab 100644 --- a/src/Base.zig +++ b/src/Base.zig @@ -5,6 +5,8 @@ const Dimensions = @import("Dimensions.zig"); const Scales = @import("Scales.zig"); const Scalar = @import("Scalar.zig").Scalar; +// TODO: Add common constants like G + fn PhysicalConstant(comptime d: Dimensions.ArgOpts, comptime val: f64, comptime s: Scales.ArgOpts) type { return struct { const dims = Dimensions.init(d); diff --git a/src/Scalar.zig b/src/Scalar.zig index f2c78a4..6231b53 100644 --- a/src/Scalar.zig +++ b/src/Scalar.zig @@ -63,7 +63,7 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o const TargetType = Scalar(T, dims.argsOpt(), hlp.finerScales(Self, RhsType).argsOpt()); const lhs_val = if (comptime Self == TargetType) self.value else self.to(TargetType).value; const rhs_val = if (comptime RhsType == TargetType) rhs_s.value else rhs_s.to(TargetType).value; - return .{ .value = lhs_val + rhs_val }; + return .{ .value = if (comptime hlp.isInt(T)) lhs_val +| rhs_val else lhs_val + rhs_val }; } /// Subtract two quantities. Dimensions must match — compile error otherwise. @@ -84,7 +84,7 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o const TargetType = Scalar(T, dims.argsOpt(), hlp.finerScales(Self, RhsType).argsOpt()); const lhs_val = if (comptime Self == TargetType) self.value else self.to(TargetType).value; const rhs_val = if (comptime RhsType == TargetType) rhs_s.value else rhs_s.to(TargetType).value; - return .{ .value = lhs_val - rhs_val }; + return .{ .value = if (comptime hlp.isInt(T)) lhs_val -| rhs_val else lhs_val - rhs_val }; } /// Multiply two quantities. Dimension exponents are summed: `L¹ * T⁻¹ → L¹T⁻¹`. @@ -104,7 +104,7 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o const lhs_val = if (comptime Self == SelfNorm) self.value else self.to(SelfNorm).value; const rhs_val = if (comptime RhsType == RhsNorm) rhs_s.value else rhs_s.to(RhsNorm).value; - return .{ .value = lhs_val * rhs_val }; + return .{ .value = if (comptime hlp.isInt(T)) lhs_val *| rhs_val else lhs_val * rhs_val }; } /// Divide two quantities. Dimension exponents are subtracted: `L¹ / T¹ → L¹T⁻¹`. @@ -121,7 +121,7 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o const RhsNorm = Scalar(T, RhsType.dims.argsOpt(), hlp.finerScales(Self, RhsType).argsOpt()); const lhs_val = if (comptime Self == SelfNorm) self.value else self.to(SelfNorm).value; const rhs_val = if (comptime RhsType == RhsNorm) rhs_s.value else rhs_s.to(RhsNorm).value; - if (comptime @typeInfo(T) == .int) { + if (comptime hlp.isInt(T)) { return .{ .value = @divTrunc(lhs_val, rhs_val) }; } else { return .{ .value = lhs_val / rhs_val }; @@ -148,8 +148,8 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o dims.scale(exp).argsOpt(), scales.argsOpt(), ) { - if (comptime @typeInfo(T) == .int) - return .{ .value = std.math.powi(T, self.value, exp) catch @panic("Integer overflow in pow") } + if (comptime hlp.isInt(T)) + return .{ .value = std.math.powi(T, self.value, exp) catch std.math.maxInt(T) } else return .{ .value = std.math.pow(T, self.value, @as(T, @floatFromInt(exp))) }; } @@ -163,7 +163,7 @@ pub fn Scalar(comptime T: type, comptime d_opt: Dimensions.ArgOpts, comptime s_o @compileError("Cannot take sqrt of " ++ dims.str() ++ ": exponents must be even."); if (self.value < 0) return .{ .value = 0 }; - if (comptime @typeInfo(T) == .int) { + if (comptime hlp.isInt(T)) { const UnsignedT = @Int(.unsigned, @typeInfo(T).int.bits); const u_len_sq = @as(UnsignedT, @intCast(self.value)); return .{ .value = @as(T, @intCast(std.math.sqrt(u_len_sq))) }; diff --git a/src/Scales.zig b/src/Scales.zig index 2544315..0732f93 100644 --- a/src/Scales.zig +++ b/src/Scales.zig @@ -3,6 +3,8 @@ const hlp = @import("helper.zig"); const Dimensions = @import("Dimensions.zig"); const Dimension = @import("Dimensions.zig").Dimension; +// TODO: add more scales like feet and inch + /// Use to initiate Scalar and Scales type pub const ArgOpts = struct { L: UnitScale = .none, @@ -53,17 +55,17 @@ pub const UnitScale = enum(isize) { /// Helper to get the actual scaling factor pub inline fn getFactor(self: @This()) comptime_float { - return switch (self) { + return comptime switch (self) { 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 inline fn getFactorInt(self: @This()) comptime_int { - return switch (self) { - inline .P, .T, .G, .M, .k, .h, .da, .none, .d, .c, .m, .u, .n, .p, .f => comptime std.math.powi(i32, 10.0, @intFromEnum(self)) catch 0, - inline else => comptime @intFromEnum(self), + 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), }; } }; diff --git a/src/Vector.zig b/src/Vector.zig index 333b46f..64da36b 100644 --- a/src/Vector.zig +++ b/src/Vector.zig @@ -272,7 +272,10 @@ pub fn Vector(comptime len: usize, comptime Q: type) type { scales.argsOpt(), ) { var res_val: T = 1; - inline for (self.data) |v| + if (comptime hlp.isInt(T)) { + inline for (self.data) |v| + res_val = res_val *| v; + } else inline for (self.data) |v| res_val *= v; return .{ .value = res_val }; } diff --git a/src/benchmark.zig b/src/benchmark.zig index 6f76719..1ffb7cc 100644 --- a/src/benchmark.zig +++ b/src/benchmark.zig @@ -78,17 +78,17 @@ fn bench_Scalar(writer: *std.Io.Writer) !void { \\ , .{ ITERS, SAMPLES }); - const Types = .{ i16, i32, i64, i128, i256, f32, f64, f128 }; - const TNames = .{ "i16", "i32", "i64", "i128", "i256", "f32", "f64", "f128" }; - const Ops = .{ "add", "sub", "mulBy", "divBy", "to" }; + const Types = .{ i16, i32, i64, i128, i256, f32, f64 }; + const TNames = .{ "i16", "i32", "i64", "i128", "i256", "f32", "f64" }; + const Ops = .{ "add", "sub", "mulBy", "divBy", "to", "abs", "pow", "eq", "gt", "mulBy(n)" }; var results_matrix: [Ops.len][Types.len]f64 = undefined; comptime var tidx: usize = 0; inline for (Types, TNames) |T, tname| { - const M = Scalar(T, .init(.{ .L = 1 }), .init(.{})); - const KM = Scalar(T, .init(.{ .L = 1 }), .init(.{ .L = .k })); - const S = Scalar(T, .init(.{ .T = 1 }), .init(.{})); + const M = Scalar(T, .{ .L = 1 }, .{}); + const KM = Scalar(T, .{ .L = 1 }, .{ .L = .k }); + const S = Scalar(T, .{ .T = 1 }, .{}); inline for (Ops, 0..) |op_name, oidx| { var samples: [SAMPLES]f64 = undefined; @@ -107,8 +107,16 @@ fn bench_Scalar(writer: *std.Io.Writer) !void { (M{ .value = getVal(T, i, 63) }).mulBy(M{ .value = getVal(T, i +% 1, 63) }) else if (comptime std.mem.eql(u8, op_name, "divBy")) (M{ .value = getVal(T, i +% 10, 63) }).divBy(S{ .value = getVal(T, i, 63) }) - else - (KM{ .value = getVal(T, i, 15) }).to(M); + else if (comptime std.mem.eql(u8, op_name, "to")) + (KM{ .value = getVal(T, i, 15) }).to(M) + else if (comptime std.mem.eql(u8, op_name, "abs")) + (M{ .value = getVal(T, i, 63) }).abs() + else if (comptime std.mem.eql(u8, op_name, "eq")) + (M{ .value = getVal(T, i, 63) }).eq(M{ .value = getVal(T, i +% 3, 63) }) + else if (comptime std.mem.eql(u8, op_name, "gt")) + (M{ .value = getVal(T, i, 63) }).gt(M{ .value = getVal(T, i +% 3, 63) }) + else // "mulBy(n)" — bare comptime_int, dimensionless + (M{ .value = getVal(T, i, 63) }).mulBy(3); }, ); } @@ -133,9 +141,9 @@ fn bench_Scalar(writer: *std.Io.Writer) !void { try writer.print("└───────────────────┴──────┴─────────────────────┴─────────────────────┘\n\n", .{}); try writer.print("Median Summary (ns/op):\n", .{}); - try writer.print("┌──────────────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐\n", .{}); - try writer.print("│ Operation │ i16 │ i32 │ i64 │ i128 │ i256 │ f32 │ f64 │ f128 │\n", .{}); - try writer.print("├──────────────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤\n", .{}); + try writer.print("┌──────────────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐\n", .{}); + try writer.print("│ Operation │ i16 │ i32 │ i64 │ i128 │ i256 │ f32 │ f64 │\n", .{}); + try writer.print("├──────────────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤\n", .{}); inline for (Ops, 0..) |op_name, oidx| { try writer.print("│ {s:<11} │", .{op_name}); @@ -146,7 +154,7 @@ fn bench_Scalar(writer: *std.Io.Writer) !void { try writer.print("\n", .{}); } - try writer.print("└──────────────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘\n", .{}); + try writer.print("└──────────────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘\n", .{}); } fn bench_vsNative(writer: *std.Io.Writer) !void { @@ -180,8 +188,8 @@ fn bench_vsNative(writer: *std.Io.Writer) !void { var native_total_ns: f64 = 0; var quantity_total_ns: f64 = 0; - const M = Scalar(T, .init(.{ .L = 1 }), .init(.{})); - const S = Scalar(T, .init(.{ .T = 1 }), .init(.{})); + const M = Scalar(T, .{ .L = 1 }, .{}); + const S = Scalar(T, .{ .T = 1 }, .{}); std.mem.doNotOptimizeAway({ for (0..SAMPLES) |_| { @@ -278,9 +286,9 @@ fn bench_crossTypeVsNative(writer: *std.Io.Writer) !void { var native_total_ns: f64 = 0; var quantity_total_ns: f64 = 0; - const M1 = Scalar(T1, .init(.{ .L = 1 }), .init(.{})); - const M2 = Scalar(T2, .init(.{ .L = 1 }), .init(.{})); - const S2 = Scalar(T2, .init(.{ .T = 1 }), .init(.{})); + const M1 = Scalar(T1, .{ .L = 1 }, .{}); + const M2 = Scalar(T2, .{ .L = 1 }, .{}); + const S2 = Scalar(T2, .{ .T = 1 }, .{}); std.mem.doNotOptimizeAway({ for (0..SAMPLES) |_| { @@ -367,28 +375,36 @@ fn bench_Vector(writer: *std.Io.Writer) !void { try writer.print( \\ \\ Vector benchmark — {d} iterations, {d} samples/cell - \\ (Results in ns/op) + \\ (Results in ns/op; "---" = not applicable for this length) \\ - \\┌─────────────┬──────┬─────────┬─────────┬─────────┐ - \\│ Operation │ Type │ Len=3 │ Len=4 │ Len=16 │ - \\├─────────────┼──────┼─────────┼─────────┼─────────┤ + \\┌──────────────────┬──────┬─────────┬─────────┬─────────┐ + \\│ Operation │ Type │ Len=3 │ Len=4 │ Len=16 │ + \\├──────────────────┼──────┼─────────┼─────────┼─────────┤ \\ , .{ ITERS, SAMPLES }); const Types = .{ i32, i64, i128, f32, f64 }; const TNames = .{ "i32", "i64", "i128", "f32", "f64" }; const Lengths = .{ 3, 4, 16 }; - const Ops = .{ "add", "divBy", "mulByScalar", "length" }; + // "cross" is only valid for len=3; other cells will show " --- " + const Ops = .{ "add", "divBy", "mulByScalar", "dot", "cross", "product", "pow", "length" }; inline for (Ops, 0..) |op_name, o_idx| { inline for (Types, TNames) |T, tname| { - try writer.print("│ {s:<11} │ {s:<4} │", .{ op_name, tname }); + try writer.print("│ {s:<16} │ {s:<4} │", .{ op_name, tname }); inline for (Lengths) |len| { - const Q_base = Scalar(T, .init(.{ .L = 1 }), .init(.{})); - const Q_time = Scalar(T, .init(.{ .T = 1 }), .init(.{})); + const Q_base = Scalar(T, .{ .L = 1 }, .{}); + const Q_time = Scalar(T, .{ .T = 1 }, .{}); const V = Vector(len, Q_base); + // cross product is only defined for len == 3 + const is_cross = comptime std.mem.eql(u8, op_name, "cross"); + if (comptime is_cross and len != 3) { + try writer.print(" --- │", .{}); + continue; + } + var samples: [SAMPLES]f64 = undefined; std.mem.doNotOptimizeAway({ @@ -405,6 +421,17 @@ fn bench_Vector(writer: *std.Io.Writer) !void { } else if (comptime std.mem.eql(u8, op_name, "mulByScalar")) { const s_val = Q_time{ .value = getVal(T, i +% 2, 63) }; _ = v1.mulByScalar(s_val); + } else if (comptime std.mem.eql(u8, op_name, "dot")) { + const v2 = V.initDefault(getVal(T, i +% 5, 63)); + _ = v1.dot(v2); + } else if (comptime std.mem.eql(u8, op_name, "cross")) { + // len == 3 guaranteed by the guard above + const v2 = V.initDefault(getVal(T, i +% 5, 63)); + _ = v1.cross(v2); + } else if (comptime std.mem.eql(u8, op_name, "product")) { + _ = v1.product(); + } else if (comptime std.mem.eql(u8, op_name, "pow")) { + _ = v1.pow(2); } else if (comptime std.mem.eql(u8, op_name, "length")) { _ = v1.length(); } @@ -422,8 +449,8 @@ fn bench_Vector(writer: *std.Io.Writer) !void { } if (o_idx < Ops.len - 1) { - try writer.print("├─────────────┼──────┼─────────┼─────────┼─────────┤\n", .{}); + try writer.print("├──────────────────┼──────┼─────────┼─────────┼─────────┤\n", .{}); } } - try writer.print("└─────────────┴──────┴─────────┴─────────┴─────────┘\n", .{}); + try writer.print("└──────────────────┴──────┴─────────┴─────────┴─────────┘\n", .{}); }