(* (c) Copyright 2006-2016 Microsoft Corporation and Inria.                  *)
(* Distributed under the terms of CeCILL-B.                                  *)
From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq choice.
From mathcomp Require Import fintype finset fingroup morphism perm action.
From mathcomp Require Import ssralg countalg.

(*****************************************************************************)
(* This file clones the entire ssralg hierachy for finite types; this allows *)
(* type inference to function properly on expressions that mix combinatorial *)
(* and algebraic operators (e.g., [set x + y | x in A, y in A]).             *)
(*   finZmodType, finRingType, finComRingType, finUnitRingType,              *)
(*   finComUnitRingType, finIdomType, finFieldType finLmodType,              *)
(*   finLalgType finAlgType finUnitAlgType                                   *)
(*      == the finite counterparts of zmodType, etc.                         *)
(* Note that a finFieldType is canonically decidable. All these structures   *)
(* can be derived using [xxxType of T] forms, e.g., if R has both canonical  *)
(* finType and ringType structures, then                                     *)
(*     Canonical R_finRingType := Eval hnf in [finRingType of R].            *)
(* declares the derived finRingType structure for R. As the implementation   *)
(* of the derivation is somewhat involved, the Eval hnf normalization is     *)
(* strongly recommended.                                                     *)
(*   This file also provides direct tie-ins with finite group theory:        *)
(*  [baseFinGroupType of R for +%R] == the (canonical) additive group        *)
(*      [finGroupType of R for +%R]    structures for R                      *)
(*                         {unit R} == the type of units of R, which has a   *)
(*                                     canonical group structure.            *)
(*                FinRing.unit R Ux == the element of {unit R} corresponding *)
(*                                     to x, where Ux : x \in GRing.unit.    *)
(*                           'U%act == the action by right multiplication of *)
(*                                     {unit R} on R, via FinRing.unit_act.  *)
(*                                     (This is also a group action.)        *)
(*****************************************************************************)

Local Open Scope ring_scope.

Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.

Module FinRing.

Local Notation mixin_of T b := (Finite.mixin_of (EqType T b)).

Section Generic.

(* Implicits *)
Variables (type base_type : Type) (class_of base_of : Type -> Type).
Variable to_choice : forall T, base_of T -> Choice.class_of T.
Variable base_sort : base_type -> Type.

(* Explicits *)
Variable Pack : forall T, class_of T -> type.
Variable Class : forall T b, mixin_of T (to_choice b) -> class_of T.
Variable base_class : forall bT, base_of (base_sort bT).

Definition gen_pack T :=
  fun bT b & phant_id (base_class bT) b =>
  fun fT m & phant_id (Finite.class fT) (Finite.Class m) =>
  Pack (@Class T b m).

End Generic.

Arguments gen_pack [type base_type class_of base_of to_choice base_sort].
Local Notation fin_ c := (@Finite.Class _ c c).
Local Notation do_pack pack T := (pack T _ _ id _ _ id).
Import GRing.Theory.

Definition groupMixin V := FinGroup.Mixin (@addrA V) (@add0r V) (@addNr V).
Local Notation base_group T vT fT :=
  (@FinGroup.PackBase T (groupMixin vT) (Finite.class fT)).
Local Notation fin_group B V := (@FinGroup.Pack B (@addNr V)).

Module Zmodule.

Section ClassDef.

Set Primitive Projections.
Record class_of M :=
  Class { base : GRing.Zmodule.class_of M; mixin : mixin_of M base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.Zmodule.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.Zmodule.class_of R :=
  CountRing.Zmodule.Class c (mixin c).
Local Coercion mixin : class_of >-> mixin_of.

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.Zmodule.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition zmod_finType := @Finite.Pack zmodType (fin_ class).
Definition zmod_baseFinGroupType := base_group zmodType zmodType finType.
Definition zmod_finGroupType := fin_group zmod_baseFinGroupType zmodType.
Definition countZmod_finType := @Finite.Pack countZmodType (fin_ class).
Definition countZmod_baseFinGroupType :=
  base_group countZmodType zmodType finType.
Definition countZmod_finGroupType :=
  fin_group countZmod_baseFinGroupType zmodType.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.Zmodule.class_of.
Coercion base2 : class_of >-> CountRing.Zmodule.class_of.
Coercion mixin : class_of >-> mixin_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Canonical zmod_finType.
Canonical zmod_baseFinGroupType.
Canonical zmod_finGroupType.
Canonical countZmod_finType.
Canonical countZmod_baseFinGroupType.
Canonical countZmod_finGroupType.
Notation finZmodType := type.
Notation "[ 'finZmodType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finZmodType'  'of'  T ]") : form_scope.
Notation "[ 'baseFinGroupType' 'of' R 'for' +%R ]" :=
    (BaseFinGroupType R (groupMixin _))
  (at level 0, format "[ 'baseFinGroupType'  'of'  R  'for'  +%R ]")
    : form_scope.
Notation "[ 'finGroupType' 'of' R 'for' +%R ]" :=
    (@FinGroup.clone R _ (finGroupType _) id _ id)
  (at level 0, format "[ 'finGroupType'  'of'  R  'for'  +%R ]") : form_scope.
End Exports.

End Zmodule.
Import Zmodule.Exports.

Section AdditiveGroup.

Variable U : finZmodType.
Implicit Types x y : U.

Lemma zmod1gE : 1%g = 0 :> U.            Proof. by []. Qed.
Lemma zmodVgE x : x^-1%g = - x.          Proof. by []. Qed.
Lemma zmodMgE x y : (x * y)%g = x + y.   Proof. by []. Qed.
Lemma zmodXgE n x : (x ^+ n)%g = x *+ n. Proof. by []. Qed.
Lemma zmod_mulgC x y : commute x y.      Proof. exact: addrC. Qed.
Lemma zmod_abelian (A : {set U}) : abelian A.
Proof. by apply/centsP=> x _ y _; apply: zmod_mulgC. Qed.

End AdditiveGroup.

Module Ring.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.Ring.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.Ring.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.Ring.class_of R :=
  CountRing.Ring.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : Zmodule.class_of R :=
  Zmodule.Class (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.Ring.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition ring_finType := @Finite.Pack ringType (fin_ class).
Definition ring_baseFinGroupType := base_group ringType zmodType finType.
Definition ring_finGroupType := fin_group ring_baseFinGroupType zmodType.
Definition ring_finZmodType := @Zmodule.Pack ringType class.
Definition countRing_finType := @Finite.Pack countRingType (fin_ class).
Definition countRing_baseFinGroupType :=
  base_group countRingType zmodType finType.
Definition countRing_finGroupType :=
  fin_group countRing_baseFinGroupType zmodType.
Definition countRing_finZmodType := @Zmodule.Pack countRingType class.

End ClassDef.

Module Import Exports.
Coercion base : class_of >-> GRing.Ring.class_of.
Coercion base2 : class_of >-> CountRing.Ring.class_of.
Coercion base3 : class_of >-> Zmodule.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Canonical ring_finType.
Canonical ring_baseFinGroupType.
Canonical ring_finGroupType.
Canonical ring_finZmodType.
Canonical countRing_finType.
Canonical countRing_baseFinGroupType.
Canonical countRing_finGroupType.
Canonical countRing_finZmodType.
Notation finRingType := type.
Notation "[ 'finRingType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finRingType'  'of'  T ]") : form_scope.
End Exports.

Section Unit.

Variable R : finRingType.

Definition is_inv (x y : R) := (x * y == 1) && (y * x == 1).
Definition unit := [qualify a x : R | [exists y, is_inv x y]].
Definition inv x := odflt x (pick (is_inv x)).

Lemma mulVr : {in unit, left_inverse 1 inv *%R}.
Proof.
rewrite /inv => x Ux; case: pickP => [y | no_y]; last by case/pred0P: Ux.
by case/andP=> _; move/eqP.
Qed.

Lemma mulrV : {in unit, right_inverse 1 inv *%R}.
Proof.
rewrite /inv => x Ux; case: pickP => [y | no_y]; last by case/pred0P: Ux.
by case/andP; move/eqP.
Qed.

Lemma intro_unit x y : y * x = 1 /\ x * y = 1 -> x \is a unit.
Proof.
by case=> yx1 xy1; apply/existsP; exists y; rewrite /is_inv xy1 yx1 !eqxx.
Qed.

Lemma invr_out : {in [predC unit], inv =1 id}.
Proof.
rewrite /inv => x nUx; case: pickP => // y invxy.
by case/existsP: nUx; exists y.
Qed.

Definition UnitMixin := GRing.UnitRing.Mixin mulVr mulrV intro_unit invr_out.

End Unit.

End Ring.
Import Ring.Exports.

Module ComRing.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.ComRing.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.ComRing.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.ComRing.class_of R :=
  CountRing.ComRing.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : Ring.class_of R :=
  Ring.Class (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.ComRing.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition comRingType := @GRing.ComRing.Pack cT class.
Definition countComRingType := @CountRing.ComRing.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition comRing_finType := @Finite.Pack comRingType (fin_ class).
Definition comRing_baseFinGroupType := base_group comRingType zmodType finType.
Definition comRing_finGroupType := fin_group comRing_baseFinGroupType zmodType.
Definition comRing_finZmodType := @Zmodule.Pack comRingType class.
Definition comRing_finRingType := @Ring.Pack comRingType class.
Definition countComRing_finType := @Finite.Pack countComRingType (fin_ class).
Definition countComRing_baseFinGroupType :=
  base_group countComRingType zmodType finType.
Definition countComRing_finGroupType :=
  fin_group countComRing_baseFinGroupType zmodType.
Definition countComRing_finZmodType := @Zmodule.Pack countComRingType class.
Definition countComRing_finRingType := @Ring.Pack countComRingType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.ComRing.class_of.
Coercion base2 : class_of >-> CountRing.ComRing.class_of.
Coercion base3 : class_of >-> Ring.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion comRingType : type >-> GRing.ComRing.type.
Canonical comRingType.
Coercion countComRingType : type >-> CountRing.ComRing.type.
Canonical countComRingType.
Canonical comRing_finType.
Canonical comRing_baseFinGroupType.
Canonical comRing_finGroupType.
Canonical comRing_finZmodType.
Canonical comRing_finRingType.
Canonical countComRing_finType.
Canonical countComRing_baseFinGroupType.
Canonical countComRing_finGroupType.
Canonical countComRing_finZmodType.
Canonical countComRing_finRingType.
Notation finComRingType := FinRing.ComRing.type.
Notation "[ 'finComRingType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finComRingType'  'of'  T ]") : form_scope.
End Exports.

End ComRing.
Import ComRing.Exports.

Module UnitRing.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.UnitRing.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.UnitRing.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.UnitRing.class_of R :=
  CountRing.UnitRing.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : Ring.class_of R :=
  Ring.Class (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.UnitRing.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition unitRingType := @GRing.UnitRing.Pack cT class.
Definition countUnitRingType := @CountRing.UnitRing.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition unitRing_finType := @Finite.Pack unitRingType (fin_ class).
Definition unitRing_baseFinGroupType :=
  base_group unitRingType zmodType finType.
Definition unitRing_finGroupType :=
  fin_group unitRing_baseFinGroupType zmodType.
Definition unitRing_finZmodType := @Zmodule.Pack unitRingType class.
Definition unitRing_finRingType := @Ring.Pack unitRingType class.
Definition countUnitRing_finType :=
  @Finite.Pack countUnitRingType (fin_ class).
Definition countUnitRing_baseFinGroupType :=
  base_group countUnitRingType zmodType finType.
Definition countUnitRing_finGroupType :=
  fin_group countUnitRing_baseFinGroupType zmodType.
Definition countUnitRing_finZmodType := @Zmodule.Pack countUnitRingType class.
Definition countUnitRing_finRingType := @Ring.Pack countUnitRingType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.UnitRing.class_of.
Coercion base2 : class_of >-> CountRing.UnitRing.class_of.
Coercion base3 : class_of >-> Ring.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion unitRingType : type >-> GRing.UnitRing.type.
Canonical unitRingType.
Coercion countUnitRingType : type >-> CountRing.UnitRing.type.
Canonical countUnitRingType.
Canonical unitRing_finType.
Canonical unitRing_baseFinGroupType.
Canonical unitRing_finGroupType.
Canonical unitRing_finZmodType.
Canonical unitRing_finRingType.
Canonical countUnitRing_finType.
Canonical countUnitRing_baseFinGroupType.
Canonical countUnitRing_finGroupType.
Canonical countUnitRing_finZmodType.
Canonical countUnitRing_finRingType.
Notation finUnitRingType := FinRing.UnitRing.type.
Notation "[ 'finUnitRingType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finUnitRingType'  'of'  T ]") : form_scope.
End Exports.

End UnitRing.
Import UnitRing.Exports.

Section UnitsGroup.

Variable R : finUnitRingType.

Inductive unit_of (phR : phant R) := Unit (x : R) of x \is a GRing.unit.
Bind Scope group_scope with unit_of.

Let phR := Phant R.
Local Notation uT := (unit_of phR).
Implicit Types u v : uT.
Definition uval u := let: Unit x _ := u in x.

Canonical unit_subType := [subType for uval].
Definition unit_eqMixin := Eval hnf in [eqMixin of uT by <:].
Canonical unit_eqType := Eval hnf in EqType uT unit_eqMixin.
Definition unit_choiceMixin := [choiceMixin of uT by <:].
Canonical unit_choiceType := Eval hnf in ChoiceType uT unit_choiceMixin.
Definition unit_countMixin := [countMixin of uT by <:].
Canonical unit_countType := Eval hnf in CountType uT unit_countMixin.
Canonical unit_subCountType := Eval hnf in [subCountType of uT].
Definition unit_finMixin := [finMixin of uT by <:].
Canonical unit_finType := Eval hnf in FinType uT unit_finMixin.
Canonical unit_subFinType := Eval hnf in [subFinType of uT].

Definition unit1 := Unit phR (@GRing.unitr1 _).
Lemma unit_inv_proof u : (val u)^-1 \is a GRing.unit.
Proof. by rewrite unitrV ?(valP u). Qed.
Definition unit_inv u := Unit phR (unit_inv_proof u).
Lemma unit_mul_proof u v : val u * val v \is a GRing.unit.
Proof. by rewrite (unitrMr _ (valP u)) ?(valP v). Qed.
Definition unit_mul u v := Unit phR (unit_mul_proof u v).
Lemma unit_muluA : associative unit_mul.
Proof. by move=> u v w; apply/val_inj/mulrA. Qed.
Lemma unit_mul1u : left_id unit1 unit_mul.
Proof. by move=> u; apply/val_inj/mul1r. Qed.
Lemma unit_mulVu : left_inverse unit1 unit_inv unit_mul.
Proof. by move=> u; apply/val_inj/(mulVr (valP u)). Qed.

Definition unit_GroupMixin := FinGroup.Mixin unit_muluA unit_mul1u unit_mulVu.
Canonical unit_baseFinGroupType :=
  Eval hnf in BaseFinGroupType uT unit_GroupMixin.
Canonical unit_finGroupType := Eval hnf in FinGroupType unit_mulVu.

Lemma val_unit1 : val (1%g : uT) = 1. Proof. by []. Qed.
Lemma val_unitM x y : val (x * y : uT)%g = val x * val y. Proof. by []. Qed.
Lemma val_unitV x : val (x^-1 : uT)%g = (val x)^-1. Proof. by []. Qed.
Lemma val_unitX n x : val (x ^+ n : uT)%g = val x ^+ n.
Proof. by case: n; last by elim=> //= n ->. Qed.

Definition unit_act x u := x * val u.
Lemma unit_actE x u : unit_act x u = x * val u. Proof. by []. Qed.

Canonical unit_action :=
  @TotalAction _ _ unit_act (@mulr1 _) (fun _ _ _ => mulrA _ _ _).
Lemma unit_is_groupAction : @is_groupAction _ R setT setT unit_action.
Proof.
move=> u _ /[1!inE]; apply/andP; split; first by apply/subsetP=> x /[1!inE].
by apply/morphicP=> x y _ _; rewrite !actpermE /= [_ u]mulrDl.
Qed.
Canonical unit_groupAction := GroupAction unit_is_groupAction.

End UnitsGroup.

Module Import UnitsGroupExports.
Bind Scope group_scope with unit_of.
Canonical unit_subType.
Canonical unit_eqType.
Canonical unit_choiceType.
Canonical unit_countType.
Canonical unit_subCountType.
Canonical unit_finType.
Canonical unit_subFinType.
Canonical unit_baseFinGroupType.
Canonical unit_finGroupType.
Canonical unit_action.
Canonical unit_groupAction.
End UnitsGroupExports.

Notation unit R Ux := (Unit (Phant R) Ux).

Module ComUnitRing.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.ComUnitRing.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.ComUnitRing.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.ComUnitRing.class_of R :=
  CountRing.ComUnitRing.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : ComRing.class_of R :=
  ComRing.Class (mixin c).
Local Coercion base4 R (c : class_of R) : UnitRing.class_of R :=
  @UnitRing.Class R (base c) (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.ComUnitRing.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition comRingType := @GRing.ComRing.Pack cT class.
Definition countComRingType := @CountRing.ComRing.Pack cT class.
Definition finComRingType := @ComRing.Pack cT class.
Definition unitRingType := @GRing.UnitRing.Pack cT class.
Definition countUnitRingType := @CountRing.UnitRing.Pack cT class.
Definition finUnitRingType := @UnitRing.Pack cT class.
Definition comUnitRingType := @GRing.ComUnitRing.Pack cT class.
Definition countComUnitRingType := @CountRing.ComUnitRing.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition comUnitRing_finType := @Finite.Pack comUnitRingType (fin_ class).
Definition comUnitRing_baseFinGroupType :=
  base_group comUnitRingType zmodType finType.
Definition comUnitRing_finGroupType :=
  fin_group comUnitRing_baseFinGroupType zmodType.
Definition comUnitRing_finZmodType := @Zmodule.Pack comUnitRingType class.
Definition comUnitRing_finRingType := @Ring.Pack comUnitRingType class.
Definition comUnitRing_finComRingType := @ComRing.Pack comUnitRingType class.
Definition comUnitRing_finUnitRingType := @UnitRing.Pack comUnitRingType class.
Definition countComUnitRing_finType :=
  @Finite.Pack countComUnitRingType (fin_ class).
Definition countComUnitRing_baseFinGroupType :=
  base_group countComUnitRingType zmodType finType.
Definition countComUnitRing_finGroupType :=
  fin_group countComUnitRing_baseFinGroupType zmodType.
Definition countComUnitRing_finZmodType :=
  @Zmodule.Pack countComUnitRingType class.
Definition countComUnitRing_finRingType :=
  @Ring.Pack countComUnitRingType class.
Definition countComUnitRing_finComRingType :=
  @ComRing.Pack countComUnitRingType class.
Definition countComUnitRing_finUnitRingType :=
  @UnitRing.Pack countComUnitRingType class.
Definition unitRing_finComRingType := @ComRing.Pack unitRingType class.
Definition countUnitRing_finComRingType :=
  @ComRing.Pack countUnitRingType class.
Definition comRing_finUnitRingType := @UnitRing.Pack comRingType class.
Definition countComRing_finUnitRingType :=
  @UnitRing.Pack countComRingType class.
Definition finComRing_finUnitRingType := @UnitRing.Pack finComRingType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.ComUnitRing.class_of.
Coercion base2 : class_of >-> CountRing.ComUnitRing.class_of.
Coercion base3 : class_of >-> ComRing.class_of.
Coercion base4 : class_of >-> UnitRing.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion comRingType : type >-> GRing.ComRing.type.
Canonical comRingType.
Coercion countComRingType : type >-> CountRing.ComRing.type.
Canonical countComRingType.
Coercion finComRingType : type >-> ComRing.type.
Canonical finComRingType.
Coercion unitRingType : type >-> GRing.UnitRing.type.
Canonical unitRingType.
Coercion countUnitRingType : type >-> CountRing.UnitRing.type.
Canonical countUnitRingType.
Coercion finUnitRingType : type >-> UnitRing.type.
Canonical finUnitRingType.
Coercion comUnitRingType : type >-> GRing.ComUnitRing.type.
Canonical comUnitRingType.
Coercion countComUnitRingType : type >-> CountRing.ComUnitRing.type.
Canonical countComUnitRingType.
Canonical comUnitRing_finType.
Canonical comUnitRing_baseFinGroupType.
Canonical comUnitRing_finGroupType.
Canonical comUnitRing_finZmodType.
Canonical comUnitRing_finRingType.
Canonical comUnitRing_finComRingType.
Canonical comUnitRing_finUnitRingType.
Canonical countComUnitRing_finType.
Canonical countComUnitRing_baseFinGroupType.
Canonical countComUnitRing_finGroupType.
Canonical countComUnitRing_finZmodType.
Canonical countComUnitRing_finRingType.
Canonical countComUnitRing_finComRingType.
Canonical countComUnitRing_finUnitRingType.
Canonical unitRing_finComRingType.
Canonical countUnitRing_finComRingType.
Canonical comRing_finUnitRingType.
Canonical countComRing_finUnitRingType.
Canonical finComRing_finUnitRingType.
Notation finComUnitRingType := FinRing.ComUnitRing.type.
Notation "[ 'finComUnitRingType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finComUnitRingType'  'of'  T ]") : form_scope.
End Exports.

End ComUnitRing.
Import ComUnitRing.Exports.

Module IntegralDomain.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.IntegralDomain.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.IntegralDomain.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.IntegralDomain.class_of R :=
  CountRing.IntegralDomain.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : ComUnitRing.class_of R :=
  ComUnitRing.Class (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.IntegralDomain.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition comRingType := @GRing.ComRing.Pack cT class.
Definition countComRingType := @CountRing.ComRing.Pack cT class.
Definition finComRingType := @ComRing.Pack cT class.
Definition unitRingType := @GRing.UnitRing.Pack cT class.
Definition countUnitRingType := @CountRing.UnitRing.Pack cT class.
Definition finUnitRingType := @UnitRing.Pack cT class.
Definition comUnitRingType := @GRing.ComUnitRing.Pack cT class.
Definition countComUnitRingType := @CountRing.ComUnitRing.Pack cT class.
Definition finComUnitRingType := @ComUnitRing.Pack cT class.
Definition idomainType := @GRing.IntegralDomain.Pack cT class.
Definition countIdomainType := @CountRing.IntegralDomain.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition idomain_finType := @Finite.Pack idomainType (fin_ class).
Definition idomain_baseFinGroupType := base_group idomainType zmodType finType.
Definition idomain_finGroupType := fin_group idomain_baseFinGroupType zmodType.
Definition idomain_finZmodType := @Zmodule.Pack idomainType class.
Definition idomain_finRingType := @Ring.Pack idomainType class.
Definition idomain_finUnitRingType := @UnitRing.Pack idomainType class.
Definition idomain_finComRingType := @ComRing.Pack idomainType class.
Definition idomain_finComUnitRingType := @ComUnitRing.Pack idomainType class.
Definition countIdomain_finType := @Finite.Pack countIdomainType (fin_ class).
Definition countIdomain_baseFinGroupType :=
  base_group countIdomainType zmodType finType.
Definition countIdomain_finGroupType :=
  fin_group countIdomain_baseFinGroupType zmodType.
Definition countIdomain_finZmodType := @Zmodule.Pack countIdomainType class.
Definition countIdomain_finRingType := @Ring.Pack countIdomainType class.
Definition countIdomain_finUnitRingType :=
  @UnitRing.Pack countIdomainType class.
Definition countIdomain_finComRingType := @ComRing.Pack countIdomainType class.
Definition countIdomain_finComUnitRingType :=
  @ComUnitRing.Pack countIdomainType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.IntegralDomain.class_of.
Coercion base2 : class_of >-> CountRing.IntegralDomain.class_of.
Coercion base3 : class_of >-> ComUnitRing.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion comRingType : type >-> GRing.ComRing.type.
Canonical comRingType.
Coercion countComRingType : type >-> CountRing.ComRing.type.
Canonical countComRingType.
Coercion finComRingType : type >-> ComRing.type.
Canonical finComRingType.
Coercion unitRingType : type >-> GRing.UnitRing.type.
Canonical unitRingType.
Coercion countUnitRingType : type >-> CountRing.UnitRing.type.
Canonical countUnitRingType.
Coercion finUnitRingType : type >-> UnitRing.type.
Canonical finUnitRingType.
Coercion comUnitRingType : type >-> GRing.ComUnitRing.type.
Canonical comUnitRingType.
Coercion countComUnitRingType : type >-> CountRing.ComUnitRing.type.
Canonical countComUnitRingType.
Coercion finComUnitRingType : type >-> ComUnitRing.type.
Canonical finComUnitRingType.
Coercion idomainType : type >-> GRing.IntegralDomain.type.
Canonical idomainType.
Coercion countIdomainType : type >-> CountRing.IntegralDomain.type.
Canonical countIdomainType.
Canonical idomain_finType.
Canonical idomain_baseFinGroupType.
Canonical idomain_finGroupType.
Canonical idomain_finZmodType.
Canonical idomain_finRingType.
Canonical idomain_finUnitRingType.
Canonical idomain_finComRingType.
Canonical idomain_finComUnitRingType.
Canonical countIdomain_finType.
Canonical countIdomain_baseFinGroupType.
Canonical countIdomain_finGroupType.
Canonical countIdomain_finZmodType.
Canonical countIdomain_finRingType.
Canonical countIdomain_finUnitRingType.
Canonical countIdomain_finComRingType.
Canonical countIdomain_finComUnitRingType.
Notation finIdomainType := FinRing.IntegralDomain.type.
Notation "[ 'finIdomainType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finIdomainType'  'of'  T ]") : form_scope.
End Exports.

End IntegralDomain.
Import IntegralDomain.Exports.

Module Field.

Section ClassDef.

Set Primitive Projections.
Record class_of R :=
  Class { base : GRing.Field.class_of R; mixin : mixin_of R base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.Field.class_of.
Local Coercion base2 R (c : class_of R) : CountRing.Field.class_of R :=
  CountRing.Field.Class c (mixin c).
Local Coercion base3 R (c : class_of R) : IntegralDomain.class_of R :=
  IntegralDomain.Class (mixin c).

Structure type := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Definition pack := gen_pack Pack Class GRing.Field.class.
Variable cT : type.
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition comRingType := @GRing.ComRing.Pack cT class.
Definition countComRingType := @CountRing.ComRing.Pack cT class.
Definition finComRingType := @ComRing.Pack cT class.
Definition unitRingType := @GRing.UnitRing.Pack cT class.
Definition countUnitRingType := @CountRing.UnitRing.Pack cT class.
Definition finUnitRingType := @UnitRing.Pack cT class.
Definition comUnitRingType := @GRing.ComUnitRing.Pack cT class.
Definition countComUnitRingType := @CountRing.ComUnitRing.Pack cT class.
Definition finComUnitRingType := @ComUnitRing.Pack cT class.
Definition idomainType := @GRing.IntegralDomain.Pack cT class.
Definition countIdomainType := @CountRing.IntegralDomain.Pack cT class.
Definition finIdomainType := @IntegralDomain.Pack cT class.
Definition fieldType := @GRing.Field.Pack cT class.
Definition countFieldType := @CountRing.Field.Pack cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition field_finType := @Finite.Pack fieldType (fin_ class).
Definition field_baseFinGroupType := base_group fieldType zmodType finType.
Definition field_finGroupType := fin_group field_baseFinGroupType zmodType.
Definition field_finZmodType := @Zmodule.Pack fieldType class.
Definition field_finRingType := @Ring.Pack fieldType class.
Definition field_finUnitRingType := @UnitRing.Pack fieldType class.
Definition field_finComRingType := @ComRing.Pack fieldType class.
Definition field_finComUnitRingType := @ComUnitRing.Pack fieldType class.
Definition field_finIdomainType := @IntegralDomain.Pack fieldType class.
Definition countField_finType := @Finite.Pack countFieldType (fin_ class).
Definition countField_baseFinGroupType :=
  base_group countFieldType zmodType finType.
Definition countField_finGroupType :=
  fin_group countField_baseFinGroupType zmodType.
Definition countField_finZmodType := @Zmodule.Pack countFieldType class.
Definition countField_finRingType := @Ring.Pack countFieldType class.
Definition countField_finUnitRingType := @UnitRing.Pack countFieldType class.
Definition countField_finComRingType := @ComRing.Pack countFieldType class.
Definition countField_finComUnitRingType :=
  @ComUnitRing.Pack countFieldType class.
Definition countField_finIdomainType :=
  @IntegralDomain.Pack countFieldType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.Field.class_of.
Coercion base2 : class_of >-> CountRing.Field.class_of.
Coercion base3 : class_of >-> IntegralDomain.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion comRingType : type >-> GRing.ComRing.type.
Canonical comRingType.
Coercion countComRingType : type >-> CountRing.ComRing.type.
Canonical countComRingType.
Coercion finComRingType : type >-> ComRing.type.
Canonical finComRingType.
Coercion unitRingType : type >-> GRing.UnitRing.type.
Canonical unitRingType.
Coercion countUnitRingType : type >-> CountRing.UnitRing.type.
Canonical countUnitRingType.
Coercion finUnitRingType : type >-> UnitRing.type.
Canonical finUnitRingType.
Coercion comUnitRingType : type >-> GRing.ComUnitRing.type.
Canonical comUnitRingType.
Coercion countComUnitRingType : type >-> CountRing.ComUnitRing.type.
Canonical countComUnitRingType.
Coercion finComUnitRingType : type >-> ComUnitRing.type.
Canonical finComUnitRingType.
Coercion idomainType : type >-> GRing.IntegralDomain.type.
Canonical idomainType.
Coercion countIdomainType : type >-> CountRing.IntegralDomain.type.
Canonical countIdomainType.
Coercion finIdomainType : type >-> IntegralDomain.type.
Canonical finIdomainType.
Coercion fieldType : type >-> GRing.Field.type.
Canonical fieldType.
Coercion countFieldType : type >-> CountRing.Field.type.
Canonical countFieldType.
Canonical field_finType.
Canonical field_baseFinGroupType.
Canonical field_finGroupType.
Canonical field_finZmodType.
Canonical field_finRingType.
Canonical field_finUnitRingType.
Canonical field_finComRingType.
Canonical field_finComUnitRingType.
Canonical field_finIdomainType.
Canonical countField_finType.
Canonical countField_baseFinGroupType.
Canonical countField_finGroupType.
Canonical countField_finZmodType.
Canonical countField_finRingType.
Canonical countField_finUnitRingType.
Canonical countField_finComRingType.
Canonical countField_finComUnitRingType.
Canonical countField_finIdomainType.
Notation finFieldType := FinRing.Field.type.
Notation "[ 'finFieldType' 'of' T ]" := (do_pack pack T)
  (at level 0, format "[ 'finFieldType'  'of'  T ]") : form_scope.
End Exports.

End Field.
Import Field.Exports.

Section DecideField.

Variable F : Field.type.

Fixpoint sat e f :=
  match f with
  | GRing.Bool b => b
  | t1 == t2 => (GRing.eval e t1 == GRing.eval e t2)%bool
  | GRing.Unit t => GRing.eval e t \is a GRing.unit
  | f1 /\ f2 => sat e f1 && sat e f2
  | f1 \/ f2 => sat e f1 || sat e f2
  | f1 ==> f2 => (sat e f1 ==> sat e f2)%bool
  | ~ f1 => ~~ sat e f1
  | ('exists 'X_k, f1) => [exists x : F, sat (set_nth 0%R e k x) f1]
  | ('forall 'X_k, f1) => [forall x : F, sat (set_nth 0%R e k x) f1]
  end%T.

Lemma decidable : GRing.DecidableField.axiom sat.
Proof.
move=> e f; elim: f e;
  try by move=> f1 IH1 f2 IH2 e /=; case IH1; case IH2; constructor; tauto.
- by move=> b e; apply: idP.
- by move=> t1 t2 e; apply: eqP.
- by move=> t e; apply: idP.
- by move=> f IH e /=; case: IH; constructor.
- by move=> i f IH e; apply: (iffP existsP) => [] [x fx]; exists x; apply/IH.
by move=> i f IH e; apply: (iffP forallP) => f_ x; apply/IH.
Qed.

Definition DecidableFieldMixin := DecFieldMixin decidable.

End DecideField.

Module DecField.

Section Joins.

Variable cT : Field.type.
Let class : Field.class_of cT := Field.class cT.

Canonical decFieldType := Eval hnf in DecFieldType cT (DecidableFieldMixin cT).
Definition countDecFieldType := [countDecFieldType of cT].
Definition decField_finType := @Finite.Pack decFieldType (fin_ class).
Definition decField_finZmodType := @Zmodule.Pack decFieldType class.
Definition decField_finRingType := @Ring.Pack decFieldType class.
Definition decField_finUnitRingType := @UnitRing.Pack decFieldType class.
Definition decField_finComRingType := @ComRing.Pack decFieldType class.
Definition decField_finComUnitRingType := @ComUnitRing.Pack decFieldType class.
Definition decField_finIdomainType := @IntegralDomain.Pack decFieldType class.
Definition decField_baseFinGroupType :=
  base_group decFieldType decField_finZmodType decField_finZmodType.
Definition decField_finGroupType := fin_group decField_baseFinGroupType cT.
Definition countDecField_finType := @Finite.Pack countDecFieldType (fin_ class).
Definition countDecField_finZmodType := @Zmodule.Pack countDecFieldType class.
Definition countDecField_finRingType := @Ring.Pack countDecFieldType class.
Definition countDecField_finUnitRingType :=
  @UnitRing.Pack countDecFieldType class.
Definition countDecField_finComRingType :=
  @ComRing.Pack countDecFieldType class.
Definition countDecField_finComUnitRingType :=
  @ComUnitRing.Pack countDecFieldType class.
Definition countDecField_finIdomainType :=
  @IntegralDomain.Pack countDecFieldType class.
Definition countDecField_baseFinGroupType :=
  base_group countDecFieldType countDecField_finZmodType
             countDecField_finZmodType.
Definition countDecField_finGroupType :=
  fin_group countDecField_baseFinGroupType cT.

End Joins.

Module Exports.
Coercion decFieldType : Field.type >-> GRing.DecidableField.type.
Coercion countDecFieldType : Field.type >-> CountRing.DecidableField.type.
Canonical decFieldType.
Canonical countDecFieldType.
Canonical decField_finType.
Canonical decField_finZmodType.
Canonical decField_finRingType.
Canonical decField_finUnitRingType.
Canonical decField_finComRingType.
Canonical decField_finComUnitRingType.
Canonical decField_finIdomainType.
Canonical decField_baseFinGroupType.
Canonical decField_finGroupType.
Canonical countDecField_finType.
Canonical countDecField_finZmodType.
Canonical countDecField_finRingType.
Canonical countDecField_finUnitRingType.
Canonical countDecField_finComRingType.
Canonical countDecField_finComUnitRingType.
Canonical countDecField_finIdomainType.
Canonical countDecField_baseFinGroupType.
Canonical countDecField_finGroupType.

End Exports.

End DecField.

Module Lmodule.

Section ClassDef.

Variable R : ringType.

Set Primitive Projections.
Record class_of M :=
  Class { base : GRing.Lmodule.class_of R M; mixin : mixin_of M base }.
Unset Primitive Projections.
Local Coercion base : class_of >-> GRing.Lmodule.class_of.
Local Coercion base2 R (c : class_of R) : Zmodule.class_of R :=
  Zmodule.Class (mixin c).

Structure type (phR : phant R) := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Variables (phR : phant R) (cT : type phR).
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.
Definition pack := gen_pack (Pack phR) Class (@GRing.Lmodule.class R phR).

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition lmodType := @GRing.Lmodule.Pack R phR cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition lmod_countType := @Countable.Pack lmodType (fin_ class).
Definition lmod_finType := @Finite.Pack lmodType (fin_ class).
Definition lmod_baseFinGroupType := base_group lmodType zmodType finType.
Definition lmod_finGroupType := fin_group lmod_baseFinGroupType zmodType.
Definition lmod_countZmodType := @CountRing.Zmodule.Pack lmodType class.
Definition lmod_finZmodType := @Zmodule.Pack lmodType class.

End ClassDef.

Module Import Exports.
Coercion base : class_of >-> GRing.Lmodule.class_of.
Coercion base2 : class_of >-> Zmodule.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion lmodType : type >-> GRing.Lmodule.type.
Canonical lmodType.
Canonical lmod_countType.
Canonical lmod_finType.
Canonical lmod_baseFinGroupType.
Canonical lmod_finGroupType.
Canonical lmod_countZmodType.
Canonical lmod_finZmodType.
Notation finLmodType R := (FinRing.Lmodule.type (Phant R)).
Notation "[ 'finLmodType' R 'of' T ]" := (do_pack (@pack _ (Phant R)) T)
  (at level 0, format "[ 'finLmodType'  R  'of'  T ]") : form_scope.
End Exports.

End Lmodule.
Import Lmodule.Exports.

Module Lalgebra.

Section ClassDef.

Variable R : ringType.

Set Primitive Projections.
Record class_of M :=
  Class { base : GRing.Lalgebra.class_of R M; mixin : mixin_of M base }.
Unset Primitive Projections.
Definition base2 M (c : class_of M) := Ring.Class (mixin c).
Definition base3 M (c : class_of M) := @Lmodule.Class _ _ (base c) (mixin c).
Local Coercion base : class_of >-> GRing.Lalgebra.class_of.
Local Coercion base2 : class_of >-> Ring.class_of.
Local Coercion base3 : class_of >-> Lmodule.class_of.

Structure type (phR : phant R) := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Variables (phR : phant R) (cT : type phR).
Definition pack := gen_pack (Pack phR) Class (@GRing.Lalgebra.class R phR).
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition lmodType := @GRing.Lmodule.Pack R phR cT class.
Definition finLmodType := @Lmodule.Pack R phR cT class.
Definition lalgType := @GRing.Lalgebra.Pack R phR cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition lalg_countType := @Countable.Pack lalgType (fin_ class).
Definition lalg_finType := @Finite.Pack lalgType (fin_ class).
Definition lalg_baseFinGroupType := base_group lalgType zmodType finType.
Definition lalg_finGroupType := fin_group lalg_baseFinGroupType zmodType.
Definition lalg_countZmodType := @CountRing.Zmodule.Pack lalgType class.
Definition lalg_finZmodType := @Zmodule.Pack lalgType class.
Definition lalg_finLmodType := @Lmodule.Pack R phR lalgType class.
Definition lalg_countRingType := @CountRing.Ring.Pack lalgType class.
Definition lalg_finRingType := @Ring.Pack lalgType class.
Definition lmod_countRingType := @CountRing.Ring.Pack lmodType class.
Definition lmod_finRingType := @Ring.Pack lmodType class.
Definition finLmod_ringType := @GRing.Ring.Pack finLmodType class.
Definition finLmod_countRingType := @CountRing.Ring.Pack finLmodType class.
Definition finLmod_finRingType := @Ring.Pack finLmodType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.Lalgebra.class_of.
Coercion base2 : class_of >-> Ring.class_of.
Coercion base3 : class_of >-> Lmodule.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion lmodType : type >-> GRing.Lmodule.type.
Canonical lmodType.
Coercion finLmodType : type >-> Lmodule.type.
Canonical finLmodType.
Coercion lalgType : type >-> GRing.Lalgebra.type.
Canonical lalgType.
Canonical lalg_countType.
Canonical lalg_finType.
Canonical lalg_baseFinGroupType.
Canonical lalg_finGroupType.
Canonical lalg_countZmodType.
Canonical lalg_finZmodType.
Canonical lalg_finLmodType.
Canonical lalg_countRingType.
Canonical lalg_finRingType.
Canonical lmod_countRingType.
Canonical lmod_finRingType.
Canonical finLmod_ringType.
Canonical finLmod_countRingType.
Canonical finLmod_finRingType.
Notation finLalgType R := (FinRing.Lalgebra.type (Phant R)).
Notation "[ 'finLalgType' R 'of' T ]" := (do_pack (@pack _ (Phant R)) T)
  (at level 0, format "[ 'finLalgType'  R  'of'  T ]") : form_scope.
End Exports.

End Lalgebra.
Import Lalgebra.Exports.

Module Algebra.

Section ClassDef.

Variable R : ringType.

Set Primitive Projections.
Record class_of M :=
  Class { base : GRing.Algebra.class_of R M; mixin : mixin_of M base }.
Unset Primitive Projections.
Definition base2 M (c : class_of M) := Lalgebra.Class (mixin c).
Local Coercion base : class_of >-> GRing.Algebra.class_of.
Local Coercion base2 : class_of >->Lalgebra.class_of.

Structure type (phR : phant R) := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Variables (phR : phant R) (cT : type phR).
Definition pack := gen_pack (Pack phR) Class (@GRing.Algebra.class R phR).
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition lmodType := @GRing.Lmodule.Pack R phR cT class.
Definition finLmodType := @Lmodule.Pack R phR cT class.
Definition lalgType := @GRing.Lalgebra.Pack R phR cT class.
Definition finLalgType := @Lalgebra.Pack R phR cT class.
Definition algType := @GRing.Algebra.Pack R phR cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition alg_countType := @Countable.Pack algType (fin_ class).
Definition alg_finType := @Finite.Pack algType (fin_ class).
Definition alg_baseFinGroupType := base_group algType zmodType finType.
Definition alg_finGroupType := fin_group alg_baseFinGroupType zmodType.
Definition alg_countZmodType := @CountRing.Zmodule.Pack algType class.
Definition alg_finZmodType := @Zmodule.Pack algType class.
Definition alg_countRingType := @CountRing.Ring.Pack algType class.
Definition alg_finRingType := @Ring.Pack algType class.
Definition alg_finLmodType := @Lmodule.Pack R phR algType class.
Definition alg_finLalgType := @Lalgebra.Pack R phR algType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.Algebra.class_of.
Coercion base2 : class_of >-> Lalgebra.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion lmodType : type >-> GRing.Lmodule.type.
Canonical lmodType.
Coercion finLmodType : type >-> Lmodule.type.
Canonical finLmodType.
Coercion lalgType : type >-> GRing.Lalgebra.type.
Canonical lalgType.
Coercion finLalgType : type >-> Lalgebra.type.
Canonical finLalgType.
Coercion algType : type >-> GRing.Algebra.type.
Canonical algType.
Canonical alg_countType.
Canonical alg_finType.
Canonical alg_baseFinGroupType.
Canonical alg_finGroupType.
Canonical alg_countZmodType.
Canonical alg_finZmodType.
Canonical alg_countRingType.
Canonical alg_finRingType.
Canonical alg_finLmodType.
Canonical alg_finLalgType.
Notation finAlgType R := (type (Phant R)).
Notation "[ 'finAlgType' R 'of' T ]" := (do_pack (@pack _ (Phant R)) T)
  (at level 0, format "[ 'finAlgType'  R  'of'  T ]") : form_scope.
End Exports.

End Algebra.
Import Algebra.Exports.

Module UnitAlgebra.

Section ClassDef.

Variable R : unitRingType.

Set Primitive Projections.
Record class_of M :=
  Class { base : GRing.UnitAlgebra.class_of R M; mixin : mixin_of M base }.
Unset Primitive Projections.
Definition base2 M (c : class_of M) := Algebra.Class (mixin c).
Definition base3 M (c : class_of M) := @UnitRing.Class _ (base c) (mixin c).

Local Coercion base : class_of >-> GRing.UnitAlgebra.class_of.
Local Coercion base2 : class_of >-> Algebra.class_of.
Local Coercion base3 : class_of >-> UnitRing.class_of.

Structure type (phR : phant R) := Pack {sort; _ : class_of sort}.
Local Coercion sort : type >-> Sortclass.
Variables (phR : phant R) (cT : type phR).
Definition pack := gen_pack (Pack phR) Class (@GRing.UnitAlgebra.class R phR).
Definition class := let: Pack _ c as cT' := cT return class_of cT' in c.

Definition eqType := @Equality.Pack cT class.
Definition choiceType := @Choice.Pack cT class.
Definition countType := @Countable.Pack cT (fin_ class).
Definition finType := @Finite.Pack cT (fin_ class).
Definition zmodType := @GRing.Zmodule.Pack cT class.
Definition countZmodType := @CountRing.Zmodule.Pack cT class.
Definition finZmodType := @Zmodule.Pack cT class.
Definition ringType := @GRing.Ring.Pack cT class.
Definition countRingType := @CountRing.Ring.Pack cT class.
Definition finRingType := @Ring.Pack cT class.
Definition unitRingType := @GRing.UnitRing.Pack cT class.
Definition countUnitRingType := @CountRing.UnitRing.Pack cT class.
Definition finUnitRingType := @UnitRing.Pack cT class.
Definition lmodType := @GRing.Lmodule.Pack R phR cT class.
Definition finLmodType := @Lmodule.Pack R phR cT class.
Definition lalgType := @GRing.Lalgebra.Pack R phR cT class.
Definition finLalgType := @Lalgebra.Pack R phR cT class.
Definition algType := @GRing.Algebra.Pack R phR cT class.
Definition finAlgType := @Algebra.Pack R phR cT class.
Definition unitAlgType := @GRing.UnitAlgebra.Pack R phR cT class.
Definition baseFinGroupType := base_group cT zmodType finType.
Definition finGroupType := fin_group baseFinGroupType zmodType.

Definition unitAlg_countType := @Countable.Pack unitAlgType (fin_ class).
Definition unitAlg_finType := @Finite.Pack unitAlgType (fin_ class).
Definition unitAlg_baseFinGroupType := base_group unitAlgType zmodType finType.
Definition unitAlg_finGroupType := fin_group unitAlg_baseFinGroupType zmodType.
Definition unitAlg_countZmodType := @CountRing.Zmodule.Pack unitAlgType class.
Definition unitAlg_finZmodType := @Zmodule.Pack unitAlgType class.
Definition unitAlg_countRingType := @CountRing.Ring.Pack unitAlgType class.
Definition unitAlg_finRingType := @Ring.Pack unitAlgType class.
Definition unitAlg_countUnitRingType :=
  @CountRing.UnitRing.Pack unitAlgType class.
Definition unitAlg_finUnitRingType := @UnitRing.Pack unitAlgType class.
Definition unitAlg_finLmodType := @Lmodule.Pack R phR unitAlgType class.
Definition unitAlg_finLalgType := @Lalgebra.Pack R phR unitAlgType class.
Definition unitAlg_finAlgType := @Algebra.Pack R phR unitAlgType class.
Definition unitRing_finLmodType := @Lmodule.Pack R phR unitRingType class.
Definition unitRing_finLalgType := @Lalgebra.Pack R phR unitRingType class.
Definition unitRing_finAlgType := @Algebra.Pack R phR unitRingType class.
Definition countUnitRing_lmodType :=
  @GRing.Lmodule.Pack R phR countUnitRingType class.
Definition countUnitRing_finLmodType :=
  @Lmodule.Pack R phR countUnitRingType class.
Definition countUnitRing_lalgType :=
  @GRing.Lalgebra.Pack R phR countUnitRingType class.
Definition countUnitRing_finLalgType :=
  @Lalgebra.Pack R phR countUnitRingType class.
Definition countUnitRing_algType :=
  @GRing.Algebra.Pack R phR countUnitRingType class.
Definition countUnitRing_finAlgType :=
  @Algebra.Pack R phR countUnitRingType class.
Definition finUnitRing_lmodType :=
  @GRing.Lmodule.Pack R phR finUnitRingType class.
Definition finUnitRing_finLmodType :=
  @Lmodule.Pack R phR finUnitRingType class.
Definition finUnitRing_lalgType :=
  @GRing.Lalgebra.Pack R phR finUnitRingType class.
Definition finUnitRing_finLalgType :=
  @Lalgebra.Pack R phR finUnitRingType class.
Definition finUnitRing_algType :=
  @GRing.Algebra.Pack R phR finUnitRingType class.
Definition finUnitRing_finAlgType :=
  @Algebra.Pack R phR finUnitRingType class.

End ClassDef.

Module Exports.
Coercion base : class_of >-> GRing.UnitAlgebra.class_of.
Coercion base2 : class_of >-> Algebra.class_of.
Coercion base3 : class_of >-> UnitRing.class_of.
Coercion sort : type >-> Sortclass.
Bind Scope ring_scope with sort.
Coercion eqType : type >-> Equality.type.
Canonical eqType.
Coercion choiceType : type >-> Choice.type.
Canonical choiceType.
Coercion countType : type >-> Countable.type.
Canonical countType.
Coercion finType : type >-> Finite.type.
Canonical finType.
Coercion baseFinGroupType : type >-> FinGroup.base_type.
Canonical baseFinGroupType.
Coercion finGroupType : type >-> FinGroup.type.
Canonical finGroupType.
Coercion zmodType : type >-> GRing.Zmodule.type.
Canonical zmodType.
Coercion countZmodType : type >-> CountRing.Zmodule.type.
Canonical countZmodType.
Coercion finZmodType : type >-> Zmodule.type.
Canonical finZmodType.
Coercion ringType : type >-> GRing.Ring.type.
Canonical ringType.
Coercion countRingType : type >-> CountRing.Ring.type.
Canonical countRingType.
Coercion finRingType : type >-> Ring.type.
Canonical finRingType.
Coercion unitRingType : type >-> GRing.UnitRing.type.
Canonical unitRingType.
Coercion countUnitRingType : type >-> CountRing.UnitRing.type.
Canonical countUnitRingType.
Coercion finUnitRingType : type >-> UnitRing.type.
Canonical finUnitRingType.
Coercion lmodType : type >-> GRing.Lmodule.type.
Canonical lmodType.
Coercion finLmodType : type >-> Lmodule.type.
Canonical finLmodType.
Coercion lalgType : type >-> GRing.Lalgebra.type.
Canonical lalgType.
Coercion finLalgType : type >-> Lalgebra.type.
Canonical finLalgType.
Coercion algType : type >-> GRing.Algebra.type.
Canonical algType.
Coercion finAlgType : type >-> Algebra.type.
Canonical finAlgType.
Coercion unitAlgType : type >-> GRing.UnitAlgebra.type.
Canonical unitAlgType.
Canonical unitAlg_countType.
Canonical unitAlg_finType.
Canonical unitAlg_baseFinGroupType.
Canonical unitAlg_finGroupType.
Canonical unitAlg_countZmodType.
Canonical unitAlg_finZmodType.
Canonical unitAlg_countRingType.
Canonical unitAlg_finRingType.
Canonical unitAlg_countUnitRingType.
Canonical unitAlg_finUnitRingType.
Canonical unitAlg_finLmodType.
Canonical unitAlg_finLalgType.
Canonical unitAlg_finAlgType.
Canonical unitRing_finLmodType.
Canonical unitRing_finLalgType.
Canonical unitRing_finAlgType.
Canonical countUnitRing_lmodType.
Canonical countUnitRing_finLmodType.
Canonical countUnitRing_lalgType.
Canonical countUnitRing_finLalgType.
Canonical countUnitRing_algType.
Canonical countUnitRing_finAlgType.
Canonical finUnitRing_lmodType.
Canonical finUnitRing_finLmodType.
Canonical finUnitRing_lalgType.
Canonical finUnitRing_finLalgType.
Canonical finUnitRing_algType.
Canonical finUnitRing_finAlgType.
Notation finUnitAlgType R := (type (Phant R)).
Notation "[ 'finUnitAlgType' R 'of' T ]" := (do_pack (@pack _ (Phant R)) T)
  (at level 0, format "[ 'finUnitAlgType'  R  'of'  T ]") : form_scope.
End Exports.

End UnitAlgebra.
Import UnitAlgebra.Exports.

Module Theory.

Definition zmod1gE := zmod1gE.
Definition zmodVgE := zmodVgE.
Definition zmodMgE := zmodMgE.
Definition zmodXgE := zmodXgE.
Definition zmod_mulgC := zmod_mulgC.
Definition zmod_abelian := zmod_abelian.
Definition val_unit1 := val_unit1.
Definition val_unitM := val_unitM.
Definition val_unitX := val_unitX.
Definition val_unitV := val_unitV.
Definition unit_actE := unit_actE.

End Theory.

End FinRing.

Import FinRing.
Export Zmodule.Exports Ring.Exports ComRing.Exports.
Export UnitRing.Exports UnitsGroupExports ComUnitRing.Exports.
Export IntegralDomain.Exports Field.Exports DecField.Exports.
Export Lmodule.Exports Lalgebra.Exports Algebra.Exports UnitAlgebra.Exports.

Notation "{ 'unit' R }" := (unit_of (Phant R))
  (at level 0, format "{ 'unit'  R }") : type_scope.
Prenex Implicits FinRing.uval.
Notation "''U'" := (unit_action _) (at level 8) : action_scope.
Notation "''U'" := (unit_groupAction _) (at level 8) : groupAction_scope.


Require Import Nijn.Prelude.
Require Import Nijn.Syntax.Signature.Types.
Require Import Nijn.Syntax.Signature.Contexts.
Require Import Nijn.Syntax.Signature.Terms.
Require Import Nijn.Syntax.Signature.TermWeakenings.
Require Import Nijn.Syntax.Signature.TermSubstitutions.

(** * Rewriting systems *)

(** First, we look at certain closure properties and how to create relations closed under these. The first one, is compatibility closure, which says that you can rewrite under all type formers. *)
Inductive compatibilityClosure
          {B : Type}
          {F : Type}
          {ar : F -> ty B}
          (R : forall (C : con B) (A : ty B), tm ar C A -> tm ar C A -> Type)
          (C : con B)
  : forall (A : ty B), tm ar C A -> tm ar C A -> Type
  :=
| App_l : forall {A1 A2 : ty B}
                 {f1 f2 : tm ar C (A1 ⟶ A2)}
                 (x : tm ar C A1),
    compatibilityClosure R _ _ f1 f2
    -> compatibilityClosure R _ _ (f1 · x) (f2 · x)
| App_r : forall {A1 A2 : ty B}
                 (f : tm ar C (A1 ⟶ A2))
                 {x1 x2 : tm ar C A1},
    compatibilityClosure R _ _ x1 x2
    -> compatibilityClosure R _ _ (f · x1) (f · x2)
| CLam : forall {A1 A2 : ty B}
               {f1 f2 : tm ar (A1 ,, C) A2},
    compatibilityClosure R _ _ f1 f2
    -> compatibilityClosure R _ _ (λ f1) (λ f2)
| CStep : forall {A : ty B}
                 {t1 t2 : tm ar C A},
    R _ _ t1 t2 -> compatibilityClosure R _ _ t1 t2.

Arguments App_l {_ _ _ _ _ _ _ _ _} _ _.
Arguments App_r {_ _ _ _ _ _ _} _ {_ _} _.
Arguments CLam {_ _ _ _ _ _ _ _ _} _.
Arguments CStep {_ _ _ _ _ _ _ _} _.

(** To formulate the beta rule, we need a particular substitution *)
Definition beta_sub
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           (t : tm ar C A)
  : sub ar C (A ,, C)
  := ExtendSub (idSub C ar) t.

(** Reducing a beta redex *)
Inductive baseBetaStep
          {B : Type}
          {F : Type}
          {ar : F -> ty B}
          (C : con B)
  : forall (A : ty B), tm ar C A -> tm ar C A -> Type
  :=
| Beta : forall {A1 A2 : ty B}
                (f : tm ar (A1 ,, C) A2)
                (x : tm ar C A1),
    baseBetaStep _ _ ((λ f) · x) (subTm f (beta_sub x)).

Arguments Beta {_ _ _ _ _ _} _ _.

(** A single beta step *)
Definition betaStep
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
  : tm ar C A -> tm ar C A -> Type
  := compatibilityClosure baseBetaStep C A.

Notation "t1 '∼>β' t2" := (betaStep t1 t2) (at level 70). (* \sim is used *)

Definition betaRed
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
  : tm ar C A -> tm ar C A -> Type
  := transitiveClosure (fun t1 t2 => t1 ∼>β t2).

Notation "t1 '∼>β+' t2" := (betaRed t1 t2) (at level 70). (* \sim is used *)

(** Formers for beta reduction *)
Definition betaRed_step
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           {t1 t2 : tm ar C A}
           (p : t1 ∼>β t2)
  : t1 ∼>β+ t2
  := TStep p.

Definition beta_Trans
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           {t1 t2 t3 : tm ar C A}
           (p : t1 ∼>β+ t2)
           (q : t2 ∼>β+ t3)
  : t1 ∼>β+ t3
  := Trans p q.

Definition beta_rewrite_transport
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           (p : A1 = A2)
           {x1 x2 : tm ar C A1}
           (r : x1 ∼>β x2)
  : transport (tm ar C) p x1 ∼>β transport (tm ar C) p x2
  := match p with
     | eq_refl => r
     end.

Definition beta_App_l_help
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 A3 : ty B}
           {f1 f2 : tm ar C A3}
           (x : tm ar C A1)
           (p : f1 ∼>β+ f2)
           (q : A3 = (A1 ⟶ A2))
  : ((transport (tm ar C) q f1) · x) ∼>β+ ((transport (tm ar C) q f2) · x).
Proof.
  induction p.
  - apply TStep.
    apply App_l.
    apply beta_rewrite_transport.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition beta_App_l
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar C (A1 ⟶ A2)}
           (x : tm ar C A1)
           (p : f1 ∼>β+ f2)
  : (f1 · x) ∼>β+ (f2 · x)
  := beta_App_l_help x p eq_refl.

