diff --git a/asm/lumbda b/asm/lumbda index 8fccc7c..6cf4790 100755 Binary files a/asm/lumbda and b/asm/lumbda differ diff --git a/asm/lumbda-gc b/asm/lumbda-gc index 5214f87..55eba6e 100755 Binary files a/asm/lumbda-gc and b/asm/lumbda-gc differ diff --git a/asm/lumbda-gc.o b/asm/lumbda-gc.o index 16533c0..29f33f3 100644 Binary files a/asm/lumbda-gc.o and b/asm/lumbda-gc.o differ diff --git a/asm/lumbda.o b/asm/lumbda.o index 0089363..8e1a7e1 100644 Binary files a/asm/lumbda.o and b/asm/lumbda.o differ diff --git a/asm/lumbda.s b/asm/lumbda.s index e873157..0d2fd50 100644 --- a/asm/lumbda.s +++ b/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 diff --git a/asm/test.sh b/asm/test.sh index 73c55f4..d11bfe6 100644 --- a/asm/test.sh +++ b/asm/test.sh @@ -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" diff --git a/c/builtins.c b/c/builtins.c index 16b64a6..49f5872 100644 --- a/c/builtins.c +++ b/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); diff --git a/lumbda.py b/lumbda.py index 6cfdce1..14600d6 100644 --- a/lumbda.py +++ b/lumbda.py @@ -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]))) diff --git a/tests/functional.lsp b/tests/functional.lsp index 34ac7da..8d16486 100644 --- a/tests/functional.lsp +++ b/tests/functional.lsp @@ -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