import data.real.basic

--OUTPUT 1
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  have h1 : (a + b + c) / (b + c) + (a + b + c) / (a + c) + (a + b + c) / (a + b) ≥ 9 / 2, from sorry,
  have h2 : (a + b + c) / (b + c) + (a + b + c) / (a + c) + (a + b + c) / (a + b) ≥ 3 / ((b + c) + (a + c) + (a + b)), from sorry,
  have h3 : (1 / (b + c) + 1 / (a + c) + 1 / (a + b)) / 3 ≥ 3 / ((b + c) + (a + c) + (a + b)), from sorry,
  show (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2), from sorry,
end

--OUTPUT 2
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
      calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
        calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
          calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
            calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
              calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                      calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                        calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                          calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                            calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                              calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                      calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                        calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                          calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                            calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                              calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                      calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                        calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                          calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                            calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                              calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                      calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                        calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                          calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                            calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                              calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
                                                                                calc (a / (b + c)) + (b / (a + c)) + (c / (a
end --Needs more than 2000 tokens!

--OUTPUT 3
theorem nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
begin
  calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) : by {
    calc (a / (b + c)) + (b / (a + c)) + (c / (a + b)) = (a + b + c) / (b + c) + (a + b + c) / (a + c) + (a + b + c) / (a + b) : by sorry
    ... ≥ (9 * (a + b + c)) / ((b + c) + (a + c) + (a + b)) : by sorry
    ... ≥ (3 / ((b + c) + (a + c) + (a + b))) : by sorry
    ... ≥ (3 / 2) : by sorry,
  },
end

/- FEW SHOT PROMPTS TO CODEX(START)
/--`theorem`
Power Set is Closed under Intersection
Let $S$ be a set.

Let $\powerset S$ be the power set of $S$.

Then:
:$\forall A, B \in \powerset S: A \cap B \in \powerset S$
`proof`
Let $A, B \in \powerset S$.

Then by the definition of power set, $A \subseteq S$ and $B \subseteq S$.

From Intersection is Subset we have that $A \cap B \subseteq A$.

It follows from Subset Relation is Transitive that $A \cap B \subseteq S$.

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from sorry,
  have h2 : (A ∩ B) ⊆ A, from sorry,
  have h3 : (A ∩ B) ⊆ S, from sorry,
  show (A ∩ B) ∈  𝒫 S, from sorry,
end

/--`theorem`
Square of Sum
 :$\forall x, y \in \R: \paren {x + y}^2 = x^2 + 2 x y + y^2$
`proof`
Follows from the distribution of multiplication over addition:

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  calc (x + y)^2 = (x+y)*(x+y) : by sorry
  ... = x*(x+y) + y*(x+y) : by sorry
  ... = x*x + x*y + y*x + y*y : by sorry
  ... = x^2 + 2*x*y + y^2 : by sorry,
end

/--`theorem`
Identity of Group is Unique
Let $\struct {G, \circ}$ be a group. Then there is a unique identity element $e \in G$.
`proof`
From Group has Latin Square Property, there exists a unique $x \in G$ such that:
:$a x = b$

and there exists a unique $y \in G$ such that:
:$y a = b$

Setting $b = a$, this becomes:

There exists a unique $x \in G$ such that:
:$a x = a$

and there exists a unique $y \in G$ such that:
:$y a = a$

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from sorry,
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from sorry,

  have h3 : ∀ a : G, ∃! x : G, a * x = a, from sorry,
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from sorry,

  have h5 : ∀ a : G, classical.some (h3 a) = (1 : G), from sorry,
  have h6 : ∀ a : G, classical.some (h4 a) = (1 : G), from sorry,

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by {
    use (1 : G),
    have h7 : ∀ e : G, (∀ a : G, e * a = a ∧ a * e = a) → e = 1, from by {
      assume (e : G) (h7 : ∀ a : G, e * a = a ∧ a * e = a),
      have h8 : ∀ a : G, e = classical.some (h3 a), from sorry,
      have h9 : ∀ a : G, e = classical.some (h4 a), from sorry,
      show e = (1 : G), from sorry,
    },
    sorry,
  }
end

/--`theorem`
Nesbitt's inequality
Let $a$, $b$ and $c$ be positive real numbers.

Then:
:$\dfrac a {b + c} + \dfrac b {a + c} + \dfrac c {a + b} \ge \dfrac 3 2$

`proof`
{{begin-eqn}}
{{eqn | l = \frac a {b + c} + \frac b {a + c} + \frac c {a + b}
      | o = \ge
      | r = \dfrac 3 2
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac 9 2
      | c = by adding $3$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {a + b + c} {b + c} + \frac {a + b + c} {a + c} + \frac {a + b + c} {a + b}
      | o = \ge
      | r = \frac {9 \paren {a + b + c} } {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = as $\dfrac {a + b + c} {\paren {b + c} + \paren {a + c} + \paren {a + b} } = \dfrac 1 2$
}}
{{eqn | ll= \leadstoandfrom
      | l = \frac {\frac 1 {b + c} + \frac 1 {a + c} + \frac 1 {a + b} } 3
      | o = \ge
      | r = \frac 3 {\paren {b + c} + \paren {a + c} + \paren {a + b} }
      | c = dividing by $3 \paren {a + b + c}$
}}
{{end-eqn}}
These are the arithmetic mean and the harmonic mean of $\dfrac 1 {b + c}$, $\dfrac 1 {a + c}$ and $\dfrac 1 {a + b}$.

From Arithmetic Mean is Never Less than Harmonic Mean the last inequality is true.

Thus Nesbitt's Inequality holds.
{{qed}}

-/
theorem  nesbitt_inequality {a b c : ℝ} (ha : 0 < a) (hb : 0 < b) (hc : 0 < c) : (a / (b + c)) + (b / (a + c)) + (c / (a + b)) ≥ (3 / 2) :=
FEW SHOT PROMPTS TO CODEX(END)-/


import data.real.basic
import algebra.ring.basic
import algebra.group_power.order
import algebra.group_with_zero.basic

theorem IMO_2008_Pb_4 (f:ℝ  → ℝ)(hf0:∀(x:ℝ), (0<x) → (0 <f x))
(hf: ∀(x y z w :ℝ), (0<x∧0<z∧0<y∧0<w) → ((f w^2)+(f x^2))/ (f (y^2)+(f (z^2))) = (w^2+x^2)/(y^2+z^2)):
(∀ (x:ℝ), 0<x→ f x = x)∨(∀ (x:ℝ),0<x → f x = 1/x):=
begin
  sorry
end


import to_mathlib_maybe.Hilbert_space
import entropy
import state

import Lemma3

notation `D(`ρ`,`σ`)` := 1/2 * ∥ρ - σ∥₁
notation ρ`^⊗`n := tpow ρ n

variables {ℋ : Type} [complex_hilbert_space ℋ]
{ρ ρ' : module.End ℂ ℋ} [quantum_state ρ]
[H(ρ) ≤ H(ρ')]

def dephasing_channel_in_eigenbasis (ρ : module.End ℂ ℋ) [quantum_state ρ] : quantum_channel := sorry

notation `𝒟 ` ρ := dephasing_channel_in_eigenbasis ρ

/--
"The next Lemma will be essential to construct a candidate
catalyst by making use of Lemma 3. It is based on the Schur-Horn
theorem, which states that for any d×d Hermitian matrix its vector
of eigenvalues λ majorizes the vector of diagonal elements in every
orthonormal basis. Conversely, every vector that is majorized by λ
may be obtained as the diagonal elements in a suitable orthonormal
basis."
-/
lemma basic_lemma :
∀ ε > 0, ∃ (n : ℕ), ∃ (U : module.End ℂ ℋ) [unitary U], ∀ k ≤ n,
D(ρ', 𝒟(pTr_{1,..,n}\{k} (U * ρ^⊗n * U†))) ≤ ε :=
begin
  sorry
end


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Seul Baek

! This file was ported from Lean 3 source module tactic.omega.int.main
! leanprover-community/mathlib commit 58581d0fe523063f5651df0619be2bf65012a94a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Tactic.Omega.ProveUnsats
import Mathbin.Tactic.Omega.Int.Dnf

/-
Main procedure for linear integer arithmetic.
-/
open Tactic

namespace Omega

namespace Int

open Omega.Int

run_cmd
  mk_simp_attr `sugar

attribute [sugar]
  Ne not_le not_lt Int.lt_iff_add_one_le or_false_iff false_or_iff and_true_iff true_and_iff GE.ge GT.gt mul_add add_mul one_mul mul_one mul_comm sub_eq_add_neg imp_iff_not_or iff_iff_not_or_and_or_not

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
unsafe def desugar :=
  sorry
#align omega.int.desugar omega.int.desugar

theorem univClose_of_unsat_clausify (m : Nat) (p : Preform) :
    Clauses.Unsat (dnf (¬* p)) → UnivClose p (fun x => 0) m
  | h1 => by
    apply univ_close_of_valid
    apply valid_of_unsat_not
    apply unsat_of_clauses_unsat
    exact h1
#align omega.int.univ_close_of_unsat_clausify Omega.Int.univClose_of_unsat_clausify

/-- Given a (p : preform), return the expr of a (t : univ_close m p) -/
unsafe def prove_univ_close (m : Nat) (p : Preform) : tactic expr := do
  let x ← prove_unsats (dnf (¬* p))
  return q(univClose_of_unsat_clausify $(q(m)) $(q(p)) $(x))
#align omega.int.prove_univ_close omega.int.prove_univ_close

-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Reification to imtermediate shadow syntax that retains exprs -/ unsafe
  def
    to_exprterm
    : expr → tactic exprterm
    |
        q( - $ ( x ) )
        =>
        ( do let z ← eval_expr' Int x return ( exprterm.cst ( - z : Int ) ) )
          <|>
          ( return <| exprterm.exp ( - 1 : Int ) x )
      | q( $ ( mx ) * $ ( zx ) ) => do let z ← eval_expr' Int zx return ( exprterm.exp z mx )
      |
        q( $ ( t1x ) + $ ( t2x ) )
        =>
        do let t1 ← to_exprterm t1x let t2 ← to_exprterm t2x return ( exprterm.add t1 t2 )
      |
        x
        =>
        ( do let z ← eval_expr' Int x return ( exprterm.cst z ) ) <|> ( return <| exprterm.exp 1 x )
#align omega.int.to_exprterm omega.int.to_exprterm

-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Reification to imtermediate shadow syntax that retains exprs -/ unsafe
  def
    to_exprform
    : expr → tactic exprform
    |
        q( $ ( tx1 ) = $ ( tx2 ) )
        =>
        do let t1 ← to_exprterm tx1 let t2 ← to_exprterm tx2 return ( exprform.eq t1 t2 )
      |
        q( $ ( tx1 ) ≤ $ ( tx2 ) )
        =>
        do let t1 ← to_exprterm tx1 let t2 ← to_exprterm tx2 return ( exprform.le t1 t2 )
      | q( ¬ $ ( px ) ) => do let p ← to_exprform px return ( exprform.not p )
      |
        q( $ ( px ) ∨ $ ( qx ) )
        =>
        do let p ← to_exprform px let q ← to_exprform qx return ( exprform.or p q )
      |
        q( $ ( px ) ∧ $ ( qx ) )
        =>
        do let p ← to_exprform px let q ← to_exprform qx return ( exprform.and p q )
      | q( _ → $ ( px ) ) => to_exprform px
      | x => ( trace "Cannot reify expr : " >> trace x ) >> failed
#align omega.int.to_exprform omega.int.to_exprform

/-- List of all unreified exprs -/
unsafe def exprterm.exprs : exprterm → List expr
  | exprterm.cst _ => []
  | exprterm.exp _ x => [x]
  | exprterm.add t s => List.union t.exprs s.exprs
#align omega.int.exprterm.exprs omega.int.exprterm.exprs

/-- List of all unreified exprs -/
unsafe def exprform.exprs : exprform → List expr
  | exprform.eq t s => List.union t.exprs s.exprs
  | exprform.le t s => List.union t.exprs s.exprs
  | exprform.not p => p.exprs
  | exprform.or p q => List.union p.exprs q.exprs
  | exprform.and p q => List.union p.exprs q.exprs
#align omega.int.exprform.exprs omega.int.exprform.exprs

/-- Reification to an intermediate shadow syntax which eliminates exprs,
    but still includes non-canonical terms -/
unsafe def exprterm.to_preterm (xs : List expr) : exprterm → tactic Preterm
  | exprterm.cst k => return (&k)
  | exprterm.exp k x =>
    let m := xs.indexOfₓ x
    if m < xs.length then return (k ** m) else failed
  | exprterm.add xa xb => do
    let a ← xa.to_preterm
    let b ← xb.to_preterm
    return (a +* b)
#align omega.int.exprterm.to_preterm omega.int.exprterm.to_preterm

/-- Reification to an intermediate shadow syntax which eliminates exprs,
    but still includes non-canonical terms -/
unsafe def exprform.to_preform (xs : List expr) : exprform → tactic Preform
  | exprform.eq xa xb => do
    let a ← xa.to_preterm xs
    let b ← xb.to_preterm xs
    return (a =* b)
  | exprform.le xa xb => do
    let a ← xa.to_preterm xs
    let b ← xb.to_preterm xs
    return (a ≤* b)
  | exprform.not xp => do
    let p ← xp.to_preform
    return (¬* p)
  | exprform.or xp xq => do
    let p ← xp.to_preform
    let q ← xq.to_preform
    return (p ∨* q)
  | exprform.and xp xq => do
    let p ← xp.to_preform
    let q ← xq.to_preform
    return (p ∧* q)
#align omega.int.exprform.to_preform omega.int.exprform.to_preform

/-- Reification to an intermediate shadow syntax which eliminates exprs,
    but still includes non-canonical terms. -/
unsafe def to_preform (x : expr) : tactic (Preform × Nat) := do
  let xf ← to_exprform x
  let xs := xf.exprs
  let f ← xf.to_preform xs
  return (f, xs)
#align omega.int.to_preform omega.int.to_preform

/-- Return expr of proof of current LIA goal -/
unsafe def prove : tactic expr := do
  let (p, m) ← target >>= to_preform
  trace_if_enabled `omega p
  prove_univ_close m p
#align omega.int.prove omega.int.prove

/-- Succeed iff argument is the expr of ℤ -/
unsafe def eq_int (x : expr) : tactic Unit :=
  if x = q(Int) then skip else failed
#align omega.int.eq_int omega.int.eq_int

-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Check whether argument is expr of a well-formed formula of LIA-/ unsafe
  def
    wff
    : expr → tactic Unit
    | q( ¬ $ ( px ) ) => wff px
      | q( $ ( px ) ∨ $ ( qx ) ) => wff px >> wff qx
      | q( $ ( px ) ∧ $ ( qx ) ) => wff px >> wff qx
      | q( $ ( px ) ↔ $ ( qx ) ) => wff px >> wff qx
      |
        q( $ ( expr.pi _ _ px qx ) )
        =>
        Monad.cond
          ( if expr.has_var px then return true else is_prop px )
            ( wff px >> wff qx )
            ( eq_int px >> wff qx )
      | q( @ LT.lt $ ( dx ) $ ( h ) _ _ ) => eq_int dx
      | q( @ LE.le $ ( dx ) $ ( h ) _ _ ) => eq_int dx
      | q( @ Eq $ ( dx ) _ _ ) => eq_int dx
      | q( @ GE.ge $ ( dx ) $ ( h ) _ _ ) => eq_int dx
      | q( @ GT.gt $ ( dx ) $ ( h ) _ _ ) => eq_int dx
      | q( @ Ne $ ( dx ) _ _ ) => eq_int dx
      | q( True ) => skip
      | q( False ) => skip
      | _ => failed
#align omega.int.wff omega.int.wff

/-- Succeed iff argument is expr of term whose type is wff -/
unsafe def wfx (x : expr) : tactic Unit :=
  infer_type x >>= wff
#align omega.int.wfx omega.int.wfx

/-- Intro all universal quantifiers over ℤ -/
unsafe def intro_ints_core : tactic Unit := do
  let x ← target
  match x with
    | expr.pi _ _ q(Int) _ => intro_fresh >> intro_ints_core
    | _ => skip
#align omega.int.intro_ints_core omega.int.intro_ints_core

unsafe def intro_ints : tactic Unit := do
  let expr.pi _ _ q(Int) _ ← target
  intro_ints_core
#align omega.int.intro_ints omega.int.intro_ints

/-- If the goal has universal quantifiers over integers, introduce all of them.
Otherwise, revert all hypotheses that are formulas of linear integer arithmetic. -/
unsafe def preprocess : tactic Unit :=
  intro_ints <|> revert_cond_all wfx >> desugar
#align omega.int.preprocess omega.int.preprocess

end Int

end Omega

open Omega.Int

/-- The core omega tactic for integers. -/
unsafe def omega_int (is_manual : Bool) : tactic Unit :=
  andthen (andthen desugar (if is_manual then skip else preprocess)) ((prove >>= apply) >> skip)
#align omega_int omega_int


/-
Copyright (c) 2022 Jannis Limperg. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jannis Limperg
-/

-- This file tests the builtin rules that destruct hypotheses with product-like
-- types.

import Aesop

set_option aesop.check.all true

@[aesop safe constructors]
inductive Ex (α : Sort u) (β : α → Prop) : Prop
  | intro (fst : α) (snd : β fst)

@[aesop safe constructors]
structure Sig (α : Sort u) (β : α → Sort v) : Sort _ where
  fst : α
  snd : β fst

example (h : α ∧ β) : Sig α (λ _ => β) := by
  aesop

example (h : α × β) : Sig α (λ _ => β) := by
  aesop

example (h : PProd α β) : Sig α (λ _ => β) := by
  aesop

example (h : MProd α β) : Sig α (λ _ => β) := by
  aesop

example {p : α → Prop} (h : ∃ a, p a) : Ex α p := by
  aesop

example {p : α → Prop} (h : { a // p a }) : Sig α p := by
  aesop

example {p : α → Type} (h : Σ a, p a) : Sig α p := by
  aesop

example {p : α → Type} (h : Σ' a, p a) : Sig α p := by
  aesop


/-
Copyright (c) 2022 Yakov Pechersky. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yakov Pechersky

! This file was ported from Lean 3 source module data.finsupp.big_operators
! leanprover-community/mathlib commit f16e7a22e11fc09c71f25446ac1db23a24e8a0bd
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finsupp.Defs
import Mathbin.Data.Finset.Pairwise

/-!

# Sums of collections of finsupp, and their support

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
This file provides results about the `finsupp.support` of sums of collections of `finsupp`,
including sums of `list`, `multiset`, and `finset`.

The support of the sum is a subset of the union of the supports:
* `list.support_sum_subset`
* `multiset.support_sum_subset`
* `finset.support_sum_subset`

The support of the sum of pairwise disjoint finsupps is equal to the union of the supports
* `list.support_sum_eq`
* `multiset.support_sum_eq`
* `finset.support_sum_eq`

Member in the support of the indexed union over a collection iff
it is a member of the support of a member of the collection:
* `list.mem_foldr_sup_support_iff`
* `multiset.mem_sup_map_support_iff`
* `finset.mem_sup_support_iff`

-/

variable {ι M : Type _} [DecidableEq ι]

/- warning: list.support_sum_subset -> List.support_sum_subset is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddMonoid.{u2} M] (l : List.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.hasSubset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (List.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finsupp.add.{u1, u2} ι M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (Finsupp.zero.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) l)) (List.foldr.{max u1 u2, u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finset.{u1} ι) (Function.comp.{max (succ u1) (succ u2), succ u1, succ u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toHasSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))))) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)))) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.hasEmptyc.{u1} ι)) l)
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddMonoid.{u2} M] (l : List.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.instHasSubsetFinset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2) (List.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finsupp.add.{u1, u2} ι M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (Finsupp.zero.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) l)) (List.foldr.{max u2 u1, u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finset.{u1} ι) (Function.comp.{succ (max u2 u1), succ u1, succ u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (fun (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.45 : Finset.{u1} ι) (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.47 : Finset.{u1} ι) => Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b)))) x._@.Mathlib.Data.Finsupp.BigOperators._hyg.45 x._@.Mathlib.Data.Finsupp.BigOperators._hyg.47) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2))) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.instEmptyCollectionFinset.{u1} ι)) l)
Case conversion may be inaccurate. Consider using '#align list.support_sum_subset List.support_sum_subsetₓ'. -/
theorem List.support_sum_subset [AddMonoid M] (l : List (ι →₀ M)) :
    l.Sum.support ⊆ l.foldr ((· ⊔ ·) ∘ Finsupp.support) ∅ :=
  by
  induction' l with hd tl IH
  · simp
  · simp only [List.sum_cons, Finset.union_comm]
    refine' finsupp.support_add.trans (Finset.union_subset_union _ IH)
    rfl