Definition beta_App_r
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           (f : tm ar C (A1 ⟶ A2))
           {x1 x2 : tm ar C A1}
           (p : x1 ∼>β+ x2)
  : (f · x1) ∼>β+ (f · x2).
Proof.
  induction p.
  - apply TStep.
    apply App_r.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition beta_Lam
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar (A1 ,, C) A2}
           (p : f1 ∼>β+ f2)
  : (λ f1) ∼>β+ (λ f2).
Proof.
  induction p.
  - apply TStep.
    apply CLam.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition beta_betaRed
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           (f : tm ar (A1 ,, C) A2)
           (x : tm ar C A1)
  : ((λ f) · x) ∼>β+ (subTm f (beta_sub x)).
Proof.
  apply TStep.
  apply CStep.
  apply Beta.
Defined.

(** ** Any number of beta rewriting steps *)
Definition betaRed_nonneg
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
  : tm ar C A -> tm ar C A -> Type
  := reflexiveClosure (fun t1 t2 => t1 ∼>β+ t2).

Notation "t1 '∼>β*' t2" := (betaRed_nonneg t1 t2) (at level 70). (* \sim is used *)

Definition betaRed_nonneg_refl
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           (t : tm ar C A)
  : t ∼>β* t.
Proof.
  right.
  reflexivity.
