1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Fixes and improvements to number-theoretic division operators

* libguile/numbers.c (scm_euclidean_quotient, scm_euclidean_divide,
  scm_centered_quotient, scm_centered_divide): Fix bug in inum/inum
  case, where (quotient most-negative-fixnum -1) would not be converted
  to a bignum.

  (scm_euclidean_quotient): Be more anal-retentive about calling
  scm_remember_upto_here_1 after mpz_sgn, (even though mpz_sgn is
  documented as being implemented as a macro and certainly won't
  do any allocation).  It's better to be safe than sorry here.

  (scm_euclidean_quotient, scm_centered_quotient): In the bignum/inum
  case, check if the divisor is 1, since this will allow us to avoid
  allocating a new bignum.

  (scm_euclidean_divide, scm_centered_quotient, scm_centered_divide):
  When computing the intermediate truncated quotient (xx / yy) and
  remainder, use (xx % yy) instead of (xx - qq * yy), on the theory that
  the compiler is more likely to handle this case intelligently and
  maybe combine the operations.

  (scm_euclidean_divide): In the bignum/inum case, we know that the
  remainder will fit in an fixnum, so don't bother allocating a bignum
  for it.

  (scm_euclidean_quotient, scm_euclidean_remainder,
  scm_euclidean_divide, scm_centered_quotient, scm_centered_remainder,
  scm_centered_divide): Minor stylistic changes.

* test-suite/tests/numbers.test: Rework testing framework for
  number-theoretic division operators to be more efficient and
  comprehensive in its testing of code paths and problem cases.
This commit is contained in:
Mark H Weaver 2011-02-10 15:40:57 -05:00 committed by Andy Wingo
parent bc3d34f587
commit 4a46bc2a5f
2 changed files with 203 additions and 154 deletions

View file

