; Book grcd ; Contributed by Matyas Sustik ; Date created 2000-05-06 ; $Id: grcd.lisp,v 1.16 2001/09/05 19:18:24 matyas Exp $ ; ; Special thanks to Robert Bellarmine Krug and Matt Kaufmann for their ; insight and help in simplifying the proofs. ; ; Robert B.K. helped me through with the basics of ACL2 use, best of ; all he made me understand to use disable! ; ; Matt K. pointed out several unnecessary hypothesis and not well ; formed rewrite rules in the very first version of this book (where ; the rules have changed so much those comments were removed with the ; rules but the philosophy behind them is still present) he also ; helped with the use of local and encapsulate in order the book to ; certify. (in-package "ACL2") (include-book "int-division") (include-book "ordinals/e0-ordinal" :dir :system) (set-well-founded-relation e0-ord-<) (defmacro np (x) (list 'and (list 'integerp x) (list '<= 0 x))) (defmacro pintp (x) (list 'and (list 'integerp x) (list '< 0 x))) ; We define the Euclidean algoritm for natural numbers, but we return ; not only the greatest common divisor but the coefficients of the ; linear combination which produces the gcd from the arguments. We ; also return the quotients. ; ; Then the definition is extended for all the integers. The reason ; that we want gcd defined for all integers is to be able to easily ; prove: (a; b) = (a; b + a*c). It is cumbersome to deal with the c < ; 0, b + a*c > 0 case which makes a possible induction argument ; complicated. ; ; Care must be taken to get (Euclid-alg-nat a b) and (Euclid-alg-nat b ; a) to have the same coefficients. Note that the linear combination ; is not unique. (defun Euclid-alg-nat (a b) (declare (xargs :measure (if (and (np a) (np b)) (cons (+ a 1) (+ b 1)) 0))) (if (and (np a) (np b)) (cond ((equal a b) (mv a 1 0 1 1)) ; has to be the first for (0 0). ((equal b 0) (mv a 1 0 1 0)) ((equal a 0) (mv b 0 1 0 1)) ((< b a) (mv-let (g x y u v) (Euclid-alg-nat b a) (mv g y x v u))) (t (mv-let (g x y u v) (Euclid-alg-nat a (- b a)) (mv g (- x y) y u (+ u v))))) (mv 0 0 0 0 0))) (defun Euclid-alg (a b) (if (and (integerp a) (integerp b)) (cond ((and (<= 0 a) (<= 0 b)) (Euclid-alg-nat a b)) ((and (> 0 a) (<= 0 b)) (mv-let (g x y u v) (Euclid-alg-nat (- a) b) (mv g (- x) y (- u) v))) ((and (<= 0 a) (> 0 b)) (mv-let (g x y u v) (Euclid-alg-nat a (- b)) (mv g x (- y) u (- v)))) (t (mv-let (g x y u v) (Euclid-alg-nat (- a) (- b)) (mv g (- x) (- y) (- u) (- v))))) (mv 0 0 0 0 0))) (local (defthm Euclid-alg-nat-gives-gcd (implies (and (np a) (np b)) (mv-let (g x y u v) (Euclid-alg-nat a b) (and (equal (* g u) a) (equal (* g v) b) (equal (+ (* x a) (* y b)) g)))))) ; MattK: The following may allow the previous rules to be applied ; automatically more often, since (mv-nth 0 ...) will not be expanded ; to (car ...). To see what's going on, try :pr ; Euclid-alg-nat-gives-gcdq. Anyhow, some terms of the form (car ...) ; below now is mv-nth. (local (in-theory (disable mv-nth))) ; This basically says that (car (Euclid-alg a b)) is the greatest among ; divisors. (local (defthm Euclid-alg-nat-commutes-with-* (implies (and (np a) (np b) (pintp c)) (equal (Euclid-alg-nat (* a c) (* b c)) (mv-let (g x y u v) (Euclid-alg-nat a b) (mv (* g c) x y u v)))))) ; The use of :corollary was suggested by Robert B.K. (local (defthm Euclid-alg-nat-type (implies (and (np a) (np b)) (mv-let (g x y u v) (Euclid-alg-nat a b) (and (np g) (integerp x) (integerp y) (np u) (np v)))) :rule-classes ((:type-prescription :corollary (np (mv-nth 0 (Euclid-alg-nat a b)))) (:type-prescription :corollary (integerp (mv-nth 1 (Euclid-alg-nat a b)))) (:type-prescription :corollary (integerp (mv-nth 2 (Euclid-alg-nat a b)))) (:type-prescription :corollary (np (mv-nth 3 (Euclid-alg-nat a b)))) (:type-prescription :corollary (np (mv-nth 4 (Euclid-alg-nat a b))))))) ; Matt K. suggested this rule to be a :type-prescription rule. (local (defthm Euclid-alg-nat-0-0 (implies (and (np a) (np b) (or (< 0 a) (< 0 b))) (and (integerp (mv-nth 0 (Euclid-alg-nat a b))) (< 0 (mv-nth 0 (Euclid-alg-nat a b))))) :rule-classes :type-prescription)) (local (defun ind-int (n) (if (integerp n) (if (< 0 n) (ind-int (+ -1 n)) t) t))) (local (defthm Euclid-alg-nat-spec-1-a (implies (np a) (equal (Euclid-alg-nat 1 a) (mv 1 1 0 1 a))) :hints (("Goal" :induct (ind-int a)) ("Subgoal *1/2''" :expand (Euclid-alg-nat a 1))))) ; This cannot be a rewrite rule. ACL2 would not find a loop stopper ; for this complex expression. ; MattK: So, we supply our own loop stopper. See the documentation on ; :rewrite in topic rule-classes, which points to the documentation ; for loop-stopper. (local (defthm Euclid-alg-nat-is-commutative (implies (and (np a) (np b) (or (< a b) (< b a))) (equal (Euclid-alg-nat a b) (mv-let (g x y u v) (Euclid-alg-nat b a) (mv g y x v u)))) :rule-classes ((:rewrite :loop-stopper ((a b)))))) ; Access functions: (defun grcd (a b) (mv-let (g x y u v) (Euclid-alg a b) (declare (ignore x y u v)) g)) (defun grcd-lin-1 (a b) (mv-let (g x y u v) (Euclid-alg a b) (declare (ignore g y u v)) x)) (defun grcd-lin-2 (a b) (mv-let (g x y u v) (Euclid-alg a b) (declare (ignore g x u v)) y)) (defun grcd-quotient-1 (a b) (mv-let (g x y u v) (Euclid-alg a b) (declare (ignore g x y v)) u)) (defun grcd-quotient-2 (a b) (mv-let (g x y u v) (Euclid-alg a b) (declare (ignore g x y u)) v)) (defthm grcd-type-int (np (grcd a b)) :rule-classes :type-prescription) ; MattK: It seems reasonable to generate a case split in subsequent theorems ; when we try to apply this lemma but we cannot eliminate the case that b=0; ; presumably that extra case will be easy to prove. (defthm grcd-positive (implies (and (integerp a) (integerp b) (case-split (not (and (equal 0 a) (equal 0 b))))) (pintp (grcd a b))) :rule-classes :type-prescription) (defthm grcd-lin-1-type (integerp (grcd-lin-1 a b)) :rule-classes :type-prescription) (defthm grcd-lin-2-type (integerp (grcd-lin-2 a b)) :rule-classes :type-prescription) (defthm grcd-quotient-1-type (integerp (grcd-quotient-1 a b)) :rule-classes :type-prescription) (defthm grcd-quotient-2-type (integerp (grcd-quotient-2 a b)) :rule-classes :type-prescription) ; MattK: I prefer an unconditional rule here. (And in the next two ; theorems.) (defthm grcd-spec-a-0 (equal (grcd a 0) (abs (ifix a)))) (defthm grcd-spec-a-a (equal (grcd a a) (abs (ifix a)))) (defthm grcd-spec-a-1 (equal (grcd 1 a) (if (integerp a) 1 0))) (in-theory (disable Euclid-alg-nat)) ; Thanks to Matt K. to point out how to split into cases in the proof. (defthm grcd-commutativity (implies (and (integerp a) (integerp b)) (equal (grcd a b) (grcd b a))) :hints (("Goal" :cases ((and (<= a 0) (<= b 0) (< (- a) (- b))) (and (<= a 0) (<= b 0) (< (- b) (- a))) (and (<= a 0) (<= 0 b) (< (- a) b)) (and (<= a 0) (<= 0 b) (< b (- a))) (and (<= 0 a) (<= b 0) (< a (- b))) (and (<= 0 a) (<= b 0) (< (- b) a)) (and (<= 0 a) (<= 0 b) (< a b)) (and (<= 0 a) (<= 0 b) (< b a)))))) (local (in-theory (disable Euclid-alg-nat-gives-gcd))) (local (defthm Euclid-alg-nat-gives-gcd-alternate (implies (and (np a) (np b)) (equal (mv-nth 0 (Euclid-alg-nat a b)) (+ (* (mv-nth 1 (Euclid-alg-nat a b)) a) (* (mv-nth 2 (Euclid-alg-nat a b)) b)))) :hints (("Goal" :use Euclid-alg-nat-gives-gcd)))) (defthm grcd-is-linear-combination (equal (grcd a b) (+ (* a (grcd-lin-1 a b)) (* b (grcd-lin-2 a b))))) (local (in-theory (disable Euclid-alg-nat-gives-gcd-alternate))) (local (in-theory (disable Euclid-alg-nat-commutes-with-*))) (defthm grcd-commutes-with-* (implies (and (integerp a) (integerp b) (np c)) (equal (grcd (* a c) (* b c)) (* (grcd a b) c))) :hints (("Goal" :use (Euclid-alg-nat-commutes-with-* (:instance Euclid-alg-nat-commutes-with-* (a (- a)) (b (- b))) (:instance Euclid-alg-nat-commutes-with-* (b (- b))) (:instance Euclid-alg-nat-commutes-with-* (b (- b))) (:instance Euclid-alg-nat-commutes-with-* (a (- a))))))) ; MattK: On a hunch, I switched which of the following two lemmas to keep ; enabled for the proof of grcd-divides-op-1-lemma. Lucky me. (local (in-theory (enable Euclid-alg-nat-gives-gcd))) (local (in-theory (disable Euclid-alg-nat-gives-gcd-alternate))) (defthm grcd-divides-op-1-lemma (implies (and (integerp a) (integerp b)) (equal (* (grcd a b) (grcd-quotient-1 a b)) a))) (defthm grcd-divides-op-2-lemma (implies (and (integerp a) (integerp b)) (equal (* (grcd a b) (grcd-quotient-2 a b)) b))) ; We have all the theorems about the access functions so we can disable ; them. (in-theory (disable grcd)) (in-theory (disable grcd-lin-1)) (in-theory (disable grcd-lin-2)) (in-theory (disable grcd-quotient-1)) (in-theory (disable grcd-quotient-2)) (defthm grcd-quotient-1-not-zero (implies (and (integerp a) (integerp b) (equal 0 (grcd-quotient-1 a b))) (equal (equal 0 (grcd-quotient-1 a b)) (equal 0 a))) :hints (("Goal" :in-theory (disable grcd-divides-op-1-lemma) :use grcd-divides-op-1-lemma))) (defthm grcd-quotient-2-not-zero (implies (and (integerp a) (integerp b) (equal 0 (grcd-quotient-2 a b))) (equal (equal 0 (grcd-quotient-2 a b)) (equal 0 b))) :hints (("Goal" :in-theory (disable grcd-divides-op-2-lemma) :use grcd-divides-op-2-lemma))) (defthm common-divisor-divides-grcd (implies (and (divides d a) (divides d b)) (divides d (grcd a b)))) ; This is a useful lemma but not frequently needed. (in-theory (disable grcd-is-linear-combination)) (local (defthm grcd-quotients-give-a-/-b-lemma (implies (and (integerp a) (integerp b) (not (equal 0 b))) (equal (/ (* (grcd a b) (grcd-quotient-1 a b)) (* (grcd a b) (grcd-quotient-2 a b))) (/ a b))) :rule-classes nil :hints (("Goal" :in-theory (disable associativity-of-* distributivity-of-/-over-* /-cancellation-on-left))))) (defthm grcd-quotients-gives-a-/-b (implies (and (integerp a) (integerp b) (not (equal 0 b))) (equal (/ (grcd-quotient-1 a b) (grcd-quotient-2 a b)) (/ a b))) :hints (("Goal" :use grcd-quotients-give-a-/-b-lemma))) (local (defthm grcd-quotients-are-relative-prime-lemma-1 (implies (and (integerp a) (integerp b)) (equal (grcd a b) (grcd (* (grcd-quotient-1 a b) (grcd a b)) (* (grcd-quotient-2 a b) (grcd a b))))) :rule-classes nil)) (local (defthm grcd-quotients-are-relative-prime-lemma-2 (implies (and (integerp a) (integerp b)) (equal (grcd (* (grcd-quotient-1 a b) (grcd a b)) (* (grcd-quotient-2 a b) (grcd a b))) (* (grcd a b) (grcd (grcd-quotient-1 a b) (grcd-quotient-2 a b))))) :rule-classes nil :hints (("Goal" :use ((:instance grcd-commutes-with-* (a (grcd-quotient-1 a b)) (b (grcd-quotient-2 a b)) (c (grcd a b)))))))) (defthm grcd-quotients-are-relative-primes (implies (and (integerp a) (integerp b)) (equal (grcd (grcd-quotient-1 a b) (grcd-quotient-2 a b)) 1)) :hints (("Goal" :use (grcd-quotients-are-relative-prime-lemma-1 grcd-quotients-are-relative-prime-lemma-2)))) (defthm grcd-quotient-1-description (implies (and (integerp a) (integerp b)) (equal (integer-quotient (grcd a b) a) (grcd-quotient-1 a b))) :hints (("Goal" :use ((:instance integer-quotient-*-cancellation (a (grcd a b)) (q (grcd-quotient-1 a b))))))) (defthm grcd-quotient-2-description (implies (and (integerp a) (integerp b)) (equal (integer-quotient (grcd a b) b) (grcd-quotient-2 a b))) :hints (("Goal" :use ((:instance integer-quotient-*-cancellation (a (grcd a b)) (q (grcd-quotient-2 a b))))))) (in-theory (enable divides-integer-quotient-equivalence)) (defthm grcd-divides-op-1 (implies (and (integerp a) (integerp b)) (divides (grcd a b) a))) (defthm grcd-divides-op-2 (implies (and (integerp a) (integerp b)) (divides (grcd a b) b))) (in-theory (disable divides-integer-quotient-equivalence)) ; MattK: Might as well make this a :rewrite rule. (defthm grcd-addition-lemma-1 (implies (and (integerp a) (integerp b) (integerp c)) (divides (grcd a b) (grcd a (+ b (* a c)))))) ; MattK: Might as well make this a :rewrite rule. (defthm grcd-addition-lemma-2 (implies (and (integerp a) (integerp b) (integerp c)) (divides (grcd a (+ b (* a c))) (grcd a b))) :hints (("Goal" :use ((:instance grcd-addition-lemma-1 (b (+ b (* a c))) (c (- c))))))) ; Note that this is not proved with an induction on c. (defthm grcd-addition-lemma (implies (and (integerp a) (integerp b) (integerp c)) (equal (grcd a (+ b (* a c))) (grcd a b))) :hints (("Goal" :use ((:instance equality-from-division (a (grcd a (+ b (* a c)))) (b (grcd a b))))))) (defthm grcd-a-b-divides-a-bc (implies (and (integerp a) (integerp b) (integerp c)) (divides (grcd a b) (grcd a (* b c))))) ; The following 6 lemmas lead up to the proof of: ; (a; c) = 1 => (a; b) = (a; b*c). ; The major steps are separated in the lemmas: ; (a; c) = 1 => 1 = a*x + c*z ; multiply with b: b = b*a*x + b*c*z ; (a; b) = (a; b*a*x + b*c*z) = (a ; b*c*z) ; From the grcd-a-b-divides-a-bc theorem we have: ; (a; b) divides (a; b*c) which in turn divides (a; b*c*z). ; using the divider-< lemmas we are done. (local (defthm grcd-multiplication-lemma-1 (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (grcd a (* b c (grcd-lin-2 a c))) (grcd a (+ (* b a (grcd-lin-1 a c)) (* b c (grcd-lin-2 a c)))))) :hints (("Goal" :use ((:instance grcd-addition-lemma (b (* b c (grcd-lin-2 a c))) (c (* b (grcd-lin-1 a c))))))))) ; MattK; Might as well make the following a :rewrite rule. I removed ; the hypothesis (integerp b), which involved the free variable b and ; thus could prevent the rule from firing. Unfortunately, the proof ; of grcd-multiplication-lemma-4 failed when I made this a :rewrite ; rule, which is why it is now local to an encapsulate. (local (encapsulate () (local (defthm grcd-multiplication-lemma-2 (implies (and (integerp a) (integerp c) (equal 1 (grcd a c))) (equal (+ (* a (grcd-lin-1 a c)) (* c (grcd-lin-2 a c))) 1)) :hints (("Goal" :in-theory (enable grcd-is-linear-combination))))) (defthm grcd-multiplication-lemma-3 (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (* b (+ (* a (grcd-lin-1 a c)) (* c (grcd-lin-2 a c)))) b)) :rule-classes nil))) (local (defthm grcd-multiplication-lemma-4 (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (+ (* b a (grcd-lin-1 a c)) (* b c (grcd-lin-2 a c))) b)) :hints (("Goal" :use grcd-multiplication-lemma-3)))) (local (defthm grcd-multiplication-lemma-5 (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (grcd a (+ (* b a (grcd-lin-1 a c)) (* b c (grcd-lin-2 a c)))) (grcd a b))) :rule-classes nil :hints (("Goal" :use grcd-multiplication-lemma-4)))) (local (defthm grcd-multiplication-lemma-6 (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (grcd a (* b c (grcd-lin-2 a c))) (grcd a b))) :rule-classes nil :hints (("Goal" :use grcd-multiplication-lemma-5)))) (in-theory (disable common-divisor-divides-grcd)) (defthm grcd-multiplication-with-relative-prime (implies (and (integerp a) (integerp b) (integerp c) (equal 1 (grcd a c))) (equal (grcd a (* b c)) (grcd a b))) :hints (("Goal" :use ((:instance grcd-a-b-divides-a-bc (b (* b c)) (c (grcd-lin-2 a c))) grcd-multiplication-lemma-6 (:instance equality-from-division (a (grcd a b)) (b (grcd a (* b c)))))))) (in-theory (enable divides-integer-quotient-equivalence)) ; The following may be useful theorems but are not needed for the ; Fibonacci theorem in fibonacci.lisp. They can be used to prove the ; existence of lowest terms for rationals. (defthm grcd-*-cancellation (implies (and (equal b (* a (integer-quotient a b))) (np a) (integerp b)) (equal (grcd a b) a)) :hints (("Goal" :use (:instance grcd-commutes-with-* (a 1) (b (integer-quotient a b)) (c a))))) (defthm grcd-divides-connection-lemma-1 (implies (and (np a) (integerp b)) (implies (divides a b) (equal (grcd a b) a))) :hints (("Goal'''" :use grcd-*-cancellation))) (in-theory (disable divides-integer-quotient-equivalence)) (defthm grcd-divides-connection-lemma-2 (implies (and (np a) (integerp b)) (implies (equal (grcd a b) a) (divides a b))) :hints (("Goal'''" :use grcd-divides-op-2))) (defthm grcd-divides-connection (implies (and (np a) (integerp b)) (equal (divides a b) (equal (grcd a b) a)))) (in-theory (disable grcd-*-cancellation)) (in-theory (disable grcd-divides-connection-lemma-1)) (in-theory (disable grcd-divides-connection-lemma-2)) (defthm divide-with-relative-prime-lemma-1 (implies (and (np a) (integerp b) (integerp c) (divides a (* b c)) (equal (grcd a c) 1)) (equal (grcd a (* b c)) a)) :rule-classes nil) (defthm divide-with-relative-prime-lemma-2 (implies (and (np a) (integerp b) (integerp c) (divides a (* b c)) (equal (grcd a c) 1)) (equal (grcd a b) a)) :rule-classes nil :hints (("Goal" :use divide-with-relative-prime-lemma-1))) (defthm divide-with-relative-prime (implies (and (np a) (integerp b) (integerp c) (divides a (* b c)) (equal (grcd a c) 1)) (divides a b)) :hints (("Goal" :use divide-with-relative-prime-lemma-2)))