(in-package "ACL2")

;; This book illustrates a simple application of the theory of quadratic
;; residues to the Mersenne prime problem.  We show that certain Mersenne
;; numbers may be factored by applying a theorem of Euler, which combines
;; two results: (1) Euler's criterion for quadratic residues and (2) the
;; second supplement to the law of quadratic reciprocity, which computes
;; the quadratic character of 2 modulo an odd prime p.

;; The two theorems and the relevant definitions are contained in the
;; book "gauss":

(include-book "gauss")

(comp t)

; We skip some forms by default, because they take a long time to run.
(defmacro maybe-skip (x)
; Redefine to be x if you want to execute forms inside (maybe-skip ...).
  (declare (ignore x))
  '(value-triple nil))

;; For a prime p, the Mersenne number Mp is defined to be 2^p-1:

(defun m (p) (1- (expt 2 p)))

;; We would like to determine the primality of Mp.  For small values of p,
;; this can be done simply by executing the function primep:

(defthm mersenne-19
    (primep (m 19)))

;;[Time: .4 seconds]

(maybe-skip
(defthm mersenne-31
    (primep (m 31)))
)

;;[Time: 65 minutes]

;; The timing of mersenne-31 suggests that the computation of mersenne-61
;; is intractable:

;;(defthm mersenne-61
;;    (primep (m 61)))

;;[Time: a billion hours?]

;; Naturally, we can handle larger composites than primes:

(defthm mersenne-23
    (not (primep (m 23))))

;;[Time: .02 seconds]

(maybe-skip
(defthm mersenne-67
    (not (primep (m 67))))
)

;;[Time: 288 seconds]

(maybe-skip
(defthm mersenne-999671
    (not (primep (m 999671))))
)

;;[Time: 165 minutes]

;;(defthm mersenne-19876271
;;    (not (primep (m 19876271))))

;;[Error: Attempt to create an integer that is too large to represent.]

;; There is an obvious optimization of this procedure, based on
;; the observation that if n has a proper divisor, then it has one 
;; that is at most sqrt(n).  Thus, we define an alternative to
;; the function least-divisor that stops at sqrt(n), and
;; establish a rewrite rule:

(defun least-divisor-2 (k n)
  (declare (xargs :measure (nfix (- n k))))
  (if (and (integerp n)
	   (integerp k)
	   (< 1 k)
	   (<= k n))
      (if (> (* k k) n)
	  n
	(if (divides k n)
	    k
	  (least-divisor-2 (1+ k) n)))
    nil))

(comp t)

(defthm least-divisor-rewrite-lemma-1
    (implies (and (integerp n)
		  (integerp k)
		  (< 1 k)
		  (<= k n)
		  (<= (* (least-divisor k n) (least-divisor k n)) n))
	     (equal (least-divisor k n)
		    (least-divisor-2 k n)))
  :rule-classes ()
  :hints (("Subgoal *1/7" :use (least-divisor-divides))
	  ("Subgoal *1/1" :use (least-divisor-divides))))

(defthm least-divisor-rewrite-lemma-2
    (implies (and (integerp n)
		  (> n 1)
		  (not (= n (least-divisor 2 n))))
	     (<= (* (least-divisor 2 n) (least-divisor 2 n)) n))
  :rule-classes ()
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance least-divisor-divides (k 2))
			(:instance least-divisor-is-least (k 2) (d (/ n (least-divisor 2 n))))
			(:instance *-weakly-monotonic (x (least-divisor 2 n)) (y 1) (y+ (/ n (least-divisor 2 n))))
			(:instance *-weakly-monotonic (x (least-divisor 2 n)) (y+ 1) (y (/ n (least-divisor 2 n))))))))

(defthm least-divisor-rewrite-lemma-3
    (implies (and (integerp n)
		  (integerp k)
		  (< 1 k)
		  (<= k n)
		  (= n (least-divisor k n)))
	     (= n (least-divisor-2 k n)))
  :rule-classes ()
  :hints (("Goal" :use (least-divisor-divides 
			(:instance least-divisor-is-least (d (least-divisor-2 2 n)))))))

(defthm least-divisor-rewrite
    (equal (least-divisor 2 n)
	   (least-divisor-2 2 n))
  :hints (("Goal" :use (least-divisor-rewrite-lemma-2
			(:instance least-divisor-rewrite-lemma-3 (k 2))
			(:instance least-divisor-rewrite-lemma-1 (k 2))))))

;; In order to arrange that the more efficient variant is executed, we must
;; disable the executable counterparts of primep and least-proper-divisor
;; and enable the logical function primep.  This allows us to establish
;; primality somewhat more efficiently:

(in-theory (enable primep))

(in-theory (disable (primep) (least-divisor)))

(defthm mersenne-31-revisited
    (primep (m 31)))

;;[Time: .05 seconds]

(maybe-skip
(defthm mersenne-61
    (primep (m 61)))
)

;;[Time: 54  minutes]

;; However, the above optimization does not help at all in the composite case.
;; In the special case where mod(p,4) = 3 and 2*p+1 is prime, the following 
;; theorem may be used.  This theorem is attributed to Euler in Hardy and
;; Wright, where it appears as Theorem 103.  It is an immediate comsequence
;; of Euler's Criterion and the Second Supplement.

(defthm theorem-103
    (implies (and (primep p)
		  (= (mod p 4) 3)
		  (primep (1+ (* 2 p))))
	     (divides (1+ (* 2 p)) (m p)))
  :rule-classes ()
  :hints (("Goal" :use ((:instance second-supplement (p (1+ (* 2 p))))
			(:instance euler-criterion (m 2) (p (1+ (* 2 p))))
			(:instance mod-sum (a 1) (b (* 2 p)) (n 8))
			(:instance mod-prod (k 2) (m p) (n 4))
			(:instance divides-leq (x (1+ (* 2 p))) (y 2))
			(:instance divides-mod-equal (a (expt 2 p)) (b 1) (n (1+ (* 2 p))))))))

;; With the exception of p = 3 (in which case Mp = 2*p+1), Mp is composite
;; for any p satisfying the hypothesis of theorem-103:

(defthm expt-2-p-bnd
    (implies (and (integerp p)
		  (> p 3))
	     (> (expt 2 p) (* 2 (1+ p))))
  :rule-classes ())

(defthm theorem-103-corollary
    (implies (and (primep p)
		  (= (mod p 4) 3)
		  (> p 3)
		  (primep (1+ (* 2 p))))	  
	     (not (primep (m p))))
  :hints (("Goal" :use (theorem-103
			expt-2-p-bnd
			(:instance primep-no-divisor (p (1- (expt 2 p))) (d (1+ (* 2 p))))))))

;; In order to arrange for theorem-103-corollary to be used as a
;; rewrite rule, we must disable the logical expansion of both
;; primep and m.  We must also re-enable the executable counterpart
;; of primep to allow the computation of primep(p) and primep(2*p+1):

(in-theory (disable primep m (m)))
(in-theory (enable (primep)))

(defthm mersenne-23-revisited
    (not (primep (m 23))))

;;[Time: .01 seconds]

(defthm mersenne-999671-revisited
    (not (primep (m 999671))))

;;[Time: .01 seconds]

(maybe-skip
(defthm mersenne-19876271
    (not (primep (m 19876271))))
)

;;[Time: 47 seconds]

