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

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

/-!
# Finite intervals of finitely supported functions

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file provides the `locally_finite_order` instance for `ι →₀ α` when `α` itself is locally
finite and calculates the cardinality of its finite intervals.

## Main declarations

* `finsupp.range_singleton`: Postcomposition with `has_singleton.singleton` on `finset` as a
  `finsupp`.
* `finsupp.range_Icc`: Postcomposition with `finset.Icc` as a `finsupp`.

Both these definitions use the fact that `0 = {0}` to ensure that the resulting function is finitely
supported.
-/

noncomputable section

open Finset Finsupp Function

open BigOperators Classical Pointwise

variable {ι α : Type _}

namespace Finsupp

section RangeSingleton

variable [Zero α] {f : ι →₀ α} {i : ι} {a : α}

#print Finsupp.rangeSingleton /-
/-- Pointwise `finset.singleton` bundled as a `finsupp`. -/
@[simps]
def rangeSingleton (f : ι →₀ α) : ι →₀ Finset α
    where
  toFun i := {f i}
  support := f.support
  mem_support_toFun i := by
    rw [← not_iff_not, not_mem_support_iff, not_ne_iff]
    exact singleton_injective.eq_iff.symm
#align finsupp.range_singleton Finsupp.rangeSingleton
-/

#print Finsupp.mem_rangeSingleton_apply_iff /-
theorem mem_rangeSingleton_apply_iff : a ∈ f.rangeSingleton i ↔ a = f i :=
  mem_singleton
#align finsupp.mem_range_singleton_apply_iff Finsupp.mem_rangeSingleton_apply_iff
-/

end RangeSingleton

section RangeIcc

variable [Zero α] [PartialOrder α] [LocallyFiniteOrder α] {f g : ι →₀ α} {i : ι} {a : α}

#print Finsupp.rangeIcc /-
/-- Pointwise `finset.Icc` bundled as a `finsupp`. -/
@[simps toFun]
def rangeIcc (f g : ι →₀ α) : ι →₀ Finset α
    where
  toFun i := Icc (f i) (g i)
  support :=
    haveI := Classical.decEq ι
    f.support ∪ g.support
  mem_support_toFun i :=
    by
    rw [mem_union, ← not_iff_not, not_or, not_mem_support_iff, not_mem_support_iff, not_ne_iff]
    exact Icc_eq_singleton_iff.symm
#align finsupp.range_Icc Finsupp.rangeIcc
-/

/- warning: finsupp.range_Icc_support -> Finsupp.rangeIcc_support is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : Zero.{u2} α] [_inst_2 : PartialOrder.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_2)] [_inst_4 : DecidableEq.{succ u1} ι] (f : Finsupp.{u1, u2} ι α _inst_1) (g : Finsupp.{u1, u2} ι α _inst_1), Eq.{succ u1} (Finset.{u1} ι) (Finsupp.support.{u1, u2} ι (Finset.{u2} α) (Finset.zero.{u2} α _inst_1) (Finsupp.rangeIcc.{u1, u2} ι α _inst_1 _inst_2 _inst_3 f g)) (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_1 f) (Finsupp.support.{u1, u2} ι α _inst_1 g))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : Zero.{u1} α] [_inst_2 : PartialOrder.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_2)] (_inst_4 : Finsupp.{u2, u1} ι α _inst_1) (f : Finsupp.{u2, u1} ι α _inst_1), Eq.{succ u2} (Finset.{u2} ι) (Finsupp.support.{u2, u1} ι (Finset.{u1} α) (Finset.zero.{u1} α _inst_1) (Finsupp.rangeIcc.{u2, u1} ι α _inst_1 _inst_2 _inst_3 _inst_4 f)) (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_1 _inst_4) (Finsupp.support.{u2, u1} ι α _inst_1 f))
Case conversion may be inaccurate. Consider using '#align finsupp.range_Icc_support Finsupp.rangeIcc_supportₓ'. -/
@[simp]
theorem rangeIcc_support [DecidableEq ι] (f g : ι →₀ α) :
    (rangeIcc f g).support = f.support ∪ g.support := by convert rfl
#align finsupp.range_Icc_support Finsupp.rangeIcc_support

#print Finsupp.mem_rangeIcc_apply_iff /-
theorem mem_rangeIcc_apply_iff : a ∈ f.rangeIcc g i ↔ f i ≤ a ∧ a ≤ g i :=
  mem_Icc
#align finsupp.mem_range_Icc_apply_iff Finsupp.mem_rangeIcc_apply_iff
-/

end RangeIcc

section PartialOrder

variable [PartialOrder α] [Zero α] [LocallyFiniteOrder α] (f g : ι →₀ α)

instance : LocallyFiniteOrder (ι →₀ α) := by
  haveI := Classical.decEq ι <;> haveI := Classical.decEq α <;>
    exact
      LocallyFiniteOrder.ofIcc (ι →₀ α) (fun f g => (f.support ∪ g.support).Finsupp <| f.rangeIcc g)
        fun f g x =>
        by
        refine'
          (mem_finsupp_iff_of_support_subset <| Finset.subset_of_eq <| range_Icc_support _ _).trans
            _
        simp_rw [mem_range_Icc_apply_iff]
        exact forall_and

/- warning: finsupp.Icc_eq -> Finsupp.icc_eq is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Zero.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] (f : Finsupp.{u1, u2} ι α _inst_2) (g : Finsupp.{u1, u2} ι α _inst_2) [_inst_4 : DecidableEq.{succ u1} ι], Eq.{succ (max u1 u2)} (Finset.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2)) (Finset.Icc.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finsupp.preorder.{u1, u2} ι α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)) (Finsupp.locallyFiniteOrder.{u1, u2} ι α _inst_1 _inst_2 _inst_3) f g) (Finset.finsupp.{u1, u2} ι α _inst_2 (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_2 f) (Finsupp.support.{u1, u2} ι α _inst_2 g)) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι (Finset.{u2} α) (Finset.zero.{u2} α _inst_2)) (fun (_x : Finsupp.{u1, u2} ι (Finset.{u2} α) (Finset.zero.{u2} α _inst_2)) => ι -> (Finset.{u2} α)) (Finsupp.coeFun.{u1, u2} ι (Finset.{u2} α) (Finset.zero.{u2} α _inst_2)) (Finsupp.rangeIcc.{u1, u2} ι α _inst_2 _inst_1 _inst_3 f g)))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : Zero.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] (f : Finsupp.{u2, u1} ι α _inst_2) (g : Finsupp.{u2, u1} ι α _inst_2), Eq.{max (succ u2) (succ u1)} (Finset.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2)) (Finset.Icc.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finsupp.preorder.{u2, u1} ι α _inst_2 (PartialOrder.toPreorder.{u1} α _inst_1)) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α _inst_1 _inst_2 _inst_3) f g) (Finset.finsupp.{u2, u1} ι α _inst_2 (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_2 f) (Finsupp.support.{u2, u1} ι α _inst_2 g)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι (Finset.{u1} α) (Finset.zero.{u1} α _inst_2)) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => Finset.{u1} α) a) (Finsupp.funLike.{u2, u1} ι (Finset.{u1} α) (Finset.zero.{u1} α _inst_2)) (Finsupp.rangeIcc.{u2, u1} ι α _inst_2 _inst_1 _inst_3 f g)))
Case conversion may be inaccurate. Consider using '#align finsupp.Icc_eq Finsupp.icc_eqₓ'. -/
theorem icc_eq [DecidableEq ι] : Icc f g = (f.support ∪ g.support).Finsupp (f.rangeIcc g) := by
  convert rfl
#align finsupp.Icc_eq Finsupp.icc_eq

/- warning: finsupp.card_Icc -> Finsupp.card_Icc is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Zero.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] (f : Finsupp.{u1, u2} ι α _inst_2) (g : Finsupp.{u1, u2} ι α _inst_2) [_inst_4 : DecidableEq.{succ u1} ι], Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finset.Icc.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finsupp.preorder.{u1, u2} ι α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)) (Finsupp.locallyFiniteOrder.{u1, u2} ι α _inst_1 _inst_2 _inst_3) f g)) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_2 f) (Finsupp.support.{u1, u2} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u2} α (Finset.Icc.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) f i) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) g i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : Zero.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] (f : Finsupp.{u2, u1} ι α _inst_2) (g : Finsupp.{u2, u1} ι α _inst_2), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finset.Icc.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finsupp.preorder.{u2, u1} ι α _inst_2 (PartialOrder.toPreorder.{u1} α _inst_1)) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α _inst_1 _inst_2 _inst_3) f g)) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_2 f) (Finsupp.support.{u2, u1} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Icc.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_3 (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) f i) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) g i))))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Icc Finsupp.card_Iccₓ'. -/
theorem card_Icc [DecidableEq ι] :
    (Icc f g).card = ∏ i in f.support ∪ g.support, (Icc (f i) (g i)).card := by
  simp_rw [Icc_eq, card_finsupp, range_Icc_to_fun]
#align finsupp.card_Icc Finsupp.card_Icc

/- warning: finsupp.card_Ico -> Finsupp.card_Ico is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Zero.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] (f : Finsupp.{u1, u2} ι α _inst_2) (g : Finsupp.{u1, u2} ι α _inst_2) [_inst_4 : DecidableEq.{succ u1} ι], Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finset.Ico.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finsupp.preorder.{u1, u2} ι α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)) (Finsupp.locallyFiniteOrder.{u1, u2} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_2 f) (Finsupp.support.{u1, u2} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u2} α (Finset.Icc.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) f i) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : Zero.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] (f : Finsupp.{u2, u1} ι α _inst_2) (g : Finsupp.{u2, u1} ι α _inst_2), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finset.Ico.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finsupp.preorder.{u2, u1} ι α _inst_2 (PartialOrder.toPreorder.{u1} α _inst_1)) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_2 f) (Finsupp.support.{u2, u1} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Icc.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_3 (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) f i) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Ico Finsupp.card_Icoₓ'. -/
theorem card_Ico [DecidableEq ι] :
    (Ico f g).card = (∏ i in f.support ∪ g.support, (Icc (f i) (g i)).card) - 1 := by
  rw [card_Ico_eq_card_Icc_sub_one, card_Icc]
#align finsupp.card_Ico Finsupp.card_Ico

/- warning: finsupp.card_Ioc -> Finsupp.card_Ioc is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Zero.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] (f : Finsupp.{u1, u2} ι α _inst_2) (g : Finsupp.{u1, u2} ι α _inst_2) [_inst_4 : DecidableEq.{succ u1} ι], Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finset.Ioc.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finsupp.preorder.{u1, u2} ι α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)) (Finsupp.locallyFiniteOrder.{u1, u2} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_2 f) (Finsupp.support.{u1, u2} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u2} α (Finset.Icc.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) f i) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : Zero.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] (f : Finsupp.{u2, u1} ι α _inst_2) (g : Finsupp.{u2, u1} ι α _inst_2), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finset.Ioc.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finsupp.preorder.{u2, u1} ι α _inst_2 (PartialOrder.toPreorder.{u1} α _inst_1)) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_2 f) (Finsupp.support.{u2, u1} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Icc.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_3 (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) f i) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Ioc Finsupp.card_Iocₓ'. -/
theorem card_Ioc [DecidableEq ι] :
    (Ioc f g).card = (∏ i in f.support ∪ g.support, (Icc (f i) (g i)).card) - 1 := by
  rw [card_Ioc_eq_card_Icc_sub_one, card_Icc]
