(*************************************************************) (* This file is distributed under the terms of the *) (* GNU Lesser General Public License Version 2.1 *) (*************************************************************) (* Benjamin.Gregoire@inria.fr Laurent.Thery@inria.fr *) (*************************************************************) (********************************************************************** EGroup.v Given an element a, create the group {e, a, a^2, ..., a^n} **********************************************************************) Require Import Coq.ZArith.ZArith. Require Import Coqprime.Tactic. Require Import Coq.Lists.List. Require Import Coqprime.ZCAux. Require Import Coq.ZArith.ZArith Coq.ZArith.Znumtheory. Require Import Coq.Arith.Wf_nat. Require Import Coqprime.UList. Require Import Coqprime.FGroup. Require Import Coqprime.Lagrange. Open Scope Z_scope. Section EGroup. Variable A: Set. Variable A_dec: forall a b: A, {a = b} + {~ a = b}. Variable op: A -> A -> A. Variable a: A. Variable G: FGroup op. Hypothesis a_in_G: In a G.(s). (************************************** The power function for the group **************************************) Set Implicit Arguments. Definition gpow n := match n with Zpos p => iter_pos p _ (op a) G.(e) | _ => G.(e) end. Unset Implicit Arguments. Theorem gpow_0: gpow 0 = G.(e). simpl; sauto. Qed. Theorem gpow_1 : gpow 1 = a. simpl; sauto. Qed. (************************************** Some properties of the power function **************************************) Theorem gpow_in: forall n, In (gpow n) G.(s). intros n; case n; simpl; auto. intros p; apply iter_pos_invariant with (Inv := fun x => In x G.(s)); auto. Qed. Theorem gpow_op: forall b p, In b G.(s) -> iter_pos p _ (op a) b = op (iter_pos p _ (op a) G.(e)) b. intros b p; generalize b; elim p; simpl; auto; clear b p. intros p Rec b Hb. assert (H: In (gpow (Zpos p)) G.(s)). apply gpow_in. rewrite (Rec b); try rewrite (fun x y => Rec (op x y)); try rewrite (fun x y => Rec (iter_pos p A x y)); auto. repeat rewrite G.(assoc); auto. intros p Rec b Hb. assert (H: In (gpow (Zpos p)) G.(s)). apply gpow_in. rewrite (Rec b); try rewrite (fun x y => Rec (op x y)); try rewrite (fun x y => Rec (iter_pos p A x y)); auto. repeat rewrite G.(assoc); auto. intros b H; rewrite e_is_zero_r; auto. Qed. Theorem gpow_add: forall n m, 0 <= n -> 0 <= m -> gpow (n + m) = op (gpow n) (gpow m). intros n; case n. intros m _ _; simpl; apply sym_equal; apply e_is_zero_l; apply gpow_in. 2: intros p m H; contradict H; auto with zarith. intros p1 m; case m. intros _ _; simpl; apply sym_equal; apply e_is_zero_r. exact (gpow_in (Zpos p1)). 2: intros p2 _ H; contradict H; auto with zarith. intros p2 _ _; simpl. rewrite iter_pos_plus; rewrite (fun x y => gpow_op (iter_pos p2 A x y)); auto. exact (gpow_in (Zpos p2)). Qed. Theorem gpow_1_more: forall n, 0 < n -> gpow n = G.(e) -> forall m, 0 <= m -> exists p, 0 <= p < n /\ gpow m = gpow p. intros n H1 H2 m Hm; generalize Hm; pattern m; apply Z_lt_induction; auto with zarith; clear m Hm. intros m Rec Hm. case (Zle_or_lt n m); intros H3. case (Rec (m - n)); auto with zarith. intros p (H4,H5); exists p; split; auto. replace m with (n + (m - n)); auto with zarith. rewrite gpow_add; try rewrite H2; try rewrite H5; sauto; auto with zarith. generalize gpow_in; sauto. exists m; auto. Qed. Theorem gpow_i: forall n m, 0 <= n -> 0 <= m -> gpow n = gpow (n + m) -> gpow m = G.(e). intros n m H1 H2 H3; generalize gpow_in; intro PI. apply g_cancel_l with (g:= G) (a := gpow n); sauto. rewrite <- gpow_add; try rewrite <- H3; sauto. Qed. (************************************** We build the support by iterating the power function **************************************) Set Implicit Arguments. Fixpoint support_aux (b: A) (n: nat) {struct n}: list A := b::let c := op a b in match n with O => nil | (S n1) =>if A_dec c G.(e) then nil else support_aux c n1 end. Definition support := support_aux G.(e) (Zabs_nat (g_order G)). Unset Implicit Arguments. (************************************** Some properties of the support that helps to prove that we have a group **************************************) Theorem support_aux_gpow: forall n m b, 0 <= m -> In b (support_aux (gpow m) n) -> exists p, (0 <= p < length (support_aux (gpow m) n))%nat /\ b = gpow (m + Z_of_nat p). intros n; elim n; simpl. intros n1 b Hm [H1 | H1]; exists 0%nat; simpl; rewrite Zplus_0_r; auto; case H1. intros n1 Rec m b Hm [H1 | H1]. exists 0%nat; simpl; rewrite Zplus_0_r; auto; auto with arith. generalize H1; case (A_dec (op a (gpow m)) G.(e)); clear H1; simpl; intros H1 H2. case H2. case (Rec (1 + m) b); auto with zarith. rewrite gpow_add; auto with zarith. rewrite gpow_1; auto. intros p (Hp1, Hp2); exists (S p); split; auto with zarith. rewrite <- gpow_1. rewrite <- gpow_add; auto with zarith. rewrite inj_S; rewrite Hp2; eq_tac; auto with zarith. Qed. Theorem gpow_support_aux_not_e: forall n m p, 0 <= m -> m < p < m + Z_of_nat (length (support_aux (gpow m) n)) -> gpow p <> G.(e). intros n; elim n; simpl. intros m p Hm (H1, H2); contradict H2; auto with zarith. intros n1 Rec m p Hm; case (A_dec (op a (gpow m)) G.(e)); simpl. intros _ (H1, H2); contradict H2; auto with zarith. assert (tmp: forall p, Zpos (P_of_succ_nat p) = 1 + Z_of_nat p). intros p1; apply trans_equal with (Z_of_nat (S p1)); auto; rewrite inj_S; auto with zarith. rewrite tmp. intros H1 (H2, H3); case (Zle_lt_or_eq (1 + m) p); auto with zarith; intros H4; subst. apply (Rec (1 + m)); try split; auto with zarith. rewrite gpow_add; auto with zarith. rewrite gpow_1; auto with zarith. rewrite gpow_add; try rewrite gpow_1; auto with zarith. Qed. Theorem support_aux_not_e: forall n m b, 0 <= m -> In b (tail (support_aux (gpow m) n)) -> ~ b = G.(e). intros n; elim n; simpl. intros m b Hm H; case H. intros n1 Rec m b Hm; case (A_dec (op a (gpow m)) G.(e)); intros H1 H2; simpl; auto. assert (Hm1: 0 <= 1 + m); auto with zarith. generalize( Rec (1 + m) b Hm1) H2; case n1; auto; clear Hm1. intros _ [H3 | H3]; auto. contradict H1; subst; auto. rewrite gpow_add; simpl; try rewrite e_is_zero_r; auto with zarith. intros n2; case (A_dec (op a (op a (gpow m))) G.(e)); intros H3. intros _ [H4 | H4]. contradict H1; subst; auto. case H4. intros H4 [H5 | H5]; subst; auto. Qed. Theorem support_aux_length_le: forall n a, (length (support_aux a n) <= n + 1)%nat. intros n; elim n; simpl; auto. intros n1 Rec a1; case (A_dec (op a a1) G.(e)); simpl; auto with arith. Qed. Theorem support_aux_length_le_is_e: forall n m, 0 <= m -> (length (support_aux (gpow m) n) <= n)%nat -> gpow (m + Z_of_nat (length (support_aux (gpow m) n))) = G.(e) . intros n; elim n; simpl; auto. intros m _ H1; contradict H1; auto with arith. intros n1 Rec m Hm; case (A_dec (op a (gpow m)) G.(e)); simpl; intros H1. intros H2; rewrite Zplus_comm; rewrite gpow_add; simpl; try rewrite e_is_zero_r; auto with zarith. assert (tmp: forall p, Zpos (P_of_succ_nat p) = 1 + Z_of_nat p). intros p1; apply trans_equal with (Z_of_nat (S p1)); auto; rewrite inj_S; auto with zarith. rewrite tmp; clear tmp. rewrite <- gpow_1. rewrite <- gpow_add; auto with zarith. rewrite Zplus_assoc; rewrite (Zplus_comm 1); intros H2; apply Rec; auto with zarith. Qed. Theorem support_aux_in: forall n m p, 0 <= m -> (p < length (support_aux (gpow m) n))% nat -> (In (gpow (m + Z_of_nat p)) (support_aux (gpow m) n)). intros n; elim n; simpl; auto; clear n. intros m p Hm H1; replace p with 0%nat. left; eq_tac; auto with zarith. generalize H1; case p; simpl; auto with arith. intros n H2; contradict H2; apply le_not_lt; auto with arith. intros n1 Rec m p Hm; case (A_dec (op a (gpow m)) G.(e)); simpl; intros H1 H2; auto. replace p with 0%nat. left; eq_tac; auto with zarith. generalize H2; case p; simpl; auto with arith. intros n H3; contradict H3; apply le_not_lt; auto with arith. generalize H2; case p; simpl; clear H2. rewrite Zplus_0_r; auto. intros n. assert (tmp: forall p, Zpos (P_of_succ_nat p) = 1 + Z_of_nat p). intros p1; apply trans_equal with (Z_of_nat (S p1)); auto; rewrite inj_S; auto with zarith. rewrite tmp; clear tmp. rewrite <- gpow_1; rewrite <- gpow_add; auto with zarith. rewrite Zplus_assoc; rewrite (Zplus_comm 1); intros H2; right; apply Rec; auto with zarith. Qed. Theorem support_aux_ulist: forall n m, 0 <= m -> (forall p, 0 <= p < m -> gpow (1 + p) <> G.(e)) -> ulist (support_aux (gpow m) n). intros n; elim n; auto; clear n. intros m _ _; auto. simpl; apply ulist_cons; auto. intros n1 Rec m Hm H. simpl; case (A_dec (op a (gpow m)) G.(e)); auto. intros He; apply ulist_cons; auto. intros H1; case (support_aux_gpow n1 (1 + m) (gpow m)); auto with zarith. rewrite gpow_add; try rewrite gpow_1; auto with zarith. intros p (Hp1, Hp2). assert (H2: gpow (1 + Z_of_nat p) = G.(e)). apply gpow_i with m; auto with zarith. rewrite Hp2; eq_tac; auto with zarith. case (Zle_or_lt m (Z_of_nat p)); intros H3; auto. 2: case (H (Z_of_nat p)); auto with zarith. case (support_aux_not_e (S n1) m (gpow (1 + Z_of_nat p))); auto. rewrite gpow_add; auto with zarith; simpl; rewrite e_is_zero_r; auto. case (A_dec (op a (gpow m)) G.(e)); auto. intros _; rewrite <- gpow_1; repeat rewrite <- gpow_add; auto with zarith. replace (1 + Z_of_nat p) with ((1 + m) + (Z_of_nat (p - Zabs_nat m))); auto with zarith. apply support_aux_in; auto with zarith. rewrite inj_minus1; auto with zarith. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. apply inj_le_rev. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. rewrite <- gpow_1; repeat rewrite <- gpow_add; auto with zarith. apply (Rec (1 + m)); auto with zarith. intros p H1; case (Zle_lt_or_eq p m); intros; subst; auto with zarith. rewrite gpow_add; auto with zarith. rewrite gpow_1; auto. Qed. Theorem support_gpow: forall b, (In b support) -> exists p, 0 <= p < Z_of_nat (length support) /\ b = gpow p. intros b H; case (support_aux_gpow (Zabs_nat (g_order G)) 0 b); auto with zarith. intros p ((H1, H2), H3); exists (Z_of_nat p); repeat split; auto with zarith. apply inj_lt; auto. Qed. Theorem support_incl_G: incl support G.(s). intros a1 H; case (support_gpow a1); auto; intros p (H1, H2); subst; apply gpow_in. Qed. Theorem gpow_support_not_e: forall p, 0 < p < Z_of_nat (length support) -> gpow p <> G.(e). intros p (H1, H2); apply gpow_support_aux_not_e with (m := 0) (n := length G.(s)); simpl; try split; auto with zarith. rewrite <- (Zabs_nat_Z_of_nat (length G.(s))); auto. Qed. Theorem support_not_e: forall b, In b (tail support) -> ~ b = G.(e). intros b H; apply (support_aux_not_e (Zabs_nat (g_order G)) 0); auto with zarith. Qed. Theorem support_ulist: ulist support. apply (support_aux_ulist (Zabs_nat (g_order G)) 0); auto with zarith. Qed. Theorem support_in_e: In G.(e) support. unfold support; case (Zabs_nat (g_order G)); simpl; auto with zarith. Qed. Theorem gpow_length_support_is_e: gpow (Z_of_nat (length support)) = G.(e). apply (support_aux_length_le_is_e (Zabs_nat (g_order G)) 0); simpl; auto with zarith. unfold g_order; rewrite Zabs_nat_Z_of_nat; apply ulist_incl_length. rewrite <- (Zabs_nat_Z_of_nat (length G.(s))); auto. exact support_ulist. rewrite <- (Zabs_nat_Z_of_nat (length G.(s))); auto. exact support_incl_G. Qed. Theorem support_in: forall p, 0 <= p < Z_of_nat (length support) -> In (gpow p) support. intros p (H, H1); unfold support. rewrite <- (Zabs_eq p); auto with zarith. rewrite <- (inj_Zabs_nat p); auto. generalize (support_aux_in (Zabs_nat (g_order G)) 0); simpl; intros H2; apply H2; auto with zarith. rewrite <- (fun x => Zabs_nat_Z_of_nat (@length A x)); auto. apply Zabs_nat_lt; split; auto. Qed. Theorem support_internal: forall a b, In a support -> In b support -> In (op a b) support. intros a1 b1 H1 H2. case support_gpow with (1 := H1); auto; intros p1 ((H3, H4), H5); subst. case support_gpow with (1 := H2); auto; intros p2 ((H5, H6), H7); subst. rewrite <- gpow_add; auto with zarith. case gpow_1_more with (m:= p1 + p2) (2 := gpow_length_support_is_e); auto with zarith. intros p3 ((H8, H9), H10); rewrite H10; apply support_in; auto with zarith. Qed. Theorem support_i_internal: forall a, In a support -> In (G.(i) a) support. generalize gpow_in; intros Hp. intros a1 H1. case support_gpow with (1 := H1); auto. intros p1 ((H2, H3), H4); case Zle_lt_or_eq with (1 := H2); clear H2; intros H2; subst. 2: rewrite gpow_0; rewrite i_e; apply support_in_e. replace (G.(i) (gpow p1)) with (gpow (Z_of_nat (length support - Zabs_nat p1))). apply support_in; auto with zarith. rewrite inj_minus1. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. apply inj_le_rev; rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. apply g_cancel_l with (g:= G) (a := gpow p1); sauto. rewrite <- gpow_add; auto with zarith. replace (p1 + Z_of_nat (length support - Zabs_nat p1)) with (Z_of_nat (length support)). rewrite gpow_length_support_is_e; sauto. rewrite inj_minus1; auto with zarith. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. apply inj_le_rev; rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. Qed. (************************************** We are now ready to build the group **************************************) Definition Gsupport: (FGroup op). generalize support_incl_G; unfold incl; intros Ho. apply mkGroup with support G.(e) G.(i); sauto. apply support_ulist. apply support_internal. intros a1 b1 c1 H1 H2 H3; apply G.(assoc); sauto. apply support_in_e. apply support_i_internal. Defined. (************************************** Definition of the order of an element **************************************) Set Implicit Arguments. Definition e_order := Z_of_nat (length support). Unset Implicit Arguments. (************************************** Some properties of the order of an element **************************************) Theorem gpow_e_order_is_e: gpow e_order = G.(e). apply (support_aux_length_le_is_e (Zabs_nat (g_order G)) 0); simpl; auto with zarith. unfold g_order; rewrite Zabs_nat_Z_of_nat; apply ulist_incl_length. rewrite <- (Zabs_nat_Z_of_nat (length G.(s))); auto. exact support_ulist. rewrite <- (Zabs_nat_Z_of_nat (length G.(s))); auto. exact support_incl_G. Qed. Theorem gpow_e_order_lt_is_not_e: forall n, 1 <= n < e_order -> gpow n <> G.(e). intros n (H1, H2); apply gpow_support_not_e; auto with zarith. Qed. Theorem e_order_divide_g_order: (e_order | g_order G). change ((g_order Gsupport) | g_order G). apply lagrange; auto. exact support_incl_G. Qed. Theorem e_order_pos: 0 < e_order. unfold e_order, support; case (Zabs_nat (g_order G)); simpl; auto with zarith. Qed. Theorem e_order_divide_gpow: forall n, 0 <= n -> gpow n = G.(e) -> (e_order | n). generalize gpow_in; intros Hp. generalize e_order_pos; intros Hp1. intros n Hn; generalize Hn; pattern n; apply Z_lt_induction; auto; clear n Hn. intros n Rec Hn H. case (Zle_or_lt e_order n); intros H1. case (Rec (n - e_order)); auto with zarith. apply g_cancel_l with (g:= G) (a := gpow e_order); sauto. rewrite G.(e_is_zero_r); auto with zarith. rewrite <- gpow_add; try (rewrite gpow_e_order_is_e; rewrite <- H; eq_tac); auto with zarith. intros k Hk; exists (1 + k). rewrite Zmult_plus_distr_l; rewrite <- Hk; auto with zarith. case (Zle_lt_or_eq 0 n); auto with arith; intros H2; subst. contradict H; apply support_not_e. generalize H1; unfold e_order, support. case (Zabs_nat (g_order G)); simpl; auto. intros H3; contradict H3; auto with zarith. intros n1; case (A_dec (op a G.(e)) G.(e)); simpl; intros _ H3. contradict H3; auto with zarith. generalize H3; clear H3. assert (tmp: forall p, Zpos (P_of_succ_nat p) = 1 + Z_of_nat p). intros p1; apply trans_equal with (Z_of_nat (S p1)); auto; rewrite inj_S; auto with zarith. rewrite tmp; clear tmp; intros H3. change (In (gpow n) (support_aux (gpow 1) n1)). replace n with (1 + Z_of_nat (Zabs_nat n - 1)). apply support_aux_in; auto with zarith. rewrite <- (fun x => Zabs_nat_Z_of_nat (@length A x)). replace (Zabs_nat n - 1)%nat with (Zabs_nat (n - 1)). apply Zabs_nat_lt; split; auto with zarith. rewrite G.(e_is_zero_r) in H3; try rewrite gpow_1; auto with zarith. apply inj_eq_rev; rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. rewrite inj_minus1; auto with zarith. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. apply inj_le_rev; rewrite inj_Zabs_nat; simpl; auto with zarith. rewrite Zabs_eq; auto with zarith. rewrite inj_minus1; auto with zarith. rewrite inj_Zabs_nat; auto with zarith. rewrite Zabs_eq; auto with zarith. rewrite Zplus_comm; simpl; auto with zarith. apply inj_le_rev; rewrite inj_Zabs_nat; simpl; auto with zarith. rewrite Zabs_eq; auto with zarith. exists 0; auto with arith. Qed. End EGroup. Theorem gpow_gpow: forall (A : Set) (op : A -> A -> A) (a : A) (G : FGroup op), In a (s G) -> forall n m, 0 <= n -> 0 <= m -> gpow a G (n * m ) = gpow (gpow a G n) G m. intros A op a G H n m; case n. simpl; intros _ H1; generalize H1. pattern m; apply natlike_ind; simpl; auto. intros x H2 Rec _; unfold Zsucc; rewrite gpow_add; simpl; auto with zarith. repeat rewrite G.(e_is_zero_r); auto with zarith. apply gpow_in; sauto. intros p1 _; case m; simpl; auto. assert(H1: In (iter_pos p1 A (op a) (e G)) (s G)). refine (gpow_in _ _ _ _ _ (Zpos p1)); auto. intros p2 _; pattern p2; apply Pind; simpl; auto. rewrite Pmult_1_r; rewrite G.(e_is_zero_r); try rewrite G.(e_is_zero_r); auto. intros p3 Rec; rewrite Pplus_one_succ_r; rewrite Pmult_plus_distr_l. rewrite Pmult_1_r. simpl; repeat rewrite iter_pos_plus; simpl. rewrite G.(e_is_zero_r); auto. rewrite gpow_op with (G:= G); try rewrite Rec; auto. apply sym_equal; apply gpow_op; auto. intros p Hp; contradict Hp; auto with zarith. Qed. Theorem gpow_e: forall (A : Set) (op : A -> A -> A) (G : FGroup op) n, 0 <= n -> gpow G.(e) G n = G.(e). intros A op G n; case n; simpl; auto with zarith. intros p _; elim p; simpl; auto; intros p1 Rec; repeat rewrite Rec; auto. Qed. Theorem gpow_pow: forall (A : Set) (op : A -> A -> A) (a : A) (G : FGroup op), In a (s G) -> forall n, 0 <= n -> gpow a G (2 ^ n) = G.(e) -> forall m, n <= m -> gpow a G (2 ^ m) = G.(e). intros A op a G H n H1 H2 m Hm. replace m with (n + (m - n)); auto with zarith. rewrite Zpower_exp; auto with zarith. rewrite gpow_gpow; auto with zarith. rewrite H2; apply gpow_e. apply Zpower_ge_0; auto with zarith. Qed. Theorem gpow_mult: forall (A : Set) (op : A -> A -> A) (a b: A) (G : FGroup op) (comm: forall a b, In a (s G) -> In b (s G) -> op a b = op b a), In a (s G) -> In b (s G) -> forall n, 0 <= n -> gpow (op a b) G n = op (gpow a G n) (gpow b G n). intros A op a b G comm Ha Hb n; case n; simpl; auto. intros _; rewrite G.(e_is_zero_r); auto. 2: intros p Hp; contradict Hp; auto with zarith. intros p _; pattern p; apply Pind; simpl; auto. repeat rewrite G.(e_is_zero_r); auto. intros p3 Rec; rewrite Pplus_one_succ_r. repeat rewrite iter_pos_plus; simpl. repeat rewrite (fun x y H z => gpow_op A op x G H (op y z)) ; auto. rewrite Rec. repeat rewrite G.(e_is_zero_r); auto. assert(H1: In (iter_pos p3 A (op a) (e G)) (s G)). refine (gpow_in _ _ _ _ _ (Zpos p3)); auto. assert(H2: In (iter_pos p3 A (op b) (e G)) (s G)). refine (gpow_in _ _ _ _ _ (Zpos p3)); auto. repeat rewrite <- G.(assoc); try eq_tac; auto. rewrite (fun x y => comm (iter_pos p3 A x y) b); auto. rewrite (G.(assoc) a); try apply comm; auto. Qed. Theorem Zdivide_mult_rel_prime: forall a b c : Z, (a | c) -> (b | c) -> rel_prime a b -> (a * b | c). intros a b c (q1, H1) (q2, H2) H3. assert (H4: (a | q2)). apply Gauss with (2 := H3). exists q1; rewrite <- H1; rewrite H2; auto with zarith. case H4; intros q3 H5; exists q3; rewrite H2; rewrite H5; auto with zarith. Qed. Theorem order_mult: forall (A : Set) (op : A -> A -> A) (A_dec: forall a b: A, {a = b} + {~ a = b}) (G : FGroup op) (comm: forall a b, In a (s G) -> In b (s G) -> op a b = op b a) (a b: A), In a (s G) -> In b (s G) -> rel_prime (e_order A_dec a G) (e_order A_dec b G) -> e_order A_dec (op a b) G = e_order A_dec a G * e_order A_dec b G. intros A op A_dec G comm a b Ha Hb Hab. assert (Hoat: 0 < e_order A_dec a G); try apply e_order_pos. assert (Hobt: 0 < e_order A_dec b G); try apply e_order_pos. assert (Hoabt: 0 < e_order A_dec (op a b) G); try apply e_order_pos. assert (Hoa: 0 <= e_order A_dec a G); auto with zarith. assert (Hob: 0 <= e_order A_dec b G); auto with zarith. apply Zle_antisym; apply Zdivide_le; auto with zarith. apply Zmult_lt_O_compat; auto. apply e_order_divide_gpow; sauto; auto with zarith. rewrite gpow_mult; auto with zarith. rewrite gpow_gpow; auto with zarith. rewrite gpow_e_order_is_e; auto with zarith. rewrite gpow_e; auto. rewrite Zmult_comm. rewrite gpow_gpow; auto with zarith. rewrite gpow_e_order_is_e; auto with zarith. rewrite gpow_e; auto. apply Zdivide_mult_rel_prime; auto. apply Gauss with (2 := Hab). apply e_order_divide_gpow; auto with zarith. rewrite <- (gpow_e _ _ G (e_order A_dec b G)); auto. rewrite <- (gpow_e_order_is_e _ A_dec _ (op a b) G); auto with zarith. rewrite <- gpow_gpow; auto with zarith. rewrite (Zmult_comm (e_order A_dec (op a b) G)). rewrite gpow_mult; auto with zarith. rewrite gpow_gpow with (a := b); auto with zarith. rewrite gpow_e_order_is_e; auto with zarith. rewrite gpow_e; auto with zarith. rewrite G.(e_is_zero_r); auto with zarith. apply gpow_in; auto. apply Gauss with (2 := rel_prime_sym _ _ Hab). apply e_order_divide_gpow; auto with zarith. rewrite <- (gpow_e _ _ G (e_order A_dec a G)); auto. rewrite <- (gpow_e_order_is_e _ A_dec _ (op a b) G); auto with zarith. rewrite <- gpow_gpow; auto with zarith. rewrite (Zmult_comm (e_order A_dec (op a b) G)). rewrite gpow_mult; auto with zarith. rewrite gpow_gpow with (a := a); auto with zarith. rewrite gpow_e_order_is_e; auto with zarith. rewrite gpow_e; auto with zarith. rewrite G.(e_is_zero_l); auto with zarith. apply gpow_in; auto. Qed. Theorem fermat_gen: forall (A : Set) (A_dec: forall (a b: A), {a = b} + {a <>b}) (op : A -> A -> A) (a: A) (G : FGroup op), In a G.(s) -> gpow a G (g_order G) = G.(e). intros A A_dec op a G H. assert (H1: (e_order A_dec a G | g_order G)). apply e_order_divide_g_order; auto. case H1; intros q; intros Hq; rewrite Hq. assert (Hq1: 0 <= q). apply Zmult_le_reg_r with (e_order A_dec a G); auto with zarith. apply Zlt_gt; apply e_order_pos. rewrite Zmult_0_l; rewrite <- Hq; apply Zlt_le_weak; apply g_order_pos. rewrite Zmult_comm; rewrite gpow_gpow; auto with zarith. rewrite gpow_e_order_is_e; auto with zarith. apply gpow_e; auto. apply Zlt_le_weak; apply e_order_pos. Qed. Theorem order_div: forall (A : Set) (A_dec: forall (a b: A), {a = b} + {a <>b}) (op : A -> A -> A) (a: A) (G : FGroup op) m, 0 < m -> (forall p, prime p -> (p | m) -> gpow a G (m / p) <> G.(e)) -> In a G.(s) -> gpow a G m = G.(e) -> e_order A_dec a G = m. intros A Adec op a G m Hm H H1 H2. assert (F1: 0 <= m); auto with zarith. case (e_order_divide_gpow A Adec op a G H1 m F1 H2); intros q Hq. assert (F2: 1 <= q). case (Zle_or_lt 0 q); intros HH. case (Zle_lt_or_eq _ _ HH); auto with zarith. intros HH1; generalize Hm; rewrite Hq; rewrite <- HH1; auto with zarith. assert (F2: 0 <= (- q) * e_order Adec a G); auto with zarith. apply Zmult_le_0_compat; auto with zarith. apply Zlt_le_weak; apply e_order_pos. generalize F2; rewrite Zopp_mult_distr_l_reverse; rewrite <- Hq; auto with zarith. case (Zle_lt_or_eq _ _ F2); intros H3; subst; auto with zarith. case (prime_dec q); intros Hq. case (H q); auto with zarith. rewrite Zmult_comm; rewrite Z_div_mult; auto with zarith. apply gpow_e_order_is_e; auto. case (Zdivide_div_prime_le_square _ H3 Hq); intros r (Hr1, (Hr2, Hr3)). case (H _ Hr1); auto. apply Zdivide_trans with (1 := Hr2). apply Zdivide_factor_r. case Hr2; intros q1 Hq1; subst. assert (F3: 0 < r). generalize (prime_ge_2 _ Hr1); auto with zarith. rewrite <- Zmult_assoc; rewrite Zmult_comm; rewrite <- Zmult_assoc; rewrite Zmult_comm; rewrite Z_div_mult; auto with zarith. rewrite gpow_gpow; auto with zarith. rewrite gpow_e_order_is_e; try rewrite gpow_e; auto. apply Zmult_le_reg_r with r; auto with zarith. apply Zlt_le_weak; apply e_order_pos. apply Zmult_le_reg_r with r; auto with zarith. Qed.