@ -1089,6 +1089,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -1096,7 +1097,6 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
scm_num_overflow (s_scm_euclidean_quotient);
else
{
scm_t_inum xx = SCM_I_INUM (x);
scm_t_inum qq = xx / yy;
if (xx < qq * yy)
{
@ -1105,19 +1105,25 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
else
qq++;
}
return SCM_I_MAKINUM (qq);
if (SCM_LIKELY (SCM_FIXABLE (qq)))
return SCM_I_MAKINUM (qq);
else
return scm_i_inum2big (qq);
}
}
else if (SCM_BIGP (y))
{
if (SCM_I_INUM (x) >= 0)
if (xx >= 0)
return SCM_INUM0;
else
return SCM_I_MAKINUM (- mpz_sgn (SCM_I_BIG_MPZ (y)));
{
scm_t_inum qq = - mpz_sgn (SCM_I_BIG_MPZ (y));
scm_remember_upto_here_1 (y);
return SCM_I_MAKINUM (qq);
}
}
else if (SCM_REALP (y))
return scm_i_inexact_euclidean_quotient
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_euclidean_quotient (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_euclidean_quotient (x, y);
else
@ -1131,6 +1137,8 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_quotient, "euclidean-quotient", 2, 0, 0,
scm_t_inum yy = SCM_I_INUM (y);
if (SCM_UNLIKELY (yy == 0))
scm_num_overflow (s_scm_euclidean_quotient);
else if (SCM_UNLIKELY (yy == 1))
return x;
else
{
SCM q = scm_i_mkbig ();
@ -1246,6 +1254,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -1253,7 +1262,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
scm_num_overflow (s_scm_euclidean_remainder);
else
{
scm_t_inum rr = SCM_I_INUM (x) % yy;
scm_t_inum rr = xx % yy;
if (rr >= 0)
return SCM_I_MAKINUM (rr);
else if (yy > 0)
@ -1264,7 +1273,6 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
}
else if (SCM_BIGP (y))
{
scm_t_inum xx = SCM_I_INUM (x);
if (xx >= 0)
return x;
else if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
@ -1284,8 +1292,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_remainder, "euclidean-remainder", 2, 0, 0,
}
}
else if (SCM_REALP (y))
return scm_i_inexact_euclidean_remainder
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_euclidean_remainder (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_euclidean_remainder (x, y);
else
@ -1420,6 +1427,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -1427,9 +1435,10 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
scm_num_overflow (s_scm_euclidean_divide);
else
{
scm_t_inum xx = SCM_I_INUM (x);
scm_t_inum qq = xx / yy;
scm_t_inum rr = xx - qq * yy;
scm_t_inum rr = xx % yy;
SCM q;
if (rr < 0)
{
if (yy > 0)
@ -1437,13 +1446,15 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
else
{ rr -= yy; qq++; }
}
return scm_values (scm_list_2 (SCM_I_MAKINUM (qq),
SCM_I_MAKINUM (rr)));
if (SCM_LIKELY (SCM_FIXABLE (qq)))
q = SCM_I_MAKINUM (qq);
else
q = scm_i_inum2big (qq);
return scm_values (scm_list_2 (q, SCM_I_MAKINUM (rr)));
}
}
else if (SCM_BIGP (y))
{
scm_t_inum xx = SCM_I_INUM (x);
if (xx >= 0)
return scm_values (scm_list_2 (SCM_INUM0, x));
else if (mpz_sgn (SCM_I_BIG_MPZ (y)) > 0)
@ -1464,8 +1475,7 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
}
}
else if (SCM_REALP (y))
return scm_i_inexact_euclidean_divide
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_euclidean_divide (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_euclidean_divide (x, y);
else
@ -1482,19 +1492,19 @@ SCM_PRIMITIVE_GENERIC (scm_euclidean_divide, "euclidean/", 2, 0, 0,
else
{
SCM q = scm_i_mkbig ();
SCM r = scm_i_mkbig ();
scm_t_inum rr;
if (yy > 0)
mpz_fdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
SCM_I_BIG_MPZ (x), yy);
rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
SCM_I_BIG_MPZ (x), yy);
else
{
mpz_fdiv_qr_ui (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (r),
SCM_I_BIG_MPZ (x), -yy);
rr = mpz_fdiv_q_ui (SCM_I_BIG_MPZ (q),
SCM_I_BIG_MPZ (x), -yy);
mpz_neg (SCM_I_BIG_MPZ (q), SCM_I_BIG_MPZ (q));
}
scm_remember_upto_here_1 (x);
return scm_values (scm_list_2 (scm_i_normbig (q),
scm_i_normbig (r)));
SCM_I_MAKINUM (rr)));
}
}
else if (SCM_BIGP (y))
@ -1607,6 +1617,7 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -1614,9 +1625,8 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
scm_num_overflow (s_scm_centered_quotient);
else
{
scm_t_inum xx = SCM_I_INUM (x);
scm_t_inum qq = xx / yy;
scm_t_inum rr = xx - qq * yy;
scm_t_inum rr = xx % yy;
if (SCM_LIKELY (xx > 0))
{
if (SCM_LIKELY (yy > 0))
@ -1643,19 +1653,20 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
qq++;
}
}
return SCM_I_MAKINUM (qq);
if (SCM_LIKELY (SCM_FIXABLE (qq)))
return SCM_I_MAKINUM (qq);
else
return scm_i_inum2big (qq);
}
}
else if (SCM_BIGP (y))
{
/* Pass a denormalized bignum version of x (even though it
can fit in a fixnum) to scm_i_bigint_centered_quotient */
return scm_i_bigint_centered_quotient
(scm_i_long2big (SCM_I_INUM (x)), y);
return scm_i_bigint_centered_quotient (scm_i_long2big (xx), y);
}
else if (SCM_REALP (y))
return scm_i_inexact_centered_quotient
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_centered_quotient (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_centered_quotient (x, y);
else
@ -1669,6 +1680,8 @@ SCM_PRIMITIVE_GENERIC (scm_centered_quotient, "centered-quotient", 2, 0, 0,
scm_t_inum yy = SCM_I_INUM (y);
if (SCM_UNLIKELY (yy == 0))
scm_num_overflow (s_scm_centered_quotient);
else if (SCM_UNLIKELY (yy == 1))
return x;
else
{
SCM q = scm_i_mkbig ();
@ -1833,6 +1846,7 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, "centered-remainder", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -1840,7 +1854,6 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, "centered-remainder", 2, 0, 0,
scm_num_overflow (s_scm_centered_remainder);
else
{
scm_t_inum xx = SCM_I_INUM (x);
scm_t_inum rr = xx % yy;
if (SCM_LIKELY (xx > 0))
{
@ -1875,12 +1888,10 @@ SCM_PRIMITIVE_GENERIC (scm_centered_remainder, "centered-remainder", 2, 0, 0,
{
/* Pass a denormalized bignum version of x (even though it
can fit in a fixnum) to scm_i_bigint_centered_remainder */
return scm_i_bigint_centered_remainder
(scm_i_long2big (SCM_I_INUM (x)), y);
return scm_i_bigint_centered_remainder (scm_i_long2big (xx), y);
}
else if (SCM_REALP (y))
return scm_i_inexact_centered_remainder
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_centered_remainder (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_centered_remainder (x, y);
else
@ -2062,6 +2073,7 @@ SCM_PRIMITIVE_GENERIC (scm_centered_divide, "centered/", 2, 0, 0,
{
if (SCM_LIKELY (SCM_I_INUMP (x)))
{
scm_t_inum xx = SCM_I_INUM (x);
if (SCM_LIKELY (SCM_I_INUMP (y)))
{
scm_t_inum yy = SCM_I_INUM (y);
@ -2069,9 +2081,10 @@ SCM_PRIMITIVE_GENERIC (scm_centered_divide, "centered/", 2, 0, 0,
scm_num_overflow (s_scm_centered_divide);
else
{
scm_t_inum xx = SCM_I_INUM (x);
scm_t_inum qq = xx / yy;
scm_t_inum rr = xx - qq * yy;
scm_t_inum rr = xx % yy;
SCM q;
if (SCM_LIKELY (xx > 0))
{
if (SCM_LIKELY (yy > 0))
@ -2098,20 +2111,21 @@ SCM_PRIMITIVE_GENERIC (scm_centered_divide, "centered/", 2, 0, 0,
{ qq++; rr -= yy; }
}
}
return scm_values (scm_list_2 (SCM_I_MAKINUM (qq),
SCM_I_MAKINUM (rr)));
if (SCM_LIKELY (SCM_FIXABLE (qq)))
q = SCM_I_MAKINUM (qq);
else
q = scm_i_inum2big (qq);
return scm_values (scm_list_2 (q, SCM_I_MAKINUM (rr)));
}
}
else if (SCM_BIGP (y))
{
/* Pass a denormalized bignum version of x (even though it
can fit in a fixnum) to scm_i_bigint_centered_divide */
return scm_i_bigint_centered_divide
(scm_i_long2big (SCM_I_INUM (x)), y);
return scm_i_bigint_centered_divide (scm_i_long2big (xx), y);
}
else if (SCM_REALP (y))
return scm_i_inexact_centered_divide
(SCM_I_INUM (x), SCM_REAL_VALUE (y));
return scm_i_inexact_centered_divide (xx, SCM_REAL_VALUE (y));
else if (SCM_FRACTIONP (y))
return scm_i_slow_exact_centered_divide (x, y);
else

View file

@ -4116,6 +4116,7 @@
(pass-if "-100i swings back to 45deg down"
(eqv-loosely? +7.071-7.071i (sqrt -100.0i))))
;;;
;;; euclidean/
;;; euclidean-quotient
@ -4127,130 +4128,164 @@
(with-test-prefix "Number-theoretic division"
;; Tests that (lo <= x < hi),
;; Tests that (lo <1 x <2 hi),
;; but allowing for imprecision
;; if x is inexact.
(define (test-within-range? lo hi x)
(define (test-within-range? lo <1 x <2 hi)
(if (exact? x)
(and (<= lo x) (< x hi))
(and (<1 lo x) (<2 x hi))
(let ((lo (- lo test-epsilon))
(hi (+ hi test-epsilon)))
(<= lo x hi))))
;; (cartesian-product-map list '(a b) '(1 2))
;; ==> ((a 1) (a 2) (b 1) (b 2))
(define (cartesian-product-map f . lsts)
(define (cartmap rev-head lsts)
(if (null? lsts)
(list (apply f (reverse rev-head)))
(append-map (lambda (x) (cartmap (cons x rev-head) (cdr lsts)))
(car lsts))))
(cartmap '() lsts))
(define (cartesian-product-for-each f . lsts)
(define (cartfor rev-head lsts)
(if (null? lsts)
(apply f (reverse rev-head))
(for-each (lambda (x) (cartfor (cons x rev-head) (cdr lsts)))
(car lsts))))
(cartfor '() lsts))
(define (safe-euclidean-quotient x y)
(cond ((not (and (real? x) (real? y))) (throw 'wrong-type-arg))
((zero? y) (throw 'divide-by-zero))
((nan? y) (nan))
((positive? y) (floor (/ x y)))
((negative? y) (ceiling (/ x y)))
(else (throw 'unknown-problem))))
(define (safe-euclidean-remainder x y)
(let ((q (safe-euclidean-quotient x y)))
(- x (* y q))))
(define (valid-euclidean-answer? x y q r)
(if (and (finite? x) (finite? y))
(and (eq? (exact? q)
(exact? r)
(and (exact? x) (exact? y)))
(integer? q)
(test-eqv? r (- x (* q y)))
(test-within-range? 0 (abs y) r))
(and (test-eqv? q (safe-euclidean-quotient x y))
(test-eqv? r (safe-euclidean-remainder x y)))))
(define (safe-centered-quotient x y)
(cond ((not (and (real? x) (real? y))) (throw 'wrong-type-arg))
((zero? y) (throw 'divide-by-zero))
((nan? y) (nan))
((positive? y) (floor (+ 1/2 (/ x y))))
((negative? y) (ceiling (+ -1/2 (/ x y))))
(else (throw 'unknown-problem))))
(define (safe-centered-remainder x y)
(let ((q (safe-centered-quotient x y)))
(- x (* y q))))
(and (eq? (exact? q)
(exact? r)
(and (exact? x) (exact? y)))
(test-eqv? r (- x (* q y)))
(if (and (finite? x) (finite? y))
(and (integer? q)
(test-within-range? 0 <= r < (abs y)))
(test-eqv? q (/ x y)))))
(define (valid-centered-answer? x y q r)
(if (and (finite? x) (finite? y))
(and (eq? (exact? q)
(exact? r)
(and (exact? x) (exact? y)))
(integer? q)
(test-eqv? r (- x (* q y)))
(test-within-range? (* -1/2 (abs y))
(* +1/2 (abs y))
r))
(and (test-eqv? q (safe-centered-quotient x y))
(test-eqv? r (safe-centered-remainder x y)))))
(and (eq? (exact? q)
(exact? r)
(and (exact? x) (exact? y)))
(test-eqv? r (- x (* q y)))
(if (and (finite? x) (finite? y))
(and (integer? q)
(test-within-range?
(* -1/2 (abs y)) <= r < (* +1/2 (abs y))))
(test-eqv? q (/ x y)))))
(define test-numerators
(append (cartesian-product-map * '(1 -1)
'(123 125 127 130 3 5 10
123.2 125.0 127.2 130.0
123/7 125/7 127/7 130/7))
(cartesian-product-map * '(1 -1)
'(123 125 127 130 3 5 10)
(list 1
(+ 1 most-positive-fixnum)
(+ 2 most-positive-fixnum)))
(list 0 +0.0 -0.0 +inf.0 -inf.0 +nan.0
most-negative-fixnum
(1+ most-positive-fixnum)
(1- most-negative-fixnum))))
(define (for lsts f) (apply for-each f lsts))
(define test-denominators
(list 10 5 10/7 127/2 10.0 63.5
-10 -5 -10/7 -127/2 -10.0 -63.5
+inf.0 -inf.0 +nan.0 most-negative-fixnum
(+ 1 most-positive-fixnum) (+ -1 most-negative-fixnum)
(+ 2 most-positive-fixnum) (+ -2 most-negative-fixnum)))
(define big (expt 10 (1+ (inexact->exact (ceiling (log10 fixnum-max))))))
(define (run-division-tests quo+rem quo rem valid-answer?)
(define (test n d)
(run-test (list n d) #t
(lambda ()
(let-values (((q r) (quo+rem n d)))
(and (test-eqv? q (quo n d))
(test-eqv? r (rem n d))
(valid-answer? n d q r))))))
(define (test+/- n d)
(test n d )
(test n (- d))
(cond ((not (zero? n))
(test (- n) d )
(test (- n) (- d)))))
(define (test-for-exception n d exception)
(let ((name (list n d)))
(pass-if-exception name exception (quo+rem n d))
(pass-if-exception name exception (quo n d))
(pass-if-exception name exception (rem n d))))
(run-test "documented?" #t
(lambda ()
(and (documented? quo+rem)
(documented? quo)
(documented? rem))))
(with-test-prefix "inum / inum"
(with-test-prefix "fixnum-min / -1"
(test fixnum-min -1))
(for '((1 2 5 10)) ;; denominators
(lambda (d)
(for '((0 1 2 5 10)) ;; multiples
(lambda (m)
(for '((-2 -1 0 1 2 3 4 5 7 10
12 15 16 19 20)) ;; offsets
(lambda (b)
(test+/- (+ b (* m d))
d))))))))
(with-test-prefix "inum / big"
(with-test-prefix "fixnum-min / -fixnum-min"
(test fixnum-min (- fixnum-min)))
(with-test-prefix "fixnum-max / (2*fixnum-max)"
(test+/- fixnum-max (* 2 fixnum-max)))
(for `((0 1 2 10 ,(1- fixnum-max) ,fixnum-max))
(lambda (n)
(test n (1+ fixnum-max))
(test (- n) (1+ fixnum-max))
(test n (1- fixnum-min))
(test (- n) (1- fixnum-min)))))
(with-test-prefix "big / inum"
(with-test-prefix "-fixnum-min / fixnum-min"
(test (- fixnum-min) fixnum-min))
(for '((1 4 5 10)) ;; denominators
(lambda (d)
(for `((1 2 5 ,@(if (even? d)
'(1/2 3/2 5/2)
'()))) ;; multiples
(lambda (m)
(for '((-2 -1 0 1 2)) ;; offsets
(lambda (b)
(test+/- (+ b (* m d big))
d))))))))
(with-test-prefix "big / big"
(for `((,big ,(1+ big))) ;; denominators
(lambda (d)
(for `((1 2 5 ,@(if (even? d)
'(1/2 3/2 5/2)
'()))) ;; multiples
(lambda (m)
(for '((-2 -1 0 1 2)) ;; offsets
(lambda (b)
(test+/- (+ b (* m d))
d))))))))
(with-test-prefix "inexact"
(for '((0.5 1.5 2.25 5.75)) ;; denominators
(lambda (d)
(for '((0 1 2 5 1/2 3/2 5/2)) ;; multiples
(lambda (m)
(for '((-2 -1 0 1 2)) ;; offsets
(lambda (b)
(test+/- (+ b (* m d))
d))))))))
(with-test-prefix "fractions"
(for '((1/10 16/3 10/7)) ;; denominators
(lambda (d)
(for '((0 1 2 5 1/2 3/2 5/2)) ;; multiples
(lambda (m)
(for '((-2/9 -1/11 0 1/3 2/3)) ;; offsets
(lambda (b)
(test+/- (+ b (* m d))
d))))))))
(with-test-prefix "mixed types"
(for `((10 ,big 12.0 10/7 +inf.0 -inf.0 +nan.0)) ;; denominators
(lambda (d)
(for `((25 ,(* 3/2 big) 130.0 15/7
0 0.0 -0.0 +inf.0 -inf.0 +nan.0)) ;; numerators
(lambda (n)
(test+/- n d))))))
(with-test-prefix "divide by zero"
(for `((0 0.0 +0.0)) ;; denominators
(lambda (d)
(for `((15 ,(* 3/2 big) 18.0 33/7
0 0.0 -0.0 +inf.0 -inf.0 +nan.0)) ;; numerators
(lambda (n)
(test-for-exception
n d exception:numerical-overflow)))))))
(with-test-prefix "euclidean/"
(pass-if (documented? euclidean/))
(pass-if (documented? euclidean-quotient))
(pass-if (documented? euclidean-remainder))
(cartesian-product-for-each
(lambda (n d)
(run-test (list 'euclidean/ n d) #t
(lambda ()
(let-values (((q r) (euclidean/ n d)))
(and (test-eqv? q (euclidean-quotient n d))
(test-eqv? r (euclidean-remainder n d))
(valid-euclidean-answer? n d q r))))))
test-numerators test-denominators))
(run-division-tests euclidean/
euclidean-quotient
euclidean-remainder
valid-euclidean-answer?))
(with-test-prefix "centered/"
(pass-if (documented? centered/))
(pass-if (documented? centered-quotient))
(pass-if (documented? centered-remainder))
(cartesian-product-for-each
(lambda (n d)
(run-test (list 'centered/ n d) #t
(lambda ()
(let-values (((q r) (centered/ n d)))
(and (test-eqv? q (centered-quotient n d))
(test-eqv? r (centered-remainder n d))
(valid-centered-answer? n d q r))))))
test-numerators test-denominators)))
(run-division-tests centered/
centered-quotient
centered-remainder
valid-centered-answer?)))