Defined.

Definition betaRed_nonneg_step
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           {t1 t2 : tm ar C A}
           (p : t1 ∼>β t2)
  : t1 ∼>β* t2.
Proof.
  left.
  apply betaRed_step.
  exact p.
Qed.

Definition betaRed_nonneg_steps
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           {t1 t2 : tm ar C A}
           (p : t1 ∼>β+ t2)
  : t1 ∼>β* t2.
Proof.
  left.
  exact p.
Qed.

Definition beta_nonneg_Trans
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A : ty B}
           {t1 t2 t3 : tm ar C A}
           (p : t1 ∼>β* t2)
           (q : t2 ∼>β* t3)
  : t1 ∼>β* t3.
Proof.
  destruct p as [r1 | p], q as [r2 | q].
  - left.
    exact (beta_Trans r1 r2).
  - subst.
    left.
    exact r1.
  - subst.
    left.
    exact r2.
  - subst.
    right.
    reflexivity.
Defined.

Definition beta_nonneg_App_l
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar C (A1 ⟶ A2)}
           (x : tm ar C A1)
           (p : f1 ∼>β* f2)
  : (f1 · x) ∼>β* (f2 · x).
Proof.
  destruct p as [r | p].
  - left.
    apply beta_App_l.
    exact r.
  - subst.
    right.
    reflexivity.
