categories.varieties categories.product forget_algebra forget_variety theory.groups.
Require Import
  abstract_algebra universal_algebra ua_homomorphisms workaround_tactics.

Inductive op := mult | inv | one.

Definition sig: Signature := single_sorted_signature
  (λ o, match o with one => O | inv => 1%nat | mult => 2%nat end).

Section laws.
  Global Instance: SemiGroupOp (Term0 sig nat tt) :=
    λ x, App sig _ _ _ (App sig _ _ _ (Op sig nat mult) x).
  Global Instance: MonoidUnit (Term0 sig nat tt) := Op sig nat one.
  Global Instance: GroupInv (Term0 sig nat tt) :=
    λ x, App sig _ _ _ (Op sig nat inv) x.

  Local Notation x := (Var sig nat 0%nat tt).
  Local Notation y := (Var sig nat 1%nat tt).
  Local Notation z := (Var sig nat 2%nat tt).

  Import notations.

  Inductive Laws: EqEntailment sig Prop :=
    | e_mult_assoc: Laws (x & (y & z) === (x & y) & z)
    | e_mult_1_l: Laws (mon_unit & x === x)
    | e_mult_1_r: Laws (x & mon_unit === x)
    | e_mult_inv_l: Laws (-x & x === mon_unit)
    | e_mult_inv_r: Laws (x & -x === mon_unit).
End laws.

Definition theory: EquationalTheory := Build_EquationalTheory sig Laws.
Definition Object := varieties.Object theory.

Definition forget: Object setoids.Object :=
  @product.project unit
    (λ _, setoids.Object)
    (λ _, _: Arrows setoids.Object) _
    (λ _, _: CatId setoids.Object)
    (λ _, _: CatComp setoids.Object)
    (λ _, _: Category setoids.Object) tt
      forget_algebra.object theory forget_variety.forget theory.

Instance encode_operations A `{!SemiGroupOp A} `(GroupInv A) `{!MonoidUnit A}: AlgebraOps sig (λ _, A) :=
  λ o, match o with mult => (&) | inv => (-) | one => mon_unit: A end.

Section decode_operations.
  Context `{AlgebraOps theory A}.

  Global Instance: MonoidUnit (A tt) := algebra_op one.
  Global Instance: SemiGroupOp (A tt) := algebra_op mult.
  Global Instance: GroupInv (A tt) := algebra_op inv.
End decode_operations.

Section encode_variety_and_ops.
  Context A `{Group A}.

  Global Instance encode_algebra_and_ops: Algebra sig _.

  Global Instance encode_variety_and_ops: InVariety theory (λ _, A).

  Definition object: Object := varieties.object theory (λ _, A).
End encode_variety_and_ops.

Lemma encode_algebra_only `{!AlgebraOps theory A} `{ u, Equiv (A u)} `{!Group (A tt)}: Algebra theory A .

Global Instance decode_variety_and_ops `{InVariety theory A}: Group (A tt).

Lemma encode_morphism_only
  `{AlgebraOps theory A} `{ u, Equiv (A u)}
  `{AlgebraOps theory B} `{ u, Equiv (B u)}
  (f: u, A u B u) `{!Group (A tt)} `{!Group (B tt)} `{!Monoid_Morphism (f tt)}: HomoMorphism sig A B f.

Lemma encode_morphism_and_ops `{Group A} `{Group B} `{!Monoid_Morphism (f: A B)}:
  @HomoMorphism sig (λ _, A) (λ _, B) _ _ ( _) ( _) (λ _, f).

Lemma decode_morphism_and_ops
  `{InVariety theory x} `{InVariety theory y} `{!HomoMorphism theory x y f}:
    Monoid_Morphism (f tt).