mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-15 08:15:07 +00:00
704 lines
23 KiB
Zig
704 lines
23 KiB
Zig
const std = @import("std");
|
|
const math = std.math;
|
|
const RocList = @import("list.zig").RocList;
|
|
const RocStr = @import("str.zig").RocStr;
|
|
const WithOverflow = @import("utils.zig").WithOverflow;
|
|
const Ordering = @import("utils.zig").Ordering;
|
|
const roc_panic = @import("panic.zig").panic_help;
|
|
|
|
pub fn NumParseResult(comptime T: type) type {
|
|
// on the roc side we sort by alignment; putting the errorcode last
|
|
// always works out (no number with smaller alignment than 1)
|
|
return extern struct {
|
|
value: T,
|
|
errorcode: u8, // 0 indicates success
|
|
};
|
|
}
|
|
|
|
pub const F32Parts = extern struct {
|
|
fraction: u32,
|
|
exponent: u8,
|
|
sign: bool,
|
|
};
|
|
|
|
pub const F64Parts = extern struct {
|
|
fraction: u64,
|
|
exponent: u16,
|
|
sign: bool,
|
|
};
|
|
|
|
pub const U256 = struct {
|
|
hi: u128,
|
|
lo: u128,
|
|
};
|
|
|
|
pub fn mul_u128(a: u128, b: u128) U256 {
|
|
var hi: u128 = undefined;
|
|
var lo: u128 = undefined;
|
|
|
|
const bits_in_dword_2: u32 = 64;
|
|
const lower_mask: u128 = math.maxInt(u128) >> bits_in_dword_2;
|
|
|
|
lo = (a & lower_mask) * (b & lower_mask);
|
|
|
|
var t = lo >> bits_in_dword_2;
|
|
|
|
lo &= lower_mask;
|
|
|
|
t += (a >> bits_in_dword_2) * (b & lower_mask);
|
|
|
|
lo += (t & lower_mask) << bits_in_dword_2;
|
|
|
|
hi = t >> bits_in_dword_2;
|
|
|
|
t = lo >> bits_in_dword_2;
|
|
|
|
lo &= lower_mask;
|
|
|
|
t += (b >> bits_in_dword_2) * (a & lower_mask);
|
|
|
|
lo += (t & lower_mask) << bits_in_dword_2;
|
|
|
|
hi += t >> bits_in_dword_2;
|
|
|
|
hi += (a >> bits_in_dword_2) * (b >> bits_in_dword_2);
|
|
|
|
return .{ .hi = hi, .lo = lo };
|
|
}
|
|
|
|
pub fn exportParseInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(buf: RocStr) callconv(.C) NumParseResult(T) {
|
|
// a radix of 0 will make zig determine the radix from the frefix:
|
|
// * A prefix of "0b" implies radix=2,
|
|
// * A prefix of "0o" implies radix=8,
|
|
// * A prefix of "0x" implies radix=16,
|
|
// * Otherwise radix=10 is assumed.
|
|
const radix = 0;
|
|
if (std.fmt.parseInt(T, buf.asSlice(), radix)) |success| {
|
|
return .{ .errorcode = 0, .value = success };
|
|
} else |_| {
|
|
return .{ .errorcode = 1, .value = 0 };
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportParseFloat(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(buf: RocStr) callconv(.C) NumParseResult(T) {
|
|
if (std.fmt.parseFloat(T, buf.asSlice())) |success| {
|
|
return .{ .errorcode = 0, .value = success };
|
|
} else |_| {
|
|
return .{ .errorcode = 1, .value = 0 };
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportNumToFloatCast(comptime T: type, comptime F: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(x: T) callconv(.C) F {
|
|
return @floatFromInt(x);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportPow(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(base: T, exp: T) callconv(.C) T {
|
|
switch (@typeInfo(T)) {
|
|
// std.math.pow can handle ints via powi, but it turns any errors to unreachable
|
|
// we want to catch overflow and report a proper error to the user
|
|
.Int => {
|
|
if (std.math.powi(T, base, exp)) |value| {
|
|
return value;
|
|
} else |err| switch (err) {
|
|
error.Overflow => roc_panic("Integer raised to power overflowed!", 0),
|
|
error.Underflow => return 0,
|
|
}
|
|
},
|
|
else => {
|
|
return std.math.pow(T, base, exp);
|
|
},
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportIsNan(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) bool {
|
|
return std.math.isNan(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportIsInfinite(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) bool {
|
|
return std.math.isInf(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportIsFinite(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) bool {
|
|
return std.math.isFinite(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAsin(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return std.math.asin(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAcos(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return std.math.acos(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAtan(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return std.math.atan(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportSin(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return math.sin(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportCos(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return math.cos(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportTan(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return math.tan(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportLog(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return @log(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportFAbs(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return @abs(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportSqrt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: T) callconv(.C) T {
|
|
return math.sqrt(input);
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportRound(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: F) callconv(.C) T {
|
|
return @as(T, @intFromFloat((math.round(input))));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportFloor(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: F) callconv(.C) T {
|
|
return @as(T, @intFromFloat((math.floor(input))));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportCeiling(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: F) callconv(.C) T {
|
|
return @as(T, @intFromFloat((math.ceil(input))));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportDivCeil(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(a: T, b: T) callconv(.C) T {
|
|
return math.divCeil(T, a, b) catch {
|
|
roc_panic("Integer division by 0!", 0);
|
|
};
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn ToIntCheckedResult(comptime T: type) type {
|
|
// On the Roc side we sort by alignment; putting the errorcode last
|
|
// always works out (no number with smaller alignment than 1).
|
|
return extern struct {
|
|
value: T,
|
|
out_of_bounds: bool,
|
|
};
|
|
}
|
|
|
|
pub fn exportToIntCheckingMax(comptime From: type, comptime To: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: From) callconv(.C) ToIntCheckedResult(To) {
|
|
if (input > std.math.maxInt(To)) {
|
|
return .{ .out_of_bounds = true, .value = 0 };
|
|
}
|
|
return .{ .out_of_bounds = false, .value = @as(To, @intCast(input)) };
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(From), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportToIntCheckingMaxAndMin(comptime From: type, comptime To: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(input: From) callconv(.C) ToIntCheckedResult(To) {
|
|
if (input > std.math.maxInt(To) or input < std.math.minInt(To)) {
|
|
return .{ .out_of_bounds = true, .value = 0 };
|
|
}
|
|
return .{ .out_of_bounds = false, .value = @as(To, @intCast(input)) };
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(From), .linkage = .strong });
|
|
}
|
|
|
|
fn isMultipleOf(comptime T: type, lhs: T, rhs: T) bool {
|
|
if (rhs == 0 or rhs == -1) {
|
|
// lhs is a multiple of rhs iff
|
|
//
|
|
// - rhs == -1
|
|
// - both rhs and lhs are 0
|
|
//
|
|
// the -1 case is important for overflow reasons `isize::MIN % -1` crashes in rust
|
|
return (rhs == -1) or (lhs == 0);
|
|
} else {
|
|
const rem = @mod(lhs, rhs);
|
|
return rem == 0;
|
|
}
|
|
}
|
|
|
|
pub fn exportIsMultipleOf(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) bool {
|
|
return @call(.always_inline, isMultipleOf, .{ T, self, other });
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
fn addWithOverflow(comptime T: type, self: T, other: T) WithOverflow(T) {
|
|
switch (@typeInfo(T)) {
|
|
.Int => {
|
|
const answer = @addWithOverflow(self, other);
|
|
return .{ .value = answer[0], .has_overflowed = answer[1] == 1 };
|
|
},
|
|
else => {
|
|
const answer = self + other;
|
|
const overflowed = !std.math.isFinite(answer);
|
|
return .{ .value = answer, .has_overflowed = overflowed };
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn exportAddWithOverflow(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) WithOverflow(T) {
|
|
return @call(.always_inline, addWithOverflow, .{ T, self, other });
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAddSaturatedInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = addWithOverflow(T, self, other);
|
|
if (result.has_overflowed) {
|
|
// We can unambiguously tell which way it wrapped, because we have N+1 bits including the overflow bit
|
|
if (result.value >= 0 and @typeInfo(T).Int.signedness == .signed) {
|
|
return std.math.minInt(T);
|
|
} else {
|
|
return std.math.maxInt(T);
|
|
}
|
|
} else {
|
|
return result.value;
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAddWrappedInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
return self +% other;
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportAddOrPanic(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = addWithOverflow(T, self, other);
|
|
if (result.has_overflowed) {
|
|
roc_panic("Integer addition overflowed!", 0);
|
|
} else {
|
|
return result.value;
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
fn subWithOverflow(comptime T: type, self: T, other: T) WithOverflow(T) {
|
|
switch (@typeInfo(T)) {
|
|
.Int => {
|
|
const answer = @subWithOverflow(self, other);
|
|
return .{ .value = answer[0], .has_overflowed = answer[1] == 1 };
|
|
},
|
|
else => {
|
|
const answer = self - other;
|
|
const overflowed = !std.math.isFinite(answer);
|
|
return .{ .value = answer, .has_overflowed = overflowed };
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn exportSubWithOverflow(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) WithOverflow(T) {
|
|
return @call(.always_inline, subWithOverflow, .{ T, self, other });
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportSubSaturatedInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = subWithOverflow(T, self, other);
|
|
if (result.has_overflowed) {
|
|
if (@typeInfo(T).Int.signedness == .unsigned) {
|
|
return 0;
|
|
} else if (self < 0) {
|
|
return std.math.minInt(T);
|
|
} else {
|
|
return std.math.maxInt(T);
|
|
}
|
|
} else {
|
|
return result.value;
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportSubWrappedInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
return self -% other;
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportSubOrPanic(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = subWithOverflow(T, self, other);
|
|
if (result.has_overflowed) {
|
|
roc_panic("Integer subtraction overflowed!", 0);
|
|
} else {
|
|
return result.value;
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
fn mulWithOverflow(comptime T: type, comptime W: type, self: T, other: T) WithOverflow(T) {
|
|
switch (@typeInfo(T)) {
|
|
.Int => {
|
|
if (T == i128) {
|
|
const is_answer_negative = (self < 0) != (other < 0);
|
|
const max = std.math.maxInt(i128);
|
|
const min = std.math.minInt(i128);
|
|
|
|
const self_u128 = @abs(self);
|
|
if (self_u128 > @as(u128, @intCast(std.math.maxInt(i128)))) {
|
|
if (other == 0) {
|
|
return .{ .value = 0, .has_overflowed = false };
|
|
} else if (other == 1) {
|
|
return .{ .value = self, .has_overflowed = false };
|
|
} else if (is_answer_negative) {
|
|
return .{ .value = min, .has_overflowed = true };
|
|
} else {
|
|
return .{ .value = max, .has_overflowed = true };
|
|
}
|
|
}
|
|
|
|
const other_u128 = @abs(other);
|
|
if (other_u128 > @as(u128, @intCast(std.math.maxInt(i128)))) {
|
|
if (self == 0) {
|
|
return .{ .value = 0, .has_overflowed = false };
|
|
} else if (self == 1) {
|
|
return .{ .value = other, .has_overflowed = false };
|
|
} else if (is_answer_negative) {
|
|
return .{ .value = min, .has_overflowed = true };
|
|
} else {
|
|
return .{ .value = max, .has_overflowed = true };
|
|
}
|
|
}
|
|
|
|
const answer256: U256 = mul_u128(self_u128, other_u128);
|
|
|
|
if (is_answer_negative) {
|
|
if (answer256.hi != 0 or answer256.lo > (1 << 127)) {
|
|
return .{ .value = min, .has_overflowed = true };
|
|
} else if (answer256.lo == (1 << 127)) {
|
|
return .{ .value = min, .has_overflowed = false };
|
|
} else {
|
|
return .{ .value = -@as(i128, @intCast(answer256.lo)), .has_overflowed = false };
|
|
}
|
|
} else {
|
|
if (answer256.hi != 0 or answer256.lo > @as(u128, @intCast(max))) {
|
|
return .{ .value = max, .has_overflowed = true };
|
|
} else {
|
|
return .{ .value = @as(i128, @intCast(answer256.lo)), .has_overflowed = false };
|
|
}
|
|
}
|
|
} else {
|
|
const self_wide: W = self;
|
|
const other_wide: W = other;
|
|
const answer: W = self_wide * other_wide;
|
|
|
|
const max: W = std.math.maxInt(T);
|
|
const min: W = std.math.minInt(T);
|
|
|
|
if (answer > max) {
|
|
return .{ .value = max, .has_overflowed = true };
|
|
} else if (answer < min) {
|
|
return .{ .value = min, .has_overflowed = true };
|
|
} else {
|
|
return .{ .value = @as(T, @intCast(answer)), .has_overflowed = false };
|
|
}
|
|
}
|
|
},
|
|
else => {
|
|
const answer = self * other;
|
|
const overflowed = !std.math.isFinite(answer);
|
|
return .{ .value = answer, .has_overflowed = overflowed };
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn exportMulWithOverflow(comptime T: type, comptime W: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) WithOverflow(T) {
|
|
return @call(.always_inline, mulWithOverflow, .{ T, W, self, other });
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportMulSaturatedInt(comptime T: type, comptime W: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = @call(.always_inline, mulWithOverflow, .{ T, W, self, other });
|
|
return result.value;
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportMulWrappedInt(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
return self *% other;
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn shiftRightZeroFillI128(self: i128, other: u8) callconv(.C) i128 {
|
|
if (other & 0b1000_0000 > 0) {
|
|
return 0;
|
|
} else {
|
|
return self >> @as(u7, @intCast(other));
|
|
}
|
|
}
|
|
|
|
pub fn shiftRightZeroFillU128(self: u128, other: u8) callconv(.C) u128 {
|
|
if (other & 0b1000_0000 > 0) {
|
|
return 0;
|
|
} else {
|
|
return self >> @as(u7, @intCast(other));
|
|
}
|
|
}
|
|
|
|
pub fn compareI128(self: i128, other: i128) callconv(.C) Ordering {
|
|
if (self == other) {
|
|
return Ordering.EQ;
|
|
} else if (self < other) {
|
|
return Ordering.LT;
|
|
} else {
|
|
return Ordering.GT;
|
|
}
|
|
}
|
|
|
|
pub fn compareU128(self: u128, other: u128) callconv(.C) Ordering {
|
|
if (self == other) {
|
|
return Ordering.EQ;
|
|
} else if (self < other) {
|
|
return Ordering.LT;
|
|
} else {
|
|
return Ordering.GT;
|
|
}
|
|
}
|
|
|
|
pub fn lessThanI128(self: i128, other: i128) callconv(.C) bool {
|
|
return self < other;
|
|
}
|
|
|
|
pub fn lessThanOrEqualI128(self: i128, other: i128) callconv(.C) bool {
|
|
return self <= other;
|
|
}
|
|
|
|
pub fn greaterThanI128(self: i128, other: i128) callconv(.C) bool {
|
|
return self > other;
|
|
}
|
|
|
|
pub fn greaterThanOrEqualI128(self: i128, other: i128) callconv(.C) bool {
|
|
return self >= other;
|
|
}
|
|
|
|
pub fn lessThanU128(self: u128, other: u128) callconv(.C) bool {
|
|
return self < other;
|
|
}
|
|
|
|
pub fn lessThanOrEqualU128(self: u128, other: u128) callconv(.C) bool {
|
|
return self <= other;
|
|
}
|
|
|
|
pub fn greaterThanU128(self: u128, other: u128) callconv(.C) bool {
|
|
return self > other;
|
|
}
|
|
|
|
pub fn greaterThanOrEqualU128(self: u128, other: u128) callconv(.C) bool {
|
|
return self >= other;
|
|
}
|
|
|
|
pub fn exportMulOrPanic(comptime T: type, comptime W: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T, other: T) callconv(.C) T {
|
|
const result = @call(.always_inline, mulWithOverflow, .{ T, W, self, other });
|
|
if (result.has_overflowed) {
|
|
roc_panic("Integer multiplication overflowed!", 0);
|
|
} else {
|
|
return result.value;
|
|
}
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportCountLeadingZeroBits(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T) callconv(.C) u8 {
|
|
return @as(u8, @clz(self));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportCountTrailingZeroBits(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T) callconv(.C) u8 {
|
|
return @as(u8, @ctz(self));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn exportCountOneBits(comptime T: type, comptime name: []const u8) void {
|
|
const f = struct {
|
|
fn func(self: T) callconv(.C) u8 {
|
|
return @as(u8, @popCount(self));
|
|
}
|
|
}.func;
|
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .strong });
|
|
}
|
|
|
|
pub fn f32ToParts(self: f32) callconv(.C) F32Parts {
|
|
const u32Value = @as(u32, @bitCast(self));
|
|
return F32Parts{
|
|
.fraction = u32Value & 0x7fffff,
|
|
.exponent = @truncate(u32Value >> 23 & 0xff),
|
|
.sign = u32Value >> 31 & 1 == 1,
|
|
};
|
|
}
|
|
|
|
pub fn f64ToParts(self: f64) callconv(.C) F64Parts {
|
|
const u64Value = @as(u64, @bitCast(self));
|
|
return F64Parts{
|
|
.fraction = u64Value & 0xfffffffffffff,
|
|
.exponent = @truncate(u64Value >> 52 & 0x7ff),
|
|
.sign = u64Value >> 63 & 1 == 1,
|
|
};
|
|
}
|
|
|
|
pub fn f32FromParts(parts: F32Parts) callconv(.C) f32 {
|
|
return @as(f32, @bitCast(parts.fraction & 0x7fffff | (@as(u32, parts.exponent) << 23) | (@as(u32, @intFromBool(parts.sign)) << 31)));
|
|
}
|
|
|
|
pub fn f64FromParts(parts: F64Parts) callconv(.C) f64 {
|
|
return @as(f64, @bitCast(parts.fraction & 0xfffffffffffff | (@as(u64, parts.exponent & 0x7ff) << 52) | (@as(u64, @intFromBool(parts.sign)) << 63)));
|
|
}
|