Defined.

Definition beta_nonneg_App_r
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           (f : tm ar C (A1 ⟶ A2))
           {x1 x2 : tm ar C A1}
           (p : x1 ∼>β* x2)
  : (f · x1) ∼>β* (f · x2).
Proof.
  destruct p as [r | p].
  - left.
    apply beta_App_r.
    exact r.
  - subst.
    right.
    reflexivity.
Defined.

Definition beta_nonneg_Lam
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar (A1 ,, C) A2}
           (p : f1 ∼>β* f2)
  : (λ f1) ∼>β* (λ f2).
Proof.
  destruct p as [r | p].
  - left.
    apply beta_Lam.
    exact r.
  - subst.
    right.
    reflexivity.
Defined.

Definition beta_nonneg_betaRed
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {C : con B}
           {A1 A2 : ty B}
           (f : tm ar (A1 ,, C) A2)
           (x : tm ar C A1)
  : ((λ f) · x) ∼>β* (subTm f (beta_sub x)).
Proof.
  left.
  apply beta_betaRed.
Defined.

(** * Rewriting 43lq5ion ro4 an AFS *)

(** An algebraic functional system is given by a type of base types, a type of function symbols, an arity for every function symbols, and a collection of rewrite rules. Given such data, we can define the rewrite relation. *)

