Skip to content

Deterministic IntegerMath #481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,39 @@ interface IMath<T> {
trunc(x: T): T;
}

interface IIntegerMath {
/** Returns the absolute value of `x`. */
abs<T>(x: T): T;
/** Returns the number of leading zero bits in the 32-bit or 64-bit binary representation of `x`. */
clz<T>(x: T): T;
/** Returns the number of leading zero bits in the 32-bit binary representation of `x`. */
clz32<T>(x: T): T;
/** Returns the lowest-valued number of its arguments. */
min<T>(x: T, y: T): T;
/** Returns the largest-valued number of its arguments. */
max<T>(x: T, y: T): T;
/** Returns same value as input without changes. */
ceil<T>(x: T): T;
/** Returns same value as input without changes. */
floor<T>(x: T): T;
/** Returns same value as input without changes. */
round<T>(x: T): T;
/** Returns same value as input without changes. */
trunc<T>(x: T): T;
/** Returns the result of the C-like 32-bit multiplication of `a` and `b`. */
imul<T>(x: T, y: T): T;
/** Returns `base` to the power of `exponent`. */
pow<T>(base: T, exponent: T): T;
/** Returns the base 2 logarithm of `x` as `floor(log2(x))` */
log2<T>(x: T): T;
/** Returns the sign of `x`, indicating whether the number is positive, negative or zero. */
sign<T>(x: T): i32;
/** Returns whether the sign bit of `x` is set */
signbit<T>(x: T): bool;
/** Returns the square root of `x` as `floor(sqrt(x))`. */
sqrt<T>(x: T): T;
}

interface INativeMath<T> extends IMath<T> {
/** Contains sin value produced after Math/Mathf.sincos */
sincos_sin: T;
Expand All @@ -1427,6 +1460,8 @@ declare const NativeMathf: INativeMath<f32>;
declare const Math: IMath<f64>;
/** Alias of {@link NativeMathf} or {@link JSMath} respectively. Defaults to `NativeMathf`. */
declare const Mathf: IMath<f32>;
/** Integer math implemented natively. */
declare const IntegerMath: IIntegerMath;

declare class Date {
/** Returns the UTC timestamp in milliseconds of the specified date. */
Expand Down
188 changes: 188 additions & 0 deletions std/assembly/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,194 @@ export namespace NativeMathf {
}
}

export namespace IntegerMath {

@inline
export function abs<T>(x: T): T {
if (isInteger<T>()) {
return builtin_abs<T>(x);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function clz<T>(x: T): T {
if (isInteger<T>()) {
return builtin_clz<T>(x);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function clz32<T>(x: T): T {
if (isInteger<T>()) {
return builtin_clz(<u32>x);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function min<T>(x: T, y: T): T {
if (isInteger<T>()) {
return builtin_min<T>(x, y);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function max<T>(x: T, y: T): T {
if (isInteger<T>()) {
return builtin_max<T>(x, y);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function ceil<T>(x: T): T {
return x;
}

@inline
export function floor<T>(x: T): T {
return x;
}

@inline
export function round<T>(x: T): T {
return x;
}

@inline
export function trunc<T>(x: T): T {
return x;
}

@inline
export function imul<T>(x: T, y: T): T {
if (isInteger<T>()) {
return <T>(<i32>x * <i32>y);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function pow<T>(x: T, y: i32): T {
if (isInteger<T>()) {
if (sizeof<T>() == 8) return <T>ipow64(<i64>x, y);
if (sizeof<T>() <= 4) return <T>ipow32(<i32>x, y);
}
throw new TypeError("Unexpected generic type");
}

// floor(log2(x))
@inline
export function log2<T>(x: T): T {
if (isInteger<T>()) {
if (isSigned<T>()) {
if (x <= 0) throw new RangeError("Math.log2 received negative or zero argument");
} else {
if (!x) throw new RangeError("Math.log2 received zero argument");
}
if (sizeof<T>() <= 4) return <T>ilog2_32(<u32>x);
if (sizeof<T>() == 8) return <T>ilog2_64(<u64>x);
}
throw new TypeError("Unexpected generic type");
}

@inline
export function sign<T>(x: T): i32 {
if (isInteger<T>()) {
if (isSigned<T>()) {
return <i32>(x > 0) - <i32>(x < 0);
} else {
return <i32>(x != 0);
}
}
throw new TypeError("Unexpected generic type");
}

@inline
export function signbit<T>(x: T): bool {
if (isInteger<T>()) {
if (isSigned<T>()) {
return x < 0;
} else {
return false;
}
}
throw new TypeError("Unexpected generic type");
}

@inline
export function sqrt<T>(x: T): T {
if (isInteger<T>()) {
if (isSigned<T>()) {
if (x < 0) throw new RangeError("Math.sqrt received negative argument");
}
if (sizeof<T>() <= 4) return <T>isqrt32(<u32>x);
if (sizeof<T>() == 8) return <T>isqrt64(<u64>x);
}
throw new TypeError("Unexpected generic type");
}
}

@inline
export function ilog2_32(x: u32): u32 {
return 31 - builtin_clz(x);
}

@inline
export function ilog2_64(x: u64): u64 {
return 63 - builtin_clz(x);
}

// Complexity: O(log n)
// Iterative version of approach described in this article:
// https://www.cs.uni-potsdam.de/ti/kreitz/PDF/03cucs-intsqrt.pdf
export function isqrt32(x: u32): u32 {
if (x < 2) return x;
var s = 2;
var xs = x >> 2;
while (xs && xs != x) {
s += 2;
xs = x >> s;
}
s -= 2;
var ux = x;
var res: u32 = 0;
while (s >= 0) {
res <<= 1;
let m = res + 1;
if (m * m <= ux >> s) {
res = m;
}
s -= 2;
}
return res;
}

export function isqrt64(x: u64): u64 {
if (x < 2) return x;
var s = 2;
var xs = x >> 2;
while (xs && xs != x) {
s += 2;
xs = x >> s;
}
s -= 2;
var ux = x;
var res: u64 = 0;
while (s >= 0) {
res <<= 1;
let m = res + 1;
if (m * m <= ux >> s) {
res = m;
}
s -= 2;
}
return res;
}

export function ipow32(x: i32, e: i32): i32 {
var out = 1;
if (ASC_SHRINK_LEVEL < 1) {
Expand Down
Loading