#align list.support_sum_subset List.support_sum_subset

/- warning: multiset.support_sum_subset -> Multiset.support_sum_subset is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Multiset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.hasSubset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Multiset.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s)) (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) (Multiset.map.{max u1 u2, u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) s))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Multiset.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.instHasSubsetFinset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Multiset.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s)) (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) (Multiset.map.{max u2 u1, u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) s))
Case conversion may be inaccurate. Consider using '#align multiset.support_sum_subset Multiset.support_sum_subsetₓ'. -/
theorem Multiset.support_sum_subset [AddCommMonoid M] (s : Multiset (ι →₀ M)) :
    s.Sum.support ⊆ (s.map Finsupp.support).sup :=
  by
  induction s using Quot.inductionOn
  simpa using List.support_sum_subset _
#align multiset.support_sum_subset Multiset.support_sum_subset

/- warning: finset.support_sum_subset -> Finset.support_sum_subset is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Finset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.hasSubset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.sum.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s (id.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))))) (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) s (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Finset.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))), HasSubset.Subset.{u1} (Finset.{u1} ι) (Finset.instHasSubsetFinset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Finset.sum.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s (id.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))) (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) s (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))
Case conversion may be inaccurate. Consider using '#align finset.support_sum_subset Finset.support_sum_subsetₓ'. -/
theorem Finset.support_sum_subset [AddCommMonoid M] (s : Finset (ι →₀ M)) :
    (s.Sum id).support ⊆ Finset.sup s Finsupp.support := by
  classical convert Multiset.support_sum_subset s.1 <;> simp