(** First we look at the base steps in an AFS *)
Inductive baseRewStep
          {B : Type}
          {F : Type}
          {ar : F -> ty B}
          {R : Type}
          {Rcon : R -> con B}
          {Rtar : R -> ty B}
          (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
          (C : con B)
  : forall (A : ty B), tm ar C A -> tm ar C A -> Type
  :=
| AFSBeta : forall {A : ty B}
                   (t1 t2 : tm ar C A),
    baseBetaStep _ _ t1 t2 -> baseRewStep lhs rhs _ _ t1 t2
| AFSRew : forall (r : R)
                  (s : sub ar C (Rcon r)),
    baseRewStep lhs rhs _ _ (lhs r [ s ]) (rhs r [ s ]).

Arguments AFSBeta {_ _ _ _ _ _} _ _ {_ _ _ _} _.
Arguments AFSRew {_ _ _ _ _ _} _ _ {_} _ _.

(** Now we introduce the single step relation *)
Definition rewStep
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           {A : ty B}
  : tm ar C A -> tm ar C A -> Type
  := compatibilityClosure (baseRewStep lhs rhs) C A.

(** The rewriting relation in an AFS *)
Definition rew
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           {A : ty B}
  : tm ar C A -> tm ar C A -> Type
  := transitiveClosure (fun t1 t2 => rewStep lhs rhs t1 t2).

(** Formers for reduction in an AFS *)
Definition rew_step
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           {A : ty B}
           {t1 t2 : tm ar C A}
           (p : rewStep lhs rhs t1 t2)
  : rew lhs rhs t1 t2
  := TStep p.

Definition rew_Trans
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           {lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r)}
           {C : con B}
           {A : ty B}
           {t1 t2 t3 : tm ar C A}
           (p : rew lhs rhs t1 t2)
           (q : rew lhs rhs t2 t3)
  : rew lhs rhs t1 t3
  := Trans p q.

