Added abs, pow and product operations
This commit is contained in:
parent
c4e462add3
commit
0dd9e02f59
@ -75,6 +75,15 @@ pub fn sub(comptime a: Self, comptime b: Self) Self {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Multiply exponents by a scalar integer. Used internally by `pow` in Scalar.
|
||||
pub fn scale(comptime a: Self, comptime exp: comptime_int) Self {
|
||||
@setEvalBranchQuota(10_000);
|
||||
var result = Self.initFill(0);
|
||||
inline for (std.enums.values(Dimension)) |d|
|
||||
result.set(d, a.get(d) * exp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns true if every dimension exponent is equal. Used to enforce type compatibility in `add`, `sub`, `to`.
|
||||
pub fn eql(comptime a: Self, comptime b: Self) bool {
|
||||
inline for (std.enums.values(Dimension)) |d|
|
||||
|
||||
@ -107,6 +107,28 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the absolute value of the quantity.
|
||||
/// Dimensions and scales remain entirely unchanged.
|
||||
pub inline fn abs(self: Self) Self {
|
||||
if (comptime @typeInfo(T) == .int)
|
||||
return .{ .value = @intCast(@abs(self.value)) }
|
||||
else
|
||||
return .{ .value = @abs(self.value) };
|
||||
}
|
||||
|
||||
/// Raises the quantity to a compile-time integer exponent.
|
||||
/// Dimension exponents are multiplied by the exponent: `(L²)³ → L⁶`.
|
||||
pub inline fn pow(self: Self, comptime exp: comptime_int) Scalar(
|
||||
T,
|
||||
dims.scale(exp),
|
||||
s,
|
||||
) {
|
||||
if (comptime @typeInfo(T) == .int)
|
||||
return .{ .value = std.math.powi(T, self.value, exp) catch @panic("Integer overflow in pow") }
|
||||
else
|
||||
return .{ .value = std.math.pow(T, self.value, @as(T, @floatFromInt(exp))) };
|
||||
}
|
||||
|
||||
/// Convert to a compatible unit type. The scale ratio is computed at comptime.
|
||||
/// Compile error if dimensions don't match.
|
||||
pub inline fn to(self: Self, comptime Dest: type) Dest {
|
||||
@ -549,3 +571,36 @@ test "Format Scalar" {
|
||||
res = try std.fmt.bufPrint(&buf, "{d:_>10.1}", .{m});
|
||||
try std.testing.expectEqualStrings("_______1.2m", res);
|
||||
}
|
||||
|
||||
test "Abs" {
|
||||
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const m1 = Meter{ .value = -50 };
|
||||
const m2 = m1.abs();
|
||||
|
||||
try std.testing.expectEqual(50, m2.value);
|
||||
try std.testing.expectEqual(1, @TypeOf(m2).dims.get(.L));
|
||||
|
||||
const m_float = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const m3 = m_float{ .value = -42.5 };
|
||||
try std.testing.expectEqual(42.5, m3.abs().value);
|
||||
}
|
||||
|
||||
test "Pow" {
|
||||
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const d = Meter{ .value = 4 };
|
||||
|
||||
const area = d.pow(2);
|
||||
try std.testing.expectEqual(16, area.value);
|
||||
try std.testing.expectEqual(2, @TypeOf(area).dims.get(.L));
|
||||
|
||||
const volume = d.pow(3);
|
||||
try std.testing.expectEqual(64, volume.value);
|
||||
try std.testing.expectEqual(3, @TypeOf(volume).dims.get(.L));
|
||||
|
||||
// Float test
|
||||
const MeterF = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const d_f = MeterF{ .value = 2.0 };
|
||||
const area_f = d_f.pow(3);
|
||||
try std.testing.expectEqual(8.0, area_f.value);
|
||||
try std.testing.expectEqual(3, @TypeOf(area_f).dims.get(.L));
|
||||
}
|
||||
|
||||
@ -90,21 +90,21 @@ pub fn set(comptime self: *Scales, comptime key: Dimension, comptime val: UnitSc
|
||||
/// Compute the combined scale factor for a given dimension signature.
|
||||
/// Each dimension's prefix is raised to its exponent and multiplied together.
|
||||
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| {
|
||||
var factor: f64 = 1.0;
|
||||
for (std.enums.values(Dimension)) |dim| {
|
||||
const power = comptime d.get(dim);
|
||||
if (comptime power == 0) continue;
|
||||
if (power == 0) continue;
|
||||
|
||||
const base = comptime s.get(dim).getFactor();
|
||||
const base = s.get(dim).getFactor();
|
||||
|
||||
var i: comptime_int = 0;
|
||||
const abs_power = if (power < 0) -power else power;
|
||||
inline while (i < abs_power) : (i += 1) {
|
||||
while (i < abs_power) : (i += 1) {
|
||||
if (power > 0)
|
||||
factor *= base
|
||||
else
|
||||
factor /= base;
|
||||
}
|
||||
}
|
||||
return factor;
|
||||
return comptime factor;
|
||||
}
|
||||
|
||||
@ -180,6 +180,45 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns a vector where each component is the absolute value of the original.
|
||||
pub inline fn abs(self: Self) Self {
|
||||
var res: Self = undefined;
|
||||
inline for (self.data, 0..) |v, i| {
|
||||
const q = Q{ .value = v };
|
||||
res.data[i] = q.abs().value;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Multiplies all components of the vector together.
|
||||
/// Resulting dimensions are (Original Dims * len).
|
||||
pub inline fn product(self: Self) Scalar(
|
||||
T,
|
||||
dims.scale(len),
|
||||
scales,
|
||||
) {
|
||||
var res_val: T = 1;
|
||||
inline for (self.data) |v|
|
||||
res_val *= v;
|
||||
return .{ .value = res_val };
|
||||
}
|
||||
|
||||
/// Raises every component to a compile-time integer power.
|
||||
/// Dimensions are scaled by the exponent.
|
||||
pub inline fn pow(self: Self, comptime exp: comptime_int) Vector(len, Scalar(
|
||||
T,
|
||||
dims.scale(exp),
|
||||
scales,
|
||||
)) {
|
||||
const ResScalar = Scalar(T, dims.scale(exp), s);
|
||||
var res: Vector(len, ResScalar) = undefined;
|
||||
inline for (self.data, 0..) |v, i| {
|
||||
const q = Q{ .value = v };
|
||||
res.data[i] = q.pow(exp).value;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns true only if all components are equal after scale resolution.
|
||||
pub inline fn eqAll(self: Self, rhs: anytype) bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
@ -594,3 +633,25 @@ test "Vector Dot and Cross Products" {
|
||||
// Torque dimensions are same as Energy but as a Vector
|
||||
try std.testing.expectEqual(2, @TypeOf(torque).dims.get(.L));
|
||||
}
|
||||
|
||||
test "Vector Abs, Pow, and Product" {
|
||||
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
|
||||
const v1 = Meter.Vec3{ .data = .{ -2.0, 3.0, -4.0 } };
|
||||
|
||||
// 1. Abs
|
||||
const v_abs = v1.abs();
|
||||
try std.testing.expectEqual(2.0, v_abs.data[0]);
|
||||
try std.testing.expectEqual(4.0, v_abs.data[2]);
|
||||
|
||||
// 2. Product (L1 * L1 * L1 = L3)
|
||||
const vol = v_abs.product();
|
||||
try std.testing.expectEqual(24.0, vol.value);
|
||||
try std.testing.expectEqual(3, @TypeOf(vol).dims.get(.L));
|
||||
|
||||
// 3. Pow (Scalar exponent: (L1)^2 = L2)
|
||||
const area_vec = v_abs.pow(2);
|
||||
try std.testing.expectEqual(4.0, area_vec.data[0]);
|
||||
try std.testing.expectEqual(16.0, area_vec.data[2]);
|
||||
try std.testing.expectEqual(2, @TypeOf(area_vec).dims.get(.L));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user