#align finset.support_sum_subset Finset.support_sum_subset

/- warning: list.mem_foldr_sup_support_iff -> List.mem_foldr_sup_support_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {l : List.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (List.foldr.{max u1 u2, u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) (Function.comp.{max (succ u1) (succ u2), succ u1, succ u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toHasSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))))) (Finsupp.support.{u1, u2} ι M _inst_2)) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.hasEmptyc.{u1} ι)) l)) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (List.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (List.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f l) (fun (hf : Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (List.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (List.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f l) => Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {l : List.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (List.foldr.{max u2 u1, u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) (Function.comp.{succ (max u2 u1), succ u1, succ u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (fun (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.336 : Finset.{u1} ι) (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.338 : Finset.{u1} ι) => Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b)))) x._@.Mathlib.Data.Finsupp.BigOperators._hyg.336 x._@.Mathlib.Data.Finsupp.BigOperators._hyg.338) (Finsupp.support.{u1, u2} ι M _inst_2)) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.instEmptyCollectionFinset.{u1} ι)) l)) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (List.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (List.instMembershipList.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f l) (fun (hf : Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (List.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (List.instMembershipList.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f l) => Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
Case conversion may be inaccurate. Consider using '#align list.mem_foldr_sup_support_iff List.mem_foldr_sup_support_iffₓ'. -/
theorem List.mem_foldr_sup_support_iff [Zero M] {l : List (ι →₀ M)} {x : ι} :
    x ∈ l.foldr ((· ⊔ ·) ∘ Finsupp.support) ∅ ↔ ∃ (f : ι →₀ M)(hf : f ∈ l), x ∈ f.support :=
  by
  simp only [Finset.sup_eq_union, List.foldr_map, Finsupp.mem_support_iff, exists_prop]
  induction' l with hd tl IH
  · simp
  · simp only [IH, List.foldr_cons, Finset.mem_union, Finsupp.mem_support_iff, List.mem_cons]
    constructor
    · rintro (h | h)
      · exact ⟨hd, Or.inl rfl, h⟩
      · exact h.imp fun f hf => hf.imp_left Or.inr
    · rintro ⟨f, rfl | hf, h⟩
      · exact Or.inl h
      · exact Or.inr ⟨f, hf, h⟩
#align list.mem_foldr_sup_support_iff List.mem_foldr_sup_support_iff

/- warning: multiset.mem_sup_map_support_iff -> Multiset.mem_sup_map_support_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {s : Multiset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) (Multiset.map.{max u1 u2, u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M _inst_2) s))) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Multiset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (Multiset.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) (fun (hf : Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Multiset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (Multiset.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) => Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {s : Multiset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) (Multiset.map.{max u2 u1, u1} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M _inst_2) s))) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Multiset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (Multiset.instMembershipMultiset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) (fun (hf : Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Multiset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (Multiset.instMembershipMultiset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) => Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
Case conversion may be inaccurate. Consider using '#align multiset.mem_sup_map_support_iff Multiset.mem_sup_map_support_iffₓ'. -/
theorem Multiset.mem_sup_map_support_iff [Zero M] {s : Multiset (ι →₀ M)} {x : ι} :
    x ∈ (s.map Finsupp.support).sup ↔ ∃ (f : ι →₀ M)(hf : f ∈ s), x ∈ f.support :=
  Quot.inductionOn s fun _ => by simpa using List.mem_foldr_sup_support_iff
#align multiset.mem_sup_map_support_iff Multiset.mem_sup_map_support_iff

/- warning: finset.mem_sup_support_iff -> Finset.mem_sup_support_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {s : Finset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M _inst_2) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) s (Finsupp.support.{u1, u2} ι M _inst_2))) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (Finset.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) (fun (hf : Membership.Mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) (Finset.hasMem.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) => Membership.Mem.{u1, u1} ι (Finset.{u1} ι) (Finset.hasMem.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Zero.{u2} M] {s : Finset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)} {x : ι}, Iff (Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M _inst_2) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) s (Finsupp.support.{u1, u2} ι M _inst_2))) (Exists.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M _inst_2) (fun (f : Finsupp.{u1, u2} ι M _inst_2) => Exists.{0} (Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (Finset.instMembershipFinset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) (fun (hf : Membership.mem.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M _inst_2) (Finset.{max u2 u1} (Finsupp.{u1, u2} ι M _inst_2)) (Finset.instMembershipFinset.{max u1 u2} (Finsupp.{u1, u2} ι M _inst_2)) f s) => Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) x (Finsupp.support.{u1, u2} ι M _inst_2 f))))
Case conversion may be inaccurate. Consider using '#align finset.mem_sup_support_iff Finset.mem_sup_support_iffₓ'. -/
theorem Finset.mem_sup_support_iff [Zero M] {s : Finset (ι →₀ M)} {x : ι} :
    x ∈ s.sup Finsupp.support ↔ ∃ (f : ι →₀ M)(hf : f ∈ s), x ∈ f.support :=
  Multiset.mem_sup_map_support_iff
#align finset.mem_sup_support_iff Finset.mem_sup_support_iff

/- warning: list.support_sum_eq -> List.support_sum_eq is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddMonoid.{u2} M] (l : List.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)))), (List.Pairwise.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Function.onFun.{succ (max u1 u2), succ u1, 1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finset.{u1} ι) Prop (Disjoint.{u1} (Finset.{u1} ι) (Finset.partialOrder.{u1} ι) (Finset.orderBot.{u1} ι)) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)))) l) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (List.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finsupp.add.{u1, u2} ι M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (Finsupp.zero.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) l)) (List.foldr.{max u1 u2, u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finset.{u1} ι) (Function.comp.{max (succ u1) (succ u2), succ u1, succ u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2))) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toHasSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))))) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_2)))) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.hasEmptyc.{u1} ι)) l))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddMonoid.{u2} M] (l : List.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2))), (List.Pairwise.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Function.onFun.{succ (max u2 u1), succ u1, 1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finset.{u1} ι) Prop (Disjoint.{u1} (Finset.{u1} ι) (Finset.partialOrder.{u1} ι) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι)) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2))) l) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2) (List.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finsupp.add.{u1, u2} ι M (AddMonoid.toAddZeroClass.{u2} M _inst_2)) (Finsupp.zero.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) l)) (List.foldr.{max u2 u1, u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finset.{u1} ι) (Function.comp.{succ (max u2 u1), succ u1, succ u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2)) (Finset.{u1} ι) ((Finset.{u1} ι) -> (Finset.{u1} ι)) (fun (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.585 : Finset.{u1} ι) (x._@.Mathlib.Data.Finsupp.BigOperators._hyg.587 : Finset.{u1} ι) => Sup.sup.{u1} (Finset.{u1} ι) (SemilatticeSup.toSup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b)))) x._@.Mathlib.Data.Finsupp.BigOperators._hyg.585 x._@.Mathlib.Data.Finsupp.BigOperators._hyg.587) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M _inst_2))) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} ι) (Finset.instEmptyCollectionFinset.{u1} ι)) l))
Case conversion may be inaccurate. Consider using '#align list.support_sum_eq List.support_sum_eqₓ'. -/
theorem List.support_sum_eq [AddMonoid M] (l : List (ι →₀ M))
    (hl : l.Pairwise (Disjoint on Finsupp.support)) :
    l.Sum.support = l.foldr ((· ⊔ ·) ∘ Finsupp.support) ∅ :=
  by
  induction' l with hd tl IH
  · simp
  · simp only [List.pairwise_cons] at hl
    simp only [List.sum_cons, List.foldr_cons, Function.comp_apply]
    rw [Finsupp.support_add_eq, IH hl.right, Finset.sup_eq_union]
    suffices Disjoint hd.support (tl.foldr ((· ⊔ ·) ∘ Finsupp.support) ∅) by
      exact Finset.disjoint_of_subset_right (List.support_sum_subset _) this
    · rw [← List.foldr_map, ← Finset.bot_eq_empty, List.foldr_sup_eq_sup_toFinset]
      rw [Finset.disjoint_sup_right]
      intro f hf
      simp only [List.mem_toFinset, List.mem_map] at hf
      obtain ⟨f, hf, rfl⟩ := hf
      exact hl.left _ hf