Definition rewrite_transport
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           {lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r)}
           {C : con B}
           {A1 A2 : ty B}
           (p : A1 = A2)
           {x1 x2 : tm ar C A1}
           (r : rewStep lhs rhs x1 x2)
  : rewStep lhs rhs (transport (tm ar C) p x1) (transport (tm ar C) p x2)
  := match p with
     | eq_refl => r
     end.

Definition rew_App_l_help
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           {lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r)}
           {C : con B}
           {A1 A2 A3 : ty B}
           {f1 f2 : tm ar C A3}
           (x : tm ar C A1)
           (p : rew lhs rhs f1 f2)
           (q : A3 = (A1 ⟶ A2))
  : rew lhs rhs ((transport (tm ar C) q f1) · x) ((transport (tm ar C) q f2) · x).
Proof.
  induction p.
  - apply TStep.
    apply App_l.
    apply rewrite_transport.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition rew_App_l
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           {lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r)}
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar C (A1 ⟶ A2)}
           (x : tm ar C A1)
           (p : rew lhs rhs f1 f2)
  : rew lhs rhs (f1 · x) (f2 · x)
  := rew_App_l_help x p eq_refl.

Definition rew_App_r
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           {lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r)}
           {C : con B}
           {A1 A2 : ty B}
           {f : tm ar C (A1 ⟶ A2)}
           (x1 x2 : tm ar C A1)
           (p : rew lhs rhs x1 x2)
  : rew lhs rhs (f · x1) (f · x2).
