Raymond Toy pushed to branch master at cmucl / cmucl

Commits:

4 changed files:

Changes:

  • .gitlab-ci.yml
    ... ... @@ -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:
    

  • src/code/bignum.lisp
    ... ... @@ -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
     
    

  • tests/bignum.lisp
    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
    +

  • tests/issues.lisp
    ... ... @@ -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))