/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
import data.option.defs
import logic.basic
import tactic.cache
import data.rbmap.basic
import data.rbtree.default_lt

/-!
## Definitions on lists

This file contains various definitions on lists. It does not contain
proofs about these definitions, those are contained in other files in `data/list`
-/

namespace list

open function nat
universes u v w x
variables {α β γ δ ε ζ : Type*}
instance [decidable_eq α] : has_sdiff (list α) :=
⟨ list.diff ⟩

/-- Split a list at an index.

     split_at 2 [a, b, c] = ([a, b], [c]) -/
def split_at : ℕ → list α → list α × list α
| 0        a         := ([], a)
| (succ n) []        := ([], [])
| (succ n) (x :: xs) := let (l, r) := split_at n xs in (x :: l, r)

/-- An auxiliary function for `split_on_p`. -/
def split_on_p_aux {α : Type u} (P : α → Prop) [decidable_pred P] :
  list α → (list α → list α) → list (list α)
| [] f       := [f []]
| (h :: t) f :=
  if P h then f [] :: split_on_p_aux t id
  else split_on_p_aux t (λ l, f (h :: l))

/-- Split a list at every element satisfying a predicate. -/
def split_on_p {α : Type u} (P : α → Prop) [decidable_pred P] (l : list α) : list (list α) :=
split_on_p_aux P l id

/-- Split a list at every occurrence of an element.

    [1,1,2,3,2,4,4].split_on 2 = [[1,1],[3],[4,4]] -/
def split_on {α : Type u} [decidable_eq α] (a : α) (as : list α) : list (list α) :=
as.split_on_p (=a)

/-- Concatenate an element at the end of a list.

     concat [a, b] c = [a, b, c] -/
@[simp] def concat : list α → α → list α
| []     a := [a]
| (b::l) a := b :: concat l a

/-- `head' xs` returns the first element of `xs` if `xs` is non-empty;
it returns `none` otherwise -/
@[simp] def head' : list α → option α
| []       := none
| (a :: l) := some a

/-- Convert a list into an array (whose length is the length of `l`). -/
def to_array (l : list α) : array l.length α :=
{data := λ v, l.nth_le v.1 v.2}

/-- "inhabited" `nth` function: returns `default` instead of `none` in the case
  that the index is out of bounds. -/
@[simp] def inth [h : inhabited α] (l : list α) (n : nat) : α := (nth l n).iget

/-- Apply a function to the nth tail of `l`. Returns the input without
  using `f` if the index is larger than the length of the list.

     modify_nth_tail f 2 [a, b, c] = [a, b] ++ f [c] -/
@[simp] def modify_nth_tail (f : list α → list α) : ℕ → list α → list α
| 0     l      := f l
| (n+1) []     := []
| (n+1) (a::l) := a :: modify_nth_tail n l

/-- Apply `f` to the head of the list, if it exists. -/
@[simp] def modify_head (f : α → α) : list α → list α
| []     := []
| (a::l) := f a :: l

/-- Apply `f` to the nth element of the list, if it exists. -/
def modify_nth (f : α → α) : ℕ → list α → list α :=
modify_nth_tail (modify_head f)

/-- Apply `f` to the last element of `l`, if it exists. -/
@[simp] def modify_last (f : α → α) : list α → list α
| [] := []
| [x] := [f x]
| (x :: xs) := x :: modify_last xs

/-- `insert_nth n a l` inserts `a` into the list `l` after the first `n` elements of `l`
 `insert_nth 2 1 [1, 2, 3, 4] = [1, 2, 1, 3, 4]`-/
def insert_nth (n : ℕ) (a : α) : list α → list α := modify_nth_tail (list.cons a) n

section take'
variable [inhabited α]

/-- Take `n` elements from a list `l`. If `l` has less than `n` elements, append `n - length l`
elements `default α`. -/
def take' : ∀ n, list α → list α
| 0     l := []
| (n+1) l := l.head :: take' n l.tail

end take'

/-- Get the longest initial segment of the list whose members all satisfy `p`.

     take_while (λ x, x < 3) [0, 2, 5, 1] = [0, 2] -/
def take_while (p : α → Prop) [decidable_pred p] : list α → list α
| []     := []
| (a::l) := if p a then a :: take_while l else []

/-- Fold a function `f` over the list from the left, returning the list
  of partial results.

     scanl (+) 0 [1, 2, 3] = [0, 1, 3, 6] -/
def scanl (f : α → β → α) : α → list β → list α
| a []     := [a]
| a (b::l) := a :: scanl (f a b) l

/-- Auxiliary definition used to define `scanr`. If `scanr_aux f b l = (b', l')`
then `scanr f b l = b' :: l'` -/
def scanr_aux (f : α → β → β) (b : β) : list α → β × list β
| []     := (b, [])
| (a::l) := let (b', l') := scanr_aux l in (f a b', b' :: l')

/-- Fold a function `f` over the list from the right, returning the list
  of partial results.

     scanr (+) 0 [1, 2, 3] = [6, 5, 3, 0] -/
def scanr (f : α → β → β) (b : β) (l : list α) : list β :=
let (b', l') := scanr_aux f b l in b' :: l'

/-- Product of a list.

     prod [a, b, c] = ((1 * a) * b) * c -/
def prod [has_mul α] [has_one α] : list α → α := foldl (*) 1

/-- Sum of a list.

     sum [a, b, c] = ((0 + a) + b) + c -/
-- Later this will be tagged with `to_additive`, but this can't be done yet because of import
-- dependencies.
def sum [has_add α] [has_zero α] : list α → α := foldl (+) 0

/-- The alternating sum of a list. -/
def alternating_sum {G : Type*} [has_zero G] [has_add G] [has_neg G] : list G → G
| [] := 0
| (g :: []) := g
| (g :: h :: t) := g + -h + alternating_sum t

/-- The alternating product of a list. -/
def alternating_prod {G : Type*} [has_one G] [has_mul G] [has_inv G] : list G → G
| [] := 1
| (g :: []) := g
| (g :: h :: t) := g * h⁻¹ * alternating_prod t

/-- Given a function `f : α → β ⊕ γ`, `partition_map f l` maps the list by `f`
  whilst partitioning the result it into a pair of lists, `list β × list γ`,
  partitioning the `sum.inl _` into the left list, and the `sum.inr _` into the right list.
  `partition_map (id : ℕ ⊕ ℕ → ℕ ⊕ ℕ) [inl 0, inr 1, inl 2] = ([0,2], [1])`    -/
def partition_map (f : α → β ⊕ γ) : list α → list β × list γ
| [] := ([],[])
| (x::xs) :=
match f x with
| (sum.inr r) := prod.map id (cons r) $ partition_map xs
| (sum.inl l) := prod.map (cons l) id $ partition_map xs
end

/-- `find p l` is the first element of `l` satisfying `p`, or `none` if no such
  element exists. -/
def find (p : α → Prop) [decidable_pred p] : list α → option α
| []     := none
| (a::l) := if p a then some a else find l

/-- `mfind tac l` returns the first element of `l` on which `tac` succeeds, and
fails otherwise. -/
def mfind {α} {m : Type u → Type v} [monad m] [alternative m] (tac : α → m punit) : list α → m α :=
list.mfirst $ λ a, tac a $> a

/-- `mbfind' p l` returns the first element `a` of `l` for which `p a` returns
true. `mbfind'` short-circuits, so `p` is not necessarily run on every `a` in
`l`. This is a monadic version of `list.find`. -/
def mbfind' {m : Type u → Type v} [monad m] {α : Type u} (p : α → m (ulift bool)) :
  list α → m (option α)
| [] := pure none
| (x :: xs) := do
  ⟨px⟩ ← p x,
  if px then pure (some x) else mbfind' xs

section

variables {m : Type → Type v} [monad m]

/-- A variant of `mbfind'` with more restrictive universe levels. -/
def mbfind {α} (p : α → m bool) (xs : list α) : m (option α) :=
xs.mbfind' (functor.map ulift.up ∘ p)

/-- `many p as` returns true iff `p` returns true for any element of `l`.
`many` short-circuits, so if `p` returns true for any element of `l`, later
elements are not checked. This is a monadic version of `list.any`. -/
-- Implementing this via `mbfind` would give us less universe polymorphism.
def many {α : Type u} (p : α → m bool) : list α → m bool
| [] := pure false
| (x :: xs) := do px ← p x, if px then pure tt else many xs

/-- `mall p as` returns true iff `p` returns true for all elements of `l`.
`mall` short-circuits, so if `p` returns false for any element of `l`, later
elements are not checked. This is a monadic version of `list.all`. -/
def mall {α : Type u} (p : α → m bool) (as : list α) : m bool :=
bnot <$> many (λ a, bnot <$> p a) as

/-- `mbor xs` runs the actions in `xs`, returning true if any of them returns
true. `mbor` short-circuits, so if an action returns true, later actions are
not run. This is a monadic version of `list.bor`. -/
def mbor : list (m bool) → m bool :=
many id

/-- `mband xs` runs the actions in `xs`, returning true if all of them return
true. `mband` short-circuits, so if an action returns false, later actions are
not run. This is a monadic version of `list.band`. -/
def mband : list (m bool) → m bool :=
mall id

end

/-- Auxiliary definition for `foldl_with_index`. -/
def foldl_with_index_aux (f : ℕ → α → β → α) : ℕ → α → list β → α
| _ a [] := a
| i a (b :: l) := foldl_with_index_aux (i + 1) (f i a b) l

/-- Fold a list from left to right as with `foldl`, but the combining function
also receives each element's index. -/
def foldl_with_index (f : ℕ → α → β → α) (a : α) (l : list β) : α :=
foldl_with_index_aux f 0 a l

/-- Auxiliary definition for `foldr_with_index`. -/
def foldr_with_index_aux (f : ℕ → α → β → β) : ℕ → β → list α → β
| _ b [] := b
| i b (a :: l) := f i a (foldr_with_index_aux (i + 1) b l)

/-- Fold a list from right to left as with `foldr`, but the combining function
also receives each element's index. -/
def foldr_with_index (f : ℕ → α → β → β) (b : β) (l : list α) : β :=
foldr_with_index_aux f 0 b l

/-- `find_indexes p l` is the list of indexes of elements of `l` that satisfy `p`. -/
def find_indexes (p : α → Prop) [decidable_pred p] (l : list α) : list nat :=
foldr_with_index (λ i a is, if p a then i :: is else is) [] l

/-- Returns the elements of `l` that satisfy `p` together with their indexes in
`l`. The returned list is ordered by index. -/
def indexes_values (p : α → Prop) [decidable_pred p] (l : list α) : list (ℕ × α) :=
foldr_with_index (λ i a l, if p a then (i , a) :: l else l) [] l

/-- `indexes_of a l` is the list of all indexes of `a` in `l`. For example:
```
indexes_of a [a, b, a, a] = [0, 2, 3]
```
-/
def indexes_of [decidable_eq α] (a : α) : list α → list nat := find_indexes (eq a)

section mfold_with_index

variables {m : Type v → Type w} [monad m]

/-- Monadic variant of `foldl_with_index`. -/
def mfoldl_with_index {α β} (f : ℕ → β → α → m β) (b : β) (as : list α) : m β :=
as.foldl_with_index (λ i ma b, do a ← ma, f i a b) (pure b)

/-- Monadic variant of `foldr_with_index`. -/
def mfoldr_with_index {α β} (f : ℕ → α → β → m β) (b : β) (as : list α) : m β :=
as.foldr_with_index (λ i a mb, do b ← mb, f i a b) (pure b)

end mfold_with_index

section mmap_with_index

variables {m : Type v → Type w} [applicative m]

/-- Auxiliary definition for `mmap_with_index`. -/
def mmap_with_index_aux {α β} (f : ℕ → α → m β) : ℕ → list α → m (list β)
| _ [] := pure []
| i (a :: as) := list.cons <$> f i a <*> mmap_with_index_aux (i + 1) as

/-- Applicative variant of `map_with_index`. -/
def mmap_with_index {α β} (f : ℕ → α → m β) (as : list α) : m (list β) :=
mmap_with_index_aux f 0 as

/-- Auxiliary definition for `mmap_with_index'`. -/
def mmap_with_index'_aux {α} (f : ℕ → α → m punit) : ℕ → list α → m punit
| _ [] := pure ⟨⟩
| i (a :: as) := f i a *> mmap_with_index'_aux (i + 1) as

/-- A variant of `mmap_with_index` specialised to applicative actions which
return `unit`. -/
def mmap_with_index' {α} (f : ℕ → α → m punit) (as : list α) : m punit :=
mmap_with_index'_aux f 0 as

end mmap_with_index

/-- `lookmap` is a combination of `lookup` and `filter_map`.
  `lookmap f l` will apply `f : α → option α` to each element of the list,
  replacing `a → b` at the first value `a` in the list such that `f a = some b`. -/
def lookmap (f : α → option α) : list α → list α
| []     := []
| (a::l) :=
  match f a with
  | some b := b :: l
  | none   := a :: lookmap l
  end

/-- `countp p l` is the number of elements of `l` that satisfy `p`. -/
def countp (p : α → Prop) [decidable_pred p] : list α → nat
| []      := 0
| (x::xs) := if p x then succ (countp xs) else countp xs

/-- `count a l` is the number of occurrences of `a` in `l`. -/
def count [decidable_eq α] (a : α) : list α → nat := countp (eq a)

/-- `is_prefix l₁ l₂`, or `l₁ <+: l₂`, means that `l₁` is a prefix of `l₂`,
  that is, `l₂` has the form `l₁ ++ t` for some `t`. -/
def is_prefix (l₁ : list α) (l₂ : list α) : Prop := ∃ t, l₁ ++ t = l₂

/-- `is_suffix l₁ l₂`, or `l₁ <:+ l₂`, means that `l₁` is a suffix of `l₂`,
  that is, `l₂` has the form `t ++ l₁` for some `t`. -/
def is_suffix (l₁ : list α) (l₂ : list α) : Prop := ∃ t, t ++ l₁ = l₂

/-- `is_infix l₁ l₂`, or `l₁ <:+: l₂`, means that `l₁` is a contiguous
  substring of `l₂`, that is, `l₂` has the form `s ++ l₁ ++ t` for some `s, t`. -/
def is_infix (l₁ : list α) (l₂ : list α) : Prop := ∃ s t, s ++ l₁ ++ t = l₂

infix ` <+: `:50 := is_prefix
infix ` <:+ `:50 := is_suffix
infix ` <:+: `:50 := is_infix

/-- `inits l` is the list of initial segments of `l`.

     inits [1, 2, 3] = [[], [1], [1, 2], [1, 2, 3]] -/
@[simp] def inits : list α → list (list α)
| []     := [[]]
| (a::l) := [] :: map (λt, a::t) (inits l)

/-- `tails l` is the list of terminal segments of `l`.

     tails [1, 2, 3] = [[1, 2, 3], [2, 3], [3], []] -/
@[simp] def tails : list α → list (list α)
| []     := [[]]
| (a::l) := (a::l) :: tails l

def sublists'_aux : list α → (list α → list β) → list (list β) → list (list β)
| []     f r := f [] :: r
| (a::l) f r := sublists'_aux l f (sublists'_aux l (f ∘ cons a) r)

/-- `sublists' l` is the list of all (non-contiguous) sublists of `l`.
  It differs from `sublists` only in the order of appearance of the sublists;
  `sublists'` uses the first element of the list as the MSB,
  `sublists` uses the first element of the list as the LSB.

     sublists' [1, 2, 3] = [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]] -/
def sublists' (l : list α) : list (list α) :=
sublists'_aux l id []

def sublists_aux : list α → (list α → list β → list β) → list β
| []     f := []
| (a::l) f := f [a] (sublists_aux l (λys r, f ys (f (a :: ys) r)))