#align finsupp.card_Ioc Finsupp.card_Ioc

/- warning: finsupp.card_Ioo -> Finsupp.card_Ioo is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Zero.{u2} α] [_inst_3 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] (f : Finsupp.{u1, u2} ι α _inst_2) (g : Finsupp.{u1, u2} ι α _inst_2) [_inst_4 : DecidableEq.{succ u1} ι], Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finset.Ioo.{max u1 u2} (Finsupp.{u1, u2} ι α _inst_2) (Finsupp.preorder.{u1, u2} ι α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)) (Finsupp.locallyFiniteOrder.{u1, u2} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Union.union.{u1} (Finset.{u1} ι) (Finset.hasUnion.{u1} ι (fun (a : ι) (b : ι) => _inst_4 a b)) (Finsupp.support.{u1, u2} ι α _inst_2 f) (Finsupp.support.{u1, u2} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u2} α (Finset.Icc.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) f i) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α _inst_2) (fun (_x : Finsupp.{u1, u2} ι α _inst_2) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : Zero.{u1} α] [_inst_3 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] (f : Finsupp.{u2, u1} ι α _inst_2) (g : Finsupp.{u2, u1} ι α _inst_2), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finset.Ioo.{max u2 u1} (Finsupp.{u2, u1} ι α _inst_2) (Finsupp.preorder.{u2, u1} ι α _inst_2 (PartialOrder.toPreorder.{u1} α _inst_1)) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α _inst_1 _inst_2 _inst_3) f g)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Union.union.{u2} (Finset.{u2} ι) (Finset.instUnionFinset.{u2} ι (fun (a : ι) (b : ι) => Classical.propDecidable (Eq.{succ u2} ι a b))) (Finsupp.support.{u2, u1} ι α _inst_2 f) (Finsupp.support.{u2, u1} ι α _inst_2 g)) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Icc.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_3 (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) f i) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α _inst_2) ι (fun (a : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) a) (Finsupp.funLike.{u2, u1} ι α _inst_2) g i)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Ioo Finsupp.card_Iooₓ'. -/
theorem card_Ioo [DecidableEq ι] :
    (Ioo f g).card = (∏ i in f.support ∪ g.support, (Icc (f i) (g i)).card) - 2 := by
  rw [card_Ioo_eq_card_Icc_sub_two, card_Icc]
#align finsupp.card_Ioo Finsupp.card_Ioo

end PartialOrder

section CanonicallyOrdered

variable [CanonicallyOrderedAddMonoid α] [LocallyFiniteOrder α]

variable (f : ι →₀ α)

/- warning: finsupp.card_Iic -> Finsupp.card_Iic is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : CanonicallyOrderedAddMonoid.{u2} α] [_inst_2 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))] (f : Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))), Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finset.Iic.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finsupp.preorder.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finsupp.preorder.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))) (Finsupp.orderBot.{u1, u2} ι α _inst_1) (Finsupp.locallyFiniteOrder.{u1, u2} ι α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)) (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) _inst_2)) f)) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finsupp.support.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) f) (fun (i : ι) => Finset.card.{u2} α (Finset.Iic.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))) (CanonicallyOrderedAddMonoid.toOrderBot.{u2} α _inst_1) _inst_2) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (fun (_x : Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) f i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : CanonicallyOrderedAddMonoid.{u1} α] [_inst_2 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))] (f : Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finset.Iic.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finsupp.preorder.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finsupp.preorder.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (Finsupp.orderBot.{u2, u1} ι α _inst_1) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)) (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) _inst_2)) f)) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finsupp.support.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) f) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Iic.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (OrderedAddCommMonoid.toPartialOrder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (OrderedAddCommMonoid.toPartialOrder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1))) (CanonicallyOrderedAddMonoid.toOrderBot.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_2) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) ι (fun (_x : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) _x) (Finsupp.funLike.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) f i))))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Iic Finsupp.card_Iicₓ'. -/
theorem card_Iic : (Iic f).card = ∏ i in f.support, (Iic (f i)).card := by
  classical simp_rw [Iic_eq_Icc, card_Icc, Finsupp.bot_eq_zero, support_zero, empty_union,
      zero_apply, bot_eq_zero]
#align finsupp.card_Iic Finsupp.card_Iic

/- warning: finsupp.card_Iio -> Finsupp.card_Iio is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} [_inst_1 : CanonicallyOrderedAddMonoid.{u2} α] [_inst_2 : LocallyFiniteOrder.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))] (f : Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))), Eq.{1} Nat (Finset.card.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finset.Iio.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finsupp.preorder.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{max u1 u2} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (Finsupp.preorder.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))) (Finsupp.orderBot.{u1, u2} ι α _inst_1) (Finsupp.locallyFiniteOrder.{u1, u2} ι α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)) (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) _inst_2)) f)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finsupp.support.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))))) f) (fun (i : ι) => Finset.card.{u2} α (Finset.Iic.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{u2} α (PartialOrder.toPreorder.{u2} α (OrderedAddCommMonoid.toPartialOrder.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1))) (CanonicallyOrderedAddMonoid.toOrderBot.{u2} α _inst_1) _inst_2) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) (fun (_x : Finsupp.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) => ι -> α) (Finsupp.coeFun.{u1, u2} ι α (AddZeroClass.toHasZero.{u2} α (AddMonoid.toAddZeroClass.{u2} α (AddCommMonoid.toAddMonoid.{u2} α (OrderedAddCommMonoid.toAddCommMonoid.{u2} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u2} α _inst_1)))))) f i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} [_inst_1 : CanonicallyOrderedAddMonoid.{u1} α] [_inst_2 : LocallyFiniteOrder.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))] (f : Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))), Eq.{1} Nat (Finset.card.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finset.Iio.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finsupp.preorder.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{max u2 u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) (Finsupp.preorder.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) (Finsupp.orderBot.{u2, u1} ι α _inst_1) (Finsupp.instLocallyFiniteOrderFinsuppPreorderToPreorder.{u2, u1} ι α (OrderedAddCommMonoid.toPartialOrder.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)) (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) _inst_2)) f)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finsupp.support.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1)))) f) (fun (i : ι) => Finset.card.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (Finset.Iic.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (OrderedAddCommMonoid.toPartialOrder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1))) (Finset.LocallyFiniteOrder.toLocallyFiniteOrderBot.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (PartialOrder.toPreorder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (OrderedAddCommMonoid.toPartialOrder.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1))) (CanonicallyOrderedAddMonoid.toOrderBot.{u1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) i) _inst_1) _inst_2) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Finsupp.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) ι (fun (_x : ι) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : ι) => α) _x) (Finsupp.funLike.{u2, u1} ι α (AddMonoid.toZero.{u1} α (AddCommMonoid.toAddMonoid.{u1} α (OrderedAddCommMonoid.toAddCommMonoid.{u1} α (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{u1} α _inst_1))))) f i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align finsupp.card_Iio Finsupp.card_Iioₓ'. -/
theorem card_Iio : (Iio f).card = (∏ i in f.support, (Iic (f i)).card) - 1 := by
  rw [card_Iio_eq_card_Iic_sub_one, card_Iic]
#align finsupp.card_Iio Finsupp.card_Iio

end CanonicallyOrdered

end Finsupp


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Mario Carneiro

The partial recursive functions are defined similarly to the primitive
recursive functions, but now all functions are partial, implemented
using the `roption` monad, and there is an additional operation, called
μ-recursion, which performs unbounded minimization.
-/
import computability.primrec data.pfun

open encodable denumerable roption

namespace nat

section rfind
parameter (p : ℕ →. bool)

private def lbp (m n : ℕ) : Prop := m = n + 1 ∧ ∀ k ≤ n, ff ∈ p k

parameter (H : ∃ n, tt ∈ p n ∧ ∀ k < n, (p k).dom)

private def wf_lbp : well_founded lbp :=
⟨let ⟨n, pn⟩ := H in begin
  suffices : ∀m k, n ≤ k + m → acc (lbp p) k,
  { from λa, this _ _ (nat.le_add_left _ _) },
  intros m k kn,
  induction m with m IH generalizing k;
    refine ⟨_, λ y r, _⟩; rcases r with ⟨rfl, a⟩,
  { injection mem_unique pn.1 (a _ kn) },
  { exact IH _ (by rw nat.add_right_comm; exact kn) }
end⟩

