(in-package "ACL2")

;; This book contains a proof of Gauss's Lemma:  Let p be prime and let m be
;; relatively prime to p.  Let mu be the number of elements of the set 
;;    {mod(m,p), mod(2*m,p), ..., mod(((p-1)/2)*m}
;; that exceed (p-1)/2.  Then m is a quadratic residue mod p iff mu is even.

;; As a corollary, we also prove the Second Supplement to the Law of Quadratic
;; Reciprocity: 2 is a quadratic residue mod p iff mod(p,8) is either 1 or 7.

;; The proof depends on euler's criterion::

(include-book "euler")

(defun mu (n m p)
  (if (zp n) 
      0
    (if (> (mod (* m n) p) (/ (1- p) 2))
	(1+ (mu (1- n) m p))
      (mu (1- n) m p))))

(defun reflections (n m p)
  (if (zp n) 
      ()
    (if (> (mod (* m n) p) (/ (1- p) 2))
        (cons (- p (mod (* m n) p))
              (reflections (1- n) m p))
      (cons (mod (* m n) p)
	    (reflections (1- n) m p)))))

;; We shall show that reflections((p-1)/2,m,p) is a list of length (p-1)/2 of distinct
;; integers between 1 and (p-1)/2.

(defthm len-reflections
    (implies (natp n)
	     (equal (len (reflections n m p)) n)))

(defthm mod-distinct-reflect
    (implies (and (primep p)
		  (not (zp i))
		  (< i (/ p 2))
		  (not (zp j))
		  (< j (/ p 2))
		  (not (= j i))
		  (integerp m)		  
		  (not (divides p m)))
	     (not (equal (+ (mod (* m i) p) (mod (* m j) p)) p)))
  :hints (("Goal" :in-theory (enable mod-mod-sum)
		  :use ((:instance divides-leq (x p) (y (+ i j)))
			(:instance divides-mod-0 (a (+ (* m i) (* m j))) (n p))
			(:instance divides-mod-0 (a (+ (mod (* m i) p) (mod (* m j) p))) (n p))
			(:instance euclid (a (+ i j)) (b m))))))

(defthm reflections-distinct-positives-lemma-1
    (implies (and (primep p)
		  (not (= p 2))
		  (not (zp n))
		  (< n (/ p 2))
		  (integerp r)
		  (< r n)
		  (integerp m)
		  (not (divides p m)))
	     (not (member (mod (* m n) p) (reflections r m p)))))

(defthm reflections-distinct-positives-lemma-2
    (implies (and (primep p)
		  (not (= p 2))
		  (not (zp n))
		  (< n (/ p 2))
		  (integerp r)
		  (< r n)
		  (integerp m)
		  (not (divides p m)))
	     (not (member (+ p (* -1 (mod (* m n) p))) (reflections r m p))))
  :hints (("Subgoal *1/4" :in-theory (disable mod-distinct)
			  :use ((:instance mod-distinct (i n) (j r))))))

(defthm p-1-even-cor
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp n)
		  (> n (/ (1- p) 2)))
	     (>= n (/ (1+ p) 2)))
  :rule-classes ()
  :hints (("Goal" :in-theory (disable p-1-even)
		  :use (p-1-even))))

(defthm reflections-distinct-positives
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m))
		  (integerp n)
		  (< n (/ p 2)))
	     (distinct-positives (reflections n m p) (/ (1- p) 2)))
  :rule-classes ()
  :hints (("Subgoal *1/7" :use ((:instance mod-p-bnds (i n))))
	  ("Subgoal *1/4" :use ((:instance p-1-even-cor (n (mod (* m n) p)))))))

;; This result allows us to compute the product of the elements of
;; reflections((p-1)/2,m,p):

(defthm perm-reflections
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m)))
	     (perm (positives (/ (1- p) 2))
		   (reflections (/ (1- p) 2) m p)))
  :rule-classes ()
  :hints (("Goal" :use ((:instance reflections-distinct-positives (n (/ (1- p) 2)))
			(:instance pigeonhole-principle (l (reflections (/ (1- p) 2) m p)))))))

(defthm times-list-reflections
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m)))
           (equal (times-list (reflections (+ -1/2 (* 1/2 p)) m p))
                  (fact (/ (1- p) 2))))
  :hints (("Goal" :use (perm-reflections
			(:instance perm-times-list 
				   (l1 (positives (/ (1- p) 2))) 
				   (l2 (reflections (/ (1- p) 2) m p)))))))

;;  We have an alternative method for computing the same product:

(defthm mod-mult-2
    (implies (and (integerp n)
		  (integerp m)
		  (integerp a))
	     (equal (mod (+ (* n a) m) n)
		    (mod m n)))
  :hints (("Goal" :use (mod-mult))))

(defthm times-list-reflection-mod-prods
    (implies (and (not (zp p))
		  (integerp m)
		  (integerp n))
	     (equal (mod (times-list (reflections n m p)) p)
		    (if (evenp (mu n m p))
			(mod (times-list (mod-prods n m p)) p)
		      (mod (- (times-list (mod-prods n m p))) p))))
  :rule-classes ()
  :hints (("Subgoal *1/3" :use ((:instance mod-times-mod 
					   (a (times-list (reflections (1- n) m p)))
					   (b (times-list (mod-prods (1- n) m p)))
					   (c (mod (* m n) p))
					   (n p))
				(:instance mod-times-mod 
					   (a (times-list (reflections (1- n) m p)))
					   (b (- (times-list (mod-prods (1- n) m p))))
					   (c (mod (* m n) p))
					   (n p))))	  
	  ("Subgoal *1/2" :use ((:instance evenp-oddp (m (mu (1- n) m p)))
				(:instance mod-times-mod 
					   (a (times-list (reflections (1- n) m p)))
					   (b (times-list (mod-prods (1- n) m p)))
					   (c (- (mod (* m n) p)))
					   (n p))
				(:instance mod-times-mod 
					   (a (times-list (reflections (1- n) m p)))
					   (b (- (times-list (mod-prods (1- n) m p))))
					   (c (- (mod (* m n) p)))
					   (n p))))))