#align list.support_sum_eq List.support_sum_eq

/- warning: multiset.support_sum_eq -> Multiset.support_sum_eq is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Multiset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))), (Multiset.Pairwise.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Function.onFun.{succ (max u1 u2), succ u1, 1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finset.{u1} ι) Prop (Disjoint.{u1} (Finset.{u1} ι) (Finset.partialOrder.{u1} ι) (Finset.orderBot.{u1} ι)) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) s) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Multiset.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s)) (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) (Multiset.map.{max u1 u2, u1} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) s)))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Multiset.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))), (Multiset.Pairwise.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Function.onFun.{succ (max u2 u1), succ u1, 1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.{u1} ι) Prop (Disjoint.{u1} (Finset.{u1} ι) (Finset.partialOrder.{u1} ι) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι)) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) s) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Multiset.sum.{max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s)) (Multiset.sup.{u1} (Finset.{u1} ι) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) (Multiset.map.{max u2 u1, u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) s)))
Case conversion may be inaccurate. Consider using '#align multiset.support_sum_eq Multiset.support_sum_eqₓ'. -/
theorem Multiset.support_sum_eq [AddCommMonoid M] (s : Multiset (ι →₀ M))
    (hs : s.Pairwise (Disjoint on Finsupp.support)) : s.Sum.support = (s.map Finsupp.support).sup :=
  by
  induction s using Quot.inductionOn
  obtain ⟨l, hl, hd⟩ := hs
  convert List.support_sum_eq _ _
  · simp
  · simp
  · simp only [Multiset.quot_mk_to_coe'', Multiset.coe_map, Multiset.coe_eq_coe] at hl
    exact hl.symm.pairwise hd fun _ _ h => Disjoint.symm h
#align multiset.support_sum_eq Multiset.support_sum_eq

/- warning: finset.support_sum_eq -> Finset.support_sum_eq is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Finset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))), (Set.PairwiseDisjoint.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finset.partialOrder.{u1} ι) (Finset.orderBot.{u1} ι) ((fun (a : Type.{max u1 u2}) (b : Type.{max u1 u2}) [self : HasLiftT.{succ (max u1 u2), succ (max u1 u2)} a b] => self.0) (Finset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (Set.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (HasLiftT.mk.{succ (max u1 u2), succ (max u1 u2)} (Finset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (Set.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (CoeTCₓ.coe.{succ (max u1 u2), succ (max u1 u2)} (Finset.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (Set.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) (Finset.Set.hasCoeT.{max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))))) s) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.sum.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s (id.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))))) (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.lattice.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.orderBot.{u1} ι) s (Finsupp.support.{u1, u2} ι M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))))
but is expected to have type
  forall {ι : Type.{u1}} {M : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : AddCommMonoid.{u2} M] (s : Finset.{max u2 u1} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))), (Set.PairwiseDisjoint.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finset.partialOrder.{u1} ι) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) (Finset.toSet.{max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) s) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) -> (Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (Finset.sum.{max u1 u2, max u1 u2} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Finsupp.addCommMonoid.{u1, u2} ι M _inst_2) s (id.{max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))) (Finset.sup.{u1, max u1 u2} (Finset.{u1} ι) (Finsupp.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (Lattice.toSemilatticeSup.{u1} (Finset.{u1} ι) (Finset.instLatticeFinset.{u1} ι (fun (a : ι) (b : ι) => _inst_1 a b))) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} ι) s (Finsupp.support.{u1, u2} ι M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))
Case conversion may be inaccurate. Consider using '#align finset.support_sum_eq Finset.support_sum_eqₓ'. -/
theorem Finset.support_sum_eq [AddCommMonoid M] (s : Finset (ι →₀ M))
    (hs : (s : Set (ι →₀ M)).PairwiseDisjoint Finsupp.support) :
    (s.Sum id).support = Finset.sup s Finsupp.support := by
  classical
    convert Multiset.support_sum_eq s.1 _
    · exact (Finset.sum_val _).symm
    · obtain ⟨l, hl, hn⟩ : ∃ l : List (ι →₀ M), l.toFinset = s ∧ l.Nodup :=
        by
        refine' ⟨s.to_list, _, Finset.nodup_toList _⟩
        simp
      subst hl
      rwa [List.toFinset_val, list.dedup_eq_self.mpr hn, Multiset.pairwise_coe_iff_pairwise, ←
        List.pairwiseDisjoint_iff_coe_toFinset_pairwise_disjoint hn]
      intro x y hxy
      exact symmetric_disjoint hxy
#align finset.support_sum_eq Finset.support_sum_eq


/-
A labeled transition system built on top of temporal logic
-/

import .fixpoint

universes u v u' v'

open temporal

section LTS
parameters {S : Type u} {L : S → Type v}

-- TODO: Why can't this be a doc?
-- Ben S.: I think they're not allowed inside sections.
/-
A labeled trace takes a relation from start state through a label to an end state
and a trace made up of states paired with labels. In each pair the label represents
the step that will be taken from its paired state
-/
def LTS_trace  (r : ∀ s : S, L s → S → Prop)
  (t : trace (sigma L)) : Prop :=
  ∀ n : ℕ, r (t n).fst (t n).snd (t n.succ).fst

/--
Apply a function (usually a predicate) to the state of a state-label pair
-/
def inState {B} (f : S → B) (x : sigma L) : B := f x.fst

lemma inState_mono : subset.monotone (@inState Prop)
:= begin
intros P Q PQ x Hx, apply PQ, apply Hx
end

instance inState_decidable {P : S → Prop} [decP : decidable_pred P] :
  decidable_pred (@inState Prop P) :=
begin
intros x, apply decP,
end

parameter (LTS : ∀ s : S, L s → S → Prop)

/--
A trace is valid if it is a labeled transition system
-/
structure valid_trace (t : trace (sigma L)) : Prop :=
  (next_step : LTS_trace LTS t)

