Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
90d8b4b5
by Raymond Toy at 2018-07-04T09:54:08-07:00
-
e6b95b82
by Raymond Toy at 2018-07-04T12:21:25-07:00
-
3af22f92
by Raymond Toy at 2018-07-04T12:40:30-07:00
-
d652bd09
by Raymond Toy at 2018-07-04T13:31:21-07:00
-
be073d06
by Raymond Toy at 2018-07-07T10:41:33-07:00
-
01fa37d8
by Raymond Toy at 2018-07-07T12:01:45-07:00
-
0f0ac0b6
by Raymond Toy at 2018-07-15T10:45:08-07:00
-
228359b6
by Raymond Toy at 2018-07-15T13:47:44-07:00
-
cb6e99a3
by Raymond Toy at 2018-07-15T16:01:01-07:00
-
833fef6d
by Raymond Toy at 2018-07-16T00:04:01+00:00
4 changed files:
Changes:
| ... | ... | @@ -3,6 +3,7 @@ variables: |
| 3 | 3 |
version: "2018-03-x86"
|
| 4 | 4 |
|
| 5 | 5 |
linux-runner:
|
| 6 |
+ image: ubuntu:14.04
|
|
| 6 | 7 |
tags:
|
| 7 | 8 |
- linux
|
| 8 | 9 |
before_script:
|
| ... | ... | @@ -884,7 +884,12 @@ down to individual words.") |
| 884 | 884 |
(negate-bignum-in-place result))
|
| 885 | 885 |
(%normalize-bignum result (1+ (* 2 n))))))))
|
| 886 | 886 |
|
| 887 |
-(defun classical-multiply-bignums (a b)
|
|
| 887 |
+;; Bignum multiply using Knuth's algorithm. We keep this around for
|
|
| 888 |
+;; now so we can compare the new algorithm against this to make sure
|
|
| 889 |
+;; this are working.
|
|
| 890 |
+;;
|
|
| 891 |
+;; TODO: Remove this eventually?
|
|
| 892 |
+(defun classical-multiply-bignums-knuth (a b)
|
|
| 888 | 893 |
(declare (type bignum-type a b))
|
| 889 | 894 |
(let* ((a-plusp (%bignum-0-or-plusp a (%bignum-length a)))
|
| 890 | 895 |
(b-plusp (%bignum-0-or-plusp b (%bignum-length b)))
|
| ... | ... | @@ -916,6 +921,73 @@ down to individual words.") |
| 916 | 921 |
(when negate-res (negate-bignum-in-place res))
|
| 917 | 922 |
(%normalize-bignum res len-res)))
|
| 918 | 923 |
|
| 924 |
+;; Classical multiplication of bignums using Knuth's algorithm
|
|
| 925 |
+;; modified to handle signed bignums. Pretend the bignums are
|
|
| 926 |
+;; actually unsigned, do an unsigned multiply and then correct the
|
|
| 927 |
+;; result. This is based on the algorithm in Hacker's Delight.
|
|
| 928 |
+;;
|
|
| 929 |
+;; Let a[n] and b[n] represent the individual bits of each bignum with
|
|
| 930 |
+;; M being the number of bits in a and N being the number of bits in
|
|
| 931 |
+;; b. If these are interpreted as an unsigned number, then we are
|
|
| 932 |
+;; multiplying numbers
|
|
| 933 |
+;;
|
|
| 934 |
+;; (a + 2^M*a[M-1})*(b + 2^N*b[N-1])
|
|
| 935 |
+;; = a*b + 2^M*u[M-1]*b + 2^N*b[N-1]*a + 2^(M+N)*a[M-1]*b[M-1]
|
|
| 936 |
+;;
|
|
| 937 |
+;; To get the desired result, we need to subtract out the term
|
|
| 938 |
+;; 2^M*u[M-1]*b + 2^N*b[N-1]*a from the product. The last term
|
|
| 939 |
+;; doesn't need to subtracted because we know the product fits in M+N
|
|
| 940 |
+;; bits and this term is beyond that.
|
|
| 941 |
+(defun classical-multiply-bignums (a b)
|
|
| 942 |
+ (declare (type bignum-type a b))
|
|
| 943 |
+ (let* ((len-a (%bignum-length a))
|
|
| 944 |
+ (len-b (%bignum-length b))
|
|
| 945 |
+ (len-res (+ len-a len-b))
|
|
| 946 |
+ (res (%allocate-bignum len-res)))
|
|
| 947 |
+ (declare (type bignum-index len-a len-b len-res))
|
|
| 948 |
+ ;; Unsigned multiply
|
|
| 949 |
+ (dotimes (i len-a)
|
|
| 950 |
+ (declare (type bignum-index i))
|
|
| 951 |
+ (let ((carry-digit 0)
|
|
| 952 |
+ (x (%bignum-ref a i))
|
|
| 953 |
+ (k i))
|
|
| 954 |
+ (declare (type bignum-index k)
|
|
| 955 |
+ (type bignum-element-type carry-digit x))
|
|
| 956 |
+ (dotimes (j len-b)
|
|
| 957 |
+ (multiple-value-bind (big-carry res-digit)
|
|
| 958 |
+ (%multiply-and-add x (%bignum-ref b j)
|
|
| 959 |
+ (%bignum-ref res k)
|
|
| 960 |
+ carry-digit)
|
|
| 961 |
+ (declare (type bignum-element-type big-carry res-digit))
|
|
| 962 |
+ (setf (%bignum-ref res k) res-digit)
|
|
| 963 |
+ (setf carry-digit big-carry)
|
|
| 964 |
+ (incf k)))
|
|
| 965 |
+ (setf (%bignum-ref res k) carry-digit)))
|
|
| 966 |
+ (flet ((apply-correction (neg-arg neg-len pos-arg pos-len)
|
|
| 967 |
+ ;; Applies the correction by basically subtracting out
|
|
| 968 |
+ ;; 2^M*b where M is the length (in bits) of b and b is
|
|
| 969 |
+ ;; the positive term in pos-arg. neg-arg is the negative
|
|
| 970 |
+ ;; arg.
|
|
| 971 |
+ (let ((borrow 1))
|
|
| 972 |
+ (dotimes (j pos-len)
|
|
| 973 |
+ (declare (type bignum-index j))
|
|
| 974 |
+ (let ((index (+ j neg-len)))
|
|
| 975 |
+ (declare (type bignum-index index))
|
|
| 976 |
+ (multiple-value-bind (d borrow-out)
|
|
| 977 |
+ (%subtract-with-borrow (%bignum-ref res index)
|
|
| 978 |
+ (%bignum-ref pos-arg j)
|
|
| 979 |
+ borrow)
|
|
| 980 |
+ (setf (%bignum-ref res index) d)
|
|
| 981 |
+ (setf borrow borrow-out)))))))
|
|
| 982 |
+ ;; Apply corrections if either of the arguments is negative.
|
|
| 983 |
+ (unless (%bignum-0-or-plusp a len-a)
|
|
| 984 |
+ ;; A is negative
|
|
| 985 |
+ (apply-correction a len-a b len-b))
|
|
| 986 |
+ (unless (%bignum-0-or-plusp b len-b)
|
|
| 987 |
+ ;; B is negative
|
|
| 988 |
+ (apply-correction b len-b a len-a)))
|
|
| 989 |
+ (%normalize-bignum res len-res)))
|
|
| 990 |
+ |
|
| 919 | 991 |
(defparameter *min-karatsuba-bits* 512
|
| 920 | 992 |
"Use Karatsuba if the bignums have at least this many bits")
|
| 921 | 993 |
|
| 1 |
+;;; Tests for the bignum operations
|
|
| 2 |
+ |
|
| 3 |
+(defpackage :bignum-tests
|
|
| 4 |
+ (:use :cl :lisp-unit))
|
|
| 5 |
+ |
|
| 6 |
+(in-package #:bignum-tests)
|
|
| 7 |
+ |
|
| 8 |
+(define-test hd-mult.same-size
|
|
| 9 |
+ "Test bignum multiplier"
|
|
| 10 |
+ (:tag :bignum-tests)
|
|
| 11 |
+ ;; x and y are randomly generated 128 integers. No particular reason
|
|
| 12 |
+ ;; for these values, except that they're bignums.
|
|
| 13 |
+ (let ((x 248090201001762284446997112921270181259)
|
|
| 14 |
+ (y 313102667534462314033767199170708979663)
|
|
| 15 |
+ (prod 77677703722812705876871716049945873590003455155145426220435549433670954735717))
|
|
| 16 |
+ ;; Verify the we get the right results for various signed values of x and y.
|
|
| 17 |
+ (assert-equal prod (* x y))
|
|
| 18 |
+ (assert-equal (- prod) (* (- x) y))
|
|
| 19 |
+ (assert-equal (- prod) (* x (- y)))
|
|
| 20 |
+ (assert-equal prod (* (- x) (- y)))
|
|
| 21 |
+ ;; Nake sure it's commutative
|
|
| 22 |
+ (assert-equal prod (* y x))
|
|
| 23 |
+ (assert-equal (- prod) (* y (- x)))
|
|
| 24 |
+ (assert-equal (- prod) (* (- y) x))
|
|
| 25 |
+ (assert-equal prod (* (- y) (- x)))))
|
|
| 26 |
+ |
|
| 27 |
+(define-test hd-mult.diff-size
|
|
| 28 |
+ "Test bignum multiplier"
|
|
| 29 |
+ (:tag :bignum-tests)
|
|
| 30 |
+ ;; x is a randomly generated bignum. y is a small bignum.
|
|
| 31 |
+ (let ((x 248090201001762284446997112921270181259)
|
|
| 32 |
+ (y (1+ most-positive-fixnum))
|
|
| 33 |
+ (prod 133192412470079431258262755675409306410924638208))
|
|
| 34 |
+ ;; Verify the we get the right results for various signed values of x and y.
|
|
| 35 |
+ (assert-equal prod (* x y))
|
|
| 36 |
+ (assert-equal (- prod) (* (- x) y))
|
|
| 37 |
+ (assert-equal (- prod) (* x (- y)))
|
|
| 38 |
+ (assert-equal prod (* (- x) (- y)))
|
|
| 39 |
+ ;; Nake sure it's commutative
|
|
| 40 |
+ (assert-equal prod (* y x))
|
|
| 41 |
+ (assert-equal (- prod) (* y (- x)))
|
|
| 42 |
+ (assert-equal (- prod) (* (- y) x))
|
|
| 43 |
+ (assert-equal prod (* (- y) (- x)))))
|
|
| 44 |
+ |
|
| 45 |
+ |
|
| 46 |
+(define-test hd-mult.random
|
|
| 47 |
+ "Test bignum multiplier with random values"
|
|
| 48 |
+ (:tag :bignum-tests)
|
|
| 49 |
+ (let ((rng (kernel::make-random-object :state (kernel:init-random-state)
|
|
| 50 |
+ :rand 0
|
|
| 51 |
+ :cached-p nil))
|
|
| 52 |
+ (range (ash 1 128)))
|
|
| 53 |
+ (flet ((gen-bignum (x sign)
|
|
| 54 |
+ (do ((r (random x rng) (random x rng)))
|
|
| 55 |
+ ((typep r 'bignum)
|
|
| 56 |
+ (if (zerop sign)
|
|
| 57 |
+ r
|
|
| 58 |
+ (- r))))))
|
|
| 59 |
+ (dotimes (k 100)
|
|
| 60 |
+ (let* ((r1 (gen-bignum range (random 2 rng)))
|
|
| 61 |
+ (r2 (gen-bignum range (random 2 rng)))
|
|
| 62 |
+ (prod-knuth (bignum::classical-multiply-bignums-knuth r1 r2))
|
|
| 63 |
+ (prod-hd (bignum::classical-multiply-bignums r1 r2)))
|
|
| 64 |
+ (assert-equal prod-knuth prod-hd r1 r2))))))
|
|
| 65 |
+ |
|
| 66 |
+ |
|
| 67 |
+;; Just for simple timing tests so we can redo the timing tests if needed.
|
|
| 68 |
+#+nil
|
|
| 69 |
+(define-test hd-timing
|
|
| 70 |
+ "Test execution time"
|
|
| 71 |
+ (:tag :bignum-tests)
|
|
| 72 |
+ (let ((rng (kernel::make-random-object :state
|
|
| 73 |
+ (kernel:init-random-state)
|
|
| 74 |
+ :rand 0 :cached-p nil))
|
|
| 75 |
+ (range (ash 1 128))
|
|
| 76 |
+ (reps 10000))
|
|
| 77 |
+ (flet ((gen-bignum (x sign)
|
|
| 78 |
+ (do ((r (random x rng) (random x rng)))
|
|
| 79 |
+ ((typep r 'bignum)
|
|
| 80 |
+ (if (zerop sign)
|
|
| 81 |
+ r (- r))))))
|
|
| 82 |
+ (let* ((r1 (gen-bignum range 1))
|
|
| 83 |
+ (r2 (gen-bignum range 1)) res)
|
|
| 84 |
+ (time
|
|
| 85 |
+ (dotimes (k reps)
|
|
| 86 |
+ (declare (fixnum k))
|
|
| 87 |
+ (setf res (bignum::classical-multiply-bignums-knuth r1 r2))))
|
|
| 88 |
+ (print res)
|
|
| 89 |
+ (time
|
|
| 90 |
+ (dotimes (k reps)
|
|
| 91 |
+ (declare (fixnum k))
|
|
| 92 |
+ (setf res (bignum::classical-multiply-bignums r1 r2))))
|
|
| 93 |
+ (print res)))))
|
|
| 94 |
+ |
| ... | ... | @@ -397,6 +397,10 @@ |
| 397 | 397 |
(sleep 5)
|
| 398 | 398 |
(assert-eql :exited (ext:process-status p)))))
|
| 399 | 399 |
|
| 400 |
+;; For some reason this used to work linux CI but not doesn't. But
|
|
| 401 |
+;; this test passes on my Fedora and debian systesm.
|
|
| 402 |
+;; See issue #64.
|
|
| 403 |
+#-linux
|
|
| 400 | 404 |
(define-test issue.41.1
|
| 401 | 405 |
(:tag :issues)
|
| 402 | 406 |
(issue-41-tester unix:sigstop))
|