Proof.
  induction p.
  - apply TStep.
    apply App_r.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition rew_Lam
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           {A1 A2 : ty B}
           {f1 f2 : tm ar (A1 ,, C) A2}
           (p : rew lhs rhs f1 f2)
  : rew lhs rhs (λ f1) (λ f2).
Proof.
  induction p.
  - apply TStep.
    apply CLam.
    exact r.
  - exact (Trans IHp1 IHp2).
Defined.

Definition rew_betaRed
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           {A1 A2 : ty B}
           (f : tm ar (A1 ,, C) A2)
           (x : tm ar C A1)
  : rew lhs rhs ((λ f) · x) (subTm f (beta_sub x)).
Proof.
  apply TStep.
  apply CStep.
  apply AFSBeta.
  apply Beta.
Defined.

Definition rew_baseStep
           {B : Type}
           {F : Type}
           {ar : F -> ty B}
           {R : Type}
           {Rcon : R -> con B}
           {Rtar : R -> ty B}
           (lhs rhs : forall (r : R), tm ar (Rcon r) (Rtar r))
           {C : con B}
           (r : R)
           (s : sub ar C (Rcon r))
  : rew lhs rhs (subTm (lhs r) s) (subTm (rhs r) s).
Proof.
  apply TStep.
  apply CStep.
  apply AFSRew.
