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))
|