# Integer Math Standard Library for Seq
#
# Common mathematical operations for integer arithmetic.
#
# ## Usage
#
# include std:imath
#
# : main ( -- Int )
# -5 abs int->string io.write-line
# 0
# ;
#
# ## Available Functions
#
# - abs: ( Int -- Int ) - Absolute value
# - max: ( Int Int -- Int ) - Maximum of two values
# - min: ( Int Int -- Int ) - Minimum of two values
# - mod: ( Int Int -- Int Bool ) - Modulo operation (a mod b), returns success flag
# - gcd: ( Int Int -- Int ) - Greatest common divisor (Euclidean algorithm)
# - pow: ( Int Int -- Int ) - Power (base^exp) - WARNING: Not tail-recursive
# - sign: ( Int -- Int ) - Sign function: returns -1, 0, or 1
# - square: ( Int -- Int ) - Square a number
# - clamp: ( Int min max -- Int ) - Clamp value between min and max
# - int->bits: ( Int -- String ) - Binary string, minimum width, no leading zeros
# - int->bits-padded: ( Int Int -- String ) - Binary string padded/truncated to N bits
#
# ## Notes
#
# - All operations use wrapping integer arithmetic (i64)
# - Recursive functions (gcd, pow) may stack overflow for very large inputs
# - Division by zero will panic at runtime
# - int->bits and int->bits-padded are defined for non-negative ints only.
# Seq Ints are 63-bit tagged; logical shr of a negative cannot reduce to
# zero under the current tag scheme, so int->bits will not terminate on
# negative input. See docs/design/TAGGED_INT_BITWISE.md.
# - int->bits-padded truncates to the low `width` bits when width is
# smaller than the value's bit length
#
# ## Examples
#
# 48 18 gcd # Returns 6
# 2 10 pow # Returns 1024
# 15 0 100 clamp # Returns 15 (within range)
# -5 abs # Returns 5
# 10 int->bits # Returns "1010"
# 10 8 int->bits-padded # Returns "00001010"
# 255 4 int->bits-padded # Returns "1111" (low 4 bits)
#
# Absolute value
# Both branches must have identical stack effects for type checker
: abs ( Int -- Int )
dup dup 0 i.< [
# Stack: n n, n < 0, so compute 0 - n
nip 0 swap i.subtract
] [
# Stack: n n, n >= 0, keep original
drop
] if
;
# Maximum of two values
: max ( Int Int -- Int )
2dup i.> [
drop
] [
nip
] if
;
# Minimum of two values
: min ( Int Int -- Int )
2dup i.< [
drop
] [
nip
] if
;
# Modulo operation: a mod b
# Uses i.modulo which returns (Int Bool) for error handling
: mod ( Int Int -- Int Bool )
i.modulo
;
# Greatest common divisor (Euclidean algorithm)
# Note: mod returns (Int Bool), we assume valid inputs (non-zero divisor)
: gcd ( Int Int -- Int )
dup 0 i.= [
drop
] [
2dup mod # ( a b r success )
drop # drop success flag (assume valid)
rot drop # ( b r )
gcd
] if
;
# Power (base^exp using recursion)
: pow ( Int Int -- Int )
dup 0 i.= [
drop drop 1
] [
dup 1 i.= [
drop
] [
over
swap 1 i.subtract
pow
i.multiply
] if
] if
;
# Sign function: returns -1, 0, or 1
: sign ( Int -- Int )
dup 0 i.= [
drop 0
] [
dup 0 i.< [
drop 0 1 i.subtract
] [
drop 1
] if
] if
;
# Square
: square ( Int -- Int )
dup i.multiply
;
# Clamp value between min and max
# Stack: ( value min max -- clamped )
# Returns: min if value < min, max if value > max, else value
: clamp ( Int Int Int -- Int )
# Stack: value min max
rot rot
# Stack: max value min
2dup i.< [
# value < min, return min
nip nip
] [
# value >= min
drop
# Stack: max value
2dup i.> [
# value > max, return max
nip
] [
# min <= value <= max, return value
drop
] if
] if
;
# Internal: build bits from least-significant to most, prepending each
# bit-char so the accumulator reads MSB-first. Tail-recursive (TCO).
# Stack: ( n acc -- String )
: int->bits-loop ( Int String -- String )
over 0 i.= [
nip
] [
over 1 band int->string swap string.concat
swap 1 shr swap
int->bits-loop
] if
;
# Convert an Int to its binary-string representation (no leading zeros).
# Negative values render as the full 64-bit two's-complement pattern,
# because shr is logical (zero-fill).
: int->bits ( Int -- String )
dup 0 i.= [
drop "0"
] [
"" int->bits-loop
] if
;
# Internal: same shape as int->bits-loop but driven by a width counter
# instead of n reaching zero. Truncates to the low `width` bits.
# Stack: ( n width acc -- String )
: int->bits-padded-loop ( Int Int String -- String )
over 0 i.<= [
nip nip
] [
2 pick 1 band int->string swap string.concat
swap 1 i.- swap
rot 1 shr rot rot
int->bits-padded-loop
] if
;
# Convert an Int to a binary string padded/truncated to exactly `width` bits.
# width <= 0 yields the empty string.
: int->bits-padded ( Int Int -- String )
"" int->bits-padded-loop
;