/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module

prelude
public import Init.Control.Basic
public import Init.Grind.Tactics

public section

@[expose] section

namespace Option

instance instDecidableEq {α} [inst : DecidableEq α] : DecidableEq (Option α) := fun a b =>
  /-
  Structured for compatibility with the decidable-equality-with-none instances.
  -/
  match a with
  | none => match b with
    | none => .isTrue rfl
    | some _ => .isFalse (fun h => Option.noConfusion rfl (heq_of_eq h))
  | some a => match b with
    | none => .isFalse (fun h => Option.noConfusion rfl (heq_of_eq h))
    | some b => match inst a b with
      | .isTrue h => .isTrue (h ▸ rfl)
      | .isFalse n => .isFalse (fun h => Option.noConfusion rfl (heq_of_eq h) (fun h' => absurd (eq_of_heq h') n))

/--
Equality with `none` is decidable even if the wrapped type does not have decidable equality.
-/
instance decidableEqNone (o : Option α) : Decidable (o = none) :=
  /- We use a `match` instead of transferring from `isNone_iff_eq_none` for
    compatibility with the `DecidableEq` instance. -/
  match o with
  | none => .isTrue rfl
  | some _ => .isFalse (fun h => Option.noConfusion rfl (heq_of_eq h))

/--
Equality with `none` is decidable even if the wrapped type does not have decidable equality.
-/
instance decidableNoneEq (o : Option α) : Decidable (none = o) :=
  /- We use a `match` instead of transferring from `isNone_iff_eq_none` for
    compatibility with the `DecidableEq` instance. -/
  match o with
  | none => .isTrue rfl
  | some _ => .isFalse (fun h => Option.noConfusion rfl (heq_of_eq h))

deriving instance BEq for Option

@[simp, grind =] theorem getD_none : getD none a = a := rfl
@[simp, grind =] theorem getD_some : getD (some a) b = a := rfl

@[simp, grind =] theorem map_none (f : α → β) : none.map f = none := rfl
@[simp, grind =] theorem map_some (a) (f : α → β) : (some a).map f = some (f a) := rfl

/-- Lifts an optional value to any `Alternative`, sending `none` to `failure`. -/
def getM [Alternative m] : Option α → m α
  | none     => failure
  | some a   => pure a

@[simp, grind =] theorem getM_none [Alternative m] : getM none = (failure : m α) := rfl
@[simp, grind =] theorem getM_some [Alternative m] {a : α} : getM (some a) = (pure a : m α) := rfl

/-- Returns `true` on `some x` and `false` on `none`. -/
@[inline] def isSome : Option α → Bool
  | some _ => true
  | none   => false

@[simp, grind =] theorem isSome_none : @isSome α none = false := rfl
@[simp, grind =] theorem isSome_some : isSome (some a) = true := rfl

/--
Returns `true` on `none` and `false` on `some x`.

This function is more flexible than `(· == none)` because it does not require a `BEq α` instance.

Examples:
 * `(none : Option Nat).isNone = true`
 * `(some Nat.add).isNone = false`
-/
@[inline] def isNone : Option α → Bool
  | some _ => false
  | none   => true

@[simp, grind =] theorem isNone_none : @isNone α none = true := rfl
@[simp, grind =] theorem isNone_some : isNone (some a) = false := rfl

/--
Checks whether an optional value is both present and equal to some other value.

Given `x? : Option α` and `y : α`, `x?.isEqSome y` is equivalent to `x? == some y`. It is more
efficient because it avoids an allocation.
-/
@[inline] def isEqSome [BEq α] : Option α → α → Bool
  | some a, b => a == b
  | none,   _ => false

/--
Sequencing of `Option` computations.

From the perspective of `Option` as computations that might fail, this function sequences
potentially-failing computations, failing if either fails. From the perspective of `Option` as a
collection with at most one element, the function is applied to the element if present, and the
final result is empty if either the initial or the resulting collections are empty.