/-- `sublists l` is the list of all (non-contiguous) sublists of `l`; cf. `sublists'`
  for a different ordering.

     sublists [1, 2, 3] = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]] -/
def sublists (l : list α) : list (list α) :=
[] :: sublists_aux l cons

def sublists_aux₁ : list α → (list α → list β) → list β
| []     f := []
| (a::l) f := f [a] ++ sublists_aux₁ l (λys, f ys ++ f (a :: ys))

section forall₂
variables {r : α → β → Prop} {p : γ → δ → Prop}

/-- `forall₂ R l₁ l₂` means that `l₁` and `l₂` have the same length,
  and whenever `a` is the nth element of `l₁`, and `b` is the nth element of `l₂`,
  then `R a b` is satisfied. -/
inductive forall₂ (R : α → β → Prop) : list α → list β → Prop
| nil : forall₂ [] []
| cons {a b l₁ l₂} : R a b → forall₂ l₁ l₂ → forall₂ (a::l₁) (b::l₂)

attribute [simp] forall₂.nil

end forall₂

/-- Auxiliary definition used to define `transpose`.
  `transpose_aux l L` takes each element of `l` and appends it to the start of
  each element of `L`.

  `transpose_aux [a, b, c] [l₁, l₂, l₃] = [a::l₁, b::l₂, c::l₃]` -/
def transpose_aux : list α → list (list α) → list (list α)
| []     ls      := ls
| (a::i) []      := [a] :: transpose_aux i []
| (a::i) (l::ls) := (a::l) :: transpose_aux i ls

/-- transpose of a list of lists, treated as a matrix.

     transpose [[1, 2], [3, 4], [5, 6]] = [[1, 3, 5], [2, 4, 6]] -/
def transpose : list (list α) → list (list α)
| []      := []
| (l::ls) := transpose_aux l (transpose ls)

/-- List of all sections through a list of lists. A section
  of `[L₁, L₂, ..., Lₙ]` is a list whose first element comes from
  `L₁`, whose second element comes from `L₂`, and so on. -/
def sections : list (list α) → list (list α)
| []     := [[]]
| (l::L) := bind (sections L) $ λ s, map (λ a, a::s) l

section permutations

/-- An auxiliary function for defining `permutations`. `permutations_aux2 t ts r ys f` is equal to
`(ys ++ ts, (insert_left ys t ts).map f ++ r)`, where `insert_left ys t ts` (not explicitly
defined) is the list of lists of the form `insert_nth n t (ys ++ ts)` for `0 ≤ n < length ys`.

    permutations_aux2 10 [4, 5, 6] [] [1, 2, 3] id =
      ([1, 2, 3, 4, 5, 6],
       [[10, 1, 2, 3, 4, 5, 6],
        [1, 10, 2, 3, 4, 5, 6],
        [1, 2, 10, 3, 4, 5, 6]]) -/
def permutations_aux2 (t : α) (ts : list α) (r : list β) : list α → (list α → β) → list α × list β
| []      f := (ts, r)
| (y::ys) f := let (us, zs) := permutations_aux2 ys (λx : list α, f (y::x)) in
               (y :: us, f (t :: y :: us) :: zs)