lemma prove_next {P : sigma L → Prop}
  {Q : S → Prop}
  (H : ∀ s l s', LTS s l s' → P ⟨ s, l ⟩ → Q s')
  : ⊩ valid_trace
    => now P => ◯ (now (inState Q))
:= begin
intros tr validtr,
unfold next nextn now later,
intros HP,
apply H,
apply validtr.next_step,
cases (tr 0), dsimp, assumption
end

lemma valid_trace_delay : ⊩ valid_trace => ◯ valid_trace
:= begin
intros tr validtr, constructor,
simp [delayn], dsimp [LTS_trace],
intros n, apply validtr.next_step
end

lemma valid_trace_always : ⊩ valid_trace => □ valid_trace
:= begin
intros tr validtr, apply temporal_induction, assumption,
intros n, apply valid_trace_delay,
end

lemma global_always (P : tProp (sigma L))
  (H : ⊩ valid_trace => P)
  : ⊩ valid_trace => □ P
:= begin
intros tr validtr n,
apply H, apply valid_trace_always, assumption
end

lemma prove_always {P : sigma L → Prop}
  {Q : S → Prop}
  (H : ∀ s l s', LTS s l s' → P ⟨ s, l ⟩ → Q s')
  : ⊩ valid_trace
    => □ (now P => ◯ (now (inState Q)))
:= begin
apply (global_always _ _),
apply prove_next, assumption
end

lemma invariant_always (P : S → Prop)
  (H : ∀ s l s', P s → LTS s l s' → P s')
  : ⊩ valid_trace => now (inState P) => □ (now (inState P))
:= begin
intros tr validtr H0 n, induction n,
{ apply H0 },
{ apply H, apply ih_1, apply validtr.next_step }
end

lemma sigma_eta (x : sigma L) : sigma.mk x.fst x.snd  = x
:= begin induction x, reflexivity end

lemma invariant_holds_while {P : S → Prop} {Q : sigma L → Prop}
  [decidable_pred Q]
  (H : ∀ s l s', LTS s l s' → ¬ Q ⟨s, l⟩ → P s → P s')
  : ⊩ valid_trace => now (inState P)
    => ◯ (now (inState P)) 𝓦 now Q
:= begin
intros tr validtr HP,
apply weak_until_induction,
assumption,
intros n HQn HPn,
unfold next nextn, rw delayn_combine,
rw add_comm,
simp [inState] with ltl at HQn HPn,
simp [inState] with ltl,
apply (H _ _), apply validtr.next_step,
rw sigma_eta, assumption, assumption,
end

lemma LTS_now_next (P' : S → Prop) (P Q : sigma L → Prop)
  (H : ∀ s l s', LTS s l s' → P ⟨ _, l⟩ → P' s' ∨ Q ⟨ _, l ⟩)
  : ⊩ valid_trace
    => now P
    => ( ◯ (now (inState P')) ∪ now Q)
:= begin
intros tr valid HP,
specialize (H _ _ _ (valid.next_step 0)),
rw sigma_eta at H,
specialize (H HP),
induction H, left, assumption,
right, assumption
end

end LTS

section LTS_refinement

def WithSkip {S : Type u} (L : S → Type v) (s : S) : Type v := option (L s)

parameters {S : Type u} {L : S → Type v}
parameter (LTS : ∀ s : S, L s → S → Prop)

def SkipLTS (s : S) (l : WithSkip L s) (s' : S) : Prop :=
  match l with
  | none := s = s'
  | some l' := LTS s l' s'
  end

def inSkipLabel (P : sigma L → Prop) : sigma (WithSkip L) → Prop
| (sigma.mk s l) := match l with
  | none := false
  | some l' := P (sigma.mk s l')
  end

instance inSkipLabel_decidable (P) [decP : decidable_pred P]
  : decidable_pred (inSkipLabel P)
:= begin
intros x, induction x with s l,
induction l; dsimp [inSkipLabel],
apply decidable.is_false, trivial,
apply decP,
end

def fairness_SkipLTS : tProp (sigma (WithSkip L)) :=
  fair (now (inSkipLabel (λ _, true)))

lemma SkipLTS_next_state
  (P Q : S → Prop)
  (W : sigma L → Prop)
  (HLTS : ∀ s l s', LTS s l s' → P s → W ⟨ _, l ⟩ → Q s')
   : ⊩ valid_trace SkipLTS
   => now (inState P)
   => now (inSkipLabel W)
   => ◯ (now (inState Q))
:= begin
simp with ltl,
intros tr valid nowP goes,
have H := valid.next_step 0,
destruct ((tr 0)), intros s l Hsl,
rw Hsl at goes,
induction l; dsimp [inSkipLabel] at goes,
{ contradiction },
{ apply HLTS, rw Hsl at H, dsimp [SkipLTS] at H,
  apply H, rw Hsl at nowP, assumption, assumption
}
end

lemma SkipLTS_now_next (P' : S → Prop) (P Q : sigma (WithSkip L) → Prop)
  (H : ∀ s l s', LTS s l s' → P ⟨ _, some l⟩ → P' s' ∨ Q ⟨ _, some l ⟩)
  : ⊩ valid_trace SkipLTS
    => now P => now (inSkipLabel (λ _, true))
    => ( ◯ (now (inState P')) ∪ now Q)
:= begin
intros tr valid HP Hgoes,
have valid0 := valid.next_step 0,
unfold now later at Hgoes,
unfold now later at HP,
destruct ((tr 0)); intros,
rw a at Hgoes,
cases snd; dsimp [inSkipLabel] at Hgoes,
contradiction,
rw a at valid0, rw a at HP,
dsimp [SkipLTS] at valid0,
specialize (H _ _ _ valid0 HP),
induction H with H H,
left, assumption, right, unfold now later,
rw a, assumption,
end

lemma SkipLTS_state_stays_constant
  (P : S → Prop) :
  ⊩  valid_trace SkipLTS
  => □ (now (inState P)
  => ((◯ (now (inState P)))
       𝓦
       now (inSkipLabel (λ _, true)))
  )
:= begin
intros tr validtr n Pst,
apply (invariant_holds_while SkipLTS _ (delayn n tr)),
apply valid_trace_always, assumption, assumption,
apply_instance,
intros,
induction l,
{ dsimp [SkipLTS] at a, subst s', assumption },
{ exfalso, apply a_1, constructor, }
end

parameters {S' : Type u'}{L' : S' → Type v'}
parameter (LTS' : ∀ s : S', L' s → S' → Prop)

structure Refinement :=
  (S_refine : S → S')
  (L_refine : ∀ {s}, L s → L' (S_refine s))
  (refines : ∀ s l s', LTS s l s' → LTS' (S_refine s) (L_refine l) (S_refine s'))
end LTS_refinement

namespace Refinement
section
parameters {S : Type u} {S' : Type u'} {L : S → Type v} {L' : S' → Type v'}
  {LTS : ∀ s : S, L s → S → Prop}
  {LTS' : ∀ s : S', L' s → S' → Prop}

def SL_refine' (r : Refinement LTS LTS')
  : sigma L → sigma L'
| (sigma.mk s l) := sigma.mk (r.S_refine s) (r.L_refine l)
def SL_refine (r : Refinement LTS LTS')
  (x : sigma L) : sigma L'
   := sigma.mk (r.S_refine x.fst) (r.L_refine x.snd)

def SL_refine_valid_trace (r : Refinement LTS LTS')
  (tr : trace (sigma L))
  (valid : valid_trace LTS tr)
  : valid_trace LTS' (tr.map r.SL_refine)
:= begin
constructor, unfold LTS_trace,
dsimp [trace.map],
intros n, dsimp [SL_refine], apply refines, apply valid.next_step,
end

def SL_refine_transform
  (r : Refinement LTS LTS')
  (P : tProp (sigma L'))
  (H : ⊩ valid_trace LTS' => P)
  : ⊩ valid_trace LTS => (P ∘ trace.map r.SL_refine)
:= begin
intros tr validtr, dsimp [function.comp],
apply H, apply SL_refine_valid_trace, assumption
end

end
end Refinement


import tactic.localized
import algebra.group_power

open tactic
local infix ` ⊹ `:59 := nat.mul
local infix ` ↓ `:59 := pow
local infix ` ⊖ `:59 := pow
example : 2 ⊹ 3 = 6 := rfl
example : 2 ↓ 3 = 8 := rfl
example : 2 ⊖ 3 = 8 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { success_if_fail { simp [h] }, exact le_of_lt h }
section
localized "infix ` ⊹ `:59 := nat.add" in nat
localized "infix ` ↓ `:59 := nat.mul" in nat
localized "infix ` ⊖ `:59 := nat.mul" in nat.mul
localized "attribute [simp] le_of_lt" in le
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { simp [h] }
end

section
example : 2 ⊹ 3 = 6 := rfl
example : 2 ↓ 3 = 8 := rfl
example : 2 ⊖ 3 = 8 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { success_if_fail { simp [h] }, exact le_of_lt h }

-- test that `open_locale` will fail when given a nonexistent locale
run_cmd success_if_fail $ get_localized [`ceci_nest_pas_une_locale]

open_locale nat
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 8 := rfl

open_locale nat.mul
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
end

section
open_locale nat.mul nat nat.mul le
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { simp [h] }
end


import soundness.soundnessCLK
import completeness.canonicalCL
import syntax.axiomsCLK
import tactic.induction
-- import data.finset.basic

local attribute [instance] classical.prop_decidable

open set list formCLK

namespace canonical

def canonical_model_CLK {agents : Type} [hN : fintype agents] (ha : nonempty agents) :
  modelCLK agents :=
{ f := canonical_CLK ha (formCLK agents) (nprfalseCLK ha),
  -- V is as usual, such that s ∈ V (p) iff p ∈ s
  v := λ  n, {s | (formCLK.var n) ∈ s.1} }

----------------------------------------------------------
-- Filtration
----------------------------------------------------------

def cl {agents : Type} [hN : fintype agents] (ha : nonempty agents) :
  formCLK agents → set (formCLK agents)
  |  bot          := {bot, ¬ bot}
  | (var n)       := {var n, ¬ var n}
  | (imp φ ψ)     := cl φ ∪ cl ψ ∪
                     match ψ with
                     | bot := {(imp φ ψ)}
                     | _   := {(imp φ ψ), ¬ (imp φ ψ)}
                     end
  | (and φ ψ)     := cl φ ∪ cl ψ ∪ {(and φ ψ), ¬ (and φ ψ)}
  | ([G] φ)       := cl φ ∪ {([G] φ), ¬ [G] φ}
  | _ := sorry

----------------------------------------------------------
-- Truth Lemma
----------------------------------------------------------
lemma truth_lemma_CL {agents : Type} (ha : nonempty agents) [hN : fintype agents] (φ : formCLK agents)
(s : (canonical_model_CLK ha).f.states) : (s_entails_CLK (canonical_model_CLK ha) s φ) ↔ (φ ∈ s.1) :=
begin
  -- This proof is by induction on φ.
  induction' φ with n φ ψ _ _ φ ψ _ _,

  { -- case bot
    simp [s_entails_CLK],
    exact @bot_not_mem_of_ax_consistent (formCLK agents) formulaCLK s.1 s.2.1, },

  { -- case var
    simpa, },

  { -- case and
    simp [s_entails_CLK, ih_φ, ih_ψ],
    split,

    { intro h,
      exact max_ax_contains_by_set_proof_2h s.2 h.left h.right axCLK.Prop4, },

    { intro h,
      split,
      exact max_ax_contains_by_set_proof s.2 h axCLK.Prop5,
      exact max_ax_contains_by_set_proof s.2 h axCLK.Prop6, }, },

  { -- case imp
    simp [s_entails_CLK, ih_φ, ih_ψ],
    split,

    { intro h,
      exact max_ax_contains_imp_by_proof s.2 h, },

    { intros h hφ,
      exact max_ax_contains_by_set_proof_2h s.2 hφ h likemp, }, },

  { -- case E
    let states := {Γ : (set (formCLK agents)) // (max_ax_consistent Γ)},
    have hE : (canonical_model_CLK ha).f.E.E = λ s, λ G : set agents, {X | ite (G = univ)
      -- condition G = N
      (∀ φ, ({t : (canonical_model_CLK ha).f.states | φ ∈ (t.val)} ⊆ Xᶜ) → ([(∅)] φ) ∉ s.val)
      -- condition G ≠ N
      (∃ φ, {t : (canonical_model_CLK ha).f.states | φ ∈ (t.val)} ⊆ X ∧ ( [G] φ) ∈ s.val)},
      from rfl,

    specialize ih ha,

    -- It is sufficient to consider the case when G ⊂ N, because ⊢ [N]φ ↔ ¬[∅]¬φ
    cases set.eq_or_ssubset_of_subset (set.subset_univ G) with hG hG,
    -- Case G = N

    { -- ⊢ [N]φ ↔ ¬[∅]¬φ
      have hempty : axCLK (([univ]φ) ↔ ¬([∅](¬φ))), from
        @univ_iff_empty agents (formCLK agents) _ _ _,
      simp [hG] at *, clear hG,

      split,

      { -- M s ⊨ [N] φ ⇒ [N] φ ∈ s
        intro h,
        simp[s_entails_CLK, hE] at h,
        have hnin : ([∅] (¬φ)) ∉ s.val, from
        begin
          apply h (¬ φ),
          apply @eq.subset _ _ {t | s_entails_CLK (canonical_model_CLK ha) t φ}ᶜ,
          simp[ih],
          exact complement_from_contra,
        end,
        simp at hnin,

        have hin :  (¬[∅]¬φ) ∈ s.val, from not_in_from_notin s.2 hnin,
        simp at hin,

        exact max_ax_contains_by_set_proof s.2 hin (axCLK.MP (axCLK.Prop6) hempty), },

      { -- [N] φ ∈ s ⇒ M s ⊨ [N] φ
        intro h,
        simp[s_entails_CLK, hE, ih],
        intros ψ hsubseteq hf,

        simp[set.subset_def] at hsubseteq,

        have himp : ∀ (x : (canonical_model_CLK ha).f.states), ψ ∈ x.1 → (¬ φ) ∈ x.1, from
          λ t ht, not_in_from_notin t.2 (hsubseteq t ht),

        have hin : (¬ [∅] ¬φ) ∈ s.val,
          from max_ax_contains_by_set_proof s.2 h (axCLK.MP (axCLK.Prop5) hempty),

        have hnin : ([∅] ¬φ) ∉ s.val, from
          λ hf, contra_containts_pr_false s.2 hf hin,

        have hax : axCLK (ψ ~> (¬ φ)), from
          ax_imp_from_ex himp,

        have hin' : ([∅] ¬ φ) ∈ s.val,
        { apply max_ax_contains_by_set_proof s.2 hf,
          apply @derived_monoticity_rule agents (formCLK agents),
          exact hax, },

        exact hnin hin', }, },

    { -- Case G ⊂ N
      split,
      -- M, s ⊨ [G]φ ⇒ [G]φ ∈ s, when G ⊂ N

      { -- Assume M, s ⊨ [G]φ
        intro h,
        -- {s ∈ S| M, s ⊨ φ} ∈ E(s)(G), from h, by definition ⊨
        simp[s_entails_CLK] at h,
        -- ∃ψ˜ ⊆ {t ∈ S| M, t ⊨ φ} : [G]ψ ∈ s, from above, by definition E
        have huniv : G ≠ univ, from (set.ssubset_iff_subset_ne.mp hG).right,
        simp[hE, huniv] at h, clear huniv,
        -- ∃ψ˜ ⊆ {t ∈ S| M, φ ∈ t} : [G]ψ ∈ s, from above, by IH
        cases h with ψ hψ,
        have hψih : ∀ (a : (canonical_model_CLK ha).f.states), ψ ∈ ↑a → φ ∈ a.val, from
          begin
            intros t ht,
            apply (ih t).mp,
            apply hψ.left,
            exact ht,
          end,
        -- ∃ψ˜ ⊆ φ˜ : [G]ψ ∈ s, from hih, by definition ψ˜
        have hGψ : ([G]ψ) ∈ s.val, from hψ.right,
        -- ⊢ ψ → φ, since ψ˜ ⊆ φ˜ in hψih
        have himp : axCLK (ψ ~> φ), from ax_imp_from_ex hψih,
        -- ⊢ [G]ψ → [G]φ, from himp, by the derived monoticity rule
        have hGimp : axCLK (([G] ψ) ~> ([G] φ)), from
          @derived_monoticity_rule agents (formCLK agents) formulaCLK CLformulaCLK _ _ _ himp,
        -- [G]φ ∈ s, from hGimp and hGψ
        exact max_ax_contains_by_set_proof s.2 hGψ hGimp, },
      -- [G]φ ∈ s ⇒ M, s ⊨ [G]φ, when G ⊂ N

      { -- Assume [G]φ ∈ s
        intro h,
        -- ˜φ ⊆ {t ∈ S| φ ∈ t} : [G]φ ∈ s, from 4.1
        simp[s_entails_CLK],
        -- {t ∈ S| φ ∈ t} ∈ E (s)(G), from 4.2, by definition E(s)(G).
        simp[hE, (set.ssubset_iff_subset_ne.mp hG).right],
        apply exists.intro φ,
        -- {t ∈ S | M, t ⊨ φ} ∈ E(s)(G), from 4.3, by IH
        split,

        { intros t ht,
          simp[ih t],
          exact ht, },

        { exact h, }, }, }, },
  -- case K
  { have hK : (canonical_model_CLK ha).f.rel = λ i s, {t | {φ | (K' i φ) ∈ s.1} = {φ | (K' i φ) ∈ t.1}},
      from rfl,
    split,
    -- ⇒
    { intro h,
      simp at *,
      simp [s_entails_CLK] at h,
      simp [hK] at *,
      have hφ : φ ∈ s.1,
      { simp [←(ih a s)],
        apply h,
        simp, },
      have hkj : ∀ t : (canonical_model_CLK ha).f.to_frameCL.states,
        {φ : formCLK agents | K' a φ ∈ ↑s} = {φ : formCLK agents | K' a φ ∈ ↑t} → φ ∈ t.1,
      {
        intros t ht,
        simp [←(ih a t)],
        apply h,
        exact ht,
      },
      dsimp at *,
      -- have (K' i φ) ∈ s,

      -- simp [ih] at h,
      sorry,
      -- simp [(ih i)] at h,

    },
    { intro h,
      simp[s_entails_CLK, ih, hK],
      intros t ht,
      have hKt: K' a φ ∈ t.val, from
      begin
        simp[set.ext_iff] at ht,
        specialize ht φ,
        simp[←ht],
        exact h,
      end,
      exact max_ax_contains_by_set_proof t.2 hKt axCLK.T, }, },
end

----------------------------------------------------------
-- Completeness
----------------------------------------------------------

-- Completeness
----------------------------------------------------------
-- theorem completenessCLK (φ : formCLK agents) [hN : fintype agents] (ha : nonempty agents) :
--   global_valid φ → axCLK φ :=
-- begin
--   -- rw from contrapositive
--   rw ←not_imp_not,
--   -- assume ¬ ⊢ φ
--   intro hnax,
--   -- from ¬ ⊢ φ, have that {¬ φ} is a consistent set
--   have hax := @comphelper agents (formCLK agents) formulaCLK φ (nprfalseCLK ha) hnax,
--   -- with Lindenbaum, extend {¬ φ} into a maximally consistent set
--   have hmax := lindenbaum {¬φ} hax,
--   simp at *,
--   cases hmax with s hmax,
--   cases hmax with hmax hnφ,
--   -- show that φ is not globally valid,
--   -- by showing that there exists some model where φ is not valid.
--   simp[global_valid],
--   -- let that model be the canonical model
--   apply exists.intro (canonical_model_CLK ha),
--   -- in the canonical model (M) there exists some state (s) where ¬ M s ⊨ φ
--   simp[valid_m],
--   -- let that state (s) be the maximally consistent set extended from {¬ φ}
--   apply exists.intro (subtype.mk s hmax),
--   -- assume by contradiction that M s ⊨ φ
--   intro hf,
--   -- by the truth lemma φ ∈ s
--   have hφ, from (truth_lemma_CL ha φ (subtype.mk s hmax)).mp hf,
--   -- in that state (s), φ ∈ s, so we do not have ¬ φ ∈ s (by consistency)
--   -- contradiction with hnφ
--   apply contra_containts_pr_false hmax hφ hnφ,
-- end

end canonical


import algebra.camera.option

universe u

inductive sum_camera (α β : Type u)
| cinl : α → sum_camera
| cinr : β → sum_camera
| invalid : sum_camera

namespace sum_camera

infixr ` ⊕ₖ `:30 := sum_camera

lemma cinl_injective {α β : Type u} : function.injective (cinl : α → sum_camera α β) :=
by intros x y h; cases h; refl

lemma cinr_injective {α β : Type u} : function.injective (cinr : β → sum_camera α β) :=
by intros x y h; cases h; refl

inductive eq_at_prop {α β : Type u} [ofe α] [ofe β] (n : ℕ) : α ⊕ₖ β → α ⊕ₖ β → Prop
| cinl : Π {a b : α}, a =[n] b → eq_at_prop (cinl a) (cinl b)
| cinr : Π {a b : β}, a =[n] b → eq_at_prop (cinr a) (cinr b)
| invalid : eq_at_prop invalid invalid

instance ofe {α β : Type u} [ofe α] [ofe β] : ofe (α ⊕ₖ β) := {
  eq_at := eq_at_prop,
  eq_at_reflexive := begin
    intros n a,
    cases a,
    { refine eq_at_prop.cinl _, refl, },
    { refine eq_at_prop.cinr _, refl, },
    { exact eq_at_prop.invalid, },
  end,
  eq_at_symmetric := begin
    intros n a b h,
    cases h,
    { refine eq_at_prop.cinl _, symmetry, assumption, },
    { refine eq_at_prop.cinr _, symmetry, assumption, },
    { exact eq_at_prop.invalid, },
  end,
  eq_at_transitive := begin
    intros n a b c hab hbc,
    cases hab with a₁ b₁ hab₁ a₂ b₂ hab₂,
    { cases c; cases hbc with hbc,
      refine eq_at_prop.cinl _, transitivity b₁; assumption, },
    { cases c; cases hbc with hbc,
      refine eq_at_prop.cinr _, transitivity b₂; assumption, },
    { cases c; cases hbc with hbc,
      exact eq_at_prop.invalid, },
  end,
  eq_at_mono' := begin
    intros m n hmn a b h,
    cases h,
    { refine eq_at_prop.cinl _, refine eq_at_mono hmn _, assumption, },
    { refine eq_at_prop.cinr _, refine eq_at_mono hmn _, assumption, },
    { exact eq_at_prop.invalid, },
  end,
  eq_at_limit' := begin
    intros a b h,
    cases h 0,
    { refine congr_arg _ _, rw eq_at_limit,
      intro n, cases h n, assumption, },
    { refine congr_arg _ _, rw eq_at_limit,
      intro n, cases h n, assumption, },
    { refl, },
  end,
}

lemma cinl_is_nonexpansive {α β : Type u} [ofe α] [ofe β] :
  is_nonexpansive (cinl : α → α ⊕ₖ β) :=
λ m a b, eq_at_prop.cinl

lemma cinr_is_nonexpansive {α β : Type u} [ofe α] [ofe β] :
  is_nonexpansive (cinr : β → α ⊕ₖ β) :=
λ m a b, eq_at_prop.cinr

@[simp] lemma cinl_eq_at_cinl {α β : Type u} [ofe α] [ofe β] {n : ℕ} {a b : α} :
  (cinl a : α ⊕ₖ β) =[n] cinl b ↔ a =[n] b :=
begin
  split,
  { rintro (h | h | h),
    assumption, },
  { exact eq_at_prop.cinl, },
end

@[simp] lemma cinr_eq_at_cinr {α β : Type u} [ofe α] [ofe β] {n : ℕ} {a b : β} :
  (cinr a : α ⊕ₖ β) =[n] cinr b ↔ a =[n] b :=
begin
  split,
  { rintro (h | h | h),
    assumption, },
  { exact eq_at_prop.cinr, },
end

lemma cinl_eq_at {α β : Type u} [ofe α] [ofe β] {n : ℕ} {a : α} {b : α ⊕ₖ β} :
  cinl a =[n] b → ∃ b', b = cinl b' :=
begin
  rintro (h | h | h),
  exact ⟨_, rfl⟩,
end

lemma cinr_eq_at {α β : Type u} [ofe α] [ofe β] {n : ℕ} {a : β} {b : α ⊕ₖ β} :
  cinr a =[n] b → ∃ b', b = cinr b' :=
begin
  rintro (h | h | h),
  exact ⟨_, rfl⟩,
end

lemma invalid_eq_at {α β : Type u} [ofe α] [ofe β] {n : ℕ} {b : α ⊕ₖ β} :
  invalid =[n] b → b = invalid :=
begin
  rintro (h | h | h),
  refl,
end

def mul {α β : Type u} [comm_semigroup α] [comm_semigroup β] : α ⊕ₖ β → α ⊕ₖ β → α ⊕ₖ β
| (cinl a) (cinl b) := cinl (a * b)
| (cinr a) (cinr b) := cinr (a * b)
| _ _ := invalid

instance comm_semigroup {α β : Type u} [comm_semigroup α] [comm_semigroup β] :
  comm_semigroup (α ⊕ₖ β) := {
  mul := mul,
  mul_assoc := begin
    intros a b c,
    cases a; cases b; cases c;
    try { refl, },
    refine congr_arg cinl _, rw mul_assoc,
    refine congr_arg cinr _, rw mul_assoc,
  end,
  mul_comm := begin
    intros a b,
    cases a; cases b;
    try { refl, },
    refine congr_arg cinl _, rw mul_comm,
    refine congr_arg cinr _, rw mul_comm,
  end,
}

lemma cinl_mul_cinl {α β : Type u} [comm_semigroup α] [comm_semigroup β] {a b : α} :
  (cinl a : α ⊕ₖ β) * cinl b = cinl (a * b) := rfl

lemma cinr_mul_cinr {α β : Type u} [comm_semigroup α] [comm_semigroup β] {a b : β} :
  (cinr a : α ⊕ₖ β) * cinr b = cinr (a * b) := rfl

lemma cinl_eq_mul {α β : Type u} [comm_semigroup α] [comm_semigroup β] {a : α} {b c : α ⊕ₖ β} :
  cinl a = b * c → ∃ b' c', b = cinl b' ∧ c = cinl c' :=
begin
  cases b; cases c,
  repeat { intro h, cases h, },
  exact ⟨b, c, rfl, rfl⟩,
end

lemma cinr_eq_mul {α β : Type u} [comm_semigroup α] [comm_semigroup β] {a : β} {b c : α ⊕ₖ β} :
  cinr a = b * c → ∃ b' c', b = cinr b' ∧ c = cinr c' :=
begin
  cases b; cases c,
  repeat { intro h, cases h, },
  exact ⟨b, c, rfl, rfl⟩,
end

def validn {α β : Type u} [camera α] [camera β] : α ⊕ₖ β → sprop
| (cinl a) := camera.validn a
| (cinr a) := camera.validn a
| invalid := ⊥

def core {α β : Type u} [camera α] [camera β] : α ⊕ₖ β → option (α ⊕ₖ β)
| (cinl a) := (camera.core a).map cinl
| (cinr a) := (camera.core a).map cinr
| invalid := some invalid

def extend {α β : Type u} [camera α] [camera β] {n : ℕ} : Π {a b₁ b₂ : α ⊕ₖ β},
  validn a n → a =[n] b₁ * b₂ → (α ⊕ₖ β) × (α ⊕ₖ β)
| (cinl a) (cinl b₁) (cinl b₂) va hab :=
  (cinl (camera.extend va (by cases hab; assumption)).1,
   cinl (camera.extend va (by cases hab; assumption)).2)
| (cinr a) (cinr b₁) (cinr b₂) va hab :=
  (cinr (camera.extend va (by cases hab; assumption)).1,
   cinr (camera.extend va (by cases hab; assumption)).2)
| (cinl a) (cinl b₁) (cinr b₂) va hab := by exfalso; cases hab
| (cinl a) (cinl b₁) invalid va hab := by exfalso; cases hab
| (cinl a) (cinr b₁) b₂ va hab := by exfalso; cases b₂; cases hab
| (cinl a) invalid b₂ va hab := by exfalso; cases hab
| (cinr a) (cinr b₁) (cinl b₂) va hab := by exfalso; cases hab
| (cinr a) (cinr b₁) invalid va hab := by exfalso; cases hab
| (cinr a) (cinl b₁) b₂ va hab := by exfalso; cases b₂; cases hab
| (cinr a) invalid b₂ va hab := by exfalso; cases hab
| invalid _ b₂ va hab := by exfalso; cases va

private lemma mul_is_nonexpansive {α β : Type u} [camera α] [camera β] :
  is_nonexpansive (function.uncurry ((*) : α ⊕ₖ β → α ⊕ₖ β → α ⊕ₖ β)) :=
begin
  rintros n ⟨a, b⟩ ⟨c, d⟩ ⟨h₁, h₂⟩,
  cases h₁,
  case invalid { refl, },
  { cases h₂,
    case cinr { refl, },
    case invalid { refl, },
    refine cinl_is_nonexpansive _,
    refine camera.mul_eq_at _ _; assumption, },
  { cases h₂,
    case cinl { refl, },
    case invalid { refl, },
    refine cinr_is_nonexpansive _,
    refine camera.mul_eq_at _ _; assumption, },
end

private lemma cinl_core_eq_some {α β : Type u} [camera α] [camera β] {a : α} {ca : α ⊕ₖ β} :
  (cinl a : α ⊕ₖ β).core = some ca → ∃ ca', ca = cinl ca' ∧ camera.core a = some ca' :=
begin
  intro h,
  cases ca,
  { refine ⟨ca, rfl, _⟩,
    simpa only [core, option.map_eq_some', exists_eq_right] using h, },
  { simpa only [core, option.map_eq_some', and_false, exists_false] using h, },
  { simpa only [core, option.map_eq_some', and_false, exists_false] using h, },
end

private lemma cinr_core_eq_some {α β : Type u} [camera α] [camera β] {a : β} {ca : α ⊕ₖ β} :
  (cinr a : α ⊕ₖ β).core = some ca → ∃ ca', ca = cinr ca' ∧ camera.core a = some ca' :=
begin
  intro h,
  cases ca,
  { simpa only [core, option.map_eq_some', and_false, exists_false] using h, },
  { refine ⟨ca, rfl, _⟩,
    simpa only [core, option.map_eq_some', exists_eq_right] using h, },
  { simpa only [core, option.map_eq_some', and_false, exists_false] using h, },
end

private lemma core_mul_self {α β : Type u} [camera α] [camera β]
  (a : α ⊕ₖ β) {ca : α ⊕ₖ β} : core a = some ca → ca * a = a :=
begin
  intro h,
  cases a,
  { obtain ⟨ca, rfl, hca⟩ := cinl_core_eq_some h,
    exact congr_arg cinl (camera.core_mul_self _ hca), },
  { obtain ⟨ca, rfl, hca⟩ := cinr_core_eq_some h,
    exact congr_arg cinr (camera.core_mul_self _ hca), },
  { rw mul_comm, refl, },
end

private lemma core_core {α β : Type u} [camera α] [camera β]
  (a : α ⊕ₖ β) {ca : α ⊕ₖ β} : core a = some ca → core ca = some ca :=
begin
  intro h,
  cases a,
  { obtain ⟨ca, rfl, hca⟩ := cinl_core_eq_some h,
    simp only [core, option.map_eq_some', exists_eq_right],
    exact camera.core_core _ hca, },
  { obtain ⟨ca, rfl, hca⟩ := cinr_core_eq_some h,
    simp only [core, option.map_eq_some', exists_eq_right],
    exact camera.core_core _ hca, },
  { cases h, exact h, },
end

private lemma core_mono_some {α β : Type u} [camera α] [camera β]
  (a b : α ⊕ₖ β) {ca : α ⊕ₖ β} : core a = some ca → a ≼ b →
    ∃ cb : α ⊕ₖ β, core b = some cb :=
begin
  rintros h ⟨c, hc⟩,
  cases a,
  { obtain ⟨ca, rfl, hca⟩ := cinl_core_eq_some h,
    cases c; cases hc,
    { obtain ⟨d, hd⟩ := camera.core_mono_some a (a * c) hca ⟨c, rfl⟩,
      refine ⟨cinl d, _⟩,
      simp only [core, option.map_eq_some', exists_eq_right],
      exact hd, },
    { exact ⟨invalid, rfl⟩, },
    { exact ⟨invalid, rfl⟩, }, },
  { obtain ⟨ca, rfl, hca⟩ := cinr_core_eq_some h,
    cases c; cases hc,
    { exact ⟨invalid, rfl⟩, },
    { obtain ⟨d, hd⟩ := camera.core_mono_some a (a * c) hca ⟨c, rfl⟩,
      refine ⟨cinr d, _⟩,
      simp only [core, option.map_eq_some', exists_eq_right],
      exact hd, },
    { exact ⟨invalid, rfl⟩, }, },
  { cases hc,
    exact ⟨invalid, rfl⟩, },
end

private lemma core_mono {α β : Type u} [camera α] [camera β]
  (a b : α ⊕ₖ β) {ca : α ⊕ₖ β} : core a = some ca → a ≼ b → core a ≼ core b :=
begin
  rintros h ⟨c, hc⟩,
  cases a,
  { obtain ⟨ca, rfl, hca⟩ := cinl_core_eq_some h,
    cases c; cases hc,
    { obtain ⟨d, hd⟩ := camera.core_mono a (a * c) hca ⟨c, rfl⟩,
      refine ⟨d.map cinl, _⟩,
      simp only [core, option.map_eq_some', exists_eq_right, ← hd],
      cases camera.core a; cases d;
      simp only [option.map_none', option.map_some', none_mul, mul_none, some_mul_some],
      refl, },
    { refine ⟨some invalid, _⟩,
      cases (cinl a).core, refl, cases val; refl, },
    { refine ⟨some invalid, _⟩,
      cases (cinl a).core, refl, cases val; refl, }, },
  { obtain ⟨ca, rfl, hca⟩ := cinr_core_eq_some h,
    cases c; cases hc,
    { refine ⟨some invalid, _⟩,
      cases (cinr a).core, refl, cases val; refl, },
    { obtain ⟨d, hd⟩ := camera.core_mono a (a * c) hca ⟨c, rfl⟩,
      refine ⟨d.map cinr, _⟩,
      simp only [core, option.map_eq_some', exists_eq_right, ← hd],
      cases camera.core a; cases d;
      simp only [option.map_none', option.map_some', none_mul, mul_none, some_mul_some],
      refl, },
    { refine ⟨some invalid, _⟩,
      cases (cinr a).core, refl, cases val; refl, }, },
  { cases hc, exact ⟨some invalid, rfl⟩, },
end

private lemma validn_mul {α β : Type u} [camera α] [camera β] (a b : α ⊕ₖ β) :
  validn (a * b) ≤ validn a :=
begin
  intros n h,
  cases a,
  { cases b,
    exact camera.validn_mul a b n h,
    cases h,
    cases h, },
  { cases b,
    cases h,
    exact camera.validn_mul a b n h,
    cases h, },
  { cases h, },
end

private lemma extend_mul_eq {α β : Type u} [camera α] [camera β] (n : ℕ)
  (a b₁ b₂ : α ⊕ₖ β) (ha : validn a n) (hb : a =[n] b₁ * b₂) :
  a = (extend ha hb).1 * (extend ha hb).2 :=
begin
  cases a,
  { obtain ⟨b, hb'⟩ := cinl_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinl_eq_mul hb'.symm,
    rw [cinl_mul_cinl, cinl_eq_at_cinl] at hb,
    exact congr_arg cinl (camera.extend_mul_eq ha hb), },
  { obtain ⟨b, hb'⟩ := cinr_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinr_eq_mul hb'.symm,
    rw [cinr_mul_cinr, cinr_eq_at_cinr] at hb,
    exact congr_arg cinr (camera.extend_mul_eq ha hb), },
  { cases ha, },
end

private lemma extend_eq_at_left {α β : Type u} [camera α] [camera β] (n : ℕ)
  (a b₁ b₂ : α ⊕ₖ β) (ha : validn a n) (hb : a =[n] b₁ * b₂) :
  (extend ha hb).1 =[n] b₁ :=
begin
  cases a,
  { obtain ⟨b, hb'⟩ := cinl_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinl_eq_mul hb'.symm,
    rw [cinl_mul_cinl, cinl_eq_at_cinl] at hb,
    exact cinl_is_nonexpansive (camera.extend_eq_at_left ha hb), },
  { obtain ⟨b, hb'⟩ := cinr_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinr_eq_mul hb'.symm,
    rw [cinr_mul_cinr, cinr_eq_at_cinr] at hb,
    exact cinr_is_nonexpansive (camera.extend_eq_at_left ha hb), },
  { cases ha, },
end

private lemma extend_eq_at_right {α β : Type u} [camera α] [camera β] (n : ℕ)
  (a b₁ b₂ : α ⊕ₖ β) (ha : validn a n) (hb : a =[n] b₁ * b₂) :
  (extend ha hb).2 =[n] b₂ :=
begin
  cases a,
  { obtain ⟨b, hb'⟩ := cinl_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinl_eq_mul hb'.symm,
    rw [cinl_mul_cinl, cinl_eq_at_cinl] at hb,
    exact cinl_is_nonexpansive (camera.extend_eq_at_right ha hb), },
  { obtain ⟨b, hb'⟩ := cinr_eq_at hb,
    obtain ⟨b₁, b₂, rfl, rfl⟩ := cinr_eq_mul hb'.symm,
    rw [cinr_mul_cinr, cinr_eq_at_cinr] at hb,
    exact cinr_is_nonexpansive (camera.extend_eq_at_right ha hb), },
  { cases ha, },
end

instance camera {α β : Type u} [camera α] [camera β] : camera (α ⊕ₖ β) := {
  validn := ⟨validn, begin
    intros n a b h,
    cases h,
    refine nonexpansive camera.validn _, assumption,
    refine nonexpansive camera.validn _, assumption,
    intros m hmn, refl,
  end⟩,
  core := ⟨core, begin
    intros n a b h,
    cases h,
    { refine option.map_nonexpansive _ cinl_is_nonexpansive _,
      refine nonexpansive camera.core _, assumption, },
    { refine option.map_nonexpansive _ cinr_is_nonexpansive _,
      refine nonexpansive camera.core _, assumption, },
    { refl, },
  end⟩,
  extend := @extend α β _ _,
  mul_is_nonexpansive := mul_is_nonexpansive,
  core_mul_self := core_mul_self,
  core_core := core_core,
  core_mono_some := core_mono_some,
  core_mono := core_mono,
  validn_mul := validn_mul,
  extend_mul_eq := extend_mul_eq,
  extend_eq_at_left := extend_eq_at_left,
  extend_eq_at_right := extend_eq_at_right,
  ..sum_camera.ofe,
  ..sum_camera.comm_semigroup,
}

lemma mul_cinl_valid {α β : Type u} [camera α] [camera β] {a : α} {b : α ⊕ₖ β} {n : ℕ} :
  ✓[n] b * cinl a → ∃ b', b = cinl b' :=
begin
  intro h,
  cases b,
  exact ⟨b, rfl⟩,
  cases h,
  cases h,
end

lemma mul_cinr_valid {α β : Type u} [camera α] [camera β] {a : β} {b : α ⊕ₖ β} {n : ℕ} :
  ✓[n] b * cinr a → ∃ b', b = cinr b' :=
begin
  intro h,
  cases b,
  cases h,
  exact ⟨b, rfl⟩,
  cases h,
end

lemma can_update_cinl {α β : Type u} [camera α] [camera β] {a : α} {A : set α} :
  a ↝ A → (cinl a : α ⊕ₖ β) ↝ cinl '' A :=
begin
  rintros h n ⟨c, hc⟩,
  obtain ⟨c, rfl⟩ := mul_cinl_valid hc,
  obtain ⟨f, hf⟩ := h n ⟨c, hc⟩,
  exact ⟨⟨cinl f.val, f.prop⟩, f.val, hf, rfl⟩,
end

lemma can_update_cinr {α β : Type u} [camera α] [camera β] {b : β} {B : set β} :
  b ↝ B → (cinr b : α ⊕ₖ β) ↝ cinr '' B :=
begin
  rintros h n ⟨c, hc⟩,
  obtain ⟨c, rfl⟩ := mul_cinr_valid hc,
  obtain ⟨f, hf⟩ := h n ⟨c, hc⟩,
  exact ⟨⟨cinr f.val, f.prop⟩, f.val, hf, rfl⟩,
end

/-- If `cinl a` has no frame, we can update it to any valid `cinr b`.
TODO: Why does the Iris appendix add the additional `✓ b` assumption? -/
lemma can_update_swap {α β : Type u} [camera α] [camera β] (a : α) (b : β) :
  (∀ n, is_empty (time_frame a n)) → ✓ b → (cinl a : α ⊕ₖ β) ↝ {cinr b} :=
begin
  rintros ha hb n ⟨c, hc⟩,
  obtain ⟨c, rfl⟩ := mul_cinl_valid hc,
  cases (ha n).false ⟨c, hc⟩,
end

end sum_camera