~ chicken-core (chicken-5) 98bac9be2fd53bec014f273bca0b389915129cf5
commit 98bac9be2fd53bec014f273bca0b389915129cf5 Author: Peter Bex <peter@more-magic.net> AuthorDate: Sat Jan 31 16:47:00 2015 +0100 Commit: Peter Bex <peter@more-magic.net> CommitDate: Sun May 31 14:14:25 2015 +0200 Fix sqrt and signum to accept extended numbers (behaving like CL on cplxnums). Add exact-integer-sqrt and exact-integer-nth-root for exact root finding. diff --git a/chicken.h b/chicken.h index 1b52ef33..bb1fef12 100644 --- a/chicken.h +++ b/chicken.h @@ -1276,6 +1276,7 @@ extern double trunc(double); /* XXX TODO: This should probably be renamed C_u_fixnum_abs or something */ #define C_fixnum_abs(n) C_fix(abs(C_unfix(n))) #define C_a_i_fixnum_abs(ptr, n, x) (((x) & C_INT_SIGN_BIT) ? C_a_i_fixnum_negate((ptr), (n), (x)) : (x)) +#define C_i_fixnum_signum(x) ((x) == C_fix(0) ? (x) : (((x) & C_INT_SIGN_BIT) ? C_fix(-1) : C_fix(1))) #define C_i_fixnum_length(x) C_fix(C_ilen(((x) & C_INT_SIGN_BIT) ? ~C_unfix(x) : C_unfix(x))) #define C_flonum_equalp(n1, n2) C_mk_bool(C_flonum_magnitude(n1) == C_flonum_magnitude(n2)) @@ -1289,6 +1290,7 @@ extern double trunc(double); #define C_a_i_flonum_times(ptr, c, n1, n2) C_flonum(ptr, C_flonum_magnitude(n1) * C_flonum_magnitude(n2)) #define C_a_i_flonum_quotient(ptr, c, n1, n2) C_flonum(ptr, C_flonum_magnitude(n1) / C_flonum_magnitude(n2)) #define C_a_i_flonum_negate(ptr, c, n) C_flonum(ptr, -C_flonum_magnitude(n)) +#define C_a_u_i_flonum_signum(ptr, n, x) (C_flonum_magnitude(x) == 0.0 ? (x) : ((C_flonum_magnitude(x) < 0.0) ? C_flonum(ptr, -1.0) : C_flonum(ptr, 1.0))) #define C_a_i_address_to_pointer(ptr, c, addr) C_mpointer(ptr, (void *)C_num_to_unsigned_int(addr)) #define C_a_i_pointer_to_address(ptr, c, pptr) C_unsigned_int_to_num(ptr, (unsigned int)C_c_pointer_nn(pptr)) @@ -1875,6 +1877,7 @@ C_fctimport void C_ccall C_invalid_procedure(int c, C_word self, ...) C_noret; C_fctexport void C_ccall C_stop_timer(C_word c, C_word closure, C_word k) C_noret; C_fctexport void C_ccall C_abs(C_word c, C_word self, C_word k, C_word x) C_noret; C_fctexport void C_ccall C_u_integer_abs(C_word c, C_word self, C_word k, C_word x) C_noret; +C_fctexport void C_ccall C_signum(C_word c, C_word self, C_word k, C_word x) C_noret; C_fctexport void C_ccall C_apply(C_word c, C_word closure, C_word k, C_word fn, ...) C_noret; C_fctexport void C_ccall C_do_apply(C_word n, C_word closure, C_word k) C_noret; C_fctexport void C_ccall C_call_cc(C_word c, C_word closure, C_word k, C_word cont) C_noret; @@ -2841,6 +2844,11 @@ C_inline C_word C_i_flonum_max(C_word x, C_word y) return xf > yf ? x : y; } +C_inline C_word C_u_i_integer_signum(C_word x) +{ + if (x & C_FIXNUM_BIT) return C_i_fixnum_signum(x); + else return (C_bignum_negativep(x) ? C_fix(-1) : C_fix(1)); +} C_inline C_word C_a_i_flonum_quotient_checked(C_word **ptr, int c, C_word n1, C_word n2) diff --git a/chicken.import.scm b/chicken.import.scm index 47ab3c4e..1e324257 100644 --- a/chicken.import.scm +++ b/chicken.import.scm @@ -74,6 +74,8 @@ errno error exact-integer? + exact-integer-sqrt + exact-integer-nth-root exit exit-handler expand diff --git a/library.scm b/library.scm index bd5a6cf6..0d84496e 100644 --- a/library.scm +++ b/library.scm @@ -319,6 +319,11 @@ EOF (unless (##core#inline "C_i_exact_integerp" x) (##sys#error-bad-exact-integer x (and (pair? loc) (car loc))) ) ) +(define (##sys#check-exact-uinteger x . loc) + (when (or (not (##core#inline "C_i_exact_integerp" x)) + (##core#inline "C_i_integer_negativep" x)) + (##sys#error-bad-exact-uinteger x (and (pair? loc) (car loc))) ) ) + (define (##sys#check-real x . loc) (unless (##core#inline "C_i_realp" x) (##sys#error-bad-real x (and (pair? loc) (car loc))) ) ) @@ -454,6 +459,10 @@ EOF (##sys#error-hook (foreign-value "C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR" int) loc arg)) +(define (##sys#error-bad-exact-uinteger arg #!optional loc) + (##sys#error-hook + (foreign-value "C_BAD_ARGUMENT_TYPE_NO_UINTEGER_ERROR" int) loc arg)) + (define (##sys#error-bad-inexact arg #!optional loc) (##sys#error-hook (foreign-value "C_CANT_REPRESENT_INEXACT_ERROR" int) loc arg)) @@ -1073,10 +1082,13 @@ EOF (##sys#integer-times b/g1 (##sys#integer-quotient d g2))))))))) -(define (signum n) - (cond ((> n 0) (if (##sys#exact? n) 1 1.0)) - ((< n 0) (if (##sys#exact? n) -1 -1.0)) - (else (if (##sys#exact? n) 0 0.0) ) ) ) +(define (##sys#extended-signum x) + (cond + ((ratnum? x) (##core#inline "C_u_i_integer_signum" (%ratnum-numerator x))) + ((cplxnum? x) (make-polar 1 (angle x))) + (else (##sys#error-bad-number x 'signum)))) + +(define signum (##core#primitive "C_signum")) (define (flonum->ratnum x) ;; Try to multiply by two until we reach an integer @@ -1143,8 +1155,8 @@ EOF ;; minimum flonum exponent. (let* ((s (min (fx- flonum-precision 1) (fx- e minimum-denorm-flonum-expt))) - (normalized (##sys#/-2 (arithmetic-shift n s) d)) - (r (round normalized)) + (norm (##sys#/-2 (##sys#integer-shift n s) d)) + (r (round norm)) (fraction (exact->inexact r)) (exp (fx- e s))) (let ((res (fp* fraction (expt 2.0 exp)))) @@ -1152,15 +1164,15 @@ EOF (scale (lambda (n d) ; Here, 1/2 <= n/d < 2 [N3] (if (##sys#<-2 n d) ; n/d < 1? ;; Scale left [N3]; only needed once (see note in M3) - (rnd (arithmetic-shift n 1) d (fx- e 1)) + (rnd (##sys#integer-shift n 1) d (fx- e 1)) ;; Already normalized (rnd n d e))))) ;; After this step, which shifts the smaller number to ;; align with the larger, "f" in algorithm N is represented ;; in the procedures above by n/d. (if (negative? e) - (scale (arithmetic-shift an (##sys#--2 0 e)) d1) - (scale an (arithmetic-shift d1 e))))) + (scale (##sys#integer-shift an (##sys#--2 0 e)) d1) + (scale an (##sys#integer-shift d1 e))))) ((cplxnum? x) (make-complex (exact->inexact (%cplxnum-real x)) (exact->inexact (%cplxnum-imag x)))) @@ -1596,15 +1608,100 @@ EOF (##core#inline_allocate ("C_a_i_atan" 4) (exact->inexact n))))) -;; TODO: replace this with an actual sqrt implementation -(define (##sys#sqrt/loc loc x) - (##core#inline_allocate ("C_a_i_sqrt" 4) n)) +;; This is "Karatsuba Square Root" as described by Paul Zimmermann, +;; which is 3/2K(n) + O(n log n) for an input of 2n words, where K(n) +;; is the number of operations performed by Karatsuba multiplication. +(define (##sys#exact-integer-sqrt a) + ;; Because we assume a3b+a2 >= b^2/4, we must check a few edge cases: + (if (and (fixnum? a) (fx<= a 4)) + (case a + ((0 1) (values a 0)) + ((2) (values 1 1)) + ((3) (values 1 2)) + ((4) (values 2 0)) + (else (error "this should never happen"))) + (let*-values + (((len/4) (fxshr (fx+ (integer-length a) 1) 2)) + ((len/2) (fxshl len/4 1)) + ((s^ r^) (##sys#exact-integer-sqrt + (##sys#integer-shift a (fxneg len/2)))) + ((mask) (##sys#--2 (##sys#integer-shift 1 len/4) 1)) + ((a0) (##sys#integer-bitwise-and a mask)) + ((a1) (##sys#integer-bitwise-and + (##sys#integer-shift a (fxneg len/4)) mask)) + ((q u) (##sys#integer-quotient&remainder + (##sys#+-2 (arithmetic-shift r^ len/4) a1) + (##sys#integer-shift s^ 1))) + ((s) (##sys#+-2 (##sys#integer-shift s^ len/4) q)) + ((r) (##sys#+-2 (##sys#integer-shift u len/4) + (##sys#--2 a0 (##sys#*-2 q q))))) + (if (negative? r) + (values (##sys#--2 s 1) + (##sys#--2 (##sys#+-2 r (##sys#integer-shift s 1)) 1)) + (values s r))))) + +(define (exact-integer-sqrt x) + (##sys#check-exact-uinteger x 'exact-integer-sqrt) + (##sys#exact-integer-sqrt x)) + +;; This procedure is so large because it tries very hard to compute +;; exact results if at all possible. +(define (##sys#sqrt/loc loc n) + (cond ((cplxnum? n) ; Must be checked before we call "negative?" + (let ((p (##sys#/-2 (angle n) 2)) + (m (##core#inline_allocate ("C_a_i_sqrt" 4) (magnitude n))) ) + (make-complex (##sys#*-2 m (cos p)) (##sys#*-2 m (sin p)) ) )) + ((negative? n) + (make-complex .0 (##core#inline_allocate + ("C_a_i_sqrt" 4) (exact->inexact (##sys#negate n))))) + ((exact-integer? n) + (receive (s^2 r) (##sys#exact-integer-sqrt n) + (if (eq? 0 r) + s^2 + (##core#inline_allocate ("C_a_i_sqrt" 4) (exact->inexact n))))) + ((ratnum? n) ; Try to compute exact sqrt (we already know n is positive) + (receive (ns^2 nr) (##sys#exact-integer-sqrt (%ratnum-numerator n)) + (if (eq? nr 0) + (receive (ds^2 dr) + (##sys#exact-integer-sqrt (%ratnum-denominator n)) + (if (eq? dr 0) + (##sys#/-2 ns^2 ds^2) + (##sys#sqrt/loc loc (exact->inexact n)))) + (##sys#sqrt/loc loc (exact->inexact n))))) + (else (##core#inline_allocate ("C_a_i_sqrt" 4) (exact->inexact n))))) (define (sqrt x) (##sys#sqrt/loc 'sqrt x)) -;; TODO: unimplemented +(define (exact-integer-nth-root k n) + (##sys#check-exact-uinteger k 'exact-integer-nth-root) + (##sys#check-exact-uinteger n 'exact-integer-nth-root) + (##sys#exact-integer-nth-root/loc 'exact-integer-nth-root k n)) + +;; Generalized Newton's algorithm for positive integers, with a little help +;; from Wikipedia ;) https://en.wikipedia.org/wiki/Nth_root_algorithm (define (##sys#exact-integer-nth-root/loc loc k n) - (error "not yet implemented")) + (if (or (eq? 0 k) (eq? 1 k) (eq? 1 n)) ; Maybe call exact-integer-sqrt on n=2? + (values k 0) + (let ((len (integer-length k))) + (if (##sys#<-2 len n) ; Idea from Gambit: 2^{len-1} <= k < 2^{len} + (values 1 (##sys#--2 k 1)) ; Since x >= 2, we know x^{n} can't exist + ;; Set initial guess to (at least) 2^ceil(ceil(log2(k))/n) + (let* ((shift-amount (inexact->exact (ceiling (/ (fx+ len 1) n)))) + (g0 (arithmetic-shift 1 shift-amount)) + (n-1 (##sys#--2 n 1))) + (let lp ((g0 g0) + (g1 (quotient + (##sys#+-2 + (##sys#*-2 n-1 g0) + (quotient k (##sys#integer-power g0 n-1))) + n))) + (if (##sys#<-2 g1 g0) + (lp g1 (quotient + (##sys#+-2 + (##sys#*-2 n-1 g1) + (quotient k (##sys#integer-power g1 n-1))) + n)) + (values g0 (##sys#--2 k (##sys#integer-power g0 n)))))))))) (define (##sys#integer-power base e) (define (square x) (##sys#*-2 x x)) @@ -1614,7 +1711,7 @@ EOF (cond ((eq? e2 0) res) ((even? e2) ; recursion is faster than iteration here - (##sys#*-2 res (square (lp 1 (arithmetic-shift e2 -1))))) + (##sys#*-2 res (square (lp 1 (##sys#integer-shift e2 -1))))) (else (lp (##sys#*-2 res base) (##sys#--2 e2 1))))))) @@ -1758,7 +1855,7 @@ EOF (bex (fx- (fx- (integer-length mant) (integer-length scl)) flonum-precision))) (if (fx< bex 0) - (let* ((num (arithmetic-shift mant (fxneg bex))) + (let* ((num (##sys#integer-shift mant (fxneg bex))) (quo (round-quotient num scl))) (cond ((> (integer-length quo) flonum-precision) ;; Too many bits of quotient; readjust diff --git a/manual/Unit library b/manual/Unit library index 05e1a588..953e25d6 100644 --- a/manual/Unit library +++ b/manual/Unit library @@ -43,6 +43,25 @@ set, or {{#f}} otherwise. The rightmost/least-significant bit is bit 0. Returns the number of bits needed to represent the exact integer N in 2's complement notation. +==== exact-integer-sqrt + +<procedure>(exact-integer-sqrt K)</procedure> + +Returns two values {{s}} and {{r}}, where {{s^2 + r = K}} and {{K < (s+1)^2}}. +In other words, {{s}} is the closest square root we can find that's equal to or +smaller than {{K}}, and {{r}} is the rest if {{K}} isn't a neat square of two numbers. + +This procedure is compatible with the R7RS specification. + +==== exact-integer-nth-root + +<procedure>(exact-integer-nth-root K N)</procedure> + +Like {{exact-integer-sqrt}}, but with any base value. Calculates +{{\sqrt[N]{K}}}, the {{N}}th root of {{K}} and returns two values +{{s}} and {{r}} where {{s^N + r = K}} and {{K < (s+1)^N}}. + + ==== bignum? <procedure>(bignum? X)</procedure> @@ -277,10 +296,12 @@ finite if both the real and imaginary components are finite. <procedure>(signum N)</procedure> -Returns {{1}} if {{N}} is positive, {{-1}} if {{N}} -is negative or {{0}} if {{N}} is zero. {{signum}} is exactness preserving. - +For real numbers, returns {{1}} if {{N}} is positive, {{-1}} if {{N}} +is negative or {{0}} if {{N}} is zero. {{signum}} is exactness +preserving. +For complex numbers, returns a complex number of the same angle but +with magnitude 1. === File Input/Output diff --git a/runtime.c b/runtime.c index ddad4e91..57d1e014 100644 --- a/runtime.c +++ b/runtime.c @@ -832,7 +832,7 @@ static C_PTABLE_ENTRY *create_initial_ptable() { /* IMPORTANT: hardcoded table size - this must match the number of C_pte calls + 1 (NULL terminator)! */ - C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 77); + C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 78); int i = 0; if(pt == NULL) @@ -900,6 +900,7 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_integer_to_string); C_pte(C_flonum_to_string); /* IMPORTANT: have you read the comments at the start and the end of this function? */ + C_pte(C_signum); C_pte(C_abs); C_pte(C_u_integer_abs); C_pte(C_negate); @@ -5493,6 +5494,25 @@ void C_ccall C_u_integer_abs(C_word c, C_word self, C_word k, C_word x) } } +void C_ccall C_signum(C_word c, C_word self, C_word k, C_word x) +{ + if (c != 3) { + C_bad_argc_2(c, 3, self); + } else if (x & C_FIXNUM_BIT) { + C_kontinue(k, C_i_fixnum_signum(x)); + } else if (C_immediatep(x)) { + barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "signum", x); + } else if (C_block_header(x) == C_FLONUM_TAG) { + C_word *a = C_alloc(C_SIZEOF_FLONUM); + C_kontinue(k, C_a_u_i_flonum_signum(&a, 1, x)); + } else if (C_header_bits(x) == C_BIGNUM_TYPE) { + C_kontinue(k, C_bignum_negativep(x) ? C_fix(-1) : C_fix(1)); + } else { + try_extended_number("\003sysextended-signum", 2, k, x); + } +} + + /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_abs(C_word **a, int c, C_word x) { diff --git a/types.db b/types.db index e5819d7e..1a4d949a 100644 --- a/types.db +++ b/types.db @@ -849,6 +849,12 @@ (arithmetic-shift (#(procedure #:clean #:enforce #:foldable) arithmetic-shift (integer fixnum) integer) ((integer fixnum) (##sys#integer-shift #(1) #(2)))) +(exact-integer-nth-root (#(procedure #:clean #:enforce #:foldable) exact-integer-nth-root (integer integer) integer integer) + ((integer integer) (##sys#exact-integer-nth-root/loc 'exact-integer-nth-root #(1) #(2)))) + +(exact-integer-sqrt (#(procedure #:clean #:enforce #:foldable) exact-integer-sqrt (integer) integer integer) + ((integer) (##sys#exact-integer-sqrt #(1)))) + (bignum? (#(procedure #:pure #:predicate bignum) bignum? (*) boolean)) (bit-set? (#(procedure #:clean #:enforce #:foldable) bit-set? (integer integer) boolean) @@ -1230,7 +1236,16 @@ (setter (#(procedure #:clean #:enforce) setter (procedure) procedure)) (signal (procedure signal (*) . *)) -(signum (#(procedure #:clean #:enforce) signum (number) number)) + +(signum (#(procedure #:clean #:enforce) signum (number) (or fixnum float cplxnum)) + ((fixnum) (fixnum) (##core#inline "C_i_fixnum_signum" #(1))) + ((integer) (fixnum) (##core#inline "C_u_i_integer_signum" #(1))) + ((float) (float) + (##core#inline_allocate ("C_a_u_i_flonum_signum" 4) #(1))) + ((ratnum) (fixnum) + (##core#inline "C_u_i_integer_signum" (##sys#slot #(1) '1))) + ((cplxnum) ((or float cplxnum)) (##sys#extended-signum #(1)))) + (software-type (#(procedure #:pure) software-type () symbol)) (software-version (#(procedure #:pure) software-version () symbol)) (string->blob (#(procedure #:clean #:enforce) string->blob (string) blob)) @@ -1262,7 +1277,6 @@ (with-exception-handler (#(procedure #:enforce) with-exception-handler ((procedure (*) . *) (procedure () . *)) . *)) - ;; chicken (internal) (##sys#foreign-char-argument (#(procedure #:clean #:enforce) ##sys#foreign-char-argument (char) char)Trap