doc-next-gen

Init.Data.List.Basic

Module docstring

{"# Basic operations on List.

We define * basic operations on List, * simp lemmas for applying the operations on .nil and .cons arguments (in the cases where the right hand side is simple to state; otherwise these are deferred to Init.Data.List.Lemmas), * the minimal lemmas which are required for setting up Init.Data.Array.Basic.

In Init.Data.List.Impl we give tail-recursive versions of these operations along with @[csimp] lemmas,

In Init.Data.List.Lemmas we develop the full API for these functions.

Recall that length, get, set, foldl, and concat have already been defined in Init.Prelude.

The operations are organized as follow: * Equality: beq, isEqv. * Lexicographic ordering: lt, le, and instances. * Head and tail operators: head, head?, headD?, tail, tail?, tailD. * Basic operations: map, filter, filterMap, foldr, append, flatten, pure, flatMap, replicate, and reverse. * Additional functions defined in terms of these: leftpad, rightPad, and reduceOption. * Operations using indexes: mapIdx. * List membership: isEmpty, elem, contains, mem (and the notation), and decidability for predicates quantifying over membership in a List. * Sublists: take, drop, takeWhile, dropWhile, partition, dropLast, isPrefixOf, isPrefixOf?, isSuffixOf, isSuffixOf?, Subset, Sublist, rotateLeft and rotateRight. * Manipulating elements: replace, modify, insert, insertIdx, erase, eraseP, eraseIdx. * Finding elements: find?, findSome?, findIdx, indexOf, findIdx?, indexOf?, countP, count, and lookup. * Logic: any, all, or, and and. * Zippers: zipWith, zip, zipWithAll, and unzip. * Ranges and enumeration: range, zipIdx. * Minima and maxima: min? and max?. * Other functions: intersperse, intercalate, eraseDups, eraseReps, span, splitBy, removeAll (currently these functions are mostly only used in meta code, and do not have API suitable for verification).

Further operations are defined in Init.Data.List.BasicAux (because they use Array in their implementations), namely: * Variant getters: get!, get?, getD, getLast, getLast!, getLast?, and getLastD. * Head and tail: head!, tail!. * Other operations on sublists: partitionMap, rotateLeft, and rotateRight. ","## Preliminaries from Init.Prelude ","### length ","### set ","### foldl ","### concat ","## Equality ","## Lexicographic ordering ","## Alternative getters ","### getLast ","### getLast? ","### getLastD ","## Head and tail ","### head ","### head? ","### headD ","### tail ","### tail? ","### tailD ","## Basic List operations.

We define the basic functional programming operations on List: map, filter, filterMap, foldr, append, flatten, pure, bind, replicate, and reverse. ","### map ","### filter ","### filterMap ","### foldr ","### reverse ","### append ","### flatten ","### singleton ","### flatMap ","### replicate ","## Additional functions ","### leftpad and rightpad ","### reduceOption ","## List membership

  • L.contains a : Bool determines, using a [BEq α] instance, whether L contains an element · == a.
  • a ∈ L : Prop is the proposition (only decidable if α has decidable equality) that L contains an element · = a. ","### EmptyCollection ","### isEmpty ","### elem ","### contains ","### Mem ","## Sublists ","### take ","### drop ","### extract ","### takeWhile ","### dropWhile ","### partition ","### dropLast ","### Subset ","### Sublist and isSublist ","### IsPrefix / isPrefixOf / isPrefixOf? ","### IsSuffix / isSuffixOf / isSuffixOf? ","### IsInfix ","### splitAt ","### rotateLeft ","### rotateRight ","## Pairwise, Nodup ","## Manipulating elements ","### replace ","### modify ","### insert ","### erase ","### eraseIdx ","Finding elements ","### find? ","### findSome? ","### findIdx ","### idxOf ","### findIdx? ","### idxOf? ","### findFinIdx? ","### finIdxOf? ","### countP ","### count ","### lookup ","## Permutations ","### Perm ","### isPerm ","## Logical operations ","### any ","### all ","### or ","### and ","## Zippers ","### zipWith ","### zip ","### zipWithAll ","### unzip ","## Ranges and enumeration ","### range ","### range' ","### iota ","### zipIdx ","### enumFrom ","### enum ","## Minima and maxima ","### min? ","### max? ","## Other list operations

The functions are currently mostly used in meta code, and do not have sufficient API developed for verification work. ","### intersperse ","### intercalate ","### eraseDups ","### eraseReps ","### span ","### splitBy ","### removeAll ","# Runtime re-implementations using @[csimp]

More of these re-implementations are provided in Init/Data/List/Impl.lean. They can not be here, because the remaining ones required Array for their implementation.

This leaves a dangerous situation: if you import this file, but not Init/Data/List/Impl.lean, then at runtime you will get non tail-recursive versions. ","### length ","### map ","### filter ","### replicate ","## Additional functions ","### leftpad ","## Zippers ","### unzip ","## Ranges and enumeration ","### range' ","### iota ","## Other list operations ","### intersperse "}

List.length_nil theorem
: length ([] : List α) = 0
Full source
@[simp] theorem length_nil : length ([] : List α) = 0 :=
  rfl
Length of Empty List is Zero
Informal description
The length of the empty list is zero, i.e., $\text{length}([]) = 0$.
List.length_singleton theorem
{a : α} : length [a] = 1
Full source
@[simp] theorem length_singleton {a : α} : length [a] = 1 := rfl
Length of Singleton List: $\text{length}([a]) = 1$
Informal description
For any element $a$ of type $\alpha$, the length of the singleton list $[a]$ is equal to 1.
List.length_cons theorem
{a : α} {as : List α} : (cons a as).length = as.length + 1
Full source
@[simp] theorem length_cons {a : α} {as : List α} : (cons a as).length = as.length + 1 :=
  rfl
Length of Cons List: $\text{length}(a :: as) = \text{length}(as) + 1$
Informal description
For any element $a$ of type $\alpha$ and list $as$ of type $\text{List }\alpha$, the length of the list $\text{cons}(a, as)$ is equal to the length of $as$ plus one, i.e., $\text{length}(\text{cons}(a, as)) = \text{length}(as) + 1$.
List.length_set theorem
{as : List α} {i : Nat} {a : α} : (as.set i a).length = as.length
Full source
@[simp] theorem length_set {as : List α} {i : Nat} {a : α} : (as.set i a).length = as.length := by
  induction as generalizing i with
  | nil => rfl
  | cons x xs ih =>
    cases i with
    | zero => rfl
    | succ i => simp [set, ih]
Length Preservation under List Element Replacement
Informal description
For any list $as$ of elements of type $\alpha$, any natural number index $i$, and any element $a$ of type $\alpha$, the length of the list obtained by replacing the element at position $i$ in $as$ with $a$ is equal to the length of $as$.
List.foldl_nil theorem
: [].foldl f b = b
Full source
@[simp] theorem foldl_nil : [].foldl f b = b := rfl
Left Fold over Empty List Yields Initial Value
Informal description
For any binary operation $f : \alpha \to \beta \to \alpha$ and initial value $b : \alpha$, the left fold over an empty list `[]` returns the initial value, i.e., $\text{foldl } f \ b \ [] = b$.
List.foldl_cons theorem
{l : List α} {f : β → α → β} {b : β} : (a :: l).foldl f b = l.foldl f (f b a)
Full source
@[simp] theorem foldl_cons {l : List α} {f : β → α → β} {b : β} : (a :: l).foldl f b = l.foldl f (f b a) := rfl
Left Fold over Cons List: $\text{foldl } f \ b \ (a :: l) = \text{foldl } f \ (f(b, a)) \ l$
Informal description
For any list $a :: l$ of type $\text{List } \alpha$, binary operation $f : \beta \to \alpha \to \beta$, and initial value $b : \beta$, the left fold of $f$ over $a :: l$ with initial value $b$ is equal to the left fold of $f$ over $l$ with initial value $f(b, a)$. In other words: $$ \text{foldl } f \ b \ (a :: l) = \text{foldl } f \ (f(b, a)) \ l $$
List.length_concat theorem
{as : List α} {a : α} : (concat as a).length = as.length + 1
Full source
theorem length_concat {as : List α} {a : α} : (concat as a).length = as.length + 1 := by
  induction as with
  | nil => rfl
  | cons _ xs ih => simp [concat, ih]
Length of Concatenated List: $\text{length}(as \concat [a]) = \text{length}(as) + 1$
Informal description
For any list $as$ of type $\text{List } \alpha$ and any element $a$ of type $\alpha$, the length of the list obtained by appending $a$ to $as$ is equal to the length of $as$ plus one, i.e., $\text{length}(\text{concat}(as, a)) = \text{length}(as) + 1$.
List.of_concat_eq_concat theorem
{as bs : List α} {a b : α} (h : as.concat a = bs.concat b) : as = bs ∧ a = b
Full source
theorem of_concat_eq_concat {as bs : List α} {a b : α} (h : as.concat a = bs.concat b) : as = bs ∧ a = b := by
  match as, bs with
  | [], [] => simp [concat] at h; simp [h]
  | [_], [] => simp [concat] at h
  | _::_::_, [] => simp [concat] at h
  | [], [_] => simp [concat] at h
  | [], _::_::_ => simp [concat] at h
  | _::_, _::_ => simp [concat] at h; simp [h]; apply of_concat_eq_concat h.2
Equality of Concatenated Lists Implies Equality of Components
Informal description
For any two lists $as$ and $bs$ of type $\text{List } \alpha$ and any two elements $a$ and $b$ of type $\alpha$, if the concatenation of $as$ with $a$ is equal to the concatenation of $bs$ with $b$, then $as = bs$ and $a = b$.
List.beq definition
[BEq α] : List α → List α → Bool
Full source
/--
Checks whether two lists have the same length and their elements are pairwise `BEq`. Normally used
via the `==` operator.
-/
protected def beq [BEq α] : List α → List α → Bool
  | [],    []    => true
  | a::as, b::bs => a == b && List.beq as bs
  | _,     _     => false
Boolean equality for lists
Informal description
Given two lists `L₁` and `L₂` of type `List α` where `α` has a boolean equality relation `==`, the function `List.beq` returns `true` if and only if the lists have the same length and their corresponding elements are equal according to the `==` relation. This is typically used via the `==` operator for lists.
List.beq_nil_nil theorem
[BEq α] : List.beq ([] : List α) ([] : List α) = true
Full source
@[simp] theorem beq_nil_nil [BEq α] : List.beq ([] : List α) ([] : List α) = true := rfl
Boolean Equality of Empty Lists is True
Informal description
For any type $\alpha$ with a boolean equality relation, the boolean equality check between two empty lists of type $\alpha$ evaluates to `true`, i.e., $\mathtt{List.beq}\ []\ [] = \mathtt{true}$.
List.beq_cons_nil theorem
[BEq α] {a : α} {as : List α} : List.beq (a :: as) [] = false
Full source
@[simp] theorem beq_cons_nil [BEq α] {a : α} {as : List α} : List.beq (a::as) [] = false := rfl
Boolean Inequality of Nonempty List and Empty List
Informal description
For any type $\alpha$ with a boolean equality relation and for any element $a \in \alpha$ and list $as \in \text{List } \alpha$, the boolean equality check between the non-empty list $a :: as$ and the empty list $[]$ evaluates to $\text{false}$.
List.beq_nil_cons theorem
[BEq α] {a : α} {as : List α} : List.beq [] (a :: as) = false
Full source
@[simp] theorem beq_nil_cons [BEq α] {a : α} {as : List α} : List.beq [] (a::as) = false := rfl
Boolean Equality of Empty List with Non-Empty List is False
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $a \in \alpha$ and list $as \in \text{List } \alpha$, the boolean equality check between the empty list `[]` and the non-empty list `a :: as` evaluates to `false`.
List.beq_cons₂ theorem
[BEq α] {a b : α} {as bs : List α} : List.beq (a :: as) (b :: bs) = (a == b && List.beq as bs)
Full source
theorem beq_cons₂ [BEq α] {a b : α} {as bs : List α} : List.beq (a::as) (b::bs) = (a == b && List.beq as bs) := rfl
Boolean Equality of Cons Lists is Conjunction of Element and Tail Equality
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, for any elements $a, b \in \alpha$ and any lists $as, bs \in \text{List } \alpha$, the boolean equality check between the cons lists $a :: as$ and $b :: bs$ is equal to the conjunction of the boolean equality check between $a$ and $b$ and the boolean equality check between $as$ and $bs$. In symbols: \[ \text{List.beq } (a :: as) (b :: bs) = (a == b \land \text{List.beq } as bs). \]
List.instBEq instance
[BEq α] : BEq (List α)
Full source
instance [BEq α] : BEq (List α) := ⟨List.beq⟩
Boolean Equality for Lists
Informal description
For any type $\alpha$ equipped with a boolean equality relation $\mathtt{==}$, the type $\mathtt{List}\ \alpha$ of lists over $\alpha$ can be equipped with a boolean equality relation that checks if two lists have the same length and if all corresponding elements are equal according to $\mathtt{==}$.
List.instLawfulBEq instance
[BEq α] [LawfulBEq α] : LawfulBEq (List α)
Full source
instance [BEq α] [LawfulBEq α] : LawfulBEq (List α) where
  eq_of_beq {as bs} := by
    induction as generalizing bs with
    | nil => intro h; cases bs <;> first | rfl | contradiction
    | cons a as ih =>
      cases bs with
      | nil => intro h; contradiction
      | cons b bs =>
        simp [show (a::as == b::bs) = (a == b && as == bs) from rfl, -and_imp]
        intro ⟨h₁, h₂⟩
        exact ⟨h₁, ih h₂⟩
  rfl {as} := by
    induction as with
    | nil => rfl
    | cons a as ih => simp [BEq.beq, List.beq, LawfulBEq.rfl]; exact ih
Lawfulness of Boolean Equality for Lists
Informal description
For any type $\alpha$ with a boolean equality relation `==` that satisfies the `LawfulBEq` axioms (i.e., `==` coincides with propositional equality), the induced boolean equality on lists of $\alpha$ also satisfies the `LawfulBEq` axioms. Specifically: 1. If two lists are equal according to `List.beq`, then they are propositionally equal. 2. For any list $L$, the check $L == L$ evaluates to `true`.
List.isEqv definition
: (as bs : List α) → (eqv : α → α → Bool) → Bool
Full source
/--
Returns `true` if `as` and `bs` have the same length and they are pairwise related by `eqv`.

`O(min |as| |bs|)`. Short-circuits at the first non-related pair of elements.

Examples:
* `[1, 2, 3].isEqv [2, 3, 4] (· < ·) = true`
* `[1, 2, 3].isEqv [2, 2, 4] (· < ·) = false`
* `[1, 2, 3].isEqv [2, 3] (· < ·) = false`
-/
@[specialize] def isEqv : (as bs : List α) → (eqv : α → α → Bool) → Bool
  | [],    [],    _   => true
  | a::as, b::bs, eqv => eqv a b && isEqv as bs eqv
  | _,     _,     _   => false
List equivalence under pairwise relation
Informal description
The function `isEqv` takes two lists `as` and `bs` of type `List α` and a binary relation `eqv : α → α → Bool`, and returns `true` if both lists have the same length and all corresponding elements are related by `eqv`. The function short-circuits at the first pair of elements that are not related by `eqv`. The time complexity is `O(min |as| |bs|)`.
List.isEqv_nil_nil theorem
: isEqv ([] : List α) [] eqv = true
Full source
@[simp] theorem isEqv_nil_nil : isEqv ([] : List α) [] eqv = true := rfl
Empty Lists are Equivalent Under Any Relation
Informal description
For any binary relation `eqv : α → α → Bool`, the equivalence check `isEqv` applied to two empty lists `[]` returns `true`. That is, `isEqv [] [] eqv = true`.
List.isEqv_nil_cons theorem
: isEqv ([] : List α) (a :: as) eqv = false
Full source
@[simp] theorem isEqv_nil_cons : isEqv ([] : List α) (a::as) eqv = false := rfl
Empty List is Not Equivalent to Non-Empty List under $\text{isEqv}$
Informal description
For any element $a$ of type $\alpha$ and any list $as$ of type $\text{List }\alpha$, the function $\text{isEqv}$ applied to the empty list $[]$ and the non-empty list $a :: as$ with any binary relation $\text{eqv} : \alpha \to \alpha \to \text{Bool}$ returns $\text{false}$.
List.isEqv_cons_nil theorem
: isEqv (a :: as : List α) [] eqv = false
Full source
@[simp] theorem isEqv_cons_nil : isEqv (a::as : List α) [] eqv = false := rfl
Non-Empty List is Not Equivalent to Empty List under $\text{isEqv}$
Informal description
For any element $a$ of type $\alpha$, any list $as$ of type $\text{List }\alpha$, and any binary relation $\text{eqv} : \alpha \to \alpha \to \text{Bool}$, the function $\text{isEqv}$ applied to the non-empty list $a :: as$ and the empty list $[]$ returns $\text{false}$.
List.isEqv_cons₂ theorem
: isEqv (a :: as) (b :: bs) eqv = (eqv a b && isEqv as bs eqv)
Full source
theorem isEqv_cons₂ : isEqv (a::as) (b::bs) eqv = (eqv a b && isEqv as bs eqv) := rfl
Pairwise Equivalence of Cons Lists via Boolean Conjunction
Informal description
For any elements $a, b$ of type $\alpha$ and lists $as, bs$ of type $\text{List }\alpha$, the function $\text{isEqv}$ applied to the lists $a :: as$ and $b :: bs$ with a binary relation $\text{eqv} : \alpha \to \alpha \to \text{Bool}$ returns the Boolean conjunction of $\text{eqv}(a, b)$ and $\text{isEqv}(as, bs, \text{eqv})$. In other words, $\text{isEqv}(a :: as, b :: bs, \text{eqv}) = \text{eqv}(a, b) \land \text{isEqv}(as, bs, \text{eqv})$.
List.Lex inductive
(r : α → α → Prop) : (as : List α) → (bs : List α) → Prop
Full source
/--
Lexicographic ordering for lists with respect to an ordering of elements.

`as` is lexicographically smaller than `bs` if
 * `as` is empty and `bs` is non-empty, or
 * both `as` and `bs` are non-empty, and the head of `as` is less than the head of `bs` according to
   `r`, or
 * both `as` and `bs` are non-empty, their heads are equal, and the tail of `as` is less than the
   tail of `bs`.
-/
inductive Lex (r : α → α → Prop) : (as : List α) → (bs : List α) → Prop
  /-- `[]` is the smallest element in the lexicographic order. -/
  | nil {a l} : Lex r [] (a :: l)
  /--
  If the head of the first list is smaller than the head of the second, then the first list is
  lexicographically smaller than the second list.
  -/
  | rel {a₁ l₁ a₂ l₂} (h : r a₁ a₂) : Lex r (a₁ :: l₁) (a₂ :: l₂)
  /--
  If two lists have the same head, then their tails determine their lexicographic order. If the tail
  of the first list is lexicographically smaller than the tail of the second list, then the entire
  first list is lexicographically smaller than the entire second list.
  -/
  | cons {a l₁ l₂} (h : Lex r l₁ l₂) : Lex r (a :: l₁) (a :: l₂)
Lexicographic Order on Lists
Informal description
The lexicographic order `Lex r` on lists of type `α` is defined with respect to a binary relation `r` on `α`. For two lists `as` and `bs`, `as` is lexicographically smaller than `bs` if: 1. `as` is empty and `bs` is non-empty, or 2. Both `as` and `bs` are non-empty, and the head of `as` is related to the head of `bs` by `r`, or 3. Both `as` and `bs` are non-empty, their heads are equal under `r`, and the tail of `as` is lexicographically smaller than the tail of `bs` under `Lex r`.
List.decidableLex instance
[DecidableEq α] (r : α → α → Prop) [h : DecidableRel r] : (l₁ l₂ : List α) → Decidable (Lex r l₁ l₂)
Full source
instance decidableLex [DecidableEq α] (r : α → α → Prop) [h : DecidableRel r] :
    (l₁ l₂ : List α) → Decidable (Lex r l₁ l₂)
  | [], [] => isFalse nofun
  | [], _::_ => isTrue Lex.nil
  | _::_, [] => isFalse nofun
  | a::as, b::bs =>
    match h a b with
    | isTrue h₁ => isTrue (Lex.rel h₁)
    | isFalse h₁ =>
      if h₂ : a = b then
        match decidableLex r as bs with
        | isTrue h₃ => isTrue (h₂ ▸ Lex.cons h₃)
        | isFalse h₃ => isFalse (fun h => match h with
          | Lex.rel h₁' => absurd h₁' h₁
          | Lex.cons h₃' => absurd h₃' h₃)
      else
        isFalse (fun h => match h with
          | Lex.rel h₁' => absurd h₁' h₁
          | Lex.cons h₂' => h₂ rfl)
Decidability of Lexicographic Order on Lists
Informal description
For any type $\alpha$ with decidable equality and a decidable binary relation $r$ on $\alpha$, the lexicographic order $\mathrm{Lex}\,r$ on lists of $\alpha$ is decidable. That is, for any two lists $l_1$ and $l_2$ of $\alpha$, the proposition $\mathrm{Lex}\,r\,l_1\,l_2$ is decidable.
List.lt abbrev
[LT α] : List α → List α → Prop
Full source
/--
Lexicographic ordering of lists with respect to an ordering on their elements.

`as < bs` if
 * `as` is empty and `bs` is non-empty, or
 * both `as` and `bs` are non-empty, and the head of `as` is less than the head of `bs`, or
 * both `as` and `bs` are non-empty, their heads are equal, and the tail of `as` is less than the
   tail of `bs`.
-/
protected abbrev lt [LT α] : List α → List α → Prop := Lex (· < ·)
Lexicographic Order on Lists via Element Ordering
Informal description
Given a type $\alpha$ with a "less than" relation $<$, the relation $\mathrm{lt}$ on lists of type $\alpha$ is defined lexicographically. For two lists $as$ and $bs$, $as < bs$ holds if: 1. $as$ is empty and $bs$ is non-empty, or 2. Both $as$ and $bs$ are non-empty, and the head of $as$ is less than the head of $bs$, or 3. Both $as$ and $bs$ are non-empty, their heads are equal, and the tail of $as$ is less than the tail of $bs$ under the same relation.
List.instLT instance
[LT α] : LT (List α)
Full source
instance instLT [LT α] : LT (List α) := ⟨List.lt⟩
Lexicographic Order on Lists
Informal description
For any type $\alpha$ equipped with a "less than" relation $<$, the type of lists $\mathrm{List}\,\alpha$ inherits a lexicographic order defined as follows: for two lists $as$ and $bs$, $as < bs$ if either: 1. $as$ is empty and $bs$ is non-empty, or 2. Both $as$ and $bs$ are non-empty, the head of $as$ is less than the head of $bs$, or 3. Both $as$ and $bs$ are non-empty, their heads are equal, and the tail of $as$ is less than the tail of $bs$ under the same relation.
List.decidableLT instance
[DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) : Decidable (l₁ < l₂)
Full source
/-- Decidability of lexicographic ordering. -/
instance decidableLT [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
    Decidable (l₁ < l₂) := decidableLex (· < ·) l₁ l₂
Decidability of Lexicographic Order on Lists
Informal description
For any type $\alpha$ with decidable equality and a decidable "less than" relation $<$, the lexicographic order on lists of $\alpha$ is decidable. That is, for any two lists $l_1$ and $l_2$ of $\alpha$, the proposition $l_1 < l_2$ is decidable.
List.hasDecidableLt abbrev
Full source
@[deprecated decidableLT (since := "2024-12-13"), inherit_doc decidableLT]
abbrev hasDecidableLt := @decidableLT
Decidability of Lexicographic Order on Lists
Informal description
For any type $\alpha$ with decidable equality and a decidable "less than" relation $<$, the lexicographic order on lists of $\alpha$ is decidable. That is, for any two lists $l_1$ and $l_2$ of $\alpha$, the proposition $l_1 < l_2$ is decidable.
List.le definition
[LT α] (as bs : List α) : Prop
Full source
/--
Non-strict ordering of lists with respect to a strict ordering of their elements.

`as ≤ bs` if `¬ bs < as`.

This relation can be treated as a lexicographic order if the underlying `LT α` instance is
well-behaved. In particular, it should be irreflexive, asymmetric, and antisymmetric. These
requirements are precisely formulated in `List.cons_le_cons_iff`. If these hold, then `as ≤ bs` if
and only if:
 * `as` is empty, or
 * both `as` and `bs` are non-empty, and the head of `as` is less than the head of `bs`, or
 * both `as` and `bs` are non-empty, their heads are equal, and the tail of `as` is less than or
   equal to the tail of `bs`.
-/
@[reducible] protected def le [LT α] (as bs : List α) : Prop := ¬ bs < as
Non-strict lexicographic order on lists
Informal description
Given a type $\alpha$ with a strict order $<$, the non-strict lexicographic order $\leq$ on lists of type $\alpha$ is defined by $as \leq bs$ if and only if it is not the case that $bs < as$. This relation behaves as a lexicographic order when the underlying $<$ relation is well-behaved (irreflexive, asymmetric, and antisymmetric). Specifically, $as \leq bs$ holds if: 1. $as$ is empty, or 2. Both $as$ and $bs$ are non-empty, the head of $as$ is less than the head of $bs$, or 3. Both $as$ and $bs$ are non-empty, their heads are equal, and the tail of $as$ is less than or equal to the tail of $bs$ under the same relation.
List.instLE instance
[LT α] : LE (List α)
Full source
instance instLE [LT α] : LE (List α) := ⟨List.le⟩
Lexicographic Order on Lists
Informal description
For any type $\alpha$ equipped with a strict order $<$, the type `List α` of lists over $\alpha$ inherits a non-strict lexicographic order $\leq$ defined by: - The empty list is less than or equal to any list - For non-empty lists, $as \leq bs$ if either: * The head of $as$ is less than the head of $bs$, or * The heads are equal and the tail of $as$ is less than or equal to the tail of $bs$
List.decidableLE instance
[DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) : Decidable (l₁ ≤ l₂)
Full source
instance decidableLE [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
    Decidable (l₁ ≤ l₂) :=
  inferInstanceAs (Decidable (Not _))
Decidability of Non-Strict Lexicographic Order on Lists
Informal description
For any type $\alpha$ with decidable equality and a decidable "less than" relation $<$, the non-strict lexicographic order $\leq$ on lists of $\alpha$ is decidable. That is, for any two lists $l_1$ and $l_2$ of $\alpha$, the proposition $l_1 \leq l_2$ is decidable.
List.nil_lex_nil theorem
[BEq α] : lex ([] : List α) [] lt = false
Full source
theorem nil_lex_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
Lexicographic Comparison of Empty Lists is False
Informal description
For any type $\alpha$ with a boolean equality relation, the lexicographic comparison of two empty lists `[]` using any comparison function `lt` evaluates to `false`.
List.nil_lex_cons theorem
[BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true
Full source
@[simp] theorem nil_lex_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
Lexicographic Ordering: Empty List is Less Than Non-Empty List
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $b$ and list $bs$ of type $\alpha$, the lexicographic comparison of the empty list `[]` with the non-empty list `b :: bs` evaluates to `true`.
List.cons_lex_nil theorem
[BEq α] {a} {as : List α} : lex (a :: as) [] lt = false
Full source
theorem cons_lex_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
Lexicographic Comparison of Non-Empty List with Empty List is False
Informal description
For any type $\alpha$ with a boolean equality relation `==`, any element $a : \alpha$, and any list $as : \text{List } \alpha$, the lexicographic comparison of the non-empty list $a :: as$ with the empty list `[]` always evaluates to `false`.
List.cons_lex_cons theorem
[BEq α] {a b} {as bs : List α} : lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt))
Full source
@[simp] theorem cons_lex_cons [BEq α] {a b} {as bs : List α} :
    lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt)) := rfl
Lexicographic Comparison of Cons Lists: $lex(a::as, b::bs) = lt(a, b) \lor (a == b \land lex(as, bs))$
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any elements $a, b \in \alpha$ and lists $as, bs \in \text{List}(\alpha)$, the lexicographic comparison `lex` of the lists $a :: as$ and $b :: bs$ with respect to a relation `lt` is equal to `lt a b || (a == b && lex as bs lt)`. In other words, the lexicographic order first compares the head elements using `lt`, and if they are equal (as determined by `==`), it proceeds to compare the tails.
List.lex_nil theorem
[BEq α] {as : List α} : lex as [] lt = false
Full source
@[simp] theorem lex_nil [BEq α] {as : List α} : lex as [] lt = false := by
  cases as <;> simp [nil_lex_nil, cons_lex_nil]
Lexicographic Comparison of Nonempty List with Empty List is False
Informal description
For any list `as` of type `List α` equipped with a boolean equality relation `BEq α`, the lexicographic comparison `lex as [] lt` evaluates to `false`, where `lt` is a given strict order on `α`.
List.lex_nil_nil theorem
[BEq α] : lex ([] : List α) [] lt = false
Full source
@[deprecated nil_lex_nil (since := "2025-02-10")]
theorem lex_nil_nil [BEq α] : lex ([] : List α) [] lt = false := rfl
Lexicographic Order of Two Empty Lists is False
Informal description
For any type $\alpha$ with a boolean equality relation `==`, the lexicographic order comparison of two empty lists of type $\alpha$ is always `false`. That is, $\text{lex}([], []) = \text{false}$ for any lexicographic order relation `lex` on lists of $\alpha$.
List.lex_nil_cons theorem
[BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true
Full source
@[deprecated nil_lex_cons (since := "2025-02-10")]
theorem lex_nil_cons [BEq α] {b} {bs : List α} : lex [] (b :: bs) lt = true := rfl
Lexicographic Ordering: Empty List is Less Than Non-Empty List
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $b$ of type $\alpha$ and list $bs$ of type `List α`, the lexicographic comparison of the empty list `[]` with the non-empty list `b :: bs` using a given less-than relation `lt` evaluates to `true`.
List.lex_cons_nil theorem
[BEq α] {a} {as : List α} : lex (a :: as) [] lt = false
Full source
@[deprecated cons_lex_nil (since := "2025-02-10")]
theorem lex_cons_nil [BEq α] {a} {as : List α} : lex (a :: as) [] lt = false := rfl
Lexicographic Comparison of Non-Empty List with Empty List is False
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $a$ of $\alpha$ and any list $as$ of type $\text{List } \alpha$, the lexicographic comparison of the non-empty list $a :: as$ with the empty list `[]` evaluates to `false`.
List.lex_cons_cons theorem
[BEq α] {a b} {as bs : List α} : lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt))
Full source
@[deprecated cons_lex_cons (since := "2025-02-10")]
theorem lex_cons_cons [BEq α] {a b} {as bs : List α} :
    lex (a :: as) (b :: bs) lt = (lt a b || (a == b && lex as bs lt)) := rfl