;; Gauss's Lemma follows from the equation of the two expressions 
;; for the product.  We consider two cases according to the parity 
;; of mu:

(defthm euler-mu-even
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m))
		  (evenp (mu (/ (1- p) 2) m p)))
	     (= (mod (expt m (/ (1- p) 2)) p) 1))
  :rule-classes ()
  :hints (("Goal" :use ((:instance expt-positive-integer-type (a m) (i (/ (1- p) 2)))
			(:instance times-list-reflection-mod-prods (n (/ (1- p) 2)))
			(:instance mod-mod-prods (n (/ (1- p) 2)))
			(:instance not-divides-p-fact (n (/ (1- p) 2)))
			(:instance mod-times-prime (a (fact (/ (1- p) 2))) (b (expt m (/ (1- p) 2))) (c 1))))))

(defthm euler-mu-odd
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m))
		  (oddp (mu (/ (1- p) 2) m p)))
	     (= (mod (expt m (/ (1- p) 2)) p) (- p 1)))
  :rule-classes ()
  :hints (("Goal" :use ((:instance expt-positive-integer-type (a m) (i (/ (1- p) 2)))
			(:instance times-list-reflection-mod-prods (n (/ (1- p) 2)))
			(:instance mod-mod-prods (n (/ (1- p) 2)))
			(:instance not-divides-p-fact (n (/ (1- p) 2)))
			(:instance mod-times-prime 
				   (a (- (fact (/ (1- p) 2)))) (b (expt m (/ (1- p) 2))) (c -1))
			(:instance mod-mult (m -1) (a 1) (n p))
			(:instance divides-product (x p) (y (- (fact (/ (1- p) 2)))) (z -1))
			(:instance mod-times-mod 
				   (a (times-list (mod-prods (/ (1- p) 2) m p)))
				   (b (* (fact (/ (1- p) 2)) (expt m (/ (1- p) 2))))
				   (c -1)
				   (n p))))))

(defthm gauss-lemma
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m)))
	     (iff (evenp (mu (/ (1- p) 2) m p))
		  (residue m p)))
  :rule-classes ()
  :hints (("Goal" :use (euler-mu-even
			euler-mu-odd
			euler-criterion))))

;; For the proof of the Second Supplement, we show first that
;;   mu((p-1)/2,2,p) = (p-1)/2 - fl((p-1)/4):

(defthm mu-0-rewrite
    (implies (and (not (zp p))
		  (natp n)
		  (<= (* 2 n) (/ (1- p) 2)))
	     (equal (mu n 2 p) 0)))

(defthm mu-rewrite-lemma-1
    (implies (and (primep p)
		  (not (= p 2))
		  (natp k)
		  (<= (* 2 k) (/ (1- p) 2))
		  (< (/ (1- p) 2) (* 2 (1+ k)))
		  (integerp n)
		  (<= k n)
		  (<= n (/ (1- p) 2)))
	     (= (mu n 2 p) (- n k)))
  :rule-classes ())

(defthm mu-rewrite-lemma-2
    (implies (and (primep p)
		  (not (= p 2)))
	     (equal (mu (+ -1/2 (* 1/2 p)) 2 p)
		    (- (/ (1- p) 2) (fl (/ (1- p) 4)))))
  :hints (("Goal" :use ((:instance mu-rewrite-lemma-1
				   (k (fl (/ (1- p) 4)))
				   (n (/ (1- p) 2)))))))

;; Let k = fl(p/8) and m = mod(p,8).  Then p = 8*k + m.  It follows that
;;   mu((p-1)/2,2,p) = 2*k + (m-1)/2 - fl((m-1)/4):

(defthmd mu-rewrite-lemma-3
    (implies (and (primep p)
		  (not (= p 2)))
	     (equal (mod p 8)
		    (- p (* 8 (fl (/ p 8))))))
  :hints (("Goal" :use ((:instance mod-def (x p) (y 8))))))

(defthm mu-rewrite
    (implies (and (primep p)
		  (not (= p 2)))
	     (equal (mu (+ -1/2 (* 1/2 p)) 2 p)
		    (+ (* 2 (fl (/ p 8))) (- (/ (1- (mod p 8)) 2) (fl (/ (1- (mod p 8)) 4))))))
  :hints (("Goal" :in-theory (enable mu-rewrite-lemma-3))))

;; The desired result now follows by a simple case analysis:

(defthm no-integer-7-8
    (implies (and (integerp m)
		  (< 7 m))
	     (not (< m 8))))

(defthm mod-p-8-choices
    (implies (and (primep p)
		  (not (= p 2)))
	     (member (mod p 8) '(1 3 5 7)))
  :rule-classes ()
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance mod-def (x p) (y 8))
			(:instance primep-no-divisor (d 2))
			(:instance primep-no-divisor (d 8))
			(:instance mod-bnd-1 (m p) (n 8))
			(:instance member-positives (x (mod p 8)) (n 7))
			(:instance divides-mod-0 (n 8) (a p))))))

(defthm second-supplement    
    (implies (and (primep p)
		  (not (= p 2)))
	     (iff (residue 2 p)
		  (or (= (mod p 8) 1)
		      (= (mod p 8) 7))))
  :rule-classes ()
  :hints (("Goal" :use (mod-p-8-choices
			(:instance gauss-lemma (m 2))
			(:instance divides-leq (x p) (y 2))))))
