isqrt: add integer square root builtin to all three impls
Semantics: (isqrt n) → floor(sqrt(n)). Negative argument errors.
Matches Python 3.8+ math.isqrt and R7RS exact-integer-sqrt contract.
- Python: wraps math.isqrt via lambda registration
- C: hand-rolled bit-by-bit algorithm in bi_isqrt (O(log n), no FPU)
- asm: new BI_ISQRT=109, bit-by-bit algorithm in integer registers
(%r8/%r9/%r10). Negative input → stderr + exit(1) like other
errors. GC builtin constants bumped to 110-114.
Tests:
- tests/functional.lsp: 7 shared tests (0, 1, perfect squares, floor
cases, large values). Python + C now 196 each (was 189).
- asm/test.sh: 5 asm-local tests. asm suite now 142 (was 137).
MOAD: all three implementations O(log n), no O(N²) hazards.
This commit is contained in:
parent
f57de49f7b
commit
6e9d3ea52f
9 changed files with 98 additions and 8 deletions
BIN
asm/lumbda
BIN
asm/lumbda
Binary file not shown.
BIN
asm/lumbda-gc
BIN
asm/lumbda-gc
Binary file not shown.
BIN
asm/lumbda-gc.o
BIN
asm/lumbda-gc.o
Binary file not shown.
BIN
asm/lumbda.o
BIN
asm/lumbda.o
Binary file not shown.
73
asm/lumbda.s
73
asm/lumbda.s
|
|
@ -194,15 +194,16 @@
|
|||
.equ BI_HS_SIZE, 106
|
||||
.equ BI_HS_LIST, 107
|
||||
.equ BI_TCPSENDFILE, 108
|
||||
.equ BI_ISQRT, 109
|
||||
.ifdef GC_NAIVE
|
||||
.equ BI_GC_COLLECT, 109
|
||||
.equ BI_GC_STATS, 110
|
||||
.equ BI_WITH_ARENA, 111
|
||||
.equ BI_ARENA_STATS, 112
|
||||
.equ BI_ARENA_SET_MODE, 113
|
||||
.equ BI_COUNT, 114
|
||||
.equ BI_GC_COLLECT, 110
|
||||
.equ BI_GC_STATS, 111
|
||||
.equ BI_WITH_ARENA, 112
|
||||
.equ BI_ARENA_STATS, 113
|
||||
.equ BI_ARENA_SET_MODE, 114
|
||||
.equ BI_COUNT, 115
|
||||
.else
|
||||
.equ BI_COUNT, 109
|
||||
.equ BI_COUNT, 110
|
||||
.endif
|
||||
|
||||
# ============================================================
|
||||
|
|
@ -299,6 +300,7 @@ bn_listp: .byte 5; .ascii "list?"
|
|||
bn_substr: .byte 9; .ascii "substring"
|
||||
bn_expt: .byte 4; .ascii "expt"
|
||||
bn_gcd: .byte 3; .ascii "gcd"
|
||||
bn_isqrt: .byte 5; .ascii "isqrt"
|
||||
bn_integerp: .byte 8; .ascii "integer?"
|
||||
bn_portalsave: .byte 11; .ascii "portal-save"
|
||||
bn_portalresume:.byte 13; .ascii "portal-resume"
|
||||
|
|
@ -388,6 +390,7 @@ bi_names:
|
|||
.quad bn_htexists, bn_htsize, bn_htkeys, bn_htvals, bn_htalist
|
||||
.quad bn_hsmake, bn_hsp, bn_hsadd, bn_hshas, bn_hssize, bn_hslist
|
||||
.quad bn_tcpsendfile
|
||||
.quad bn_isqrt
|
||||
.ifdef GC_NAIVE
|
||||
.quad bn_gccollect, bn_gcstats, bn_witharena, bn_arenastats, bn_arenamode
|
||||
.endif
|
||||
|
|
@ -399,6 +402,8 @@ err_notproc: .ascii "Error: not a procedure\n"
|
|||
.equ err_notproc_len, . - err_notproc
|
||||
err_oom: .ascii "Error: out of memory\n"
|
||||
.equ err_oom_len, . - err_oom
|
||||
err_isqrt_neg: .ascii "Error: isqrt: negative argument\n"
|
||||
.equ err_isqrt_neg_len, . - err_isqrt_neg
|
||||
|
||||
# Print strings
|
||||
s_true: .ascii "#t"
|
||||
|
|
@ -3355,6 +3360,8 @@ eval_list:
|
|||
je bi_expt
|
||||
cmpq $BI_GCD, %rax
|
||||
je bi_gcd
|
||||
cmpq $BI_ISQRT, %rax
|
||||
je bi_isqrt
|
||||
cmpq $BI_INTEGERP, %rax
|
||||
je bi_integerp
|
||||
cmpq $BI_PORTALSAVE, %rax
|
||||
|
|
@ -4663,6 +4670,58 @@ bi_gcd:
|
|||
call make_int
|
||||
RET_VAL
|
||||
|
||||
# bi_isqrt: floor of integer square root (bit-by-bit, no FPU).
|
||||
# Negative input -> error exit. Registers: %r8 = x, %r9 = res, %r10 = bit.
|
||||
bi_isqrt:
|
||||
GETARG %rax
|
||||
sarq $3, %rax # untag
|
||||
testq %rax, %rax
|
||||
js .bisqrt_neg
|
||||
cmpq $2, %rax
|
||||
jl .bisqrt_small # v < 2 -> result is v itself
|
||||
movq %rax, %r8 # x
|
||||
xorq %r9, %r9 # res
|
||||
movq $1, %r10
|
||||
shlq $62, %r10 # bit = 1<<62
|
||||
.bisqrt_align:
|
||||
cmpq %r8, %r10
|
||||
jbe .bisqrt_loop
|
||||
shrq $2, %r10
|
||||
jmp .bisqrt_align
|
||||
.bisqrt_loop:
|
||||
testq %r10, %r10
|
||||
jz .bisqrt_ret
|
||||
movq %r9, %rcx
|
||||
addq %r10, %rcx # rcx = res + bit
|
||||
cmpq %rcx, %r8
|
||||
jb .bisqrt_no
|
||||
subq %rcx, %r8 # x -= res + bit
|
||||
shrq $1, %r9
|
||||
addq %r10, %r9 # res = (res>>1) + bit
|
||||
jmp .bisqrt_next
|
||||
.bisqrt_no:
|
||||
shrq $1, %r9 # res >>= 1
|
||||
.bisqrt_next:
|
||||
shrq $2, %r10 # bit >>= 2
|
||||
jmp .bisqrt_loop
|
||||
.bisqrt_ret:
|
||||
movq %r9, %rdi
|
||||
call make_int
|
||||
RET_VAL
|
||||
.bisqrt_small:
|
||||
movq %rax, %rdi
|
||||
call make_int
|
||||
RET_VAL
|
||||
.bisqrt_neg:
|
||||
movq $SYS_WRITE, %rax
|
||||
movq $2, %rdi
|
||||
leaq err_isqrt_neg(%rip), %rsi
|
||||
movq $err_isqrt_neg_len, %rdx
|
||||
syscall
|
||||
movq $SYS_EXIT, %rax
|
||||
movq $1, %rdi
|
||||
syscall
|
||||
|
||||
bi_vector:
|
||||
# (vector e1 e2 ...) — build from remaining args in %r12
|
||||
# Count args
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ check "max" "(max 3 1 4)" "4"
|
|||
check "expt" "(* 2 2 2 2 2 2 2 2 2 2)" "1024"
|
||||
check "div" "(/ 10 2)" "5"
|
||||
check "expt-bi" "(expt 2 10)" "1024"
|
||||
check "isqrt-0" "(isqrt 0)" "0"
|
||||
check "isqrt-1" "(isqrt 1)" "1"
|
||||
check "isqrt-perfect" "(isqrt 144)" "12"
|
||||
check "isqrt-floor" "(isqrt 10)" "3"
|
||||
check "isqrt-big" "(isqrt 1000000000000)" "1000000"
|
||||
check "odd?" "(odd? 3)" "#t"
|
||||
check "odd?-even" "(odd? 4)" "#f"
|
||||
check "even?" "(even? 4)" "#t"
|
||||
|
|
|
|||
20
c/builtins.c
20
c/builtins.c
|
|
@ -70,6 +70,24 @@ static Value bi_div(Value *a, int n, Env *e) {
|
|||
}
|
||||
|
||||
MATH_1(sqrt, sqrt)
|
||||
|
||||
static Value bi_isqrt(Value *a, int n, Env *e) {
|
||||
(void)e; CHECK_ARITY("isqrt", 1);
|
||||
int64_t v = as_number_int(AS_NUM(a[0]));
|
||||
if (v < 0) lisp_error("isqrt: negative argument: %lld", (long long)v);
|
||||
if (v < 2) return VAL_INT(v);
|
||||
uint64_t x = (uint64_t)v;
|
||||
uint64_t res = 0;
|
||||
uint64_t bit = (uint64_t)1 << 62;
|
||||
while (bit > x) bit >>= 2;
|
||||
while (bit != 0) {
|
||||
if (x >= res + bit) { x -= res + bit; res = (res >> 1) + bit; }
|
||||
else { res >>= 1; }
|
||||
bit >>= 2;
|
||||
}
|
||||
return VAL_INT((int64_t)res);
|
||||
}
|
||||
|
||||
MATH_1(sin, sin)
|
||||
MATH_1(cos, cos)
|
||||
MATH_1(tan, tan)
|
||||
|
|
@ -1752,7 +1770,7 @@ Env *make_global_env(void) {
|
|||
DEF("expt", bi_expt); DEF("abs", bi_abs);
|
||||
DEF("floor", bi_floor); DEF("ceiling", bi_ceiling); DEF("round", bi_round);
|
||||
DEF("truncate", bi_truncate);
|
||||
DEF("sqrt", bi_sqrt); DEF("log", bi_log); DEF("exp", bi_exp);
|
||||
DEF("sqrt", bi_sqrt); DEF("isqrt", bi_isqrt); DEF("log", bi_log); DEF("exp", bi_exp);
|
||||
DEF("sin", bi_sin); DEF("cos", bi_cos); DEF("tan", bi_tan);
|
||||
DEF("asin", bi_asin); DEF("acos", bi_acos); DEF("atan", bi_atan);
|
||||
DEF("min", bi_min); DEF("max", bi_max);
|
||||
|
|
|
|||
|
|
@ -2871,6 +2871,7 @@ def make_global_env():
|
|||
d(S('floor/'), lambda a, _: (math.floor(_num(a[0]) / _num(a[1])),
|
||||
_num(a[0]) - _num(a[1]) * math.floor(_num(a[0]) / _num(a[1]))))
|
||||
d(S('sqrt'), lambda a, _: math.sqrt(_num(a[0])))
|
||||
d(S('isqrt'), lambda a, _: math.isqrt(int(_num(a[0]))))
|
||||
d(S('log'), lambda a, _: math.log(_num(a[0])) if len(a) == 1 else math.log(_num(a[0]), _num(a[1])))
|
||||
d(S('exp'), lambda a, _: math.exp(_num(a[0])))
|
||||
d(S('sin'), lambda a, _: math.sin(_num(a[0])))
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@
|
|||
(assert-equal "max" (max 3 1 4 1 5) 5)
|
||||
(assert-equal "modulo" (modulo 10 3) 1)
|
||||
(assert-equal "expt" (expt 2 10) 1024)
|
||||
(assert-equal "isqrt-0" (isqrt 0) 0)
|
||||
(assert-equal "isqrt-1" (isqrt 1) 1)
|
||||
(assert-equal "isqrt-perfect" (isqrt 144) 12)
|
||||
(assert-equal "isqrt-floor" (isqrt 10) 3)
|
||||
(assert-equal "isqrt-just-below" (isqrt 99) 9)
|
||||
(assert-equal "isqrt-just-above" (isqrt 101) 10)
|
||||
(assert-equal "isqrt-large" (isqrt 1000000000000) 1000000)
|
||||
|
||||
;;; ═══════════════════════════════════════════════════════════════
|
||||
;;; Comparison
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue