is-prime.rkt
#lang racket

(provide (contract-out
          (prime? (-> integer? integer? boolean?))))

(define (mod-op op a b n)
  (modulo (op 
           (modulo a n)
           (modulo b n))
          n))

(define (get-s-d x)
  (let find-s-d ((s 0)
                 (d x))
    (if (even? d)
        (find-s-d (add1 s) (arithmetic-shift d -1))
        (values s d))))

(define (mod-exp a x n)
  (cond ((zero? x) 1)
        ((odd? x)
         (mod-op * (mod-exp (mod-op * a a n) (quotient x 2) n) a n))
        ((even? x)
         (mod-exp (mod-op * a a n) (quotient x 2) n))))

(define (prime? x k)
  (let-values (((s d) (get-s-d (sub1 x))))
    (let loop ((i 0))
      (cond ((>= i k)
             #t)
            (else 
             (let ((test (mod-exp (add1 (random (- x 3))) d x)))
               (if (or (= test 1) (= test (sub1 x)))
                   (loop (add1 i))
                   (let carmichael-test ((r 1)
                                         (ctest (modulo (* test test) x)))
                     (cond ((>= r s)
                            #f)
                           ((= ctest 1)
                            #f)
                           ((= ctest (sub1 x))
                            (loop (add1 i)))
                           (else
                            (carmichael-test (add1 r) (modulo (* ctest ctest) x))))))))))))