def rfind_x : {n // tt ∈ p n ∧ ∀m < n, ff ∈ p m} :=
suffices ∀ k, (∀n < k, ff ∈ p n) → {n // tt ∈ p n ∧ ∀m < n, ff ∈ p m},
from this 0 (λ n, (nat.not_lt_zero _).elim),
@well_founded.fix _ _ lbp wf_lbp begin
  intros m IH al,
  have pm : (p m).dom,
  { rcases H with ⟨n, h₁, h₂⟩,
    rcases lt_trichotomy m n with h₃|h₃|h₃,
    { exact h₂ _ h₃ },
    { rw h₃, exact h₁.fst },
    { injection mem_unique h₁ (al _ h₃) } },
  cases e : (p m).get pm,
  { suffices,
    exact IH _ ⟨rfl, this⟩ (λ n h, this _ (le_of_lt_succ h)),
    intros n h, cases lt_or_eq_of_le h with h h,
    { exact al _ h },
    { rw h, exact ⟨_, e⟩ } },
  { exact ⟨m, ⟨_, e⟩, al⟩ }
end

end rfind

def rfind (p : ℕ →. bool) : roption ℕ :=
⟨_, λ h, (rfind_x p h).1⟩

theorem rfind_spec {p : ℕ →. bool} {n : ℕ} (h : n ∈ rfind p) : tt ∈ p n :=
h.snd ▸ (rfind_x p h.fst).2.1

theorem rfind_min {p : ℕ →. bool} {n : ℕ} (h : n ∈ rfind p) :
  ∀ {m : ℕ}, m < n → ff ∈ p m :=
h.snd ▸ (rfind_x p h.fst).2.2

@[simp] theorem rfind_dom {p : ℕ →. bool} :
  (rfind p).dom ↔ ∃ n, tt ∈ p n ∧ ∀ {m : ℕ}, m < n → (p m).dom :=
iff.rfl

@[simp] theorem rfind_dom' {p : ℕ →. bool} :
  (rfind p).dom ↔ ∃ n, tt ∈ p n ∧ ∀ {m : ℕ}, m ≤ n → (p m).dom :=
exists_congr $ λ n, and_congr_right $ λ pn,
⟨λ H m h, (eq_or_lt_of_le h).elim (λ e, e.symm ▸ pn.fst) (H _),
 λ H m h, H (le_of_lt h)⟩

@[simp] theorem mem_rfind {p : ℕ →. bool} {n : ℕ} :
  n ∈ rfind p ↔ tt ∈ p n ∧ ∀ {m : ℕ}, m < n → ff ∈ p m :=
⟨λ h, ⟨rfind_spec h, @rfind_min _ _ h⟩,
  λ ⟨h₁, h₂⟩, let ⟨m, hm⟩ := dom_iff_mem.1 $
    (@rfind_dom p).2 ⟨_, h₁, λ m mn, (h₂ mn).fst⟩ in
  begin
    rcases lt_trichotomy m n with h|h|h,
    { injection mem_unique (h₂ h) (rfind_spec hm) },
    { rwa ← h },
    { injection mem_unique h₁ (rfind_min hm h) },
  end⟩

theorem rfind_min' {p : ℕ → bool} {m : ℕ} (pm : p m) :
  ∃ n ∈ rfind p, n ≤ m :=
have tt ∈ (p : ℕ →. bool) m, from ⟨trivial, pm⟩,
let ⟨n, hn⟩ := dom_iff_mem.1 $
  (@rfind_dom p).2 ⟨m, this, λ k h, ⟨⟩⟩ in
⟨n, hn, not_lt.1 $ λ h,
  by injection mem_unique this (rfind_min hn h)⟩

theorem rfind_zero_none
  (p : ℕ →. bool) (p0 : p 0 = none) : rfind p = none :=
eq_none_iff.2 $ λ a h,
let ⟨n, h₁, h₂⟩ := rfind_dom'.1 h.fst in
(p0 ▸ h₂ (zero_le _) : (@roption.none bool).dom)

def rfind_opt {α} (f : ℕ → option α) : roption α :=
(rfind (λ n, (f n).is_some)).bind (λ n, f n)

theorem rfind_opt_spec {α} {f : ℕ → option α} {a}
 (h : a ∈ rfind_opt f) : ∃ n, a ∈ f n :=
let ⟨n, h₁, h₂⟩ := mem_bind_iff.1 h in ⟨n, mem_coe.1 h₂⟩

theorem rfind_opt_dom {α} {f : ℕ → option α} :
  (rfind_opt f).dom ↔ ∃ n a, a ∈ f n :=
⟨λ h, (rfind_opt_spec ⟨h, rfl⟩).imp (λ n h, ⟨_, h⟩),
 λ h, begin
  have h' : ∃ n, (f n).is_some :=
    h.imp (λ n, option.is_some_iff_exists.2),
  have s := nat.find_spec h',
  have fd : (rfind (λ n, (f n).is_some)).dom :=
    ⟨nat.find h', by simpa using s.symm, λ _ _, trivial⟩,
  refine ⟨fd, _⟩,
  have := rfind_spec (get_mem fd),
  simp at this ⊢,
  cases option.is_some_iff_exists.1 this.symm with a e,
  rw e, trivial
end⟩

theorem rfind_opt_mono {α} {f : ℕ → option α}
  (H : ∀ {a m n}, m ≤ n → a ∈ f m → a ∈ f n)
  {a} : a ∈ rfind_opt f ↔ ∃ n, a ∈ f n :=
⟨rfind_opt_spec, λ ⟨n, h⟩, begin
  have h' := rfind_opt_dom.2 ⟨_, _, h⟩,
  cases rfind_opt_spec ⟨h', rfl⟩ with k hk,
  have := (H (le_max_left _ _) h).symm.trans
          (H (le_max_right _ _) hk),
  simp at this, simp [this, get_mem]
end⟩

inductive partrec : (ℕ →. ℕ) → Prop
| zero : partrec (pure 0)
| succ : partrec succ
| left : partrec (λ n, n.unpair.1)
| right : partrec (λ n, n.unpair.2)
| pair {f g} : partrec f → partrec g → partrec (λ n, mkpair <$> f n <*> g n)
| comp {f g} : partrec f → partrec g → partrec (λ n, g n >>= f)
| prec {f g} : partrec f → partrec g → partrec (unpaired (λ a n,
    n.elim (f a) (λ y IH, do i ← IH, g (mkpair a (mkpair y i)))))
| rfind {f} : partrec f → partrec (λ a,
    rfind (λ n, (λ m, m = 0) <$> f (mkpair a n)))

namespace partrec

theorem of_eq {f g : ℕ →. ℕ} (hf : partrec f) (H : ∀ n, f n = g n) : partrec g :=
(funext H : f = g) ▸ hf

theorem of_eq_tot {f : ℕ →. ℕ} {g : ℕ → ℕ}
  (hf : partrec f) (H : ∀ n, g n ∈ f n) : partrec g :=
hf.of_eq (λ n, eq_some_iff.2 (H n))

theorem of_primrec {f : ℕ → ℕ} (hf : primrec f) : partrec f :=
begin
  induction hf,
  case nat.primrec.zero { exact zero },
  case nat.primrec.succ { exact succ },
  case nat.primrec.left { exact left },
  case nat.primrec.right { exact right },
  case nat.primrec.pair : f g hf hg pf pg {
    refine (pf.pair pg).of_eq_tot (λ n, _),
    simp [has_seq.seq] },
  case nat.primrec.comp : f g hf hg pf pg {
    refine (pf.comp pg).of_eq_tot (λ n, _),
    simp },
  case nat.primrec.prec : f g hf hg pf pg {
    refine (pf.prec pg).of_eq_tot (λ n, _),
    simp,
    induction n.unpair.2 with m IH, {simp},
    simp, exact ⟨_, IH, rfl⟩ },
end

protected theorem some : partrec some := of_primrec primrec.id

theorem none : partrec (λ n, none) :=
(of_primrec (nat.primrec.const 1)).rfind.of_eq $
λ n, eq_none_iff.2 $ λ a ⟨h, e⟩, by simpa using h

theorem prec' {f g h}
  (hf : partrec f) (hg : partrec g) (hh : partrec h) :
  partrec (λ a, (f a).bind (λ n, n.elim (g a)
    (λ y IH, do i ← IH, h (mkpair a (mkpair y i))))) :=
((prec hg hh).comp (pair partrec.some hf)).of_eq $
λ a, ext $ λ s, by simp [(<*>)]; exact
⟨λ ⟨n, h₁, h₂⟩, ⟨_, ⟨_, h₁, rfl⟩, by simpa using h₂⟩,
 λ ⟨_, ⟨n, h₁, rfl⟩, h₂⟩, ⟨_, h₁, by simpa using h₂⟩⟩

theorem ppred : partrec (λ n, ppred n) :=
have primrec₂ (λ n m, if n = nat.succ m then 0 else 1),
from (primrec.ite
  (@@primrec_rel.comp _ _ _ _ _ _ _ primrec.eq
    primrec.fst
    (_root_.primrec.succ.comp primrec.snd))
  (_root_.primrec.const 0) (_root_.primrec.const 1)).to₂,
(of_primrec (primrec₂.unpaired'.2 this)).rfind.of_eq $
λ n, begin
  cases n; simp,
  { exact eq_none_iff.2 (λ a ⟨⟨m, h, _⟩, _⟩,
      by simpa [show 0 ≠ m.succ, by intro h; injection h] using h) },
  { refine eq_some_iff.2 _,
    simp, intros m h, simp [ne_of_gt h] }
end

end partrec

end nat

def partrec {α σ} [primcodable α] [primcodable σ]
  (f : α →. σ) := nat.partrec (λ n,
  roption.bind (decode α n) (λ a, (f a).map encode))

def partrec₂ {α β σ} [primcodable α] [primcodable β] [primcodable σ]
  (f : α → β →. σ) := partrec (λ p : α × β, f p.1 p.2)

def computable {α σ} [primcodable α] [primcodable σ] (f : α → σ) := partrec (f : α →. σ)

def computable₂ {α β σ} [primcodable α] [primcodable β] [primcodable σ]
  (f : α → β → σ) := computable (λ p : α × β, f p.1 p.2)

theorem primrec.to_comp {α σ} [primcodable α] [primcodable σ]
  {f : α → σ} (hf : primrec f) : computable f :=
(nat.partrec.ppred.comp (nat.partrec.of_primrec hf)).of_eq $
λ n, by simp; cases decode α n; simp

theorem primrec₂.to_comp {α β σ} [primcodable α] [primcodable β] [primcodable σ]
  {f : α → β → σ} (hf : primrec₂ f) : computable₂ f := hf.to_comp

theorem computable.part {α σ} [primcodable α] [primcodable σ]
  {f : α → σ} (hf : computable f) : partrec (f : α →. σ) := hf

theorem computable₂.part {α β σ} [primcodable α] [primcodable β] [primcodable σ]
  {f : α → β → σ} (hf : computable₂ f) : partrec₂ (λ a, (f a : β →. σ)) := hf

namespace computable
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

theorem of_eq {f g : α → σ} (hf : computable f) (H : ∀ n, f n = g n) : computable g :=
(funext H : f = g) ▸ hf

theorem const (s : σ) : computable (λ a : α, s) :=
(primrec.const _).to_comp

theorem of_option {f : α → option β}
  (hf : computable f) : partrec (λ a, (f a : roption β)) :=
(nat.partrec.ppred.comp hf).of_eq $ λ n, begin
  cases decode α n with a; simp,
  cases f a with b; simp
end

theorem to₂ {f : α × β → σ} (hf : computable f) : computable₂ (λ a b, f (a, b)) :=
hf.of_eq $ λ ⟨a, b⟩, rfl

protected theorem id : computable (@id α) := primrec.id.to_comp

theorem fst : computable (@prod.fst α β) := primrec.fst.to_comp

theorem snd : computable (@prod.snd α β) := primrec.snd.to_comp

theorem pair {f : α → β} {g : α → γ}
  (hf : computable f) (hg : computable g) : computable (λ a, (f a, g a)) :=
(hf.pair hg).of_eq $
λ n, by cases decode α n; simp [(<*>)]

theorem unpair : computable nat.unpair := primrec.unpair.to_comp

theorem succ : computable nat.succ := primrec.succ.to_comp
theorem pred : computable nat.pred := primrec.pred.to_comp
theorem nat_bodd : computable nat.bodd := primrec.nat_bodd.to_comp
theorem nat_div2 : computable nat.div2 := primrec.nat_div2.to_comp

theorem sum_inl : computable (@sum.inl α β) := primrec.sum_inl.to_comp
theorem sum_inr : computable (@sum.inr α β) := primrec.sum_inr.to_comp

theorem list_cons : computable₂ (@list.cons α) := primrec.list_cons.to_comp
theorem list_reverse : computable (@list.reverse α) := primrec.list_reverse.to_comp
theorem list_nth : computable₂ (@list.nth α) := primrec.list_nth.to_comp
theorem list_append : computable₂ ((++) : list α → list α → list α) := primrec.list_append.to_comp
theorem list_concat : computable₂ (λ l (a:α), l ++ [a]) := primrec.list_concat.to_comp
theorem list_length : computable (@list.length α) := primrec.list_length.to_comp

theorem vector_cons {n} : computable₂ (@vector.cons α n) := primrec.vector_cons.to_comp
theorem vector_to_list {n} : computable (@vector.to_list α n) := primrec.vector_to_list.to_comp
theorem vector_length {n} : computable (@vector.length α n) := primrec.vector_length.to_comp
theorem vector_head {n} : computable (@vector.head α n) := primrec.vector_head.to_comp
theorem vector_tail {n} : computable (@vector.tail α n) := primrec.vector_tail.to_comp
theorem vector_nth {n} : computable₂ (@vector.nth α n) := primrec.vector_nth.to_comp
theorem vector_nth' {n} : computable (@vector.nth α n) := primrec.vector_nth'.to_comp
theorem vector_of_fn' {n} : computable (@vector.of_fn α n) := primrec.vector_of_fn'.to_comp

theorem fin_app {n} : computable₂ (@id (fin n → σ)) := primrec.fin_app.to_comp

protected theorem encode : computable (@encode α _) :=
primrec.encode.to_comp

protected theorem decode : computable (decode α) :=
primrec.decode.to_comp

protected theorem of_nat (α) [denumerable α] : computable (of_nat α) :=
(primrec.of_nat _).to_comp

theorem encode_iff {f : α → σ} : computable (λ a, encode (f a)) ↔ computable f :=
iff.rfl

theorem option_some : computable (@option.some α) :=
primrec.option_some.to_comp

end computable

namespace partrec
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

open computable

theorem of_eq {f g : α →. σ} (hf : partrec f) (H : ∀ n, f n = g n) : partrec g :=
(funext H : f = g) ▸ hf

theorem of_eq_tot {f : α →. σ} {g : α → σ}
  (hf : partrec f) (H : ∀ n, g n ∈ f n) : computable g :=
hf.of_eq (λ a, eq_some_iff.2 (H a))

theorem none : partrec (λ a : α, @roption.none σ) :=
nat.partrec.none.of_eq $ λ n, by cases decode α n; simp

protected theorem some : partrec (@roption.some α) := computable.id

theorem const' (s : roption σ) : partrec (λ a : α, s) :=
by haveI := classical.dec s.dom; exact
(of_option (const (to_option s))).of_eq (λ a, of_to_option s)

protected theorem bind {f : α →. β} {g : α → β →. σ}
  (hf : partrec f) (hg : partrec₂ g) : partrec (λ a, (f a).bind (g a)) :=
(hg.comp (nat.partrec.some.pair hf)).of_eq $
λ n, by simp [(<*>)]; cases e : decode α n with a;
  simp [e, encodek]

theorem map {f : α →. β} {g : α → β → σ}
  (hf : partrec f) (hg : computable₂ g) : partrec (λ a, (f a).map (g a)) :=
by simpa [bind_some_eq_map] using
   @@partrec.bind _ _ _ (λ a b, roption.some (g a b)) hf hg

theorem to₂ {f : α × β →. σ} (hf : partrec f) : partrec₂ (λ a b, f (a, b)) :=
hf.of_eq $ λ ⟨a, b⟩, rfl

theorem nat_elim
  {f : α → ℕ} {g : α →. σ} {h : α → ℕ × σ →. σ}
  (hf : computable f) (hg : partrec g) (hh : partrec₂ h) :
  partrec (λ a, (f a).elim (g a) (λ y IH, IH.bind (λ i, h a (y, i)))) :=
(nat.partrec.prec' hf hg hh).of_eq $ λ n, begin
  cases e : decode α n with a; simp [e],
  induction f a with m IH; simp,
  rw [IH, bind_map],
  congr, funext s,
  simp [encodek]
end

theorem comp {f : β →. σ} {g : α → β}
  (hf : partrec f) (hg : computable g) : partrec (λ a, f (g a)) :=
(hf.comp hg).of_eq $
λ n, by simp; cases e : decode α n with a;
  simp [e, encodek]

theorem nat_iff {f : ℕ →. ℕ} : partrec f ↔ nat.partrec f :=
by simp [partrec, map_id']

theorem map_encode_iff {f : α →. σ} : partrec (λ a, (f a).map encode) ↔ partrec f :=
iff.rfl

end partrec

namespace partrec₂
variables {α : Type*} {β : Type*} {γ : Type*} {δ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable δ] [primcodable σ]

theorem unpaired {f : ℕ → ℕ →. α} : partrec (nat.unpaired f) ↔ partrec₂ f :=
⟨λ h, by simpa using h.comp primrec₂.mkpair.to_comp,
 λ h, h.comp primrec.unpair.to_comp⟩

theorem unpaired' {f : ℕ → ℕ →. ℕ} : nat.partrec (nat.unpaired f) ↔ partrec₂ f :=
partrec.nat_iff.symm.trans unpaired

theorem comp
  {f : β → γ →. σ} {g : α → β} {h : α → γ}
  (hf : partrec₂ f) (hg : computable g) (hh : computable h) :
  partrec (λ a, f (g a) (h a)) := hf.comp (hg.pair hh)

theorem comp₂
  {f : γ → δ →. σ} {g : α → β → γ} {h : α → β → δ}
  (hf : partrec₂ f) (hg : computable₂ g) (hh : computable₂ h) :
  partrec₂ (λ a b, f (g a b) (h a b)) := hf.comp hg hh

end partrec₂

namespace computable
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

theorem comp {f : β → σ} {g : α → β}
  (hf : computable f) (hg : computable g) :
  computable (λ a, f (g a)) := hf.comp hg

theorem comp₂ {f : γ → σ} {g : α → β → γ}
  (hf : computable f) (hg : computable₂ g) :
  computable₂ (λ a b, f (g a b)) := hf.comp hg

end computable

namespace computable₂
variables {α : Type*} {β : Type*} {γ : Type*} {δ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable δ] [primcodable σ]

theorem comp
  {f : β → γ → σ} {g : α → β} {h : α → γ}
  (hf : computable₂ f) (hg : computable g) (hh : computable h) :
  computable (λ a, f (g a) (h a)) := hf.comp (hg.pair hh)

theorem comp₂
  {f : γ → δ → σ} {g : α → β → γ} {h : α → β → δ}
  (hf : computable₂ f) (hg : computable₂ g) (hh : computable₂ h) :
  computable₂ (λ a b, f (g a b) (h a b)) := hf.comp hg hh

end computable₂

namespace partrec
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

open computable

theorem rfind {p : α → ℕ →. bool} (hp : partrec₂ p) :
  partrec (λ a, nat.rfind (p a)) :=
(nat.partrec.rfind $ hp.map
  ((primrec.dom_bool (λ b, cond b 0 1))
    .comp primrec.snd).to₂.to_comp).of_eq $
λ n, begin
  cases e : decode α n with a;
    simp [e, nat.rfind_zero_none, map_id'],
  congr, funext n,
  simp [roption.map_map, (∘)],
  apply map_id' (λ b, _),
  cases b; refl
end

theorem rfind_opt {f : α → ℕ → option σ} (hf : computable₂ f) :
  partrec (λ a, nat.rfind_opt (f a)) :=
(rfind (primrec.option_is_some.to_comp.comp hf).part.to₂).bind
  (of_option hf)

theorem nat_cases_right
  {f : α → ℕ} {g : α → σ} {h : α → ℕ →. σ}
  (hf : computable f) (hg : computable g) (hh : partrec₂ h) :
  partrec (λ a, (f a).cases (some (g a)) (h a)) :=
(nat_elim hf hg (hh.comp fst (pred.comp $ hf.comp fst)).to₂).of_eq $
λ a, begin
  simp, cases f a; simp,
  refine ext (λ b, ⟨λ H, _, λ H, _⟩),
  { rcases mem_bind_iff.1 H with ⟨c, h₁, h₂⟩, exact h₂ },
  { have : ∀ m, (nat.elim (roption.some (g a))
      (λ y IH, IH.bind (λ _, h a n)) m).dom,
    { intro, induction m; simp [*, H.fst] },
    exact ⟨⟨this n, H.fst⟩, H.snd⟩ }
end

theorem bind_decode2_iff {f : α →. σ} : partrec f ↔
  nat.partrec (λ n, roption.bind (decode2 α n) (λ a, (f a).map encode)) :=
⟨λ hf, nat_iff.1 $ (of_option primrec.decode2.to_comp).bind $
  (map hf (computable.encode.comp snd).to₂).comp snd,
λ h, map_encode_iff.1 $ by simpa [encodek2]
  using (nat_iff.2 h).comp (@computable.encode α _)⟩

theorem vector_m_of_fn : ∀ {n} {f : fin n → α →. σ}, (∀ i, partrec (f i)) →
  partrec (λ (a : α), vector.m_of_fn (λ i, f i a))
| 0     f hf := const _
| (n+1) f hf := by simp [vector.m_of_fn]; exact
  (hf 0).bind (partrec.bind ((vector_m_of_fn (λ i, hf i.succ)).comp fst)
    (primrec.vector_cons.to_comp.comp (snd.comp fst) snd))

end partrec

@[simp] theorem vector.m_of_fn_roption_some {α n} : ∀ (f : fin n → α),
  vector.m_of_fn (λ i, roption.some (f i)) = roption.some (vector.of_fn f) :=
vector.m_of_fn_pure

namespace computable
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

theorem option_some_iff {f : α → σ} : computable (λ a, some (f a)) ↔ computable f :=
⟨λ h, encode_iff.1 $ primrec.pred.to_comp.comp $ encode_iff.2 h,
 option_some.comp⟩

theorem bind_decode_iff {f : α → β → option σ} : computable₂ (λ a n,
  (decode β n).bind (f a)) ↔ computable₂ f :=
⟨λ hf, nat.partrec.of_eq
    (((partrec.nat_iff.2 (nat.partrec.ppred.comp $
        nat.partrec.of_primrec $ primcodable.prim β)).comp snd).bind
      (computable.comp hf fst).to₂.part) $
  λ n, by simp;
    cases decode α n.unpair.1; simp;
    cases decode β n.unpair.2; simp,
λ hf, begin
  have : partrec (λ a : α × ℕ, (encode (decode β a.2)).cases
    (some option.none) (λ n, roption.map (f a.1) (decode β n))) :=
  partrec.nat_cases_right (primrec.encdec.to_comp.comp snd)
    (const none) ((of_option (computable.decode.comp snd)).map
      (hf.comp (fst.comp $ fst.comp fst) snd).to₂),
  refine this.of_eq (λ a, _),
  simp, cases decode β a.2; simp [encodek]
end⟩

theorem map_decode_iff {f : α → β → σ} : computable₂ (λ a n,
  (decode β n).map (f a)) ↔ computable₂ f :=
bind_decode_iff.trans option_some_iff

theorem nat_elim
  {f : α → ℕ} {g : α → σ} {h : α → ℕ × σ → σ}
  (hf : computable f) (hg : computable g) (hh : computable₂ h) :
  computable (λ a, (f a).elim (g a) (λ y IH, h a (y, IH))) :=
(partrec.nat_elim hf hg hh.part).of_eq $
λ a, by simp; induction f a; simp *

theorem nat_cases {f : α → ℕ} {g : α → σ} {h : α → ℕ → σ}
  (hf : computable f) (hg : computable g) (hh : computable₂ h) :
  computable (λ a, (f a).cases (g a) (h a)) :=
nat_elim hf hg (hh.comp fst $ fst.comp snd).to₂

theorem cond {c : α → bool} {f : α → σ} {g : α → σ}
  (hc : computable c) (hf : computable f) (hg : computable g) :
  computable (λ a, cond (c a) (f a) (g a)) :=
(nat_cases (encode_iff.2 hc) hg (hf.comp fst).to₂).of_eq $
λ a, by cases c a; refl

theorem option_cases {o : α → option β} {f : α → σ} {g : α → β → σ}
  (ho : computable o) (hf : computable f) (hg : computable₂ g) :
  @computable _ σ _ _ (λ a, option.cases_on (o a) (f a) (g a)) :=
option_some_iff.1 $
(nat_cases (encode_iff.2 ho) (option_some_iff.2 hf)
    (map_decode_iff.2 hg)).of_eq $
λ a, by cases o a; simp [encodek]; refl

theorem option_bind {f : α → option β} {g : α → β → option σ}
  (hf : computable f) (hg : computable₂ g) :
  computable (λ a, (f a).bind (g a)) :=
(option_cases hf (const option.none) hg).of_eq $
λ a, by cases f a; refl

theorem option_map {f : α → option β} {g : α → β → σ}
  (hf : computable f) (hg : computable₂ g) : computable (λ a, (f a).map (g a)) :=
option_bind hf (option_some.comp₂ hg)

theorem sum_cases
  {f : α → β ⊕ γ} {g : α → β → σ} {h : α → γ → σ}
  (hf : computable f) (hg : computable₂ g) (hh : computable₂ h) :
  @computable _ σ _ _ (λ a, sum.cases_on (f a) (g a) (h a)) :=
option_some_iff.1 $
(cond (nat_bodd.comp $ encode_iff.2 hf)
  (option_map (computable.decode.comp $ nat_div2.comp $ encode_iff.2 hf) hh)
  (option_map (computable.decode.comp $ nat_div2.comp $ encode_iff.2 hf) hg)).of_eq $
λ a, by cases f a with b c;
  simp [nat.div2_bit, nat.bodd_bit, encodek]; refl

theorem nat_strong_rec
  (f : α → ℕ → σ) {g : α → list σ → option σ} (hg : computable₂ g)
  (H : ∀ a n, g a ((list.range n).map (f a)) = some (f a n)) : computable₂ f :=
suffices computable₂ (λ a n, (list.range n).map (f a)), from
  option_some_iff.1 $
  (list_nth.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq $
  λ a, by simp [list.nth_range (nat.lt_succ_self a.2)]; refl,
option_some_iff.1 $
(nat_elim snd (const (option.some [])) (to₂ $
  option_bind (snd.comp snd) $ to₂ $
  option_map
    (hg.comp (fst.comp $ fst.comp fst) snd)
    (to₂ $ list_concat.comp (snd.comp fst) snd))).of_eq $
λ a, begin
  simp, induction a.2 with n IH, {refl},
  simp [IH, H, list.range_concat]
end

theorem list_of_fn : ∀ {n} {f : fin n → α → σ},
  (∀ i, computable (f i)) → computable (λ a, list.of_fn (λ i, f i a))
| 0     f hf := const []
| (n+1) f hf := by simp [list.of_fn_succ]; exact
  list_cons.comp (hf 0) (list_of_fn (λ i, hf i.succ))

theorem vector_of_fn {n} {f : fin n → α → σ}
  (hf : ∀ i, computable (f i)) : computable (λ a, vector.of_fn (λ i, f i a)) :=
(partrec.vector_m_of_fn hf).of_eq $ λ a, by simp

end computable

namespace partrec
variables {α : Type*} {β : Type*} {γ : Type*} {σ : Type*}
variables [primcodable α] [primcodable β] [primcodable γ] [primcodable σ]

open computable

theorem option_some_iff {f : α →. σ} :
  partrec (λ a, (f a).map option.some) ↔ partrec f :=
⟨λ h, (nat.partrec.ppred.comp h).of_eq $
   λ n, by simp [roption.bind_assoc, bind_some_eq_map],
 λ hf, hf.map (option_some.comp snd).to₂⟩

theorem option_cases_right {o : α → option β} {f : α → σ} {g : α → β →. σ}
  (ho : computable o) (hf : computable f) (hg : partrec₂ g) :
  @partrec _ σ _ _ (λ a, option.cases_on (o a) (some (f a)) (g a)) :=
have partrec (λ (a : α), nat.cases (roption.some (f a))
  (λ n, roption.bind (decode β n) (g a)) (encode (o a))) :=
nat_cases_right (encode_iff.2 ho) hf.part $
  ((@computable.decode β _).comp snd).of_option.bind
    (hg.comp (fst.comp fst) snd).to₂,
this.of_eq $ λ a, by cases o a with b; simp [encodek]

theorem sum_cases_right
  {f : α → β ⊕ γ} {g : α → β → σ} {h : α → γ →. σ}
  (hf : computable f) (hg : computable₂ g) (hh : partrec₂ h) :
  @partrec _ σ _ _ (λ a, sum.cases_on (f a) (λ b, some (g a b)) (h a)) :=
have partrec (λ a, (option.cases_on
  (sum.cases_on (f a) (λ b, option.none) option.some : option γ)
  (some (sum.cases_on (f a) (λ b, some (g a b))
     (λ c, option.none)))
  (λ c, (h a c).map option.some) : roption (option σ))) :=
option_cases_right
  (sum_cases hf (const option.none).to₂ (option_some.comp snd).to₂)
  (sum_cases hf (option_some.comp hg) (const option.none).to₂)
  (option_some_iff.2 hh),
option_some_iff.1 $ this.of_eq $ λ a, by cases f a; simp

theorem sum_cases_left
  {f : α → β ⊕ γ} {g : α → β →. σ} {h : α → γ → σ}
  (hf : computable f) (hg : partrec₂ g) (hh : computable₂ h) :
  @partrec _ σ _ _ (λ a, sum.cases_on (f a) (g a) (λ c, some (h a c))) :=
(sum_cases_right (sum_cases hf
  (sum_inr.comp snd).to₂ (sum_inl.comp snd).to₂) hh hg).of_eq $
λ a, by cases f a; simp

private lemma fix_aux
  {f : α →. σ ⊕ α} (hf : partrec f)
  (a : α) (b : σ) :
  let F : α → ℕ →. σ ⊕ α := λ a n,
    n.elim (some (sum.inr a)) $ λ y IH, IH.bind $ λ s,
    sum.cases_on s (λ _, roption.some s) f in
  (∃ (n : ℕ), ((∃ (b' : σ), sum.inl b' ∈ F a n) ∧
        ∀ {m : ℕ}, m < n → (∃ (b : α), sum.inr b ∈ F a m)) ∧
      sum.inl b ∈ F a n) ↔ b ∈ pfun.fix f a :=
begin
  intro, refine ⟨λ h, _, λ h, _⟩,
  { rcases h with ⟨n, ⟨_x, h₁⟩, h₂⟩,
    have : ∀ m a' (_: sum.inr a' ∈ F a m)
      (_: b ∈ pfun.fix f a'), b ∈ pfun.fix f a,
    { intros m a' am ba,
      induction m with m IH generalizing a'; simp [F] at am,
      { rwa ← am },
      rcases am with ⟨a₂, am₂, fa₂⟩,
      exact IH _ am₂ (pfun.mem_fix_iff.2 (or.inr ⟨_, fa₂, ba⟩)) },
    cases n; simp [F] at h₂, {cases h₂},
    rcases h₂ with h₂ | ⟨a', am', fa'⟩,
    { cases h₁ (nat.lt_succ_self _) with a' h,
      injection mem_unique h h₂ },
    { exact this _ _ am' (pfun.mem_fix_iff.2 (or.inl fa')) } },
  { suffices : ∀ a' (_: b ∈ pfun.fix f a') k (_: sum.inr a' ∈ F a k),
       ∃ n, sum.inl b ∈ F a n ∧
         ∀ (m < n) (_ : k ≤ m), ∃ a₂, sum.inr a₂ ∈ F a m,
    { rcases this _ h 0 (by simp [F]) with ⟨n, hn₁, hn₂⟩,
      exact ⟨_, ⟨⟨_, hn₁⟩, λ m mn, hn₂ m mn (nat.zero_le _)⟩, hn₁⟩ },
    intros a₁ h₁,
    apply pfun.fix_induction h₁, intros a₂ h₂ IH k hk,
    rcases pfun.mem_fix_iff.1 h₂ with h₂ | ⟨a₃, am₃, fa₃⟩,
    { refine ⟨k.succ, _, λ m mk km, ⟨a₂, _⟩⟩,
      { simp [F], exact or.inr ⟨_, hk, h₂⟩ },
      { rwa le_antisymm (nat.le_of_lt_succ mk) km } },
    { rcases IH _ fa₃ am₃ k.succ _ with ⟨n, hn₁, hn₂⟩,
      { refine ⟨n, hn₁, λ m mn km, _⟩,
        cases lt_or_eq_of_le km with km km,
        { exact hn₂ _ mn km },
        { exact km ▸ ⟨_, hk⟩ } },
      { simp [F], exact ⟨_, hk, am₃⟩ } } }
end

theorem fix
  {f : α →. σ ⊕ α} (hf : partrec f) : partrec (pfun.fix f) :=
let F : α → ℕ →. σ ⊕ α := λ a n,
  n.elim (some (sum.inr a)) $ λ y IH, IH.bind $ λ s,
  sum.cases_on s (λ _, roption.some s) f in
have hF : partrec₂ F :=
partrec.nat_elim snd (sum_inr.comp fst).part
  (sum_cases_right (snd.comp snd)
    (snd.comp $ snd.comp fst).to₂
    (hf.comp snd).to₂).to₂,
let p := λ a n, @roption.map _ bool
  (λ s, sum.cases_on s (λ_, tt) (λ _, ff)) (F a n) in
have hp : partrec₂ p := hF.map ((sum_cases computable.id
  (const tt).to₂ (const ff).to₂).comp snd).to₂,
(hp.rfind.bind (hF.bind
  (sum_cases_right snd snd.to₂ none.to₂).to₂).to₂).of_eq $
λ a, ext $ λ b, by simp; apply fix_aux hf

end partrec


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

import algebra.module.basic
import algebra.gcd_monoid.basic
import algebra.group_ring_action
import group_theory.group_action.defs

/-!
# Instances on punit

This file collects facts about algebraic structures on the one-element type, e.g. that it is a
commutative ring.
-/

universes u

namespace punit
variables {R S : Type*} (x y : punit.{u+1}) (s : set punit.{u+1})

@[to_additive]
instance : comm_group punit :=
by refine_struct
{ mul := λ _ _, star,
  one := star,
  inv := λ _, star,
  div := λ _ _, star,
  npow := λ _ _, star,
  zpow := λ _ _, star,
  .. };
intros; exact subsingleton.elim _ _

@[simp, to_additive] lemma one_eq : (1 : punit) = star := rfl
@[simp, to_additive] lemma mul_eq : x * y = star := rfl
-- `sub_eq` simplifies `punit.sub_eq`, but the latter is eligible for `dsimp`
@[simp, nolint simp_nf, to_additive] lemma div_eq : x / y = star := rfl
-- `neg_eq` simplifies `punit.neg_eq`, but the latter is eligible for `dsimp`
@[simp, nolint simp_nf, to_additive] lemma inv_eq : x⁻¹ = star := rfl

instance : comm_ring punit :=
by refine
{ nat_cast := λ _, punit.star,
  .. punit.comm_group,
  .. punit.add_comm_group,
  .. };
intros; exact subsingleton.elim _ _

instance : cancel_comm_monoid_with_zero punit :=
by refine
{ .. punit.comm_ring,
  .. };
intros; exact subsingleton.elim _ _

instance : normalized_gcd_monoid punit :=
by refine
{ gcd := λ _ _, star,
  lcm := λ _ _, star,
  norm_unit := λ x, 1,
  gcd_dvd_left := λ _ _, ⟨star, subsingleton.elim _ _⟩,
  gcd_dvd_right := λ _ _, ⟨star, subsingleton.elim _ _⟩,
  dvd_gcd := λ _ _ _ _ _, ⟨star, subsingleton.elim _ _⟩,
  gcd_mul_lcm := λ _ _, ⟨1, subsingleton.elim _ _⟩,
  .. };
intros; exact subsingleton.elim _ _

@[simp] lemma gcd_eq : gcd x y = star := rfl
@[simp] lemma lcm_eq : lcm x y = star := rfl
@[simp] lemma norm_unit_eq : norm_unit x = 1 := rfl

instance : complete_boolean_algebra punit :=
by refine
{ le := λ _ _, true,
  le_antisymm := λ _ _ _ _, subsingleton.elim _ _,
  lt := λ _ _, false,
  lt_iff_le_not_le := λ _ _, iff_of_false not_false (λ H, H.2 trivial),
  top := star,
  bot := star,
  sup := λ _ _, star,
  inf := λ _ _, star,
  Sup := λ _, star,
  Inf := λ _, star,
  compl := λ _, star,
  sdiff := λ _ _, star,
  .. };
intros; trivial <|> simp only [eq_iff_true_of_subsingleton]

@[simp] lemma top_eq : (⊤ : punit) = star := rfl
@[simp] lemma bot_eq : (⊥ : punit) = star := rfl
@[simp] lemma sup_eq : x ⊔ y = star := rfl
@[simp] lemma inf_eq : x ⊓ y = star := rfl
@[simp] lemma Sup_eq : Sup s = star := rfl
@[simp] lemma Inf_eq : Inf s = star := rfl
@[simp] lemma compl_eq : xᶜ = star := rfl
@[simp] lemma sdiff_eq : x \ y = star := rfl
@[simp] protected lemma le : x ≤ y := trivial
@[simp] lemma not_lt : ¬(x < y) := not_false

instance : canonically_ordered_add_monoid punit :=
by refine
{ exists_add_of_le := λ _ _ _, ⟨star, subsingleton.elim _ _⟩,
  .. punit.comm_ring, .. punit.complete_boolean_algebra, .. };
intros; trivial

instance : linear_ordered_cancel_add_comm_monoid punit :=
{ add_left_cancel := λ _ _ _ _, subsingleton.elim _ _,
  le_of_add_le_add_left := λ _ _ _ _, trivial,
  le_total := λ _ _, or.inl trivial,
  decidable_le := λ _ _, decidable.true,
  decidable_eq := punit.decidable_eq,
  decidable_lt := λ _ _, decidable.false,
  .. punit.canonically_ordered_add_monoid }

instance : has_scalar R punit :=
{ smul := λ _ _, star }

@[simp] lemma smul_eq (r : R) : r • y = star := rfl

instance : is_central_scalar R punit := ⟨λ _ _, rfl⟩

instance : smul_comm_class R S punit := ⟨λ _ _ _, subsingleton.elim _ _⟩

instance [has_scalar R S] : is_scalar_tower R S punit := ⟨λ _ _ _, subsingleton.elim _ _⟩

instance [has_zero R] : smul_with_zero R punit :=
by refine { ..punit.has_scalar, .. };
intros; exact subsingleton.elim _ _

instance [monoid R] : mul_action R punit :=
by refine { ..punit.has_scalar, .. };
intros; exact subsingleton.elim _ _

instance [monoid R] : distrib_mul_action R punit :=
by refine { ..punit.mul_action, .. };
intros; exact subsingleton.elim _ _

instance [monoid R] : mul_distrib_mul_action R punit :=
by refine { ..punit.mul_action, .. };
intros; exact subsingleton.elim _ _

instance [semiring R] : mul_semiring_action R punit :=
{ ..punit.distrib_mul_action, ..punit.mul_distrib_mul_action }

instance [monoid_with_zero R] : mul_action_with_zero R punit :=
{ .. punit.mul_action, .. punit.smul_with_zero }

instance [semiring R] : module R punit :=
by refine { .. punit.distrib_mul_action, .. };
intros; exact subsingleton.elim _ _

end punit


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.alias
import Mathlib.tactic.lint.default
import Mathlib.PostPort

universes u u_1

namespace Mathlib

/-!
# Lemmas about inequalities

This file contains some lemmas about `≤`/`≥`/`<`/`>`, and `cmp`.

* We simplify `a ≥ b` and `a > b` to `b ≤ a` and `b < a`, respectively. This way we can formulate
  all lemmas using `≤`/`<` avoiding duplication.

* In some cases we introduce dot syntax aliases so that, e.g., from
  `(hab : a ≤ b) (hbc : b ≤ c) (hbc' : b < c)` one can prove `hab.trans hbc : a ≤ c` and
  `hab.trans_lt hbc' : a < c`.
-/

theorem has_le.le.trans {α : Type u} [preorder α] {a : α} {b : α} {c : α} : a ≤ b → b ≤ c → a ≤ c :=
  le_trans

theorem has_le.le.trans_lt {α : Type u} [preorder α] {a : α} {b : α} {c : α} : a ≤ b → b < c → a < c :=
  lt_of_le_of_lt

theorem has_le.le.antisymm {α : Type u} [partial_order α] {a : α} {b : α} : a ≤ b → b ≤ a → a = b :=
  le_antisymm

theorem has_le.le.lt_of_ne {α : Type u} [partial_order α] {a : α} {b : α} : a ≤ b → a ≠ b → a < b :=
  lt_of_le_of_ne

theorem has_le.le.lt_of_not_le {α : Type u} [preorder α] {a : α} {b : α} : a ≤ b → ¬b ≤ a → a < b :=
  lt_of_le_not_le

theorem has_le.le.lt_or_eq {α : Type u} [partial_order α] {a : α} {b : α} : a ≤ b → a < b ∨ a = b :=
  lt_or_eq_of_le

theorem has_lt.lt.le {α : Type u} [preorder α] {a : α} {b : α} : a < b → a ≤ b :=
  le_of_lt

theorem has_lt.lt.trans {α : Type u} [preorder α] {a : α} {b : α} {c : α} : a < b → b < c → a < c :=
  lt_trans

theorem has_lt.lt.trans_le {α : Type u} [preorder α] {a : α} {b : α} {c : α} : a < b → b ≤ c → a < c :=
  lt_of_lt_of_le

theorem has_lt.lt.ne {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) : a ≠ b :=
  ne_of_lt

theorem has_lt.lt.not_lt {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) : ¬b < a :=
  lt_asymm

theorem eq.le {α : Type u} [preorder α] {a : α} {b : α} : a = b → a ≤ b :=
  le_of_eq

/-- A version of `le_refl` where the argument is implicit -/
theorem le_rfl {α : Type u} [preorder α] {x : α} : x ≤ x :=
  le_refl x

namespace eq

/--
If `x = y` then `y ≤ x`. Note: this lemma uses `y ≤ x` instead of `x ≥ y`,
because `le` is used almost exclusively in mathlib.
-/
protected theorem ge {α : Type u} [preorder α] {x : α} {y : α} (h : x = y) : y ≤ x :=
  le (Eq.symm h)

end eq

theorem eq.trans_le {α : Type u} [preorder α] {x : α} {y : α} {z : α} (h1 : x = y) (h2 : y ≤ z) : x ≤ z :=
  has_le.le.trans (eq.le h1) h2

namespace has_le.le

protected theorem ge {α : Type u} [HasLessEq α] {x : α} {y : α} (h : x ≤ y) : y ≥ x :=
  h

theorem trans_eq {α : Type u} [preorder α] {x : α} {y : α} {z : α} (h1 : x ≤ y) (h2 : y = z) : x ≤ z :=
  trans h1 (eq.le h2)

theorem lt_iff_ne {α : Type u} [partial_order α] {x : α} {y : α} (h : x ≤ y) : x < y ↔ x ≠ y :=
  { mp := fun (h : x < y) => has_lt.lt.ne h, mpr := lt_of_ne h }

theorem le_iff_eq {α : Type u} [partial_order α] {x : α} {y : α} (h : x ≤ y) : y ≤ x ↔ y = x :=
  { mp := fun (h' : y ≤ x) => antisymm h' h, mpr := eq.le }

theorem lt_or_le {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) (c : α) : a < c ∨ c ≤ b :=
  or.imp id (fun (hc : a ≥ c) => le_trans hc h) (lt_or_ge a c)

theorem le_or_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) (c : α) : a ≤ c ∨ c < b :=
  or.imp id (fun (hc : a > c) => lt_of_lt_of_le hc h) (le_or_gt a c)

theorem le_or_le {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) (c : α) : a ≤ c ∨ c ≤ b :=
  or.elim (le_or_lt h c) Or.inl fun (h : c < b) => Or.inr (le_of_lt h)

end has_le.le

namespace has_lt.lt

protected theorem gt {α : Type u} [HasLess α] {x : α} {y : α} (h : x < y) : y > x :=
  h

protected theorem false {α : Type u} [preorder α] {x : α} : x < x → False :=
  lt_irrefl x

theorem ne' {α : Type u} [preorder α] {x : α} {y : α} (h : x < y) : y ≠ x :=
  ne.symm (ne h)

theorem lt_or_lt {α : Type u} [linear_order α] {x : α} {y : α} (h : x < y) (z : α) : x < z ∨ z < y :=
  or.elim (lt_or_ge z y) Or.inr fun (hz : z ≥ y) => Or.inl (trans_le h hz)

end has_lt.lt

namespace ge

end ge

protected theorem ge.le {α : Type u} [HasLessEq α] {x : α} {y : α} (h : x ≥ y) : y ≤ x :=
  h

namespace gt

end gt

protected theorem gt.lt {α : Type u} [HasLess α] {x : α} {y : α} (h : x > y) : y < x :=
  h

theorem ge_of_eq {α : Type u} [preorder α] {a : α} {b : α} (h : a = b) : a ≥ b :=
  eq.ge h

@[simp] theorem ge_iff_le {α : Type u} [preorder α] {a : α} {b : α} : a ≥ b ↔ b ≤ a :=
  iff.rfl

@[simp] theorem gt_iff_lt {α : Type u} [preorder α] {a : α} {b : α} : a > b ↔ b < a :=
  iff.rfl

theorem not_le_of_lt {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) : ¬b ≤ a :=
  and.right (le_not_le_of_lt h)

theorem has_lt.lt.not_le {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) : ¬b ≤ a :=
  not_le_of_lt

theorem not_lt_of_le {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : ¬b < a :=
  fun (ᾰ : b < a) => idRhs False (has_lt.lt.not_le ᾰ h)

theorem has_le.le.not_lt {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : ¬b < a :=
  not_lt_of_le

theorem le_iff_eq_or_lt {α : Type u} [partial_order α] {a : α} {b : α} : a ≤ b ↔ a = b ∨ a < b :=
  iff.trans le_iff_lt_or_eq or.comm

theorem lt_iff_le_and_ne {α : Type u} [partial_order α] {a : α} {b : α} : a < b ↔ a ≤ b ∧ a ≠ b := sorry

theorem eq_iff_le_not_lt {α : Type u} [partial_order α] {a : α} {b : α} : a = b ↔ a ≤ b ∧ ¬a < b := sorry

theorem eq_or_lt_of_le {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≤ b) : a = b ∨ a < b :=
  or.symm (has_le.le.lt_or_eq h)

theorem has_le.le.eq_or_lt {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≤ b) : a = b ∨ a < b :=
  eq_or_lt_of_le

theorem ne.le_iff_lt {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≠ b) : a ≤ b ↔ a < b :=
  { mp := fun (h' : a ≤ b) => lt_of_le_of_ne h' h, mpr := fun (h : a < b) => has_lt.lt.le h }

@[simp] theorem ne_iff_lt_iff_le {α : Type u} [partial_order α] {a : α} {b : α} : a ≠ b ↔ a < b ↔ a ≤ b :=
  { mp := fun (h : a ≠ b ↔ a < b) => classical.by_cases le_of_eq (le_of_lt ∘ iff.mp h),
    mpr := fun (h : a ≤ b) => { mp := lt_of_le_of_ne h, mpr := ne_of_lt } }

theorem lt_of_not_ge' {α : Type u} [linear_order α] {a : α} {b : α} (h : ¬b ≤ a) : a < b :=
  has_le.le.lt_of_not_le (or.resolve_right (le_total a b) h) h

theorem lt_iff_not_ge' {α : Type u} [linear_order α] {x : α} {y : α} : x < y ↔ ¬y ≤ x :=
  { mp := not_le_of_gt, mpr := lt_of_not_ge' }

theorem le_of_not_lt {α : Type u} [linear_order α] {a : α} {b : α} : ¬a < b → b ≤ a :=
  iff.mp not_lt

theorem lt_or_le {α : Type u} [linear_order α] (a : α) (b : α) : a < b ∨ b ≤ a :=
  lt_or_ge

theorem le_or_lt {α : Type u} [linear_order α] (a : α) (b : α) : a ≤ b ∨ b < a :=
  le_or_gt

theorem ne.lt_or_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≠ b) : a < b ∨ b < a :=
  lt_or_gt_of_ne h

theorem not_lt_iff_eq_or_lt {α : Type u} [linear_order α] {a : α} {b : α} : ¬a < b ↔ a = b ∨ b < a :=
  iff.trans not_lt (iff.trans le_iff_eq_or_lt (or_congr eq_comm iff.rfl))

theorem exists_ge_of_linear {α : Type u} [linear_order α] (a : α) (b : α) : ∃ (c : α), a ≤ c ∧ b ≤ c := sorry

theorem lt_imp_lt_of_le_imp_le {α : Type u} {β : Type u_1} [linear_order α] [preorder β] {a : α} {b : α} {c : β} {d : β} (H : a ≤ b → c ≤ d) (h : d < c) : b < a :=
  lt_of_not_ge' fun (h' : a ≤ b) => has_le.le.not_lt (H h') h

theorem le_imp_le_of_lt_imp_lt {α : Type u} {β : Type u_1} [preorder α] [linear_order β] {a : α} {b : α} {c : β} {d : β} (H : d < c → b < a) (h : a ≤ b) : c ≤ d :=
  le_of_not_gt fun (h' : c > d) => has_lt.lt.not_le (H h') h

theorem le_imp_le_iff_lt_imp_lt {α : Type u} {β : Type u_1} [linear_order α] [linear_order β] {a : α} {b : α} {c : β} {d : β} : a ≤ b → c ≤ d ↔ d < c → b < a :=
  { mp := lt_imp_lt_of_le_imp_le, mpr := le_imp_le_of_lt_imp_lt }

theorem lt_iff_lt_of_le_iff_le' {α : Type u} {β : Type u_1} [preorder α] [preorder β] {a : α} {b : α} {c : β} {d : β} (H : a ≤ b ↔ c ≤ d) (H' : b ≤ a ↔ d ≤ c) : b < a ↔ d < c :=
  iff.trans lt_iff_le_not_le (iff.trans (and_congr H' (not_congr H)) (iff.symm lt_iff_le_not_le))

theorem lt_iff_lt_of_le_iff_le {α : Type u} {β : Type u_1} [linear_order α] [linear_order β] {a : α} {b : α} {c : β} {d : β} (H : a ≤ b ↔ c ≤ d) : b < a ↔ d < c :=
  iff.trans (iff.symm not_le) (iff.trans (not_congr H) not_le)

theorem le_iff_le_iff_lt_iff_lt {α : Type u} {β : Type u_1} [linear_order α] [linear_order β] {a : α} {b : α} {c : β} {d : β} : a ≤ b ↔ c ≤ d ↔ (b < a ↔ d < c) :=
  { mp := lt_iff_lt_of_le_iff_le,
    mpr := fun (H : b < a ↔ d < c) => iff.trans (iff.symm not_lt) (iff.trans (not_congr H) not_lt) }

theorem eq_of_forall_le_iff {α : Type u} [partial_order α] {a : α} {b : α} (H : ∀ (c : α), c ≤ a ↔ c ≤ b) : a = b :=
  le_antisymm (iff.mp (H a) (le_refl a)) (iff.mpr (H b) (le_refl b))

theorem le_of_forall_le {α : Type u} [preorder α] {a : α} {b : α} (H : ∀ (c : α), c ≤ a → c ≤ b) : a ≤ b :=
  H a (le_refl a)

theorem le_of_forall_le' {α : Type u} [preorder α] {a : α} {b : α} (H : ∀ (c : α), a ≤ c → b ≤ c) : b ≤ a :=
  H a (le_refl a)

theorem le_of_forall_lt {α : Type u} [linear_order α] {a : α} {b : α} (H : ∀ (c : α), c < a → c < b) : a ≤ b :=
  le_of_not_lt fun (h : b < a) => lt_irrefl b (H b h)

theorem forall_lt_iff_le {α : Type u} [linear_order α] {a : α} {b : α} : (∀ {c : α}, c < a → c < b) ↔ a ≤ b :=
  { mp := le_of_forall_lt, mpr := fun (h : a ≤ b) (c : α) (hca : c < a) => lt_of_lt_of_le hca h }

theorem le_of_forall_lt' {α : Type u} [linear_order α] {a : α} {b : α} (H : ∀ (c : α), a < c → b < c) : b ≤ a :=
  le_of_not_lt fun (h : a < b) => lt_irrefl b (H b h)

theorem forall_lt_iff_le' {α : Type u} [linear_order α] {a : α} {b : α} : (∀ {c : α}, a < c → b < c) ↔ b ≤ a :=
  { mp := le_of_forall_lt', mpr := fun (h : b ≤ a) (c : α) (hac : a < c) => lt_of_le_of_lt h hac }

theorem eq_of_forall_ge_iff {α : Type u} [partial_order α] {a : α} {b : α} (H : ∀ (c : α), a ≤ c ↔ b ≤ c) : a = b :=
  le_antisymm (iff.mpr (H b) (le_refl b)) (iff.mp (H a) (le_refl a))

/-- monotonicity of `≤` with respect to `→` -/
theorem le_implies_le_of_le_of_le {α : Type u} {a : α} {b : α} {c : α} {d : α} [preorder α] (h₀ : c ≤ a) (h₁ : b ≤ d) : a ≤ b → c ≤ d :=
  fun (h₂ : a ≤ b) => le_trans (le_trans h₀ h₂) h₁

namespace decidable

-- See Note [decidable namespace]

theorem le_imp_le_iff_lt_imp_lt {α : Type u} {β : Type u_1} [linear_order α] [linear_order β] {a : α} {b : α} {c : β} {d : β} : a ≤ b → c ≤ d ↔ d < c → b < a :=
  { mp := lt_imp_lt_of_le_imp_le, mpr := le_imp_le_of_lt_imp_lt }

-- See Note [decidable namespace]

theorem le_iff_le_iff_lt_iff_lt {α : Type u} {β : Type u_1} [linear_order α] [linear_order β] {a : α} {b : α} {c : β} {d : β} : a ≤ b ↔ c ≤ d ↔ (b < a ↔ d < c) :=
  { mp := lt_iff_lt_of_le_iff_le,
    mpr := fun (H : b < a ↔ d < c) => iff.trans (iff.symm not_lt) (iff.trans (not_congr H) not_lt) }

end decidable

/-- Like `cmp`, but uses a `≤` on the type instead of `<`. Given two elements
`x` and `y`, returns a three-way comparison result `ordering`. -/
def cmp_le {α : Type u_1} [HasLessEq α] [DecidableRel LessEq] (x : α) (y : α) : ordering :=
  ite (x ≤ y) (ite (y ≤ x) ordering.eq ordering.lt) ordering.gt

theorem cmp_le_swap {α : Type u_1} [HasLessEq α] [is_total α LessEq] [DecidableRel LessEq] (x : α) (y : α) : ordering.swap (cmp_le x y) = cmp_le y x := sorry

theorem cmp_le_eq_cmp {α : Type u_1} [preorder α] [is_total α LessEq] [DecidableRel LessEq] [DecidableRel Less] (x : α) (y : α) : cmp_le x y = cmp x y := sorry

namespace ordering

/-- `compares o a b` means that `a` and `b` have the ordering relation
  `o` between them, assuming that the relation `a < b` is defined -/
@[simp] def compares {α : Type u} [HasLess α] : ordering → α → α → Prop :=
  sorry

theorem compares_swap {α : Type u} [HasLess α] {a : α} {b : α} {o : ordering} : compares (swap o) a b ↔ compares o b a :=
  ordering.cases_on o iff.rfl eq_comm iff.rfl

theorem compares.of_swap {α : Type u} [HasLess α] {a : α} {b : α} {o : ordering} : compares (swap o) a b → compares o b a :=
  iff.mp compares_swap

theorem swap_eq_iff_eq_swap {o : ordering} {o' : ordering} : swap o = o' ↔ o = swap o' := sorry

theorem compares.eq_lt {α : Type u} [preorder α] {o : ordering} {a : α} {b : α} : compares o a b → (o = lt ↔ a < b) := sorry

theorem compares.ne_lt {α : Type u} [preorder α] {o : ordering} {a : α} {b : α} : compares o a b → (o ≠ lt ↔ b ≤ a) := sorry

theorem compares.eq_eq {α : Type u} [preorder α] {o : ordering} {a : α} {b : α} : compares o a b → (o = eq ↔ a = b) := sorry

theorem compares.eq_gt {α : Type u} [preorder α] {o : ordering} {a : α} {b : α} (h : compares o a b) : o = gt ↔ b < a :=
  iff.trans (iff.symm swap_eq_iff_eq_swap) (compares.eq_lt (compares.swap h))

theorem compares.ne_gt {α : Type u} [preorder α] {o : ordering} {a : α} {b : α} (h : compares o a b) : o ≠ gt ↔ a ≤ b :=
  iff.trans (not_congr (iff.symm swap_eq_iff_eq_swap)) (compares.ne_lt (compares.swap h))

theorem compares.le_total {α : Type u} [preorder α] {a : α} {b : α} {o : ordering} : compares o a b → a ≤ b ∨ b ≤ a := sorry

theorem compares.le_antisymm {α : Type u} [preorder α] {a : α} {b : α} {o : ordering} : compares o a b → a ≤ b → b ≤ a → a = b := sorry

theorem compares.inj {α : Type u} [preorder α] {o₁ : ordering} {o₂ : ordering} {a : α} {b : α} : compares o₁ a b → compares o₂ a b → o₁ = o₂ := sorry

theorem compares_iff_of_compares_impl {α : Type u} {β : Type u_1} [linear_order α] [preorder β] {a : α} {b : α} {a' : β} {b' : β} (h : ∀ {o : ordering}, compares o a b → compares o a' b') (o : ordering) : compares o a b ↔ compares o a' b' := sorry

theorem swap_or_else (o₁ : ordering) (o₂ : ordering) : swap (or_else o₁ o₂) = or_else (swap o₁) (swap o₂) :=
  ordering.cases_on o₁ (Eq.refl (swap (or_else lt o₂))) (Eq.refl (swap (or_else eq o₂))) (Eq.refl (swap (or_else gt o₂)))

theorem or_else_eq_lt (o₁ : ordering) (o₂ : ordering) : or_else o₁ o₂ = lt ↔ o₁ = lt ∨ o₁ = eq ∧ o₂ = lt :=
  ordering.cases_on o₁ (ordering.cases_on o₂ (of_as_true trivial) (of_as_true trivial) (of_as_true trivial))
    (ordering.cases_on o₂ (of_as_true trivial) (of_as_true trivial) (of_as_true trivial))
    (ordering.cases_on o₂ (of_as_true trivial) (of_as_true trivial) (of_as_true trivial))

end ordering

theorem cmp_compares {α : Type u} [linear_order α] (a : α) (b : α) : ordering.compares (cmp a b) a b := sorry

theorem cmp_swap {α : Type u} [preorder α] [DecidableRel Less] (a : α) (b : α) : ordering.swap (cmp a b) = cmp b a := sorry

/-- Generate a linear order structure from a preorder and `cmp` function. -/
def linear_order_of_compares {α : Type u} [preorder α] (cmp : α → α → ordering) (h : ∀ (a b : α), ordering.compares (cmp a b) a b) : linear_order α :=
  linear_order.mk preorder.le preorder.lt preorder.le_refl preorder.le_trans sorry sorry
    (fun (a b : α) => decidable_of_iff (cmp a b ≠ ordering.gt) sorry)
    (fun (a b : α) => decidable_of_iff (cmp a b = ordering.eq) sorry)
    fun (a b : α) => decidable_of_iff (cmp a b = ordering.lt) sorry

@[simp] theorem cmp_eq_lt_iff {α : Type u} [linear_order α] (x : α) (y : α) : cmp x y = ordering.lt ↔ x < y :=
  ordering.compares.eq_lt (cmp_compares x y)

@[simp] theorem cmp_eq_eq_iff {α : Type u} [linear_order α] (x : α) (y : α) : cmp x y = ordering.eq ↔ x = y :=
  ordering.compares.eq_eq (cmp_compares x y)

@[simp] theorem cmp_eq_gt_iff {α : Type u} [linear_order α] (x : α) (y : α) : cmp x y = ordering.gt ↔ y < x :=
  ordering.compares.eq_gt (cmp_compares x y)

@[simp] theorem cmp_self_eq_eq {α : Type u} [linear_order α] (x : α) : cmp x x = ordering.eq :=
  eq.mpr (id (Eq._oldrec (Eq.refl (cmp x x = ordering.eq)) (propext (cmp_eq_eq_iff x x)))) (Eq.refl x)

theorem cmp_eq_cmp_symm {α : Type u} [linear_order α] {x : α} {y : α} {β : Type u_1} [linear_order β] {x' : β} {y' : β} : cmp x y = cmp x' y' ↔ cmp y x = cmp y' x' := sorry

theorem lt_iff_lt_of_cmp_eq_cmp {α : Type u} [linear_order α] {x : α} {y : α} {β : Type u_1} [linear_order β] {x' : β} {y' : β} (h : cmp x y = cmp x' y') : x < y ↔ x' < y' := sorry

theorem le_iff_le_of_cmp_eq_cmp {α : Type u} [linear_order α] {x : α} {y : α} {β : Type u_1} [linear_order β] {x' : β} {y' : β} (h : cmp x y = cmp x' y') : x ≤ y ↔ x' ≤ y' := sorry


/-
Copyright (c) 2022 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.CategoryTheory.Category.Basic
import Mathlib.Util.AddRelatedDecl
import Mathlib.Lean.Meta.Simp

/-!
# The `reasoc` attribute

Adding `@[reassoc]` to a lemma named `F` of shape `∀ .., f = g`,
where `f g : X ⟶ Y` in some category
will create a new lemmas named `F_assoc` of shape
`∀ .. {Z : C} (h : Y ⟶ Z), f ≫ h = g ≫ h`
but with the conclusions simplified used the axioms for a category
(`Category.comp_id`, `Category.id_comp`, and `Category.assoc`).

This is useful for generating lemmas which the simplifier can use even on expressions
that are already right associated.

There is also a term elaborator `reassoc_of% t` for use within proofs.
-/

open Lean Meta Elab Tactic
open Mathlib.Tactic

namespace CategoryTheory

variable {C : Type _} [Category C]

/-- A variant of `eq_whisker` with a more convenient argument order for use in tactics.  -/
theorem eq_whisker' {X Y : C} {f g : X ⟶ Y} (w : f = g) {Z : C} (h : Y ⟶ Z) :
    f ≫ h = g ≫ h := by rw [w]

/-- Simplify an expression using only the axioms of a category. -/
def categorySimp (e : Expr) : MetaM Simp.Result :=
  simpOnlyNames [``Category.comp_id, ``Category.id_comp, ``Category.assoc] e
    (config := { decide := false })

/--
Given an equation `f = g` between morphisms `X ⟶ Y` in a category (possibly after a `∀` binder),
produce the equation `∀ {Z} (h : Y ⟶ Z), f ≫ h = g ≫ h`,
but with compositions fully right associated and identities removed.
-/
def reassocExpr (e : Expr) : MetaM Expr := do
  mapForallTelescope (fun e => do simpType categorySimp (← mkAppM ``eq_whisker' #[e])) e

/-- Syntax for the `reassoc` attribute -/
syntax (name := reassoc) "reassoc" ("(" &"attr" ":=" Parser.Term.attrInstance,* ")")? : attr

initialize registerBuiltinAttribute {
  name := `reassoc
  descr := ""
  applicationTime := .afterCompilation
  add := fun src ref kind => match ref with
  | `(attr| reassoc $[(attr := $stx?,*)]?) => MetaM.run' do
    if (kind != AttributeKind.global) then
      throwError "`reassoc` can only be used as a global attribute"
    addRelatedDecl src "_assoc" ref stx? fun type value levels => do
      pure (← reassocExpr (← mkExpectedTypeHint value type), levels)
  | _ => throwUnsupportedSyntax }

open Term in
/--
`reassoc_of% t`, where `t` is
an equation `f = g` between morphisms `X ⟶ Y` in a category (possibly after a `∀` binder),
produce the equation `∀ {Z} (h : Y ⟶ Z), f ≫ h = g ≫ h`,
but with compositions fully right associated and identities removed.
-/
elab "reassoc_of% " t:term : term => do
  reassocExpr (← elabTerm t none)

end CategoryTheory


import mathlib.measure_theory.measure.measure_space
import probability.independence

open measure_theory measurable_space set
open_locale big_operators

namespace probability_theory
variables {Ω ι : Type*} [measurable_space Ω] {π : ι → set (set Ω)} {μ : measure Ω}

lemma Indep_sets.meas_Inter [fintype ι] (h : Indep_sets π μ) {f : ι → set Ω} (hf : ∀ i, f i ∈ π i) :
  μ (⋂ i, f i) = ∏ i, μ (f i) :=
by simp [← h _ (λ i _, hf _)]

lemma Indep_comap_iff {s : ι → set Ω} :
  Indep (λ i, measurable_space.comap (∈ s i) ⊤) μ ↔ Indep_set s μ :=
by { simp_rw ←generate_from_singleton, refl }

alias Indep_comap_iff ↔ _ Indep_set.Indep_comap

lemma Indep_sets_singleton_iff {s : ι → set Ω} {μ : measure Ω} :
  Indep_sets (λ i, {s i}) μ ↔ ∀ t, μ (⋂ i ∈ t, s i) = ∏ i in t, μ (s i) :=
forall_congr $ λ t,
  ⟨λ h, h $ λ _ _, mem_singleton _,
  λ h f hf, begin
    refine eq.trans _ (h.trans $ finset.prod_congr rfl $ λ i hi, congr_arg _ $ (hf i hi).symm),
    rw Inter₂_congr hf,
  end⟩

lemma Indep_set_iff_Indep_sets_singleton {s : ι → set Ω} (hs : ∀ i, measurable_set (s i))
  (μ : measure Ω . volume_tac) [is_probability_measure μ] :
  Indep_set s μ ↔ Indep_sets (λ i, {s i}) μ :=
⟨Indep.Indep_sets $ λ _, rfl, Indep_sets.Indep _
  (λ i, generate_from_le $ by { rintro t (rfl : t = s i), exact hs _}) _
    (λ _, is_pi_system.singleton $ s _) $ λ _, rfl⟩

variables [is_probability_measure μ]

lemma Indep_set_iff_measure_Inter_eq_prod {s : ι → set Ω} (hs : ∀ i, measurable_set (s i)) :
  Indep_set s μ ↔ ∀ t, μ (⋂ i ∈ t, s i) = ∏ i in t, μ (s i) :=
(Indep_set_iff_Indep_sets_singleton hs μ).trans Indep_sets_singleton_iff

lemma Indep_sets.Indep_set_of_mem {s : ι → set Ω} {S : ι → set (set Ω)} (hs : ∀ i, s i ∈ S i)
  (hs_meas : ∀ i, measurable_set (s i)) (h_indep : Indep_sets S μ) :
  Indep_set s μ :=
(Indep_set_iff_measure_Inter_eq_prod hs_meas).2 $ λ t, h_indep _ $ λ i _, hs _

end probability_theory