(in-package "ACL2")

;; This book contains a formalization of Eisenstein's proof of Gauss's 
;; Law of Quadratic Reciprocity: if p and q are distinct odd primes, 
;; then
;;  (residue(p,q) <=> residue(q,p)) <=> ((p-1)/2)*((q-1)/2) is even.

;; The proof is based on Gauss's Lemma:

(include-book "gauss")

;; We shall need the following facts pertaing to divisibility bt 2.

(defthm evenp-mod
    (implies (integerp x)
	     (= (mod x 2)
		(if (evenp x)
		    0
		  1)))
  :rule-classes ()
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance mod012 (m x))
			(:instance divides-mod-0 (a x) (n 2))))))

(defthm evenp-iff-evenp-plus
    (implies (and (integerp x)
		  (integerp y))
	     (equal (equal (evenp x) (evenp y))
		    (evenp (+ x y))))
  :rule-classes ()
  :hints (("Goal" :use (evenp-mod
			(:instance evenp-mod (x y))
			(:instance evenp-mod (x (+ x y)))
			(:instance mod-mod-sum (a x) (b y) (n 2))))))

(defthm evenp-minus
    (implies (integerp x)
	     (equal (evenp (- x)) (evenp x)))
  :rule-classes ()
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance divides-product (x 2) (y (- x)) (z -1))
			(:instance divides-product (x 2) (y x) (z -1))))))

(defthm evenp-iff-evenp-minus
    (implies (and (integerp x)
		  (integerp y))
	     (equal (equal (evenp x) (evenp y))
		    (evenp (- x y))))
  :rule-classes ()
  :hints (("Goal" :use ((:instance evenp-minus (x y))
			(:instance evenp-iff-evenp-plus (y (- y)))))))

(defthm evenp-iff-evenp-iff-evenp-plus
    (implies (and (integerp x)
		  (integerp y)
		  (integerp z))
	     (equal (equal (evenp x) (evenp y))
		    (equal (evenp (+ x z)) (evenp (+ y z)))))
  :rule-classes ()
  :hints (("Goal" :use (evenp-iff-evenp-minus
			(:instance evenp-iff-evenp-minus (x (+ x z)) (y (+ y z)))))))

(defthm evenp-times
    (implies (and (integerp x)
		  (integerp y))
	     (equal (evenp (* x y))
		    (or (evenp x) (evenp y))))
  :hints (("Goal" :in-theory (enable divides)
		  :use (evenp-iff-evenp-minus
			(:instance euclid (p 2) (a x) (b y))
			(:instance divides-product (x 2) (y x) (z y))
			(:instance divides-product (x 2) (y y) (z x))))))

(defthm oddp-odd-prime
    (implies (and (primep p)
		  (not (equal p 2)))
	     (not (evenp p)))
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance primep-no-divisor (d 2))))))

(in-theory (disable evenp))

;; Our first goal is to derive yet another characterization of quadratic residues:
;; if m is odd and relatively prime to an odd prime p, then  m is a quadratic residue 
;; mod p iff the sum
;;    fl(m/p) + fl(2*m/p) + fl(3*m/p) + ... + fl(((p-1)/2)*m/p)
;; is even.

;; We require the following relation between reflections, mod-prods, and mu, which
;; follows easily from the definitions:

(defun plus-list (l)
  (if (consp l)
      (+ (ifix (car l)) (plus-list (cdr l)))
    0))

(defthm even-mu
    (implies (and (primep p)
		  (not (equal p 2))
		  (integerp m))
	     (equal (evenp (mu n m p))
		    (equal (evenp (plus-list (mod-prods n m p)))
			   (evenp (plus-list (reflections n m p))))))
  :rule-classes ()
  :hints (("Subgoal *1/3" :use ((:instance evenp-iff-evenp-iff-evenp-plus
					   (x (plus-list (mod-prods (1- n) m p)))
					   (y (plus-list (reflections (1- n) m p)))
					   (z (mod (* m n) p)))))
	  ("Subgoal *1/2" :use ((:instance evenp-iff-evenp-plus
					   (x (plus-list (mod-prods (1- n) m p)))
					   (y (mod (* m n) p)))
				(:instance evenp-iff-evenp-plus
					   (x (plus-list (reflections (1- n) m p)))
					   (y (- p (mod (* m n) p))))
				(:instance evenp-iff-evenp-minus
					   (x p)
					   (y (mod (* m n) p)))
				(:instance evenp-oddp (m (mu (1- n) m p)))))))

;; We shall instantiate the above lemma with n = (p-1)/2.  In "gauss",
;; we showed that reflections((p-1)/2,m,p) is a permutation of 
;; positives((p-1)/2).  It follows that these two lists have the same
;; sum:

