ona/source/coral/math.zig

164 lines
4.5 KiB
Zig
Raw Normal View History

2023-05-23 02:08:34 +02:00
const std = @import("std");
///
/// Errors that may occur during checked integer arithmetic operations.
///
2023-04-19 01:25:35 +02:00
pub const CheckedArithmeticError = error {
IntOverflow,
};
pub fn Float(comptime bits: comptime_int) type {
return @Type(.{.Float = .{.bits = bits}});
}
pub fn Signed(comptime bits: comptime_int) type {
return @Type(.{.Int = .{
.signedness = .signed,
.bits = bits,
}});
}
pub fn Unsigned(comptime bits: comptime_int) type {
return @Type(.{.Int = .{
.signedness = .unsigned,
.bits = bits,
}});
}
2023-05-23 02:08:34 +02:00
///
/// Two-dimensional vector type.
///
pub const Vector2 = extern struct {
2023-04-19 01:25:35 +02:00
x: f32,
y: f32,
2023-05-23 02:08:34 +02:00
///
/// A [Vector2] with a value of `0` assigned to all of the components.
///
2023-04-19 01:25:35 +02:00
pub const zero = Vector2{.x = 0, .y = 0};
};
2023-05-23 02:08:34 +02:00
///
/// Attempts to perform a checked addition between `a` and `b`, returning the result or [CheckedArithmeticError] if the
/// operation tried to invoke safety-checked behavior.
///
/// `checked_add` can be seen as an alternative to the language-native addition operator (+) that exposes the safety-
/// checked behavior in the form of an error type that may be caught or tried on.
///
2023-04-19 01:25:35 +02:00
pub fn checked_add(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a + b) {
const result = @addWithOverflow(a, b);
2023-05-23 02:08:34 +02:00
if (result.@"1" != 0) {
return error.IntOverflow;
}
2023-04-19 01:25:35 +02:00
return result.@"0";
}
2023-05-23 02:08:34 +02:00
///
/// Attempts to perform a checked integer cast to `Int` on `value`, returning the result or [CheckedArithmeticError] if
/// the operation tried to invoke safety-checked behavior.
///
/// `checked_cast` can be seen as an alternative to the language-native `@intCast` builtin that exposes the safety-
/// checked behavior in the form of an error type that may be caught or tried on.
///
pub fn checked_cast(comptime Int: type, value: anytype) CheckedArithmeticError!Int {
const int_type_info = @typeInfo(Int);
if (int_type_info != .Int) {
@compileError("`Int` must be of type int");
}
if ((value < min_int(int_type_info.Int)) or (value > max_int(int_type_info.Int))) {
return error.IntOverflow;
}
return @intCast(Int, value);
}
///
/// Attempts to perform a checked multiplication between `a` and `b`, returning the result or [CheckedArithmeticError]
/// if the operation tried to invoke safety-checked behavior.
///
/// `checked_mul` can be seen as an alternative to the language-native multiplication operator (*) that exposes the
/// safety-checked behavior in the form of an error type that may be caught or tried on.
///
2023-04-19 01:25:35 +02:00
pub fn checked_mul(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a * b) {
const result = @mulWithOverflow(a, b);
2023-05-23 02:08:34 +02:00
if (result.@"1" != 0) {
return error.IntOverflow;
}
2023-04-19 01:25:35 +02:00
return result.@"0";
}
2023-05-23 02:08:34 +02:00
///
/// Attempts to perform a checked subtraction between `a` and `b`, returning the result or [CheckedArithmeticError] if
/// the operation tried to invoke safety-checked behavior.
///
/// `checked_sub` can be seen as an alternative to the language-native subtraction operator (-) that exposes the safety-
/// checked behavior in the form of an error type that may be caught or tried on.
///
2023-04-19 01:25:35 +02:00
pub fn checked_sub(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a - b) {
const result = @subWithOverflow(a, b);
2023-05-23 02:08:34 +02:00
if (result.@"1" != 0) {
return error.IntOverflow;
}
2023-04-19 01:25:35 +02:00
return result.@"0";
}
2023-05-23 02:08:34 +02:00
///
/// Returns `value` clamped between the inclusive bounds of `lower` and `upper`.
///
pub fn clamp(value: anytype, lower: anytype, upper: anytype) @TypeOf(value, lower, upper) {
return max(lower, min(upper, value));
2023-04-19 01:25:35 +02:00
}
2023-05-23 02:08:34 +02:00
///
/// Returns `true` if `value` is clamped within the inclusive bounds of `lower` and `upper`.
///
pub fn is_clamped(value: anytype, lower: anytype, upper: anytype) bool {
return (value >= lower) and (value <= upper);
2023-04-19 01:25:35 +02:00
}
2023-05-23 02:08:34 +02:00
///
/// Returns the maximum value between `a` and `b`.
///
pub fn max(a: anytype, b: anytype) @TypeOf(a, b) {
return @max(a, b);
}
///
/// Returns the maximum value that the integer described by `int` may express.
///
pub fn max_int(comptime int: std.builtin.Type.Int) comptime_int {
const bit_count = int.bits;
2023-04-19 01:25:35 +02:00
if (bit_count == 0) return 0;
2023-05-23 02:08:34 +02:00
return (1 << (bit_count - @boolToInt(int.signedness == .signed))) - 1;
2023-04-19 01:25:35 +02:00
}
2023-05-23 02:08:34 +02:00
///
/// Returns the minimum value between `a` and `b`.
///
pub fn min(a: anytype, b: anytype) @TypeOf(a, b) {
return @min(a, b);
}
///
/// Returns the minimum value that the integer described by `int` may express.
///
pub fn min_int(comptime int: std.builtin.Type.Int) comptime_int {
if (int.signedness == .unsigned) return 0;
const bit_count = int.bits;
if (bit_count == 0) return 0;
return -(1 << (bit_count - 1));
2023-04-19 01:25:35 +02:00
}