/-- Given a congruence relation `c` on a type `M` with a multiplication, the order-preserving
    bijection between the set of congruence relations containing `c` and the congruence relations
    on the quotient of `M` by `c`. -/
@[to_additive "Given an additive congruence relation `c` on a type `M` with an addition,
the order-preserving bijection between the set of additive congruence relations containing `c` and
the additive congruence relations on the quotient of `M` by `c`."]
def correspondence : { d // c ≤ d } ≃o Con c.Quotient
    where
  toFun d :=
    d.1.mapOfSurjective (↑) _ (by rw [mul_ker_mk_eq]; exact d.2) <| @exists_rep _ c.toSetoid
  invFun d :=
    ⟨comap ((↑) : M → c.Quotient) (fun x y => rfl) d, fun x y h =>
      show d x y by rw [c.eq.2 h]; exact d.refl _⟩
  left_inv d :=
    --Porting note: by exact needed for unknown reason
    by exact
      Subtype.ext_iff_val.2 <|
        ext fun x y =>
          ⟨fun h =>
            let ⟨a, b, hx, hy, H⟩ := h
            d.1.trans (d.1.symm <| d.2 <| c.eq.1 hx) <| d.1.trans H <| d.2 <| c.eq.1 hy,
            fun h => ⟨_, _, rfl, rfl, h⟩⟩
  right_inv d :=
    --Porting note: by exact needed for unknown reason
    by exact
      ext fun x y =>
        ⟨fun h =>
          let ⟨_, _, hx, hy, H⟩ := h
          hx ▸ hy ▸ H,
          Con.induction_on₂ x y fun w z h => ⟨w, z, rfl, rfl, h⟩⟩
  map_rel_iff' := @fun s t => by
    constructor
    · intros h x y hs
      rcases h ⟨x, y, rfl, rfl, hs⟩ with ⟨a, b, hx, hy, ht⟩
      

Proof state:
case mp.intro.intro.intro.intro
M : Type u_1
N : Type u_2
P : Type u_3
inst✝² : Mul M
inst✝¹ : Mul N
inst✝ : Mul P
c : Con M
s t : { d // c ≤ d }
h :
  {
        toFun := fun d =>
          mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
            (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
            (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q),
        invFun := fun d =>
          { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
            property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) },
        left_inv :=
          (_ :
            ∀ (d : { d // c ≤ d }),
              (fun d =>
                    { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
                      property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) })
                  ((fun d =>
                      mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
                        (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
                        (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q))
                    d) =
                d),
        right_inv :=
          (_ :
            ∀ (d : Con (Con.Quotient c)),
              (fun d =>
                    mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
                      (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
                      (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q))
                  ((fun d =>
                      { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
                        property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) })
                    d) =
                d) }
      s ≤
    {
        toFun := fun d =>
          mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
            (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
            (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q),
        invFun := fun d =>
          { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
            property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) },
        left_inv :=
          (_ :
            ∀ (d : { d // c ≤ d }),
              (fun d =>
                    { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
                      property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) })
                  ((fun d =>
                      mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
                        (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
                        (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q))
                    d) =
                d),
        right_inv :=
          (_ :
            ∀ (d : Con (Con.Quotient c)),
              (fun d =>
                    mapOfSurjective (↑d) toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1))
                      (_ : mulKer toQuotient (_ : ∀ (x x_1 : M), ↑(x * x_1) = ↑(x * x_1)) ≤ ↑d)
                      (_ : ∀ (q : Quotient c.toSetoid), ∃ a, ⟦a⟧ = q))
                  ((fun d =>
                      { val := comap toQuotient (_ : ∀ (x y : M), ↑(x * y) = ↑(x * y)) d,
                        property := (_ : ∀ (x y : M), c x y → d ↑x ↑y) })
                    d) =
                d) }
      t
x y : M
hs : ↑s x y
a b : M
hx : ↑a = ↑x
hy : ↑b = ↑y
ht : Rel (↑t).toSetoid a b
⊢ ↑t x y

Next tactic:
exact t.1.trans (t.1.symm <| t.2 <| eq_rel.1 hx) (t.1.trans ht (t.2 <| eq_rel.1 hy))