(defthm perm-plus-list-lemma
  (implies (member x m)
	   (equal (+ (ifix x) (plus-list (remove1 x m))) (plus-list m)))
  :rule-classes ())

(defthm perm-plus-list
  (implies (perm l m) 
	   (equal (plus-list l) (plus-list m)))
  :rule-classes ()
  :hints (("Subgoal *1/2" :use ((:instance perm-plus-list-lemma (x (car l)))))))

(defthm plus-list-reflections
  (implies (and (primep p)
		(not (equal p 2))
		(integerp m)
		(not (divides p m)))
	   (equal (plus-list (positives (/ (1- p) 2)))
		  (plus-list (reflections (/ (1- p) 2) m p))))		  
  :rule-classes ()
  :hints (("Goal" :use (perm-reflections
			(:instance perm-plus-list 
				   (m (reflections (/ (1- p) 2) m p))
				   (l (positives (/ (1- p) 2))))))))

;; Combining Gauss's Lemma with the above results, we have the following 
;; characterization of quadratic residues:

(defthm residue-mod-prods-positives
    (implies (and (primep p)
		  (not (equal p 2))
		  (integerp m)
		  (not (divides p m)))
	     (equal (residue m p)
		    (equal (evenp (plus-list (mod-prods (/ (1- p) 2) m p)))
			   (evenp (plus-list (positives (/ (1- p) 2)))))))
  :rule-classes ()
  :hints (("Goal" :use (plus-list-reflections
			gauss-lemma
			(:instance even-mu (n (/ (1- p) 2)))))))

;;  Next, we sum the equation 
;;        m*n = fl(m*n/p)* p + mod(m*n,p)
;; from n = 1 to n = (p-1)/2:

(defun fl-prods (n m p)
  (if (zp n)
      ()
      (cons (fl (/ (* m n) p))
            (fl-prods (1- n) m p))))

(defthm fl-mod-plus-list
    (implies (and (integerp p)
		  (integerp m))
	     (equal (* m (plus-list (positives n)))
		    (+ (* p (plus-list (fl-prods n m p)))
		       (plus-list (mod-prods n m p)))))
  :rule-classes ()
  :hints (("Subgoal *1/2''" :use ((:instance mod-def (x (* m n)) (y p))))))

;; Reducing the above equation mod 2 yields the desired result::

(defthm fl-mod-plus-list-evenp
    (implies (and (integerp p)
		  (integerp m)
		  (oddp m)
		  (oddp p))
	     (equal (evenp (plus-list (positives n)))
		    (equal (evenp (plus-list (fl-prods n m p)))
			   (evenp (plus-list (mod-prods n m p))))))
  :rule-classes ()
  :hints (("Goal" :use (fl-mod-plus-list
			(:instance evenp-iff-evenp-plus
				   (x (* p (plus-list (fl-prods n m p))))
				   (y (plus-list (mod-prods n m p))))))))

(defthm residue-quotients
    (implies (and (primep p)
		  (not (= p 2))
		  (integerp m)
		  (not (divides p m))
		  (oddp m))
	     (equal (residue m p)
		    (evenp (plus-list (fl-prods (/ (1- p) 2) m p)))))
  :rule-classes ()
  :hints (("Goal" :use (residue-mod-prods-positives
			(:instance fl-mod-plus-list-evenp (n (/ (1- p) 2)))))))

;; We instantiate the above result with m = q and again with m = p and p = q.
;; This gives us the following:

(defthm equal-residue-even-plus
  (implies (and (primep p)
		(not (equal p 2))
		(primep q)
		(not (equal q 2))
		(not (equal p q)))
	   (iff (equal (residue q p) (residue p q))
		(evenp (+ (plus-list (fl-prods (/ (1- p) 2) q p))
			  (plus-list (fl-prods (/ (1- q) 2) p q))))))
  :hints (("Goal" :use ((:instance residue-quotients (m q))
			(:instance residue-quotients (m p) (p q))
			(:instance evenp-iff-evenp-plus
				   (x (plus-list (fl-prods (/ (1- p) 2) q p)))
				   (y (plus-list (fl-prods (/ (1- q) 2) p q))))
			(:instance primep-no-divisor (d q))
			(:instance primep-no-divisor (d p) (p q))))))