Lexicographic Ordering of Cons Lists: $\text{lex}(a :: as, b :: bs, lt) = lt(a, b) \lor (a == b \land \text{lex}(as, bs, lt))$
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any elements $a, b \in \alpha$ and lists $as, bs \in \text{List } \alpha$, the lexicographic comparison of the lists $a :: as$ and $b :: bs$ with respect to a relation $lt$ is given by: \[ \text{lex}(a :: as, b :: bs, lt) = (lt(a, b) \lor (a == b \land \text{lex}(as, bs, lt))) \] where $\text{lex}$ denotes the lexicographic ordering function, $lt$ is a strict order relation on $\alpha$, and $::$ denotes list cons operation.
List.getLast definition
: ∀ (as : List α), as ≠ [] → α
Full source
/--
Returns the last element of a non-empty list.

Examples:
 * `["circle", "rectangle"].getLast (by decide) = "rectangle"`
 * `["circle"].getLast (by decide) = "circle"`
-/
def getLast : ∀ (as : List α), as ≠ [] → α
  | [],       h => absurd rfl h
  | [a],      _ => a
  | _::b::as, _ => getLast (b::as) (fun h => List.noConfusion h)
Last element of a non-empty list
Informal description
Given a non-empty list `as` of type `List α` and a proof `h` that `as` is not empty, the function returns the last element of `as`.
List.getLast? definition
: List α → Option α
Full source
/--
Returns the last element in the list, or `none` if the list is empty.

Alternatives include `List.getLastD`, which takes a fallback value for empty lists, and
`List.getLast!`, which panics on empty lists.

Examples:
 * `["circle", "rectangle"].getLast? = some "rectangle"`
 * `["circle"].getLast? = some "circle"`
 * `([] : List String).getLast? = none`
-/
def getLast? : List α → Option α
  | []    => none
  | a::as => some (getLast (a::as) (fun h => List.noConfusion h))
Optional last element of a list
Informal description
Given a list `L` of type `List α`, the function returns the last element of `L` as an optional value (`some x` if `L` is non-empty with last element `x`, or `none` if `L` is empty).
List.getLast?_nil theorem
: @getLast? α [] = none
Full source
@[simp] theorem getLast?_nil : @getLast? α [] = none := rfl
Empty List Has No Last Element
Informal description
For any type $\alpha$, the optional last element of the empty list is `none`, i.e., $\text{getLast?}(\text{nil}) = \text{none}$.
List.getLastD definition
: (as : List α) → (fallback : α) → α
Full source
/--
Returns the last element in the list, or `fallback` if the list is empty.

Alternatives include `List.getLast?`, which returns an `Option`, and `List.getLast!`, which panics
on empty lists.

Examples:
 * `["circle", "rectangle"].getLastD "oval" = "rectangle"`
 * `["circle"].getLastD "oval" = "circle"`
 * `([] : List String).getLastD "oval" = "oval"`
-/
def getLastD : (as : List α) → (fallback : α) → α
  | [],   a₀ => a₀
  | a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
Last element of a list with default value
Informal description
Given a list `as` of type `List α` and a default value `fallback` of type `α`, the function returns the last element of `as` if the list is non-empty, and returns `fallback` if the list is empty.
List.getLastD_nil theorem
{a : α} : getLastD [] a = a
Full source
theorem getLastD_nil {a : α} : getLastD [] a = a := rfl
Default Value of `getLastD` on Empty List
Informal description
For any default value $a$ of type $\alpha$, the function `getLastD` applied to the empty list `[]` with default $a$ returns $a$, i.e., $\text{getLastD}\ []\ a = a$.
List.getLastD_cons theorem
{a b : α} {l} : getLastD (b :: l) a = getLastD l b
Full source
theorem getLastD_cons {a b : α} {l} : getLastD (b::l) a = getLastD l b := by cases l <;> rfl
Last Element with Default on Cons List: $\text{getLastD}(b :: l, a) = \text{getLastD}(l, b)$
Informal description
For any elements $a, b$ of type $\alpha$ and any list $l$ of type $\operatorname{List} \alpha$, the last element (with default $a$) of the list $b :: l$ is equal to the last element (with default $b$) of the list $l$.
List.head definition
: (as : List α) → as ≠ [] → α
Full source
/--
Returns the first element of a non-empty list.
-/
def head : (as : List α) → as ≠ [] → α
  | a::_, _ => a
First element of a non-empty list
Informal description
Given a non-empty list \( L \) of elements of type \( \alpha \), the function returns the first element of \( L \). The function requires a proof that \( L \) is not empty.
List.head_cons theorem
{a : α} {l : List α} {h} : head (a :: l) h = a
Full source
@[simp] theorem head_cons {a : α} {l : List α} {h} : head (a::l) h = a := rfl
Head of Cons List is First Element
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of type $\operatorname{List} \alpha$, the head of the list $a :: l$ (with proof $h$ that the list is non-empty) is equal to $a$.
List.head? definition
: List α → Option α
Full source
/--
Returns the first element in the list, if there is one. Returns `none` if the list is empty.

Use `List.headD` to provide a fallback value for empty lists, or `List.head!` to panic on empty
lists.

Examples:
 * `([] : List Nat).head? = none`
 * `[3, 2, 1].head? = some 3`
-/
def head? : List α → Option α
  | []   => none
  | a::_ => some a
First element of a list (optional)
Informal description
Given a list `L` of elements of type `α`, the function returns the first element of `L` wrapped in `some` if the list is non-empty, and returns `none` if the list is empty.
List.head?_nil theorem
: head? ([] : List α) = none
Full source
@[simp] theorem head?_nil : head? ([] : List α) = none := rfl
Head of Empty List is None
Informal description
For any type $\alpha$, the head of the empty list (of type $\operatorname{List} \alpha$) is `none`, i.e., $\operatorname{head?}([]) = \operatorname{none}$.
List.head?_cons theorem
{a : α} {l : List α} : head? (a :: l) = some a
Full source
@[simp] theorem head?_cons {a : α} {l : List α} : head? (a::l) = some a := rfl
Head of Cons List is Some Element
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of type $\operatorname{List} \alpha$, the head of the list constructed by prepending $a$ to $l$ is $\operatorname{some} a$. That is, $\operatorname{head?}(a :: l) = \operatorname{some} a$.
List.headD definition
: (as : List α) → (fallback : α) → α
Full source
/--
Returns the first element in the list if there is one, or `fallback` if the list is empty.

Use `List.head?` to return an `Option`, and `List.head!` to panic on empty lists.

Examples:
 * `[].headD "empty" = "empty"`
 * `[].headD 2 = 2`
 * `["head", "shoulders", "knees"].headD "toes" = "head"`
-/
def headD : (as : List α) → (fallback : α) → α
  | [],   fallback => fallback
  | a::_, _  => a
Default head of a list
Informal description
Given a list `as` of type `List α` and a default value `fallback` of type `α`, the function returns the first element of `as` if the list is non-empty, and returns `fallback` if the list is empty.
List.headD_nil theorem
{d : α} : headD [] d = d
Full source
@[simp] theorem headD_nil {d : α} : headD [] d = d := rfl
Default Head of Empty List Equals Fallback Value
Informal description
For any default value $d$ of type $\alpha$, the default head of the empty list with fallback $d$ is equal to $d$. That is, $\operatorname{headD}([], d) = d$.
List.headD_cons theorem
{a : α} {l : List α} {d : α} : headD (a :: l) d = a
Full source
@[simp] theorem headD_cons {a : α} {l : List α} {d : α} : headD (a::l) d = a := rfl
Default Head of Cons List Equals First Element
Informal description
For any element $a$ of type $\alpha$, any list $l$ of type $\text{List}\,\alpha$, and any default value $d$ of type $\alpha$, the default head of the list $a :: l$ with fallback $d$ is equal to $a$.
List.tail definition
: List α → List α
Full source
/--
Drops the first element of a nonempty list, returning the tail. Returns `[]` when the argument is
empty.

Examples:
 * `["apple", "banana", "grape"].tail = ["banana", "grape"]`
 * `["apple"].tail = []`
 * `([] : List String).tail = []`
-/
def tail : List α → List α
  | []    => []
  | _::as => as
Tail of a list
Informal description
Given a list `L` of elements of type `α`, the function returns the tail of the list, which is the list obtained by removing the first element. If the input list is empty, the function returns the empty list.
List.tail_nil theorem
: tail ([] : List α) = []
Full source
@[simp] theorem tail_nil : tail ([] : List α) = [] := rfl
Tail of Empty List is Empty
Informal description
The tail of the empty list is the empty list, i.e., $\text{tail}([]) = []$.
List.tail_cons theorem
{a : α} {as : List α} : tail (a :: as) = as
Full source
@[simp] theorem tail_cons {a : α} {as : List α} : tail (a::as) = as := rfl
Tail of Cons List Equals Original List
Informal description
For any element $a$ of type $\alpha$ and any list $as$ of type $\text{List}\,\alpha$, the tail of the list constructed by prepending $a$ to $as$ (denoted $a :: as$) is equal to $as$.
List.tail? definition
: List α → Option (List α)
Full source
/--
Drops the first element of a nonempty list, returning the tail. Returns `none` when the argument is
empty.

Alternatives include `List.tail`, which returns the empty list on failure, `List.tailD`, which
returns an explicit fallback value, and `List.tail!`, which panics on the empty list.

Examples:
 * `["apple", "banana", "grape"].tail? = some ["banana", "grape"]`
 * `["apple"].tail? = some []`
 * `([] : List String).tail = none`
-/
def tail? : List α → Option (List α)
  | []    => none
  | _::as => some as
Optional tail of a list
Informal description
Given a list `L` of type `List α`, the function returns `some tail` where `tail` is the list obtained by removing the first element of `L`, or `none` if `L` is empty.
List.tail?_nil theorem
: tail? ([] : List α) = none
Full source
@[simp] theorem tail?_nil : tail? ([] : List α) = none := rfl
Optional Tail of Empty List is None
Informal description
For an empty list `[]` of type `List α`, the optional tail operation returns `none`.
List.tail?_cons theorem
{a : α} {l : List α} : tail? (a :: l) = some l
Full source
@[simp] theorem tail?_cons {a : α} {l : List α} : tail? (a::l) = some l := rfl
Optional Tail of Cons List is Some Tail
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of type $\text{List}\ \alpha$, the optional tail of the list $a :: l$ is $\text{some}\ l$.
List.tailD definition
(l fallback : List α) : List α
Full source
/--
Drops the first element of a nonempty list, returning the tail. Returns `none` when the argument is
empty.

Alternatives include `List.tail`, which returns the empty list on failure, `List.tail?`, which
returns an `Option`, and `List.tail!`, which panics on the empty list.

Examples:
 * `["apple", "banana", "grape"].tailD ["orange"] = ["banana", "grape"]`
 * `["apple"].tailD ["orange"] = []`
 * `[].tailD ["orange"] = ["orange"]`
-/
def tailD (l fallback : List α) : List α :=
  match l with
  | [] => fallback
  | _ :: tl => tl