This function is often accessed via the `>>=` operator from the `Bind (Option α)` instance, or
implicitly via `do`-notation, but it is also idiomatic to call it using [generalized field
notation](lean-manual://section/generalized-field-notation).

Examples:
 * `none.bind (fun x => some x) = none`
 * `(some 4).bind (fun x => some x) = some 4`
 * `none.bind (Option.guard (· > 2)) = none`
 * `(some 2).bind (Option.guard (· > 2)) = none`
 * `(some 4).bind (Option.guard (· > 2)) = some 4`
-/
@[inline] protected def bind : Option α → (α → Option β) → Option β
  | none,   _ => none
  | some a, f => f a

@[simp, grind =] theorem bind_none (f : α → Option β) : none.bind f = none := rfl
@[simp, grind =] theorem bind_some (a) (f : α → Option β) : (some a).bind f = f a := rfl

@[deprecated bind_none (since := "2025-05-03")]
abbrev none_bind := @bind_none
@[deprecated bind_some (since := "2025-05-03")]
abbrev some_bind := @bind_some

/--
Runs the monadic action `f` on `o`'s value, if any, and returns the result, or  `none` if there is
no value.

From the perspective of `Option` as a collection with at most one element, the monadic the function
is applied to the element if present, and the final result is empty if either the initial or the
resulting collections are empty.
-/
@[inline] protected def bindM [Pure m] (f : α → m (Option β)) : Option α → m (Option β)
  | none => pure none
  | some a => f a

/--
Applies a function in some applicative functor to an optional value, returning `none` with no
effects if the value is missing.

Runs a monadic function `f` on an optional value, returning the result. If the optional value is
`none`, the function is not called and the result is also `none`.

From the perspective of `Option` as a container with at most one element, this is analogous to
`List.mapM`, returning the result of running the monadic function on all elements of the container.

This function only requires `m` to be an applicative functor. An alias `Option.mapA` is provided.
-/
@[inline] protected def mapM [Applicative m] (f : α → m β) : Option α → m (Option β)
  | none => pure none
  | some x => some <$> f x

@[simp, grind =] theorem mapM_none [Applicative m] (f : α → m β) : none.mapM f = pure none := rfl
@[simp, grind =] theorem mapM_some [Applicative m] (x) (f : α → m β) : (some x).mapM f = some <$> f x := rfl

/--
Applies a function in some applicative functor to an optional value, returning `none` with no
effects if the value is missing.

This is an alias for `Option.mapM`, which already works for applicative functors.
-/
@[inline] protected def mapA [Applicative m] (f : α → m β) : Option α → m (Option β) :=
  Option.mapM f

/-- For verification purposes, we replace `mapA` with `mapM`. -/
@[simp, grind =] theorem mapA_eq_mapM [Applicative m] {f : α → m β} : Option.mapA f o = Option.mapM f o := rfl

@[simp, grind =]
theorem map_id : (Option.map id : Option α → Option α) = id :=
  funext (fun o => match o with | none => rfl | some _ => rfl)

/--
Keeps an optional value only if it satisfies a monadic Boolean predicate.

If `Option` is thought of as a collection that contains at most one element, then `Option.filterM`
is analogous to `List.filterM`.
-/
@[inline] protected def filterM [Applicative m] (p : α → m Bool) : Option α → m (Option α)
  | none => pure none
  | some a => (fun b => if b then some a else none) <$> p a

/--
Keeps an optional value only if it satisfies a Boolean predicate.

If `Option` is thought of as a collection that contains at most one element, then `Option.filter` is
analogous to `List.filter` or `Array.filter`.

Examples:
 * `(some 5).filter (· % 2 == 0) = none`
 * `(some 4).filter (· % 2 == 0) = some 4`
 * `none.filter (fun x : Nat => x % 2 == 0) = none`
 * `none.filter (fun x : Nat => true) = none`
-/
@[always_inline, inline] protected def filter (p : α → Bool) : Option α → Option α
  | some a => if p a then some a else none
  | none   => none

/--
Checks whether an optional value either satisfies a Boolean predicate or is `none`.

Examples:
 * `(some 33).all (· % 2 == 0) = false
 * `(some 22).all (· % 2 == 0) = true
 * `none.all (fun x : Nat => x % 2 == 0) = true
-/
@[always_inline, inline] protected def all (p : α → Bool) : Option α → Bool
  | some a => p a
  | none   => true

@[simp, grind =] theorem all_none : Option.all p none = true := rfl
@[simp, grind =] theorem all_some : Option.all p (some x) = p x := rfl

/--
Checks whether an optional value is not `none` and satisfies a Boolean predicate.

Examples:
 * `(some 33).any (· % 2 == 0) = false
 * `(some 22).any (· % 2 == 0) = true
 * `none.any (fun x : Nat => true) = false
-/
@[always_inline, inline] protected def any (p : α → Bool) : Option α → Bool
  | some a => p a
  | none   => false

@[simp, grind =] theorem any_none : Option.any p none = false := rfl
@[simp, grind =] theorem any_some : Option.any p (some x) = p x := rfl

/--
Implementation of `OrElse`'s `<|>` syntax for `Option`. If the first argument is `some a`, returns
`some a`, otherwise evaluates and returns the second argument.

See also `or` for a version that is strict in the second argument.
-/
@[always_inline, macro_inline] protected def orElse : Option α → (Unit → Option α) → Option α
  | some a, _ => some a
  | none,   b => b ()

@[simp, grind =] theorem orElse_some : (some a).orElse b = some a := rfl
@[simp, grind =] theorem orElse_none : none.orElse b = b () := rfl

instance : OrElse (Option α) where
  orElse := Option.orElse

/--
Returns the first of its arguments that is `some`, or `none` if neither is `some`.

This is similar to the `<|>` operator, also known as `OrElse.orElse`, but both arguments are always
evaluated without short-circuiting.
-/
@[always_inline, macro_inline] def or : Option α → Option α → Option α
  | some a, _ => some a
  | none,   b => b

/--
Lifts an ordering relation to `Option`, such that `none` is the least element.

It can be understood as adding a distinguished least element, represented by `none`, to both `α` and
`β`.

This definition is part of the implementation of the `LT (Option α)` instance. However, because it
can be used with heterogeneous relations, it is sometimes useful on its own.

Examples:
 * `Option.lt (fun n k : Nat => n < k) none none = False`
 * `Option.lt (fun n k : Nat => n < k) none (some 3) = True`
 * `Option.lt (fun n k : Nat => n < k) (some 3) none = False`
 * `Option.lt (fun n k : Nat => n < k) (some 4) (some 5) = True`
 * `Option.lt (fun n k : Nat => n < k) (some 4) (some 4) = False`
-/
@[inline] protected def lt (r : α → β → Prop) : Option α → Option β → Prop
  | none,   some _ => True
  | some x, some y => r x y
  | _,      _      => False

@[inline] protected def le (r : α → β → Prop) : Option α → Option β → Prop
  | none,   some _ => True
  | none,   none   => True
  | some _, none   => False
  | some x, some y => r x y

instance (r : α → β → Prop) [s : DecidableRel r] : DecidableRel (Option.lt r)
  | none,   some _ => isTrue  trivial
  | some x, some y => s x y
  | some _, none   => isFalse not_false
  | none,   none   => isFalse not_false

namespace SomeLtNone

/--
Lifts an ordering relation to `Option` such that `none` is the *greatest* element.

It can be understood as adding a distinguished greatest element, represented by `none`, to both `α`
and `β`.

Caution: Given `LT α`, `Option.SomeLtNone.lt LT.lt` differs from the `LT (Option α)` instance,
which is implemented by `Option.lt Lt.lt`.

Examples:
 * `Option.lt (fun n k : Nat => n < k) none none = False`
 * `Option.lt (fun n k : Nat => n < k) none (some 3) = False`
 * `Option.lt (fun n k : Nat => n < k) (some 3) none = True`
 * `Option.lt (fun n k : Nat => n < k) (some 4) (some 5) = True`
 * `Option.le (fun n k : Nat => n < k) (some 5) (some 4) = False`
 * `Option.lt (fun n k : Nat => n < k) (some 4) (some 4) = False`
-/
def lt {α} (r : α → β → Prop) : Option α → Option β → Prop
  | none, _ => False
  | some _, none => True
  | some x, some y => r x y

/--
Lifts an ordering relation to `Option` such that `none` is the *greatest* element.

It can be understood as adding a distinguished greatest element, represented by `none`, to both `α`
and `β`.

Caution: Given `LE α`, `Option.SomeLtNone.le LE.le` differs from the `LE (Option α)` instance,
which is implemented by `Option.le LE.le`.

Examples:
 * `Option.le (fun n k : Nat => n < k) none none = True`
 * `Option.le (fun n k : Nat => n < k) none (some 3) = False`
 * `Option.le (fun n k : Nat => n < k) (some 3) none = True`
 * `Option.le (fun n k : Nat => n < k) (some 4) (some 5) = True`
 * `Option.le (fun n k : Nat => n < k) (some 5) (some 4) = False`
 * `Option.le (fun n k : Nat => n < k) (some 4) (some 4) = True`
-/
def le {α} (r : α → β → Prop) : Option α → Option β → Prop
  | none, none => True
  | none, some _ => False
  | some _, none => True
  | some x, some y => r x y

end SomeLtNone

/--
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
it is returned and the function is not used.

The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
both are `none`, then the value is `none`.

Examples:
 * `Option.merge (· + ·) none (some 3) = some 3`
 * `Option.merge (· + ·) (some 2) (some 3) = some 5`
 * `Option.merge (· + ·) (some 2) none = some 2`
 * `Option.merge (· + ·) none none = none`
-/
def merge (fn : α → α → α) : Option α → Option α → Option α
  | none  , none   => none
  | some x, none   => some x
  | none  , some y => some y
  | some x, some y => some <| fn x y

/--
A case analysis function for `Option`.

Given a value for `none` and a function to apply to the contents of `some`, `Option.elim` checks
which constructor a given `Option` consists of, and uses the appropriate argument.

`Option.elim` is an elimination principle for `Option`. In particular, it is a non-dependent version
of `Option.recOn`. It can also be seen as a combination of `Option.map` and `Option.getD`.

Examples:
 * `(some "hello").elim 0 String.length = 5`
 * `none.elim 0 String.length = 0`
-/
@[inline] protected def elim : Option α → β → (α → β) → β
  | some x, _, f => f x
  | none, y, _ => y

/--
Extracts the value from an option that can be proven to be `some`.
-/
@[inline] def get {α : Type u} : (o : Option α) → isSome o → α
  | some x, _ => x

@[simp, grind =] theorem some_get : ∀ {x : Option α} (h : isSome x), some (x.get h) = x
| some _, _ => rfl
@[simp, grind =] theorem get_some (x : α) (h : isSome (some x)) : (some x).get h = x := rfl

/--
Returns `none` if a value doesn't satisfy a Boolean predicate, or the value itself otherwise.

From the perspective of `Option` as computations that might fail, this function is a run-time
assertion operator in the `Option` monad.

Examples:
 * `Option.guard (· > 2) 1 = none`
 * `Option.guard (· > 2) 5 = some 5`
-/
@[inline] def guard (p : α → Bool) (a : α) : Option α :=
  if p a then some a else none

/--
Converts an optional value to a list with zero or one element.

Examples:
 * `(some "value").toList = ["value"]`
 * `none.toList = []`
-/
@[inline] def toList : Option α → List α
  | none => .nil
  | some a => .cons a .nil

/--
Converts an optional value to an array with zero or one element.

Examples:
 * `(some "value").toArray = #["value"]`
 * `none.toArray = #[]`
-/
@[inline] def toArray : Option α → Array α
  | none => List.toArray .nil
  | some a => List.toArray (.cons a .nil)

/-- Lifts a relation `α → β → Prop` to a relation `Option α → Option β → Prop` by just adding
`none ~ none`. -/
inductive Rel (r : α → β → Prop) : Option α → Option β → Prop
  /-- If `a ~ b`, then `some a ~ some b` -/
  | some {a b} : r a b → Rel r (some a) (some b)
  /-- `none ~ none` -/
  | none : Rel r none none

/--
Flattens nested optional values, preserving any value found.

This is analogous to `List.flatten`.

Examples:
 * `none.join = none`
 * `(some none).join = none`
 * `(some (some v)).join = some v`
-/
@[inline] def join (x : Option (Option α)) : Option α := x.bind id

@[simp, grind =] theorem join_none : (none : Option (Option α)).join = none := rfl
@[simp, grind =] theorem join_some : (some o).join = o := rfl

/--
Converts an optional monadic computation into a monadic computation of an optional value.

This function only requires `m` to be an applicative functor.

Example:
```lean example
#eval show IO (Option String) from
  Option.sequence <| some do
    IO.println "hello"
    return "world"
```
```output
hello
```
```output
some "world"
```
-/
@[inline] def sequence [Applicative m] {α : Type u} : Option (m α) → m (Option α)
  | none => pure none
  | some f => some <$> f

@[simp, grind =] theorem sequence_none [Applicative m] : (none : Option (m α)).sequence = pure none := rfl
@[simp, grind =] theorem sequence_some [Applicative m] (f : m α) : (some f).sequence = some <$> f := rfl

/--
A monadic case analysis function for `Option`.

Given a fallback computation for `none` and a monadic operation to apply to the contents of `some`,
`Option.elimM` checks which constructor a given `Option` consists of, and uses the appropriate
argument.

`Option.elimM` can also be seen as a combination of `Option.mapM` and `Option.getDM`. It is a
monadic analogue of `Option.elim`.
-/
@[inline] def elimM [Monad m] (x : m (Option α)) (y : m β) (z : α → m β) : m β :=
  do (← x).elim y z

/--
Gets the value in an option, monadically computing a default value on `none`.

This is the monadic analogue of `Option.getD`.
-/
@[inline] def getDM [Pure m] (x : Option α) (y : m α) : m α :=
  match x with
  | some a => pure a
  | none => y

@[simp, grind =] theorem getDM_none [Pure m] (y : m α) : (none : Option α).getDM y = y := rfl
@[simp, grind =] theorem getDM_some [Pure m] (a : α) (y : m α) : (some a).getDM y = pure a := rfl

instance (α) [BEq α] [ReflBEq α] : ReflBEq (Option α) where
  rfl {x} := private
    match x with
    | some _ => BEq.rfl (α := α)
    | none => rfl

instance (α) [BEq α] [LawfulBEq α] : LawfulBEq (Option α) where
  eq_of_beq {x y h} := by
    match x, y with
    | some x, some y => rw [LawfulBEq.eq_of_beq (α := α) h]
    | none, none => rfl

/--
The minimum of two optional values, with `none` treated as the least element. This function is
usually accessed through the `Min (Option α)` instance, rather than directly.

Prior to `nightly-2025-02-27`, `none` was treated as the greatest element, so
`min none (some x) = min (some x) none = some x`.

Examples:
 * `Option.min (some 2) (some 5) = some 2`
 * `Option.min (some 5) (some 2) = some 2`
 * `Option.min (some 2) none = none`
 * `Option.min none (some 5) = none`
 * `Option.min none none = none`
-/
protected def min [Min α] : Option α → Option α → Option α
  | some x, some y => some (Min.min x y)
  | some _, none => none
  | none, some _ => none
  | none, none => none

instance [Min α] : Min (Option α) where min := Option.min

@[simp, grind =] theorem min_some_some [Min α] {a b : α} : min (some a) (some b) = some (min a b) := rfl
@[simp, grind =] theorem min_none_left [Min α] {o : Option α} : min none o = none := by
  cases o <;> rfl
@[simp, grind =] theorem min_none_right [Min α] {o : Option α} : min o none = none := by
  cases o <;> rfl

@[deprecated min_none_right (since := "2025-05-12")]
theorem min_some_none [Min α] {a : α} : min (some a) none = none := rfl
@[deprecated min_none_left (since := "2025-05-12")]
theorem min_none_some [Min α] {b : α} : min none (some b) = none := rfl
@[deprecated min_none_left (since := "2025-05-12")]
theorem min_none_none [Min α] : min (none : Option α) none = none := rfl

/--
The maximum of two optional values.

This function is usually accessed through the `Max (Option α)` instance, rather than directly.

Examples:
 * `Option.max (some 2) (some 5) = some 5`
 * `Option.max (some 5) (some 2) = some 5`
 * `Option.max (some 2) none = some 2`
 * `Option.max none (some 5) = some 5`
 * `Option.max none none = none`
-/
protected def max [Max α] : Option α → Option α → Option α
  | some x, some y => some (Max.max x y)
  | some x, none => some x
  | none, some y => some y
  | none, none => none

instance [Max α] : Max (Option α) where max := Option.max

@[simp, grind =] theorem max_some_some [Max α] {a b : α} : max (some a) (some b) = some (max a b) := rfl
@[simp, grind =] theorem max_none_left [Max α] {o : Option α} : max none o = o := by
  cases o <;> rfl
@[simp, grind =] theorem max_none_right [Max α] {o : Option α} : max o none = o := by
  cases o <;> rfl

@[deprecated max_none_right (since := "2025-05-12")]
theorem max_some_none [Max α] {a : α} : max (some a) none = some a := rfl
@[deprecated max_none_left (since := "2025-05-12")]
theorem max_none_some [Max α] {b : α} : max none (some b) = some b := rfl
@[deprecated max_none_left (since := "2025-05-12")]
theorem max_none_none [Max α] : max (none : Option α) none = none := rfl


end Option

instance [LT α] : LT (Option α) where
  lt := Option.lt (· < ·)

instance [LE α] : LE (Option α) where
  le := Option.le (· ≤ ·)

@[always_inline]
instance : Functor Option where
  map := Option.map

@[always_inline]
instance : Monad Option where
  pure := Option.some
  bind := Option.bind

@[always_inline]
instance : Alternative Option where
  failure := Option.none
  orElse  := Option.orElse

-- This is a duplicate of `Option.getM`; one may be deprecated in the future.
def liftOption [Alternative m] : Option α → m α
  | some a => pure a
  | none   => failure

/--
Recover from failing `Option` computations with a handler function.

This function is usually accessed through the `MonadExceptOf Unit Option` instance.

Examples:
 * `Option.tryCatch none (fun () => some "handled") = some "handled"`
 * `Option.tryCatch (some "succeeded") (fun () => some "handled") = some "succeeded"`
-/
@[always_inline, inline] protected def Option.tryCatch (x : Option α) (handle : Unit → Option α) : Option α :=
  match x with
  | some _ => x
  | none => handle ()

instance : MonadExceptOf Unit Option where
  throw    := fun _ => Option.none
  tryCatch := Option.tryCatch
