diff --git a/CHANGELOG.md b/CHANGELOG.md
index 029a7817..ce315795 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`lastIndexOfRange`, `lastIndexOfRangeWith`, `binarySearch`, `binarySearchWithComparer`,
`binarySearchRangeWithComparer`) and copies elements into an array (`blit`). `replace` throws `ArgumentException` for a
value that no element equals, as the builder of an indexed list does.
+* `Stack` has every function of the FSharp.Core `List` module that applies to an immutable stack, with its semantics and
+ exceptions and with stable sorts, plus `consMany`, `ofReadOnlySpan`, `ofSpan`, `skipLast`, `sortWithComparer`,
+ `takeLast`, `tryTail`, `tryUncons` and `uncons`. A stack enumerates its elements from the top, and every function
+ treats it as a list in that order; `rev` puts the last element on top. Its functions name their stacks `stack`,
+ `stack1` and `stacks` where `List` names its lists, and the exceptions report these names. `consMany`, `uncons`,
+ `tryHead`, `tryTail` and `tryUncons` compile to `PushMany`, `PeekPop`, `TryPeek`, `TryPop` and `TryPeekPop`, in the
+ `IStack`, `Option` and `Tuple` modules too. A function that takes a sequence throws `ArgumentNullException` that names
+ the parameter for a `null` sequence and for a default `FlatList` given as a sequence, which would fail with
+ `InvalidOperationException` while the function enumerates it; the `IStack` module and the `stack { }` expression do the
+ same.
+* The `IStack` module has the functions of `Stack` for `IImmutableStack<'T>`, without the creation functions, plus
+ `clear`. Its functions keep the implementation of the input stack where the result has the element type of the input,
+ and return a `Stack` otherwise. `append` pushes the first stack onto the second one, which receives the elements, so
+ its result keeps the implementation and the nodes of the second stack, as `List.append` shares its second list.
+* The `FSharp.Collections.Immutable.Option` and `FSharp.Collections.Immutable.Tuple` namespaces have `Stack` and `IStack`
+ modules that repeat the functions that use value options or struct tuples with options and reference tuples.
+* `stack { }` builds a stack whose first element is on top.
### Changed
@@ -124,6 +141,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `IndexedList.rev` builds the reversed list from a reversed copy of the elements. At 100 000 integers it takes about 3 ms
where `reverse`, which called `ImmutableList.Reverse`, took about 23 ms, and it allocates 5.2 MB where `reverse`
allocated 4.8 MB.
+* `Stack`: `ofSeq` and `ofArray` put the first element on top, so that the stack enumerates the elements in the order of
+ the input. They wrapped `ImmutableStack.CreateRange`, which puts the last element on top. A `null` input throws
+ `ArgumentNullException` for `source` in `ofSeq` and for `array` in `ofArray`, where 1.0.0 named both parameters `items`.
+ A default `FlatList` given as a sequence to `ofSeq` throws the same exception as a `null` sequence, where it threw
+ `InvalidOperationException`.
+* `Stack`: `cons`, `head` and `tail` take and return `ImmutableStack<'T>`, where they took and returned
+ `IImmutableStack<'T>`; the `IStack` module has them for the interface. They compile to `Push`, `Peek` and `Pop`, where
+ 1.0.0 compiled them under their F# names.
+* `Stack`: `head` and `tail` of an empty stack throw the `ArgumentException` of `List.head` and `List.tail`, where they
+ threw the `InvalidOperationException` of `ImmutableStack`.
+* `Stack`: the complete active pattern `Cons | Nil`, which matched a reference tuple, is replaced by the struct partial
+ active patterns `Cons`, which matches a struct tuple, and `Nil`.
### Removed
@@ -161,6 +190,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* The `IndexedSeq` module and the `IIndexedSeq<'T>` abbreviation of `IReadOnlyList<'T>`: use `IReadOnlyList<'T>`, whose
`Count` and indexer replace `IndexedSeq.length` and `IndexedSeq.item`. `IndexedSeq.check` needs no replacement, because
every function of the list modules throws `ArgumentNullException` for a `null` list.
+* `Stack.push`, `peek` and `pop`: use `cons`, `head` and `uncons`. `pop` returned the top and the stack below it as a
+ reference tuple; `uncons` returns a struct tuple.
## [1.0.0] - 2020-02-23
diff --git a/README.md b/README.md
index 239ef746..a11bc426 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,8 @@ Please also join the [F# Open Source Group](http://fsharp.github.com)
| `FlatList` | `ImmutableArray<'T>` | `flatList { }` |
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
-| `Stack` | `ImmutableStack<'T>` | – |
+| `Stack` | `ImmutableStack<'T>` | `stack { }` |
+| `IStack` | `IImmutableStack<'T>` | – |
| `Queue` | `ImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
diff --git a/docsSrc/Explanations/Background.md b/docsSrc/Explanations/Background.md
index 21f275db..880dbc42 100644
--- a/docsSrc/Explanations/Background.md
+++ b/docsSrc/Explanations/Background.md
@@ -29,7 +29,8 @@ While F# has its own built-in immutable collections, System.Collections.Immutabl
| `FlatList` | `ImmutableArray<'T>` | `flatList { }` |
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
-| `Stack` | `ImmutableStack<'T>` | – |
+| `Stack` | `ImmutableStack<'T>` | `stack { }` |
+| `IStack` | `IImmutableStack<'T>` | – |
| `Queue` | `ImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
diff --git a/docsSrc/index.md b/docsSrc/index.md
index 8fd0c476..7ab3147d 100644
--- a/docsSrc/index.md
+++ b/docsSrc/index.md
@@ -11,7 +11,8 @@ F# bindings for [System.Collections.Immutable](https://learn.microsoft.com/en-us
| `FlatList` | `ImmutableArray<'T>` | `flatList { }` |
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
-| `Stack` | `ImmutableStack<'T>` | – |
+| `Stack` | `ImmutableStack<'T>` | `stack { }` |
+| `IStack` | `IImmutableStack<'T>` | – |
| `Queue` | `ImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
diff --git a/src/FSharp.Collections.Immutable/ComputationExpressions.fs b/src/FSharp.Collections.Immutable/ComputationExpressions.fs
index 972f77ec..4ad50f4a 100644
--- a/src/FSharp.Collections.Immutable/ComputationExpressions.fs
+++ b/src/FSharp.Collections.Immutable/ComputationExpressions.fs
@@ -6,3 +6,5 @@ module FSharp.Collections.Immutable.ComputationExpressions
let flatList = FlatListCE ()
let indexedList = IndexedListCE ()
+
+let stack = StackCE ()
diff --git a/src/FSharp.Collections.Immutable/ComputationExpressions.fsi b/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
index e588c81b..dbfa0803 100644
--- a/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
+++ b/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
@@ -33,3 +33,19 @@ val flatList : FlatListCE
/// Evaluates to an indexed list of 0, 1, 4, 9, 100 and 200.
///
val indexedList : IndexedListCE
+
+///
+/// Builds a stack, with the first element of the expression on top.
+///
+///
+///
+/// let squares = stack {
+/// yield 0
+/// for i in 1..3 do
+/// yield i * i
+/// yield! [ 100; 200 ]
+/// }
+///
+/// Evaluates to a stack of 0, 1, 4, 9, 100 and 200, with 0 on top.
+///
+val stack : StackCE
diff --git a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
index f66316ea..73aa6463 100644
--- a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
+++ b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
@@ -40,6 +40,7 @@
+
@@ -68,7 +69,21 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/FSharp.Collections.Immutable/Helper/StackConstruction.fs b/src/FSharp.Collections.Immutable/Helper/StackConstruction.fs
new file mode 100644
index 00000000..90fdf63d
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Helper/StackConstruction.fs
@@ -0,0 +1,46 @@
+///
+/// The construction of the stacks that the stack modules and the stack { } expression return.
+///
+///
+///
+/// pushes the elements in order and leaves the last element on top. The stack modules keep the first element on top
+/// instead, so that a stack enumerates its elements in the order in which it was created (ADR 0001, D5). The module serves
+/// the stack modules, their Tuple counterparts and the computation expression, so that they build their stacks with
+/// one implementation.
+///
+module internal FSharp.Collections.Immutable.StackConstruction
+
+open System.Collections.Immutable
+
+///
+/// Pushes the elements of an array onto a stack, from the last element to the first one.
+///
+/// The elements; neither checked for nor kept.
+///
+/// The stack to push the elements onto, an or an
+/// .
+///
+/// A stack of the elements of the array, the first one on top, followed by the elements of the stack.
+///
+/// O(m), where m is the length of the array. The result shares the nodes of the stack and keeps its implementation.
+///
+let inline pushArray (items : 'T array) (stack : ^Stack) : ^Stack when ^Stack : (member Push : 'T -> ^Stack) =
+ // The constraint serves the concrete stack and the interface with one loop: each Push returns the type it is called
+ // on, so the result keeps the implementation of the stack
+ let mutable result = stack
+
+ // Pushing from the last element leaves the first one on top, so that the result enumerates the array in its order
+ for index = items.Length - 1 downto 0 do
+ result <- (^Stack : (member Push : 'T -> ^Stack) (result, items[index]))
+
+ result
+
+///
+/// A stack whose top is the first element of an array, which enumerates the elements in the order of the array.
+///
+/// The elements; neither checked for nor kept.
+/// The stack of the elements.
+///
+/// O(n). The function pushes the elements onto the empty stack from the last one to the first one.
+///
+let ofArrayUnchecked (array : 'T array) : ImmutableStack<'T> = pushArray array ImmutableStack<'T>.Empty
diff --git a/src/FSharp.Collections.Immutable/Stack.fs b/src/FSharp.Collections.Immutable/Stack.fs
deleted file mode 100644
index 774ad6b5..00000000
--- a/src/FSharp.Collections.Immutable/Stack.fs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace FSharp.Collections.Immutable
-
-type IStack<'T> = System.Collections.Immutable.IImmutableStack<'T>
-
-type Stack<'T> = System.Collections.Immutable.ImmutableStack<'T>
-
-[]
-module Stack =
- type internal StackFactory = System.Collections.Immutable.ImmutableStack
-
- let inline internal check (stack : IStack<_>) = checkNotNull (nameof stack) stack
-
- let inline empty<'T> = StackFactory.Create<'T>()
-
- let inline ofSeq source = StackFactory.CreateRange source
-
- let inline ofArray (array : 'T[]) : Stack<'T> = ofSeq array
-
- let inline toSeq (stack : IStack<_>) = stack :> seq<_>
-
- let push head stack : IStack<'T> =
- check stack
- stack.Push head
-
- let cons head stack = push head stack
-
- let peek stack =
- check stack
- stack.Peek ()
-
- let head stack = peek stack
-
- let tail stack : IStack<_> =
- check stack
- stack.Pop ()
-
- let pop stack =
- check stack
- stack.Peek (), tail stack
-
- let (|Cons|Nil|) stack =
- check stack
-
- if stack.IsEmpty then
- Nil
- else
- Cons (stack.Peek (), stack.Pop ())
-
-/////////////
-module ImmutableStack = Stack
diff --git a/src/FSharp.Collections.Immutable/Stack/IStack.fs b/src/FSharp.Collections.Immutable/Stack/IStack.fs
new file mode 100644
index 00000000..ec72f51e
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Stack/IStack.fs
@@ -0,0 +1,991 @@
+[]
+module FSharp.Collections.Immutable.IStack
+
+// System.Collections.Generic stays closed: its Stack<'T> would shadow the Stack<'T> abbreviation of this library
+open System
+open System.Collections.Immutable
+open System.Linq
+open FSharp.Collections.Immutable.StackConstruction
+
+// The interface has the members Clear, Push, Peek and Pop of the stacks. A result with the element type of the input keeps
+// the implementation of the input: it comes from Pop, or from Clear followed by Push from the last element of a snapshot to
+// the first one. A result of another element type is an ImmutableStack in the same order. The checks of ListChecks throw
+// what the List functions throw, with the names of the parameters of this module (ADR 0001, D5 and D6); Lengths.stackUpTo
+// counts a stack only as far as a check needs it.
+
+/// A stack of the implementation of the input with the given elements, the first one on top.
+let private rebuild (stack : IStack<'T>) (items : 'T array) = pushArray items (stack.Clear ())
+
+/// The stack below the first elements; the caller checks that the stack has them.
+let private dropTop (count : int) (stack : IStack<'T>) =
+ let mutable node = stack
+
+ for _ in 1..count do
+ node <- node.Pop ()
+
+ node
+
+/// The first elements in an array and the stack below them; the caller checks that the stack has them.
+let private splitTop (count : int) (stack : IStack<'T>) =
+ let top = Array.zeroCreate count
+ let mutable node = stack
+
+ for index = 0 to count - 1 do
+ top[index] <- node.Peek ()
+ node <- node.Pop ()
+
+ struct (top, node)
+
+/// The index and the first element that satisfies the predicate, or ValueNone.
+let private firstMatch (predicate : 'T -> bool) (stack : IStack<'T>) =
+ use enumerator = stack.GetEnumerator ()
+ let mutable result = ValueNone
+ let mutable index = 0
+
+ while result.IsNone && enumerator.MoveNext () do
+ let item = enumerator.Current
+
+ if predicate item then
+ result <- ValueSome (struct (index, item))
+
+ index <- index + 1
+
+ result
+
+/// The index and the last element that satisfies the predicate, or ValueNone; List searches a copy from the end too.
+let private lastMatch (predicate : 'T -> bool) (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ let mutable index = array.Length - 1
+
+ while index >= 0 && not (predicate array[index]) do
+ index <- index - 1
+
+ if index < 0 then
+ ValueNone
+ else
+ ValueSome (struct (index, array[index]))
+
+/// The first value that the chooser returns for the elements, in their order.
+let private firstChoice (chooser : 'T -> 'U voption) (stack : IStack<'T>) =
+ use enumerator = stack.GetEnumerator ()
+ let mutable result = ValueNone
+
+ while result.IsNone && enumerator.MoveNext () do
+ result <- chooser enumerator.Current
+
+ result
+
+// ---- Properties ----
+
+[]
+let isEmpty (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ stack.IsEmpty
+
+[]
+let length (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // The interface does not tell the length
+ Lengths.stackUpTo Int32.MaxValue stack
+
+// ---- Access ----
+
+[]
+let exactlyOne (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // List.exactlyOne reports its input under the name "source"; two elements tell a longer stack apart
+ ListChecks.exactlyOne "source" (Lengths.stackUpTo 2 stack)
+ stack.Peek ()
+
+[]
+let head (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmpty (nameof stack) (Lengths.stackUpTo 1 stack)
+ stack.Peek ()
+
+[]
+let last (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmpty (nameof stack) (Lengths.stackUpTo 1 stack)
+ Enumerable.Last stack
+
+[]
+let tail (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmpty (nameof stack) (Lengths.stackUpTo 1 stack)
+ stack.Pop ()
+
+[]
+let tryExactlyOne (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack = 1 then
+ ValueSome (stack.Peek ())
+ else
+ ValueNone
+
+[]
+let tryHead (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ if stack.IsEmpty then
+ ValueNone
+ else
+ ValueSome (stack.Peek ())
+
+[]
+let tryLast (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if stack.IsEmpty then
+ ValueNone
+ else
+ ValueSome (Enumerable.Last stack)
+
+[]
+let tryTail (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ if stack.IsEmpty then
+ ValueNone
+ else
+ ValueSome (stack.Pop ())
+
+[]
+let tryUncons (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if stack.IsEmpty then
+ ValueNone
+ else
+ ValueSome (struct (stack.Peek (), stack.Pop ()))
+
+[]
+let uncons (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // An empty stack throws what List.head throws
+ ListChecks.notEmpty (nameof stack) (Lengths.stackUpTo 1 stack)
+ struct (stack.Peek (), stack.Pop ())
+
+[]
+[]
+let (|Cons|_|) (stack : IStack<'T>) = tryUncons stack
+
+[]
+let (|Nil|_|) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ stack.IsEmpty
+
+// ---- Search ----
+
+[]
+let contains (value : 'T) (source : IStack<'T>) =
+ checkNotNull (nameof source) source
+ // F# equality with the value on the left, as List.contains compares
+ (firstMatch (fun item -> value = item) source).IsSome
+
+[]
+let exists (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ (firstMatch predicate stack).IsSome
+
+[]
+let exists2 predicate (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.exists2 (nameof stack2) predicate (Snapshot.ofSeq (nameof stack1) stack1) (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let find (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ match firstMatch predicate stack with
+ | ValueSome (struct (_, item)) -> item
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findBack (predicate : 'T -> bool) (stack : IStack<'T>) =
+ match lastMatch predicate stack with
+ | ValueSome (struct (_, item)) -> item
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findIndex (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ match firstMatch predicate stack with
+ | ValueSome (struct (index, _)) -> index
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findIndexBack (predicate : 'T -> bool) (stack : IStack<'T>) =
+ match lastMatch predicate stack with
+ | ValueSome (struct (index, _)) -> index
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let forall (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ (firstMatch (fun item -> not (predicate item)) stack).IsNone
+
+[]
+let forall2 predicate (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.forall2
+ (nameof stack1)
+ (nameof stack2)
+ predicate
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let pick (chooser : 'T -> 'U voption) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ match firstChoice chooser stack with
+ | ValueSome value -> value
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let tryFind (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ match firstMatch predicate stack with
+ | ValueSome (struct (_, item)) -> ValueSome item
+ | ValueNone -> ValueNone
+
+[]
+let tryFindBack (predicate : 'T -> bool) (stack : IStack<'T>) =
+ match lastMatch predicate stack with
+ | ValueSome (struct (_, item)) -> ValueSome item
+ | ValueNone -> ValueNone
+
+[]
+let tryFindIndex (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ match firstMatch predicate stack with
+ | ValueSome (struct (index, _)) -> ValueSome index
+ | ValueNone -> ValueNone
+
+[]
+let tryFindIndexBack (predicate : 'T -> bool) (stack : IStack<'T>) =
+ match lastMatch predicate stack with
+ | ValueSome (struct (index, _)) -> ValueSome index
+ | ValueNone -> ValueNone
+
+[]
+let tryPick (chooser : 'T -> 'U voption) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ firstChoice chooser stack
+
+// ---- Transformation ----
+
+[]
+let allPairs (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ Arrays.allPairs structPair (Snapshot.ofSeq (nameof stack1) stack1) (Snapshot.ofSeq (nameof stack2) stack2)
+ |> ofArrayUnchecked
+
+[]
+let append (stack1 : IStack<'T>) (stack2 : IStack<'T>) =
+ checkNotNull (nameof stack1) stack1
+ checkNotNull (nameof stack2) stack2
+
+ if stack1.IsEmpty then
+ stack2
+ else
+ // The elements of the first stack go on top of the second one, whose implementation and nodes the result keeps
+ pushArray (Snapshot.ofSeq (nameof stack1) stack1) stack2
+
+[]
+let choose (chooser : 'T -> 'U voption) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let chosen = ResizeArray<'U>()
+
+ for item in stack do
+ match chooser item with
+ | ValueSome value -> chosen.Add value
+ | ValueNone -> ()
+
+ ofArrayUnchecked (chosen.ToArray ())
+
+[]
+let chunkBySize chunkSize (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.positive (nameof chunkSize) chunkSize
+
+ Array.chunkBySize chunkSize (Snapshot.ofSeq (nameof stack) stack)
+ |> Array.map ofArrayUnchecked
+ |> ofArrayUnchecked
+
+[]
+let collect (mapping : 'T -> IStack<'U>) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let collected = ResizeArray<'U>()
+
+ for item in stack do
+ collected.AddRange (mapping item)
+
+ ofArrayUnchecked (collected.ToArray ())
+
+[]
+let concat (stacks : seq>) =
+ checkNotNullOrDefaultSeq (nameof stacks) stacks
+ let concatenated = ResizeArray<'T>()
+
+ for stack in stacks do
+ checkNotNull (nameof stacks) stack
+ concatenated.AddRange stack
+
+ ofArrayUnchecked (concatenated.ToArray ())
+
+[]
+let cons (value : 'T) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ stack.Push value
+
+[]
+let consMany (values : seq<'T>) (stack : IStack<'T>) =
+ checkNotNullOrDefaultSeq (nameof values) values
+ checkNotNull (nameof stack) stack
+ pushArray (Snapshot.ofSeq (nameof values) values) stack
+
+[]
+let filter (predicate : 'T -> bool) (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ rebuild stack (Array.filter predicate array)
+
+[]
+let indexed (stack : IStack<'T>) =
+ Arrays.indexed structPair (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let map (mapping : 'T -> 'U) (stack : IStack<'T>) =
+ Array.map mapping (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let map2 (mapping : 'T1 -> 'T2 -> 'U) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.map2
+ (nameof stack1)
+ (nameof stack2)
+ mapping
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+ |> ofArrayUnchecked
+
+[]
+let map3 (mapping : 'T1 -> 'T2 -> 'T3 -> 'U) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) (stack3 : IStack<'T3>) =
+ ListArrays.map3
+ (nameof stack1)
+ (nameof stack2)
+ (nameof stack3)
+ mapping
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+ (Snapshot.ofSeq (nameof stack3) stack3)
+ |> ofArrayUnchecked
+
+[]
+let mapi (mapping : int -> 'T -> 'U) (stack : IStack<'T>) =
+ Array.mapi mapping (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let mapi2 (mapping : int -> 'T1 -> 'T2 -> 'U) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.mapi2
+ (nameof stack1)
+ (nameof stack2)
+ mapping
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+ |> ofArrayUnchecked
+
+[]
+let pairwise (stack : IStack<'T>) =
+ Arrays.pairwise structPair (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let permute indexMap (stack : IStack<'T>) =
+ // List.permute applies the algorithm of Array.permute to a copy of the list, with the same checks
+ let array = Snapshot.ofSeq (nameof stack) stack
+ rebuild stack (Array.permute indexMap array)
+
+[]
+let rev (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ // Pushing the elements in their order leaves the last one on top
+ let mutable reversed = stack.Clear ()
+
+ for item in stack do
+ reversed <- reversed.Push item
+
+ reversed
+
+[]
+let transpose (stacks : seq>) =
+ checkNotNullOrDefaultSeq (nameof stacks) stacks
+
+ let arrays =
+ stacks
+ |> Seq.map (fun stack -> Snapshot.ofSeq (nameof stacks) stack)
+ |> Seq.toArray
+
+ // List.transpose names the lists "list.[0]", "list.[1]", …, which are the stacks "stack.[0]", "stack.[1]", … here;
+ // once their lengths agree, Array.transpose agrees with it
+ ListChecks.transpose "stack" (arrays |> Array.map _.Length)
+
+ Array.transpose arrays
+ |> Array.map ofArrayUnchecked
+ |> ofArrayUnchecked
+
+[]
+let unzip (stack : IStack) =
+ let struct (first, second) = Arrays.unzip id (Snapshot.ofSeq (nameof stack) stack)
+ struct (ofArrayUnchecked first, ofArrayUnchecked second)
+
+[]
+let unzip3 (stack : IStack) =
+ let struct (first, second, third) = Arrays.unzip3 id (Snapshot.ofSeq (nameof stack) stack)
+ struct (ofArrayUnchecked first, ofArrayUnchecked second, ofArrayUnchecked third)
+
+[]
+let where (predicate : 'T -> bool) (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ rebuild stack (Array.filter predicate array)
+
+[]
+let windowed windowSize (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.positive (nameof windowSize) windowSize
+
+ Array.windowed windowSize (Snapshot.ofSeq (nameof stack) stack)
+ |> Array.map ofArrayUnchecked
+ |> ofArrayUnchecked
+
+[]
+let zip (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ let array1 = Snapshot.ofSeq (nameof stack1) stack1
+ let array2 = Snapshot.ofSeq (nameof stack2) stack2
+ ListChecks.sameLengths (nameof stack1) array1.Length (nameof stack2) array2.Length
+ Arrays.zip structPair array1 array2 |> ofArrayUnchecked
+
+[]
+let zip3 (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) (stack3 : IStack<'T3>) =
+ let array1 = Snapshot.ofSeq (nameof stack1) stack1
+ let array2 = Snapshot.ofSeq (nameof stack2) stack2
+ let array3 = Snapshot.ofSeq (nameof stack3) stack3
+ ListChecks.zip3 (nameof stack1) array1.Length (nameof stack2) array2.Length (nameof stack3) array3.Length
+
+ Arrays.zip3 structTriple array1 array2 array3
+ |> ofArrayUnchecked
+
+// ---- Folding ----
+
+// The inline functions can use public members only, so they check the stack themselves; the empty stack throws what
+// List.average and List.averageBy throw, with the name "source"
+[]
+let inline average (stack : IStack< ^T >) : ^T =
+ if obj.ReferenceEquals (stack, null) then
+ nullArg (nameof stack)
+
+ if stack.IsEmpty then
+ invalidArg "source" ErrorStrings.InputSequenceEmpty
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^T>
+ let mutable count = 0
+
+ for item in stack do
+ sum <- Checked.(+) sum item
+ count <- count + 1
+
+ LanguagePrimitives.DivideByInt< ^T> sum count
+
+[]
+let inline averageBy ([] projection : 'T -> ^U) (stack : IStack<'T>) : ^U =
+ if obj.ReferenceEquals (stack, null) then
+ nullArg (nameof stack)
+
+ if stack.IsEmpty then
+ invalidArg "source" ErrorStrings.InputSequenceEmpty
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^U>
+ let mutable count = 0
+
+ for item in stack do
+ sum <- Checked.(+) sum (projection item)
+ count <- count + 1
+
+ LanguagePrimitives.DivideByInt< ^U> sum count
+
+[]
+let compareWith (comparer : 'T -> 'T -> int) (stack1 : IStack<'T>) (stack2 : IStack<'T>) =
+ // Array.compareWith compares as List.compareWith does: the first difference, then the lengths
+ Array.compareWith comparer (Snapshot.ofSeq (nameof stack1) stack1) (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let fold (folder : 'State -> 'T -> 'State) (state : 'State) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let mutable state = state
+
+ for item in stack do
+ state <- folder state item
+
+ state
+
+[]
+let fold2 (folder : 'State -> 'T1 -> 'T2 -> 'State) (state : 'State) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.fold2
+ (nameof stack1)
+ (nameof stack2)
+ folder
+ state
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let foldBack (folder : 'T -> 'State -> 'State) (stack : IStack<'T>) (state : 'State) =
+ Array.foldBack folder (Snapshot.ofSeq (nameof stack) stack) state
+
+[]
+let foldBack2 (folder : 'T1 -> 'T2 -> 'State -> 'State) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) (state : 'State) =
+ let array1 = Snapshot.ofSeq (nameof stack1) stack1
+ let array2 = Snapshot.ofSeq (nameof stack2) stack2
+ // List.foldBack2 checks the lengths before it calls the folder
+ ListChecks.foldBack2 (nameof stack1) array1.Length (nameof stack2) array2.Length
+ Array.foldBack2 folder array1 array2 state
+
+[]
+let iter (action : 'T -> unit) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ for item in stack do
+ action item
+
+[]
+let iter2 (action : 'T1 -> 'T2 -> unit) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.iter2
+ (nameof stack1)
+ (nameof stack2)
+ action
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let iteri (action : int -> 'T -> unit) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let mutable index = 0
+
+ for item in stack do
+ action index item
+ index <- index + 1
+
+[]
+let iteri2 (action : int -> 'T1 -> 'T2 -> unit) (stack1 : IStack<'T1>) (stack2 : IStack<'T2>) =
+ ListArrays.iteri2
+ (nameof stack1)
+ (nameof stack2)
+ action
+ (Snapshot.ofSeq (nameof stack1) stack1)
+ (Snapshot.ofSeq (nameof stack2) stack2)
+
+[]
+let mapFold (mapping : 'State -> 'T -> struct ('Result * 'State)) (state : 'State) (stack : IStack<'T>) =
+ let source = Snapshot.ofSeq (nameof stack) stack
+ let results = Array.zeroCreate source.Length
+ let mutable state = state
+
+ for index = 0 to source.Length - 1 do
+ let struct (result, next) = mapping state source[index]
+ results[index] <- result
+ state <- next
+
+ struct (ofArrayUnchecked results, state)
+
+[]
+let mapFoldBack (mapping : 'T -> 'State -> struct ('Result * 'State)) (stack : IStack<'T>) (state : 'State) =
+ let source = Snapshot.ofSeq (nameof stack) stack
+ let results = Array.zeroCreate source.Length
+ let mutable state = state
+
+ for index = source.Length - 1 downto 0 do
+ let struct (result, next) = mapping source[index] state
+ results[index] <- result
+ state <- next
+
+ struct (ofArrayUnchecked results, state)
+
+// Seq.max and the others compare as the List functions do once the stack has elements, so the List checks come first
+
+[]
+let max (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmptySequence (nameof stack) (Lengths.stackUpTo 1 stack)
+ Seq.max stack
+
+[]
+let maxBy (projection : 'T -> 'U) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmptySequence (nameof stack) (Lengths.stackUpTo 1 stack)
+ Seq.maxBy projection stack
+
+[]
+let min (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmptySequence (nameof stack) (Lengths.stackUpTo 1 stack)
+ Seq.min stack
+
+[]
+let minBy (projection : 'T -> 'U) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmptySequence (nameof stack) (Lengths.stackUpTo 1 stack)
+ Seq.minBy projection stack
+
+[]
+let reduce (reduction : 'T -> 'T -> 'T) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.notEmpty (nameof stack) (Lengths.stackUpTo 1 stack)
+ Seq.reduce reduction stack
+
+[]
+let reduceBack (reduction : 'T -> 'T -> 'T) (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ ListChecks.notEmpty (nameof stack) array.Length
+ Array.reduceBack reduction array
+
+[]
+let scan (folder : 'State -> 'T -> 'State) (state : 'State) (stack : IStack<'T>) =
+ Array.scan folder state (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let scanBack (folder : 'T -> 'State -> 'State) (stack : IStack<'T>) (state : 'State) =
+ Array.scanBack folder (Snapshot.ofSeq (nameof stack) stack) state
+ |> ofArrayUnchecked
+
+[]
+let inline sum (stack : IStack< ^T >) : ^T =
+ if obj.ReferenceEquals (stack, null) then
+ nullArg (nameof stack)
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^T>
+
+ for item in stack do
+ sum <- Checked.(+) sum item
+
+ sum
+
+[]
+let inline sumBy ([] projection : 'T -> ^U) (stack : IStack<'T>) : ^U =
+ if obj.ReferenceEquals (stack, null) then
+ nullArg (nameof stack)
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^U>
+
+ for item in stack do
+ sum <- Checked.(+) sum (projection item)
+
+ sum
+
+// ---- Partitioning and grouping ----
+
+[]
+let countBy (projection : 'T -> 'Key) (stack : IStack<'T>) =
+ Arrays.countBy structPair projection (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let distinct (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ rebuild stack (Array.distinct array)
+
+[]
+let distinctBy (projection : 'T -> 'Key) (stack : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof stack) stack
+ rebuild stack (Array.distinctBy projection array)
+
+[]
+let except (itemsToExclude : seq<'T>) (stack : IStack<'T>) =
+ // List.except checks the items to exclude first and does not read them for an empty list
+ checkNotNullOrDefaultSeq (nameof itemsToExclude) itemsToExclude
+ checkNotNull (nameof stack) stack
+
+ if stack.IsEmpty then
+ stack
+ else
+ rebuild stack (Array.except itemsToExclude (Snapshot.ofSeq (nameof stack) stack))
+
+[]
+let groupBy (projection : 'T -> 'Key) (stack : IStack<'T>) =
+ Arrays.groupBy (fun key items -> struct (key, ofArrayUnchecked items)) projection (Snapshot.ofSeq (nameof stack) stack)
+ |> ofArrayUnchecked
+
+[]
+let partition (predicate : 'T -> bool) (stack : IStack<'T>) =
+ let accepted, rejected = Array.partition predicate (Snapshot.ofSeq (nameof stack) stack)
+ struct (rebuild stack accepted, rebuild stack rejected)
+
+[]
+let partitionWith (partitioner : 'T -> Choice<'T1, 'T2>) (stack : IStack<'T>) =
+ let first, second = Array.partitionWith partitioner (Snapshot.ofSeq (nameof stack) stack)
+ struct (ofArrayUnchecked first, ofArrayUnchecked second)
+
+[]
+let skip count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // A stack shorter than the count stops the counting at its length, which the exception reports
+ ListChecks.skip (nameof count) count (Lengths.stackUpTo count stack)
+ dropTop count stack
+
+[]
+let skipLast count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if count <= 0 then
+ stack
+ else
+ let length = Lengths.stackUpTo Int32.MaxValue stack
+ // The checks of List.skip
+ ListChecks.skip (nameof count) count length
+ let struct (top, _) = splitTop (length - count) stack
+ rebuild stack top
+
+[]
+let skipWhile (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let mutable node = stack
+
+ while not node.IsEmpty && predicate (node.Peek ()) do
+ node <- node.Pop ()
+
+ node
+
+[]
+let splitAt index (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // A stack shorter than the index stops the counting at its length, which the exception reports
+ ListChecks.splitAt (nameof index) index (Lengths.stackUpTo index stack)
+ let struct (top, rest) = splitTop index stack
+ struct ((if rest.IsEmpty then stack else rebuild stack top), rest)
+
+[]
+let splitInto count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ ListChecks.positive (nameof count) count
+
+ Array.splitInto count (Snapshot.ofSeq (nameof stack) stack)
+ |> Array.map ofArrayUnchecked
+ |> ofArrayUnchecked
+
+[]
+let take count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ // A stack shorter than the count stops the counting at its length, which the exception reports
+ ListChecks.take (nameof count) count (Lengths.stackUpTo count stack)
+ let struct (top, rest) = splitTop count stack
+ if rest.IsEmpty then stack else rebuild stack top
+
+[]
+let takeLast count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let length = Lengths.stackUpTo Int32.MaxValue stack
+ // The checks of List.take
+ ListChecks.take (nameof count) count length
+ dropTop (length - count) stack
+
+[]
+let takeWhile (predicate : 'T -> bool) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let taken = ResizeArray<'T>()
+ let mutable node = stack
+ let mutable go = true
+
+ while go && not node.IsEmpty do
+ let item = node.Peek ()
+
+ if predicate item then
+ taken.Add item
+ node <- node.Pop ()
+ else
+ go <- false
+
+ if node.IsEmpty then
+ stack
+ else
+ rebuild stack (taken.ToArray ())
+
+[]
+let truncate count (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ let struct (top, rest) = splitTop (Lengths.stackUpTo count stack) stack
+ if rest.IsEmpty then stack else rebuild stack top
+
+// ---- Sorting ----
+
+// The sorts are stable, as the sorts of List are (ADR 0001, D5). The functions that compare the elements, or that call the
+// projection in the comparison as List.sortByDescending does, sort with the Seq functions of FSharp.Core, which share the
+// stable sort of List: the same comparisons run in Array.Sort, which wraps the exception of a comparison in an
+// InvalidOperationException (D6). Seq.sortDescending and Seq.sortByDescending are inline and box the elements in every
+// comparison once they are compiled into a generic function, so the two descending sorts call Seq.sortWith through
+// DescendingSorts, with the cached comparer. sortBy and sortWithComparer sort with LINQ, which computes the keys first, as
+// List.sortBy does, and wraps the exception of a comparison as Array.Sort does. The sorted elements go to an array, which
+// the stack of the implementation of the input is pushed from. A stack of one element is returned without a call of the
+// projection or the comparer, as List returns it.
+
+[]
+let sort (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (Enumerable.ToArray (Seq.sort stack))
+
+[]
+let sortBy (projection : 'T -> 'Key) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (stack.OrderBy(Func<'T, 'Key> projection, DefaultComparer<'Key>.Instance).ToArray())
+
+[]
+let sortByDescending (projection : 'T -> 'Key) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (Enumerable.ToArray (DescendingSorts.sortByDescending projection stack))
+
+[]
+let sortDescending (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (Enumerable.ToArray (DescendingSorts.sortDescending stack))
+
+[]
+let sortWith (comparer : 'T -> 'T -> int) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (Enumerable.ToArray (Seq.sortWith comparer stack))
+
+[]
+let sortWithComparer (comparer : Collections.Generic.IComparer<'T>) (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+
+ if Lengths.stackUpTo 2 stack <= 1 then
+ stack
+ else
+ rebuild stack (stack.Order(comparer).ToArray())
+
+// ---- Random ----
+
+// The functions draw the random numbers as the List functions do, from a snapshot after the List checks: the Array
+// functions throw other messages for an empty array
+
+[]
+let randomChoice (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.notEmptySequence (nameof source) array.Length
+ array[Random.Shared.Next (0, array.Length)]
+
+[]
+let randomChoiceBy (randomizer : unit -> float) (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.notEmptySequence (nameof source) array.Length
+ array[ListChecks.randomIndex (nameof randomizer) randomizer array.Length]
+
+[]
+let randomChoiceWith (random : Random) (source : IStack<'T>) =
+ checkNotNull (nameof random) random
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.notEmptySequence (nameof source) array.Length
+ array[random.Next (0, array.Length)]
+
+[]
+let randomChoices count (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomChoices (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomChoices count array)
+
+[]
+let randomChoicesBy (randomizer : unit -> float) count (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomChoices (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomChoicesBy randomizer count array)
+
+[]
+let randomChoicesWith (random : Random) count (source : IStack<'T>) =
+ checkNotNull (nameof random) random
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomChoices (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomChoicesWith random count array)
+
+[]
+let randomSample count (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomSample (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomSample count array)
+
+[]
+let randomSampleBy (randomizer : unit -> float) count (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomSample (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomSampleBy randomizer count array)
+
+[]
+let randomSampleWith (random : Random) count (source : IStack<'T>) =
+ checkNotNull (nameof random) random
+ let array = Snapshot.ofSeq (nameof source) source
+ ListChecks.randomSample (nameof count) count (nameof source) array.Length
+ rebuild source (Array.randomSampleWith random count array)
+
+[]
+let randomShuffle (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlace array
+ rebuild source array
+
+[]
+let randomShuffleBy (randomizer : unit -> float) (source : IStack<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlaceBy randomizer array
+ rebuild source array
+
+[]
+let randomShuffleWith (random : Random) (source : IStack<'T>) =
+ checkNotNull (nameof random) random
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlaceWith random array
+ rebuild source array
+
+// ---- Conversion ----
+
+[]
+let toArray (stack : IStack<'T>) = Snapshot.ofSeq (nameof stack) stack
+
+[]
+let toList (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ List.ofSeq stack
+
+[]
+let toSeq (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ stack :> seq<'T>
+
+// ---- Type-specific ----
+
+[]
+let clear (stack : IStack<'T>) =
+ checkNotNull (nameof stack) stack
+ stack.Clear ()
diff --git a/src/FSharp.Collections.Immutable/Stack/IStack.fsi b/src/FSharp.Collections.Immutable/Stack/IStack.fsi
new file mode 100644
index 00000000..e6483af7
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Stack/IStack.fsi
@@ -0,0 +1,1439 @@
+///
+/// Functions for , the interface of the immutable stacks.
+///
+///
+///
+/// The module mirrors as
+/// does: each function has the semantics, the argument checks and the
+/// exceptions of the function with the same name in that module (ADR 0001), its parameters and exceptions have the names
+/// of the stack, and its sorting functions are stable. A stack enumerates its elements from the top to the bottom, and
+/// every function treats it as a list in that order. The module has no creation functions, because an interface cannot
+/// create a stack.
+///
+///
+/// A function whose result has the element type of its input keeps the implementation of the input: it builds the result
+/// with the members of the input, such as followed by
+/// from the last element to the first one, or
+/// . A function that changes the element type, or that
+/// combines several stacks into one, returns a , except
+/// :
+/// it pushes the first stack onto the second one, so its result has the implementation of the second stack.
+///
+///
+/// Every function throws for a stack. A function that
+/// takes a sequence throws it for a sequence and for a default
+/// that is given as a sequence, which would fail with
+/// when the function enumerates it. The complexities in the remarks are
+/// the ones of an .
+///
+///
+/// Functions that return an optional value return a , and functions
+/// that return tuples return struct tuples. The namespaces and
+/// contain a module with the same name whose functions use options
+/// and reference tuples instead.
+///
+///
+[]
+module FSharp.Collections.Immutable.IStack
+
+// System.Collections.Generic stays closed: its Stack<'T> would shadow the Stack<'T> abbreviation of this library
+open System
+open System.Collections.Immutable
+
+// ---- Properties ----
+
+///
+/// Returns whether the stack has no elements.
+///
+/// The input stack.
+/// when the stack is empty.
+///
+/// O(1). The function wraps .
+///
+[]
+val isEmpty : stack : IStack<'T> -> bool
+
+///
+/// Returns the number of elements of the stack.
+///
+/// The input stack.
+/// The length of the stack.
+///
+/// O(n): the interface does not tell the length, so the function counts the elements.
+///
+[]
+val length : stack : IStack<'T> -> int
+
+// ---- Access ----
+
+/// Returns the only element of the stack.
+/// The input stack.
+/// The only element.
+/// Thrown when the stack does not have exactly one element.
+/// O(1).
+[]
+val exactlyOne : stack : IStack<'T> -> 'T
+
+///
+/// Returns the first element of the stack, the element on top.
+///
+/// The input stack.
+/// The first element.
+/// Thrown when the stack is empty.
+///
+/// O(1). The function wraps .
+///
+[]
+val head : stack : IStack<'T> -> 'T
+
+///
+/// Returns the last element of the stack, the element at the bottom.
+///
+/// The input stack.
+/// The last element.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val last : stack : IStack<'T> -> 'T
+
+///
+/// Returns the stack without its first element: the stack below the top.
+///
+/// The input stack.
+/// The elements after the first one, in a stack of the implementation of the input.
+/// Thrown when the stack is empty.
+///
+/// O(1). The function wraps .
+///
+[]
+val tail : stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the only element of the stack, or ValueNone when it does not have exactly one element.
+///
+/// The input stack.
+/// The only element, or ValueNone.
+///
+/// O(1).
+///
+[]
+val tryExactlyOne : stack : IStack<'T> -> 'T voption
+
+///
+/// Returns the first element of the stack, or ValueNone when it is empty.
+///
+/// The input stack.
+/// The first element, or ValueNone.
+///
+/// O(1).
+///
+[]
+val tryHead : stack : IStack<'T> -> 'T voption
+
+///
+/// Returns the last element of the stack, or ValueNone when it is empty.
+///
+/// The input stack.
+/// The last element, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryLast : stack : IStack<'T> -> 'T voption
+
+///
+/// Returns the stack without its first element, or ValueNone when it is empty.
+///
+/// The input stack.
+/// The elements after the first one, in a stack of the implementation of the input, or ValueNone.
+///
+/// O(1).
+///
+[]
+val tryTail : stack : IStack<'T> -> IStack<'T> voption
+
+///
+/// Returns the first element and the other elements, or ValueNone when the stack is empty.
+///
+/// The input stack.
+/// The first element and the elements after it, or ValueNone.
+///
+/// O(1).
+///
+[]
+val tryUncons : stack : IStack<'T> -> struct ('T * IStack<'T>) voption
+
+///
+/// Returns the first element and the other elements.
+///
+/// The input stack.
+/// The first element and the elements after it, in a stack of the implementation of the input.
+/// Thrown when the stack is empty.
+///
+/// O(1). The function throws what
+/// throws.
+///
+[]
+val uncons : stack : IStack<'T> -> struct ('T * IStack<'T>)
+
+///
+/// Matches a stack with a first element, and returns that element and the other elements.
+///
+/// The input stack.
+/// The first element and the elements after it, or ValueNone for an empty stack.
+///
+/// O(1). A struct partial pattern, so that a match allocates no option.
+///
+[]
+[]
+val (|Cons|_|) : stack : IStack<'T> -> struct ('T * IStack<'T>) voption
+
+/// Matches an empty stack.
+/// The input stack.
+/// when the stack is empty.
+/// O(1).
+[]
+val (|Nil|_|) : stack : IStack<'T> -> bool
+
+// ---- Search ----
+
+///
+/// Returns whether the stack contains an element equal to a value, in F# structural equality.
+///
+/// The value to find.
+/// The input stack.
+/// when an element equals the value.
+///
+/// O(n).
+///
+[]
+val contains : value : 'T -> source : IStack<'T> -> bool when 'T : equality
+
+/// Returns whether any element satisfies the predicate.
+/// The function to test the elements.
+/// The input stack.
+/// when an element satisfies the predicate.
+/// O(n).
+[]
+val exists : predicate : ('T -> bool) -> stack : IStack<'T> -> bool
+
+///
+/// Returns whether any pair of elements at the same index satisfies the predicate.
+///
+/// The function to test the pairs.
+/// The first stack.
+/// The second stack.
+/// when a pair satisfies the predicate.
+/// Thrown when no pair satisfies the predicate and the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val exists2 : predicate : ('T1 -> 'T2 -> bool) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> bool
+
+/// Returns the first element that satisfies the predicate.
+/// The function to test the elements.
+/// The input stack.
+/// The first element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+/// O(n).
+[]
+val find : predicate : ('T -> bool) -> stack : IStack<'T> -> 'T
+
+/// Returns the last element that satisfies the predicate.
+/// The function to test the elements.
+/// The input stack.
+/// The last element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+/// O(n).
+[]
+val findBack : predicate : ('T -> bool) -> stack : IStack<'T> -> 'T
+
+///
+/// Returns the index of the first element that satisfies the predicate.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The index of the first element that satisfies the predicate; the top has the index 0.
+/// Thrown when no element satisfies the predicate.
+///
+/// O(n).
+///
+[]
+val findIndex : predicate : ('T -> bool) -> stack : IStack<'T> -> int
+
+///
+/// Returns the index of the last element that satisfies the predicate.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The index of the last element that satisfies the predicate; the top has the index 0.
+/// Thrown when no element satisfies the predicate.
+///
+/// O(n).
+///
+[]
+val findIndexBack : predicate : ('T -> bool) -> stack : IStack<'T> -> int
+
+/// Returns whether all elements satisfy the predicate.
+/// The function to test the elements.
+/// The input stack.
+/// when every element satisfies the predicate.
+/// O(n).
+[]
+val forall : predicate : ('T -> bool) -> stack : IStack<'T> -> bool
+
+///
+/// Returns whether all pairs of elements at the same index satisfy the predicate.
+///
+/// The function to test the pairs.
+/// The first stack.
+/// The second stack.
+/// when every pair satisfies the predicate.
+/// Thrown when every pair satisfies the predicate and the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val forall2 : predicate : ('T1 -> 'T2 -> bool) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> bool
+
+///
+/// Returns the first value that the chooser returns for the elements.
+///
+/// The function that returns a value, or ValueNone to go on.
+/// The input stack.
+/// The first chosen value.
+/// Thrown when the chooser returns ValueNone for every element.
+///
+/// O(n).
+///
+[]
+val pick : chooser : ('T -> 'U voption) -> stack : IStack<'T> -> 'U
+
+///
+/// Returns the first element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The first element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFind : predicate : ('T -> bool) -> stack : IStack<'T> -> 'T voption
+
+///
+/// Returns the last element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The last element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindBack : predicate : ('T -> bool) -> stack : IStack<'T> -> 'T voption
+
+///
+/// Returns the index of the first element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The index of the first element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindIndex : predicate : ('T -> bool) -> stack : IStack<'T> -> int voption
+
+///
+/// Returns the index of the last element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The index of the last element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindIndexBack : predicate : ('T -> bool) -> stack : IStack<'T> -> int voption
+
+///
+/// Returns the first value that the chooser returns for the elements, or ValueNone.
+///
+/// The function that returns a value, or ValueNone to go on.
+/// The input stack.
+/// The first chosen value, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryPick : chooser : ('T -> 'U voption) -> stack : IStack<'T> -> 'U voption
+
+// ---- Transformation ----
+
+///
+/// Returns every element of the first stack paired with every element of the second one.
+///
+/// The first stack.
+/// The second stack.
+/// The pairs, in the order of the first stack and then of the second one.
+///
+/// O(n × m).
+///
+[]
+val allPairs : stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> Stack
+
+///
+/// Returns a stack of the elements of the first stack followed by the elements of the second one.
+///
+/// The first stack, whose elements come first, on top.
+/// The second stack, whose elements come last.
+/// The concatenation, in a stack of the implementation of the second stack.
+///
+///
+/// O(n), where n is the length of the first stack. The function pushes the elements of the first stack onto the second
+/// one with , from the last element to the
+/// first one. When the first stack is empty, the function returns the second one.
+///
+///
+/// The collection that receives the elements keeps its implementation (ADR 0001, D14). Here it is the second stack: the
+/// result keeps its implementation and its nodes, as
+///
+/// shares its second list.
+///
+/// adds the elements of the second list to the first one instead, so its result keeps the implementation of the first
+/// list.
+///
+///
+[]
+val append : stack1 : IStack<'T> -> stack2 : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the values that the chooser returns for the elements.
+///
+/// The function that returns a value, or ValueNone to skip the element.
+/// The input stack.
+/// The chosen values, in the order of their elements.
+///
+/// O(n).
+///
+[]
+val choose : chooser : ('T -> 'U voption) -> stack : IStack<'T> -> Stack<'U>
+
+/// Divides the stack into chunks of a given size.
+/// The maximum size of a chunk.
+/// The input stack.
+/// The chunks; the last one can be smaller.
+/// Thrown when is not positive.
+/// O(n).
+[]
+val chunkBySize : chunkSize : int -> stack : IStack<'T> -> Stack>
+
+///
+/// Concatenates the stacks that a function returns for the elements.
+///
+/// The function that returns a stack for each element.
+/// The input stack.
+/// The concatenation of the returned stacks.
+///
+/// O(n + the total length of the returned stacks).
+///
+[]
+val collect : mapping : ('T -> IStack<'U>) -> stack : IStack<'T> -> Stack<'U>
+
+/// Concatenates a sequence of stacks.
+/// The stacks.
+/// The concatenation, in a stack, because the stacks can have different implementations.
+/// Thrown when is or a default flat list, or one of its stacks is .
+/// O(the total length).
+[]
+val concat : stacks : seq> -> Stack<'T>
+
+///
+/// Returns a stack with a value added before the first element: the value pushed on top.
+///
+/// The value to add.
+/// The input stack.
+/// A stack of the implementation of the input with the value and the elements.
+///
+/// O(1). The function wraps .
+///
+[]
+val cons : value : 'T -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns a stack with values added before the first element, in their order: the first value ends on top.
+///
+/// The values to add, in the order in which the result enumerates them.
+/// The input stack.
+/// A stack of the implementation of the input with the values followed by the elements.
+/// Thrown when is or a default flat list.
+///
+/// O(m), where m is the number of values. The function pushes the values from the last one to the first one with
+/// .
+///
+[]
+val consMany : values : seq<'T> -> stack : IStack<'T> -> IStack<'T>
+
+/// Returns the elements that satisfy the predicate.
+/// The function to test the elements.
+/// The input stack.
+/// The elements that satisfy the predicate, in their order, in a stack of the implementation of the input.
+/// O(n).
+[]
+val filter : predicate : ('T -> bool) -> stack : IStack<'T> -> IStack<'T>
+
+/// Returns every element with its index.
+/// The input stack.
+/// The pairs of index and element; the top has the index 0.
+/// O(n).
+[]
+val indexed : stack : IStack<'T> -> Stack
+
+/// Returns the results of a function applied to each element.
+/// The function to transform the elements, called in the order of the elements.
+/// The input stack.
+/// The results, in the order of the elements.
+/// O(n).
+[]
+val map : mapping : ('T -> 'U) -> stack : IStack<'T> -> Stack<'U>
+
+///
+/// Returns the results of a function applied to the pairs of elements at the same index.
+///
+/// The function to transform the pairs.
+/// The first stack.
+/// The second stack.
+/// The results.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val map2 : mapping : ('T1 -> 'T2 -> 'U) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> Stack<'U>
+
+///
+/// Returns the results of a function applied to the triples of elements at the same index.
+///
+/// The function to transform the triples.
+/// The first stack.
+/// The second stack.
+/// The third stack.
+/// The results.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val map3 :
+ mapping : ('T1 -> 'T2 -> 'T3 -> 'U) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> stack3 : IStack<'T3> -> Stack<'U>
+
+///
+/// Returns the results of a function applied to each element and its index.
+///
+/// The function to transform the elements.
+/// The input stack.
+/// The results.
+///
+/// O(n).
+///
+[]
+val mapi : mapping : (int -> 'T -> 'U) -> stack : IStack<'T> -> Stack<'U>
+
+///
+/// Returns the results of a function applied to the index and the pair of elements at that index.
+///
+/// The function to transform the pairs.
+/// The first stack.
+/// The second stack.
+/// The results.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val mapi2 : mapping : (int -> 'T1 -> 'T2 -> 'U) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> Stack<'U>
+
+/// Returns every element with its successor.
+/// The input stack.
+/// The pairs of neighbours; empty for fewer than two elements.
+/// O(n).
+[]
+val pairwise : stack : IStack<'T> -> Stack
+
+///
+/// Returns the elements moved to the indices that a function computes.
+///
+/// The function that maps the index of each element to its new index.
+/// The input stack.
+/// The permuted elements, in a stack of the implementation of the input.
+/// Thrown when the function does not compute a permutation.
+///
+/// O(n).
+///
+[]
+val permute : indexMap : (int -> int) -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the elements in reverse order.
+///
+/// The input stack.
+/// The reversed elements, in a stack of the implementation of the input.
+///
+/// O(n). The function pushes the elements onto the cleared stack in their order.
+///
+[]
+val rev : stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the transpose of a sequence of stacks of the same length.
+///
+/// The stacks.
+/// A stack whose element i holds the element i of every input stack.
+/// Thrown when is or a default flat list, or one of its stacks is .
+/// Thrown when the stacks have different lengths.
+///
+/// O(the total length).
+///
+[]
+val transpose : stacks : seq> -> Stack>
+
+/// Splits a stack of pairs into two stacks.
+/// The input stack.
+/// The first components and the second components.
+/// O(n).
+[]
+val unzip : stack : IStack -> struct (Stack<'T1> * Stack<'T2>)
+
+/// Splits a stack of triples into three stacks.
+/// The input stack.
+/// The first, the second and the third components.
+/// O(n).
+[]
+val unzip3 : stack : IStack -> struct (Stack<'T1> * Stack<'T2> * Stack<'T3>)
+
+///
+/// Returns the elements that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The elements that satisfy the predicate, in their order, in a stack of the implementation of the input.
+///
+/// O(n). A synonym of .
+///
+[]
+val where : predicate : ('T -> bool) -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the windows of a given size that slide over the stack.
+///
+/// The size of a window.
+/// The input stack.
+/// The windows; empty when the stack is shorter than a window.
+/// Thrown when is not positive.
+///
+/// O(n × windowSize).
+///
+[]
+val windowed : windowSize : int -> stack : IStack<'T> -> Stack>
+
+/// Combines two stacks into a stack of pairs.
+/// The first stack.
+/// The second stack.
+/// The pairs of elements at the same index.
+/// Thrown when the stacks have different lengths.
+/// O(n).
+[]
+val zip : stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> Stack
+
+/// Combines three stacks into a stack of triples.
+/// The first stack.
+/// The second stack.
+/// The third stack.
+/// The triples of elements at the same index.
+/// Thrown when the stacks have different lengths.
+/// O(n).
+[]
+val zip3 : stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> stack3 : IStack<'T3> -> Stack
+
+// ---- Folding ----
+
+/// Returns the average of the elements.
+/// The input stack.
+/// The average.
+/// Thrown when the stack is empty.
+/// O(n).
+[]
+val inline average :
+ stack : IStack< ^T > -> ^T
+ when ^T : (static member (+) : ^T * ^T -> ^T)
+ and ^T : (static member DivideByInt : ^T * int -> ^T)
+ and ^T : (static member Zero : ^T)
+
+///
+/// Returns the average of the results of a function applied to each element.
+///
+/// The function to transform the elements.
+/// The input stack.
+/// The average of the results.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val inline averageBy :
+ projection : ('T -> ^U) -> stack : IStack<'T> -> ^U
+ when ^U : (static member (+) : ^U * ^U -> ^U)
+ and ^U : (static member DivideByInt : ^U * int -> ^U)
+ and ^U : (static member Zero : ^U)
+
+/// Compares two stacks element by element with a comparer.
+/// The function that compares two elements.
+/// The first stack.
+/// The second stack.
+///
+/// The first non-zero result of the comparer, or the comparison of the lengths when every common element compares equal.
+///
+/// O(n).
+[]
+val compareWith : comparer : ('T -> 'T -> int) -> stack1 : IStack<'T> -> stack2 : IStack<'T> -> int
+
+///
+/// Applies a function to a state and each element, from the first element to the last one.
+///
+/// The function that computes the next state.
+/// The initial state.
+/// The input stack.
+/// The final state.
+///
+/// O(n).
+///
+[]
+val fold : folder : ('State -> 'T -> 'State) -> state : 'State -> stack : IStack<'T> -> 'State
+
+///
+/// Applies a function to a state and each pair of elements at the same index, from the first index to the last one.
+///
+/// The function that computes the next state.
+/// The initial state.
+/// The first stack.
+/// The second stack.
+/// The final state.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val fold2 : folder : ('State -> 'T1 -> 'T2 -> 'State) -> state : 'State -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> 'State
+
+///
+/// Applies a function to each element and a state, from the last element to the first one.
+///
+/// The function that computes the next state.
+/// The input stack.
+/// The initial state.
+/// The final state.
+///
+/// O(n).
+///
+[]
+val foldBack : folder : ('T -> 'State -> 'State) -> stack : IStack<'T> -> state : 'State -> 'State
+
+///
+/// Applies a function to each pair of elements at the same index and a state, from the last index to the first one.
+///
+/// The function that computes the next state.
+/// The first stack.
+/// The second stack.
+/// The initial state.
+/// The final state.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val foldBack2 :
+ folder : ('T1 -> 'T2 -> 'State -> 'State) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> state : 'State -> 'State
+
+/// Applies an action to each element.
+/// The action.
+/// The input stack.
+/// O(n).
+[]
+val iter : action : ('T -> unit) -> stack : IStack<'T> -> unit
+
+///
+/// Applies an action to each pair of elements at the same index.
+///
+/// The action.
+/// The first stack.
+/// The second stack.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val iter2 : action : ('T1 -> 'T2 -> unit) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> unit
+
+/// Applies an action to each element and its index.
+/// The action.
+/// The input stack.
+/// O(n).
+[]
+val iteri : action : (int -> 'T -> unit) -> stack : IStack<'T> -> unit
+
+///
+/// Applies an action to the index and the pair of elements at that index.
+///
+/// The action.
+/// The first stack.
+/// The second stack.
+/// Thrown when the stacks have different lengths.
+///
+/// O(n).
+///
+[]
+val iteri2 : action : (int -> 'T1 -> 'T2 -> unit) -> stack1 : IStack<'T1> -> stack2 : IStack<'T2> -> unit
+
+///
+/// Maps each element and threads a state through the mapping, from the first element to the last one.
+///
+/// The function that returns the result for an element and the next state.
+/// The initial state.
+/// The input stack.
+/// The results and the final state.
+///
+/// O(n).
+///
+[]
+val mapFold :
+ mapping : ('State -> 'T -> struct ('Result * 'State)) ->
+ state : 'State ->
+ stack : IStack<'T> ->
+ struct (Stack<'Result> * 'State)
+
+///
+/// Maps each element and threads a state through the mapping, from the last element to the first one.
+///
+/// The function that returns the result for an element and the next state.
+/// The input stack.
+/// The initial state.
+/// The results, in the order of the elements, and the final state.
+///
+/// O(n).
+///
+[]
+val mapFoldBack :
+ mapping : ('T -> 'State -> struct ('Result * 'State)) ->
+ stack : IStack<'T> ->
+ state : 'State ->
+ struct (Stack<'Result> * 'State)
+
+/// Returns the greatest element, in F# generic comparison.
+/// The input stack.
+/// The greatest element.
+/// Thrown when the stack is empty.
+/// O(n).
+[]
+val max : stack : IStack<'T> -> 'T when 'T : comparison
+
+///
+/// Returns the element with the greatest result of a projection.
+///
+/// The function to transform the elements for the comparison.
+/// The input stack.
+/// The first element with the greatest result.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val maxBy : projection : ('T -> 'U) -> stack : IStack<'T> -> 'T when 'U : comparison
+
+/// Returns the smallest element, in F# generic comparison.
+/// The input stack.
+/// The smallest element.
+/// Thrown when the stack is empty.
+/// O(n).
+[]
+val min : stack : IStack<'T> -> 'T when 'T : comparison
+
+///
+/// Returns the element with the smallest result of a projection.
+///
+/// The function to transform the elements for the comparison.
+/// The input stack.
+/// The first element with the smallest result.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val minBy : projection : ('T -> 'U) -> stack : IStack<'T> -> 'T when 'U : comparison
+
+///
+/// Combines the elements with a function, from the first element to the last one.
+///
+/// The function that combines the result so far with the next element.
+/// The input stack.
+/// The combined value.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val reduce : reduction : ('T -> 'T -> 'T) -> stack : IStack<'T> -> 'T
+
+///
+/// Combines the elements with a function, from the last element to the first one.
+///
+/// The function that combines an element with the result so far.
+/// The input stack.
+/// The combined value.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val reduceBack : reduction : ('T -> 'T -> 'T) -> stack : IStack<'T> -> 'T
+
+///
+/// Returns every state of a fold from the first element to the last one, the initial state first.
+///
+/// The function that computes the next state.
+/// The initial state.
+/// The input stack.
+/// The states; one more than the elements.
+///
+/// O(n).
+///
+[]
+val scan : folder : ('State -> 'T -> 'State) -> state : 'State -> stack : IStack<'T> -> Stack<'State>
+
+///
+/// Returns every state of a fold from the last element to the first one, the initial state last.
+///
+/// The function that computes the next state.
+/// The input stack.
+/// The initial state.
+/// The states; one more than the elements.
+///
+/// O(n).
+///
+[]
+val scanBack : folder : ('T -> 'State -> 'State) -> stack : IStack<'T> -> state : 'State -> Stack<'State>
+
+/// Returns the sum of the elements.
+/// The input stack.
+/// The sum; zero for an empty stack.
+/// O(n).
+[]
+val inline sum : stack : IStack< ^T > -> ^T when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T)
+
+///
+/// Returns the sum of the results of a function applied to each element.
+///
+/// The function to transform the elements.
+/// The input stack.
+/// The sum of the results; zero for an empty stack.
+///
+/// O(n).
+///
+[]
+val inline sumBy :
+ projection : ('T -> ^U) -> stack : IStack<'T> -> ^U
+ when ^U : (static member (+) : ^U * ^U -> ^U) and ^U : (static member Zero : ^U)
+
+// ---- Partitioning and grouping ----
+
+/// Counts the elements of each key of a projection.
+/// The function that returns the key of an element.
+/// The input stack.
+/// The keys, in the order of their first occurrence, with the number of their elements.
+/// O(n). The keys compare in F# structural equality.
+[]
+val countBy : projection : ('T -> 'Key) -> stack : IStack<'T> -> Stack when 'Key : equality
+
+///
+/// Returns the elements without duplicates, in F# structural equality.
+///
+/// The input stack.
+/// The first occurrence of each element, in their order, in a stack of the implementation of the input.
+///
+/// O(n).
+///
+[]
+val distinct : stack : IStack<'T> -> IStack<'T> when 'T : equality
+
+///
+/// Returns the elements without duplicates of the result of a projection.
+///
+/// The function that returns the key of an element.
+/// The input stack.
+/// The first element of each key, in their order, in a stack of the implementation of the input.
+///
+/// O(n). The keys compare in F# structural equality.
+///
+[]
+val distinctBy : projection : ('T -> 'Key) -> stack : IStack<'T> -> IStack<'T> when 'Key : equality
+
+///
+/// Returns the distinct elements that do not equal any of the excluded items.
+///
+/// The items to remove.
+/// The input stack.
+/// The distinct remaining elements, in their order, in a stack of the implementation of the input.
+/// Thrown when is or a default flat list.
+///
+/// O(n + m). An empty stack is returned without a look at the items to exclude.
+///
+[]
+val except : itemsToExclude : seq<'T> -> stack : IStack<'T> -> IStack<'T> when 'T : equality
+
+/// Groups the elements by the key of a projection.
+/// The function that returns the key of an element.
+/// The input stack.
+/// The keys, in the order of their first occurrence, with their elements in their order.
+/// O(n). The keys compare in F# structural equality.
+[]
+val groupBy : projection : ('T -> 'Key) -> stack : IStack<'T> -> Stack)> when 'Key : equality
+
+/// Splits the elements by a predicate.
+/// The function to test the elements.
+/// The input stack.
+///
+/// The elements that satisfy the predicate and the other ones, both in their order, in stacks of the implementation of
+/// the input.
+///
+/// O(n).
+[]
+val partition : predicate : ('T -> bool) -> stack : IStack<'T> -> struct (IStack<'T> * IStack<'T>)
+
+///
+/// Splits the elements by the choice that a function returns for them.
+///
+/// The function that returns Choice1Of2 or Choice2Of2 for an element.
+/// The input stack.
+/// The values of the first choices and of the second choices, both in the order of the elements.
+///
+/// O(n).
+///
+[]
+val partitionWith : partitioner : ('T -> Choice<'T1, 'T2>) -> stack : IStack<'T> -> struct (Stack<'T1> * Stack<'T2>)
+
+///
+/// Returns the stack without its first elements.
+///
+/// The number of elements to skip; a negative count skips nothing.
+/// The input stack.
+/// The remaining elements, in a stack of the implementation of the input.
+/// Thrown when the count exceeds the length.
+///
+/// O(count). The function pops the elements with .
+///
+[]
+val skip : count : int -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the stack without its last elements.
+///
+/// The number of elements to skip; a negative count skips nothing.
+/// The input stack.
+/// The remaining elements, in a stack of the implementation of the input.
+/// Thrown when the count exceeds the length.
+///
+/// O(n). The stack itself is returned when nothing is skipped. The checks follow
+/// .
+///
+[]
+val skipLast : count : int -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the stack without its first elements that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The elements from the first one that does not satisfy the predicate, in a stack of the implementation of the input.
+///
+/// O(k), where k is the number of skipped elements.
+///
+[]
+val skipWhile : predicate : ('T -> bool) -> stack : IStack<'T> -> IStack<'T>
+
+/// Splits the stack at an index.
+/// The index of the first element of the second part.
+/// The input stack.
+/// The elements before the index and the elements from the index, in stacks of the implementation of the input.
+/// Thrown when the index is negative.
+/// Thrown when the index exceeds the length.
+/// O(index).
+[]
+val splitAt : index : int -> stack : IStack<'T> -> struct (IStack<'T> * IStack<'T>)
+
+///
+/// Splits the stack into at most a given number of parts of nearly equal length.
+///
+/// The maximum number of parts.
+/// The input stack.
+/// The parts; the first ones are one element longer when the length does not divide evenly.
+/// Thrown when is not positive.
+///
+/// O(n).
+///
+[]
+val splitInto : count : int -> stack : IStack<'T> -> Stack>
+
+///
+/// Returns the first elements of the stack.
+///
+/// The number of elements to take.
+/// The input stack.
+/// The first elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative.
+/// Thrown when the count exceeds the length.
+///
+/// O(count). The stack itself is returned when the count is the length.
+///
+[]
+val take : count : int -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the last elements of the stack.
+///
+/// The number of elements to take.
+/// The input stack.
+/// The last elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative.
+/// Thrown when the count exceeds the length.
+///
+/// O(n). The function pops the other elements with .
+/// The checks follow
+/// .
+///
+[]
+val takeLast : count : int -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the first elements of the stack that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input stack.
+/// The elements before the first one that does not satisfy the predicate, in a stack of the implementation of the input.
+///
+/// O(k), where k is the length of the result. The stack itself is returned when every element satisfies the predicate.
+///
+[]
+val takeWhile : predicate : ('T -> bool) -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Returns at most a given number of first elements.
+///
+/// The maximum number of elements; a count that is not positive returns an empty stack.
+/// The input stack.
+/// The first elements, in a stack of the implementation of the input.
+///
+/// O(k), where k is the length of the result. The stack itself is returned when it is not longer than the count.
+///
+[]
+val truncate : count : int -> stack : IStack<'T> -> IStack<'T>
+
+// ---- Sorting ----
+
+///
+/// Sorts the elements in the order of F# generic comparison.
+///
+/// The input stack.
+/// The sorted elements, the smallest on top, in a stack of the implementation of the input.
+///
+/// Thrown when the comparison of two elements throws, as in .
+///
+///
+/// O(n log n). This is a stable sort, as :
+/// equal elements keep their order.
+///
+[]
+val sort : stack : IStack<'T> -> IStack<'T> when 'T : comparison
+
+///
+/// Sorts the elements by the result of a projection, in the order of F# generic comparison.
+///
+/// The function that returns the key of an element.
+/// The input stack.
+/// The sorted elements, in a stack of the implementation of the input.
+///
+/// O(n log n). This is a stable sort: elements with equal keys keep their order.
+///
+[]
+val sortBy : projection : ('T -> 'Key) -> stack : IStack<'T> -> IStack<'T> when 'Key : comparison
+
+///
+/// Sorts the elements by the result of a projection, in descending order.
+///
+/// The function that returns the key of an element.
+/// The input stack.
+/// The sorted elements, in a stack of the implementation of the input.
+///
+/// Thrown when the projection or the comparison of two keys throws: the projection runs in the comparison, as in
+/// .
+///
+///
+/// O(n log n). This is a stable sort: elements with equal keys keep their order.
+///
+[]
+val sortByDescending : projection : ('T -> 'Key) -> stack : IStack<'T> -> IStack<'T> when 'Key : comparison
+
+///
+/// Sorts the elements in descending order of F# generic comparison.
+///
+/// The input stack.
+/// The sorted elements, the greatest on top, in a stack of the implementation of the input.
+///
+/// Thrown when the comparison of two elements throws, as in .
+///
+///
+/// O(n log n). This is a stable sort: equal elements keep their order.
+///
+[]
+val sortDescending : stack : IStack<'T> -> IStack<'T> when 'T : comparison
+
+///
+/// Sorts the elements with a comparison function.
+///
+/// The function that compares two elements.
+/// The input stack.
+/// The sorted elements, in a stack of the implementation of the input.
+///
+/// Thrown when the comparer throws, as in
+/// .
+///
+///
+/// O(n log n). This is a stable sort: elements that compare equal keep their order.
+///
+[]
+val sortWith : comparer : ('T -> 'T -> int) -> stack : IStack<'T> -> IStack<'T>
+
+///
+/// Sorts the elements with a comparer.
+///
+/// The comparer.
+/// The input stack.
+/// The sorted elements, in a stack of the implementation of the input.
+///
+/// O(n log n). This is a stable sort: elements that compare equal keep their order. The function wraps
+/// .
+///
+[]
+val sortWithComparer : comparer : Collections.Generic.IComparer<'T> -> stack : IStack<'T> -> IStack<'T>
+
+// ---- Random ----
+
+///
+/// Returns a random element, chosen with .
+///
+/// The input stack.
+/// A random element.
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val randomChoice : source : IStack<'T> -> 'T
+
+///
+/// Returns a random element, chosen with a function that returns numbers in [0, 1).
+///
+/// The function that returns a random number in [0, 1).
+/// The input stack.
+/// A random element.
+/// Thrown when the stack is empty.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n).
+///
+[]
+val randomChoiceBy : randomizer : (unit -> float) -> source : IStack<'T> -> 'T
+
+///
+/// Returns a random element, chosen with a given random number generator.
+///
+/// The random number generator.
+/// The input stack.
+/// A random element.
+/// Thrown when is .
+/// Thrown when the stack is empty.
+///
+/// O(n).
+///
+[]
+val randomChoiceWith : random : Random -> source : IStack<'T> -> 'T
+
+///
+/// Returns random elements, chosen with ; an element can repeat.
+///
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative, or positive for an empty stack.
+///
+/// O(n + count).
+///
+[]
+val randomChoices : count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns random elements, chosen with a function that returns numbers in [0, 1); an element can repeat.
+///
+/// The function that returns a random number in [0, 1).
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative, or positive for an empty stack.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n + count).
+///
+[]
+val randomChoicesBy : randomizer : (unit -> float) -> count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns random elements, chosen with a given random number generator; an element can repeat.
+///
+/// The random number generator.
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when is .
+/// Thrown when the count is negative, or positive for an empty stack.
+///
+/// O(n + count).
+///
+[]
+val randomChoicesWith : random : Random -> count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns random elements at distinct indices, chosen with .
+///
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative or exceeds the length.
+///
+/// O(n + count).
+///
+[]
+val randomSample : count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns random elements at distinct indices, chosen with a function that returns numbers in [0, 1).
+///
+/// The function that returns a random number in [0, 1).
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when the count is negative or exceeds the length.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n + count).
+///
+[]
+val randomSampleBy : randomizer : (unit -> float) -> count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns random elements at distinct indices, chosen with a given random number generator.
+///
+/// The random number generator.
+/// The number of elements to choose.
+/// The input stack.
+/// The chosen elements, in a stack of the implementation of the input.
+/// Thrown when is .
+/// Thrown when the count is negative or exceeds the length.
+///
+/// O(n + count).
+///
+[]
+val randomSampleWith : random : Random -> count : int -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the elements in a random order, chosen with .
+///
+/// The input stack.
+/// The shuffled elements, in a stack of the implementation of the input.
+///
+/// O(n).
+///
+[]
+val randomShuffle : source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the elements in a random order, chosen with a function that returns numbers in [0, 1).
+///
+/// The function that returns a random number in [0, 1).
+/// The input stack.
+/// The shuffled elements, in a stack of the implementation of the input.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n).
+///
+[]
+val randomShuffleBy : randomizer : (unit -> float) -> source : IStack<'T> -> IStack<'T>
+
+///
+/// Returns the elements in a random order, chosen with a given random number generator.
+///
+/// The random number generator.
+/// The input stack.
+/// The shuffled elements, in a stack of the implementation of the input.
+/// Thrown when is .
+///
+/// O(n).
+///
+[]
+val randomShuffleWith : random : Random -> source : IStack<'T> -> IStack<'T>
+
+// ---- Conversion ----
+
+///
+/// Returns a new array with the elements, from the top to the bottom.
+///
+/// The input stack.
+/// A copy of the elements.
+///
+/// O(n).
+///
+[]
+val toArray : stack : IStack<'T> -> 'T array
+
+/// Returns a list of the elements, from the top to the bottom.
+/// The input stack.
+/// The list of the elements.
+/// O(n).
+[]
+val toList : stack : IStack<'T> -> 'T list
+
+///