Tail of a list with fallback
Informal description
Given a list `l` and a fallback list `fallback`, the function returns the tail of `l` (i.e., the list without its first element) if `l` is nonempty, otherwise it returns `fallback`. **Examples:** - For `l = ["apple", "banana", "grape"]` and `fallback = ["orange"]`, the result is `["banana", "grape"]`. - For `l = ["apple"]` and `fallback = ["orange"]`, the result is `[]`. - For `l = []` and `fallback = ["orange"]`, the result is `["orange"]`.
List.tailD_nil theorem
{l' : List α} : tailD [] l' = l'
Full source
@[simp] theorem tailD_nil {l' : List α} : tailD [] l' = l' := rfl
Tail of Empty List with Fallback
Informal description
For any list `l'` of type `List α`, the tail of the empty list with fallback `l'` is equal to `l'`. That is, $\text{tailD}\ [\,]\ l' = l'$.
List.tailD_cons theorem
{a : α} {l : List α} {l' : List α} : tailD (a :: l) l' = l
Full source
@[simp] theorem tailD_cons {a : α} {l : List α} {l' : List α} : tailD (a::l) l' = l := rfl
Tail of Nonempty List with Fallback Equals Tail
Informal description
For any element $a$ of type $\alpha$ and any lists $l, l'$ of type $\text{List}\ \alpha$, the tail of the list $a :: l$ with fallback $l'$ is equal to $l$. That is, $\text{tailD}(a :: l, l') = l$.
List.map definition
(f : α → β) : (l : List α) → List β
Full source
/--
Applies a function to each element of the list, returning the resulting list of values.

`O(|l|)`.

Examples:
* `[a, b, c].map f = [f a, f b, f c]`
* `[].map Nat.succ = []`
* `["one", "two", "three"].map (·.length) = [3, 3, 5]`
* `["one", "two", "three"].map (·.reverse) = ["eno", "owt", "eerht"]`
-/
@[specialize] def map (f : α → β) : (l : List α) → List β
  | []    => []
  | a::as => f a :: map f as
List mapping function
Informal description
The function `List.map` applies a function $f : \alpha \to \beta$ to each element of a list $l$ of type $\text{List}\ \alpha$, returning a new list of type $\text{List}\ \beta$ with the results. For example, mapping $f$ over $[a, b, c]$ yields $[f a, f b, f c]$, and mapping over the empty list yields the empty list. The operation has time complexity $O(|l|)$.
List.map_nil theorem
{f : α → β} : map f [] = []
Full source
@[simp] theorem map_nil {f : α → β} : map f [] = [] := rfl
Mapping Over Empty List Yields Empty List
Informal description
For any function $f : \alpha \to \beta$, mapping $f$ over the empty list yields the empty list, i.e., $\text{map}\ f\ [] = []$.
List.map_cons theorem
{f : α → β} {a : α} {l : List α} : map f (a :: l) = f a :: map f l
Full source
@[simp] theorem map_cons {f : α → β} {a : α} {l : List α} : map f (a :: l) = f a :: map f l := rfl
Cons Preservation under List Mapping
Informal description
For any function $f : \alpha \to \beta$, element $a \in \alpha$, and list $l$ of type $\text{List}\ \alpha$, the map of $f$ over the cons list $a :: l$ equals the cons of $f(a)$ with the map of $f$ over $l$, i.e., \[ \text{map}\ f\ (a :: l) = f(a) :: \text{map}\ f\ l. \]
List.filter definition
(p : α → Bool) : (l : List α) → List α
Full source
/--
Returns the list of elements in `l` for which `p` returns `true`.

`O(|l|)`.

Examples:
* `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]`
* `[1, 2, 5, 2, 7, 7].filter (fun _ => false) = []`
* `[1, 2, 5, 2, 7, 7].filter (fun _ => true) = [1, 2, 5, 2, 7, 7]`
-/
def filter (p : α → Bool) : (l : List α) → List α
  | [] => []
  | a::as => match p a with
    | true => a :: filter p as
    | false => filter p as
Filter elements of a list satisfying a predicate
Informal description
Given a predicate `p : α → Bool` and a list `l : List α`, the function `List.filter p l` returns the sublist of `l` consisting of all elements `a` for which `p a` evaluates to `true`. The order of elements in the original list is preserved in the result. **Examples:** - `[1, 2, 5, 2, 7, 7].filter (· > 2) = [5, 7, 7]` - `[1, 2, 5, 2, 7, 7].filter (fun _ => false) = []` - `[1, 2, 5, 2, 7, 7].filter (fun _ => true) = [1, 2, 5, 2, 7, 7]` The operation has time complexity $O(|l|)$, where $|l|$ is the length of the input list.
List.filter_nil theorem
{p : α → Bool} : filter p [] = []
Full source
@[simp] theorem filter_nil {p : α → Bool} : filter p [] = [] := rfl
Filtering the Empty List Yields the Empty List
Informal description
For any predicate $p : \alpha \to \text{Bool}$, filtering the empty list $[]$ with $p$ results in the empty list, i.e., $\text{filter } p [] = []$.
List.filterMap definition
(f : α → Option β) : List α → List β
Full source
/--
Applies a function that returns an `Option` to each element of a list, collecting the non-`none`
values.

`O(|l|)`.

Example:
```lean example
#eval [1, 2, 5, 2, 7, 7].filterMap fun x =>
  if x > 2 then some (2 * x) else none
```
```output
[10, 14, 14]
```
-/
@[specialize] def filterMap (f : α → Option β) : List α → List β
  | []   => []
  | a::as =>
    match f a with
    | none   => filterMap f as
    | some b => b :: filterMap f as
Filter and map a list with an optional-valued function
Informal description
Given a function \( f : \alpha \to \text{Option } \beta \) and a list \( L : \text{List } \alpha \), the function `List.filterMap` applies \( f \) to each element of \( L \) and collects all the results of the form \(\text{some } b\), discarding any \(\text{none}\) values. The resulting list has type \(\text{List } \beta\). Example: Applying \(\lambda x.\, \text{if } x > 2 \text{ then some } (2 \cdot x) \text{ else none}\) to \([1, 2, 5, 2, 7, 7]\) yields \([10, 14, 14]\).
List.filterMap_nil theorem
{f : α → Option β} : filterMap f [] = []
Full source
@[simp] theorem filterMap_nil {f : α → Option β} : filterMap f [] = [] := rfl
Empty List Property of `filterMap`
Informal description
For any function $f : \alpha \to \text{Option } \beta$, applying the `filterMap` operation to the empty list yields the empty list, i.e., $\text{filterMap } f \ [] = []$.
List.filterMap_cons theorem
{f : α → Option β} {a : α} {l : List α} : filterMap f (a :: l) = match f a with | none => filterMap f l | some b => b :: filterMap f l
Full source
theorem filterMap_cons {f : α → Option β} {a : α} {l : List α} :
    filterMap f (a :: l) =
      match f a with
      | none => filterMap f l
      | some b => b :: filterMap f l := rfl
Recursive Definition of `filterMap` on Cons List
Informal description
Given a function $f : \alpha \to \text{Option } \beta$, an element $a \in \alpha$, and a list $l : \text{List } \alpha$, the result of applying `filterMap` to the list $a :: l$ is: - If $f(a) = \text{none}$, then $\text{filterMap } f (a :: l) = \text{filterMap } f l$. - If $f(a) = \text{some } b$ for some $b \in \beta$, then $\text{filterMap } f (a :: l) = b :: \text{filterMap } f l$.
List.foldr definition
(f : α → β → β) (init : β) : (l : List α) → β
Full source
/--
Folds a function over a list from the right, accumulating a value starting with `init`. The
accumulated value is combined with the each element of the list in reverse order, using `f`.

`O(|l|)`. Replaced at runtime with `List.foldrTR`.

Examples:
 * `[a, b, c].foldr f init  = f a (f b (f c init))`
 * `[1, 2, 3].foldr (toString · ++ ·) "" = "123"`
 * `[1, 2, 3].foldr (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[specialize] def foldr (f : α → β → β) (init : β) : (l : List α) → β
  | []     => init
  | a :: l => f a (foldr f init l)
Right fold over a list
Informal description
Given a binary function \( f : \alpha \to \beta \to \beta \), an initial value \( \text{init} : \beta \), and a list \( l : \text{List } \alpha \), the function `List.foldr` computes the right-fold of \( f \) over \( l \) starting with \( \text{init} \). Specifically, it applies \( f \) to each element of \( l \) in reverse order, combining it with the accumulated value. For example: - \( \text{foldr } f \text{ init } [a, b, c] = f a (f b (f c \text{ init})) \). - \( \text{foldr } (\lambda x y \Rightarrow \text{toString } x \mathbin{+\!\!+} y) \text{ "" } [1, 2, 3] = \text{"123"} \). - \( \text{foldr } (\lambda x y \Rightarrow \text{s!"(\{x\} \{y\})"}) \text{ "!" } [1, 2, 3] = \text{"(1 (2 (3 !)))"} \). The operation has time complexity \( O(|l|) \).
List.foldr_nil theorem
: [].foldr f b = b
Full source
@[simp] theorem foldr_nil : [].foldr f b = b := rfl
Right fold over empty list equals initial value
Informal description
For any binary function $f$ and initial value $b$, the right fold of $f$ over the empty list $[]$ with starting value $b$ equals $b$.
List.foldr_cons theorem
{a} {l : List α} {f : α → β → β} {b} : (a :: l).foldr f b = f a (l.foldr f b)
Full source
@[simp] theorem foldr_cons {a} {l : List α} {f : α → β → β} {b} :
    (a :: l).foldr f b = f a (l.foldr f b) := rfl
Right-fold decomposition for cons lists
Informal description
For any element $a$ of type $\alpha$, any list $l$ of type $\text{List } \alpha$, any binary function $f : \alpha \to \beta \to \beta$, and any initial value $b$ of type $\beta$, the right-fold of $f$ over the list $a :: l$ with initial value $b$ is equal to $f$ applied to $a$ and the right-fold of $f$ over $l$ with initial value $b$. In other words: $$ \text{foldr } f \text{ } b \text{ } (a :: l) = f \text{ } a \text{ } (\text{foldr } f \text{ } b \text{ } l) $$
List.reverseAux definition
: List α → List α → List α
Full source
/-- Auxiliary for `List.reverse`. `List.reverseAux l r = l.reverse ++ r`, but it is defined directly. -/
def reverseAux : List α → List α → List α
  | [],   r => r
  | a::l, r => reverseAux l (a::r)
Reverse auxiliary function for lists
Informal description
The function `List.reverseAux` takes two lists `l` and `r` and returns the concatenation of the reverse of `l` with `r`. It is defined recursively by: - If `l` is empty (`[]`), return `r`. - If `l` is of the form `a::l'`, then recursively process `l'` with `a` prepended to `r`.
List.reverseAux_nil theorem
: reverseAux [] r = r
Full source
@[simp] theorem reverseAux_nil : reverseAux [] r = r := rfl
Reverse Auxiliary Function on Empty List
Informal description
For any list `r`, the auxiliary reverse function applied to the empty list `[]` and `r` returns `r` itself, i.e., $\text{reverseAux}\ \text{[]}\ r = r$.
List.reverseAux_cons theorem
: reverseAux (a :: l) r = reverseAux l (a :: r)
Full source
@[simp] theorem reverseAux_cons : reverseAux (a::l) r = reverseAux l (a::r) := rfl
Recursive Relation for Reverse Auxiliary Function on Lists
Informal description
For any element $a$ of type $\alpha$ and lists $l, r$ of type $\text{List}\ \alpha$, the reverse auxiliary function satisfies the recursive relation: \[ \text{reverseAux}(a :: l, r) = \text{reverseAux}(l, a :: r) \] where $a :: l$ denotes the list with head $a$ and tail $l$.
List.reverse definition
(as : List α) : List α
Full source
/--
Reverses a list.

`O(|as|)`.

Because of the “functional but in place” optimization implemented by Lean's compiler, this function
does not allocate a new list when its reference to the input list is unshared: it simply walks the
linked list and reverses all the node pointers.

Examples:
* `[1, 2, 3, 4].reverse = [4, 3, 2, 1]`
* `[].reverse = []`
-/
def reverse (as : List α) : List α :=
  reverseAux as []
List reversal
Informal description
The function reverses the order of elements in a list. For a list `as` of type `List α`, it returns a new list with the elements of `as` in reverse order. The operation has time complexity $O(n)$ where $n$ is the length of the list. When the input list is unshared, the implementation optimizes by reversing the node pointers in place without allocating a new list. Examples: - `[1, 2, 3, 4].reverse = [4, 3, 2, 1]` - `[].reverse = []`
List.reverse_nil theorem
: reverse ([] : List α) = []
Full source
@[simp] theorem reverse_nil : reverse ([] : List α) = [] := rfl
Empty List Reversal Identity
Informal description
The reverse of the empty list is the empty list itself, i.e., $\text{reverse}([]) = []$.
List.reverseAux_reverseAux theorem
{as bs cs : List α} : reverseAux (reverseAux as bs) cs = reverseAux bs (reverseAux (reverseAux as []) cs)
Full source
theorem reverseAux_reverseAux {as bs cs : List α} :
    reverseAux (reverseAux as bs) cs = reverseAux bs (reverseAux (reverseAux as []) cs) := by
  induction as generalizing bs cs with
  | nil => rfl
  | cons a as ih => simp [reverseAux, ih (bs := a::bs), ih (bs := [a])]
Reverse Auxiliary Function Composition Identity
Informal description
For any lists $as$, $bs$, and $cs$ of elements of type $\alpha$, the following equality holds: $$\text{reverseAux}(\text{reverseAux}(as, bs), cs) = \text{reverseAux}(bs, \text{reverseAux}(\text{reverseAux}(as, []), cs))$$ where $\text{reverseAux}(l, r)$ is the auxiliary function that reverses the list $l$ and appends it to $r$.
List.append definition
: (xs ys : List α) → List α
Full source
/--
Appends two lists. Normally used via the `++` operator.

Appending lists takes time proportional to the length of the first list: `O(|xs|)`.

Examples:
  * `[1, 2, 3] ++ [4, 5] = [1, 2, 3, 4, 5]`.
  * `[] ++ [4, 5] = [4, 5]`.
  * `[1, 2, 3] ++ [] = [1, 2, 3]`.
-/
protected def append : (xs ys : List α) → List α
  | [],    bs => bs
  | a::as, bs => a :: List.append as bs
List concatenation (append)
Informal description
The function `List.append` concatenates two lists `xs` and `ys` of type `List α`, returning a new list where the elements of `xs` are followed by the elements of `ys`. The operation has time complexity $O(|xs|)$, where $|xs|$ is the length of the first list. For example: - $[1, 2, 3] \mathbin{+\kern-0.5em+} [4, 5] = [1, 2, 3, 4, 5]$ - $[] \mathbin{+\kern-0.5em+} [4, 5] = [4, 5]$ - $[1, 2, 3] \mathbin{+\kern-0.5em+} [] = [1, 2, 3]$
List.appendTR definition
(as bs : List α) : List α
Full source
/--
Appends two lists. Normally used via the `++` operator.

Appending lists takes time proportional to the length of the first list: `O(|xs|)`.

This is a tail-recursive version of `List.append`.

Examples:
  * `[1, 2, 3] ++ [4, 5] = [1, 2, 3, 4, 5]`.
  * `[] ++ [4, 5] = [4, 5]`.
  * `[1, 2, 3] ++ [] = [1, 2, 3]`.
-/
-- The @[csimp] lemma for `appendTR` must be set up immediately, because otherwise `Append (List α)`
-- instance below will not use it.
def appendTR (as bs : List α) : List α :=
  reverseAux as.reverse bs
Tail-recursive list append
Informal description
The tail-recursive function that appends two lists `as` and `bs` by first reversing `as` and then using the auxiliary reverse function to append the reversed `as` to `bs`. This operation has time complexity $O(|as|)$, where $|as|$ is the length of the first list. Examples: - $\text{appendTR}([1, 2, 3], [4, 5]) = [1, 2, 3, 4, 5]$ - $\text{appendTR}([], [4, 5]) = [4, 5]$ - $\text{appendTR}([1, 2, 3], []) = [1, 2, 3]$
List.append_eq_appendTR theorem
: @List.append = @appendTR
Full source
@[csimp] theorem append_eq_appendTR : @List.append = @appendTR := by
  apply funext; intro α; apply funext; intro as; apply funext; intro bs
  simp [appendTR, reverse]
  induction as with
  | nil  => rfl
  | cons a as ih =>
    rw [reverseAux, reverseAux_reverseAux]
    simp [List.append, ih, reverseAux]
Equivalence of List Append and Tail-Recursive Append
Informal description
The function `List.append` for concatenating two lists is equal to the tail-recursive version `appendTR`.
List.instAppend instance
: Append (List α)
Full source
instance : Append (List α) := ⟨List.append⟩
The Append Operation on Lists
Informal description
The type `List α` of lists over a type `α` is equipped with a canonical append operation `++` that concatenates two lists.
List.append_eq theorem
{as bs : List α} : List.append as bs = as ++ bs
Full source
@[simp] theorem append_eq {as bs : List α} : List.append as bs = as ++ bs := rfl
Equivalence of `List.append` and `++` operations on lists
Informal description
For any two lists `as` and `bs` of type `List α`, the result of applying the `List.append` function to `as` and `bs` is equal to the result of the append operation `as ++ bs`.
List.nil_append theorem
(as : List α) : [] ++ as = as
Full source
@[simp] theorem nil_append (as : List α) : [] ++ as = as := rfl
Empty List Concatenation Identity
Informal description
For any list `as` of type `List α`, concatenating the empty list `[]` with `as` results in `as`, i.e., `[] ++ as = as`.
List.cons_append theorem
{a : α} {as bs : List α} : (a :: as) ++ bs = a :: (as ++ bs)
Full source
@[simp] theorem cons_append {a : α} {as bs : List α} : (a::as) ++ bs = a::(as ++ bs) := rfl
Cons Concatenation Identity: $(a :: as) ++ bs = a :: (as ++ bs)$
Informal description
For any element $a$ of type $\alpha$ and any lists $as, bs$ of type $\List \alpha$, the concatenation of the list $a :: as$ with $bs$ is equal to the list $a :: (as ++ bs)$.
List.append_nil theorem
(as : List α) : as ++ [] = as
Full source
@[simp] theorem append_nil (as : List α) : as ++ [] = as := by
  induction as with
  | nil => rfl
  | cons a as ih =>
    simp_all only [HAppend.hAppend, Append.append, List.append]
Right Identity Property of List Concatenation
Informal description
For any list `as` of type `List α`, concatenating `as` with the empty list `[]` yields `as` itself, i.e., $as \mathbin{+\kern-0.5em+} [] = as$.
List.instLawfulIdentityHAppendNil instance
: Std.LawfulIdentity (α := List α) (· ++ ·) []
Full source
instance : Std.LawfulIdentity (α := List α) (· ++ ·) [] where
  left_id := nil_append
  right_id := append_nil
Empty List as Identity for Concatenation
Informal description
The empty list `[]` is a lawful identity element for the list concatenation operation `++` on `List α`. This means that for any list `as`, both `[] ++ as = as` and `as ++ [] = as` hold, and these identities satisfy the necessary coherence conditions.
List.length_append theorem
{as bs : List α} : (as ++ bs).length = as.length + bs.length
Full source
@[simp] theorem length_append {as bs : List α} : (as ++ bs).length = as.length + bs.length := by
  induction as with
  | nil => simp
  | cons _ as ih => simp [ih, Nat.succ_add]
Length of Concatenated Lists: $\text{length}(as \mathbin{+\kern-1.5ex+} bs) = \text{length}(as) + \text{length}(bs)$
Informal description
For any two lists $as$ and $bs$ of elements of type $\alpha$, the length of their concatenation $as \mathbin{+\kern-1.5ex+} bs$ is equal to the sum of their lengths, i.e., $$\text{length}(as \mathbin{+\kern-1.5ex+} bs) = \text{length}(as) + \text{length}(bs).$$
List.append_assoc theorem
(as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs)
Full source
@[simp] theorem append_assoc (as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs) := by
  induction as with
  | nil => rfl
  | cons a as ih => simp [ih]
Associativity of List Concatenation: $(as \mathbin{+\kern-1.5ex+} bs) \mathbin{+\kern-1.5ex+} cs = as \mathbin{+\kern-1.5ex+} (bs \mathbin{+\kern-1.5ex+} cs)$
Informal description
For any three lists $as$, $bs$, and $cs$ of elements of type $\alpha$, the concatenation operation satisfies the associativity property: $$(as \mathbin{+\kern-1.5ex+} bs) \mathbin{+\kern-1.5ex+} cs = as \mathbin{+\kern-1.5ex+} (bs \mathbin{+\kern-1.5ex+} cs)$$ where $\mathbin{+\kern-1.5ex+}$ denotes list concatenation.
List.instAssociativeHAppend instance
: Std.Associative (α := List α) (· ++ ·)
Full source
instance : Std.Associative (α := List α) (· ++ ·) := ⟨append_assoc⟩
Associativity of List Concatenation
Informal description
The list concatenation operation `++` on `List α` is associative. That is, for any three lists `as`, `bs`, and `cs` of elements of type `α`, the equality `(as ++ bs) ++ cs = as ++ (bs ++ cs)` holds.
List.append_cons theorem
(as : List α) (b : α) (bs : List α) : as ++ b :: bs = as ++ [b] ++ bs
Full source
theorem append_cons (as : List α) (b : α) (bs : List α) : as ++ b :: bs = as ++ [b] ++ bs := by
  simp
List Concatenation with Cons: $as \mathbin{+\kern-1.5ex+} (b :: bs) = as \mathbin{+\kern-1.5ex+} [b] \mathbin{+\kern-1.5ex+} bs$
Informal description
For any list $as$ of elements of type $\alpha$, any element $b$ of type $\alpha$, and any list $bs$ of elements of type $\alpha$, the concatenation of $as$ with the list $b :: bs$ is equal to the concatenation of $as$ with the singleton list $[b]$ followed by $bs$. That is: $$as \mathbin{+\kern-1.5ex+} (b :: bs) = as \mathbin{+\kern-1.5ex+} [b] \mathbin{+\kern-1.5ex+} bs$$ where $\mathbin{+\kern-1.5ex+}$ denotes list concatenation and $::$ denotes list cons operation.
List.concat_eq_append theorem
{as : List α} {a : α} : as.concat a = as ++ [a]
Full source
@[simp] theorem concat_eq_append {as : List α} {a : α} : as.concat a = as ++ [a] := by
  induction as <;> simp [concat, *]
Equality of List Concatenation and Singleton Append
Informal description
For any list `as` of elements of type `α` and any element `a` of type `α`, the operation of appending `a` to the end of `as` (denoted `as.concat a`) is equal to the concatenation of `as` with the singleton list `[a]` (denoted `as ++ [a]`).
List.reverseAux_eq_append theorem
{as bs : List α} : reverseAux as bs = reverseAux as [] ++ bs
Full source
theorem reverseAux_eq_append {as bs : List α} : reverseAux as bs = reverseAux as [] ++ bs := by
  induction as generalizing bs with
  | nil => simp [reverseAux]
  | cons a as ih =>
    simp [reverseAux]
    rw [ih (bs := a :: bs), ih (bs := [a]), append_assoc]
    rfl
Reverse Auxiliary Function Equals Reverse with Empty Accumulator Followed by Append
Informal description
For any two lists `as` and `bs` of elements of type `α`, the auxiliary reverse operation `reverseAux as bs` is equal to the concatenation of `reverseAux as []` with `bs`. In other words, the auxiliary reverse operation with an accumulator `bs` is equivalent to first reversing `as` with an empty accumulator and then appending `bs`.
List.reverse_cons theorem
{a : α} {as : List α} : reverse (a :: as) = reverse as ++ [a]
Full source
@[simp] theorem reverse_cons {a : α} {as : List α} : reverse (a :: as) = reverse as ++ [a] := by
  simp [reverse, reverseAux]
  rw [← reverseAux_eq_append]
Reverse of Cons List Equals Reverse of Tail Appended with Head
Informal description
For any element $a$ of type $\alpha$ and any list `as` of elements of type $\alpha$, the reverse of the list `a :: as` is equal to the concatenation of the reverse of `as` with the singleton list `[a]`. In other words, $\text{reverse}(a \cdot \text{as}) = \text{reverse}(\text{as}) \cdot [a]$.
List.flatten definition
: List (List α) → List α
Full source
/--
Concatenates a list of lists into a single list, preserving the order of the elements.

`O(|flatten L|)`.

Examples:
* `[["a"], ["b", "c"]].flatten = ["a", "b", "c"]`
* `[["a"], [], ["b", "c"], ["d", "e", "f"]].flatten = ["a", "b", "c", "d", "e", "f"]`
-/
def flatten : List (List α) → List α
  | []      => []
  | l :: L => l ++ flatten L
List concatenation (flatten)
Informal description
Given a list of lists `L : List (List α)`, the function `flatten` concatenates all the inner lists in `L` into a single list while preserving the order of elements. For example: - `flatten [["a"], ["b", "c"]] = ["a", "b", "c"]` - `flatten [["a"], [], ["b", "c"], ["d", "e", "f"]] = ["a", "b", "c", "d", "e", "f"]` The operation has time complexity `O(n)`, where `n` is the total number of elements in the resulting flattened list.
List.flatten_nil theorem
: List.flatten ([] : List (List α)) = []
Full source
@[simp] theorem flatten_nil : List.flatten ([] : List (List α)) = [] := rfl
Flatten of Empty List is Empty
Informal description
The flatten operation applied to the empty list of lists results in the empty list, i.e., $\text{flatten}([]) = []$.
List.flatten_cons theorem
: (l :: L).flatten = l ++ L.flatten
Full source
@[simp] theorem flatten_cons : (l :: L).flatten = l ++ L.flatten := rfl
Flattening a Cons List Equals Concatenation with Flattened Tail
Informal description
For any list `l : List α` and any list of lists `L : List (List α)`, the flattening of the cons list `l :: L` is equal to the concatenation of `l` with the flattening of `L`. In other words, $\text{flatten}(l :: L) = l +\!\!+ \text{flatten}(L)$.
List.join abbrev
Full source
@[deprecated flatten (since := "2024-10-14"), inherit_doc flatten] abbrev join := @flatten
List Concatenation (join)
Informal description
Given a list of lists `L : List (List α)`, the function `join` concatenates all the inner lists in `L` into a single list while preserving the order of elements. For example: - `join [["a"], ["b", "c"]] = ["a", "b", "c"]` - `join [["a"], [], ["b", "c"], ["d", "e", "f"]] = ["a", "b", "c", "d", "e", "f"]` The operation has time complexity `O(n)`, where `n` is the total number of elements in the resulting concatenated list.
List.singleton definition
{α : Type u} (a : α) : List α
Full source
/--
Constructs a single-element list.

Examples:
 * `List.singleton 5 = [5]`.
 * `List.singleton "green" = ["green"]`.
 * `List.singleton [1, 2, 3] = [[1, 2, 3]]`
-/
@[inline] protected def singleton {α : Type u} (a : α) : List α := [a]
Singleton list constructor
Informal description
Given an element $a$ of type $\alpha$, the function returns a list containing only $a$ as its single element.
List.pure abbrev
Full source
@[deprecated singleton (since := "2024-10-16")] protected abbrev pure := @singleton
Singleton List Construction via `pure`
Informal description
Given an element $a$ of type $\alpha$, the function `List.pure` returns a singleton list containing $a$ as its only element, i.e., $[a]$.
List.flatMap definition
{α : Type u} {β : Type v} (b : α → List β) (as : List α) : List β
Full source
/--
Applies a function that returns a list to each element of a list, and concatenates the resulting
lists.

Examples:
* `[2, 3, 2].flatMap List.range = [0, 1, 0, 1, 2, 0, 1]`
* `["red", "blue"].flatMap String.toList = ['r', 'e', 'd', 'b', 'l', 'u', 'e']`
-/
@[inline] def flatMap {α : Type u} {β : Type v} (b : α → List β) (as : List α) : List β := flatten (map b as)
List flat mapping (monadic bind)
Informal description
Given a function $f : \alpha \to \text{List}\ \beta$ and a list $L : \text{List}\ \alpha$, the function $\text{flatMap}\ f\ L$ applies $f$ to each element of $L$ and concatenates the resulting lists. For example: - $\text{flatMap}\ (\lambda x \mapsto [x, x])\ [1, 2] = [1, 1, 2, 2]$ - $\text{flatMap}\ \text{range}\ [2, 3, 2] = [0, 1, 0, 1, 2, 0, 1]$
List.flatMap_nil theorem
{f : α → List β} : List.flatMap f [] = []
Full source
@[simp] theorem flatMap_nil {f : α → List β} : List.flatMap f [] = [] := by simp [flatten, List.flatMap]
FlatMap of Empty List Yields Empty List
Informal description
For any function $f : \alpha \to \text{List}\ \beta$, the flatMap operation applied to the empty list yields the empty list, i.e., $\text{flatMap}\ f\ [] = []$.
List.flatMap_cons theorem
{x : α} {xs : List α} {f : α → List β} : List.flatMap f (x :: xs) = f x ++ List.flatMap f xs
Full source
@[simp] theorem flatMap_cons {x : α} {xs : List α} {f : α → List β} :
  List.flatMap f (x :: xs) = f x ++ List.flatMap f xs := by simp [flatten, List.flatMap]
Recursive Definition of flatMap for Non-Empty Lists
Informal description
For any function $f : \alpha \to \text{List}\ \beta$, element $x \in \alpha$, and list $xs \in \text{List}\ \alpha$, the flatMap operation satisfies the recursive relation: \[ \text{flatMap}\ f\ (x :: xs) = f\ x +\!\!\!+ \text{flatMap}\ f\ xs \] where $+\!\!\!+$ denotes list concatenation.
List.bind abbrev
Full source
@[deprecated flatMap (since := "2024-10-16")] abbrev bind := @flatMap
List Monadic Bind Operation (FlatMap)
Informal description
Given a function $f : \alpha \to \text{List}\ \beta$ and a list $L : \text{List}\ \alpha$, the function $\text{bind}\ f\ L$ applies $f$ to each element of $L$ and concatenates the resulting lists. For example: - $\text{bind}\ (\lambda x \mapsto [x, x])\ [1, 2] = [1, 1, 2, 2]$ - $\text{bind}\ \text{range}\ [2, 3, 2] = [0, 1, 0, 1, 2, 0, 1]$
List.nil_flatMap abbrev
Full source
@[deprecated flatMap_nil (since := "2024-10-16")] abbrev nil_flatMap := @flatMap_nil
FlatMap of Empty List Yields Empty List
Informal description
For any function $f : \alpha \to \text{List}\ \beta$, the flatMap operation applied to the empty list yields the empty list, i.e., $\text{flatMap}\ f\ [] = []$.
List.cons_flatMap abbrev
Full source
@[deprecated flatMap_cons (since := "2024-10-16")] abbrev cons_flatMap := @flatMap_cons
Recursive Definition of flatMap for Non-Empty Lists
Informal description
For any function $f : \alpha \to \text{List}\ \beta$, element $x \in \alpha$, and list $xs \in \text{List}\ \alpha$, the flatMap operation satisfies the recursive relation: \[ \text{flatMap}\ f\ (x :: xs) = f\ x +\!\!\!+ \text{flatMap}\ f\ xs \] where $+\!\!\!+$ denotes list concatenation.
List.replicate definition
: (n : Nat) → (a : α) → List α
Full source
/--
Creates a list that contains `n` copies of `a`.

* `List.replicate 5 "five" = ["five", "five", "five", "five", "five"]`
* `List.replicate 0 "zero" = []`
* `List.replicate 2 ' ' = [' ', ' ']`
-/
def replicate : (n : Nat) → (a : α) → List α
  | 0,   _ => []
  | n+1, a => a :: replicate n a
List replication function
Informal description
The function `List.replicate` takes a natural number `n` and an element `a` of type `α`, and returns a list containing `n` copies of `a`. For example: - `List.replicate 5 "five"` produces `["five", "five", "five", "five", "five"]` - `List.replicate 0 "zero"` produces the empty list `[]` - `List.replicate 2 ' '` produces `[' ', ' ']`
List.replicate_zero theorem
{a : α} : replicate 0 a = []
Full source
@[simp] theorem replicate_zero {a : α} : replicate 0 a = [] := rfl
Replicating Zero Times Yields Empty List
Informal description
For any element $a$ of type $\alpha$, the list obtained by replicating $a$ zero times is the empty list, i.e., $\mathrm{replicate}\,0\,a = []$.
List.replicate_succ theorem
{a : α} {n : Nat} : replicate (n + 1) a = a :: replicate n a
Full source
theorem replicate_succ {a : α} {n : Nat} : replicate (n+1) a = a :: replicate n a := rfl
Recursive Definition of List Replication: $\mathrm{replicate}\,(n+1)\,a = a :: \mathrm{replicate}\,n\,a$
Informal description
For any element $a$ of type $\alpha$ and any natural number $n$, the list obtained by replicating $a$ $n+1$ times is equal to the list constructed by prepending $a$ to the list obtained by replicating $a$ $n$ times, i.e., \[ \mathrm{replicate}\,(n + 1)\,a = a :: \mathrm{replicate}\,n\,a. \]
List.length_replicate theorem
{n : Nat} {a : α} : (replicate n a).length = n
Full source
@[simp] theorem length_replicate {n : Nat} {a : α} : (replicate n a).length = n := by
  induction n with
  | zero => simp
  | succ n ih => simp only [ih, replicate_succ, length_cons, Nat.succ_eq_add_one]
Length of Replicated List Equals Replication Count
Informal description
For any natural number $n$ and any element $a$ of type $\alpha$, the length of the list obtained by replicating $a$ $n$ times is equal to $n$, i.e., $\mathrm{length}(\mathrm{replicate}\,n\,a) = n$.
List.leftpad definition
(n : Nat) (a : α) (l : List α) : List α
Full source
/--
Pads `l : List α` on the left with repeated occurrences of `a : α` until it is of length `n`. If `l`
already has at least `n` elements, it is returned unmodified.

Examples:
 * `[1, 2, 3].leftpad 5 0 = [0, 0, 1, 2, 3]`
 * `["red", "green", "blue"].leftpad 4 "blank" = ["blank", "red", "green", "blue"]`
 * `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
 * `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
-/
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
Left padding of a list
Informal description
Given a natural number $n$, an element $a$ of type $\alpha$, and a list $l$ of elements of type $\alpha$, the function `List.leftpad` pads $l$ on the left with repeated occurrences of $a$ until the resulting list has length $n$. If $l$ already has length at least $n$, it is returned unchanged. More formally, the result is the concatenation of $\mathrm{replicate}\,(n - \mathrm{length}\,l)\,a$ with $l$.
List.rightpad definition
(n : Nat) (a : α) (l : List α) : List α
Full source
/--
Pads `l : List α` on the right with repeated occurrences of `a : α` until it is of length `n`. If
`l` already has at least `n` elements, it is returned unmodified.

Examples:
 * `[1, 2, 3].rightpad 5 0 = [1, 2, 3, 0, 0]`
 * `["red", "green", "blue"].rightpad 4 "blank" = ["red", "green", "blue", "blank"]`
 * `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
 * `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
-/
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
Right-padding a list to a given length
Informal description
Given a list $l$ of elements of type $\alpha$, a natural number $n$, and an element $a$ of type $\alpha$, the function `rightpad` pads $l$ on the right with repeated occurrences of $a$ until the resulting list has length $n$. If $l$ already has length at least $n$, it is returned unchanged. More precisely, the result is the concatenation of $l$ with a list of $n - \text{length}(l)$ copies of $a$.
List.reduceOption definition
{α} : List (Option α) → List α
Full source
/-- Drop `none`s from a list, and replace each remaining `some a` with `a`. -/
@[inline] def reduceOption {α} : List (Option α) → List α :=
  List.filterMap id
Filter out `none` values from a list of options
Informal description
Given a list of optional values of type `Option α`, the function `List.reduceOption` removes all `none` values and extracts the values from the remaining `some a` elements, returning a list of type `List α`.
List.instEmptyCollection instance
: EmptyCollection (List α)
Full source
instance : EmptyCollection (List α) := ⟨List.nil⟩
Empty List as Empty Collection
Informal description
The type `List α` of lists over any type `α` has a canonical empty collection structure, where the empty list `[]` serves as the empty collection.
List.empty_eq theorem
: (∅ : List α) = []
Full source
@[simp] theorem empty_eq : ( : List α) = [] := rfl
Empty Collection Equals Empty List
Informal description
The empty collection of type `List α` is equal to the empty list `[]`.
List.isEmpty definition
: List α → Bool
Full source
/--
Checks whether a list is empty.

`O(1)`.

Examples:
* `[].isEmpty = true`
* `["grape"].isEmpty = false`
* `["apple", "banana"].isEmpty = false`
-/
def isEmpty : List α → Bool
  | []     => true
  | _ :: _ => false
List emptiness check
Informal description
The function checks whether a list is empty, returning `true` if the list has no elements and `false` otherwise. This operation has constant time complexity \(O(1)\). Examples: - The empty list `[]` returns `true`. - A non-empty list like `["grape"]` or `["apple", "banana"]` returns `false`.
List.isEmpty_nil theorem
: ([] : List α).isEmpty = true
Full source
@[simp] theorem isEmpty_nil : ([] : List α).isEmpty = true := rfl
Emptiness Check for Empty List: `isEmpty([]) = true`
Informal description
For the empty list `[]` of any type `α`, the `isEmpty` function returns `true`.
List.isEmpty_cons theorem
: (x :: xs : List α).isEmpty = false
Full source
@[simp] theorem isEmpty_cons : (x :: xs : List α).isEmpty = false := rfl
Non-emptiness of Cons List: $\text{isEmpty}(x :: \text{xs}) = \text{false}$
Informal description
For any element $x$ of type $\alpha$ and any list `xs` of type `List α`, the function `isEmpty` applied to the list `x :: xs` returns `false`.
List.elem definition
[BEq α] (a : α) : (l : List α) → Bool
Full source
/--
Checks whether `a` is an element of `l`, using `==` to compare elements.

`O(|l|)`. `List.contains` is a synonym that takes the list before the element.

The preferred simp normal form is `l.contains a`. When `LawfulBEq α` is available,
`l.contains a = true ↔ a ∈ l` and `l.contains a = false ↔ a ∉ l`.

Example:
* `List.elem 3 [1, 4, 2, 3, 3, 7] = true`
* `List.elem 5 [1, 4, 2, 3, 3, 7] = false`
-/
def elem [BEq α] (a : α) : (l : List α) → Bool
  | []    => false
  | b::bs => match a == b with
    | true  => true
    | false => elem a bs
List membership check via boolean equality
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, the function `List.elem` checks whether an element $a$ of type $\alpha$ is present in a list $l$ of type $\text{List}\ \alpha$. The function returns `true` if $a$ is found in $l$ using the `==` relation, and `false` otherwise. The time complexity is $O(|l|)$.
List.elem_nil theorem
[BEq α] : ([] : List α).elem a = false
Full source
@[simp] theorem elem_nil [BEq α] : ([] : List α).elem a = false := rfl
Empty List Membership Check: $\text{elem}(a, []) = \text{false}$
Informal description
For any type $\alpha$ with a boolean equality relation `==`, the function `List.elem` applied to the empty list `[]` and any element $a$ of type $\alpha$ returns `false`. That is, $\text{elem}(a, []) = \text{false}$.
List.elem_cons theorem
[BEq α] {a : α} : (b :: bs).elem a = match a == b with | true => true | false => bs.elem a
Full source
theorem elem_cons [BEq α] {a : α} :
    (b::bs).elem a = match a == b with | true => true | false => bs.elem a := rfl
Recursive Definition of List Membership via Boolean Equality
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $a \in \alpha$, the membership check of $a$ in a list $b :: bs$ (where $b \in \alpha$ and $bs$ is a list of elements of type $\alpha$) is defined as follows: if $a == b$ evaluates to `true`, then the result is `true`; otherwise, the result is the membership check of $a$ in the tail $bs$.
List.contains abbrev
[BEq α] (as : List α) (a : α) : Bool
Full source
/--
Checks whether `a` is an element of `as`, using `==` to compare elements.

`O(|as|)`. `List.elem` is a synonym that takes the element before the list.

The preferred simp normal form is `l.contains a`, and when `LawfulBEq α` is available,
`l.contains a = true ↔ a ∈ l` and `l.contains a = false ↔ a ∉ l`.

Examples:
* `[1, 4, 2, 3, 3, 7].contains 3 = true`
* `List.contains [1, 4, 2, 3, 3, 7] 5 = false`
-/
abbrev contains [BEq α] (as : List α) (a : α) : Bool :=
  elem a as
List Membership Check via Boolean Equality ($\text{List.contains}$)
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, the function `List.contains` checks whether an element $a$ of type $\alpha$ is present in a list $as$ of type $\text{List}\ \alpha$. The function returns `true` if $a$ is found in $as$ using the `==` relation, and `false` otherwise. The time complexity is $O(|as|)$.
List.contains_nil theorem
[BEq α] : ([] : List α).contains a = false
Full source
@[simp] theorem contains_nil [BEq α] : ([] : List α).contains a = false := rfl
Empty List Contains No Elements
Informal description
For any type $\alpha$ with a boolean equality relation, the empty list `[]` does not contain any element $a$ of type $\alpha$, i.e., `[].contains a = false`.
List.Mem inductive
(a : α) : List α → Prop
Full source
/--
List membership, typically accessed via the `∈` operator.

`a ∈ l` means that `a` is an element of the list `l`. Elements are compared according to Lean's
logical equality.

The related function `List.elem` is a Boolean membership test that uses a `BEq α` instance.

Examples:
* `a ∈ [x, y, z] ↔ a = x ∨ a = y ∨ a = z`
-/
inductive Mem (a : α) : List α → Prop
  /-- The head of a list is a member: `a ∈ a :: as`. -/
  | head (as : List α) : Mem a (a::as)
  /-- A member of the tail of a list is a member of the list: `a ∈ l → a ∈ b :: l`. -/
  | tail (b : α) {as : List α} : Mem a as → Mem a (b::as)
List Membership
Informal description
The proposition that an element \( a \) is a member of a list \( l \), denoted \( a \in l \). This means there exists an element in \( l \) that is equal to \( a \) under Lean's logical equality. For example, \( a \in [x, y, z] \) holds if and only if \( a = x \), \( a = y \), or \( a = z \).
List.instMembership instance
: Membership α (List α)
Full source
instance : Membership α (List α) where
  mem l a := Mem a l
Membership Relation for Lists
Informal description
For any type $\alpha$, the membership relation $a \in l$ for a list $l$ of elements of type $\alpha$ holds if and only if $a$ appears in $l$.
List.mem_of_elem_eq_true theorem
[BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true → a ∈ as
Full source
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = truea ∈ as := by
  match as with
  | [] => simp [elem]
  | a'::as =>
    simp [elem]
    split
    next h => intros; simp [BEq.beq] at h; subst h; apply Mem.head
    next _ => intro h; exact Mem.tail _ (mem_of_elem_eq_true h)
Boolean Membership Implies Propositional Membership in Lists
Informal description
For any type $\alpha$ with a lawful boolean equality relation `==`, if the boolean membership check `elem a as` evaluates to `true` for an element $a$ and a list $as$ of type $\text{List}\ \alpha$, then $a$ is a member of $as$ (i.e., $a \in as$ holds).
List.elem_eq_true_of_mem theorem
[BEq α] [LawfulBEq α] {a : α} {as : List α} (h : a ∈ as) : elem a as = true
Full source
theorem elem_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : List α} (h : a ∈ as) : elem a as = true := by
  induction h with
  | head _ => simp [elem]
  | tail _ _ ih => simp [elem]; split; rfl; assumption
Membership in List Implies Boolean Check is True
Informal description
For any type $\alpha$ with a decidable boolean equality relation `==` that coincides with propositional equality (i.e., `[BEq α]` and `[LawfulBEq α]`), if an element $a \in \alpha$ is a member of a list $as$ (i.e., $a \in as$), then the boolean membership check `elem a as` evaluates to `true`.
List.instDecidableMemOfLawfulBEq instance
[BEq α] [LawfulBEq α] (a : α) (as : List α) : Decidable (a ∈ as)
Full source
instance [BEq α] [LawfulBEq α] (a : α) (as : List α) : Decidable (a ∈ as) :=
  decidable_of_decidable_of_iff (Iff.intro mem_of_elem_eq_true elem_eq_true_of_mem)
Decidability of List Membership with Lawful Boolean Equality
Informal description
For any type $\alpha$ with a lawful boolean equality relation `==`, the membership relation $a \in l$ for an element $a$ in a list $l$ of type $\text{List}\ \alpha$ is decidable. This means that given any $a$ and $l$, we can constructively determine whether $a$ appears in $l$.
List.mem_append_left theorem
{a : α} {as : List α} (bs : List α) : a ∈ as → a ∈ as ++ bs
Full source
theorem mem_append_left {a : α} {as : List α} (bs : List α) : a ∈ asa ∈ as ++ bs := by
  intro h
  induction h with
  | head => apply Mem.head
  | tail => apply Mem.tail; assumption
Membership Preservation Under Left Append: $a \in as \implies a \in as \mathbin{+\kern-0.5em+} bs$
Informal description
For any element $a$ of type $\alpha$ and any list $as$ of elements of type $\alpha$, if $a$ is a member of $as$, then $a$ is also a member of the concatenated list $as \mathbin{+\kern-0.5em+} bs$ for any list $bs$ of elements of type $\alpha$.
List.mem_append_right theorem
{b : α} (as : List α) {bs : List α} : b ∈ bs → b ∈ as ++ bs
Full source
theorem mem_append_right {b : α} (as : List α) {bs : List α} : b ∈ bsb ∈ as ++ bs := by
  intro h
  induction as with
  | nil  => simp [h]
  | cons => apply Mem.tail; assumption
Membership Preservation Under Right Concatenation
Informal description
For any element $b$ of type $\alpha$ and any lists $as, bs$ of type $\alpha$, if $b$ is an element of $bs$, then $b$ is also an element of the concatenated list $as \mathbin{+\kern-0.5em+} bs$.
List.decidableBEx instance
(p : α → Prop) [DecidablePred p] : ∀ l : List α, Decidable (Exists fun x => x ∈ l ∧ p x)
Full source
instance decidableBEx (p : α → Prop) [DecidablePred p] :
    ∀ l : List α, Decidable (Exists fun x => x ∈ lx ∈ l ∧ p x)
  | [] => isFalse nofun
  | x :: xs =>
    if h₁ : p x then isTrue ⟨x, .head .., h₁⟩ else
      match decidableBEx p xs with
      | isTrue h₂ => isTrue <| let ⟨y, hm, hp⟩ := h₂; ⟨y, .tail _ hm, hp⟩
      | isFalse h₂ => isFalse fun
        | ⟨y, .tail _ h, hp⟩ => h₂ ⟨y, h, hp⟩
        | ⟨_, .head .., hp⟩ => h₁ hp
Decidability of Bounded Existential Quantifier over Lists
Informal description
For any predicate $p$ on a type $\alpha$ with a decidable truth value at each point, and for any list $l$ of elements of type $\alpha$, it is decidable whether there exists an element $x \in l$ such that $p(x)$ holds.
List.decidableBAll instance
(p : α → Prop) [DecidablePred p] : ∀ l : List α, Decidable (∀ x, x ∈ l → p x)
Full source
instance decidableBAll (p : α → Prop) [DecidablePred p] :
    ∀ l : List α, Decidable (∀ x, x ∈ l → p x)
  | [] => isTrue nofun
  | x :: xs =>
    if h₁ : p x then
      match decidableBAll p xs with
      | isTrue h₂ => isTrue fun
        | y, .tail _ h => h₂ y h
        | _, .head .. => h₁
      | isFalse h₂ => isFalse fun H => h₂ fun y hm => H y (.tail _ hm)
    else isFalse fun H => h₁ <| H x (.head ..)
Decidability of Universal Quantification over Lists
Informal description
For any predicate $p$ on a type $\alpha$ with a decidable truth value at each point, and for any list $l$ of elements of type $\alpha$, the proposition $\forall x \in l, p(x)$ is decidable.
List.take definition
: (n : Nat) → (xs : List α) → List α
Full source
/--
Extracts the first `n` elements of `xs`, or the whole list if `n` is greater than `xs.length`.

`O(min n |xs|)`.

Examples:
* `[a, b, c, d, e].take 0 = []`
* `[a, b, c, d, e].take 3 = [a, b, c]`
* `[a, b, c, d, e].take 6 = [a, b, c, d, e]`
-/
def take : (n : Nat) → (xs : List α) → List α
  | 0,   _     => []
  | _+1, []    => []
  | n+1, a::as => a :: take n as
Take first \( n \) elements of a list
Informal description
Given a natural number \( n \) and a list \( xs \), the function returns the first \( n \) elements of \( xs \). If \( n \) is greater than the length of \( xs \), the entire list is returned. The operation has time complexity \( O(\min(n, \text{length}(xs))) \). **Examples:** - \(\text{take}\ 0\ [a, b, c, d, e] = []\) - \(\text{take}\ 3\ [a, b, c, d, e] = [a, b, c]\) - \(\text{take}\ 6\ [a, b, c, d, e] = [a, b, c, d, e]\)
List.take_nil theorem
{i : Nat} : ([] : List α).take i = []
Full source
@[simp] theorem take_nil {i : Nat} : ([] : List α).take i = [] := by cases i <;> rfl
Taking Elements from Empty List Yields Empty List
Informal description
For any natural number $i$, taking the first $i$ elements of the empty list results in the empty list, i.e., $\text{take}\ i\ [] = []$.
List.take_zero theorem
{l : List α} : l.take 0 = []
Full source
@[simp] theorem take_zero {l : List α} : l.take 0 = [] := rfl
Taking Zero Elements Yields Empty List
Informal description
For any list $l$ of elements of type $\alpha$, taking the first $0$ elements of $l$ results in the empty list, i.e., $\text{take}\ 0\ l = []$.
List.take_succ_cons theorem
{a : α} {as : List α} {i : Nat} : (a :: as).take (i + 1) = a :: as.take i
Full source
@[simp] theorem take_succ_cons {a : α} {as : List α} {i : Nat} : (a::as).take (i+1) = a :: as.take i := rfl
Recursive Definition of List Take Operation: $\text{take}\ (i+1)\ (a :: as) = a :: (\text{take}\ i\ as)$
Informal description
For any element $a$ of type $\alpha$, any list $as$ of elements of type $\alpha$, and any natural number $i$, taking the first $i+1$ elements of the list $a :: as$ (constructed by prepending $a$ to $as$) results in the list $a :: (\text{take}\ i\ as)$. In other words, $\text{take}\ (i+1)\ (a :: as) = a :: (\text{take}\ i\ as)$.
List.drop definition
: (n : Nat) → (xs : List α) → List α
Full source
/--
Removes the first `n` elements of the list `xs`. Returns the empty list if `n` is greater than the
length of the list.

`O(min n |xs|)`.

Examples:
* `[0, 1, 2, 3, 4].drop 0 = [0, 1, 2, 3, 4]`
* `[0, 1, 2, 3, 4].drop 3 = [3, 4]`
* `[0, 1, 2, 3, 4].drop 6 = []`
-/
def drop : (n : Nat) → (xs : List α) → List α
  | 0,   as     => as
  | _+1, []    => []
  | n+1, _::as => drop n as
Drop first $n$ elements from a list
Informal description
Given a natural number $n$ and a list $xs$ of elements of type $\alpha$, the function `List.drop n xs` returns the list obtained by removing the first $n$ elements of $xs$. If $n$ is greater than the length of $xs$, the result is the empty list. The operation has time complexity $O(\min(n, |xs|))$. Examples: * $\text{drop}\ 0\ [0, 1, 2, 3, 4] = [0, 1, 2, 3, 4]$ * $\text{drop}\ 3\ [0, 1, 2, 3, 4] = [3, 4]$ * $\text{drop}\ 6\ [0, 1, 2, 3, 4] = []$
List.drop_nil theorem
: ([] : List α).drop i = []
Full source
@[simp] theorem drop_nil : ([] : List α).drop i = [] := by
  cases i <;> rfl
Drop Operation Preserves Empty List: $\text{drop}\ i\ [] = []$
Informal description
For any natural number $i$, dropping the first $i$ elements from the empty list results in the empty list, i.e., $\text{drop}\ i\ [] = []$.
List.drop_zero theorem
{l : List α} : l.drop 0 = l
Full source
@[simp] theorem drop_zero {l : List α} : l.drop 0 = l := rfl
Identity of Drop Zero Elements: $\text{drop}\ 0\ l = l$
Informal description
For any list $l$ of elements of type $\alpha$, dropping the first $0$ elements leaves the list unchanged, i.e., $\text{drop}\ 0\ l = l$.
List.drop_succ_cons theorem
{a : α} {l : List α} {i : Nat} : (a :: l).drop (i + 1) = l.drop i
Full source
@[simp] theorem drop_succ_cons {a : α} {l : List α} {i : Nat} : (a :: l).drop (i + 1) = l.drop i := rfl
Recursive Property of List Drop: $\text{drop}\ (i+1)\ (a :: l) = \text{drop}\ i\ l$
Informal description
For any element $a$ of type $\alpha$, any list $l$ of elements of type $\alpha$, and any natural number $i$, dropping the first $i+1$ elements from the list $a :: l$ is equal to dropping the first $i$ elements from $l$. In other words, $\text{drop}\ (i+1)\ (a :: l) = \text{drop}\ i\ l$.
List.drop_eq_nil_of_le theorem
{as : List α} {i : Nat} (h : as.length ≤ i) : as.drop i = []
Full source
theorem drop_eq_nil_of_le {as : List α} {i : Nat} (h : as.length ≤ i) : as.drop i = [] := by
  match as, i with
  | [],    i   => simp
  | _::_,  0   => simp at h
  | _::as, i+1 => simp only [length_cons] at h; exact @drop_eq_nil_of_le as i (Nat.le_of_succ_le_succ h)
Drop Operation Yields Empty List When Index Exceeds Length: $\text{drop}\ i\ as = []$ if $|as| \leq i$
Informal description
For any list $as$ of elements of type $\alpha$ and any natural number $i$, if the length of $as$ is less than or equal to $i$, then dropping the first $i$ elements from $as$ results in the empty list, i.e., $\text{drop}\ i\ as = []$.
List.extract_eq_drop_take theorem
{l : List α} {start stop : Nat} : l.extract start stop = (l.drop start).take (stop - start)
Full source
@[simp] theorem extract_eq_drop_take {l : List α} {start stop : Nat} :
    l.extract start stop = (l.drop start).take (stop - start) := rfl
Extract as Drop then Take
Informal description
For any list $l$ of elements of type $\alpha$ and natural numbers $start$ and $stop$, the sublist obtained by extracting elements from index $start$ to $stop$ (exclusive) is equal to taking the first $(stop - start)$ elements after dropping the first $start$ elements of $l$. That is, \[ \text{extract}\ l\ start\ stop = \text{take}\ (stop - start)\ (\text{drop}\ start\ l). \]
List.takeWhile definition
(p : α → Bool) : (xs : List α) → List α
Full source
/--
 Returns the longest initial segment of `xs` for which `p` returns true.

`O(|xs|)`.

Examples:
* `[7, 6, 4, 8].takeWhile (· > 5) = [7, 6]`
* `[7, 6, 6, 5].takeWhile (· > 5) = [7, 6, 6]`
* `[7, 6, 6, 8].takeWhile (· > 5) = [7, 6, 6, 8]`
-/
def takeWhile (p : α → Bool) : (xs : List α) → List α
  | []       => []
  | hd :: tl => match p hd with
   | true  => hd :: takeWhile p tl
   | false => []
Take elements from a list while a predicate holds
Informal description
Given a predicate \( p : \alpha \to \text{Bool} \) and a list \( xs : \text{List } \alpha \), the function \( \text{takeWhile } p \, xs \) returns the longest initial segment of \( xs \) for which \( p \) holds for every element. The operation has time complexity \( O(|xs|) \). **Examples:** - \( \text{takeWhile } (\lambda x \Rightarrow x > 5) \, [7, 6, 4, 8] = [7, 6] \) - \( \text{takeWhile } (\lambda x \Rightarrow x > 5) \, [7, 6, 6, 5] = [7, 6, 6] \) - \( \text{takeWhile } (\lambda x \Rightarrow x > 5) \, [7, 6, 6, 8] = [7, 6, 6, 8] \)
List.takeWhile_nil theorem
: ([] : List α).takeWhile p = []
Full source
@[simp] theorem takeWhile_nil : ([] : List α).takeWhile p = [] := rfl
`takeWhile` Preserves Empty List
Informal description
For any predicate $p$ on elements of type $\alpha$, applying the `takeWhile` operation to the empty list `[]` results in the empty list, i.e., $\text{takeWhile}\, p\, [] = []$.
List.dropWhile definition
(p : α → Bool) : List α → List α
Full source
/--
Removes the longest prefix of a list for which `p` returns `true`.

Elements are removed from the list until one is encountered for which `p` returns `false`. This
element and the remainder of the list are returned.

`O(|l|)`.

Examples:
 * `[1, 3, 2, 4, 2, 7, 4].dropWhile (· < 4) = [4, 2, 7, 4]`
 * `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 4) = [8, 3, 2, 4, 2, 7, 4]`
 * `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 100) = []`
-/
def dropWhile (p : α → Bool) : List α → List α
  | []   => []
  | a::l => match p a with
    | true  => dropWhile p l
    | false => a::l
Drop elements from a list while a predicate holds
Informal description
Given a predicate `p : α → Bool` and a list `l : List α`, the function `dropWhile p l` removes the longest prefix of `l` for which `p` returns `true` and returns the remaining elements. The operation has time complexity $O(|l|)$. **Examples:** - `[1, 3, 2, 4, 2, 7, 4].dropWhile (· < 4) = [4, 2, 7, 4]` - `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 4) = [8, 3, 2, 4, 2, 7, 4]` - `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 100) = []`
List.dropWhile_nil theorem
: ([] : List α).dropWhile p = []
Full source
@[simp] theorem dropWhile_nil : ([] : List α).dropWhile p = [] := rfl
`dropWhile` Preserves Empty List
Informal description
For any predicate $p$ on elements of type $\alpha$, applying the `dropWhile` operation to the empty list `[]` results in the empty list, i.e., $\text{dropWhile}\, p\, [] = []$.
List.partition definition
(p : α → Bool) (as : List α) : List α × List α
Full source
/--
Returns a pair of lists that together contain all the elements of `as`. The first list contains
those elements for which `p` returns `true`, and the second contains those for which `p` returns
`false`.

`O(|l|)`. `as.partition p` is equivalent to `(as.filter p, as.filter (not ∘ p))`, but it is slightly
more efficient since it only has to do one pass over the list.

Examples:
 * `[1, 2, 5, 2, 7, 7].partition (· > 2) = ([5, 7, 7], [1, 2, 2])`
 * `[1, 2, 5, 2, 7, 7].partition (fun _ => false) = ([], [1, 2, 5, 2, 7, 7])`
 * `[1, 2, 5, 2, 7, 7].partition (fun _ => true) = ([1, 2, 5, 2, 7, 7], [])`
-/
@[inline] def partition (p : α → Bool) (as : List α) : ListList α × List α :=
  loop as ([], [])
where
  @[specialize] loop : List α → ListList α × List αListList α × List α
  | [],    (bs, cs) => (bs.reverse, cs.reverse)
  | a::as, (bs, cs) =>
    match p a with
    | true  => loop as (a::bs, cs)
    | false => loop as (bs, a::cs)
Partition a list by a predicate
Informal description
Given a predicate $p : \alpha \to \text{Bool}$ and a list $L$ of elements of type $\alpha$, the function $\text{partition}\, p\, L$ returns a pair of lists $(L_1, L_2)$, where $L_1$ contains all elements of $L$ for which $p$ evaluates to $\text{true}$, and $L_2$ contains all elements for which $p$ evaluates to $\text{false}$. The order of elements in $L_1$ and $L_2$ is reversed from their original order in $L$. This operation has time complexity $O(|L|)$ and is equivalent to $(L.\text{filter}\, p, L.\text{filter}\, (\neg \circ p))$ but is more efficient as it performs only a single pass over the list. **Examples:** - $\text{partition}\, (\lambda x.\, x > 2)\, [1, 2, 5, 2, 7, 7] = ([5, 7, 7], [1, 2, 2])$ - $\text{partition}\, (\lambda \_.\, \text{false})\, L = ([], L)$ - $\text{partition}\, (\lambda \_.\, \text{true})\, L = (L, [])$
List.dropLast definition
{α} : List α → List α
Full source
/--
Removes the last element of the list, if one exists.

Examples:
* `[].dropLast = []`
* `["tea"].dropLast = []`
* `["tea", "coffee", "juice"].dropLast = ["tea", "coffee"]`
-/
def dropLast {α} : List α → List α
  | []    => []
  | [_]   => []
  | a::as => a :: dropLast as
Remove last element of a list
Informal description
Given a list \( L \) of elements of type \( \alpha \), the function `dropLast` returns a new list obtained by removing the last element of \( L \). If \( L \) is empty or contains only one element, the result is the empty list. **Examples:** - \( \text{dropLast} \, [] = [] \) - \( \text{dropLast} \, [x] = [] \) - \( \text{dropLast} \, [x, y, z] = [x, y] \)
List.dropLast_nil theorem
: ([] : List α).dropLast = []
Full source
@[simp] theorem dropLast_nil : ([] : List α).dropLast = [] := rfl
$\text{dropLast} \, [] = []$
Informal description
For any type $\alpha$, applying the `dropLast` function to the empty list `[]` results in the empty list `[]`, i.e., $\text{dropLast} \, [] = []$.
List.dropLast_single theorem
: [x].dropLast = []
Full source
@[simp] theorem dropLast_single : [x].dropLast = [] := rfl
DropLast of Singleton List Yields Empty List
Informal description
For any element $x$ of type $\alpha$, the result of removing the last element from the singleton list $[x]$ is the empty list, i.e., $\text{dropLast}([x]) = []$.
List.dropLast_cons₂ theorem
: (x :: y :: zs).dropLast = x :: (y :: zs).dropLast
Full source
@[simp] theorem dropLast_cons₂ :
    (x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
Recursive Definition of DropLast for Non-Singleton Lists
Informal description
For any elements $x, y$ of type $\alpha$ and any list $zs$ of elements of type $\alpha$, the result of removing the last element from the list $x :: y :: zs$ is equal to the list obtained by prepending $x$ to the result of removing the last element from $y :: zs$, i.e., $$ \text{dropLast}(x :: y :: zs) = x :: \text{dropLast}(y :: zs). $$
List.length_dropLast_cons theorem
{a : α} {as : List α} : (a :: as).dropLast.length = as.length
Full source
@[simp] theorem length_dropLast_cons {a : α} {as : List α} : (a :: as).dropLast.length = as.length := by
  match as with
  | []       => rfl
  | b::bs => simp [dropLast, length_dropLast_cons]
Length Preservation of DropLast Operation on Cons List
Informal description
For any element $a$ of type $\alpha$ and any list $as$ of elements of type $\alpha$, the length of the list obtained by removing the last element from $a :: as$ is equal to the length of $as$. In other words, $\text{length}(\text{dropLast}(a :: as)) = \text{length}(as)$.
List.Subset definition
(l₁ l₂ : List α)
Full source
/--
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
-/
protected def Subset (l₁ l₂ : List α) := ∀ ⦃a : α⦄, a ∈ l₁a ∈ l₂
List subset relation
Informal description
For two lists \( l_1 \) and \( l_2 \) of elements of type \( \alpha \), \( l_1 \subseteq l_2 \) means that every element \( a \) in \( l_1 \) is also an element of \( l_2 \), ignoring multiplicity.
List.instHasSubset instance
: HasSubset (List α)
Full source
instance : HasSubset (List α) := ⟨List.Subset⟩
Subset Relation on Lists
Informal description
For any type $\alpha$, the type of lists $\text{List } \alpha$ is equipped with the subset relation $\subseteq$, where for two lists $l_1$ and $l_2$, $l_1 \subseteq l_2$ means that every element of $l_1$ appears in $l_2$ (ignoring multiplicity).
List.instDecidableRelSubsetOfDecidableEq instance
[DecidableEq α] : DecidableRel (Subset : List α → List α → Prop)
Full source
instance [DecidableEq α] : DecidableRel (Subset : List α → List α → Prop) :=
  fun _ _ => decidableBAll _ _
Decidability of List Subset Relation for Types with Decidable Equality
Informal description
For any type $\alpha$ with decidable equality, the subset relation $\subseteq$ on lists of $\alpha$ is decidable. That is, given two lists $l_1$ and $l_2$ of elements of type $\alpha$, the proposition $l_1 \subseteq l_2$ (meaning every element of $l_1$ appears in $l_2$) is decidable.
List.Sublist inductive
{α} : List α → List α → Prop
Full source
/--
The first list is a non-contiguous sub-list of the second list. Typically written with the `<+`
operator.

In other words, `l₁ <+ l₂` means that `l₁` can be transformed into `l₂` by repeatedly inserting new
elements.
-/
inductive Sublist {α} : List α → List α → Prop
  /-- The base case: `[]` is a sublist of `[]` -/
  | slnil : Sublist [] []
  /-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
  | cons a : Sublist l₁ l₂ → Sublist l₁ (a :: l₂)
  /-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
  | cons₂ a : Sublist l₁ l₂ → Sublist (a :: l₁) (a :: l₂)
Sublist relation on lists
Informal description
The relation `Sublist` between two lists `l₁` and `l₂` of type `List α` indicates that `l₁` can be obtained from `l₂` by deleting some (possibly non-contiguous) elements. In other words, `l₁` is a subsequence of `l₂`. This is typically denoted using the `<+` operator, where `l₁ <+ l₂` means that `l₁` is a sublist of `l₂`.
List.term_<+_ definition
: Lean.TrailingParserDescr✝
Full source
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
Sublist notation
Informal description
The notation `l₁ <+ l₂` denotes that the list `l₁` is a sublist of `l₂`, meaning `l₁` can be obtained from `l₂` by deleting some (possibly non-contiguous) elements while preserving the order of the remaining elements. This is equivalent to saying `l₁` is a subsequence of `l₂`.
List.isSublist definition
[BEq α] : List α → List α → Bool
Full source
/--
True if the first list is a potentially non-contiguous sub-sequence of the second list, comparing
elements with the `==` operator.

The relation `List.Sublist` is a logical characterization of this property.

Examples:
* `[1, 3].isSublist [0, 1, 2, 3, 4] = true`
* `[1, 3].isSublist [0, 1, 2, 4] = false`
-/
def isSublist [BEq α] : List α → List α → Bool
  | [], _ => true
  | _, [] => false
  | l₁@(hd₁::tl₁), hd₂::tl₂ =>
    if hd₁ == hd₂
    then tl₁.isSublist tl₂
    else l₁.isSublist tl₂
Boolean sublist check for lists
Informal description
Given two lists `l₁` and `l₂` of type `List α` where `α` has a boolean equality relation `==`, the function `isSublist` returns `true` if `l₁` is a (potentially non-contiguous) subsequence of `l₂` under the `==` relation, and `false` otherwise. More precisely: - The empty list is a sublist of any list. - A non-empty list is not a sublist of the empty list. - For non-empty lists `hd₁::tl₁` and `hd₂::tl₂`, if `hd₁ == hd₂`, then recursively check if `tl₁` is a sublist of `tl₂`. Otherwise, check if `hd₁::tl₁` is a sublist of `tl₂`. Examples: - `[1, 3].isSublist [0, 1, 2, 3, 4] = true` - `[1, 3].isSublist [0, 1, 2, 4] = false`
List.IsPrefix definition
(l₁ : List α) (l₂ : List α) : Prop
Full source
/--
The first list is a prefix of the second.

`IsPrefix l₁ l₂`, written `l₁ <+: l₂`, means that there exists some `t : List α` such that `l₂` has
the form `l₁ ++ t`.

The function `List.isPrefixOf` is a Boolean equivalent.
-/
def IsPrefix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => l₁ ++ t = l₂
List prefix relation
Informal description
A list $l_1$ is a prefix of another list $l_2$, written $l_1 <+: l_2$, if there exists a list $t$ such that $l_2$ is the concatenation of $l_1$ and $t$, i.e., $l_2 = l_1 ++ t$.
List.term_<+:_ definition
: Lean.TrailingParserDescr✝
Full source
@[inherit_doc] infixl:50 " <+: " => IsPrefix
List prefix notation
Informal description
The infix notation `L₁ <+: L₂` denotes that the list `L₁` is a prefix of the list `L₂`, meaning there exists some list `L₃` such that `L₂ = L₁ ++ L₃`.
List.isPrefixOf definition
[BEq α] : List α → List α → Bool
Full source
/--
Checks whether the first list is a prefix of the second.

The relation `List.IsPrefixOf` expresses this property with respect to logical equality.

Examples:
 * `[1, 2].isPrefixOf [1, 2, 3] = true`
 * `[1, 2].isPrefixOf [1, 2] = true`
 * `[1, 2].isPrefixOf [1] = false`
 * `[1, 2].isPrefixOf [1, 1, 2, 3] = false`
-/
def isPrefixOf [BEq α] : List α → List α → Bool
  | [],    _     => true
  | _,     []    => false
  | a::as, b::bs => a == b && isPrefixOf as bs
Boolean check for list prefix
Informal description
Given two lists `L₁` and `L₂` of type `List α` where `α` has a boolean equality relation `==`, the function `isPrefixOf L₁ L₂` returns `true` if `L₁` is a prefix of `L₂`, and `false` otherwise. Specifically: - The empty list is a prefix of any list. - A non-empty list is not a prefix of the empty list. - For non-empty lists `a::as` and `b::bs`, the function checks if `a == b` and recursively checks if `as` is a prefix of `bs`.
List.isPrefixOf_nil_left theorem
[BEq α] : isPrefixOf ([] : List α) l = true
Full source
@[simp] theorem isPrefixOf_nil_left [BEq α] : isPrefixOf ([] : List α) l = true := by
  simp [isPrefixOf]
Empty List is Prefix of Any List
Informal description
For any list $l$ of type $\text{List}\,\alpha$ where $\alpha$ has a boolean equality relation, the empty list is a prefix of $l$, i.e., $\text{isPrefixOf}\,[]\,l = \text{true}$.
List.isPrefixOf_cons_nil theorem
[BEq α] : isPrefixOf (a :: as) ([] : List α) = false
Full source
@[simp] theorem isPrefixOf_cons_nil [BEq α] : isPrefixOf (a::as) ([] : List α) = false := rfl
Non-empty List is Not a Prefix of the Empty List
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any element $a \in \alpha$ and list `as : List α`, the boolean check `isPrefixOf (a :: as) []` evaluates to `false`. In other words, a non-empty list is never a prefix of the empty list.
List.isPrefixOf_cons₂ theorem
[BEq α] {a : α} : isPrefixOf (a :: as) (b :: bs) = (a == b && isPrefixOf as bs)
Full source
theorem isPrefixOf_cons₂ [BEq α] {a : α} :
    isPrefixOf (a::as) (b::bs) = (a == b && isPrefixOf as bs) := rfl
Prefix Check for Cons Lists: $(a :: \mathit{as}) \text{ isPrefixOf } (b :: \mathit{bs}) \leftrightarrow (a == b) \land (\mathit{as} \text{ isPrefixOf } \mathit{bs})$
Informal description
For any type $\alpha$ with a boolean equality relation `==`, and for any elements $a, b \in \alpha$ and lists $\mathit{as}, \mathit{bs} \in \text{List } \alpha$, the boolean check for whether the list $a :: \mathit{as}$ is a prefix of $b :: \mathit{bs}$ is equivalent to the conjunction of the boolean equality $a == b$ and the boolean check for whether $\mathit{as}$ is a prefix of $\mathit{bs}$.
List.isPrefixOf? definition
[BEq α] : (l₁ : List α) → (l₂ : List α) → Option (List α)
Full source
/--
If the first list is a prefix of the second, returns the result of dropping the prefix.

In other words, `isPrefixOf? l₁ l₂` returns `some t` when `l₂ == l₁ ++ t`.

Examples:
 * `[1, 2].isPrefixOf? [1, 2, 3] = some [3]`
 * `[1, 2].isPrefixOf? [1, 2] = some []`
 * `[1, 2].isPrefixOf? [1] = none`
 * `[1, 2].isPrefixOf? [1, 1, 2, 3] = none`
-/
def isPrefixOf? [BEq α] : (l₁ : List α) → (l₂ : List α) → Option (List α)
  | [], l₂ => some l₂
  | _, [] => none
  | (x₁ :: l₁), (x₂ :: l₂) =>
    if x₁ == x₂ then isPrefixOf? l₁ l₂ else none
Optional suffix after prefix check for lists
Informal description
Given two lists $l_1$ and $l_2$ of type $\text{List } \alpha$ where $\alpha$ has a boolean equality relation, the function $\text{isPrefixOf?}$ checks if $l_1$ is a prefix of $l_2$. If it is, it returns $\text{some } t$ where $t$ is the remaining suffix of $l_2$ after removing $l_1$ (i.e., $l_2 = l_1 ++ t$). If $l_1$ is not a prefix of $l_2$, it returns $\text{none}$. Examples: - $\text{isPrefixOf? } [1, 2] [1, 2, 3] = \text{some } [3]$ - $\text{isPrefixOf? } [1, 2] [1, 2] = \text{some } []$ - $\text{isPrefixOf? } [1, 2] [1] = \text{none}$ - $\text{isPrefixOf? } [1, 2] [1, 1, 2, 3] = \text{none}$
List.isSuffixOf definition
[BEq α] (l₁ l₂ : List α) : Bool
Full source
/--
Checks whether the first list is a suffix of the second.

The relation `List.IsSuffixOf` expresses this property with respect to logical equality.

Examples:
 * `[2, 3].isSuffixOf [1, 2, 3] = true`
 * `[2, 3].isSuffixOf [1, 2, 3, 4] = false`
 * `[2, 3].isSuffixOf [1, 2] = false`
 * `[2, 3].isSuffixOf [1, 1, 2, 3] = true`
-/
def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
  isPrefixOf l₁.reverse l₂.reverse
Boolean check for list suffix
Informal description
Given two lists $L_1$ and $L_2$ of type $\text{List } \alpha$ where $\alpha$ has a boolean equality relation $\text{BEq } \alpha$, the function $\text{isSuffixOf } L_1 L_2$ returns $\text{true}$ if $L_1$ is a suffix of $L_2$, and $\text{false}$ otherwise. This is implemented by checking whether the reverse of $L_1$ is a prefix of the reverse of $L_2$. Examples: - $\text{isSuffixOf } [2, 3] [1, 2, 3] = \text{true}$ - $\text{isSuffixOf } [2, 3] [1, 2, 3, 4] = \text{false}$ - $\text{isSuffixOf } [2, 3] [1, 2] = \text{false}$ - $\text{isSuffixOf } [2, 3] [1, 1, 2, 3] = \text{true}$
List.isSuffixOf_nil_left theorem
[BEq α] : isSuffixOf ([] : List α) l = true
Full source
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
  simp [isSuffixOf]
Empty List is Suffix of Any List
Informal description
For any list $l$ of type $\text{List}\,\alpha$ where $\alpha$ has a boolean equality relation, the empty list is a suffix of $l$, i.e., $\text{isSuffixOf}\,[]\,l = \text{true}$.
List.isSuffixOf? definition
[BEq α] (l₁ l₂ : List α) : Option (List α)
Full source
/--
If the first list is a suffix of the second, returns the result of dropping the suffix from the
second.

In other words, `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.

Examples:
 * `[2, 3].isSuffixOf? [1, 2, 3] = some [1]`
 * `[2, 3].isSuffixOf? [1, 2, 3, 4] = none`
 * `[2, 3].isSuffixOf? [1, 2] = none`
 * `[2, 3].isSuffixOf? [1, 1, 2, 3] = some [1, 1]`
-/
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
  Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
Optional prefix before suffix check for lists
Informal description
Given two lists $l_1$ and $l_2$ of type $\text{List}\,\alpha$ where $\alpha$ has a boolean equality relation, the function checks if $l_1$ is a suffix of $l_2$. If it is, it returns $\text{some}\,t$ where $t$ is the prefix of $l_2$ before the suffix $l_1$ (i.e., $l_2 = t ++ l_1$). If $l_1$ is not a suffix of $l_2$, it returns $\text{none}$. Examples: - $\text{isSuffixOf?}\,[2, 3]\,[1, 2, 3] = \text{some}\,[1]$ - $\text{isSuffixOf?}\,[2, 3]\,[1, 2, 3, 4] = \text{none}$ - $\text{isSuffixOf?}\,[2, 3]\,[1, 2] = \text{none}$ - $\text{isSuffixOf?}\,[2, 3]\,[1, 1, 2, 3] = \text{some}\,[1, 1]$
List.IsSuffix definition
(l₁ : List α) (l₂ : List α) : Prop
Full source
/--
The first list is a suffix of the second.

`IsSuffix l₁ l₂`, written `l₁ <:+ l₂`, means that there exists some `t : List α` such that `l₂` has
the form `t ++ l₁`.

The function `List.isSuffixOf` is a Boolean equivalent.
-/
def IsSuffix (l₁ : List α) (l₂ : List α) : Prop := Exists fun t => t ++ l₁ = l₂
List suffix relation
Informal description
For two lists $l_1$ and $l_2$ of type $\alpha$, the relation $\text{IsSuffix}\,l_1\,l_2$ (written $l_1 <:+ l_2$) holds if there exists a list $t$ such that $l_2$ can be written as the concatenation $t ++ l_1$. This is equivalent to saying $l_1$ appears at the end of $l_2$.
List.term_<:+_ definition
: Lean.TrailingParserDescr✝
Full source
@[inherit_doc] infixl:50 " <:+ " => IsSuffix
List suffix notation
Informal description
The infix notation `L₁ <:+ L₂` denotes that the list `L₁` is a suffix of the list `L₂`, meaning there exists some list `L` such that `L ++ L₁ = L₂`.
List.IsInfix definition
(l₁ : List α) (l₂ : List α) : Prop
Full source
/--
The first list is a contiguous sub-list of the second list. Typically written with the `<:+:`
operator.

In other words, `l₁ <:+: l₂` means that there exist lists `s : List α` and `t : List α` such that
`l₂` has the form `s ++ l₁ ++ t`.
-/
def IsInfix (l₁ : List α) (l₂ : List α) : Prop := Exists fun s => Exists fun t => s ++ l₁ ++ t = l₂
Infix relation between lists
Informal description
A list `l₁` is an infix of another list `l₂` if there exist lists `s` and `t` such that `l₂` can be expressed as the concatenation `s ++ l₁ ++ t`. In other words, `l₁` appears as a contiguous subsequence within `l₂`.
List.term_<:+:_ definition
: Lean.TrailingParserDescr✝
Full source
@[inherit_doc] infixl:50 " <:+: " => IsInfix
Infix relation between lists
Informal description
The infix notation `L₁ <:+: L₂` is defined as `List.IsInfix L₁ L₂`, which represents the proposition that list `L₁` is an infix of list `L₂`, meaning `L₁` appears as a contiguous subsequence within `L₂`.
List.splitAt definition
(n : Nat) (l : List α) : List α × List α
Full source
/--
Splits a list at an index, resulting in the first `n` elements of `l` paired with the remaining
elements.

If `n` is greater than the length of `l`, then the resulting pair consists of `l` and the empty
list. `List.splitAt` is equivalent to a combination of `List.take` and `List.drop`, but it is more
efficient.

Examples:
* `["red", "green", "blue"].splitAt 2 = (["red", "green"], ["blue"])`
* `["red", "green", "blue"].splitAt 3 = (["red", "green", "blue], [])`
* `["red", "green", "blue"].splitAt 4 = (["red", "green", "blue], [])`
-/
def splitAt (n : Nat) (l : List α) : ListList α × List α := go l n [] where
  /--
  Auxiliary for `splitAt`:
  `splitAt.go l xs n acc = (acc.reverse ++ take n xs, drop n xs)` if `n < xs.length`,
  and `(l, [])` otherwise.
  -/
  go : List α → NatList α → ListList α × List α
  | [], _, _ => (l, []) -- This branch ensures the pointer equality of the result with the input
                        -- without any runtime branching cost.
  | x :: xs, n+1, acc => go xs n (x :: acc)
  | xs, _, acc => (acc.reverse, xs)
Split list at index \( n \)
Informal description
Given a natural number \( n \) and a list \( l \), the function `List.splitAt` splits \( l \) into a pair of lists \((l_1, l_2)\), where \( l_1 \) consists of the first \( n \) elements of \( l \), and \( l_2 \) contains the remaining elements. If \( n \) exceeds the length of \( l \), then \( l_1 \) is the entire list \( l \) and \( l_2 \) is the empty list. This operation is more efficient than separately applying `List.take` and `List.drop`.
List.rotateLeft_nil theorem
: ([] : List α).rotateLeft n = []
Full source
@[simp] theorem rotateLeft_nil : ([] : List α).rotateLeft n = [] := rfl
Left rotation of empty list is empty
Informal description
For any natural number $n$, rotating the empty list `[]` left by $n$ positions results in the empty list `[]$.
List.rotateRight_nil theorem
: ([] : List α).rotateRight n = []
Full source
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
Right Rotation of Empty List is Empty
Informal description
For any natural number $n$, rotating an empty list to the right by $n$ positions results in the empty list.
List.Pairwise inductive
: List α → Prop
Full source
/--
Each element of a list is related to all later elements of the list by `R`.

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

For example, `Pairwise (· ≠ ·) l` asserts that `l` has no duplicates, and if `Pairwise (· < ·) l`
asserts that `l` is (strictly) sorted.

Examples:
 * `Pairwise (· < ·) [1, 2, 3] ↔ (1 < 2 ∧ 1 < 3) ∧ 2 < 3`
 * `Pairwise (· = ·) [1, 2, 3] = False`
 * `Pairwise (· ≠ ·) [1, 2, 3] = True`
-/
inductive Pairwise : List α → Prop
  /-- All elements of the empty list are vacuously pairwise related. -/
  | nil : Pairwise []
  /--
  A nonempty list is pairwise related with `R` if the head is related to every element of the tail
  and the tail is itself pairwise related.

  That is, `a :: l` is `Pairwise R` if:
   * `R` relates `a` to every element of `l`
   * `l` is `Pairwise R`.
  -/
  | cons : ∀ {a : α} {l : List α}, (∀ a', a' ∈ l → R a a') → Pairwise l → Pairwise (a :: l)
Pairwise relation on a list
Informal description
The inductive predicate `Pairwise R l` states that for every pair of elements in the list `l`, where one element appears before the other, the relation `R` holds between them. More precisely, for a list `l = [a₁, a₂, ..., aₙ]`, `Pairwise R l` means that for all `i < j`, the relation `R aᵢ aⱼ` holds. For example: - `Pairwise (· < ·) [1, 2, 3]` means `1 < 2 ∧ 1 < 3 ∧ 2 < 3`. - `Pairwise (· ≠ ·) [1, 2, 3]` asserts that all elements are distinct. - `Pairwise (· = ·) [1, 2, 3]` is false since no two distinct elements are equal.
List.pairwise_cons theorem
: Pairwise R (a :: l) ↔ (∀ a', a' ∈ l → R a a') ∧ Pairwise R l
Full source
@[simp] theorem pairwise_cons : PairwisePairwise R (a::l) ↔ (∀ a', a' ∈ l → R a a') ∧ Pairwise R l :=
  ⟨fun | .cons h₁ h₂ => ⟨h₁, h₂⟩, fun ⟨h₁, h₂⟩ => h₂.cons h₁⟩
Pairwise Relation on Cons List: $R$-Pairwise of $a :: l$ iff $R$ Holds Between $a$ and All Elements in $l$ and $l$ is $R$-Pairwise
Informal description
For any relation $R$ and list $l$ with head $a$ and tail $l$, the list $a :: l$ satisfies the pairwise relation $R$ if and only if $R$ holds between $a$ and every element $a'$ in $l$, and the tail $l$ itself satisfies the pairwise relation $R$. In other words, the pairwise relation holds for the list $a :: l$ if and only if: 1. For every element $a'$ in $l$, the relation $R(a, a')$ holds, and 2. The list $l$ satisfies the pairwise relation $R$.
List.instDecidablePairwise instance
[DecidableRel R] : (l : List α) → Decidable (Pairwise R l)
Full source
instance instDecidablePairwise [DecidableRel R] :
    (l : List α) → Decidable (Pairwise R l)
  | [] => isTrue .nil
  | hd :: tl =>
    match instDecidablePairwise tl with
    | isTrue ht =>
      match decidableBAll (R hd) tl with
      | isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
      | isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
    | isFalse hf => isFalse fun | .cons _ ih => hf ih
Decidability of Pairwise Relations on Lists
Informal description
For any type $\alpha$ and decidable binary relation $R$ on $\alpha$, the property `Pairwise R l` is decidable for any list $l$ of elements of type $\alpha$. That is, given a list $l$, one can algorithmically determine whether for every pair of elements $(x, y)$ in $l$ where $x$ appears before $y$, the relation $R(x, y)$ holds.
List.Nodup definition
: List α → Prop
Full source
/--
The list has no duplicates: it contains every element at most once.

It is defined as `Pairwise (· ≠ ·)`: each element is unequal to all other elements.
-/
def Nodup : List α → Prop := Pairwise (· ≠ ·)
List with no duplicates
Informal description
A list `l` of elements of type `α` is said to have no duplicates (denoted `Nodup l`) if every pair of distinct elements in `l` are unequal, i.e., for any two elements `x` and `y` in `l`, if `x` appears before `y` in `l`, then `x ≠ y`.
List.nodupDecidable instance
[DecidableEq α] : ∀ l : List α, Decidable (Nodup l)
Full source
instance nodupDecidable [DecidableEq α] : ∀ l : List α, Decidable (Nodup l) :=
  instDecidablePairwise
Decidability of List Distinctness
Informal description
For any type $\alpha$ with decidable equality, the property of a list $l$ of elements of $\alpha$ having no duplicate elements is decidable. That is, given a list $l$, one can algorithmically determine whether all elements in $l$ are distinct.
List.replace definition
[BEq α] : (l : List α) → (a : α) → (b : α) → List α
Full source
/--
Replaces the first element of the list `l` that is equal to `a` with `b`. If no element is equal to
`a`, then the list is returned unchanged.

`O(|l|)`.

Examples:
* `[1, 4, 2, 3, 3, 7].replace 3 6 = [1, 4, 2, 6, 3, 7]`
* `[1, 4, 2, 3, 3, 7].replace 5 6 = [1, 4, 2, 3, 3, 7]`
-/
def replace [BEq α] : (l : List α) → (a : α) → (b : α) → List α
  | [],    _, _ => []
  | a::as, b, c => match b == a with
    | true  => c::as
    | false => a :: replace as b c
Replace first occurrence in a list
Informal description
Given a list $l$ of elements of type $\alpha$ with a boolean equality relation `==`, and elements $a, b : \alpha$, the function `List.replace l a b` returns a new list where the first occurrence of $a$ in $l$ (if any) is replaced by $b$. If $a$ does not appear in $l$, the original list is returned unchanged. The operation has time complexity $O(|l|)$. **Examples:** - $[1, 4, 2, 3, 3, 7].\text{replace}\ 3\ 6 = [1, 4, 2, 6, 3, 7]$ - $[1, 4, 2, 3, 3, 7].\text{replace}\ 5\ 6 = [1, 4, 2, 3, 3, 7]$
List.replace_nil theorem
[BEq α] : ([] : List α).replace a b = []
Full source
@[simp] theorem replace_nil [BEq α] : ([] : List α).replace a b = [] := rfl
Empty List Replacement Yields Empty List
Informal description
For any type $\alpha$ with a boolean equality relation and any elements $a, b \in \alpha$, replacing $a$ with $b$ in the empty list yields the empty list, i.e., $\text{replace}([], a, b) = []$.
List.replace_cons theorem
[BEq α] {a : α} : (a :: as).replace b c = match b == a with | true => c :: as | false => a :: replace as b c
Full source
theorem replace_cons [BEq α] {a : α} :
    (a::as).replace b c = match b == a with | true => c::as | false => a :: replace as b c :=
  rfl
Recursive Definition of List Replacement on Cons Cell
Informal description
Given a list of the form $a :: \text{as}$ (where $a$ is the head and $\text{as}$ is the tail), and elements $b, c$ of type $\alpha$ with a boolean equality relation `==`, the result of replacing the first occurrence of $b$ in the list with $c$ is: - If $b == a$ evaluates to `true`, then the result is $c :: \text{as}$. - Otherwise, the result is $a :: \text{replace as b c}$.
List.modifyTailIdx definition
(l : List α) (i : Nat) (f : List α → List α) : List α
Full source
/--
Replaces the `n`th tail of `l` with the result of applying `f` to it. Returns the input without
using `f` if the index is larger than the length of the List.

Examples:
```lean example
["circle", "square", "triangle"].modifyTailIdx 1 List.reverse
```
```output
["circle", "triangle", "square"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx 1 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle", "square", "triangle"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx 2 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle", "triangle"]
```
```lean example
["circle", "square", "triangle"].modifyTailIdx 5 (fun xs => xs ++ xs)
```
```output
["circle", "square", "triangle"]
```
-/
def modifyTailIdx (l : List α) (i : Nat) (f : List α → List α) : List α :=
  go i l
where
  go : NatList α → List α
  | 0, l => f l
  | _+1, [] => []
  | i+1, a :: l => a :: go i l
Modify tail sublist at index
Informal description
Given a list `l` of elements of type `α`, a natural number `i`, and a function `f` that transforms lists of type `α`, the function `modifyTailIdx` replaces the sublist starting at the `i`-th position (counting from 0) with the result of applying `f` to that sublist. If `i` is greater than or equal to the length of `l`, the original list `l` is returned unchanged. More formally, for a list `l = [a₀, a₁, ..., aₙ₋₁]` and index `i`, the function: - If `i = 0`, returns `f(l)` - If `0 < i ≤ n-1`, returns `[a₀, ..., a_{i-1}] ++ f([a_i, ..., a_{n-1}])` - If `i ≥ n`, returns `l` unchanged
List.modifyTailIdx_zero theorem
{l : List α} : l.modifyTailIdx 0 f = f l
Full source
@[simp] theorem modifyTailIdx_zero {l : List α} : l.modifyTailIdx 0 f = f l := rfl
Modification of Entire List at Index Zero
Informal description
For any list $l$ of elements of type $\alpha$ and any function $f$ that transforms lists of type $\alpha$, modifying the tail sublist starting at index $0$ results in applying $f$ to the entire list $l$. That is, $\text{modifyTailIdx}(l, 0, f) = f(l)$.
List.modifyTailIdx_succ_nil theorem
{i : Nat} : ([] : List α).modifyTailIdx (i + 1) f = []
Full source
@[simp] theorem modifyTailIdx_succ_nil {i : Nat} : ([] : List α).modifyTailIdx (i + 1) f = [] := rfl
Empty List Invariance Under `modifyTailIdx` at Successor Index
Informal description
For any natural number $i$ and any function $f$ on lists of type $\alpha$, applying the `modifyTailIdx` operation to the empty list `[]` with index $i+1$ returns the empty list, i.e., $\text{modifyTailIdx} \, [] \, (i+1) \, f = []$.
List.modifyTailIdx_succ_cons theorem
{i : Nat} {a : α} {l : List α} : (a :: l).modifyTailIdx (i + 1) f = a :: l.modifyTailIdx i f
Full source
@[simp] theorem modifyTailIdx_succ_cons {i : Nat} {a : α} {l : List α} :
    (a :: l).modifyTailIdx (i + 1) f = a :: l.modifyTailIdx i f := rfl
Recursive Modification of Tail Sublist in Cons Case
Informal description
For any natural number $i$, element $a$ of type $\alpha$, and list $l$ of type $\alpha$, modifying the tail sublist starting at index $i+1$ of the list $a :: l$ with function $f$ is equal to prepending $a$ to the result of modifying the tail sublist starting at index $i$ of $l$ with $f$. That is: $$(a :: l).\text{modifyTailIdx}\ (i + 1)\ f = a :: (l.\text{modifyTailIdx}\ i\ f)$$
List.modifyHead definition
(f : α → α) : List α → List α
Full source
/--
Replace the head of the list with the result of applying `f` to it. Returns the empty list if the
list is empty.

Examples:
 * `[1, 2, 3].modifyHead (· * 10) = [10, 2, 3]`
 * `[].modifyHead (· * 10) = []`
-/
@[inline] def modifyHead (f : α → α) : List α → List α
  | [] => []
  | a :: l => f a :: l
Modify the head of a list
Informal description
Given a function \( f : \alpha \to \alpha \) and a list \( L : \text{List } \alpha \), the operation modifies the first element of \( L \) by applying \( f \) to it. If \( L \) is empty, the result is the empty list. More precisely: - If \( L = [] \), then \( \text{modifyHead } f \, L = [] \). - If \( L = a :: l \), then \( \text{modifyHead } f \, L = f a :: l \).
List.modifyHead_nil theorem
{f : α → α} : [].modifyHead f = []
Full source
@[simp] theorem modifyHead_nil {f : α → α} : [].modifyHead f = [] := by rw [modifyHead]
Modify Head Preserves Empty List
Informal description
For any function $f : \alpha \to \alpha$, applying the `modifyHead` operation to the empty list $[]$ yields the empty list, i.e., $[].\text{modifyHead}\ f = []$.
List.modifyHead_cons theorem
{a : α} {l : List α} {f : α → α} : (a :: l).modifyHead f = f a :: l
Full source
@[simp] theorem modifyHead_cons {a : α} {l : List α} {f : α → α} :
    (a :: l).modifyHead f = f a :: l := by rw [modifyHead]
Modification of Head Element in Non-Empty List
Informal description
For any element $a$ of type $\alpha$, any list $l$ of type $\text{List } \alpha$, and any function $f : \alpha \to \alpha$, modifying the head of the list $(a :: l)$ with $f$ results in the list $(f a :: l)$. In other words, $\text{modifyHead } f (a :: l) = f a :: l$.
List.modify definition
(l : List α) (i : Nat) (f : α → α) : List α
Full source
/--
Replaces the element at the given index, if it exists, with the result of applying `f` to it. If the
index is invalid, the list is returned unmodified.

Examples:
 * `[1, 2, 3].modify 0 (· * 10) = [10, 2, 3]`
 * `[1, 2, 3].modify 2 (· * 10) = [1, 2, 30]`
 * `[1, 2, 3].modify 3 (· * 10) = [1, 2, 3]`
-/
def modify (l : List α) (i : Nat) (f : α → α) : List α :=
  l.modifyTailIdx i (modifyHead f)
Modify element at index in list
Informal description
Given a list \( L \) of elements of type \( \alpha \), a natural number index \( i \), and a function \( f : \alpha \to \alpha \), the operation `modify` replaces the element at position \( i \) in \( L \) (if it exists) with \( f \) applied to that element. If \( i \) is out of bounds (i.e., \( i \geq \text{length } L \)), the list remains unchanged. More formally: - If \( L = [a_0, a_1, \ldots, a_{n-1}] \) and \( 0 \leq i < n \), then \[ \text{modify } L \, i \, f = [a_0, \ldots, a_{i-1}, f a_i, a_{i+1}, \ldots, a_{n-1}]. \] - If \( i \geq n \), then \( \text{modify } L \, i \, f = L \).
List.insert definition
[BEq α] (a : α) (l : List α) : List α
Full source
/--
Inserts an element into a list without duplication.

If the element is present in the list, the list is returned unmodified. Otherwise, the new element
is inserted at the head of the list.

Examples:
 * `[1, 2, 3].insert 0 = [0, 1, 2, 3]`
 * `[1, 2, 3].insert 4 = [4, 1, 2, 3]`
 * `[1, 2, 3].insert 2 = [1, 2, 3]`
-/
@[inline] protected def insert [BEq α] (a : α) (l : List α) : List α :=
  if l.elem a then l else a :: l
Insertion into list without duplication
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, the function `List.insert` takes an element $a$ of type $\alpha$ and a list $l$ of type $\text{List}\ \alpha$, and returns a new list where $a$ is inserted at the head of $l$ if $a$ is not already present in $l$ (as determined by the `==` relation). If $a$ is already present in $l$, the list $l$ is returned unchanged. More formally: - If $a \in l$ (using `==`), then $\text{insert}\ a\ l = l$. - Otherwise, $\text{insert}\ a\ l = a :: l$.
List.insertIdx definition
(xs : List α) (i : Nat) (a : α) : List α
Full source
/--
Inserts an element into a list at the specified index. If the index is greater than the length of
the list, then the list is returned unmodified.

In other words, the new element is inserted into the list `l` after the first `i` elements of `l`.

Examples:
 * `["tues", "thur", "sat"].insertIdx 1 "wed" = ["tues", "wed", "thur", "sat"]`
 * `["tues", "thur", "sat"].insertIdx 2 "wed" = ["tues", "thur", "wed", "sat"]`
 * `["tues", "thur", "sat"].insertIdx 3 "wed" = ["tues", "thur", "sat", "wed"]`
 * `["tues", "thur", "sat"].insertIdx 4 "wed" = ["tues", "thur", "sat"]`
-/
def insertIdx (xs : List α) (i : Nat) (a : α) : List α :=
  xs.modifyTailIdx i (cons a)
Insert element at index in list
Informal description
Given a list \( L \) of elements of type \( \alpha \), a natural number index \( i \), and an element \( a \) of type \( \alpha \), the function `insertIdx` inserts \( a \) into \( L \) at position \( i \). If \( i \) is greater than or equal to the length of \( L \), the list remains unchanged. More formally: - If \( L = [x_0, x_1, \ldots, x_{n-1}] \) and \( 0 \leq i \leq n \), then \[ \text{insertIdx } L \, i \, a = [x_0, \ldots, x_{i-1}, a, x_i, \ldots, x_{n-1}]. \] - If \( i > n \), then \( \text{insertIdx } L \, i \, a = L \).
List.erase definition
{α} [BEq α] : List α → α → List α
Full source
/--
Removes the first occurrence of `a` from `l`. If `a` does not occur in `l`, the list is returned
unmodified.

`O(|l|)`.

Examples:
* `[1, 5, 3, 2, 5].erase 5 = [1, 3, 2, 5]`
* `[1, 5, 3, 2, 5].erase 6 = [1, 5, 3, 2, 5]`
-/
protected def erase {α} [BEq α] : List α → α → List α
  | [],    _ => []
  | a::as, b => match a == b with
    | true  => as
    | false => a :: List.erase as b
Removal of first occurrence in a list
Informal description
Given a list $l$ of elements of type $\alpha$ with a boolean equality relation `[BEq α]`, and an element $a$ of type $\alpha$, the function `List.erase` removes the first occurrence of $a$ from $l$. If $a$ does not appear in $l$, the list remains unchanged. The operation has time complexity $O(|l|)$. Examples: - $\text{erase}~[1, 5, 3, 2, 5]~5 = [1, 3, 2, 5]$ - $\text{erase}~[1, 5, 3, 2, 5]~6 = [1, 5, 3, 2, 5]$
List.erase_nil theorem
[BEq α] (a : α) : [].erase a = []
Full source
@[simp] theorem erase_nil [BEq α] (a : α) : [].erase a = [] := rfl
Empty List Erasure Identity
Informal description
For any type $\alpha$ with a boolean equality relation and any element $a \in \alpha$, removing $a$ from the empty list results in the empty list, i.e., $\text{erase}~[]~a = []$.
List.erase_cons theorem
[BEq α] {a b : α} {l : List α} : (b :: l).erase a = if b == a then l else b :: l.erase a
Full source
theorem erase_cons [BEq α] {a b : α} {l : List α} :
    (b :: l).erase a = if b == a then l else b :: l.erase a := by
  simp only [List.erase]; split <;> simp_all
Recursive Definition of List Erasure: $\text{erase}~(b :: l)~a = \text{if } b == a \text{ then } l \text{ else } b :: \text{erase}~l~a$
Informal description
For any type $\alpha$ with a boolean equality relation `[BEq α]`, and elements $a, b \in \alpha$ and a list $l$ of elements of type $\alpha$, the result of removing the first occurrence of $a$ from the list $b :: l$ is equal to $l$ if $b$ is equal to $a$ (i.e., $b == a$ is true), and otherwise is equal to $b$ followed by the result of removing the first occurrence of $a$ from $l$. In mathematical notation: $$\text{erase}~(b :: l)~a = \begin{cases} l & \text{if } b == a \\ b :: \text{erase}~l~a & \text{otherwise} \end{cases}$$
List.eraseP definition
(p : α → Bool) : List α → List α
Full source
/--
Removes the first element of a list for which `p` returns `true`. If no element satisfies `p`, then
the list is returned unchanged.

Examples:
  * `[2, 1, 2, 1, 3, 4].eraseP (· < 2) = [2, 2, 1, 3, 4]`
  * `[2, 1, 2, 1, 3, 4].eraseP (· > 2) = [2, 1, 2, 1, 4]`
  * `[2, 1, 2, 1, 3, 4].eraseP (· > 8) = [2, 1, 2, 1, 3, 4]`
-/
def eraseP (p : α → Bool) : List α → List α
  | [] => []
  | a :: l => bif p a then l else a :: eraseP p l
Remove first element satisfying a predicate
Informal description
Given a predicate `p : α → Bool` and a list `L : List α`, the function `List.eraseP p L` returns a new list obtained by removing the first element of `L` for which `p` returns `true`. If no such element exists, the original list `L` is returned unchanged. **Examples:** - `[2, 1, 2, 1, 3, 4].eraseP (· < 2) = [2, 2, 1, 3, 4]` - `[2, 1, 2, 1, 3, 4].eraseP (· > 2) = [2, 1, 2, 1, 4]` - `[2, 1, 2, 1, 3, 4].eraseP (· > 8) = [2, 1, 2, 1, 3, 4]`
List.eraseIdx definition
: (l : List α) → (i : Nat) → List α
Full source
/--
Removes the element at the specified index. If the index is out of bounds, the list is returned
unmodified.

`O(i)`.

Examples:
* `[0, 1, 2, 3, 4].eraseIdx 0 = [1, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdx 1 = [0, 2, 3, 4]`
* `[0, 1, 2, 3, 4].eraseIdx 5 = [0, 1, 2, 3, 4]`
-/
def eraseIdx : (l : List α) → (i : Nat) → List α
  | [],    _   => []
  | _::as, 0   => as
  | a::as, n+1 => a :: eraseIdx as n
Remove element at index from list
Informal description
Given a list $L$ of elements of type $\alpha$ and a natural number index $i$, the function `eraseIdx` removes the element at position $i$ in $L$ (0-based indexing). If $i$ is out of bounds (i.e., $i \geq \text{length}(L)$), the list remains unchanged. The operation has time complexity $O(i)$. **Examples:** - $\text{eraseIdx}~[0, 1, 2, 3, 4]~0 = [1, 2, 3, 4]$ - $\text{eraseIdx}~[0, 1, 2, 3, 4]~1 = [0, 2, 3, 4]$ - $\text{eraseIdx}~[0, 1, 2, 3, 4]~5 = [0, 1, 2, 3, 4]$
List.eraseIdx_nil theorem
: ([] : List α).eraseIdx i = []
Full source
@[simp] theorem eraseIdx_nil : ([] : List α).eraseIdx i = [] := rfl
Empty List Invariance under `eraseIdx`
Informal description
For any natural number index $i$, applying the `eraseIdx` operation to the empty list `[]` of type `List α` results in the empty list `[]`.
List.eraseIdx_cons_zero theorem
: (a :: as).eraseIdx 0 = as
Full source
@[simp] theorem eraseIdx_cons_zero : (a::as).eraseIdx 0 = as := rfl
Removing first element from a list: $(a :: as).\text{eraseIdx}~0 = as$
Informal description
For any element $a$ of type $\alpha$ and any list $as$ of elements of type $\alpha$, removing the element at index $0$ from the list $a :: as$ results in the list $as$.
List.eraseIdx_cons_succ theorem
: (a :: as).eraseIdx (i + 1) = a :: as.eraseIdx i
Full source
@[simp] theorem eraseIdx_cons_succ : (a::as).eraseIdx (i+1) = a :: as.eraseIdx i := rfl
Recursive Removal of Element at Successor Index in List
Informal description
For any element $a$ of type $\alpha$, any list $as$ of elements of type $\alpha$, and any natural number index $i$, removing the element at index $i+1$ from the list $a :: as$ is equal to the list obtained by prepending $a$ to the result of removing the element at index $i$ from $as$. In other words, $(a :: as).\text{eraseIdx}~(i + 1) = a :: (as.\text{eraseIdx}~i)$.
List.find? definition
(p : α → Bool) : List α → Option α
Full source
/--
Returns the first element of the list for which the predicate `p` returns `true`, or `none` if no
such element is found.

`O(|l|)`.

Examples:
* `[7, 6, 5, 8, 1, 2, 6].find? (· < 5) = some 1`
* `[7, 6, 5, 8, 1, 2, 6].find? (· < 1) = none`
-/
def find? (p : α → Bool) : List α → Option α
  | []    => none
  | a::as => match p a with
    | true  => some a
    | false => find? p as
First element satisfying a predicate in a list
Informal description
Given a predicate \( p : \alpha \to \text{Bool} \) and a list \( L : \text{List } \alpha \), the function returns the first element \( x \) in \( L \) for which \( p(x) \) is true, wrapped in `some`. If no such element exists, it returns `none`.
List.find?_nil theorem
: ([] : List α).find? p = none
Full source
@[simp] theorem find?_nil : ([] : List α).find? p = none := rfl
Empty List Yields No Result in `find?`
Informal description
For any predicate $p : \alpha \to \text{Bool}$, the function `find?` applied to the empty list `[]` returns `none`.
List.find?_cons theorem
: (a :: as).find? p = match p a with | true => some a | false => as.find? p
Full source
theorem find?_cons : (a::as).find? p = match p a with | true => some a | false => as.find? p :=
  rfl
Recursive Definition of List.find? on Cons
Informal description
For a list of the form $a :: \text{as}$ and a predicate $p : \alpha \to \text{Bool}$, the result of `find? p (a :: as)` is equal to `some a` if $p(a)$ is true, and otherwise equal to `find? p as`.
List.findSome? definition
(f : α → Option β) : List α → Option β
Full source
/--
Returns the first non-`none` result of applying `f` to each element of the list in order. Returns
`none` if `f` returns `none` for all elements of the list.

`O(|l|)`.

Examples:
 * `[7, 6, 5, 8, 1, 2, 6].findSome? (fun x => if x < 5 then some (10 * x) else none) = some 10`
 * `[7, 6, 5, 8, 1, 2, 6].findSome? (fun x => if x < 1 then some (10 * x) else none) = none`
-/
def findSome? (f : α → Option β) : List α → Option β
  | []    => none
  | a::as => match f a with
    | some b => some b
    | none   => findSome? f as
First non-none result in a list
Informal description
Given a function \( f : \alpha \to \text{Option } \beta \) and a list \( L : \text{List } \alpha \), the function `List.findSome?` returns the first non-`none` result of applying \( f \) to each element of \( L \) in order. If \( f \) returns `none` for all elements of \( L \), then the result is `none`. **Examples:** - For \( L = [7, 6, 5, 8, 1, 2, 6] \) and \( f(x) = \begin{cases} \text{some } (10x) & \text{if } x < 5 \\ \text{none} & \text{otherwise} \end{cases} \), the result is \(\text{some } 10\). - For \( L = [7, 6, 5, 8, 1, 2, 6] \) and \( f(x) = \begin{cases} \text{some } (10x) & \text{if } x < 1 \\ \text{none} & \text{otherwise} \end{cases} \), the result is \(\text{none}\).
List.findSome?_nil theorem
: ([] : List α).findSome? f = none
Full source
@[simp] theorem findSome?_nil : ([] : List α).findSome? f = none := rfl
Empty List Yields None in `findSome?`
Informal description
For any function $f : \alpha \to \text{Option } \beta$, applying `findSome?` to the empty list `[]` yields `none`, i.e., $\text{findSome? } f \ [] = \text{none}$.
List.findSome?_cons theorem
{f : α → Option β} : (a :: as).findSome? f = match f a with | some b => some b | none => as.findSome? f
Full source
theorem findSome?_cons {f : α → Option β} :
    (a::as).findSome? f = match f a with | some b => some b | none => as.findSome? f :=
  rfl
Recursive Definition of `findSome?` for Nonempty Lists
Informal description
For any function $f : \alpha \to \text{Option } \beta$ and any list $a :: as$ (consisting of head $a$ and tail $as$), the operation `findSome?` satisfies: \[ \text{findSome? } f (a :: as) = \begin{cases} \text{some } b & \text{if } f(a) = \text{some } b \\ \text{findSome? } f (as) & \text{if } f(a) = \text{none} \end{cases} \]
List.findIdx definition
(p : α → Bool) (l : List α) : Nat
Full source
/--
Returns the index of the first element for which `p` returns `true`, or the length of the list if
there is no such element.

Examples:
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = 4`
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = 7`
-/
@[inline] def findIdx (p : α → Bool) (l : List α) : Nat := go l 0 where
  /-- Auxiliary for `findIdx`: `findIdx.go p l n = findIdx p l + n` -/
  @[specialize] go : List α → NatNat
  | [], n => n
  | a :: l, n => bif p a then n else go l (n + 1)
Index of first element satisfying a predicate
Informal description
Given a predicate \( p : \alpha \to \text{Bool} \) and a list \( l : \text{List } \alpha \), the function returns the smallest index \( n \) such that \( p \) evaluates to true on the \( n \)-th element of \( l \), or the length of \( l \) if no such element exists.
List.findIdx_nil theorem
{p : α → Bool} : [].findIdx p = 0
Full source
@[simp] theorem findIdx_nil {p : α → Bool} : [].findIdx p = 0 := rfl
Index of First Satisfying Element in Empty List is Zero
Informal description
For any predicate $p : \alpha \to \text{Bool}$, the index of the first element in the empty list $[]$ satisfying $p$ is $0$.
List.idxOf definition
[BEq α] (a : α) : List α → Nat
Full source
/--
Returns the index of the first element equal to `a`, or the length of the list if no element is
equal to `a`.

Examples:
 * `["carrot", "potato", "broccoli"].idxOf "carrot" = 0`
 * `["carrot", "potato", "broccoli"].idxOf "broccoli" = 2`
 * `["carrot", "potato", "broccoli"].idxOf "tomato" = 3`
 * `["carrot", "potato", "broccoli"].idxOf "anything else" = 3`
-/
def idxOf [BEq α] (a : α) : List α → Nat := findIdx (· == a)
Index of first occurrence in a list
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, and an element $a : \alpha$, the function `List.idxOf` takes a list $L : \text{List } \alpha$ and returns the smallest index $n$ such that the $n$-th element of $L$ is equal to $a$ (i.e., `==` returns `true`). If no such element exists, it returns the length of $L$. Examples: - $\text{idxOf } \text{"carrot"} \text{ ["carrot", "potato", "broccoli"]} = 0$ - $\text{idxOf } \text{"broccoli"} \text{ ["carrot", "potato", "broccoli"]} = 2$ - $\text{idxOf } \text{"tomato"} \text{ ["carrot", "potato", "broccoli"]} = 3$
List.indexOf abbrev
Full source
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
@[deprecated idxOf (since := "2025-01-29")] abbrev indexOf := @idxOf
Index of First Occurrence in a List
Informal description
Given a type $\alpha$ with a boolean equality relation `==`, and an element $a : \alpha$, the function $\text{indexOf}$ takes a list $L : \text{List } \alpha$ and returns the smallest index $n$ such that the $n$-th element of $L$ is equal to $a$ (i.e., `==` returns `true`). If no such element exists, it returns the length of $L$. Examples: - $\text{indexOf } \text{"carrot"} \text{ ["carrot", "potato", "broccoli"]} = 0$ - $\text{indexOf } \text{"broccoli"} \text{ ["carrot", "potato", "broccoli"]} = 2$ - $\text{indexOf } \text{"tomato"} \text{ ["carrot", "potato", "broccoli"]} = 3$
List.idxOf_nil theorem
[BEq α] : ([] : List α).idxOf x = 0
Full source
@[simp] theorem idxOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
Index of element in empty list is zero
Informal description
For any type $\alpha$ with a boolean equality relation and any element $x : \alpha$, the index of $x$ in the empty list is $0$.
List.indexOf_nil theorem
[BEq α] : ([] : List α).idxOf x = 0
Full source
@[deprecated idxOf_nil (since := "2025-01-29")]
theorem indexOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
Index of Element in Empty List is Zero
Informal description
For any type $\alpha$ with a boolean equality relation and any element $x \in \alpha$, the index of $x$ in the empty list is $0$. That is, $\text{idxOf}(x, []) = 0$.
List.findIdx? definition
(p : α → Bool) (l : List α) : Option Nat
Full source
/--
Returns the index of the first element for which `p` returns `true`, or `none` if there is no such
element.

Examples:
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
-/
def findIdx? (p : α → Bool) (l : List α) : Option Nat :=
  go l 0
where
  go : List α → NatOption Nat
  | [], _ => none
  | a :: l, i => if p a then some i else go l (i + 1)
First index satisfying a predicate in a list
Informal description
Given a predicate `p : α → Bool` and a list `l : List α`, `List.findIdx? p l` returns the index of the first element in `l` for which `p` returns `true`, or `none` if no such element exists. The indices are zero-based.
List.idxOf? definition
[BEq α] (a : α) : List α → Option Nat
Full source
/--
Returns the index of the first element equal to `a`, or `none` if no element is equal to `a`.

Examples:
 * `["carrot", "potato", "broccoli"].idxOf? "carrot" = some 0`
 * `["carrot", "potato", "broccoli"].idxOf? "broccoli" = some 2`
 * `["carrot", "potato", "broccoli"].idxOf? "tomato" = none`
 * `["carrot", "potato", "broccoli"].idxOf? "anything else" = none`
-/
@[inline] def idxOf? [BEq α] (a : α) : List α → Option Nat := findIdx? (· == a)
First index of an element in a list
Informal description
Given a type $\alpha$ with a boolean equality relation `==` and an element $a$ of type $\alpha$, the function `List.idxOf? a l` returns the index of the first occurrence of $a$ in the list $l$ as an optional natural number (where indices start at 0), or `none` if $a$ does not appear in $l$. **Examples:** * `["carrot", "potato", "broccoli"].idxOf? "carrot" = some 0` * `["carrot", "potato", "broccoli"].idxOf? "broccoli" = some 2` * `["carrot", "potato", "broccoli"].idxOf? "tomato" = none`
List.indexOf? abbrev
Full source
/-- Return the index of the first occurrence of `a` in the list. -/
@[deprecated idxOf? (since := "2025-01-29")]
abbrev indexOf? := @idxOf?
First Index of an Element in a List
Informal description
Given a type $\alpha$ with a boolean equality relation `==` and an element $a$ of type $\alpha$, the function $\text{indexOf?}(a, L)$ returns the index of the first occurrence of $a$ in the list $L$ as an optional natural number (where indices start at 0), or `none` if $a$ does not appear in $L$. **Examples:** * $\text{indexOf?}(\text{"carrot"}, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}]) = \text{some } 0$ * $\text{indexOf?}(\text{"broccoli"}, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}]) = \text{some } 2$ * $\text{indexOf?}(\text{"tomato"}, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}]) = \text{none}$
List.findFinIdx? definition
(p : α → Bool) (l : List α) : Option (Fin l.length)
Full source
/--
Returns the index of the first element for which `p` returns `true`, or `none` if there is no such
element. The index is returned as a `Fin`, which guarantees that it is in bounds.

Examples:
* `[7, 6, 5, 8, 1, 2, 6].findFinIdx? (· < 5) = some (4 : Fin 7)`
* `[7, 6, 5, 8, 1, 2, 6].findFinIdx? (· < 1) = none`
-/
@[inline] def findFinIdx? (p : α → Bool) (l : List α) : Option (Fin l.length) :=
  go l 0 (by simp)
where
  go : (l' : List α) → (i : Nat) → (h : l'.length + i = l.length) → Option (Fin l.length)
  | [], _, _ => none
  | a :: l, i, h =>
    if p a then
      some ⟨i, by
        simp only [Nat.add_comm _ i, ← Nat.add_assoc] at h
        exact Nat.lt_of_add_right_lt (Nat.lt_of_succ_le (Nat.le_of_eq h))⟩
    else
      go l (i + 1) (by simp at h; simpa [← Nat.add_assoc, Nat.add_right_comm] using h)
First index satisfying a predicate (as bounded natural number)
Informal description
Given a Boolean predicate $p$ and a list $l$ of elements of type $\alpha$, the function $\text{findFinIdx?}$ returns an optional value containing the first index $i$ (as a $\text{Fin}$ type bounded by the length of $l$) where $p$ holds for the element at that index, or $\text{none}$ if no such element exists. **Examples:** * $\text{findFinIdx?} (\lambda x \Rightarrow x < 5) [7, 6, 5, 8, 1, 2, 6] = \text{some} (4 : \text{Fin} 7)$ * $\text{findFinIdx?} (\lambda x \Rightarrow x < 1) [7, 6, 5, 8, 1, 2, 6] = \text{none}$
List.finIdxOf? definition
[BEq α] (a : α) : (l : List α) → Option (Fin l.length)
Full source
/--
Returns the index of the first element equal to `a`, or the length of the list if no element is
equal to `a`. The index is returned as a `Fin`, which guarantees that it is in bounds.

Examples:
 * `["carrot", "potato", "broccoli"].finIdxOf? "carrot" = some 0`
 * `["carrot", "potato", "broccoli"].finIdxOf? "broccoli" = some 2`
 * `["carrot", "potato", "broccoli"].finIdxOf? "tomato" = none`
 * `["carrot", "potato", "broccoli"].finIdxOf? "anything else" = none`
-/
@[inline] def finIdxOf? [BEq α] (a : α) : (l : List α) → Option (Fin l.length) :=
  findFinIdx? (· == a)
First index of an element in a list (as bounded natural number)
Informal description
Given a list $L$ of elements of type $\alpha$ with a boolean equality relation `==`, and an element $a : \alpha$, the function $\text{finIdxOf?}$ returns an optional value containing the first index $i$ (as a $\text{Fin}$ type bounded by the length of $L$) where the element at that index is equal to $a$ according to the boolean equality, or $\text{none}$ if no such element exists. **Examples:** * $\text{finIdxOf?} \, \text{"carrot"} \, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}] = \text{some} \, 0$ * $\text{finIdxOf?} \, \text{"broccoli"} \, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}] = \text{some} \, 2$ * $\text{finIdxOf?} \, \text{"tomato"} \, [\text{"carrot"}, \text{"potato"}, \text{"broccoli"}] = \text{none}$
List.countP definition
(p : α → Bool) (l : List α) : Nat
Full source
/--
Counts the number of elements in the list `l` that satisfy the Boolean predicate `p`.

Examples:
 * `[1, 2, 3, 4, 5].countP (· % 2 == 0) = 2`
 * `[1, 2, 3, 4, 5].countP (· < 5) = 4`
 * `[1, 2, 3, 4, 5].countP (· > 5) = 0`
-/
@[inline] def countP (p : α → Bool) (l : List α) : Nat := go l 0 where
  /-- Auxiliary for `countP`: `countP.go p l acc = countP p l + acc`. -/
  @[specialize] go : List α → NatNat
  | [], acc => acc
  | x :: xs, acc => bif p x then go xs (acc + 1) else go xs acc
Count elements satisfying a predicate
Informal description
Given a Boolean predicate $p$ and a list $l$ of elements of type $\alpha$, the function $\text{countP}$ returns the number of elements $x$ in $l$ for which $p(x)$ is true. **Examples:** - $\text{countP} (\lambda x \Rightarrow x \bmod 2 = 0) [1, 2, 3, 4, 5] = 2$ - $\text{countP} (\lambda x \Rightarrow x < 5) [1, 2, 3, 4, 5] = 4$ - $\text{countP} (\lambda x \Rightarrow x > 5) [1, 2, 3, 4, 5] = 0$
List.count definition
[BEq α] (a : α) : List α → Nat
Full source
/--
Counts the number of times an element occurs in a list.

Examples:
 * `[1, 1, 2, 3, 5].count 1 = 2`
 * `[1, 1, 2, 3, 5].count 5 = 1`
 * `[1, 1, 2, 3, 5].count 4 = 0`
-/
@[inline] def count [BEq α] (a : α) : List α → Nat := countP (· == a)
Count occurrences of an element in a list
Informal description
Given a list $L$ of elements of type $\alpha$ with a boolean equality relation `==`, and an element $a : \alpha$, the function $\text{count}$ returns the number of elements in $L$ that are equal to $a$ according to the boolean equality. **Examples:** - $\text{count} \, 1 \, [1, 1, 2, 3, 5] = 2$ - $\text{count} \, 5 \, [1, 1, 2, 3, 5] = 1$ - $\text{count} \, 4 \, [1, 1, 2, 3, 5] = 0$
List.lookup definition
[BEq α] : α → List (α × β) → Option β
Full source
/--
Treats the list as an association list that maps keys to values, returning the first value whose key
is equal to the specified key.

`O(|l|)`.

Examples:
* `[(1, "one"), (3, "three"), (3, "other")].lookup 3 = some "three"`
* `[(1, "one"), (3, "three"), (3, "other")].lookup 2 = none`
-/
def lookup [BEq α] : α → List (α × β) → Option β
  | _, []           => none
  | a, (k, b)(k, b) :: as => match a == k with
    | true  => some b
    | false => lookup a as
Lookup in association list
Informal description
Given a key $a$ of type $\alpha$ and a list $L$ of key-value pairs $(k_i, b_i)$ where $k_i : \alpha$ and $b_i : \beta$, the function returns the first value $b_j$ such that $k_j = a$ (using the boolean equality `==`), wrapped in `some`. If no such pair exists, it returns `none`.
List.lookup_nil theorem
[BEq α] : ([] : List (α × β)).lookup a = none
Full source
@[simp] theorem lookup_nil [BEq α] : ([] : List (α × β)).lookup a = none := rfl
Lookup on Empty List Returns None
Informal description
For any type $\alpha$ with a boolean equality relation `==` and any type $\beta$, the lookup operation on the empty list of key-value pairs $(k_i, b_i)$ (where $k_i : \alpha$ and $b_i : \beta$) returns `none` for any key $a : \alpha$. In other words, if $L = []$ is the empty list, then $L.\text{lookup}(a) = \text{none}$.
List.lookup_cons theorem
[BEq α] {k : α} : ((k, b) :: as).lookup a = match a == k with | true => some b | false => as.lookup a
Full source
theorem lookup_cons [BEq α] {k : α} :
    ((k, b)(k, b)::as).lookup a = match a == k with | true => some b | false => as.lookup a :=
  rfl
Lookup Operation on Non-Empty List
Informal description
For any types $\alpha$ and $\beta$ with a boolean equality relation `==` on $\alpha$, given a key $a : \alpha$, a pair $(k, b) \in \alpha \times \beta$, and a list $as$ of key-value pairs, the lookup operation on the list $(k, b) :: as$ is defined as follows: \[ \text{lookup}(a, (k, b) :: as) = \begin{cases} \text{some } b & \text{if } a == k \\ \text{lookup}(a, as) & \text{otherwise.} \end{cases} \]
List.Perm inductive
: List α → List α → Prop
Full source
/--
Two lists are permutations of each other if they contain the same elements, each occurring the same
number of times but not necessarily in the same order.

One list can be proven to be a permutation of another by showing how to transform one into the other
by repeatedly swapping adjacent elements.

`List.isPerm` is a Boolean equivalent of this relation.
-/
inductive Perm : List α → List α → Prop
  /-- The empty list is a permutation of the empty list: `[] ~ []`. -/
  | nil : Perm [] []
  /--
  If one list is a permutation of the other, adding the same element as the head of each yields
  lists that are permutations of each other: `l₁ ~ l₂ → x::l₁ ~ x::l₂`.
  -/
  | cons (x : α) {l₁ l₂ : List α} : Perm l₁ l₂ → Perm (x :: l₁) (x :: l₂)
  /--
  If two lists are identical except for having their first two elements swapped, then they are
  permutations of each other: `x::y::l ~ y::x::l`.
  -/
  | swap (x y : α) (l : List α) : Perm (y :: x :: l) (x :: y :: l)
  /--
  Permutation is transitive: `l₁ ~ l₂ → l₂ ~ l₃ → l₁ ~ l₃`.
  -/
  | trans {l₁ l₂ l₃ : List α} : Perm l₁ l₂ → Perm l₂ l₃ → Perm l₁ l₃
Permutation of Lists
Informal description
The relation `List.Perm` between two lists of type `α` states that they are permutations of each other, meaning they contain the same elements with the same multiplicities, but possibly in different orders. This is an inductive relation that can be proven by showing how one list can be transformed into the other via a sequence of adjacent swaps.
List.term_~_ definition
: Lean.TrailingParserDescr✝
Full source
@[inherit_doc] scoped infixl:50 " ~ " => Perm
Permutation notation for lists
Informal description
The infix notation `~` is defined as an abbreviation for the `Perm` relation between two lists, representing that they are permutations of each other. The notation is left-associative with precedence level 50.
List.isPerm definition
[BEq α] : List α → List α → Bool
Full source
/--
Returns `true` if `l₁` and `l₂` are permutations of each other. `O(|l₁| * |l₂|)`.

The relation `List.Perm` is a logical characterization of permutations. When the `BEq α` instance
corresponds to `DecidableEq α`, `isPerm l₁ l₂ ↔ l₁ ~ l₂` (use the theorem `isPerm_iff`).
-/
def isPerm [BEq α] : List α → List α → Bool
  | [], l₂ => l₂.isEmpty
  | a :: l₁, l₂ => l₂.contains a && l₁.isPerm (l₂.erase a)
Permutation check for lists via boolean equality
Informal description
Given two lists `l₁` and `l₂` of elements of type `α` with a boolean equality relation `[BEq α]`, the function `List.isPerm` returns `true` if `l₁` and `l₂` are permutations of each other, i.e., if they contain the same elements with the same multiplicities but possibly in different orders. The time complexity is $O(|l₁| * |l₂|)$. When the `BEq α` instance corresponds to `DecidableEq α`, this function is equivalent to the logical permutation relation `List.Perm` (as stated by the theorem `isPerm_iff`).
List.any definition
: (l : List α) → (p : α → Bool) → Bool
Full source
/--
Returns `true` if `p` returns `true` for any element of `l`.

`O(|l|)`. Short-circuits upon encountering the first `true`.

Examples:
* `[2, 4, 6].any (· % 2 = 0) = true`
* `[2, 4, 6].any (· % 2 = 1) = false`
* `[2, 4, 5, 6].any (· % 2 = 0) = true`
* `[2, 4, 5, 6].any (· % 2 = 1) = true`
-/
def any : (l : List α) → (p : α → Bool) → Bool
  | [], _ => false
  | h :: t, p => p h || any t p
Existence of a satisfying element in a list (short-circuiting)
Informal description
Given a list `l` of elements of type `α` and a predicate `p : α → Bool`, the function `List.any` returns `true` if there exists at least one element `x` in `l` such that `p x` evaluates to `true`. The evaluation is short-circuiting: it stops as soon as a `true` result is found.
List.any_nil theorem
: [].any f = false
Full source
@[simp] theorem any_nil : [].any f = false := rfl
Empty List Satisfies No Predicate in `any`
Informal description
For any predicate $f : \alpha \to \text{Bool}$, the function `any` applied to the empty list `[]` returns `false`.
List.any_cons theorem
: (a :: l).any f = (f a || l.any f)
Full source
@[simp] theorem any_cons : (a::l).any f = (f a || l.any f) := rfl
Recursive definition of `List.any` for cons case: `(a :: l).any f = f a || l.any f`
Informal description
For any list `a :: l` (a head element `a` followed by tail list `l`) and any predicate `f : α → Bool`, the boolean expression `(a :: l).any f` is equal to the logical disjunction `f a || l.any f`.
List.all definition
: List α → (α → Bool) → Bool
Full source
/--
Returns `true` if `p` returns `true` for every element of `l`.

`O(|l|)`. Short-circuits upon encountering the first `false`.

Examples:
* `[a, b, c].all p = (p a && (p b && p c))`
* `[2, 4, 6].all (· % 2 = 0) = true`
* `[2, 4, 5, 6].all (· % 2 = 0) = false`
-/
def all : List α → (α → Bool) → Bool
  | [], _ => true
  | h :: t, p => p h && all t p
Universal quantification over a list (Boolean)
Informal description
The function `List.all` takes a list `l` of elements of type `α` and a predicate `p : α → Bool`, and returns `true` if `p` evaluates to `true` for every element in `l`. The evaluation is short-circuiting: it stops at the first element for which `p` returns `false`. **Examples:** - `[a, b, c].all p` is equivalent to `p a && (p b && p c)` - `[2, 4, 6].all (· % 2 = 0)` evaluates to `true` - `[2, 4, 5, 6].all (· % 2 = 0)` evaluates to `false`
List.all_nil theorem
: [].all f = true
Full source
@[simp] theorem all_nil : [].all f = true := rfl
Universal Quantification on Empty List is True
Informal description
For any predicate $f : \alpha \to \text{Bool}$, the evaluation of `all` on the empty list `[]` with predicate $f$ is `true`. That is, $\text{all}([], f) = \text{true}$.
List.all_cons theorem
: (a :: l).all f = (f a && l.all f)
Full source
@[simp] theorem all_cons : (a::l).all f = (f a && l.all f) := rfl
Recursive Definition of Universal Quantification over a List
Informal description
For any list `a :: l` (a head element `a` followed by a tail list `l`) and any predicate `f : α → Bool`, the result of applying `List.all` to the list with predicate `f` is equal to the logical AND of `f a` and the result of applying `List.all` to `l` with predicate `f`. In other words, $\text{all}(a :: l, f) = f(a) \land \text{all}(l, f)$.
List.or definition
(bs : List Bool) : Bool
Full source
/--
Returns `true` if `true` is an element of the list `bs`.

`O(|bs|)`. Short-circuits at the first `true` value.

* `[true, true, true].or = true`
* `[true, false, true].or = true`
* `[false, false, false].or = false`
* `[false, false, true].or = true`
* `[].or = false`
-/
def or (bs : List Bool) : Bool := bs.any id
Logical OR of a list of booleans
Informal description
Given a list of boolean values `bs`, the function returns `true` if at least one element in `bs` is `true`, and `false` otherwise. The evaluation is short-circuiting: it stops at the first `true` encountered. **Examples:** - `[true, true, true].or = true` - `[true, false, true].or = true` - `[false, false, false].or = false` - `[false, false, true].or = true` - `[].or = false`
List.or_nil theorem
: [].or = false
Full source
@[simp] theorem or_nil : [].or = false := rfl
Logical OR of Empty List is False
Informal description
The logical OR of an empty list of boolean values is `false`.
List.or_cons theorem
: (a :: l).or = (a || l.or)
Full source
@[simp] theorem or_cons : (a::l).or = (a || l.or) := rfl
Cons OR Property for Lists
Informal description
For any boolean value $a$ and list of booleans $l$, the logical OR of the list $a :: l$ is equal to the logical OR of $a$ with the logical OR of $l$. In other words, $(a :: l).\text{or} = a \lor l.\text{or}$.
List.and definition
(bs : List Bool) : Bool
Full source
/--
Returns `true` if every element of `bs` is the value `true`.

`O(|bs|)`. Short-circuits at the first `false` value.

* `[true, true, true].and = true`
* `[true, false, true].and = false`
* `[true, false, false].and = false`
* `[].and = true`
-/
def and (bs : List Bool) : Bool := bs.all id
Logical AND of a list of Booleans
Informal description
The function takes a list of Boolean values `bs` and returns `true` if every element in `bs` is `true`, and `false` otherwise. The evaluation is short-circuiting, meaning it stops at the first `false` encountered. **Examples:** - `[true, true, true].and` evaluates to `true` - `[true, false, true].and` evaluates to `false` - `[].and` evaluates to `true` (vacuously true for empty lists)
List.and_nil theorem
: [].and = true
Full source
@[simp] theorem and_nil : [].and = true := rfl
Empty List AND is True
Informal description
The logical AND of an empty list of Boolean values is `true`.
List.and_cons theorem
: (a :: l).and = (a && l.and)
Full source
@[simp] theorem and_cons : (a::l).and = (a && l.and) := rfl
Cons Rule for List AND: $(a :: l).\text{and} = a \land l.\text{and}$
Informal description
For any Boolean value $a$ and list of Booleans $l$, the logical AND of the list $a :: l$ is equal to the logical AND of $a$ with the logical AND of $l$, i.e., $(a :: l).\text{and} = (a \land l.\text{and})$.
List.zipWith definition
(f : α → β → γ) : (xs : List α) → (ys : List β) → List γ
Full source
/--
Applies a function to the corresponding elements of two lists, stopping at the end of the shorter
list.

`O(min |xs| |ys|)`.

Examples:
* `[1, 2].zipWith (· + ·) [5, 6] = [6, 8]`
* `[1, 2, 3].zipWith (· + ·) [5, 6, 10] = [6, 8, 13]`
* `[].zipWith (· + ·) [5, 6] = []`
* `[x₁, x₂, x₃].zipWith f [y₁, y₂, y₃, y₄] = [f x₁ y₁, f x₂ y₂, f x₃ y₃]`
-/
@[specialize] def zipWith (f : α → β → γ) : (xs : List α) → (ys : List β) → List γ
  | x::xs, y::ys => f x y :: zipWith f xs ys
  | _,     _     => []
Pairwise application of a function to two lists
Informal description
Given a function \( f : \alpha \to \beta \to \gamma \) and two lists \( xs : \text{List } \alpha \) and \( ys : \text{List } \beta \), the function `List.zipWith` applies \( f \) pairwise to the elements of \( xs \) and \( ys \), stopping when the shorter list is exhausted. The result is a new list of type \( \text{List } \gamma \). More formally, for lists \( xs = [x_1, x_2, \dots, x_n] \) and \( ys = [y_1, y_2, \dots, y_m] \), the output is: \[ \text{zipWith } f \, xs \, ys = [f \, x_1 \, y_1, f \, x_2 \, y_2, \dots, f \, x_k \, y_k], \] where \( k = \min(n, m) \). If either \( xs \) or \( ys \) is empty, the result is the empty list.
List.zipWith_nil_left theorem
{f : α → β → γ} : zipWith f [] l = []
Full source
@[simp] theorem zipWith_nil_left {f : α → β → γ} : zipWith f [] l = [] := rfl
Empty List Left Argument Yields Empty List in `zipWith`
Informal description
For any function $f : \alpha \to \beta \to \gamma$ and any list $l : \text{List } \beta$, applying `zipWith` with $f$ to the empty list `[]` and $l$ results in the empty list, i.e., $\text{zipWith } f \, [] \, l = []$.
List.zipWith_nil_right theorem
{f : α → β → γ} : zipWith f l [] = []
Full source
@[simp] theorem zipWith_nil_right {f : α → β → γ} : zipWith f l [] = [] := by simp [zipWith]
Empty List Right Identity for `zipWith`
Informal description
For any function $f : \alpha \to \beta \to \gamma$ and any list $l$ of type $\text{List } \alpha$, the pairwise application of $f$ to $l$ and the empty list results in the empty list, i.e., $\text{zipWith } f \, l \, [] = []$.
List.zipWith_cons_cons theorem
{f : α → β → γ} : zipWith f (a :: as) (b :: bs) = f a b :: zipWith f as bs
Full source
@[simp] theorem zipWith_cons_cons {f : α → β → γ} :
    zipWith f (a :: as) (b :: bs) = f a b :: zipWith f as bs := rfl
Cons Preservation in Pairwise Function Application
Informal description
For any function $f : \alpha \to \beta \to \gamma$ and any non-empty lists $a :: as$ (of type $\text{List } \alpha$) and $b :: bs$ (of type $\text{List } \beta$), the pairwise application of $f$ via `zipWith` satisfies: \[ \text{zipWith } f \, (a :: as) \, (b :: bs) = f \, a \, b :: \text{zipWith } f \, as \, bs. \]
List.zip definition
: List α → List β → List (Prod α β)
Full source
/--
Combines two lists into a list of pairs in which the first and second components are the
corresponding elements of each list. The resulting list is the length of the shorter of the input
lists.

`O(min |xs| |ys|)`.

Examples:
* `["Mon", "Tue", "Wed"].zip [1, 2, 3] = [("Mon", 1), ("Tue", 2), ("Wed", 3)]`
* `["Mon", "Tue", "Wed"].zip [1, 2] = [("Mon", 1), ("Tue", 2)]`
* `[x₁, x₂, x₃].zip [y₁, y₂, y₃, y₄] = [(x₁, y₁), (x₂, y₂), (x₃, y₃)]`
-/
def zip : List α → List β → List (Prod α β) :=
  zipWith Prod.mk
List zip operation (pairwise combination)
Informal description
Given two lists $xs : \text{List } \alpha$ and $ys : \text{List } \beta$, the function $\text{zip}$ combines them into a list of pairs $(x, y)$, where $x$ is from $xs$ and $y$ is from $ys$. The resulting list has length equal to the shorter of the two input lists. More formally, for lists $xs = [x_1, x_2, \dots, x_n]$ and $ys = [y_1, y_2, \dots, y_m]$, the output is: \[ \text{zip } xs \, ys = [(x_1, y_1), (x_2, y_2), \dots, (x_k, y_k)], \] where $k = \min(n, m)$. If either $xs$ or $ys$ is empty, the result is the empty list.
List.zip_nil_left theorem
: zip ([] : List α) (l : List β) = []
Full source
@[simp] theorem zip_nil_left : zip ([] : List α) (l : List β)  = [] := rfl
Empty List Left Identity for Zip Operation
Informal description
For any list $l$ of type $\text{List } \beta$, the zip operation with the empty list (of type $\text{List } \alpha$) results in the empty list, i.e., $\text{zip } [] \, l = []$.
List.zip_nil_right theorem
: zip (l : List α) ([] : List β) = []
Full source
@[simp] theorem zip_nil_right : zip (l : List α) ([] : List β)  = [] := by simp [zip, zipWith]
Empty List Right Identity for `zip`
Informal description
For any list $l$ of type $\text{List } \alpha$, the pairwise combination of $l$ with the empty list results in the empty list, i.e., $\text{zip } l \, [] = []$.
List.zip_cons_cons theorem
: zip (a :: as) (b :: bs) = (a, b) :: zip as bs
Full source
@[simp] theorem zip_cons_cons : zip (a :: as) (b :: bs) = (a, b)(a, b) :: zip as bs := rfl
Cons Preservation in List Zip Operation: $\text{zip}(a::as, b::bs) = (a,b) :: \text{zip}(as, bs)$
Informal description
For any elements $a \in \alpha$ and $b \in \beta$, and any lists $as \in \text{List } \alpha$ and $bs \in \text{List } \beta$, the zip operation on the cons-lists $(a :: as)$ and $(b :: bs)$ yields the cons-list $(a, b) :: \text{zip } as \, bs$. In other words, zipping two non-empty lists results in a new list where the first element is the pair of their heads, followed by the zip of their tails.
List.zipWithAll definition
(f : Option α → Option β → γ) : List α → List β → List γ
Full source
/--
Applies a function to the corresponding elements of both lists, stopping when there are no more
elements in either list. If one list is shorter than the other, the function is passed `none` for
the missing elements.

Examples:
* `[1, 6].zipWithAll min [5, 2] = [some 1, some 2]`
* `[1, 2, 3].zipWithAll Prod.mk [5, 6] = [(some 1, some 5), (some 2, some 6), (some 3, none)]`
* `[x₁, x₂].zipWithAll f [y] = [f (some x₁) (some y), f (some x₂) none]`
-/
def zipWithAll (f : Option α → Option β → γ) : List α → List β → List γ
  | [], bs => bs.map fun b => f none (some b)
  | a :: as, [] => (a :: as).map fun a => f (some a) none
  | a :: as, b :: bs => f a b :: zipWithAll f as bs
Generalized zip with padding
Informal description
The function `List.zipWithAll` takes a function $f : \text{Option}\ \alpha \to \text{Option}\ \beta \to \gamma$ and two lists $l_1 : \text{List}\ \alpha$ and $l_2 : \text{List}\ \beta$. It applies $f$ to corresponding elements of $l_1$ and $l_2$, padding with `none` when one list is shorter than the other. The result is a list of type $\text{List}\ \gamma$ where each element is obtained by applying $f$ to the corresponding elements (or `none` for missing elements) of the input lists. For example: - If $l_1 = [x_1, x_2]$ and $l_2 = [y_1]$, then `zipWithAll f l_1 l_2 = [f (\text{some}\ x_1) (\text{some}\ y_1), f (\text{some}\ x_2) \text{none}]$. - If $l_1 = []$ and $l_2 = [y_1, y_2]$, then `zipWithAll f l_1 l_2 = [f \text{none} (\text{some}\ y_1), f \text{none} (\text{some}\ y_2)]$.
List.zipWithAll_nil theorem
: zipWithAll f as [] = as.map fun a => f (some a) none
Full source
@[simp] theorem zipWithAll_nil :
    zipWithAll f as [] = as.map fun a => f (some a) none := by
  cases as <;> rfl
ZipWithAll with Empty List Yields Mapping of First List
Informal description
For any function $f : \text{Option}\ \alpha \to \text{Option}\ \beta \to \gamma$ and any list $as : \text{List}\ \alpha$, the operation $\text{zipWithAll}\ f\ as\ []$ is equal to mapping each element $a \in as$ to $f\ (\text{some}\ a)\ \text{none}$.
List.nil_zipWithAll theorem
: zipWithAll f [] bs = bs.map fun b => f none (some b)
Full source
@[simp] theorem nil_zipWithAll :
    zipWithAll f [] bs = bs.map fun b => f none (some b) := rfl
Empty List Behavior for Generalized Zip with Padding
Informal description
For any function $f : \text{Option}\ \alpha \to \text{Option}\ \beta \to \gamma$ and any list $bs$ of type $\text{List}\ \beta$, applying `zipWithAll` with $f$ to the empty list and $bs$ is equivalent to mapping each element $b$ of $bs$ to $f(\text{none}, \text{some}\ b)$.
List.zipWithAll_cons_cons theorem
: zipWithAll f (a :: as) (b :: bs) = f (some a) (some b) :: zipWithAll f as bs
Full source
@[simp] theorem zipWithAll_cons_cons :
    zipWithAll f (a :: as) (b :: bs) = f (some a) (some b) :: zipWithAll f as bs := rfl
Cons Preservation in Generalized Zip Operation
Informal description
For any function $f : \text{Option}\ \alpha \to \text{Option}\ \beta \to \gamma$, elements $a \in \alpha$, $b \in \beta$, and lists $as \in \text{List}\ \alpha$, $bs \in \text{List}\ \beta$, the generalized zip operation satisfies: \[ \text{zipWithAll}\ f\ (a :: as)\ (b :: bs) = f\ (\text{some}\ a)\ (\text{some}\ b) :: \text{zipWithAll}\ f\ as\ bs \]
List.unzip definition
: (l : List (α × β)) → List α × List β
Full source
/--
Separates a list of pairs into two lists that contain the respective first and second components.

`O(|l|)`.

Examples:
* `[("Monday", 1), ("Tuesday", 2)].unzip = (["Monday", "Tuesday"], [1, 2])`
* `[(x₁, y₁), (x₂, y₂), (x₃, y₃)].unzip = ([x₁, x₂, x₃], [y₁, y₂, y₃])`
* `([] : List (Nat × String)).unzip = (([], []) : List Nat × List String)`
-/
def unzip : (l : List (α × β)) → ListList α × List β
  | []          => ([], [])
  | (a, b)(a, b) :: t => match unzip t with | (as, bs) => (a::as, b::bs)
Unzip list of pairs
Informal description
Given a list of pairs $(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)$, the function `unzip` separates them into two lists $[x_1, x_2, \ldots, x_n]$ and $[y_1, y_2, \ldots, y_n]$. The operation has time complexity $O(n)$ where $n$ is the length of the input list.
List.unzip_nil theorem
: ([] : List (α × β)).unzip = ([], [])
Full source
@[simp] theorem unzip_nil : ([] : List (α × β)).unzip = ([], []) := rfl
Unzip of Empty List Yields Pair of Empty Lists
Informal description
For any types $\alpha$ and $\beta$, the unzip operation applied to the empty list of pairs $[] : \text{List}(\alpha \times \beta)$ returns a pair of empty lists $([], [])$.
List.unzip_cons theorem
{h : α × β} : (h :: t).unzip = match unzip t with | (as, bs) => (h.1 :: as, h.2 :: bs)
Full source
@[simp] theorem unzip_cons {h : α × β} :
    (h :: t).unzip = match unzip t with | (as, bs) => (h.1::as, h.2::bs) := rfl
Unzipping a Cons Pair in a List
Informal description
For any pair $h = (x, y) \in \alpha \times \beta$ and any list $t$ of pairs in $\alpha \times \beta$, the unzipping of the list $h :: t$ is equal to $(x :: as, y :: bs)$, where $(as, bs)$ is the result of unzipping $t$.
List.sum definition
{α} [Add α] [Zero α] : List α → α
Full source
/--
Computes the sum of the elements of a list.

Examples:
 * `[a, b, c].sum = a + (b + (c + 0))`
 * `[1, 2, 5].sum = 8`
-/
def sum {α} [Add α] [Zero α] : List α → α :=
  foldr (· + ·) 0
Sum of a list of elements
Informal description
Given a type $\alpha$ equipped with an addition operation and a zero element, the function `List.sum` computes the sum of all elements in a list $L : \text{List } \alpha$ by folding the addition operation over the list starting from $0$. For example: - $\text{sum } [a, b, c] = a + (b + (c + 0))$ - $\text{sum } [1, 2, 5] = 8$
List.sum_nil theorem
[Add α] [Zero α] : ([] : List α).sum = 0
Full source
@[simp] theorem sum_nil [Add α] [Zero α] : ([] : List α).sum = 0 := rfl
Sum of Empty List is Zero
Informal description
For any type $\alpha$ equipped with an addition operation and a zero element, the sum of the empty list $[]$ is equal to $0$.
List.sum_cons theorem
[Add α] [Zero α] {a : α} {l : List α} : (a :: l).sum = a + l.sum
Full source
@[simp] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
Sum of a Cons List Equals Head Plus Sum of Tail
Informal description
For any type $\alpha$ equipped with an addition operation and a zero element, and for any element $a \in \alpha$ and list $l \in \text{List } \alpha$, the sum of the list $a :: l$ is equal to $a$ plus the sum of $l$, i.e., $\text{sum}(a :: l) = a + \text{sum}(l)$.
Nat.sum definition
(l : List Nat) : Nat
Full source
/-- Sum of a list of natural numbers. -/
@[deprecated List.sum (since := "2024-10-17")]
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
Sum of a list of natural numbers
Informal description
The function `Nat.sum` takes a list of natural numbers $[n_1, n_2, \ldots, n_k]$ and returns their sum $n_1 + n_2 + \cdots + n_k$. For the empty list, it returns $0$.
Nat.sum_nil theorem
: Nat.sum ([] : List Nat) = 0
Full source
@[simp, deprecated sum_nil (since := "2024-10-17")]
theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
Sum of Empty List is Zero
Informal description
The sum of an empty list of natural numbers is equal to 0, i.e., $\text{sum}([]) = 0$.
Nat.sum_cons theorem
(a : Nat) (l : List Nat) : Nat.sum (a :: l) = a + Nat.sum l
Full source
@[simp, deprecated sum_cons (since := "2024-10-17")]
theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
    Nat.sum (a::l) = a + Nat.sum l := rfl
Sum of a Cons List Equals Head Plus Sum of Tail
Informal description
For any natural number $a$ and list of natural numbers $l$, the sum of the list $a :: l$ is equal to $a$ plus the sum of $l$, i.e., $\text{sum}(a :: l) = a + \text{sum}(l)$.
List.range definition
(n : Nat) : List Nat
Full source
/--
Returns a list of the numbers from `0` to `n` exclusive, in increasing order.

`O(n)`.

Examples:
* `range 5 = [0, 1, 2, 3, 4]`
* `range 0 = []`
* `range 2 = [0, 1]`
-/
def range (n : Nat) : List Nat :=
  loop n []
where
  loop : NatList NatList Nat
  | 0,   acc => acc
  | n+1, acc => loop n (n::acc)
List of numbers from 0 to n-1
Informal description
The function `List.range` takes a natural number `n` and returns the list of natural numbers from `0` to `n-1` in increasing order. More formally, for any natural number `n`, `List.range n` is the list `[0, 1, ..., n-1]` if `n > 0`, and the empty list `[]` if `n = 0`.
List.range_zero theorem
: range 0 = []
Full source
@[simp] theorem range_zero : range 0 = [] := rfl
Range of Zero is Empty List
Informal description
The list generated by `List.range` with input `0` is the empty list, i.e., $\text{range}(0) = []$.
List.range' definition
: (start len : Nat) → (step : Nat := 1) → List Nat
Full source
/--
Returns a list of the numbers with the given length `len`, starting at `start` and increasing by
`step` at each element.

In other words, `List.range' start len step` is `[start, start+step, ..., start+(len-1)*step]`.

Examples:
 * `List.range' 0 3 (step := 1) = [0, 1, 2]`
 * `List.range' 0 3 (step := 2) = [0, 2, 4]`
 * `List.range' 0 4 (step := 2) = [0, 2, 4, 6]`
 * `List.range' 3 4 (step := 2) = [3, 5, 7, 9]`
-/
def range' : (start len : Nat) → (step : Nat := 1) → List Nat
  | _, 0, _ => []
  | s, n+1, step => s :: range' (s+step) n step
Arithmetic sequence of natural numbers
Informal description
The function `List.range'` takes three natural numbers `start`, `len`, and `step` (with `step` defaulting to 1) and returns a list of length `len` where the first element is `start`, and each subsequent element increases by `step`. More formally, `List.range' start len step` is the list `[start, start + step, ..., start + (len - 1) * step]` if `len > 0`, and the empty list `[]` if `len = 0`.
List.iota definition
: Nat → List Nat
Full source
/--
`O(n)`. `iota n` is the numbers from `1` to `n` inclusive, in decreasing order.
* `iota 5 = [5, 4, 3, 2, 1]`
-/
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
def iota : NatList Nat
  | 0       => []
  | m@(n+1) => m :: iota n
Decreasing sequence from \( n \) to \( 1 \)
Informal description
The function `iota` takes a natural number \( n \) and returns the list of numbers from \( n \) down to \( 1 \) in decreasing order. For example, `iota 5 = [5, 4, 3, 2, 1]`. The time complexity is \( O(n) \).
List.iota_zero theorem
: iota 0 = []
Full source
@[simp] theorem iota_zero : iota 0 = [] := rfl
$\mathrm{iota}(0) = []$ (Empty List Property for iota at Zero)
Informal description
The list generated by the `iota` function evaluated at $0$ is the empty list, i.e., $\mathrm{iota}(0) = []$.
List.iota_succ theorem
: iota (i + 1) = (i + 1) :: iota i
Full source
@[simp] theorem iota_succ : iota (i+1) = (i+1) :: iota i := rfl
Recursive Definition of $\mathrm{iota}$: $\mathrm{iota}(i + 1) = (i + 1) :: \mathrm{iota}(i)$
Informal description
For any natural number $i$, the list $\mathrm{iota}(i + 1)$ is equal to the list obtained by prepending $i + 1$ to $\mathrm{iota}(i)$, i.e., $\mathrm{iota}(i + 1) = (i + 1) :: \mathrm{iota}(i)$.
List.zipIdx definition
: (l : List α) → (n : Nat := 0) → List (α × Nat)
Full source
/--
Pairs each element of a list with its index, optionally starting from an index other than `0`.

`O(|l|)`.

Examples:
* `[a, b, c].zipIdx = [(a, 0), (b, 1), (c, 2)]`
* `[a, b, c].zipIdx 5 = [(a, 5), (b, 6), (c, 7)]`
-/
def zipIdx : (l : List α) → (n : Nat := 0) → List (α × Nat)
  | [], _ => nil
  | x :: xs, n => (x, n) :: zipIdx xs (n + 1)
List elements paired with indices
Informal description
Given a list $L$ of elements of type $\alpha$ and an optional starting index $n$ (defaulting to $0$), the function `List.zipIdx` pairs each element of $L$ with its corresponding index, starting from $n$. The resulting list consists of pairs $(x, i)$ where $x$ is an element of $L$ and $i$ is its index, incrementing by $1$ for each subsequent element. For example: - $\mathrm{zipIdx}\ [a, b, c] = [(a, 0), (b, 1), (c, 2)]$ - $\mathrm{zipIdx}\ [a, b, c]\ 5 = [(a, 5), (b, 6), (c, 7)]$ The operation has time complexity $O(|L|)$.
List.zipIdx_nil theorem
: ([] : List α).zipIdx i = []
Full source
@[simp] theorem zipIdx_nil : ([] : List α).zipIdx i = [] := rfl
Empty List Yields Empty List under `zipIdx`
Informal description
For any natural number $i$, the operation `zipIdx` applied to the empty list `[]` (of type `List α`) and starting index $i$ yields the empty list, i.e., `[].zipIdx i = []`.
List.zipIdx_cons theorem
: (a :: as).zipIdx i = (a, i) :: as.zipIdx (i + 1)
Full source
@[simp] theorem zipIdx_cons : (a::as).zipIdx i = (a, i)(a, i) :: as.zipIdx (i+1) := rfl
Recursive Relation for Zipping List with Indices
Informal description
For any element $a$ of type $\alpha$, any list $as$ of elements of type $\alpha$, and any natural number $i$, the operation of pairing each element with its index (starting from $i$) satisfies the recursive relation: $$ \mathrm{zipIdx}(a :: as, i) = (a, i) :: \mathrm{zipIdx}(as, i + 1) $$ where $a :: as$ denotes the list obtained by prepending $a$ to $as$.
List.enumFrom definition
: Nat → List α → List (Nat × α)
Full source
/--
`O(|l|)`. `enumFrom n l` is like `enum` but it allows you to specify the initial index.
* `enumFrom 5 [a, b, c] = [(5, a), (6, b), (7, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enumFrom : NatList α → List (NatNat × α)
  | _, [] => nil
  | n, x :: xs   => (n, x) :: enumFrom (n + 1) xs
List enumeration with custom starting index
Informal description
Given a natural number $n$ and a list $l$ of elements of type $\alpha$, the function `enumFrom n l` returns a list of pairs where the first component is an index starting from $n$ and the second component is the corresponding element from $l$. For example, `enumFrom 5 [a, b, c]` yields `[(5, a), (6, b), (7, c)]`. The time complexity is linear in the length of the list.
List.enumFrom_nil theorem
: ([] : List α).enumFrom i = []
Full source
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enumFrom_nil : ([] : List α).enumFrom i = [] := rfl
Enumeration of Empty List from Any Index Yields Empty List
Informal description
For any natural number $i$ and the empty list $[]$ of type $\text{List}\,\alpha$, the enumeration starting from $i$ of the empty list is equal to the empty list, i.e., $\text{enumFrom}\,i\,[] = []$.
List.enumFrom_cons theorem
: (a :: as).enumFrom i = (i, a) :: as.enumFrom (i + 1)
Full source
@[deprecated zipIdx_cons (since := "2025-01-21"), simp]
theorem enumFrom_cons : (a::as).enumFrom i = (i, a)(i, a) :: as.enumFrom (i+1) := rfl
Recursive Definition of List Enumeration from Index
Informal description
For any element $a$ of type $\alpha$, list $as$ of type $\text{List } \alpha$, and natural number $i$, the enumeration of the list $a :: as$ starting from index $i$ is equal to the pair $(i, a)$ followed by the enumeration of $as$ starting from index $i + 1$. That is, \[ \text{enumFrom } i (a :: as) = (i, a) :: \text{enumFrom } (i + 1) as. \]
List.enum definition
: List α → List (Nat × α)
Full source
/--
`O(|l|)`. `enum l` pairs up each element with its index in the list.
* `enum [a, b, c] = [(0, a), (1, b), (2, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enum : List α → List (NatNat × α) := enumFrom 0
List enumeration with indices
Informal description
Given a list $l$ of elements of type $\alpha$, the function $\text{enum}$ returns a list of pairs where the first component is the index (starting from 0) and the second component is the corresponding element from $l$. For example, $\text{enum} [a, b, c] = [(0, a), (1, b), (2, c)]$. The time complexity is linear in the length of the list.
List.enum_nil theorem
: ([] : List α).enum = []
Full source
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enum_nil : ([] : List α).enum = [] := rfl
Enumeration of Empty List is Empty
Informal description
For any type $\alpha$, the enumeration of the empty list is the empty list, i.e., $\text{enum} ([] : \text{List } \alpha) = []$.
List.min? definition
[Min α] : List α → Option α
Full source
/--
Returns the smallest element of the list if it is not empty, or `none` if it is empty.

Examples:
* `[].min? = none`
* `[4].min? = some 4`
* `[1, 4, 2, 10, 6].min? = some 1`
-/
def min? [Min α] : List α → Option α
  | []    => none
  | a::as => some <| as.foldl min a
Minimum element of a list
Informal description
Given a list $L$ of elements of type $\alpha$ with a `Min` instance (decidable linear order), the function returns the smallest element of $L$ as an `Option α` (i.e., `some x` where $x$ is the minimum element if $L$ is non-empty, and `none` if $L$ is empty). Examples: - $\text{min? } [] = \text{none}$ - $\text{min? } [4] = \text{some } 4$ - $\text{min? } [1, 4, 2, 10, 6] = \text{some } 1$
List.minimum? abbrev
Full source
@[inherit_doc min?, deprecated min? (since := "2024-09-29")] abbrev minimum? := @min?
Minimum Element of a List (Optional)
Informal description
Given a list $L$ of elements of type $\alpha$ with a `Min` instance (decidable linear order), the function returns the smallest element of $L$ as an `Option α` (i.e., `some x` where $x$ is the minimum element if $L$ is non-empty, and `none` if $L$ is empty). Examples: - $\text{minimum? } [] = \text{none}$ - $\text{minimum? } [4] = \text{some } 4$ - $\text{minimum? } [1, 4, 2, 10, 6] = \text{some } 1$
List.max? definition
[Max α] : List α → Option α
Full source
/--
Returns the largest element of the list if it is not empty, or `none` if it is empty.

Examples:
* `[].max? = none`
* `[4].max? = some 4`
* `[1, 4, 2, 10, 6].max? = some 10`
-/
def max? [Max α] : List α → Option α
  | []    => none
  | a::as => some <| as.foldl max a
Maximum element of a list (optional)
Informal description
Given a list $[a_1, a_2, \ldots, a_n]$ of elements of type $\alpha$ with a `Max` instance, the function returns the largest element in the list as `some max_element` if the list is non-empty, or `none` if the list is empty. For example: - $\text{max? } [] = \text{none}$ - $\text{max? } [4] = \text{some } 4$ - $\text{max? } [1, 4, 2, 10, 6] = \text{some } 10$ The function computes the maximum by folding the `max` operation over the list, starting with the first element.
List.maximum? abbrev
Full source
@[inherit_doc max?, deprecated max? (since := "2024-09-29")] abbrev maximum? := @max?
Maximum Element of a List (Optional)
Informal description
Given a list $[a_1, a_2, \ldots, a_n]$ of elements of type $\alpha$ with a `Max` instance, the function returns the largest element in the list as `some max_element` if the list is non-empty, or `none` if the list is empty. For example: - $\text{maximum? } [] = \text{none}$ - $\text{maximum? } [4] = \text{some } 4$ - $\text{maximum? } [1, 4, 2, 10, 6] = \text{some } 10$ The function computes the maximum by folding the `max` operation over the list, starting with the first element.
List.intersperse definition
(sep : α) : (l : List α) → List α
Full source
/--
Alternates the elements of `l` with `sep`.

`O(|l|)`.

`List.intercalate` is a similar function that alternates a separator list with elements of a list of
lists.

Examples:
* `List.intersperse "then" [] = []`
* `List.intersperse "then" ["walk"] = ["walk"]`
* `List.intersperse "then" ["walk", "run"] = ["walk", "then", "run"]`
* `List.intersperse "then" ["walk", "run", "rest"] = ["walk", "then", "run", "then", "rest"]`
-/
def intersperse (sep : α) : (l : List α) → List α
  | []    => []
  | [x]   => [x]
  | x::xs => x :: sep :: intersperse sep xs
Intersperse elements in a list with a separator
Informal description
Given a separator element `sep` and a list `l`, the function `List.intersperse` returns a new list where `sep` is inserted between every pair of consecutive elements in `l`. Formally, for a list `l = [x₁, x₂, ..., xₙ]`, the result is `[x₁, sep, x₂, sep, ..., sep, xₙ]` if `n ≥ 2`, and `l` itself if `n ≤ 1`. The operation has time complexity \(O(|l|)\).
List.intersperse_nil theorem
{sep : α} : ([] : List α).intersperse sep = []
Full source
@[simp] theorem intersperse_nil {sep : α} : ([] : List α).intersperse sep = [] := rfl
Interspersing a Separator into the Empty List Yields the Empty List
Informal description
For any separator element `sep` of type `α`, the operation of interspersing `sep` into the empty list `[]` results in the empty list itself, i.e., `[].intersperse sep = []`.
List.intersperse_single theorem
{x : α} {sep : α} : [x].intersperse sep = [x]
Full source
@[simp] theorem intersperse_single {x : α} {sep : α} : [x].intersperse sep = [x] := rfl
Intersperse Preserves Singleton List: $[x].\text{intersperse}(sep) = [x]$
Informal description
For any element $x$ and separator $sep$ of type $\alpha$, the list obtained by interspersing $sep$ in the singleton list $[x]$ is equal to $[x]$ itself.
List.intersperse_cons₂ theorem
{x : α} {y : α} {zs : List α} {sep : α} : (x :: y :: zs).intersperse sep = x :: sep :: ((y :: zs).intersperse sep)
Full source
@[simp] theorem intersperse_cons₂ {x : α} {y : α} {zs : List α} {sep : α} :
    (x::y::zs).intersperse sep = x::sep::((y::zs).intersperse sep) := rfl
Recursive Definition of List Interspersion for Nonempty Lists
Informal description
For any elements $x, y$ of type $\alpha$, any list $zs$ of elements of type $\alpha$, and any separator $sep$ of type $\alpha$, the interspersion of $sep$ in the list $x :: y :: zs$ is equal to $x :: sep :: (y :: zs).\text{intersperse}(sep)$.
List.intercalate definition
(sep : List α) (xs : List (List α)) : List α
Full source
/--
Alternates the lists in `xs` with the separator `sep`, appending them. The resulting list is
flattened.

`O(|xs|)`.

`List.intersperse` is a similar function that alternates a separator element with the elements of a
list.

Examples:
* `List.intercalate sep [] = []`
* `List.intercalate sep [a] = a`
* `List.intercalate sep [a, b] = a ++ sep ++ b`
* `List.intercalate sep [a, b, c] = a ++ sep ++ b ++ sep ++ c`
-/
def intercalate (sep : List α) (xs : List (List α)) : List α :=
  (intersperse sep xs).flatten
List intercalation (flattened interspersion)
Informal description
Given a separator list `sep` and a list of lists `xs`, the function `intercalate` alternates the lists in `xs` with the separator `sep` and flattens the result. More precisely, for `xs = [L₁, L₂, ..., Lₙ]`, the result is `L₁ ++ sep ++ L₂ ++ sep ++ ... ++ sep ++ Lₙ`, where `++` denotes list concatenation. Examples: - `intercalate sep [] = []` - `intercalate sep [L] = L` - `intercalate sep [L₁, L₂] = L₁ ++ sep ++ L₂` - `intercalate sep [L₁, L₂, L₃] = L₁ ++ sep ++ L₂ ++ sep ++ L₃` The operation has time complexity \(O(|xs| \cdot |sep| + \sum_{L \in xs} |L|)\).
List.eraseDups definition
{α} [BEq α] (as : List α) : List α
Full source
/--
Erases duplicated elements in the list, keeping the first occurrence of duplicated elements.

`O(|l|^2)`.

Examples:
 * `[1, 3, 2, 2, 3, 5].eraseDups = [1, 3, 2, 5]`
 * `["red", "green", "green", "blue"].eraseDups = ["red", "green", "blue"]`
-/
def eraseDups {α} [BEq α] (as : List α) : List α :=
  loop as []
where
  loop : List α → List α → List α
  | [],    bs => bs.reverse
  | a::as, bs => match bs.elem a with
    | true  => loop as bs
    | false => loop as (a::bs)
List deduplication (keeping first occurrences)
Informal description
Given a list `as` of elements of type `α` with a boolean equality relation `==`, the function `eraseDups` returns a new list where all duplicate elements are removed, keeping only the first occurrence of each element. The operation has time complexity $O(n^2)$ where $n$ is the length of the list. Examples: - `[1, 3, 2, 2, 3, 5].eraseDups = [1, 3, 2, 5]` - `["red", "green", "green", "blue"].eraseDups = ["red", "green", "blue"]`
List.eraseReps definition
{α} [BEq α] : List α → List α
Full source
/--
Erases repeated elements, keeping the first element of each run.

`O(|l|)`.

Example:
* `[1, 3, 2, 2, 2, 3, 3, 5].eraseReps = [1, 3, 2, 3, 5]`
-/
def eraseReps {α} [BEq α] : List α → List α
  | []    => []
  | a::as => loop a as []
where
  loop {α} [BEq α] : α → List α → List α → List α
  | a, [], acc => (a::acc).reverse
  | a, a'::as, acc => match a == a' with
    | true  => loop a as acc
    | false => loop a' as (a::acc)
Remove consecutive duplicates from a list
Informal description
Given a list `l` of elements of type `α` with a boolean equality relation `[BEq α]`, the function `eraseReps` removes consecutive duplicate elements, keeping only the first occurrence of each run. The operation has time complexity $O(|l|)$. For example, applying `eraseReps` to the list `[1, 3, 2, 2, 2, 3, 3, 5]` results in `[1, 3, 2, 3, 5]`.
List.span definition
(p : α → Bool) (as : List α) : List α × List α
Full source
/--
Splits a list into the the longest initial segment for which `p` returns `true`, paired with the
remainder of the list.

`O(|l|)`.

Examples:
* `[6, 8, 9, 5, 2, 9].span (· > 5) = ([6, 8, 9], [5, 2, 9])`
* `[6, 8, 9, 5, 2, 9].span (· > 10) = ([], [6, 8, 9, 5, 2, 9])`
* `[6, 8, 9, 5, 2, 9].span (· > 0) = ([6, 8, 9, 5, 2, 9], [])`
-/
@[inline] def span (p : α → Bool) (as : List α) : ListList α × List α :=
  loop as []
where
  @[specialize] loop : List α → List α → ListList α × List α
  | [],    acc => (acc.reverse, [])
  | a::as, acc => match p a with
    | true  => loop as (a::acc)
    | false => (acc.reverse, a::as)
Splitting a list by a predicate
Informal description
Given a predicate `p : α → Bool` and a list `as : List α`, the function `List.span p as` splits `as` into a pair of lists `(l₁, l₂)`, where `l₁` is the longest initial segment of `as` for which all elements satisfy `p`, and `l₂` is the remaining part of the list. More formally, if `as = a₁ :: a₂ :: ... :: aₙ :: []`, then `l₁` is the longest prefix `a₁ :: a₂ :: ... :: aₖ` such that `p aᵢ = true` for all `1 ≤ i ≤ k`, and `l₂` is the remaining suffix `aₖ₊₁ :: ... :: aₙ :: []`. If no elements satisfy `p`, then `l₁` is empty and `l₂ = as`. If all elements satisfy `p`, then `l₁ = as` and `l₂` is empty. The operation has time complexity $O(|as|)$.
List.splitBy definition
(R : α → α → Bool) : List α → List (List α)
Full source
/--
Splits a list into the longest segments in which each pair of adjacent elements are related by `R`.

`O(|l|)`.

Examples:
* `[1, 1, 2, 2, 2, 3, 2].splitBy (· == ·) = [[1, 1], [2, 2, 2], [3], [2]]`
* `[1, 2, 5, 4, 5, 1, 4].splitBy (· < ·) = [[1, 2, 5], [4, 5], [1, 4]]`
* `[1, 2, 5, 4, 5, 1, 4].splitBy (fun _ _ => true) = [[1, 2, 5, 4, 5, 1, 4]]`
* `[1, 2, 5, 4, 5, 1, 4].splitBy (fun _ _ => false) = [[1], [2], [5], [4], [5], [1], [4]]`
-/
@[specialize] def splitBy (R : α → α → Bool) : List α → List (List α)
  | []    => []
  | a::as => loop as a [] []
where
  /--
  The arguments of `splitBy.loop l b g gs` represent the following:

  - `l : List α` are the elements which we still need to split.
  - `b : α` is the previous element for which a comparison was performed.
  - `r : List α` is the group currently being assembled, in **reverse order**.
  - `acc : List (List α)` is all of the groups that have been completed, in **reverse order**.
  -/
  @[specialize] loop : List α → α → List α → List (List α) → List (List α)
  | a::as, b, r, acc => match R b a with
    | true  => loop as a (b::r) acc
    | false => loop as a [] ((b::r).reverse::acc)
  | [], ag, r, acc => ((ag::r).reverse::acc).reverse
List partition by adjacent relation
Informal description
Given a binary relation `R` on elements of type `α` and a list `l` of elements of type `α`, the function `List.splitBy R l` partitions `l` into a list of sublists where each sublist is the longest possible segment of consecutive elements in `l` such that every pair of adjacent elements in the segment satisfies the relation `R`. The operation has time complexity $O(|l|)$. **Examples:** - `[1, 1, 2, 2, 2, 3, 2].splitBy (· == ·) = [[1, 1], [2, 2, 2], [3], [2]]` - `[1, 2, 5, 4, 5, 1, 4].splitBy (· < ·) = [[1, 2, 5], [4, 5], [1, 4]]` - `[1, 2, 5, 4, 5, 1, 4].splitBy (fun _ _ => true) = [[1, 2, 5, 4, 5, 1, 4]]` - `[1, 2, 5, 4, 5, 1, 4].splitBy (fun _ _ => false) = [[1], [2], [5], [4], [5], [1], [4]]`
List.groupBy abbrev
Full source
@[deprecated splitBy (since := "2024-10-30"), inherit_doc splitBy] abbrev groupBy := @splitBy
List Partition by Adjacent Pairwise Relation
Informal description
Given a binary relation `R : α → α → Bool` and a list `l : List α`, the function `List.groupBy R l` partitions `l` into a list of sublists where each sublist consists of consecutive elements in `l` that are pairwise related by `R`. More precisely, the resulting list of sublists satisfies: 1. The concatenation of all sublists equals the original list `l`. 2. For each sublist `[a₁, a₂, ..., aₙ]`, the relation `R aᵢ aᵢ₊₁` holds for all `1 ≤ i < n`. 3. The sublists are maximal with respect to this property.
List.removeAll definition
[BEq α] (xs ys : List α) : List α
Full source
/--
Removes all elements of `xs` that are present in `ys`.

`O(|xs| * |ys|)`.

Examples:
 * `[1, 1, 5, 1, 2, 4, 5].removeAll [1, 2, 2] = [5, 4, 5]`
 * `[1, 2, 3, 2].removeAll [] = [1, 2, 3, 2]`
 * `[1, 2, 3, 2].removeAll [3] = [1, 2, 2]`
-/
def removeAll [BEq α] (xs ys : List α) : List α :=
  xs.filter (fun x => !ys.elem x)
List difference (remove all occurrences)
Informal description
Given two lists `xs` and `ys` of elements of type `α` with a boolean equality relation `==`, the function `List.removeAll xs ys` returns a new list obtained by removing all elements from `xs` that are present in `ys` according to the `==` relation. The order of the remaining elements in `xs` is preserved. **Examples:** - `[1, 1, 5, 1, 2, 4, 5].removeAll [1, 2, 2] = [5, 4, 5]` - `[1, 2, 3, 2].removeAll [] = [1, 2, 3, 2]` - `[1, 2, 3, 2].removeAll [3] = [1, 2, 2]` The operation has time complexity $O(|xs| \cdot |ys|)$, where $|xs|$ and $|ys|$ are the lengths of the input lists.
List.length_add_eq_lengthTRAux theorem
{as : List α} {n : Nat} : as.length + n = as.lengthTRAux n
Full source
theorem length_add_eq_lengthTRAux {as : List α} {n : Nat} : as.length + n = as.lengthTRAux n := by
  induction as generalizing n with
  | nil  => simp [length, lengthTRAux]
  | cons a as ih =>
    simp [length, lengthTRAux, ← ih, Nat.succ_add]
    rfl
Equivalence of List Length and Tail-Recursive Length Auxiliary Function: $\text{length}(as) + n = \text{lengthTRAux}(as, n)$
Informal description
For any list `as` of elements of type `α` and any natural number `n`, the sum of the length of `as` and `n` is equal to the result of the tail-recursive length auxiliary function applied to `as` and `n`, i.e., $\text{length}(as) + n = \text{lengthTRAux}(as, n)$.
List.length_eq_lengthTR theorem
: @List.length = @List.lengthTR
Full source
@[csimp] theorem length_eq_lengthTR : @List.length = @List.lengthTR := by
  apply funext; intro α; apply funext; intro as
  simp [lengthTR, ← length_add_eq_lengthTRAux]
Equality of Standard and Tail-Recursive List Length Functions
Informal description
The standard list length function `List.length` is equal to the tail-recursive list length function `List.lengthTR` for any list of elements of type $\alpha$.
List.mapTR definition
(f : α → β) (as : List α) : List β
Full source
/--
Applies a function to each element of the list, returning the resulting list of values.

`O(|l|)`. This is the tail-recursive variant of `List.map`, used in runtime code.

Examples:
* `[a, b, c].mapTR f = [f a, f b, f c]`
* `[].mapTR Nat.succ = []`
* `["one", "two", "three"].mapTR (·.length) = [3, 3, 5]`
* `["one", "two", "three"].mapTR (·.reverse) = ["eno", "owt", "eerht"]`
-/
@[inline] def mapTR (f : α → β) (as : List α) : List β :=
  loop as []
where
  @[specialize] loop : List α → List β → List β
  | [],    bs => bs.reverse
  | a::as, bs => loop as (f a :: bs)
Tail-recursive list mapping
Informal description
The tail-recursive function `List.mapTR` applies a function \( f : \alpha \to \beta \) to each element of a list \( as : \text{List } \alpha \), producing a new list of type \( \text{List } \beta \). The function is implemented using an auxiliary loop that accumulates the results in reverse order and then reverses them at the end to maintain the original order.
List.mapTR_loop_eq theorem
{f : α → β} {as : List α} {bs : List β} : mapTR.loop f as bs = bs.reverse ++ map f as
Full source
theorem mapTR_loop_eq {f : α → β} {as : List α} {bs : List β} :
    mapTR.loop f as bs = bs.reverse ++ map f as := by
  induction as generalizing bs with
  | nil => simp [mapTR.loop, map]
  | cons a as ih =>
    simp only [mapTR.loop, map]
    rw [ih (bs := f a :: bs), reverse_cons, append_assoc]
    rfl
Tail-Recursive Map Loop Equals Reversed Accumulator Plus Mapped List
Informal description
For any function $f : \alpha \to \beta$, list $as : \text{List } \alpha$, and accumulator list $bs : \text{List } \beta$, the tail-recursive loop $\text{mapTR.loop}\ f\ as\ bs$ equals the concatenation of the reversed accumulator $bs$ with the mapped list $\text{map}\ f\ as$. That is, $$\text{mapTR.loop}\ f\ as\ bs = \text{reverse}(bs) \mathbin{+\kern-1.5ex+} \text{map}\ f\ as$$ where $\mathbin{+\kern-1.5ex+}$ denotes list concatenation.
List.map_eq_mapTR theorem
: @map = @mapTR
Full source
@[csimp] theorem map_eq_mapTR : @map = @mapTR :=
  funext fun α => funext fun β => funext fun f => funext fun as => by
    simp [mapTR, mapTR_loop_eq]
Equivalence of Standard and Tail-Recursive List Mapping: $\text{map} = \text{mapTR}$
Informal description
The standard list mapping function `map` is equal to its tail-recursive version `mapTR`. That is, for any function $f : \alpha \to \beta$ and list $L : \text{List } \alpha$, we have $\text{map } f \ L = \text{mapTR } f \ L$.
List.filterTR definition
(p : α → Bool) (as : List α) : List α
Full source
/--
Returns the list of elements in `l` for which `p` returns `true`.

`O(|l|)`. This is a tail-recursive version of `List.filter`, used at runtime.

Examples:
* `[1, 2, 5, 2, 7, 7].filterTR (· > 2)  = [5, 7, 7]`
* `[1, 2, 5, 2, 7, 7].filterTR (fun _ => false) = []`
* `[1, 2, 5, 2, 7, 7].filterTR (fun _ => true) = * [1, 2, 5, 2, 7, 7]`
-/
@[inline] def filterTR (p : α → Bool) (as : List α) : List α :=
  loop as []
where
  @[specialize] loop : List α → List α → List α
  | [],    acc => acc.reverse
  | a::as, acc => match p a with
     | true  => loop as (a::acc)
     | false => loop as acc
Tail-recursive list filter
Informal description
The function `filterTR` takes a predicate `p : α → Bool` and a list `as : List α`, and returns the sublist of elements in `as` for which `p` returns `true`. This is a tail-recursive implementation of the filtering operation, ensuring efficient runtime performance. More precisely, for a given list `as = [a₁, a₂, ..., aₙ]`, `filterTR p as` returns the list `[a_{i₁}, a_{i₂}, ..., a_{iₖ}]` where `1 ≤ i₁ < i₂ < ... < iₖ ≤ n` and `p(a_{iⱼ}) = true` for all `1 ≤ j ≤ k`.
List.filterTR_loop_eq theorem
{p : α → Bool} {as : List α} {bs : List α} : filterTR.loop p as bs = bs.reverse ++ filter p as
Full source
theorem filterTR_loop_eq {p : α → Bool} {as : List α} {bs : List α} :
    filterTR.loop p as bs = bs.reverse ++ filter p as := by
  induction as generalizing bs with
  | nil => simp [filterTR.loop, filter]
  | cons a as ih =>
    simp only [filterTR.loop, filter]
    split <;> simp_all
Tail-recursive filter loop equals reversed accumulator concatenated with filtered list
Informal description
For any predicate $p : \alpha \to \text{Bool}$ and lists $as, bs : \text{List } \alpha$, the tail-recursive filter loop satisfies: $$\text{filterTR.loop } p \ as \ bs = \text{reverse } bs \mathbin{+\kern-1.5ex+} \text{filter } p \ as$$ where $\mathbin{+\kern-1.5ex+}$ denotes list concatenation.
List.filter_eq_filterTR theorem
: @filter = @filterTR
Full source
@[csimp] theorem filter_eq_filterTR : @filter = @filterTR := by
  apply funext; intro α; apply funext; intro p; apply funext; intro as
  simp [filterTR, filterTR_loop_eq]
Equivalence of Standard and Tail-Recursive List Filter Functions
Informal description
The standard list filter function `filter` is equal to its tail-recursive variant `filterTR` for any type $\alpha$ and predicate $p : \alpha \to \text{Bool}$.
List.replicateTR definition
{α : Type u} (n : Nat) (a : α) : List α
Full source
/--
Creates a list that contains `n` copies of `a`.

This is a tail-recursive version of `List.replicate`.

* `List.replicateTR 5 "five" = ["five", "five", "five", "five", "five"]`
* `List.replicateTR 0 "zero" = []`
* `List.replicateTR 2 ' ' = [' ', ' ']`
-/
def replicateTR {α : Type u} (n : Nat) (a : α) : List α :=
  let rec loop : NatList α → List α
    | 0, as => as
    | n+1, as => loop n (a::as)
  loop n []
Tail-recursive list replication
Informal description
The function `List.replicateTR` takes a natural number \( n \) and an element \( a \) of type \( \alpha \), and returns a list containing \( n \) copies of \( a \). This is a tail-recursive implementation of list replication.
List.replicateTR_loop_replicate_eq theorem
{a : α} {m n : Nat} : replicateTR.loop a n (replicate m a) = replicate (n + m) a
Full source
theorem replicateTR_loop_replicate_eq {a : α} {m n : Nat} :
  replicateTR.loop a n (replicate m a) = replicate (n + m) a := by
  induction n generalizing m with simp [replicateTR.loop]
  | succ n ih => simp [Nat.succ_add]; exact ih (m := m+1)
Tail-recursive replication concatenation: $\text{replicateTR.loop}\ a\ n\ (\text{replicate}\ m\ a) = \text{replicate}\ (n + m)\ a$
Informal description
For any element $a$ of type $\alpha$ and natural numbers $m$ and $n$, the tail-recursive replication function `replicateTR.loop` applied to $a$, $n$, and the list `replicate m a` (a list containing $m$ copies of $a$) produces the same result as the list `replicate (n + m) a` (a list containing $n + m$ copies of $a$).
List.replicateTR_loop_eq theorem
: ∀ n, replicateTR.loop a n acc = replicate n a ++ acc
Full source
theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++ acc
  | 0 => rfl
  | n+1 => by rw [← replicateTR_loop_replicate_eq, replicate, replicate,
    replicateTR.loop, replicateTR_loop_eq n, replicateTR_loop_eq n, append_assoc]; rfl
Tail-recursive replication loop equals concatenation of replicated list
Informal description
For any element $a$ of type $\alpha$, natural number $n$, and list $\text{acc}$ of type $\text{List}\ \alpha$, the tail-recursive replication loop satisfies: $$\text{replicateTR.loop}\ a\ n\ \text{acc} = \text{replicate}\ n\ a \mathbin{+\kern-1.5ex+} \text{acc}$$ where $\mathbin{+\kern-1.5ex+}$ denotes list concatenation.
List.replicate_eq_replicateTR theorem
: @List.replicate = @List.replicateTR
Full source
@[csimp] theorem replicate_eq_replicateTR : @List.replicate = @List.replicateTR := by
  apply funext; intro α; apply funext; intro n; apply funext; intro a
  exact (replicateTR_loop_replicate_eq (m := 0)).symm
Equality of List Replication and Its Tail-Recursive Version: $\text{replicate} = \text{replicateTR}$
Informal description
The function `List.replicate` that creates a list with `n` copies of an element `a` is equal to its tail-recursive version `List.replicateTR`, i.e., $\text{replicate} = \text{replicateTR}$.
List.leftpadTR definition
(n : Nat) (a : α) (l : List α) : List α
Full source
/--
Pads `l : List α` on the left with repeated occurrences of `a : α` until it is of length `n`. If `l`
already has at least `n` elements, it is returned unmodified.

This is a tail-recursive version of `List.leftpad`, used at runtime.

Examples:
 * `[1, 2, 3].leftPadTR 5 0 = [0, 0, 1, 2, 3]`
 * `["red", "green", "blue"].leftPadTR 4 "blank" = ["blank", "red", "green", "blue"]`
 * `["red", "green", "blue"].leftPadTR 3 "blank" = ["red", "green", "blue"]`
 * `["red", "green", "blue"].leftPadTR 1 "blank" = ["red", "green", "blue"]`
-/
@[inline] def leftpadTR (n : Nat) (a : α) (l : List α) : List α :=
  replicateTR.loop a (n - length l) l
Tail-recursive left padding of a list
Informal description
Given a natural number $n$, an element $a$ of type $\alpha$, and a list $l$ of elements of type $\alpha$, the function $\text{leftpadTR}$ pads $l$ on the left with repeated occurrences of $a$ until the resulting list has length $n$. If $l$ already has length at least $n$, it is returned unchanged. This is a tail-recursive implementation of left padding. Examples: - $\text{leftpadTR } 5 \, 0 \, [1, 2, 3] = [0, 0, 1, 2, 3]$ - $\text{leftpadTR } 4 \, \text{"blank"} \, [\text{"red"}, \text{"green"}, \text{"blue"}] = [\text{"blank"}, \text{"red"}, \text{"green"}, \text{"blue"}]$ - $\text{leftpadTR } 3 \, \text{"blank"} \, [\text{"red"}, \text{"green"}, \text{"blue"}] = [\text{"red"}, \text{"green"}, \text{"blue"}]$ - $\text{leftpadTR } 1 \, \text{"blank"} \, [\text{"red"}, \text{"green"}, \text{"blue"}] = [\text{"red"}, \text{"green"}, \text{"blue"}]$
List.leftpad_eq_leftpadTR theorem
: @leftpad = @leftpadTR
Full source
@[csimp] theorem leftpad_eq_leftpadTR : @leftpad = @leftpadTR := by
  repeat (apply funext; intro)
  simp [leftpad, leftpadTR, replicateTR_loop_eq]
Equality of Left Padding and Its Tail-Recursive Variant: $\text{leftpad} = \text{leftpadTR}$
Informal description
The left padding function `leftpad` is equal to its tail-recursive variant `leftpadTR` for all inputs, i.e., $\text{leftpad} = \text{leftpadTR}$.
List.unzipTR definition
(l : List (α × β)) : List α × List β
Full source
/--
Separates a list of pairs into two lists that contain the respective first and second components.

`O(|l|)`. This is a tail-recursive version of `List.unzip` that's used at runtime.

Examples:
* `[("Monday", 1), ("Tuesday", 2)].unzipTR = (["Monday", "Tuesday"], [1, 2])`
* `[(x₁, y₁), (x₂, y₂), (x₃, y₃)].unzipTR = ([x₁, x₂, x₃], [y₁, y₂, y₃])`
* `([] : List (Nat × String)).unzipTR = (([], []) : List Nat × List String)`
-/
def unzipTR (l : List (α × β)) : ListList α × List β :=
  l.foldr (fun (a, b) (as, bs) => (a::as, b::bs)) ([], [])
Tail-recursive unzip of a list of pairs
Informal description
Given a list of pairs $l : \text{List } (\alpha \times \beta)$, the function $\text{unzipTR}$ separates the list into two lists: the first containing all the first components of the pairs, and the second containing all the second components. This is implemented as a tail-recursive right fold over the input list, ensuring efficient runtime performance with $O(|l|)$ time complexity. For example: - $\text{unzipTR } [(x_1, y_1), (x_2, y_2), (x_3, y_3)] = ([x_1, x_2, x_3], [y_1, y_2, y_3])$ - $\text{unzipTR } [] = ([], [])$
List.unzip_eq_unzipTR theorem
: @unzip = @unzipTR
Full source
@[csimp] theorem unzip_eq_unzipTR : @unzip = @unzipTR := by
  apply funext; intro α; apply funext; intro β; apply funext; intro l
  simp [unzipTR]; induction l <;> simp [*]
Equivalence of Standard and Tail-Recursive List Unzipping: $\text{unzip} = \text{unzipTR}$
Informal description
The standard list unzipping function `unzip` is equal to its tail-recursive variant `unzipTR` for all input lists of pairs.
List.range'_eq_range'TR theorem
: @range' = @range'TR
Full source
@[csimp] theorem range'_eq_range'TR : @range' = @range'TR := by
  apply funext; intro s; apply funext; intro n; apply funext; intro step
  let rec go (s) : ∀ n m,
    range'TR.go step n (s + step * n) (range' (s + step * n) m step) = range' s (n + m) step
  | 0, m => by simp [range'TR.go]
  | n+1, m => by
    simp [range'TR.go]
    rw [Nat.mul_succ, ← Nat.add_assoc, Nat.add_sub_cancel, Nat.add_right_comm n]
    exact go s n (m + 1)
  exact (go s n 0).symm
Equality of Standard and Tail-Recursive Arithmetic Sequence Functions: $\text{range'} = \text{range'TR}$
Informal description
The standard implementation of the arithmetic sequence function `range'` is equal to its tail-recursive variant `range'TR` for all input parameters `start`, `len`, and `step`.
List.iotaTR definition
(n : Nat) : List Nat
Full source
/-- Tail-recursive version of `List.iota`. -/
@[deprecated "Use `List.range' 1 n` instead of `iota n`." (since := "2025-01-20")]
def iotaTR (n : Nat) : List Nat :=
  let rec go : NatList NatList Nat
    | 0, r => r.reverse
    | m@(n+1), r => go n (m::r)
  go n []
Tail-recursive list of natural numbers from 1 to n
Informal description
The tail-recursive function `List.iotaTR` takes a natural number `n` and returns the list `[1, 2, ..., n]`. More precisely, for a given natural number `n`, `List.iotaTR n` computes the list of natural numbers from `1` to `n` in ascending order. The function is implemented using a tail-recursive helper function for efficiency.
List.iota_eq_iotaTR theorem
: @iota = @iotaTR
Full source
@[csimp]
theorem iota_eq_iotaTR : @iota = @iotaTR :=
  have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
    induction n generalizing r with
    | zero => simp [iota, iotaTR.go]
    | succ n ih => simp [iota, iotaTR.go, ih, append_assoc]
  funext fun n => by simp [iotaTR, aux]
Equality of Iota and Tail-Recursive Iota Functions
Informal description
The function `List.iota` is equal to its tail-recursive implementation `List.iotaTR`, i.e., $\text{iota} = \text{iotaTR}$.
List.intersperseTR definition
(sep : α) : (l : List α) → List α
Full source
/--
Alternates the elements of `l` with `sep`.

`O(|l|)`.

This is a tail-recursive version of `List.intersperse`, used at runtime.

Examples:
* `List.intersperseTR "then" [] = []`
* `List.intersperseTR "then" ["walk"] = ["walk"]`
* `List.intersperseTR "then" ["walk", "run"] = ["walk", "then", "run"]`
* `List.intersperseTR "then" ["walk", "run", "rest"] = ["walk", "then", "run", "then", "rest"]`
-/
def intersperseTR (sep : α) : (l : List α) → List α
  | [] => []
  | [x] => [x]
  | x::y::xs => x :: sep :: y :: xs.foldr (fun a r => sep :: a :: r) []
Tail-recursive list interspersion
Informal description
Given a separator element `sep` of type `α` and a list `l` of elements of type `α`, the function `List.intersperseTR` returns a new list where `sep` is inserted between consecutive elements of `l`. This is a tail-recursive implementation with time complexity $O(|l|)$. Examples: - `List.intersperseTR "then" [] = []` - `List.intersperseTR "then" ["walk"] = ["walk"]` - `List.intersperseTR "then" ["walk", "run"] = ["walk", "then", "run"]` - `List.intersperseTR "then" ["walk", "run", "rest"] = ["walk", "then", "run", "then", "rest"]`
List.intersperse_eq_intersperseTR theorem
: @intersperse = @intersperseTR
Full source
@[csimp] theorem intersperse_eq_intersperseTR : @intersperse = @intersperseTR := by
  apply funext; intro α; apply funext; intro sep; apply funext; intro l
  simp [intersperseTR]
  match l with
  | [] | [_] => rfl
  | x::y::xs => simp [intersperse]; induction xs generalizing y <;> simp [*]
Equality of List Interspersion Functions: $\text{intersperse} = \text{intersperseTR}$
Informal description
The function `List.intersperse` is equal to its tail-recursive implementation `List.intersperseTR`, i.e., $\text{intersperse} = \text{intersperseTR}$.