;; We shall complete the proof of quadratic reciprocity by showing that the sum in 
;; the above lemma equals the product ((p-1)/2) * ((q-1)/2).  This amounts to a
;; formalization of a geometric argument of Eisenstein.  (For a detailed discussion,
;; see http://www.russinoff.com/papers/gauss.html.)

;; Given two lists of integers l and m, let wins(l,m) be the number 
;; of pairs (x,y) in the cartesian product l x m such that x > y, and 
;; let losses(l,m) be the number of pairs satisfying x < y.  Assume that
;; l and m are disjoint.  Then
;;   wins(l,m) + losses(l,m) = wins(l,m) + wins(m,l) = len(l)*len(m).
;; This observation is formalized by the theorem plus-wins-wins below.

(defun wins1 (x l)
  (if (consp l)
      (if (< (car l) x)
          (1+ (wins1 x (cdr l)))
        (wins1 x (cdr l)))
    0))

(defun wins (k l)
  (if (consp k)
      (+ (wins1 (car k) l) (wins (cdr k) l))
    0))

(defun losses1 (x l)
  (if (consp l)
      (if (< x (car l)) 
          (1+ (losses1 x (cdr l)))
        (losses1 x (cdr l)))
    0))

(defun losses (k l)
  (if (consp k)
      (+ (losses1 (car k) l) (losses (cdr k) l))
    0))

(defun all-integerp (l)
  (if (consp l)
      (and (integerp (car l)) 
	   (all-integerp (cdr l)))
    t))

(defthm plus-losses1-wins1
  (implies (and (not (member x l))
		(integerp x)
		(all-integerp l))
           (equal (+ (losses1 x l) (wins1 x l))
                  (len l))))

(defthm plus-wins-losses
  (implies (and (not (intersectp-equal l m))
                (all-integerp l)
		(all-integerp m))
           (equal (+ (wins l m) (losses l m))
                  (* (len l) (len m)))))

(defthm equal-wins-losses
    (equal (losses l m) (wins m l))
  :rule-classes ())

(defthm plus-wins-wins
  (implies (and (not (intersectp-equal l m))
                (all-integerp l)
		(all-integerp m))
           (equal (+ (wins l m) (wins m l))
                  (* (len l) (len m))))
  :hints (("Goal" :use (equal-wins-losses))))

;; We shall apply the above result to the two lists
;;    l = (p, 2*p, 3*p, ..., ((q-1)/2)*p)
;; and
;;    m = (q, 2*q, 3*q, ..., ((p-1)/2)*q).
;; We must first show that l and m are disjoint.

(defun mults (n p)
  (if (zp n)
      ()
    (cons (* n p) (mults (1- n) p))))

(defthm all-integerp-mults
  (implies (integerp p)
	   (all-integerp (mults n p))))

(defthm not-divides-p-mult-q
  (implies (and (primep p)
		(primep q)
		(not (= p q))
		(not (zp j))
		(< j p))
           (not (divides p (* j q))))
  :hints (("Goal" :use ((:instance euclid (a j) (b q))
			(:instance divides-leq (x p) (y j))
			(:instance primep-no-divisor (d p) (p q))))))

(defthm no-equal-mults
  (implies (and (primep p)
		(primep q)
		(not (= p q))
		(not (zp i))
		(not (zp j))
		(< j p))
           (not (equal (* i p) (* j q))))
  :hints (("Goal" :use ((:instance divides-product (x p) (y p) (z i))))))

(defthm empty-intersect-mults-lemma
  (implies (and (primep p)
		(primep q)
		(not (= p q))
		(not (zp i))
                (not (zp j))
		(< j p))
           (not (member-equal (* i p) (mults j q)))))

(defthm empty-intersect-mults
  (implies (and (primep p)
		(primep q)
		(not (= p 2))
		(not (= q 2))
		(not (= p q)))
           (not (intersectp-equal (mults i p) 
				  (mults (+ -1/2 (* 1/2 p)) q)))))

;; The product of the lengths of the two lists is
;;    len(l)*len(m) = ((p-1)/2) * (q-1)/2).

(defthm len-mults
  (equal (len (mults n p)) 
	 (nfix n)))

;; Once we show that wins(l,m) and wins(m,l) are equal to the two sums 
;; in the lemma equal-residue-even-plus, it will follow that the sum of
;; those two sums is that product, as desired.

(defthm wins1-bnd-len
    (<= (wins1 a l) (len l))
  :rule-classes ())

(defun wins1-bnd-induction (i n)
  (declare (xargs :measure (nfix i)))
  (if (zp i)
      t
    (if (> n i)
	t
      (if (= n i)
	  t
	(if (= n (1- i))
	    (wins1-bnd-induction (1- i) n)
	  (wins1-bnd-induction (1- i) n))))))

