----------------------------------------------------------------------------------------------------
--- Copyright (c) 2014 Microsoft Corporation. All rights reserved.
--- Released under Apache 2.0 license as described in the file LICENSE.
--- Author: Jeremy Avigad
----------------------------------------------------------------------------------------------------
import logic.core.connectives algebra.function
open function

namespace congr

inductive struc {T1 : Type} {T2 : Type} (R1 : T1 → T1 → Prop) (R2 : T2 → T2 → Prop)
    (f : T1 → T2) : Prop :=
mk : (∀x y : T1, R1 x y → R2 (f x) (f y)) → struc R1 R2 f

definition app {T1 : Type} {T2 : Type} {R1 : T1 → T1 → Prop} {R2 : T2 → T2 → Prop}
    {f : T1 → T2} (C : struc R1 R2 f) {x y : T1} : R1 x y → R2 (f x) (f y) :=
struc.rec id C x y

inductive struc2 {T1 : Type} {T2 : Type} {T3 : Type} (R1 : T1 → T1 → Prop)
    (R2 : T2 → T2 → Prop) (R3 : T3 → T3 → Prop) (f : T1 → T2 → T3) : Prop :=
mk2 : (∀(x1 y1 : T1) (x2 y2 : T2), R1 x1 y1 → R2 x2 y2 → R3 (f x1 x2) (f y1 y2)) →
    struc2 R1 R2 R3 f

definition app2 {T1 : Type} {T2 : Type} {T3 : Type} {R1 : T1 → T1 → Prop}
    {R2 : T2 → T2 → Prop} {R3 : T3 → T3 → Prop} {f : T1 → T2 → T3}
    (C : struc2 R1 R2 R3 f) {x1 y1 : T1} {x2 y2 : T2}
  : R1 x1 y1 → R2 x2 y2 → R3 (f x1 x2) (f y1 y2) :=
struc2.rec id C x1 y1 x2 y2

theorem compose21
    {T2 : Type} {R2 : T2 → T2 → Prop}
    {T3 : Type} {R3 : T3 → T3 → Prop}
    {T4 : Type} {R4 : T4 → T4 → Prop}
    {g : T2 → T3 → T4} (C3 : congr.struc2 R2 R3 R4 g)
    ⦃T1 : Type⦄  -- nice!
    {R1 : T1 → T1 → Prop}
    {f1 : T1 → T2} (C1 : congr.struc R1 R2 f1)
    {f2 : T1 → T3} (C2 : congr.struc R1 R3 f2) :
  congr.struc R1 R4 (λx, g (f1 x) (f2 x)) := struc.mk (take x1 x2 H, app2 C3 (app C1 H) (app C2 H))

theorem congr_and : congr.struc2 iff iff iff and := sorry

theorem congr_and_comp [instance] {T : Type} {R : T → T → Prop} {f1 f2 : T → Prop}
     (C1 : struc R iff f1) (C2 : struc R iff f2) :
   congr.struc R iff (λx, f1 x ∧ f2 x) := congr.compose21 congr_and C1 C2

end congr
