ona/source/coral/math.zig

163 lines
4.6 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,
};
2023-05-28 03:19:46 +02:00
///
/// Returns the float type described by `float`.
///
pub fn Float(comptime float: std.builtin.Type.Float) type {
return @Type(.{.Float = float});
2023-04-19 01:25:35 +02:00
}
2023-05-28 03:19:46 +02:00
///
/// Returns the integer type described by `int`.
///
pub fn Int(comptime int: std.builtin.Type.Int) type {
return @Type(.{.Int = int});
2023-04-19 01:25:35 +02:00
}
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
///
2023-05-28 03:19:46 +02:00
/// Attempts to perform a checked integer cast to the type expressed by `int` on `value`, returning the result or
/// [CheckedArithmeticError] if the operation tried to invoke safety-checked behavior.
2023-05-23 02:08:34 +02:00
///
/// `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.
///
2023-05-28 03:19:46 +02:00
pub fn checked_cast(comptime int: std.builtin.Type.Int, value: anytype) CheckedArithmeticError!Int(int) {
if ((value < min_int(int)) or (value > max_int(int))) {
2023-05-23 02:08:34 +02:00
return error.IntOverflow;
}
2023-05-28 03:19:46 +02:00
return @intCast(Int(int), value);
2023-05-23 02:08:34 +02:00
}
///
/// 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
}
2023-05-24 01:22:12 +02:00
///
/// Returns `value` wrapped around the inclusive bounds of `lower` and `upper`.
///
pub fn wrap(value: anytype, lower: anytype, upper: anytype) @TypeOf(value, lower, upper) {
const range = upper - lower;
return if (range == 0) lower else lower + @mod((@mod((value - lower), range) + range), range);
}