(defthm wins1-upper-bnd-lemma
    (implies (and (not (zp n))
		  (not (zp p))
		  (integerp a)
		  (< a (* n p)))
	     (< (wins1 a (mults i p)) n))
  :rule-classes ()
  :hints (("Goal" :induct (wins1-bnd-induction i n))
	  ("Subgoal *1/5" :use ((:instance wins1-bnd-len (l (mults (1- i) p)))))
	  ("Subgoal *1/3" :use ((:instance wins1-bnd-len (l (mults (1- i) p)))))
	  ("Subgoal *1/2" :use ((:instance wins1-bnd-len (l (mults (1- i) p)))))))

(defthm wins1-upper-bnd
    (implies (and (not (zp p))
		  (natp a))
	     (<= (wins1 a (mults i p)) (fl (/ a p))))
  :rule-classes ()
  :hints (("Goal" :use ((:instance wins1-upper-bnd-lemma (n (1+ (fl (/ a p)))))))))

(defthm monotone-wins1
    (implies (and (integerp m)
		  (integerp n)
		  (<= m n))
	     (<= (wins1 a (mults m p)) (wins1 a (mults n p))))
  :rule-classes ()
  :hints (("Goal" :induct (mults n p))))

(defthm leq-n-wins1
    (implies (and (not (zp p))
		  (integerp n)
		  (integerp a)
		  (< (* n p) a))
	     (<= n (wins1 a (mults n p))))
  :rule-classes ()
  :hints (("Goal" :induct (mults n p))))

(defthm leq-fl-wins1
  (implies (and (not (zp p))
		(integerp n)
		(integerp a)
		(not (divides p a))
                (<= (fl (/ a p)) n))
           (<= (fl (/ a p)) (wins1 a (mults n p))))
  :rule-classes ()
  :hints (("Goal" :in-theory (enable divides)
		  :use ((:instance monotone-wins1 (m (fl (/ a p))))
			(:instance leq-n-wins1 (n (fl (/ a p))))))))

(defthm leq-times-fl
    (implies (and (integerp a)
		  (integerp c)
		  (not (zp d))
		  (not (zp b))
		  (<= (* a b) (* c d)))
	     (<= (fl (/ a d)) (fl (/ c b))))
  :rule-classes ()
  :hints (("Goal" :use ((:instance fl-def (x (/ a d)))
			(:instance n<=fl-linear (n (fl (/ a d))) (x (/ c b)))))))

(defthm leq-fl-times
    (implies (and (integerp j)
		  (integerp q)
		  (not (zp p))
		  (not (zp q))
		  (oddp p)
		  (oddp q)
		  (<= j (/ (1- p) 2)))
	     (<= (fl (/ (* j q) p)) (/ (1- q) 2)))
  :hints (("Goal" :in-theory (enable evenp)
		  :use ((:instance leq-times-fl (a (* j q)) (b 2) (c q) (d p))
			(:instance *-strongly-monotonic (y (* 2 j)) (y+ p) (x q))))))

(defthm wins1-lower-bnd
    (implies (and (not (zp j))
		  (integerp q)
		  (primep p)
		  (primep q)
		  (not (= p q))
		  (oddp p)
		  (oddp q)
		  (<= j (/ (1- p) 2)))
	     (<= (fl (/ (* j q) p))
		 (wins1 (* j q) (mults (/ (1- q) 2) p))))
  :hints (("Goal" :use (leq-fl-times
			(:instance leq-fl-wins1 (a (* j q)) (n (/ (1- q) 2)))))))

(defthm equal-fl-wins1
  (implies (and (not (zp j))
		(integerp q)
		(primep p)
		(primep q)
		(not (= p q))
		(oddp p)
		(oddp q)
		(<= j (/ (1- p) 2)))
           (equal (wins1 (* j q) (mults (+ -1/2 (* 1/2 q)) p))
                  (fl (/ (* j q) p))))
  :hints (("Goal" :use (wins1-lower-bnd
			(:instance wins1-upper-bnd (a (* j q)) (i (/ (1- q) 2)))))))

(defthm equal-wins-plus-list
  (implies (and (not (zp j))
		(integerp q)
		(primep p)
		(primep q)
		(not (= p q))
		(oddp p)
		(oddp q)
		(<= j (/ (1- p) 2)))
           (equal (plus-list (fl-prods j q p))
                  (wins (mults j q) (mults (/ (1- q) 2) p))))
  :hints (("Goal" :induct (mults j q)))) 

(defthm law-of-quadratic-reciprocity
  (implies (and (primep p) 
		(not (= p 2))
                (primep q)
		(not (= q 2))
                (not (= p q)))
           (iff (equal (residue q p) (residue p q))
                (evenp (* (/ (1- p) 2) (/ (1- q) 2))))))
