Skip to content

1.28

; One variant of the Fermat test that cannot be fooled 
; is called the Miller-Rabin test (Miller 1976; Rabin 1980). 
; This starts from an alternate form of Fermat’s Little Theorem, 
; which states that if n is a prime number and a is any positive 
; integer less than n,then a raised to the (n−1)-st power is 
; congruent to 1 modulo n. To test the primality of a number n by 
; the Miller-Rabin test, we pick a random number a < n and raise a to 
; the (n−1)-st power modulo n using the `expmod` procedure. However, 
; whenever we perform the squaring step in expmod, we check to see 
; if we have discovered a “nontrivial square root of 1 modulo n,” that 
; is, a number not equal to 1 or n − 1 whose square is equal to 1 modulo n. 
; It is possible to prove that if such a nontrivial square root of 1 exists, 
; then n is not prime. It is also possible to prove that if n is an odd 
; number that is not prime, then, for at least half the numbers a < n, 
; computing a^(n−1) in this way will reveal a nontrivial square root of 1 modulo n. 
; This is why the Miller-Rabin test cannot be fooled.) Modify the expmod procedure 
; to signal if it discovers a nontrivial square root of 1, and use this to 
; implement the Miller-Rabin test with a procedure analogous to fermat-test. 
; Check your procedure by testing various known primes and non-primes. 
; Hint: One convenient way to make expmod signal is to have it return 0.

(define (even? x) (= (remainder x 2) 0))
(define (expmod base exp m)
    (cond ((= exp 0) 1)
          ((even? exp)
           (remainder (square (test-non-trivial (expmod base (/ exp 2) m)
                                                m)) m))
          (else (remainder (* base (expmod base (- exp 1) m))
                           m))))
(define (test-non-trivial x n)
    (cond ((and (= (remainder (square x) n) 1)
               (not (= x 1))
               (not (= x (- n 1))))
           0)
          (else x)))

(define (miller-rabin-test n)
    (define (miller-rabin-test-iter a)
        (cond ((= a 0) #t)
              ((check-num (+ 1 (random (- n 1))))
                (miller-rabin-test-iter (- a 1)))
              (else #f)))
    (define (check-num a)
            (= (expmod a (- n 1) n) 1))
    (miller-rabin-test-iter 100)              
)

; Testing with actual prime numbers
(miller-rabin-test 2) ;Value: #t
(miller-rabin-test 17) ;Value: #t
(miller-rabin-test 19) ;Value: #t

; Testing with Carmichael numbers (should be false)
(miller-rabin-test 561)
(miller-rabin-test 1105)
(miller-rabin-test 1729)
(miller-rabin-test 2465)
(miller-rabin-test 2821)
(miller-rabin-test 6601)