Added comparison operation to Scalar and Vector
This commit is contained in:
parent
86841318f2
commit
4c910319d5
120
src/Scalar.zig
120
src/Scalar.zig
@ -8,12 +8,6 @@ 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
|
||||
@ -156,6 +150,94 @@ pub fn Scalar(comptime T: type, comptime d: Dimensions, comptime s: Scales) type
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares two Scalar for exact equality.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn eq(self: Self, rhs: anytype) bool {
|
||||
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 self.value == rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val == rhs_val;
|
||||
}
|
||||
|
||||
/// Compares two quantities for inequality.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn ne(self: Self, rhs: anytype) bool {
|
||||
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 self.value != rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val != rhs_val;
|
||||
}
|
||||
|
||||
/// Returns true if this quantity is strictly greater than the right-hand side.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn gt(self: Self, rhs: anytype) bool {
|
||||
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 self.value > rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val > rhs_val;
|
||||
}
|
||||
/// Returns true if this quantity is greater than or equal to the right-hand side.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn gte(self: Self, rhs: anytype) bool {
|
||||
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 self.value >= rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val >= rhs_val;
|
||||
}
|
||||
|
||||
/// Returns true if this quantity is strictly less than the right-hand side.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn lt(self: Self, rhs: anytype) bool {
|
||||
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 self.value < rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val < rhs_val;
|
||||
}
|
||||
/// Returns true if this quantity is less than or equal to the right-hand side.
|
||||
/// Dimensions must match — compile error otherwise. Scales are auto-resolved.
|
||||
pub inline fn lte(self: Self, rhs: anytype) bool {
|
||||
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 self.value <= rhs.value;
|
||||
|
||||
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;
|
||||
|
||||
return lhs_val <= rhs_val;
|
||||
}
|
||||
|
||||
/// Return a `Vector(len, Self)` type.
|
||||
pub fn Vec(_: Self, comptime len: comptime_int) type {
|
||||
return Vector(len, Self);
|
||||
@ -219,6 +301,32 @@ test "Generate quantity" {
|
||||
try std.testing.expectEqual(2, time.value);
|
||||
}
|
||||
|
||||
test "Comparisons (eq, ne, gt, gte, lt, lte)" {
|
||||
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const KiloMeter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k }));
|
||||
|
||||
const m1000 = Meter{ .value = 1000 };
|
||||
const km1 = KiloMeter{ .value = 1 };
|
||||
const km2 = KiloMeter{ .value = 2 };
|
||||
|
||||
// Equal / Not Equal
|
||||
try std.testing.expect(m1000.eq(km1));
|
||||
try std.testing.expect(km1.eq(m1000));
|
||||
try std.testing.expect(km2.ne(m1000));
|
||||
|
||||
// Greater Than / Greater Than or Equal
|
||||
try std.testing.expect(km2.gt(m1000));
|
||||
try std.testing.expect(km2.gt(km1));
|
||||
try std.testing.expect(km1.gte(m1000));
|
||||
try std.testing.expect(km2.gte(m1000));
|
||||
|
||||
// Less Than / Less Than or Equal
|
||||
try std.testing.expect(m1000.lt(km2));
|
||||
try std.testing.expect(km1.lt(km2));
|
||||
try std.testing.expect(km1.lte(m1000));
|
||||
try std.testing.expect(m1000.lte(km2));
|
||||
}
|
||||
|
||||
test "Add" {
|
||||
const Meter = Scalar(i128, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
|
||||
|
||||
176
src/Vector.zig
176
src/Vector.zig
@ -130,6 +130,133 @@ pub fn Vector(comptime len: usize, comptime Q: type) type {
|
||||
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);
|
||||
if (comptime !dims.eql(Tr.dims))
|
||||
@compileError("Dimension mismatch in eq: " ++ dims.str() ++ " vs " ++ Tr.dims.str());
|
||||
|
||||
inline for (self.data, 0..) |v, i| {
|
||||
const lhs_q = Q{ .value = v };
|
||||
const rhs_q = Tr.ScalarType{ .value = rhs.data[i] };
|
||||
if (!lhs_q.eq(rhs_q)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns true if any component differs after scale resolution.
|
||||
pub inline fn neAll(self: Self, rhs: anytype) bool {
|
||||
return !self.eqAll(rhs);
|
||||
}
|
||||
|
||||
/// Element-wise "Equal". Returns an array of booleans.
|
||||
pub inline fn eq(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).eq(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Element-wise "Not Equal". Returns an array of booleans.
|
||||
pub inline fn ne(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).ne(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Element-wise "Greater Than". Returns an array of booleans.
|
||||
pub inline fn gt(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).gt(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Element-wise "Greater Than or Equal". Returns an array of booleans.
|
||||
pub inline fn gte(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).gte(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Element-wise "Less Than". Returns an array of booleans.
|
||||
pub inline fn lt(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).lt(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Element-wise "Less Than or Equal". Returns an array of booleans.
|
||||
pub inline fn lte(self: Self, rhs: anytype) [len]bool {
|
||||
const Tr = @TypeOf(rhs);
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).lte(Tr.ScalarType{ .value = rhs.data[i] });
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Compares every element in the vector to a single scalar for equality.
|
||||
/// Returns an array of booleans [len]bool. Dimensions must match; scales are auto-resolved.
|
||||
pub inline fn eqScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).eq(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Compares every element in the vector to a single scalar for inequality.
|
||||
/// Returns an array of booleans [len]bool. Dimensions must match; scales are auto-resolved.
|
||||
pub inline fn neScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).ne(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Checks if each element in the vector is strictly greater than the given scalar.
|
||||
/// Returns an array of booleans [len]bool.
|
||||
pub inline fn gtScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).gt(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Checks if each element in the vector is greater than or equal to the given scalar.
|
||||
/// Returns an array of booleans [len]bool.
|
||||
pub inline fn gteScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).gte(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Checks if each element in the vector is strictly less than the given scalar.
|
||||
/// Returns an array of booleans [len]bool.
|
||||
pub inline fn ltScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).lt(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Checks if each element in the vector is less than or equal to the given scalar.
|
||||
/// Returns an array of booleans [len]bool.
|
||||
pub inline fn lteScalar(self: Self, scalar: anytype) [len]bool {
|
||||
var res: [len]bool = undefined;
|
||||
inline for (self.data, 0..) |v, i|
|
||||
res[i] = (Q{ .value = v }).lte(scalar);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Negate all components. Dimensions are preserved.
|
||||
pub fn negate(self: Self) Self {
|
||||
var res: Self = undefined;
|
||||
@ -344,3 +471,52 @@ test "VecX Length" {
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 25.0), v_float.lengthSqr(), 1e-4);
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 5.0), v_float.length(), 1e-4);
|
||||
}
|
||||
|
||||
test "Vector Comparisons" {
|
||||
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const KiloMeter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k }));
|
||||
|
||||
const v1 = Meter.Vec3{ .data = .{ 1000.0, 500.0, 0.0 } };
|
||||
const v2 = KiloMeter.Vec3{ .data = .{ 1.0, 0.5, 0.0 } };
|
||||
const v3 = KiloMeter.Vec3{ .data = .{ 1.0, 0.6, 0.0 } };
|
||||
|
||||
// 1. Equality (Whole vector)
|
||||
try std.testing.expect(v1.eqAll(v2));
|
||||
try std.testing.expect(v1.neAll(v3));
|
||||
|
||||
// 2. Element-wise Ordered Comparison
|
||||
const higher = v3.gt(v1); // compares 1km, 0.6km, 0km vs 1000m, 500m, 0m
|
||||
try std.testing.expectEqual(false, higher[0]); // 1km == 1000m
|
||||
try std.testing.expectEqual(true, higher[1]); // 0.6km > 500m
|
||||
try std.testing.expectEqual(false, higher[2]); // 0 == 0
|
||||
|
||||
// 3. Element-wise Equal Comparison
|
||||
const equal = v3.eq(v1); // compares 1km, 0.6km, 0km vs 1000m, 500m, 0m
|
||||
try std.testing.expectEqual(true, equal[0]); // 1km == 1000m
|
||||
try std.testing.expectEqual(false, equal[1]); // 0.6km > 500m
|
||||
try std.testing.expectEqual(true, equal[2]); // 0 == 0
|
||||
|
||||
// 3. Less than or equal
|
||||
const low_eq = v1.lte(v3);
|
||||
try std.testing.expect(low_eq[0] and low_eq[1] and low_eq[2]);
|
||||
}
|
||||
|
||||
test "Vector vs Scalar Comparisons" {
|
||||
const Meter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{}));
|
||||
const KiloMeter = Scalar(f32, Dimensions.init(.{ .L = 1 }), Scales.init(.{ .L = .k }));
|
||||
|
||||
const positions = Meter.Vec3{ .data = .{ 500.0, 1200.0, 3000.0 } };
|
||||
const threshold = KiloMeter{ .value = 1.0 }; // 1km (1000m)
|
||||
|
||||
// Check which axes exceed the 1km threshold
|
||||
const exceeded = positions.gtScalar(threshold);
|
||||
|
||||
try std.testing.expectEqual(false, exceeded[0]); // 500m > 1km is false
|
||||
try std.testing.expectEqual(true, exceeded[1]); // 1200m > 1km is true
|
||||
try std.testing.expectEqual(true, exceeded[2]); // 3000m > 1km is true
|
||||
|
||||
// Check for equality (broadcasted)
|
||||
const exact_match = positions.eqScalar(Meter{ .value = 500.0 });
|
||||
try std.testing.expect(exact_match[0] == true);
|
||||
try std.testing.expect(exact_match[1] == false);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user