private def meas : (Σ'_:list α, list α) → ℕ × ℕ | ⟨l, i⟩ := (length l + length i, length l)

local infix ` ≺ `:50 := inv_image (prod.lex (<) (<)) meas

/-- A recursor for pairs of lists. To have `C l₁ l₂` for all `l₁`, `l₂`, it suffices to have it for
`l₂ = []` and to be able to pour the elements of `l₁` into `l₂`. -/
@[elab_as_eliminator] def permutations_aux.rec {C : list α → list α → Sort v}
  (H0 : ∀ is, C [] is)
  (H1 : ∀ t ts is, C ts (t::is) → C is [] → C (t::ts) is) : ∀ l₁ l₂, C l₁ l₂
| []      is := H0 is
| (t::ts) is :=
  have h1 : ⟨ts, t :: is⟩ ≺ ⟨t :: ts, is⟩, from
    show prod.lex _ _ (succ (length ts + length is), length ts) (succ (length ts) + length is,
      length (t :: ts)),
    by rw nat.succ_add; exact prod.lex.right _ (lt_succ_self _),
  have h2 : ⟨is, []⟩ ≺ ⟨t :: ts, is⟩, from prod.lex.left _ _ (nat.lt_add_of_pos_left (succ_pos _)),
  H1 t ts is (permutations_aux.rec ts (t::is)) (permutations_aux.rec is [])
using_well_founded
{ dec_tac := tactic.assumption,
  rel_tac := λ _ _, `[exact ⟨(≺), @inv_image.wf _ _ _ meas (prod.lex_wf lt_wf lt_wf)⟩] }

/-- An auxiliary function for defining `permutations`. `permutations_aux ts is` is the set of all
permutations of `is ++ ts` that do not fix `ts`. -/
def permutations_aux : list α → list α → list (list α) :=
@@permutations_aux.rec (λ _ _, list (list α)) (λ is, [])
  (λ t ts is IH1 IH2, foldr (λy r, (permutations_aux2 t ts r y id).2) IH1 (is :: IH2))

/-- List of all permutations of `l`.

     permutations [1, 2, 3] =
       [[1, 2, 3], [2, 1, 3], [3, 2, 1],
        [2, 3, 1], [3, 1, 2], [1, 3, 2]] -/
def permutations (l : list α) : list (list α) :=
l :: permutations_aux l []

/-- `permutations'_aux t ts` inserts `t` into every position in `ts`, including the last.
This function is intended for use in specifications, so it is simpler than `permutations_aux2`,
which plays roughly the same role in `permutations`.

Note that `(permutations_aux2 t [] [] ts id).2` is similar to this function, but skips the last
position:

    permutations'_aux 10 [1, 2, 3] =
      [[10, 1, 2, 3], [1, 10, 2, 3], [1, 2, 10, 3], [1, 2, 3, 10]]
    (permutations_aux2 10 [] [] [1, 2, 3] id).2 =
      [[10, 1, 2, 3], [1, 10, 2, 3], [1, 2, 10, 3]] -/
@[simp] def permutations'_aux (t : α) : list α → list (list α)
| []      := [[t]]
| (y::ys) := (t :: y :: ys) :: (permutations'_aux ys).map (cons y)

/-- List of all permutations of `l`. This version of `permutations` is less efficient but has
simpler definitional equations. The permutations are in a different order,
but are equal up to permutation, as shown by `list.permutations_perm_permutations'`.

     permutations [1, 2, 3] =
       [[1, 2, 3], [2, 1, 3], [2, 3, 1],
        [1, 3, 2], [3, 1, 2], [3, 2, 1]] -/
@[simp] def permutations' : list α → list (list α)
| [] := [[]]
| (t::ts) := (permutations' ts).bind $ permutations'_aux t

end permutations

/-- `erasep p l` removes the first element of `l` satisfying the predicate `p`. -/
def erasep (p : α → Prop) [decidable_pred p] : list α → list α
| []     := []
| (a::l) := if p a then l else a :: erasep l

/-- `extractp p l` returns a pair of an element `a` of `l` satisfying the predicate
  `p`, and `l`, with `a` removed. If there is no such element `a` it returns `(none, l)`. -/
def extractp (p : α → Prop) [decidable_pred p] : list α → option α × list α
| []     := (none, [])
| (a::l) := if p a then (some a, l) else
  let (a', l') := extractp l in (a', a :: l')

/-- `revzip l` returns a list of pairs of the elements of `l` paired
  with the elements of `l` in reverse order.

`revzip [1,2,3,4,5] = [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]`
 -/
def revzip (l : list α) : list (α × α) := zip l l.reverse

/-- `product l₁ l₂` is the list of pairs `(a, b)` where `a ∈ l₁` and `b ∈ l₂`.

     product [1, 2] [5, 6] = [(1, 5), (1, 6), (2, 5), (2, 6)] -/
def product (l₁ : list α) (l₂ : list β) : list (α × β) :=
l₁.bind $ λ a, l₂.map $ prod.mk a

/-- `sigma l₁ l₂` is the list of dependent pairs `(a, b)` where `a ∈ l₁` and `b ∈ l₂ a`.

     sigma [1, 2] (λ_, [(5 : ℕ), 6]) = [(1, 5), (1, 6), (2, 5), (2, 6)] -/
protected def sigma {σ : α → Type*} (l₁ : list α) (l₂ : Π a, list (σ a)) : list (Σ a, σ a) :=
l₁.bind $ λ a, (l₂ a).map $ sigma.mk a

/-- Auxliary definition used to define `of_fn`.

  `of_fn_aux f m h l` returns the first `m` elements of `of_fn f`
  appended to `l` -/
def of_fn_aux {n} (f : fin n → α) : ∀ m, m ≤ n → list α → list α
| 0        h l := l
| (succ m) h l := of_fn_aux m (le_of_lt h) (f ⟨m, h⟩ :: l)

/-- `of_fn f` with `f : fin n → α` returns the list whose ith element is `f i`
  `of_fun f = [f 0, f 1, ... , f(n - 1)]` -/
def of_fn {n} (f : fin n → α) : list α :=
of_fn_aux f n (le_refl _) []

/-- `of_fn_nth_val f i` returns `some (f i)` if `i < n` and `none` otherwise. -/
def of_fn_nth_val {n} (f : fin n → α) (i : ℕ) : option α :=
if h : i < n then some (f ⟨i, h⟩) else none

/-- `disjoint l₁ l₂` means that `l₁` and `l₂` have no elements in common. -/
def disjoint (l₁ l₂ : list α) : Prop := ∀ ⦃a⦄, a ∈ l₁ → a ∈ l₂ → false

section pairwise
variables (R : α → α → Prop)

/-- `pairwise R l` means that all the elements with earlier indexes are
  `R`-related to all the elements with later indexes.

     pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3

  For example if `R = (≠)` then it asserts `l` has no duplicates,
  and if `R = (<)` then it asserts that `l` is (strictly) sorted. -/
inductive pairwise : list α → Prop
| nil : pairwise []
| cons : ∀ {a : α} {l : list α}, (∀ a' ∈ l, R a a') → pairwise l → pairwise (a::l)

variables {R}
@[simp] theorem pairwise_cons {a : α} {l : list α} :
  pairwise R (a::l) ↔ (∀ a' ∈ l, R a a') ∧ pairwise R l :=
⟨λ p, by cases p with a l n p; exact ⟨n, p⟩, λ ⟨n, p⟩, p.cons n⟩

attribute [simp] pairwise.nil

instance decidable_pairwise [decidable_rel R] (l : list α) : decidable (pairwise R l) :=
by induction l with hd tl ih; [exact is_true pairwise.nil,
  exactI decidable_of_iff' _ pairwise_cons]

end pairwise

/-- `pw_filter R l` is a maximal sublist of `l` which is `pairwise R`.
  `pw_filter (≠)` is the erase duplicates function (cf. `erase_dup`), and `pw_filter (<)` finds
  a maximal increasing subsequence in `l`. For example,

     pw_filter (<) [0, 1, 5, 2, 6, 3, 4] = [0, 1, 2, 3, 4] -/
def pw_filter (R : α → α → Prop) [decidable_rel R] : list α → list α
| []        := []
| (x :: xs) := let IH := pw_filter xs in if ∀ y ∈ IH, R x y then x :: IH else IH

section chain
variable (R : α → α → Prop)

/-- `chain R a l` means that `R` holds between adjacent elements of `a::l`.

     chain R a [b, c, d] ↔ R a b ∧ R b c ∧ R c d -/
inductive chain : α → list α → Prop
| nil {a : α} : chain a []
| cons : ∀ {a b : α} {l : list α}, R a b → chain b l → chain a (b::l)

/-- `chain' R l` means that `R` holds between adjacent elements of `l`.

     chain' R [a, b, c, d] ↔ R a b ∧ R b c ∧ R c d -/
def chain' : list α → Prop
| [] := true
| (a :: l) := chain R a l

variable {R}
@[simp] theorem chain_cons {a b : α} {l : list α} :
  chain R a (b::l) ↔ R a b ∧ chain R b l :=
⟨λ p, by cases p with _ a b l n p; exact ⟨n, p⟩, λ ⟨n, p⟩, p.cons n⟩

attribute [simp] chain.nil

instance decidable_chain [decidable_rel R] (a : α) (l : list α) : decidable (chain R a l) :=
by induction l generalizing a; simp only [chain.nil, chain_cons]; resetI; apply_instance

instance decidable_chain' [decidable_rel R] (l : list α) : decidable (chain' R l) :=
by cases l; dunfold chain'; apply_instance

end chain

/-- `nodup l` means that `l` has no duplicates, that is, any element appears at most
  once in the list. It is defined as `pairwise (≠)`. -/
def nodup : list α → Prop := pairwise (≠)

instance nodup_decidable [decidable_eq α] : ∀ l : list α, decidable (nodup l) :=
list.decidable_pairwise

/-- `erase_dup l` removes duplicates from `l` (taking only the first occurrence).
  Defined as `pw_filter (≠)`.

     erase_dup [1, 0, 2, 2, 1] = [0, 2, 1] -/
def erase_dup [decidable_eq α] : list α → list α := pw_filter (≠)

/-- `range' s n` is the list of numbers `[s, s+1, ..., s+n-1]`.
  It is intended mainly for proving properties of `range` and `iota`. -/
@[simp] def range' : ℕ → ℕ → list ℕ
| s 0     := []
| s (n+1) := s :: range' (s+1) n

/-- Drop `none`s from a list, and replace each remaining `some a` with `a`. -/
def reduce_option {α} : list (option α) → list α :=
list.filter_map id

/-- `ilast' x xs` returns the last element of `xs` if `xs` is non-empty;
it returns `x` otherwise -/
@[simp] def ilast' {α} : α → list α → α
| a []     := a
| a (b::l) := ilast' b l

/-- `last' xs` returns the last element of `xs` if `xs` is non-empty;
it returns `none` otherwise -/
@[simp] def last' {α} : list α → option α
| []     := none
| [a]    := some a
| (b::l) := last' l

/-- `rotate l n` rotates the elements of `l` to the left by `n`

     rotate [0, 1, 2, 3, 4, 5] 2 = [2, 3, 4, 5, 0, 1] -/
def rotate (l : list α) (n : ℕ) : list α :=
let (l₁, l₂) := list.split_at (n % l.length) l in l₂ ++ l₁

/-- rotate' is the same as `rotate`, but slower. Used for proofs about `rotate`-/
def rotate' : list α → ℕ → list α
| []     n     := []
| l      0     := l
| (a::l) (n+1) := rotate' (l ++ [a]) n

section choose
variables (p : α → Prop) [decidable_pred p] (l : list α)

/-- Given a decidable predicate `p` and a proof of existence of `a ∈ l` such that `p a`,
choose the first element with this property. This version returns both `a` and proofs
of `a ∈ l` and `p a`. -/
def choose_x : Π l : list α, Π hp : (∃ a, a ∈ l ∧ p a), { a // a ∈ l ∧ p a }
| [] hp := false.elim (exists.elim hp (assume a h, not_mem_nil a h.left))
| (l :: ls) hp := if pl : p l then ⟨l, ⟨or.inl rfl, pl⟩⟩ else
let ⟨a, ⟨a_mem_ls, pa⟩⟩ := choose_x ls (hp.imp
  (λ b ⟨o, h₂⟩, ⟨o.resolve_left (λ e, pl $ e ▸ h₂), h₂⟩)) in
⟨a, ⟨or.inr a_mem_ls, pa⟩⟩

/-- Given a decidable predicate `p` and a proof of existence of `a ∈ l` such that `p a`,
choose the first element with this property. This version returns `a : α`, and properties
are given by `choose_mem` and `choose_property`. -/
def choose (hp : ∃ a, a ∈ l ∧ p a) : α := choose_x p l hp

end choose

/-- Filters and maps elements of a list -/
def mmap_filter {m : Type → Type v} [monad m] {α β} (f : α → m (option β)) :
  list α → m (list β)
| []       := return []
| (h :: t) := do b ← f h, t' ← t.mmap_filter, return $
  match b with none := t' | (some x) := x::t' end

/--
`mmap_upper_triangle f l` calls `f` on all elements in the upper triangular part of `l × l`.
That is, for each `e ∈ l`, it will run `f e e` and then `f e e'`
for each `e'` that appears after `e` in `l`.

Example: suppose `l = [1, 2, 3]`. `mmap_upper_triangle f l` will produce the list
`[f 1 1, f 1 2, f 1 3, f 2 2, f 2 3, f 3 3]`.
-/
def mmap_upper_triangle {m} [monad m] {α β : Type u} (f : α → α → m β) : list α → m (list β)
| [] := return []
| (h::t) := do v ← f h h, l ← t.mmap (f h), t ← t.mmap_upper_triangle, return $ (v::l) ++ t

/--
`mmap'_diag f l` calls `f` on all elements in the upper triangular part of `l × l`.
That is, for each `e ∈ l`, it will run `f e e` and then `f e e'`
for each `e'` that appears after `e` in `l`.

Example: suppose `l = [1, 2, 3]`. `mmap'_diag f l` will evaluate, in this order,
`f 1 1`, `f 1 2`, `f 1 3`, `f 2 2`, `f 2 3`, `f 3 3`.
-/
def mmap'_diag {m} [monad m] {α} (f : α → α → m unit) : list α → m unit
| [] := return ()
| (h::t) := f h h >> t.mmap' (f h) >> t.mmap'_diag

protected def traverse {F : Type u → Type v} [applicative F] {α β : Type*} (f : α → F β) :
  list α → F (list β)
| [] := pure []
| (x :: xs) := list.cons <$> f x <*> traverse xs

/-- `get_rest l l₁` returns `some l₂` if `l = l₁ ++ l₂`.
  If `l₁` is not a prefix of `l`, returns `none` -/
def get_rest [decidable_eq α] : list α → list α → option (list α)
| l      []      := some l
| []     _       := none
| (x::l) (y::l₁) := if x = y then get_rest l l₁ else none

/--
`list.slice n m xs` removes a slice of length `m` at index `n` in list `xs`.
-/
def slice {α} : ℕ → ℕ → list α → list α
| 0 n xs := xs.drop n
| (succ n) m [] := []
| (succ n) m (x :: xs) := x :: slice n m xs

/--
Left-biased version of `list.map₂`. `map₂_left' f as bs` applies `f` to each
pair of elements `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, `f` is
applied to `none` for the remaining `aᵢ`. Returns the results of the `f`
applications and the remaining `bs`.

```
map₂_left' prod.mk [1, 2] ['a'] = ([(1, some 'a'), (2, none)], [])

map₂_left' prod.mk [1] ['a', 'b'] = ([(1, some 'a')], ['b'])
```
-/
@[simp] def map₂_left' (f : α → option β → γ) : list α → list β → (list γ × list β)
| [] bs := ([], bs)
| (a :: as) [] :=
  ((a :: as).map (λ a, f a none), [])
| (a :: as) (b :: bs) :=
  let rec := map₂_left' as bs in
  (f a (some b) :: rec.fst, rec.snd)

/--
Right-biased version of `list.map₂`. `map₂_right' f as bs` applies `f` to each
pair of elements `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, `f` is
applied to `none` for the remaining `bᵢ`. Returns the results of the `f`
applications and the remaining `as`.

```
map₂_right' prod.mk [1] ['a', 'b'] = ([(some 1, 'a'), (none, 'b')], [])

map₂_right' prod.mk [1, 2] ['a'] = ([(some 1, 'a')], [2])
```
-/
def map₂_right' (f : option α → β → γ) (as : list α) (bs : list β) : (list γ × list α) :=
map₂_left' (flip f) bs as

/--
Left-biased version of `list.zip`. `zip_left' as bs` returns the list of
pairs `(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, the
remaining `aᵢ` are paired with `none`. Also returns the remaining `bs`.

```
zip_left' [1, 2] ['a'] = ([(1, some 'a'), (2, none)], [])

zip_left' [1] ['a', 'b'] = ([(1, some 'a')], ['b'])

zip_left' = map₂_left' prod.mk

```
-/
def zip_left' : list α → list β → list (α × option β) × list β :=
map₂_left' prod.mk

/--
Right-biased version of `list.zip`. `zip_right' as bs` returns the list of
pairs `(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, the
remaining `bᵢ` are paired with `none`. Also returns the remaining `as`.

```
zip_right' [1] ['a', 'b'] = ([(some 1, 'a'), (none, 'b')], [])

zip_right' [1, 2] ['a'] = ([(some 1, 'a')], [2])

zip_right' = map₂_right' prod.mk
```
-/
def zip_right' : list α → list β → list (option α × β) × list α :=
map₂_right' prod.mk

/--
Left-biased version of `list.map₂`. `map₂_left f as bs` applies `f` to each pair
`aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, `f` is applied to `none`
for the remaining `aᵢ`.

```
map₂_left prod.mk [1, 2] ['a'] = [(1, some 'a'), (2, none)]

map₂_left prod.mk [1] ['a', 'b'] = [(1, some 'a')]

map₂_left f as bs = (map₂_left' f as bs).fst
```
-/
@[simp] def map₂_left (f : α → option β → γ) : list α → list β → list γ
| [] _ := []
| (a :: as) [] := (a :: as).map (λ a, f a none)
| (a :: as) (b :: bs) := f a (some b) :: map₂_left as bs

/--
Right-biased version of `list.map₂`. `map₂_right f as bs` applies `f` to each
pair `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, `f` is applied to
`none` for the remaining `bᵢ`.

```
map₂_right prod.mk [1, 2] ['a'] = [(some 1, 'a')]

map₂_right prod.mk [1] ['a', 'b'] = [(some 1, 'a'), (none, 'b')]

map₂_right f as bs = (map₂_right' f as bs).fst
```
-/
def map₂_right (f : option α → β → γ) (as : list α) (bs : list β) :
  list γ :=
map₂_left (flip f) bs as

/--
Left-biased version of `list.zip`. `zip_left as bs` returns the list of pairs
`(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, the
remaining `aᵢ` are paired with `none`.

```
zip_left [1, 2] ['a'] = [(1, some 'a'), (2, none)]

zip_left [1] ['a', 'b'] = [(1, some 'a')]

zip_left = map₂_left prod.mk
```
-/
def zip_left : list α → list β → list (α × option β) :=
map₂_left prod.mk

/--
Right-biased version of `list.zip`. `zip_right as bs` returns the list of pairs
`(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, the
remaining `bᵢ` are paired with `none`.

```
zip_right [1, 2] ['a'] = [(some 1, 'a')]

zip_right [1] ['a', 'b'] = [(some 1, 'a'), (none, 'b')]

zip_right = map₂_right prod.mk
```
-/
def zip_right : list α → list β → list (option α × β) :=
map₂_right prod.mk

/--
If all elements of `xs` are `some xᵢ`, `all_some xs` returns the `xᵢ`. Otherwise
it returns `none`.

```
all_some [some 1, some 2] = some [1, 2]
all_some [some 1, none  ] = none
```
-/
def all_some : list (option α) → option (list α)
| [] := some []
| (some a :: as) := cons a <$> all_some as
| (none :: as) := none

/--
`fill_nones xs ys` replaces the `none`s in `xs` with elements of `ys`. If there
are not enough `ys` to replace all the `none`s, the remaining `none`s are
dropped from `xs`.

```
fill_nones [none, some 1, none, none] [2, 3] = [2, 1, 3]
```
-/
def fill_nones {α} : list (option α) → list α → list α
| [] _ := []
| (some a :: as) as' := a :: fill_nones as as'
| (none :: as) [] := as.reduce_option
| (none :: as) (a :: as') := a :: fill_nones as as'

/--
`take_list as ns` extracts successive sublists from `as`. For `ns = n₁ ... nₘ`,
it first takes the `n₁` initial elements from `as`, then the next `n₂` ones,
etc. It returns the sublists of `as` -- one for each `nᵢ` -- and the remaining
elements of `as`. If `as` does not have at least as many elements as the sum of
the `nᵢ`, the corresponding sublists will have less than `nᵢ` elements.

```
take_list ['a', 'b', 'c', 'd', 'e'] [2, 1, 1] = ([['a', 'b'], ['c'], ['d']], ['e'])
take_list ['a', 'b'] [3, 1] = ([['a', 'b'], []], [])
```
-/
def take_list {α} : list α → list ℕ → list (list α) × list α
| xs [] := ([], xs)
| xs (n :: ns) :=
  let ⟨xs₁, xs₂⟩ := xs.split_at n in
  let ⟨xss, rest⟩ := take_list xs₂ ns in
  (xs₁ :: xss, rest)

/--
`to_rbmap as` is the map that associates each index `i` of `as` with the
corresponding element of `as`.

```
to_rbmap ['a', 'b', 'c'] = rbmap_of [(0, 'a'), (1, 'b'), (2, 'c')]
```
-/
def to_rbmap {α : Type*} : list α → rbmap ℕ α :=
foldl_with_index (λ i mapp a, mapp.insert i a) (mk_rbmap ℕ α)

/-- Auxliary definition used to define `to_chunks`.

  `to_chunks_aux n xs i` returns `(xs.take i, (xs.drop i).to_chunks (n+1))`,
  that is, the first `i` elements of `xs`, and the remaining elements chunked into
  sublists of length `n+1`. -/
def to_chunks_aux {α} (n : ℕ) : list α → ℕ → list α × list (list α)
| [] i := ([], [])
| (x::xs) 0 := let (l, L) := to_chunks_aux xs n in ([], (x::l)::L)
| (x::xs) (i+1) := let (l, L) := to_chunks_aux xs i in (x::l, L)

/--
`xs.to_chunks n` splits the list into sublists of size at most `n`,
such that `(xs.to_chunks n).join = xs`.

```
[1, 2, 3, 4, 5, 6, 7, 8].to_chunks 10 = [[1, 2, 3, 4, 5, 6, 7, 8]]
[1, 2, 3, 4, 5, 6, 7, 8].to_chunks 3 = [[1, 2, 3], [4, 5, 6], [7, 8]]
[1, 2, 3, 4, 5, 6, 7, 8].to_chunks 2 = [[1, 2], [3, 4], [5, 6], [7, 8]]
[1, 2, 3, 4, 5, 6, 7, 8].to_chunks 0 = [[1, 2, 3, 4, 5, 6, 7, 8]]
```
-/
def to_chunks {α} : ℕ → list α → list (list α)
| _ [] := []
| 0 xs := [xs]
| (n+1) (x::xs) := let (l, L) := to_chunks_aux n xs n in (x::l)::L

/--
Asynchronous version of `list.map`.
-/
meta def map_async_chunked {α β} (f : α → β) (xs : list α) (chunk_size := 1024) : list β :=
((xs.to_chunks chunk_size).map (λ xs, task.delay (λ _, list.map f xs))).bind task.get

/-!
We add some n-ary versions of `list.zip_with` for functions with more than two arguments.
These can also be written in terms of `list.zip` or `list.zip_with`.
For example, `zip_with3 f xs ys zs` could also be written as
`zip_with id (zip_with f xs ys) zs`
or as
`(zip xs $ zip ys zs).map $ λ ⟨x, y, z⟩, f x y z`.
-/

/-- Ternary version of `list.zip_with`. -/
def zip_with3 (f : α → β → γ → δ) : list α → list β → list γ → list δ
| (x::xs) (y::ys) (z::zs) := f x y z :: zip_with3 xs ys zs
| _       _       _       := []

/-- Quaternary version of `list.zip_with`. -/
def zip_with4 (f : α → β → γ → δ → ε) : list α → list β → list γ → list δ → list ε
| (x::xs) (y::ys) (z::zs) (u::us) := f x y z u :: zip_with4 xs ys zs us
| _       _       _       _       := []

/-- Quinary version of `list.zip_with`. -/
def zip_with5 (f : α → β → γ → δ → ε → ζ) : list α → list β → list γ → list δ → list ε → list ζ
| (x::xs) (y::ys) (z::zs) (u::us) (v::vs) := f x y z u v :: zip_with5 xs ys zs us vs
| _       _       _       _       _       := []

/-- An auxiliary function for `list.map_with_prefix_suffix`. -/
def map_with_prefix_suffix_aux {α β} (f : list α → α → list α → β) : list α → list α → list β
| prev [] := []
| prev (h::t) := f prev h t :: map_with_prefix_suffix_aux (prev.concat h) t

/--
`list.map_with_prefix_suffix f l` maps `f` across a list `l`.
For each `a ∈ l` with `l = pref ++ [a] ++ suff`, `a` is mapped to `f pref a suff`.

Example: if `f : list ℕ → ℕ → list ℕ → β`,
`list.map_with_prefix_suffix f [1, 2, 3]` will produce the list
`[f [] 1 [2, 3], f [1] 2 [3], f [1, 2] 3 []]`.
-/
def map_with_prefix_suffix {α β} (f : list α → α → list α → β) (l : list α) : list β :=
map_with_prefix_suffix_aux f [] l

/--
`list.map_with_complement f l` is a variant of `list.map_with_prefix_suffix`
that maps `f` across a list `l`.
For each `a ∈ l` with `l = pref ++ [a] ++ suff`, `a` is mapped to `f a (pref ++ suff)`,
i.e., the list input to `f` is `l` with `a` removed.

Example: if `f : ℕ → list ℕ → β`, `list.map_with_complement f [1, 2, 3]` will produce the list
`[f 1 [2, 3], f 2 [1, 3], f 3 [1, 2]]`.
-/
def map_with_complement {α β} (f : α → list α → β) : list α → list β :=
map_with_prefix_suffix $ λ pref a suff, f a (pref ++ suff)

end list


import tactic
import algebra.category.CommRing.basic
variables (R : Type)[comm_ring R]
structure R_exp :=
(a_0 : R)
(a_e : R)

namespace R_exp
mk_simp_attribute R_exp_ " trivial simplification "
meta def R_exp__ring : tactic unit :=
`[iterate 2 { simp only with R_exp_,ring}]

run_cmd add_interactive [`R_exp__ring]
@[ext] lemma ext : ∀ {g1 g2 : R_exp R}, g1.a_0 = g2.a_0 →  g1.a_e = g2.a_e  → g1 = g2 := λ g1 g2,
        begin
            intros h1,
            intros h2,
            cases g1,
            cases g2,
            congr ; try { assumption },   ---
        end
local notation R`[ε]` := R_exp R

def mod_ε (a : R[ε]) : R := a.a_0

instance has_coe :  has_coe(R[ε]) R := ⟨mod_ε(R)⟩  -- ? bof
/-
a ↦ a+ 0 ε
-/
def of(r : R) : R[ε] := {a_0 := r, a_e := 0}
@[R_exp_]lemma of_ext_0 (r : R) : ((of R r).a_0 = r)  := rfl
@[R_exp_]lemma of_ext_e (r : R) : ((of R r).a_e = 0)  := rfl
@[R_exp_]lemma coe_of (r : R) : ((of R r) : R) = r := rfl

/-
element : ε qui va vérifier :   ε^2 = 0
-/
def ε : R[ε]  := {a_0 := (0 : R), a_e := (1 : R)}
@[R_exp_]lemma ε_ext_0 : (ε R).a_0 = 0  := rfl
@[R_exp_]lemma ε_ext_e : (ε R).a_e = 1  := rfl

/-
Element !: 0
-/
def zero : R[ε] := of R (0 : R)
instance : has_zero (R[ε] ) := ⟨zero R⟩

@[R_exp_]lemma zero_ext_0 : (0 : R[ε]).a_0 = 0  := rfl
@[R_exp_]lemma zero_ext_e : (0 : R[ε]).a_e = 0  := rfl

/-
Addition :
-/
def add' :  (R[ε]) → (R[ε]) →  (R[ε]) := λ a b,  {a_0 := a.a_0+b.a_0, a_e := a.a_e +b.a_e}
instance : has_add (R[ε]) := ⟨add' R⟩
@[R_exp_]lemma add_ext_0 (a b : R[ε]) : (a+b).a_0 = a.a_0+ b.a_0 := rfl
@[R_exp_]lemma add_ext_e (a b : R[ε]) : (a+b).a_e = a.a_e+ b.a_e := rfl
/-
We want to show that add' give to R[ε] abelian group structure
we print the axiom !
-/
#print add_comm_group
/-
There is a lot of axiom
add_comm_group.add : Π {α : Type u} [c : add_comm_group α], α → α → α
add_comm_group.add_assoc : ∀ {α : Type u} [c : add_comm_group α] (a b c_1 : α), a + b + c_1 = a + (b + c_1)
add_comm_group.zero : Π (α : Type u) [c : add_comm_group α], α
add_comm_group.zero_add : ∀ {α : Type u} [c : add_comm_group α] (a : α), 0 + a = a
add_comm_group.add_zero : ∀ {α : Type u} [c : add_comm_group α] (a : α), a + 0 = a
add_comm_group.neg : Π {α : Type u} [c : add_comm_group α], α → α
add_comm_group.add_left_neg : ∀ {α : Type u} [c : add_comm_group α] (a : α), -a + a = 0
add_comm_group.add_comm : ∀ {α : Type u} [c : add_comm_group α] (a b : α), a + b = b + a
-/
def neg' :  (R[ε]) → (R[ε]) := λ a, {a_0 := - a.a_0, a_e := -a.a_e}

instance : has_neg(R[ε]) := ⟨neg' R⟩
@[R_exp_]lemma neg_ext_0 (a : R[ε]) : (-a).a_0 = -a.a_0  := rfl
@[R_exp_]lemma neg_ext_e (a : R[ε]) : (-a).a_e = -a.a_e  := rfl

lemma add_assoc' (a b c : R[ε]) : a + b + c = a+(b+c) := begin ext, R_exp__ring, end
lemma zero_add' (a : R[ε]) : 0 + a = a:= begin ext, R_exp__ring, end
lemma add_zero' (a : R[ε]) : a+ 0 = a:= begin ext, R_exp__ring, end
lemma add_left_neg'  (a : R[ε]) :  -a + a = 0 := begin ext,R_exp__ring, end
lemma add_comm'   (a b : R[ε]) :  a + b = b + a :=begin ext,R_exp__ring,end

#print comm_ring
/-
structure comm_ring : Type u → Type u
fields:
comm_ring.add : Π {α : Type u} [c : comm_ring α], α → α → α
comm_ring.add_assoc : ∀ {α : Type u} [c : comm_ring α] (a b c_1 : α), a + b + c_1 = a + (b + c_1)
comm_ring.zero : Π (α : Type u) [c : comm_ring α], α
comm_ring.zero_add : ∀ {α : Type u} [c : comm_ring α] (a : α), 0 + a = a
comm_ring.add_zero : ∀ {α : Type u} [c : comm_ring α] (a : α), a + 0 = a
comm_ring.neg : Π {α : Type u} [c : comm_ring α], α → α
comm_ring.add_left_neg : ∀ {α : Type u} [c : comm_ring α] (a : α), -a + a = 0
comm_ring.add_comm : ∀ {α : Type u} [c : comm_ring α] (a b : α), a + b = b + a
comm_ring.mul : Π {α : Type u} [c : comm_ring α], α → α → α
comm_ring.mul_assoc : ∀ {α : Type u} [c : comm_ring α] (a b c_1 : α), a * b * c_1 = a * (b * c_1)
comm_ring.one : Π (α : Type u) [c : comm_ring α], α
comm_ring.one_mul : ∀ {α : Type u} [c : comm_ring α] (a : α), 1 * a = a
comm_ring.mul_one : ∀ {α : Type u} [c : comm_ring α] (a : α), a * 1 = a
comm_ring.left_distrib : ∀ {α : Type u} [c : comm_ring α] (a b c_1 : α), a * (b + c_1) = a * b + a * c_1
comm_ring.right_distrib : ∀ {α : Type u} [c : comm_ring α] (a b c_1 : α), (a + b) * c_1 = a * c_1 + b * c_1
comm_ring.mul_comm : ∀ {α : Type u} [c : comm_ring α] (a b : α), a * b = b * a
-/
def mul'  : (R[ε]) → (R[ε]) →  (R[ε]) := λ a b,begin exact
{a_0 := a.a_0 * b.a_0, a_e := a.a_0 * b.a_e+ a.a_e * b.a_0}
end
instance : has_mul (R[ε]) := ⟨mul' R⟩
@[R_exp_]lemma mul_ext_0 (a b : R[ε]) : (a * b).a_0 = a.a_0 * b.a_0 := rfl
@[R_exp_]lemma mul_ext_e (a b : R[ε]) : (a*b).a_e =a.a_0 * b.a_e+ a.a_e * b.a_0 := rfl
lemma mul_assoc' ( a b c : R[ε]) : a * b * c = a * (b * c) := begin ext, R_exp__ring, end

def one  : R[ε] := of R (1 : R)

instance : has_one (R[ε] ) := ⟨one R⟩
@[R_exp_]lemma one_ext_0 : (1 : R[ε]).a_0 = 1  := rfl
@[R_exp_]lemma one_ext_e : (1 : R[ε]).a_e = 0  := rfl

lemma one_mul' (a : R[ε]) : 1 * a = a := begin ext, R_exp__ring,end
lemma mul_one' (a : R[ε]) : a * 1 = a := begin ext, R_exp__ring,end
lemma left_distrib' (a b c :  R[ε]) :  a * (b + c) = a * b + a * c := begin ext, R_exp__ring,end
lemma right_distrib' (a b c :  R[ε]) :  (a + b) *c = a * c + b * c := begin ext, R_exp__ring,end
lemma mul_comm' (a b : R[ε]) :  a * b = b * a := begin ext, R_exp__ring, end

instance : comm_ring (R[ε]) :=
  { add                  := add' R,
    add_assoc            := add_assoc' R,
    zero                 := 0,
    zero_add             := zero_add' R,
    add_zero             := add_zero' R,
    neg                  := neg' R,
    add_left_neg         := add_left_neg' R,
    add_comm             := add_comm' R,
    mul                  := mul' R,
    mul_assoc            := mul_assoc' R,
    one                  := 1,
    one_mul              := one_mul' R,
    mul_one              := mul_one' R,
    left_distrib         := left_distrib' R,
    right_distrib        := right_distrib' R,
    mul_comm             := mul_comm' R,
    }

variables (A B : Type)[comm_ring A][comm_ring B](f : ring_hom A B)
def map {A B : Type}[comm_ring A][comm_ring B](f : ring_hom A B) :  (A[ε]) → (B[ε]) :=  λ a, begin
 exact {a_0 := f a.a_0, a_e := f a.a_e},
end
@[R_exp_]lemma map_ext_0 (f : ring_hom A B) (a : A[ε]): (map f a).a_0  = f a.a_0 := rfl
@[R_exp_]lemma map_ext_e (f : ring_hom A B) (a : A[ε]): (map f a).a_e  = f a.a_e := rfl
@[R_exp_]lemma map_one {A B : Type}[comm_ring A][comm_ring B](f : ring_hom A B) : (map f 1) = 1 := begin
ext,
rw [map_ext_0,one_ext_0,one_ext_0],
rw f.map_one,
rw [map_ext_e,one_ext_e,one_ext_e],
rw f.map_zero,
end
@[R_exp_]lemma map_zero {A B : Type}[comm_ring A][comm_ring B](f : ring_hom A B) : (map f 0) = 0 := begin
ext,
rw [map_ext_0,zero_ext_0,zero_ext_0],
rw f.map_zero,
rw [map_ext_e,zero_ext_e,zero_ext_e],
rw f.map_zero,
end
@[R_exp_]lemma map_add {A B : Type}[comm_ring A][comm_ring B](f : ring_hom A B) (a b : A[ε]) :
(map f (a+b)) = map f a + map f b := begin
ext, rw [map_ext_0,add_ext_0,add_ext_0,f.map_add,map_ext_0,map_ext_0],
rw [map_ext_e,add_ext_e,add_ext_e,f.map_add,map_ext_e,map_ext_e],
end
@[R_exp_]lemma map_mul {A B : Type}[comm_ring A][comm_ring B](f : ring_hom A B) (a b : A[ε]) :
(map f (a * b)) = map f a * map f b := begin
ext, rw [map_ext_0,mul_ext_0,mul_ext_0,f.map_mul,map_ext_0,map_ext_0],
rw [map_ext_e,mul_ext_e,f.map_add,mul_ext_e,f.map_mul,f.map_mul,map_ext_0,map_ext_e,map_ext_e,map_ext_0],
end
instance has_repr [has_repr R] : has_repr(R[ε])  := ⟨λ x,   repr x.a_0 ++ "+" ++ repr x.a_e ++ "ε"⟩
def e  := ε(ℤ)
#print ring_hom.mk
def map_ (f : ring_hom A B) :=  ring_hom.mk (map f) (map_one f) (map_mul f) (map_zero f) (map_add f)
def Rε  : CommRing ⥤ CommRing  :=
{ obj := λ R, CommRing.of( R_exp R),
  map := λ R R' f,  (map_  R R' f),
}
#print Rε
theorem representable (A : CommRing)(a : A) : (a^2 = 0) → (ℤ [ε]) →  A := λ certificat x, begin
exact x.a_0+a*x.a_e,
end

end R_exp


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

import probability.martingale.basic

/-!
# Centering lemma for stochastic processes

Any `ℕ`-indexed stochastic process which is adapted and integrable can be written as the sum of a
martingale and a predictable process. This result is also known as **Doob's decomposition theorem**.
From a process `f`, a filtration `ℱ` and a measure `μ`, we define two processes
`martingale_part f ℱ μ` and `predictable_part f ℱ μ`.

## Main definitions

* `measure_theory.predictable_part f ℱ μ`: a predictable process such that
  `f = predictable_part f ℱ μ + martingale_part f ℱ μ`
* `measure_theory.martingale_part f ℱ μ`: a martingale such that
  `f = predictable_part f ℱ μ + martingale_part f ℱ μ`

## Main statements

* `measure_theory.adapted_predictable_part`: `(λ n, predictable_part f ℱ μ (n+1))` is adapted. That
  is, `predictable_part` is predictable.
* `measure_theory.martingale_martingale_part`: `martingale_part f ℱ μ` is a martingale.

-/

open topological_space filter
open_locale nnreal ennreal measure_theory probability_theory big_operators

namespace measure_theory

variables {Ω E : Type*} {m0 : measurable_space Ω} {μ : measure Ω}
  [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]
  {f : ℕ → Ω → E} {ℱ : filtration ℕ m0} {n : ℕ}

/-- Any `ℕ`-indexed stochastic process can be written as the sum of a martingale and a predictable
process. This is the predictable process. See `martingale_part` for the martingale. -/
noncomputable
def predictable_part {m0 : measurable_space Ω}
  (f : ℕ → Ω → E) (ℱ : filtration ℕ m0) (μ : measure Ω . volume_tac) : ℕ → Ω → E :=
λ n, ∑ i in finset.range n, μ[f (i+1) - f i | ℱ i]

@[simp]

lemma adapted_predictable_part : adapted ℱ (λ n, predictable_part f ℱ μ (n+1)) :=
λ n, finset.strongly_measurable_sum' _
  (λ i hin, strongly_measurable_condexp.mono (ℱ.mono (finset.mem_range_succ_iff.mp hin)))

lemma adapted_predictable_part' : adapted ℱ (λ n, predictable_part f ℱ μ n) :=
λ n, finset.strongly_measurable_sum' _
  (λ i hin, strongly_measurable_condexp.mono (ℱ.mono (finset.mem_range_le hin)))

/-- Any `ℕ`-indexed stochastic process can be written as the sum of a martingale and a predictable
process. This is the martingale. See `predictable_part` for the predictable process. -/
noncomputable
def martingale_part {m0 : measurable_space Ω}
  (f : ℕ → Ω → E) (ℱ : filtration ℕ m0) (μ : measure Ω . volume_tac) : ℕ → Ω → E :=
λ n, f n - predictable_part f ℱ μ n

lemma martingale_part_add_predictable_part (ℱ : filtration ℕ m0) (μ : measure Ω) (f : ℕ → Ω → E) :
  martingale_part f ℱ μ + predictable_part f ℱ μ = f :=
sub_add_cancel _ _

lemma martingale_part_eq_sum :
  martingale_part f ℱ μ =
    λ n, f 0 + ∑ i in finset.range n, (f (i+1) - f i - μ[f (i+1) - f i | ℱ i]) :=
begin
  rw [martingale_part, predictable_part],
  ext1 n,
  rw [finset.eq_sum_range_sub f n, ← add_sub, ← finset.sum_sub_distrib],
end

lemma adapted_martingale_part (hf : adapted ℱ f) :
  adapted ℱ (martingale_part f ℱ μ) :=
adapted.sub hf adapted_predictable_part'

lemma integrable_martingale_part (hf_int : ∀ n, integrable (f n) μ) (n : ℕ) :
  integrable (martingale_part f ℱ μ n) μ :=
begin
  rw martingale_part_eq_sum,
  exact (hf_int 0).add
    (integrable_finset_sum' _ (λ i hi, ((hf_int _).sub (hf_int _)).sub integrable_condexp)),
end

lemma martingale_martingale_part (hf : adapted ℱ f) (hf_int : ∀ n, integrable (f n) μ)
  [sigma_finite_filtration μ ℱ] :
  martingale (martingale_part f ℱ μ) ℱ μ :=
begin
  refine ⟨adapted_martingale_part hf, λ i j hij, _⟩,
  -- ⊢ μ[martingale_part f ℱ μ j | ℱ i] =ᵐ[μ] martingale_part f ℱ μ i
  have h_eq_sum : μ[martingale_part f ℱ μ j | ℱ i]
    =ᵐ[μ] f 0 + ∑ k in finset.range j, (μ[f (k+1) - f k | ℱ i] - μ[μ[f (k+1) - f k | ℱ k] | ℱ i]),
  { rw martingale_part_eq_sum,
    refine (condexp_add (hf_int 0) _).trans _,
    { exact integrable_finset_sum' _
        (λ i hij, ((hf_int _).sub (hf_int _)).sub integrable_condexp), },
    refine (eventually_eq.add eventually_eq.rfl (condexp_finset_sum (λ i hij, _))).trans _,
    { exact ((hf_int _).sub (hf_int _)).sub integrable_condexp, },
    refine eventually_eq.add _ _,
    { rw condexp_of_strongly_measurable (ℱ.le _) _ (hf_int 0),
      { apply_instance, },
      { exact (hf 0).mono (ℱ.mono (zero_le i)), }, },
    { exact eventually_eq_sum (λ k hkj, condexp_sub ((hf_int _).sub (hf_int _))
        integrable_condexp), }, },
  refine h_eq_sum.trans _,
  have h_ge : ∀ k, i ≤ k → μ[f (k + 1) - f k|ℱ i] - μ[μ[f (k + 1) - f k|ℱ k]|ℱ i] =ᵐ[μ] 0,
  { intros k hk,
    have : μ[μ[f (k + 1) - f k|ℱ k]|ℱ i] =ᵐ[μ] μ[f (k + 1) - f k|ℱ i],
    { exact condexp_condexp_of_le (ℱ.mono hk) (ℱ.le k), },
    filter_upwards [this] with x hx,
    rw [pi.sub_apply, pi.zero_apply, hx, sub_self], },
  have h_lt : ∀ k, k < i → μ[f (k + 1) - f k|ℱ i] - μ[μ[f (k + 1) - f k|ℱ k]|ℱ i]
    =ᵐ[μ] f (k + 1) - f k - μ[f (k + 1) - f k|ℱ k],
  { refine λ k hk, eventually_eq.sub _ _,
    { rw condexp_of_strongly_measurable,
      { exact ((hf (k+1)).mono (ℱ.mono (nat.succ_le_of_lt hk))).sub
          ((hf k).mono (ℱ.mono hk.le)), },
      { exact (hf_int _).sub (hf_int _), }, },
    { rw condexp_of_strongly_measurable,
      { exact strongly_measurable_condexp.mono (ℱ.mono hk.le), },
      { exact integrable_condexp, }, }, },
  rw martingale_part_eq_sum,
  refine eventually_eq.add eventually_eq.rfl _,
  rw [← finset.sum_range_add_sum_Ico _ hij,
    ← add_zero (∑ i in finset.range i, (f (i + 1) - f i - μ[f (i + 1) - f i | ℱ i]))],
  refine (eventually_eq_sum (λ k hk, h_lt k (finset.mem_range.mp hk))).add _,
  refine (eventually_eq_sum (λ k hk, h_ge k (finset.mem_Ico.mp hk).1)).trans _,
  simp only [finset.sum_const_zero, pi.zero_apply],
  refl,
end

-- The following two lemmas demonstrate the essential uniqueness of the decomposition
lemma martingale_part_add_ae_eq [sigma_finite_filtration μ ℱ] {f g : ℕ → Ω → E}
  (hf : martingale f ℱ μ) (hg : adapted ℱ (λ n, g (n + 1))) (hg0 : g 0 = 0)
  (hgint : ∀ n, integrable (g n) μ) (n : ℕ) :
  martingale_part (f + g) ℱ μ n =ᵐ[μ] f n :=
begin
  set h := f - martingale_part (f + g) ℱ μ with hhdef,
  have hh : h = predictable_part (f + g) ℱ μ - g,
  { rw [hhdef, sub_eq_sub_iff_add_eq_add, add_comm (predictable_part (f + g) ℱ μ),
      martingale_part_add_predictable_part] },
  have hhpred : adapted ℱ (λ n, h (n + 1)),
  { rw hh,
    exact adapted_predictable_part.sub hg },
  have hhmgle : martingale h ℱ μ := hf.sub (martingale_martingale_part
    (hf.adapted.add $ predictable.adapted hg $ hg0.symm ▸ strongly_measurable_zero) $
    λ n, (hf.integrable n).add $ hgint n),
  refine (eventually_eq_iff_sub.2 _).symm,
  filter_upwards [hhmgle.eq_zero_of_predictable hhpred n] with ω hω,
  rw [hhdef, pi.sub_apply] at hω,
  rw [hω, pi.sub_apply, martingale_part],
  simp [hg0],
end

lemma predictable_part_add_ae_eq [sigma_finite_filtration μ ℱ] {f g : ℕ → Ω → E}
  (hf : martingale f ℱ μ) (hg : adapted ℱ (λ n, g (n + 1))) (hg0 : g 0 = 0)
  (hgint : ∀ n, integrable (g n) μ) (n : ℕ) :
  predictable_part (f + g) ℱ μ n =ᵐ[μ] g n :=
begin
  filter_upwards [martingale_part_add_ae_eq hf hg hg0 hgint n] with ω hω,
  rw ← add_right_inj (f n ω),
  conv_rhs { rw [← pi.add_apply, ← pi.add_apply,
    ← martingale_part_add_predictable_part ℱ μ (f + g)] },
  rw [pi.add_apply, pi.add_apply, hω],
end

section difference

lemma predictable_part_bdd_difference {R : ℝ≥0} {f : ℕ → Ω → ℝ}
  (ℱ : filtration ℕ m0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
  ∀ᵐ ω ∂μ, ∀ i, |predictable_part f ℱ μ (i + 1) ω - predictable_part f ℱ μ i ω| ≤ R :=
begin
  simp_rw [predictable_part, finset.sum_apply, finset.sum_range_succ_sub_sum],
  exact ae_all_iff.2 (λ i, ae_bdd_condexp_of_ae_bdd $ ae_all_iff.1 hbdd i),
end

lemma martingale_part_bdd_difference {R : ℝ≥0} {f : ℕ → Ω → ℝ}
  (ℱ : filtration ℕ m0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
  ∀ᵐ ω ∂μ, ∀ i, |martingale_part f ℱ μ (i + 1) ω - martingale_part f ℱ μ i ω| ≤ ↑(2 * R) :=
begin
  filter_upwards [hbdd, predictable_part_bdd_difference ℱ hbdd] with ω hω₁ hω₂ i,
  simp only [two_mul, martingale_part, pi.sub_apply],
  have : |f (i + 1) ω - predictable_part f ℱ μ (i + 1) ω - (f i ω - predictable_part f ℱ μ i ω)| =
    |(f (i + 1) ω - f i ω) - (predictable_part f ℱ μ (i + 1) ω - predictable_part f ℱ μ i ω)|,
  { ring_nf }, -- `ring` suggests `ring_nf` despite proving the goal
  rw this,
  exact (abs_sub _ _).trans (add_le_add (hω₁ i) (hω₂ i)),
end

end difference

end measure_theory


/-
Copyright (c) 2021 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anne Baanen, Ashvni Narayanan
-/
import algebra.order.group.type_tags
import field_theory.ratfunc
import ring_theory.dedekind_domain.integral_closure
import ring_theory.integrally_closed
import topology.algebra.valued_field

/-!
# Function fields

This file defines a function field and the ring of integers corresponding to it.

## Main definitions
 - `function_field Fq F` states that `F` is a function field over the (finite) field `Fq`,
   i.e. it is a finite extension of the field of rational functions in one variable over `Fq`.
 - `function_field.ring_of_integers` defines the ring of integers corresponding to a function field
    as the integral closure of `Fq[X]` in the function field.
 - `function_field.infty_valuation` : The place at infinity on `Fq(t)` is the nonarchimedean
    valuation on `Fq(t)` with uniformizer `1/t`.
 -  `function_field.Fqt_infty` : The completion `Fq((t⁻¹))`  of `Fq(t)` with respect to the
    valuation at infinity.

## Implementation notes
The definitions that involve a field of fractions choose a canonical field of fractions,
but are independent of that choice. We also omit assumptions like `finite Fq` or
`is_scalar_tower Fq[X] (fraction_ring Fq[X]) F` in definitions,
adding them back in lemmas when they are needed.

## References
* [D. Marcus, *Number Fields*][marcus1977number]
* [J.W.S. Cassels, A. Frölich, *Algebraic Number Theory*][cassels1967algebraic]
* [P. Samuel, *Algebraic Theory of Numbers*][samuel1970algebraic]

## Tags
function field, ring of integers
-/

noncomputable theory
open_locale non_zero_divisors polynomial discrete_valuation

variables (Fq F : Type) [field Fq] [field F]

/-- `F` is a function field over the finite field `Fq` if it is a finite
extension of the field of rational functions in one variable over `Fq`.

Note that `F` can be a function field over multiple, non-isomorphic, `Fq`.
-/
abbreviation function_field [algebra (ratfunc Fq) F] : Prop :=
finite_dimensional (ratfunc Fq) F

/-- `F` is a function field over `Fq` iff it is a finite extension of `Fq(t)`. -/
protected lemma function_field_iff (Fqt : Type*) [field Fqt]
  [algebra Fq[X] Fqt] [is_fraction_ring Fq[X] Fqt]
  [algebra (ratfunc Fq) F] [algebra Fqt F]
  [algebra Fq[X] F] [is_scalar_tower Fq[X] Fqt F]
  [is_scalar_tower Fq[X] (ratfunc Fq) F] :
  function_field Fq F ↔ finite_dimensional Fqt F :=
begin
  let e := is_localization.alg_equiv Fq[X]⁰ (ratfunc Fq) Fqt,
  have : ∀ c (x : F), e c • x = c • x,
  { intros c x,
    rw [algebra.smul_def, algebra.smul_def],
    congr,
    refine congr_fun _ c,
    refine is_localization.ext (non_zero_divisors (Fq[X])) _ _ _ _ _ _ _;
      intros; simp only [alg_equiv.map_one, ring_hom.map_one, alg_equiv.map_mul, ring_hom.map_mul,
                         alg_equiv.commutes, ← is_scalar_tower.algebra_map_apply], },
  split; intro h; resetI,
  { let b := finite_dimensional.fin_basis (ratfunc Fq) F,
    exact finite_dimensional.of_fintype_basis (b.map_coeffs e this) },
  { let b := finite_dimensional.fin_basis Fqt F,
    refine finite_dimensional.of_fintype_basis (b.map_coeffs e.symm _),
    intros c x, convert (this (e.symm c) x).symm, simp only [e.apply_symm_apply] },
end

lemma algebra_map_injective [algebra Fq[X] F] [algebra (ratfunc Fq) F]
  [is_scalar_tower Fq[X] (ratfunc Fq) F] : function.injective ⇑(algebra_map Fq[X] F) :=
begin
  rw is_scalar_tower.algebra_map_eq Fq[X] (ratfunc Fq) F,
  exact function.injective.comp ((algebra_map (ratfunc Fq) F).injective)
    (is_fraction_ring.injective Fq[X] (ratfunc Fq)),
end

namespace function_field

/-- The function field analogue of `number_field.ring_of_integers`:
`function_field.ring_of_integers Fq Fqt F` is the integral closure of `Fq[t]` in `F`.

We don't actually assume `F` is a function field over `Fq` in the definition,
only when proving its properties.
-/
def ring_of_integers [algebra Fq[X] F] := integral_closure Fq[X] F

namespace ring_of_integers

variables [algebra Fq[X] F]

instance : is_domain (ring_of_integers Fq F) :=
(ring_of_integers Fq F).is_domain

instance : is_integral_closure (ring_of_integers Fq F) Fq[X] F :=
integral_closure.is_integral_closure _ _

variables [algebra (ratfunc Fq) F] [is_scalar_tower Fq[X] (ratfunc Fq) F]

lemma algebra_map_injective :
  function.injective ⇑(algebra_map Fq[X] (ring_of_integers Fq F)) :=
begin
  have hinj : function.injective ⇑(algebra_map Fq[X] F),
  { rw is_scalar_tower.algebra_map_eq Fq[X] (ratfunc Fq) F,
    exact function.injective.comp ((algebra_map (ratfunc Fq) F).injective)
      (is_fraction_ring.injective Fq[X] (ratfunc Fq)), },
  rw injective_iff_map_eq_zero (algebra_map Fq[X] ↥(ring_of_integers Fq F)),
  intros p hp,
  rw [← subtype.coe_inj, subalgebra.coe_zero] at hp,
  rw injective_iff_map_eq_zero (algebra_map Fq[X] F) at hinj,
  exact hinj p hp,
end

lemma not_is_field : ¬ is_field (ring_of_integers Fq F) :=
by simpa [← ((is_integral_closure.is_integral_algebra Fq[X] F).is_field_iff_is_field
  (algebra_map_injective Fq F))] using (polynomial.not_is_field Fq)

variables [function_field Fq F]

instance : is_fraction_ring (ring_of_integers Fq F) F :=
integral_closure.is_fraction_ring_of_finite_extension (ratfunc Fq) F

instance : is_integrally_closed (ring_of_integers Fq F) :=
integral_closure.is_integrally_closed_of_finite_extension (ratfunc Fq)

instance [is_separable (ratfunc Fq) F] : is_noetherian Fq[X] (ring_of_integers Fq F) :=
is_integral_closure.is_noetherian _ (ratfunc Fq) F _

instance [is_separable (ratfunc Fq) F] :
  is_dedekind_domain (ring_of_integers Fq F) :=
is_integral_closure.is_dedekind_domain Fq[X] (ratfunc Fq) F _

end ring_of_integers

/-! ### The place at infinity on Fq(t) -/

section infty_valuation

variable [decidable_eq (ratfunc Fq)]

/-- The valuation at infinity is the nonarchimedean valuation on `Fq(t)` with uniformizer `1/t`.
Explicitly, if `f/g ∈ Fq(t)` is a nonzero quotient of polynomials, its valuation at infinity is
`multiplicative.of_add(degree(f) - degree(g))`. -/
def infty_valuation_def (r : ratfunc Fq) : ℤₘ₀ :=
if r = 0 then 0 else (multiplicative.of_add r.int_degree)

lemma infty_valuation.map_zero' : infty_valuation_def Fq 0 = 0 := if_pos rfl

lemma infty_valuation.map_one' : infty_valuation_def Fq 1 = 1 :=
(if_neg one_ne_zero).trans $
  by rw [ratfunc.int_degree_one, of_add_zero, with_zero.coe_one]

lemma infty_valuation.map_mul' (x y : ratfunc Fq) :
  infty_valuation_def Fq (x * y) = infty_valuation_def Fq x * infty_valuation_def Fq y :=
begin
  rw [infty_valuation_def, infty_valuation_def, infty_valuation_def],
  by_cases hx : x = 0,
  { rw [hx, zero_mul, if_pos (eq.refl _), zero_mul] },
  { by_cases hy : y = 0,
    { rw [hy, mul_zero, if_pos (eq.refl _), mul_zero] },
    { rw [if_neg hx, if_neg hy, if_neg (mul_ne_zero hx hy), ← with_zero.coe_mul,
        with_zero.coe_inj, ← of_add_add, ratfunc.int_degree_mul hx hy], }}
end

lemma infty_valuation.map_add_le_max' (x y : ratfunc Fq) :
  infty_valuation_def Fq (x + y) ≤ max (infty_valuation_def Fq x) (infty_valuation_def Fq y) :=
begin
  by_cases hx : x = 0,
  { rw [hx, zero_add],
    conv_rhs { rw [infty_valuation_def, if_pos (eq.refl _)] },
    rw max_eq_right (with_zero.zero_le (infty_valuation_def Fq y)),
    exact le_refl _ },
  { by_cases hy : y = 0,
    { rw [hy, add_zero],
      conv_rhs { rw [max_comm, infty_valuation_def, if_pos (eq.refl _)] },
      rw max_eq_right (with_zero.zero_le (infty_valuation_def Fq x)),
      exact le_refl _ },
    { by_cases hxy : x + y = 0,
      { rw [infty_valuation_def, if_pos hxy], exact zero_le',},
      { rw [infty_valuation_def, infty_valuation_def, infty_valuation_def, if_neg hx, if_neg hy,
        if_neg hxy],
        rw [le_max_iff,
        with_zero.coe_le_coe, multiplicative.of_add_le, with_zero.coe_le_coe,
        multiplicative.of_add_le, ← le_max_iff],
        exact ratfunc.int_degree_add_le hy hxy }}}
end

@[simp] lemma infty_valuation_of_nonzero {x : ratfunc Fq} (hx : x ≠ 0) :
  infty_valuation_def Fq x = (multiplicative.of_add x.int_degree) :=
by rw [infty_valuation_def, if_neg hx]

/-- The valuation at infinity on `Fq(t)`. -/
def infty_valuation  : valuation (ratfunc Fq) ℤₘ₀ :=
{ to_fun          := infty_valuation_def Fq,
  map_zero'       := infty_valuation.map_zero' Fq,
  map_one'        := infty_valuation.map_one' Fq,
  map_mul'        := infty_valuation.map_mul' Fq,
  map_add_le_max' := infty_valuation.map_add_le_max' Fq }

@[simp] lemma infty_valuation_apply {x : ratfunc Fq} :
  infty_valuation Fq x = infty_valuation_def Fq x := rfl

@[simp] lemma infty_valuation.C {k : Fq} (hk : k ≠ 0) :
  infty_valuation_def Fq (ratfunc.C k) = (multiplicative.of_add (0 : ℤ)) :=
begin
  have hCk : ratfunc.C k ≠ 0 := (map_ne_zero _).mpr hk,
  rw [infty_valuation_def, if_neg hCk, ratfunc.int_degree_C],
end

@[simp] lemma infty_valuation.X :
  infty_valuation_def Fq (ratfunc.X) = (multiplicative.of_add (1 : ℤ)) :=
by rw [infty_valuation_def, if_neg ratfunc.X_ne_zero, ratfunc.int_degree_X]

@[simp] lemma infty_valuation.polynomial {p : Fq[X]} (hp : p ≠ 0) :
  infty_valuation_def Fq (algebra_map Fq[X] (ratfunc Fq) p) =
    (multiplicative.of_add (p.nat_degree : ℤ)) :=
begin
  have hp' : algebra_map Fq[X] (ratfunc Fq) p ≠ 0,
  { rw [ne.def, ratfunc.algebra_map_eq_zero_iff], exact hp },
  rw [infty_valuation_def, if_neg hp', ratfunc.int_degree_polynomial]
end

/-- The valued field `Fq(t)` with the valuation at infinity. -/
def infty_valued_Fqt : valued (ratfunc Fq) ℤₘ₀ :=
valued.mk' $ infty_valuation Fq

lemma infty_valued_Fqt.def {x : ratfunc Fq} :
  @valued.v (ratfunc Fq) _ _ _ (infty_valued_Fqt Fq) x = infty_valuation_def Fq x := rfl

/-- The completion `Fq((t⁻¹))`  of `Fq(t)` with respect to the valuation at infinity. -/
def Fqt_infty := @uniform_space.completion (ratfunc Fq) $ (infty_valued_Fqt Fq).to_uniform_space

instance : field (Fqt_infty Fq) :=
by { letI := infty_valued_Fqt Fq, exact uniform_space.completion.field }

instance : inhabited (Fqt_infty Fq) := ⟨(0 : Fqt_infty Fq)⟩

/-- The valuation at infinity on `k(t)` extends to a valuation on `Fqt_infty`. -/
instance valued_Fqt_infty : valued (Fqt_infty Fq) ℤₘ₀ :=
@valued.valued_completion _ _ _ _ (infty_valued_Fqt Fq)

lemma valued_Fqt_infty.def {x : Fqt_infty Fq} :
  valued.v x = @valued.extension (ratfunc Fq) _ _ _ (infty_valued_Fqt Fq) x := rfl

end infty_valuation

end function_field


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

! This file was ported from Lean 3 source module topology.algebra.star
! leanprover-community/mathlib commit 50832daea47b195a48b5b33b1c8b2162c48c3afc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Star.Pi
import Mathbin.Algebra.Star.Prod
import Mathbin.Topology.Algebra.Constructions
import Mathbin.Topology.ContinuousFunction.Basic

/-!
# Continuity of `star`

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

This file defines the `has_continuous_star` typeclass, along with instances on `pi`, `prod`,
`mul_opposite`, and `units`.
-/

open Filter Topology

open Filter

universe u

variable {ι α R S : Type _}

#print ContinuousStar /-
/-- Basic hypothesis to talk about a topological space with a continuous `star` operator. -/
class ContinuousStar (R : Type u) [TopologicalSpace R] [Star R] : Prop where
  continuous_star : Continuous (star : R → R)
#align has_continuous_star ContinuousStar
-/

export ContinuousStar (continuous_star)

section Continuity

variable [TopologicalSpace R] [Star R] [ContinuousStar R]

#print continuousOn_star /-
theorem continuousOn_star {s : Set R} : ContinuousOn star s :=
  continuous_star.ContinuousOn
#align continuous_on_star continuousOn_star
-/

#print continuousWithinAt_star /-
theorem continuousWithinAt_star {s : Set R} {x : R} : ContinuousWithinAt star s x :=
  continuous_star.ContinuousWithinAt
#align continuous_within_at_star continuousWithinAt_star
-/

#print continuousAt_star /-
theorem continuousAt_star {x : R} : ContinuousAt star x :=
  continuous_star.ContinuousAt
#align continuous_at_star continuousAt_star
-/

#print tendsto_star /-
theorem tendsto_star (a : R) : Tendsto star (𝓝 a) (𝓝 (star a)) :=
  continuousAt_star
#align tendsto_star tendsto_star
-/

/- warning: filter.tendsto.star -> Filter.Tendsto.star is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} R] [_inst_2 : Star.{u2} R] [_inst_3 : ContinuousStar.{u2} R _inst_1 _inst_2] {f : α -> R} {l : Filter.{u1} α} {y : R}, (Filter.Tendsto.{u1, u2} α R f l (nhds.{u2} R _inst_1 y)) -> (Filter.Tendsto.{u1, u2} α R (fun (x : α) => Star.star.{u2} R _inst_2 (f x)) l (nhds.{u2} R _inst_1 (Star.star.{u2} R _inst_2 y)))
but is expected to have type
  forall {α : Type.{u1}} [R : TopologicalSpace.{u1} α] [_inst_1 : Star.{u1} α] [_inst_2 : ContinuousStar.{u1} α R _inst_1] {_inst_3 : Type.{u2}} {f : _inst_3 -> α} {l : Filter.{u2} _inst_3} {y : α}, (Filter.Tendsto.{u2, u1} _inst_3 α f l (nhds.{u1} α R y)) -> (Filter.Tendsto.{u2, u1} _inst_3 α (fun (x : _inst_3) => Star.star.{u1} α _inst_1 (f x)) l (nhds.{u1} α R (Star.star.{u1} α _inst_1 y)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.star Filter.Tendsto.starₓ'. -/
theorem Filter.Tendsto.star {f : α → R} {l : Filter α} {y : R} (h : Tendsto f l (𝓝 y)) :
    Tendsto (fun x => star (f x)) l (𝓝 (star y)) :=
  (continuous_star.Tendsto y).comp h
#align filter.tendsto.star Filter.Tendsto.star

variable [TopologicalSpace α] {f : α → R} {s : Set α} {x : α}

/- warning: continuous.star -> Continuous.star is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} R] [_inst_2 : Star.{u2} R] [_inst_3 : ContinuousStar.{u2} R _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u1} α] {f : α -> R}, (Continuous.{u1, u2} α R _inst_4 _inst_1 f) -> (Continuous.{u1, u2} α R _inst_4 _inst_1 (fun (x : α) => Star.star.{u2} R _inst_2 (f x)))
but is expected to have type
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Star.{u1} α] [_inst_3 : ContinuousStar.{u1} α _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u2} R] {f : R -> α}, (Continuous.{u2, u1} R α _inst_4 _inst_1 f) -> (Continuous.{u2, u1} R α _inst_4 _inst_1 (fun (x : R) => Star.star.{u1} α _inst_2 (f x)))
Case conversion may be inaccurate. Consider using '#align continuous.star Continuous.starₓ'. -/
@[continuity]
theorem Continuous.star (hf : Continuous f) : Continuous fun x => star (f x) :=
  continuous_star.comp hf
#align continuous.star Continuous.star

/- warning: continuous_at.star -> ContinuousAt.star is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} R] [_inst_2 : Star.{u2} R] [_inst_3 : ContinuousStar.{u2} R _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u1} α] {f : α -> R} {x : α}, (ContinuousAt.{u1, u2} α R _inst_4 _inst_1 f x) -> (ContinuousAt.{u1, u2} α R _inst_4 _inst_1 (fun (x : α) => Star.star.{u2} R _inst_2 (f x)) x)
but is expected to have type
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Star.{u1} α] [_inst_3 : ContinuousStar.{u1} α _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u2} R] {f : R -> α} {x : R}, (ContinuousAt.{u2, u1} R α _inst_4 _inst_1 f x) -> (ContinuousAt.{u2, u1} R α _inst_4 _inst_1 (fun (x : R) => Star.star.{u1} α _inst_2 (f x)) x)
Case conversion may be inaccurate. Consider using '#align continuous_at.star ContinuousAt.starₓ'. -/
theorem ContinuousAt.star (hf : ContinuousAt f x) : ContinuousAt (fun x => star (f x)) x :=
  continuousAt_star.comp hf
#align continuous_at.star ContinuousAt.star

/- warning: continuous_on.star -> ContinuousOn.star is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} R] [_inst_2 : Star.{u2} R] [_inst_3 : ContinuousStar.{u2} R _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u1} α] {f : α -> R} {s : Set.{u1} α}, (ContinuousOn.{u1, u2} α R _inst_4 _inst_1 f s) -> (ContinuousOn.{u1, u2} α R _inst_4 _inst_1 (fun (x : α) => Star.star.{u2} R _inst_2 (f x)) s)
but is expected to have type
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Star.{u1} α] [_inst_3 : ContinuousStar.{u1} α _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u2} R] {f : R -> α} {s : Set.{u2} R}, (ContinuousOn.{u2, u1} R α _inst_4 _inst_1 f s) -> (ContinuousOn.{u2, u1} R α _inst_4 _inst_1 (fun (x : R) => Star.star.{u1} α _inst_2 (f x)) s)
Case conversion may be inaccurate. Consider using '#align continuous_on.star ContinuousOn.starₓ'. -/
theorem ContinuousOn.star (hf : ContinuousOn f s) : ContinuousOn (fun x => star (f x)) s :=
  continuous_star.comp_continuousOn hf
#align continuous_on.star ContinuousOn.star

/- warning: continuous_within_at.star -> ContinuousWithinAt.star is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} R] [_inst_2 : Star.{u2} R] [_inst_3 : ContinuousStar.{u2} R _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u1} α] {f : α -> R} {s : Set.{u1} α} {x : α}, (ContinuousWithinAt.{u1, u2} α R _inst_4 _inst_1 f s x) -> (ContinuousWithinAt.{u1, u2} α R _inst_4 _inst_1 (fun (x : α) => Star.star.{u2} R _inst_2 (f x)) s x)
but is expected to have type
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Star.{u1} α] [_inst_3 : ContinuousStar.{u1} α _inst_1 _inst_2] [_inst_4 : TopologicalSpace.{u2} R] {f : R -> α} {s : Set.{u2} R} {x : R}, (ContinuousWithinAt.{u2, u1} R α _inst_4 _inst_1 f s x) -> (ContinuousWithinAt.{u2, u1} R α _inst_4 _inst_1 (fun (x : R) => Star.star.{u1} α _inst_2 (f x)) s x)
Case conversion may be inaccurate. Consider using '#align continuous_within_at.star ContinuousWithinAt.starₓ'. -/
theorem ContinuousWithinAt.star (hf : ContinuousWithinAt f s x) :
    ContinuousWithinAt (fun x => star (f x)) s x :=
  hf.unit
#align continuous_within_at.star ContinuousWithinAt.star

#print starContinuousMap /-
/-- The star operation bundled as a continuous map. -/
@[simps]
def starContinuousMap : C(R, R) :=
  ⟨star, continuous_star⟩
#align star_continuous_map starContinuousMap
-/

end Continuity

section Instances

instance [Star R] [Star S] [TopologicalSpace R] [TopologicalSpace S] [ContinuousStar R]
    [ContinuousStar S] : ContinuousStar (R × S) :=
  ⟨(continuous_star.comp continuous_fst).prod_mk (continuous_star.comp continuous_snd)⟩

instance {C : ι → Type _} [∀ i, TopologicalSpace (C i)] [∀ i, Star (C i)]
    [∀ i, ContinuousStar (C i)] : ContinuousStar (∀ i, C i)
    where continuous_star := continuous_pi fun i => Continuous.star (continuous_apply i)

instance [Star R] [TopologicalSpace R] [ContinuousStar R] : ContinuousStar Rᵐᵒᵖ :=
  ⟨MulOpposite.continuous_op.comp <| MulOpposite.continuous_unop.unit⟩

instance [Monoid R] [StarSemigroup R] [TopologicalSpace R] [ContinuousStar R] : ContinuousStar Rˣ :=
  ⟨continuous_induced_rng.2 Units.continuous_embedProduct.unit⟩

end Instances


/-
Copyright (c) 2022 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying, Rémy Degenne
-/
import probability.process.stopping

/-!
# Hitting time

Given a stochastic process, the hitting time provides the first time the process ``hits'' some
subset of the state space. The hitting time is a stopping time in the case that the time index is
discrete and the process is adapted (this is true in a far more general setting however we have
only proved it for the discrete case so far).

## Main definition

* `measure_theory.hitting`: the hitting time of a stochastic process

## Main results

* `measure_theory.hitting_is_stopping_time`: a discrete hitting time of an adapted process is a
  stopping time

## Implementation notes

In the definition of the hitting time, we bound the hitting time by an upper and lower bound.
This is to ensure that our result is meaningful in the case we are taking the infimum of an
empty set or the infimum of a set which is unbounded from below. With this, we can talk about
hitting times indexed by the natural numbers or the reals. By taking the bounds to be
`⊤` and `⊥`, we obtain the standard definition in the case that the index is `ℕ∞` or `ℝ≥0∞`.

-/

open filter order topological_space
open_locale classical measure_theory nnreal ennreal topology big_operators

namespace measure_theory

variables {Ω β ι : Type*} {m : measurable_space Ω}

/-- Hitting time: given a stochastic process `u` and a set `s`, `hitting u s n m` is the first time
`u` is in `s` after time `n` and before time `m` (if `u` does not hit `s` after time `n` and
before `m` then the hitting time is simply `m`).

The hitting time is a stopping time if the process is adapted and discrete. -/
noncomputable def hitting [preorder ι] [has_Inf ι] (u : ι → Ω → β) (s : set β) (n m : ι) : Ω → ι :=
λ x, if ∃ j ∈ set.Icc n m, u j x ∈ s then Inf (set.Icc n m ∩ {i : ι | u i x ∈ s}) else m

section inequalities

variables [conditionally_complete_linear_order ι] {u : ι → Ω → β} {s : set β} {n i : ι} {ω : Ω}

/-- This lemma is strictly weaker than `hitting_of_le`. -/
lemma hitting_of_lt {m : ι} (h : m < n) : hitting u s n m ω = m :=
begin
  simp_rw [hitting],
  have h_not : ¬ ∃ (j : ι) (H : j ∈ set.Icc n m), u j ω ∈ s,
  { push_neg,
    intro j,
    rw set.Icc_eq_empty_of_lt h,
    simp only [set.mem_empty_iff_false, is_empty.forall_iff], },
  simp only [h_not, if_false],
end

lemma hitting_le {m : ι} (ω : Ω) : hitting u s n m ω ≤ m :=
begin
  cases le_or_lt n m with h_le h_lt,
  { simp only [hitting],
    split_ifs,
    { obtain ⟨j, hj₁, hj₂⟩ := h,
      exact (cInf_le (bdd_below.inter_of_left bdd_below_Icc) (set.mem_inter hj₁ hj₂)).trans hj₁.2 },
    { exact le_rfl }, },
  { rw hitting_of_lt h_lt, },
end

lemma not_mem_of_lt_hitting {m k : ι}
  (hk₁ : k < hitting u s n m ω) (hk₂ : n ≤ k) :
  u k ω ∉ s :=
begin
  classical,
  intro h,
  have hexists : ∃ j ∈ set.Icc n m, u j ω ∈ s,
  refine ⟨k, ⟨hk₂, le_trans hk₁.le $ hitting_le _⟩, h⟩,
  refine not_le.2 hk₁ _,
  simp_rw [hitting, if_pos hexists],
  exact cInf_le bdd_below_Icc.inter_of_left ⟨⟨hk₂, le_trans hk₁.le $ hitting_le _⟩, h⟩,
end

lemma hitting_eq_end_iff {m : ι} :
  hitting u s n m ω = m ↔ (∃ j ∈ set.Icc n m, u j ω ∈ s) →
    Inf (set.Icc n m ∩ {i : ι | u i ω ∈ s}) = m :=
by rw [hitting, ite_eq_right_iff]

lemma hitting_of_le {m : ι} (hmn : m ≤ n) :
  hitting u s n m ω = m :=
begin
  obtain (rfl | h) := le_iff_eq_or_lt.1 hmn,
  { simp only [hitting, set.Icc_self, ite_eq_right_iff, set.mem_Icc, exists_prop,
      forall_exists_index, and_imp],
    intros i hi₁ hi₂ hi,
    rw [set.inter_eq_left_iff_subset.2, cInf_singleton],
    exact set.singleton_subset_iff.2 (le_antisymm hi₂ hi₁ ▸ hi) },
  { exact hitting_of_lt h }
end

lemma le_hitting {m : ι} (hnm : n ≤ m) (ω : Ω) : n ≤ hitting u s n m ω :=
begin
  simp only [hitting],
  split_ifs,
  { refine le_cInf _ (λ b hb, _),
    { obtain ⟨k, hk_Icc, hk_s⟩ := h,
      exact ⟨k, hk_Icc, hk_s⟩, },
    { rw set.mem_inter_iff at hb,
      exact hb.1.1, }, },
  { exact hnm },
end

lemma le_hitting_of_exists {m : ι} (h_exists : ∃ j ∈ set.Icc n m, u j ω ∈ s) :
  n ≤ hitting u s n m ω :=
begin
  refine le_hitting _ ω,
  by_contra,
  rw set.Icc_eq_empty_of_lt (not_le.mp h) at h_exists,
  simpa using h_exists,
end

lemma hitting_mem_Icc {m : ι} (hnm : n ≤ m) (ω : Ω) : hitting u s n m ω ∈ set.Icc n m :=
⟨le_hitting hnm ω, hitting_le ω⟩

lemma hitting_mem_set [is_well_order ι (<)] {m : ι} (h_exists : ∃ j ∈ set.Icc n m, u j ω ∈ s) :
  u (hitting u s n m ω) ω ∈ s :=
begin
  simp_rw [hitting, if_pos h_exists],
  have h_nonempty : (set.Icc n m ∩ {i : ι | u i ω ∈ s}).nonempty,
  { obtain ⟨k, hk₁, hk₂⟩ := h_exists,
    exact ⟨k, set.mem_inter hk₁ hk₂⟩, },
  have h_mem := Inf_mem h_nonempty,
  rw [set.mem_inter_iff] at h_mem,
  exact h_mem.2,
end

lemma hitting_mem_set_of_hitting_lt [is_well_order ι (<)] {m : ι}
  (hl : hitting u s n m ω < m) :
  u (hitting u s n m ω) ω ∈ s :=
begin
  by_cases h : ∃ j ∈ set.Icc n m, u j ω ∈ s,
  { exact hitting_mem_set h },
  { simp_rw [hitting, if_neg h] at hl,
    exact false.elim (hl.ne rfl) }
end

lemma hitting_le_iff_of_exists [is_well_order ι (<)] {m : ι}
  (h_exists : ∃ j ∈ set.Icc n m, u j ω ∈ s) :
  hitting u s n m ω ≤ i ↔ ∃ j ∈ set.Icc n i, u j ω ∈ s :=
begin
  split; intro h',
  { exact ⟨hitting u s n m ω, ⟨le_hitting_of_exists h_exists, h'⟩, hitting_mem_set h_exists⟩, },
  { have h'' : ∃ k ∈ set.Icc n (min m i), u k ω ∈ s,
    { obtain ⟨k₁, hk₁_mem, hk₁_s⟩ := h_exists,
      obtain ⟨k₂, hk₂_mem, hk₂_s⟩ := h',
      refine ⟨min k₁ k₂, ⟨le_min hk₁_mem.1 hk₂_mem.1, min_le_min hk₁_mem.2 hk₂_mem.2⟩, _⟩,
      exact min_rec' (λ j, u j ω ∈ s) hk₁_s hk₂_s, },
    obtain ⟨k, hk₁, hk₂⟩ := h'',
    refine le_trans _ (hk₁.2.trans (min_le_right _ _)),
    exact hitting_le_of_mem hk₁.1 (hk₁.2.trans (min_le_left _ _)) hk₂, },
end

lemma hitting_le_iff_of_lt [is_well_order ι (<)] {m : ι} (i : ι) (hi : i < m) :
  hitting u s n m ω ≤ i ↔ ∃ j ∈ set.Icc n i, u j ω ∈ s :=
begin
  by_cases h_exists : ∃ j ∈ set.Icc n m, u j ω ∈ s,
  { rw hitting_le_iff_of_exists h_exists, },
  { simp_rw [hitting, if_neg h_exists],
    push_neg at h_exists,
    simp only [not_le.mpr hi, set.mem_Icc, false_iff, not_exists, and_imp],
    exact λ k hkn hki, h_exists k ⟨hkn, hki.trans hi.le⟩, },
end

lemma hitting_lt_iff [is_well_order ι (<)] {m : ι} (i : ι) (hi : i ≤ m) :
  hitting u s n m ω < i ↔ ∃ j ∈ set.Ico n i, u j ω ∈ s :=
begin
  split; intro h',
  { have h : ∃ j ∈ set.Icc n m, u j ω ∈ s,
    { by_contra,
      simp_rw [hitting, if_neg h, ← not_le] at h',
      exact h' hi, },
    exact ⟨hitting u s n m ω, ⟨le_hitting_of_exists h, h'⟩, hitting_mem_set h⟩, },
  { obtain ⟨k, hk₁, hk₂⟩ := h',
    refine lt_of_le_of_lt _ hk₁.2,
    exact hitting_le_of_mem hk₁.1 (hk₁.2.le.trans hi) hk₂, },
end

lemma hitting_eq_hitting_of_exists
  {m₁ m₂ : ι} (h : m₁ ≤ m₂) (h' : ∃ j ∈ set.Icc n m₁, u j ω ∈ s) :
  hitting u s n m₁ ω = hitting u s n m₂ ω :=
begin
  simp only [hitting, if_pos h'],
  obtain ⟨j, hj₁, hj₂⟩ := h',
  rw if_pos,
  { refine le_antisymm _ (cInf_le_cInf bdd_below_Icc.inter_of_left ⟨j, hj₁, hj₂⟩
      (set.inter_subset_inter_left _ (set.Icc_subset_Icc_right h))),
    refine le_cInf ⟨j, set.Icc_subset_Icc_right h hj₁, hj₂⟩ (λ i hi, _),
    by_cases hi' : i ≤ m₁,
    { exact cInf_le bdd_below_Icc.inter_of_left ⟨⟨hi.1.1, hi'⟩, hi.2⟩ },
    { exact ((cInf_le bdd_below_Icc.inter_of_left ⟨hj₁, hj₂⟩).trans (hj₁.2.trans le_rfl)).trans
        (le_of_lt (not_le.1 hi')) } },
  exact ⟨j, ⟨hj₁.1, hj₁.2.trans h⟩, hj₂⟩,
end

lemma hitting_mono {m₁ m₂ : ι} (hm : m₁ ≤ m₂) :
  hitting u s n m₁ ω ≤ hitting u s n m₂ ω :=
begin
  by_cases h : ∃ j ∈ set.Icc n m₁, u j ω ∈ s,
  { exact (hitting_eq_hitting_of_exists hm h).le },
  { simp_rw [hitting, if_neg h],
    split_ifs with h',
    { obtain ⟨j, hj₁, hj₂⟩ := h',
      refine le_cInf ⟨j, hj₁, hj₂⟩ _,
      by_contra hneg, push_neg at hneg,
      obtain ⟨i, hi₁, hi₂⟩ := hneg,
      exact h ⟨i, ⟨hi₁.1.1, hi₂.le⟩, hi₁.2⟩ },
    { exact hm } }
end

end inequalities

/-- A discrete hitting time is a stopping time. -/
lemma hitting_is_stopping_time
  [conditionally_complete_linear_order ι] [is_well_order ι (<)] [countable ι]
  [topological_space β] [pseudo_metrizable_space β] [measurable_space β] [borel_space β]
  {f : filtration ι m} {u : ι → Ω → β} {s : set β} {n n' : ι}
  (hu : adapted f u) (hs : measurable_set s) :
  is_stopping_time f (hitting u s n n') :=
begin
  intro i,
  cases le_or_lt n' i with hi hi,
  { have h_le : ∀ ω, hitting u s n n' ω ≤ i := λ x, (hitting_le x).trans hi,
    simp [h_le], },
  { have h_set_eq_Union : {ω | hitting u s n n' ω ≤ i} = ⋃ j ∈ set.Icc n i, u j ⁻¹' s,
    { ext x,
      rw [set.mem_set_of_eq, hitting_le_iff_of_lt _ hi],
      simp only [set.mem_Icc, exists_prop, set.mem_Union, set.mem_preimage], },
    rw h_set_eq_Union,
    exact measurable_set.Union (λ j, measurable_set.Union $
      λ hj, f.mono hj.2 _ ((hu j).measurable hs)) }
end

lemma stopped_value_hitting_mem [conditionally_complete_linear_order ι] [is_well_order ι (<)]
  {u : ι → Ω → β} {s : set β} {n m : ι} {ω : Ω} (h : ∃ j ∈ set.Icc n m, u j ω ∈ s) :
  stopped_value u (hitting u s n m) ω ∈ s :=
begin
  simp only [stopped_value, hitting, if_pos h],
  obtain ⟨j, hj₁, hj₂⟩ := h,
  have : Inf (set.Icc n m ∩ {i | u i ω ∈ s}) ∈ set.Icc n m ∩ {i | u i ω ∈ s} :=
    Inf_mem (set.nonempty_of_mem ⟨hj₁, hj₂⟩),
  exact this.2,
end

/-- The hitting time of a discrete process with the starting time indexed by a stopping time
is a stopping time. -/
lemma is_stopping_time_hitting_is_stopping_time
  [conditionally_complete_linear_order ι] [is_well_order ι (<)] [countable ι]
  [topological_space ι] [order_topology ι] [first_countable_topology ι]
  [topological_space β] [pseudo_metrizable_space β] [measurable_space β] [borel_space β]
  {f : filtration ι m} {u : ι → Ω → β} {τ : Ω → ι} (hτ : is_stopping_time f τ)
  {N : ι} (hτbdd : ∀ x, τ x ≤ N) {s : set β} (hs : measurable_set s) (hf : adapted f u) :
  is_stopping_time f (λ x, hitting u s (τ x) N x) :=
begin
  intro n,
  have h₁ : {x | hitting u s (τ x) N x ≤ n} =
    (⋃ i ≤ n, {x | τ x = i} ∩ {x | hitting u s i N x ≤ n}) ∪
    (⋃ i > n, {x | τ x = i} ∩ {x | hitting u s i N x ≤ n}),
  { ext x,
    simp [← exists_or_distrib, ← or_and_distrib_right, le_or_lt] },
  have h₂ : (⋃ i > n, {x | τ x = i} ∩ {x | hitting u s i N x ≤ n}) = ∅,
  { ext x,
    simp only [gt_iff_lt, set.mem_Union, set.mem_inter_iff, set.mem_set_of_eq,
      exists_prop, set.mem_empty_iff_false, iff_false, not_exists, not_and, not_le],
    rintro m hm rfl,
    exact lt_of_lt_of_le hm (le_hitting (hτbdd _) _) },
  rw [h₁, h₂, set.union_empty],
  exact measurable_set.Union (λ i, measurable_set.Union
    (λ hi, (f.mono hi _ (hτ.measurable_set_eq i)).inter (hitting_is_stopping_time hf hs n))),
end

section complete_lattice

variables [complete_lattice ι] {u : ι → Ω → β} {s : set β} {f : filtration ι m}

lemma hitting_eq_Inf (ω : Ω) : hitting u s ⊥ ⊤ ω = Inf {i : ι | u i ω ∈ s} :=
begin
  simp only [hitting, set.mem_Icc, bot_le, le_top, and_self, exists_true_left, set.Icc_bot,
    set.Iic_top, set.univ_inter, ite_eq_left_iff, not_exists],
  intro h_nmem_s,
  symmetry,
  rw Inf_eq_top,
  exact λ i hi_mem_s, absurd hi_mem_s (h_nmem_s i),
end

end complete_lattice

section conditionally_complete_linear_order_bot

variables [conditionally_complete_linear_order_bot ι] [is_well_order ι (<)]
variables {u : ι → Ω → β} {s : set β} {f : filtration ℕ m}

lemma hitting_bot_le_iff {i n : ι} {ω : Ω} (hx : ∃ j, j ≤ n ∧ u j ω ∈ s) :
  hitting u s ⊥ n ω ≤ i ↔ ∃ j ≤ i, u j ω ∈ s :=
begin
  cases lt_or_le i n with hi hi,
  { rw hitting_le_iff_of_lt _ hi,
    simp, },
  { simp only [(hitting_le ω).trans hi, true_iff],
    obtain ⟨j, hj₁, hj₂⟩ := hx,
    exact ⟨j, hj₁.trans hi, hj₂⟩, },
end

end conditionally_complete_linear_order_bot

end measure_theory


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kenny Lau
-/
import data.list.pairwise
import data.list.forall2

/-!
# Lists with no duplicates

`list.nodup` is defined in `data/list/defs`. In this file we prove various properties of this
predicate.
-/

universes u v

open nat function

variables {α : Type u} {β : Type v}

namespace list

@[simp] theorem forall_mem_ne {a : α} {l : list α} : (∀ (a' : α), a' ∈ l → ¬a = a') ↔ a ∉ l :=
⟨λ h m, h _ m rfl, λ h a' m e, h (e.symm ▸ m)⟩

@[simp] theorem nodup_nil : @nodup α [] := pairwise.nil

@[simp] theorem nodup_cons {a : α} {l : list α} : nodup (a::l) ↔ a ∉ l ∧ nodup l :=
by simp only [nodup, pairwise_cons, forall_mem_ne]

protected lemma pairwise.nodup {l : list α} {r : α → α → Prop} [is_irrefl α r] (h : pairwise r l) :
  nodup l :=
h.imp $ λ a b, ne_of_irrefl

lemma rel_nodup {r : α → β → Prop} (hr : relator.bi_unique r) : (forall₂ r ⇒ (↔)) nodup nodup
| _ _ forall₂.nil      := by simp only [nodup_nil]
| _ _ (forall₂.cons hab h) :=
  by simpa only [nodup_cons] using relator.rel_and (relator.rel_not (rel_mem hr hab h))
    (rel_nodup h)

theorem nodup_cons_of_nodup {a : α} {l : list α} (m : a ∉ l) (n : nodup l) : nodup (a::l) :=
nodup_cons.2 ⟨m, n⟩

theorem nodup_singleton (a : α) : nodup [a] :=
nodup_cons_of_nodup (not_mem_nil a) nodup_nil

theorem nodup_of_nodup_cons {a : α} {l : list α} (h : nodup (a::l)) : nodup l :=
(nodup_cons.1 h).2

theorem not_mem_of_nodup_cons {a : α} {l : list α} (h : nodup (a::l)) : a ∉ l :=
(nodup_cons.1 h).1

theorem not_nodup_cons_of_mem {a : α} {l : list α} : a ∈ l → ¬ nodup (a :: l) :=
imp_not_comm.1 not_mem_of_nodup_cons

theorem nodup_of_sublist {l₁ l₂ : list α} : l₁ <+ l₂ → nodup l₂ → nodup l₁ :=
pairwise_of_sublist

theorem not_nodup_pair (a : α) : ¬ nodup [a, a] :=
not_nodup_cons_of_mem $ mem_singleton_self _

theorem nodup_iff_sublist {l : list α} : nodup l ↔ ∀ a, ¬ [a, a] <+ l :=
⟨λ d a h, not_nodup_pair a (nodup_of_sublist h d), begin
  induction l with a l IH; intro h, {exact nodup_nil},
  exact nodup_cons_of_nodup
    (λ al, h a $ cons_sublist_cons _ $ singleton_sublist.2 al)
    (IH $ λ a s, h a $ sublist_cons_of_sublist _ s)
end⟩

theorem nodup_iff_nth_le_inj {l : list α} :
  nodup l ↔ ∀ i j h₁ h₂, nth_le l i h₁ = nth_le l j h₂ → i = j :=
pairwise_iff_nth_le.trans
⟨λ H i j h₁ h₂ h, ((lt_trichotomy _ _)
  .resolve_left (λ h', H _ _ h₂ h' h))
  .resolve_right (λ h', H _ _ h₁ h' h.symm),
 λ H i j h₁ h₂ h, ne_of_lt h₂ (H _ _ _ _ h)⟩

lemma nodup.ne_singleton_iff {l : list α} (h : nodup l) (x : α) :
  l ≠ [x] ↔ l = [] ∨ ∃ y ∈ l, y ≠ x :=
begin
  induction l with hd tl hl,
  { simp },
  { specialize hl (nodup_of_nodup_cons h),
    by_cases hx : tl = [x],
    { simpa [hx, and.comm, and_or_distrib_left] using h },
    { rw [←ne.def, hl] at hx,
      rcases hx with rfl | ⟨y, hy, hx⟩,
      { simp },
      { have : tl ≠ [] := ne_nil_of_mem hy,
        suffices : ∃ (y : α) (H : y ∈ hd :: tl), y ≠ x,
          { simpa [ne_nil_of_mem hy] },
        exact ⟨y, mem_cons_of_mem _ hy, hx⟩ } } }
end

lemma nth_le_eq_of_ne_imp_not_nodup (xs : list α) (n m : ℕ) (hn : n < xs.length)
  (hm : m < xs.length) (h : xs.nth_le n hn = xs.nth_le m hm) (hne : n ≠ m) :
  ¬ nodup xs :=
begin
  rw nodup_iff_nth_le_inj,
  simp only [exists_prop, exists_and_distrib_right, not_forall],
  exact ⟨n, m, ⟨hn, hm, h⟩, hne⟩
end

@[simp] theorem nth_le_index_of [decidable_eq α] {l : list α} (H : nodup l) (n h) :
  index_of (nth_le l n h) l = n :=
nodup_iff_nth_le_inj.1 H _ _ _ h $
index_of_nth_le $ index_of_lt_length.2 $ nth_le_mem _ _ _

theorem nodup_iff_count_le_one [decidable_eq α] {l : list α} : nodup l ↔ ∀ a, count a l ≤ 1 :=
nodup_iff_sublist.trans $ forall_congr $ λ a,
have [a, a] <+ l ↔ 1 < count a l, from (@le_count_iff_repeat_sublist _ _ a l 2).symm,
(not_congr this).trans not_lt

theorem nodup_repeat (a : α) : ∀ {n : ℕ}, nodup (repeat a n) ↔ n ≤ 1
| 0 := by simp [nat.zero_le]
| 1 := by simp
| (n+2) := iff_of_false
  (λ H, nodup_iff_sublist.1 H a ((repeat_sublist_repeat _).2 (le_add_left 2 n)))
  (not_le_of_lt $ le_add_left 2 n)

@[simp] theorem count_eq_one_of_mem [decidable_eq α] {a : α} {l : list α}
  (d : nodup l) (h : a ∈ l) : count a l = 1 :=
le_antisymm (nodup_iff_count_le_one.1 d a) (count_pos.2 h)

theorem nodup_of_nodup_append_left {l₁ l₂ : list α} : nodup (l₁++l₂) → nodup l₁ :=
nodup_of_sublist (sublist_append_left l₁ l₂)

theorem nodup_of_nodup_append_right {l₁ l₂ : list α} : nodup (l₁++l₂) → nodup l₂ :=
nodup_of_sublist (sublist_append_right l₁ l₂)

theorem nodup_append {l₁ l₂ : list α} : nodup (l₁++l₂) ↔ nodup l₁ ∧ nodup l₂ ∧ disjoint l₁ l₂ :=
by simp only [nodup, pairwise_append, disjoint_iff_ne]

theorem disjoint_of_nodup_append {l₁ l₂ : list α} (d : nodup (l₁++l₂)) : disjoint l₁ l₂ :=
(nodup_append.1 d).2.2

theorem nodup_append_of_nodup {l₁ l₂ : list α} (d₁ : nodup l₁) (d₂ : nodup l₂)
  (dj : disjoint l₁ l₂) : nodup (l₁++l₂) :=
nodup_append.2 ⟨d₁, d₂, dj⟩

theorem nodup_append_comm {l₁ l₂ : list α} : nodup (l₁++l₂) ↔ nodup (l₂++l₁) :=
by simp only [nodup_append, and.left_comm, disjoint_comm]

theorem nodup_middle {a : α} {l₁ l₂ : list α} : nodup (l₁ ++ a::l₂) ↔ nodup (a::(l₁++l₂)) :=
by simp only [nodup_append, not_or_distrib, and.left_comm, and_assoc, nodup_cons, mem_append,
  disjoint_cons_right]

theorem nodup_of_nodup_map (f : α → β) {l : list α} : nodup (map f l) → nodup l :=
pairwise_of_pairwise_map f $ λ a b, mt $ congr_arg f

theorem nodup_map_on {f : α → β} {l : list α} (H : ∀x∈l, ∀y∈l, f x = f y → x = y)
  (d : nodup l) : nodup (map f l) :=
pairwise_map_of_pairwise _ (by exact λ a b ⟨ma, mb, n⟩ e, n (H a ma b mb e)) (pairwise.and_mem.1 d)

theorem nodup_map_iff_inj_on {f : α → β} {l : list α} (d : nodup l) :
  nodup (map f l) ↔ (∀ (x ∈ l) (y ∈ l), f x = f y → x = y) :=
⟨inj_on_of_nodup_map, λ h, nodup_map_on h d⟩

theorem nodup_map {f : α → β} {l : list α} (hf : injective f) : nodup l → nodup (map f l) :=
nodup_map_on (assume x _ y _ h, hf h)

theorem nodup_map_iff {f : α → β} {l : list α} (hf : injective f) : nodup (map f l) ↔ nodup l :=
⟨nodup_of_nodup_map _, nodup_map hf⟩

@[simp] theorem nodup_attach {l : list α} : nodup (attach l) ↔ nodup l :=
⟨λ h, attach_map_val l ▸ nodup_map (λ a b, subtype.eq) h,
 λ h, nodup_of_nodup_map subtype.val ((attach_map_val l).symm ▸ h)⟩

theorem nodup_pmap {p : α → Prop} {f : Π a, p a → β} {l : list α} {H}
  (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : nodup l) : nodup (pmap f l H) :=
by rw [pmap_eq_map_attach]; exact nodup_map
  (λ ⟨a, ha⟩ ⟨b, hb⟩ h, by congr; exact hf a (H _ ha) b (H _ hb) h)
  (nodup_attach.2 h)

theorem nodup_filter (p : α → Prop) [decidable_pred p] {l} : nodup l → nodup (filter p l) :=
pairwise_filter_of_pairwise p

@[simp] theorem nodup_reverse {l : list α} : nodup (reverse l) ↔ nodup l :=
pairwise_reverse.trans $ by simp only [nodup, ne.def, eq_comm]

theorem nodup_erase_eq_filter [decidable_eq α] (a : α) {l} (d : nodup l) :
  l.erase a = filter (≠ a) l :=
begin
  induction d with b l m d IH, {refl},
  by_cases b = a,
  { subst h, rw [erase_cons_head, filter_cons_of_neg],
    symmetry, rw filter_eq_self, simpa only [ne.def, eq_comm] using m, exact not_not_intro rfl },
  { rw [erase_cons_tail _ h, filter_cons_of_pos, IH], exact h }
end

theorem nodup_erase_of_nodup [decidable_eq α] (a : α) {l} : nodup l → nodup (l.erase a) :=
nodup_of_sublist (erase_sublist _ _)

theorem nodup_diff [decidable_eq α] : ∀ {l₁ l₂ : list α} (h : l₁.nodup), (l₁.diff l₂).nodup
| l₁ []      h := h
| l₁ (a::l₂) h := by rw diff_cons; exact nodup_diff (nodup_erase_of_nodup _ h)

theorem mem_erase_iff_of_nodup [decidable_eq α] {a b : α} {l} (d : nodup l) :
  a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l :=
by rw nodup_erase_eq_filter b d; simp only [mem_filter, and_comm]

theorem mem_erase_of_nodup [decidable_eq α] {a : α} {l} (h : nodup l) : a ∉ l.erase a :=
λ H, ((mem_erase_iff_of_nodup h).1 H).1 rfl

theorem nodup_join {L : list (list α)} :
  nodup (join L) ↔ (∀ l ∈ L, nodup l) ∧ pairwise disjoint L :=
by simp only [nodup, pairwise_join, disjoint_left.symm, forall_mem_ne]

theorem nodup_bind {l₁ : list α} {f : α → list β} : nodup (l₁.bind f) ↔
  (∀ x ∈ l₁, nodup (f x)) ∧ pairwise (λ (a b : α), disjoint (f a) (f b)) l₁ :=
by simp only [list.bind, nodup_join, pairwise_map, and_comm, and.left_comm, mem_map,
  exists_imp_distrib, and_imp];
   rw [show (∀ (l : list β) (x : α), f x = l → x ∈ l₁ → nodup l) ↔
            (∀ (x : α), x ∈ l₁ → nodup (f x)),
       from forall_swap.trans $ forall_congr $ λ_, forall_eq']

theorem nodup_product {l₁ : list α} {l₂ : list β} (d₁ : nodup l₁) (d₂ : nodup l₂) :
  nodup (product l₁ l₂) :=
 nodup_bind.2
  ⟨λ a ma, nodup_map (left_inverse.injective (λ b, (rfl : (a,b).2 = b))) d₂,
  d₁.imp $ λ a₁ a₂ n x h₁ h₂, begin
    rcases mem_map.1 h₁ with ⟨b₁, mb₁, rfl⟩,
    rcases mem_map.1 h₂ with ⟨b₂, mb₂, ⟨⟩⟩,
    exact n rfl
  end⟩

theorem nodup_sigma {σ : α → Type*} {l₁ : list α} {l₂ : Π a, list (σ a)}
  (d₁ : nodup l₁) (d₂ : ∀ a, nodup (l₂ a)) : nodup (l₁.sigma l₂) :=
 nodup_bind.2
  ⟨λ a ma, nodup_map (λ b b' h, by injection h with _ h; exact eq_of_heq h) (d₂ a),
  d₁.imp $ λ a₁ a₂ n x h₁ h₂, begin
    rcases mem_map.1 h₁ with ⟨b₁, mb₁, rfl⟩,
    rcases mem_map.1 h₂ with ⟨b₂, mb₂, ⟨⟩⟩,
    exact n rfl
  end⟩

theorem nodup_filter_map {f : α → option β} {l : list α}
  (H : ∀ (a a' : α) (b : β), b ∈ f a → b ∈ f a' → a = a') :
  nodup l → nodup (filter_map f l) :=
pairwise_filter_map_of_pairwise f $ λ a a' n b bm b' bm' e, n $ H a a' b' (e ▸ bm) bm'

theorem nodup_concat {a : α} {l : list α} (h : a ∉ l) (h' : nodup l) : nodup (concat l a) :=
by rw concat_eq_append; exact nodup_append_of_nodup h' (nodup_singleton _) (disjoint_singleton.2 h)

theorem nodup_insert [decidable_eq α] {a : α} {l : list α} (h : nodup l) : nodup (insert a l) :=
if h' : a ∈ l then by rw [insert_of_mem h']; exact h
else by rw [insert_of_not_mem h', nodup_cons]; split; assumption

theorem nodup_union [decidable_eq α] (l₁ : list α) {l₂ : list α} (h : nodup l₂) :
  nodup (l₁ ∪ l₂) :=
begin
  induction l₁ with a l₁ ih generalizing l₂,
  { exact h },
  apply nodup_insert,
  exact ih h
end

theorem nodup_inter_of_nodup [decidable_eq α] {l₁ : list α} (l₂) : nodup l₁ → nodup (l₁ ∩ l₂) :=
nodup_filter _

@[simp] theorem nodup_sublists {l : list α} : nodup (sublists l) ↔ nodup l :=
⟨λ h, nodup_of_nodup_map _ (nodup_of_sublist (map_ret_sublist_sublists _) h),
 λ h, (pairwise_sublists h).imp (λ _ _ h, mt reverse_inj.2 h.to_ne)⟩

@[simp] theorem nodup_sublists' {l : list α} : nodup (sublists' l) ↔ nodup l :=
by rw [sublists'_eq_sublists, nodup_map_iff reverse_injective,
       nodup_sublists, nodup_reverse]

lemma nodup_sublists_len {α : Type*} (n) {l : list α}
  (nd : nodup l) : (sublists_len n l).nodup :=
nodup_of_sublist (sublists_len_sublist_sublists' _ _) (nodup_sublists'.2 nd)

lemma diff_eq_filter_of_nodup [decidable_eq α] :
  ∀ {l₁ l₂ : list α} (hl₁ : l₁.nodup), l₁.diff l₂ = l₁.filter (∉ l₂)
| l₁ []      hl₁ := by simp
| l₁ (a::l₂) hl₁ :=
begin
  rw [diff_cons, diff_eq_filter_of_nodup (nodup_erase_of_nodup _ hl₁),
    nodup_erase_eq_filter _ hl₁, filter_filter],
  simp only [mem_cons_iff, not_or_distrib, and.comm],
  congr
end

lemma mem_diff_iff_of_nodup [decidable_eq α] {l₁ l₂ : list α} (hl₁ : l₁.nodup) {a : α} :
  a ∈ l₁.diff l₂ ↔ a ∈ l₁ ∧ a ∉ l₂ :=
by rw [diff_eq_filter_of_nodup hl₁, mem_filter]

lemma nodup_update_nth : ∀ {l : list α} {n : ℕ} {a : α} (hl : l.nodup) (ha : a ∉ l),
  (l.update_nth n a).nodup
| []     n     a hl ha := nodup_nil
| (b::l) 0     a hl ha := nodup_cons.2 ⟨mt (mem_cons_of_mem _) ha, (nodup_cons.1 hl).2⟩
| (b::l) (n+1) a hl ha := nodup_cons.2
  ⟨λ h, (mem_or_eq_of_mem_update_nth h).elim
      (nodup_cons.1 hl).1
      (λ hba, ha (hba ▸ mem_cons_self _ _)),
    nodup_update_nth (nodup_cons.1 hl).2 (mt (mem_cons_of_mem _) ha)⟩

lemma nodup.map_update [decidable_eq α] {l : list α} (hl : l.nodup) (f : α → β) (x : α) (y : β) :
  l.map (function.update f x y) =
    if x ∈ l then (l.map f).update_nth (l.index_of x) y else l.map f :=
begin
  induction l with hd tl ihl, { simp },
  rw [nodup_cons] at hl,
  simp only [mem_cons_iff, map, ihl hl.2],
  by_cases H : hd = x,
  { subst hd,
    simp [update_nth, hl.1] },
  { simp [ne.symm H, H, update_nth, ← apply_ite (cons (f hd))] }
end

end list

theorem option.to_list_nodup {α} : ∀ o : option α, o.to_list.nodup
| none     := list.nodup_nil
| (some x) := list.nodup_singleton x