Defined.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e243m9_6limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e468m17_9limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e480m2e240m1_8limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition mul :
  { mul : feBW_loose -> feBW_loose -> feBW_tight
  | forall a b, phiBW_tight (mul a b) = F.mul (phiBW_loose a) (phiBW_loose b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_mul ().
  Show Ltac Profile.
Time Defined.

Print Assumptions mul.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e226m5_5limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e322m2e161m1_6limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition mul :
  { mul : feBW_loose -> feBW_loose -> feBW_tight
  | forall a b, phiBW_tight (mul a b) = F.mul (phiBW_loose a) (phiBW_loose b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_mul ().
  Show Ltac Profile.
Time Defined.

Print Assumptions mul.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e369m25_8limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e251m9_11limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e321m9_13limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e235m15_4limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e198m17_8limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e190m11_8limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e129m25_6limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e230m27_9limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e216m2e108m1_5limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition mul :
  { mul : feBW_loose -> feBW_loose -> feBW_tight
  | forall a b, phiBW_tight (mul a b) = F.mul (phiBW_loose a) (phiBW_loose b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_mul ().
  Show Ltac Profile.
Time Defined.

Print Assumptions mul.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e165m25_7limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas64_2e140m27_4limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e166m5_6limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition sub :
  { sub : feBW_tight -> feBW_tight -> feBW_loose
  | forall a b, phiBW_loose (sub a b) = F.sub (phiBW_tight a) (phiBW_tight b) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_sub ().
  Show Ltac Profile.
Time Defined.

Print Assumptions sub.


Require Export List.
Require Export Bool.
Require Export Arith.
Require Export Peano_dec.
Require Export Coq.Arith.PeanoNat.
Require Export Coq.Program.Wf.
Require Export Coq.Program.Tactics.
Require Export Coq.Logic.FunctionalExtensionality.
Require Export Recdef.

Require Import common.CpdtTactics.
Require Import common.wyv_common.
Require Import common.rhs_mat_tree.
Set Implicit Arguments.

Import WfExtensionality.

Lemma subtype_sha_sel_nom_reduce :
  forall T1 x1 L1 ss1 T1', T1 = (t_sha_sel x1 L1 ss1 T1') ->
                      forall T2 L2 T2', T2 = t_nom L2 T2' ->
                                   subtype T1 T2 = false.
Proof.
  intros.

  remember (subtype T1 T2) as sub_fn; subst T1 T2.

  unfold subtype, subtype_func in Heqsub_fn;
    simpl in Heqsub_fn;
    rewrite fix_sub_eq_ext in Heqsub_fn;
    simpl in Heqsub_fn;
    fold subtype_func in Heqsub_fn;
    auto.
Qed.


Require Import HoTT.
From GR.groupoid.grpd_bicategory Require Import
     grpd_bicategory.

Definition path_space (X : Type) : groupoid.
Proof.
  simple refine (Build_grpd _ _ _ _ _ _ _ _ _ _) ; simpl.
  - exact X.
  - exact (fun (x y : X) => BuildhSet (Trunc 0 (x = y))).
  - exact (fun _ => tr idpath).
  - exact (fun _ _ => Trunc_rec (fun p => tr p^)).
  - exact (fun _ _ _ p' q' => Trunc_rec (fun p => Trunc_rec (fun q => tr (p @ q)) q') p').
  - intros ? ? ? ? p q r ; simpl.
    strip_truncations ; simpl.
    exact (ap tr (concat_p_pp p q r)).
  - intros ? ? p.
    strip_truncations ; simpl.
    exact (ap tr (concat_1p p)).
  - intros ? ? p.
    strip_truncations ; simpl.
    exact (ap tr (concat_p1 p)).
  - intros ? ? p.
    strip_truncations ; simpl.
    exact (ap tr (concat_Vp p)).
  - intros ? ? p.
    strip_truncations ; simpl.
    exact (ap tr (concat_pV p)).
Defined.


Require Import Crypto.Arithmetic.PrimeFieldTheorems.
Require Import Crypto.Specific.solinas32_2e450m2e225m1_18limbs.Synthesis.

(* TODO : change this to field once field isomorphism happens *)
Definition square :
  { square : feBW_loose -> feBW_tight
  | forall a, phiBW_tight (square a) = F.mul (phiBW_loose a) (phiBW_loose a) }.
Proof.
  Set Ltac Profiling.
  Time synthesize_square ().
  Show Ltac Profile.
Time Defined.

Print Assumptions square.


Require Import VUtils.
Require Import VHeap.
Require Import RM.
Require Import List.
Require Import ZArith.

Module Export RMOuti.

Import ListNotations.

Open Scope Z_scope.
Open Local Scope nat_scope.
Open Local Scope string_scope.

Definition symTable :=
    [
        (pair "fact" 10);
        (pair "factret" 12);
        (pair "factrec" 13);
        (pair "end" 19)
    ].
Definition prog (num : tRegVal) : tRMCode :=
    [
(* 00 *)    (Const (V, 2) num);
(*  1 *)    (Invoke_static [(V, 2)] "fact");
(*  2 *)    (Move_result (V, 0));
(*  3 *)    (Goto "end");
(*  4 *)    (Noop);
(*  5 *)    (Noop);
(*  6 *)    (Noop);
(*  7 *)    (Noop);
(*  8 *)    (Noop);
(*  9 *)    (Noop);
(* 10 *)    (If_gtz (P, 0) "factrec");
(*  1 *)    (Const (V, 0) 1);
(*  2 *)    (Ret_val (V, 0));
(*  3 *)    (Const (V, 1) (-1));
(*  4 *)    (Add (V, 0) (P, 0) (V, 1));
(*  5 *)    (Invoke_static [(V, 0)] "fact");
(*  6 *)    (Move_result (V, 0));
(*  7 *)    (Mul (V, 0) (V, 0) (P, 0));
(*  8 *)    (Goto "factret");
(*  9 *)    (Halt)
    ].
Close Local Scope nat_scope.

End RMOuti.


Set Implicit Arguments.

Require Import Coq.Program.Equality.
Require Import Definitions Lookup Sequences.

(** * Operational Semantics *)

(** Term-reduction relation *)
Reserved Notation "t1 '⟼' t2" (at level 40, t2 at level 39).

Inductive red : sta * trm -> sta * trm -> Prop :=

(** [γ ⊢ q ⤳* λ(T)t ]      #
#
    [―――――――――――――――――――――]      #
#
    [γ | q p ⟼ γ | tᵖ]      *)
| red_app: forall γ p q T t,
    γ ⟦ defp q ⤳* defv (λ(T) t) ⟧ ->
    (γ, trm_app q p) ⟼ (γ, open_trm_p p t)

(** [γ | let x = v in t ⟼ γ, x = v | tˣ] *)
| red_let_val : forall v t γ x,
    x # γ ->
    (γ, trm_let (trm_val v) t) ⟼ (γ & x ~ v, open_trm x t)

(** [γ | let y = p in t ⟼ γ | tᵖ] *)
| red_let_path : forall t γ p,
    (γ, trm_let (trm_path p) t) ⟼ (γ, open_trm_p p t)

(** [γ | t0 ⟼ γ' | t0']                            #
#
    [―――――――――――――――――――――――――――――――――――――――――――――――]  #
#
    [γ | let x = t0 in t ⟼ γ' | let x = t0' in t]  *)
| red_let_tgt : forall t0 t γ t0' γ',
    (γ, t0) ⟼ (γ', t0') ->
    (γ, trm_let t0 t) ⟼ (γ', trm_let t0' t)

where "t1 '⟼' t2" := (red t1 t2).

(** Reflexive, transitive closure of reduction relation *)
Notation "t1 '⟼*' t2" := (star red t1 t2) (at level 40).

(** ** Normal forms *)

(** Paths and values are considered to be in normal form. *)
Inductive norm_form: trm -> Prop :=
| nf_path: forall p, norm_form (trm_path p)
| nf_val: forall v, norm_form (trm_val v).

Hint Constructors red norm_form.


Require Import List.

(* tag definition *)
Inductive tag_normal :=
| ADD : tag_normal
| AND : tag_normal.

Inductive tag_immediate :=
| ADD_I : tag_immediate
| AND_I : tag_immediate.

Inductive tag :=
| tag_n : tag_normal -> tag
| tag_i : tag_immediate -> tag.

Scheme Equality for tag_normal.
Scheme Equality for tag_immediate.

Lemma tag_beq_reflexivity : forall (t :tag), tag_beq t t = true.
Proof.
  destruct t.
  -destruct t.
   +reflexivity.
   +reflexivity.
  -destruct t.
   +reflexivity.
   +reflexivity.
Qed.

Lemma tag_beq_different : forall (t1 t2 : tag), tag_beq t1 t2 = true -> t1 = t2.
Proof.
  destruct t1.
  -{
      destruct t2.
      -{
          destruct t.
          -destruct t0.
           +reflexivity.
           +discriminate.
          -destruct t0.
           +discriminate.
           +reflexivity.
        }
      -{
          destruct t.
          -destruct t0.
           +discriminate.
           +discriminate.
          -destruct t0.
           +discriminate.
           +discriminate.
        }
    }
  -{
      destruct t2.
      -{
          destruct t.
          -destruct t0.
           +discriminate.
           +discriminate.
          -destruct t0.
           +discriminate.
           +discriminate.
        }
      -{
          destruct t.
          -destruct t0.
           +reflexivity.
           +discriminate.
          -destruct t0.
           +discriminate.
           +reflexivity.
        }
    }
Qed.

(* maybe it's not usefull to distinguish the special register than the
other because the specification says that you have different numbers for them *)

(* operand definition *)
Inductive operand :=
| immediate : nat -> operand
| reg : nat -> operand.

(* instruction definition *)
Record instruction :=
  mk_instr { instr_opcode : tag;
             instr_operande1 : operand ;
             instr_operande2 : operand ;
             instr_operande3 : operand }.

(* binary instruction definition *)
Definition binary_instruction := list bool.

(* some example to test the record structure *)
Example my_instr := mk_instr (tag_i ADD_I) (reg 10) (reg 11) (immediate 12).

Example first_field_instr := my_instr.(instr_opcode).