diff --git a/CHANGELOG.md b/CHANGELOG.md
index 029a7817..61f2434c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,22 @@ 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.
+* `Queue` has every function of the FSharp.Core `List` module that applies to an immutable queue, over the elements in the
+ order in which they leave the queue, with its semantics and exceptions and with stable sorts, plus `enqueueMany`,
+ `ofReadOnlySpan`, `ofSpan`, `skipLast`, `sortWithComparer`, `takeLast`, `tryTail`, `tryUncons` and `uncons`. Its
+ parameters and exceptions name the queues `queue`, `queue1`, `queue2`, `queue3` and `queues` where `List` names its
+ lists. `enqueueMany`, `uncons`, `tryHead`, `tryTail` and `tryUncons` compile to `EnqueueMany`, `PeekDequeue`,
+ `TryPeek`, `TryDequeue` and `TryPeekDequeue`, in the `IQueue`, `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 the BCL would reject with `InvalidOperationException` while it enumerates it; the `IQueue`
+ module and the `queue { }` expression do the same.
+* The `IQueue` module has the functions of `Queue` for `IImmutableQueue<'T>`, without the creation functions, plus
+ `clear`. Its functions keep the implementation of the input queue where the result has the element type of the input,
+ and return a `Queue` otherwise. `append` enqueues the second queue into the first one, so its result keeps the
+ implementation of the first queue, also when that queue is empty.
+* The `FSharp.Collections.Immutable.Option` and `FSharp.Collections.Immutable.Tuple` namespaces have `Queue` and `IQueue`
+ modules that repeat the functions that use value options or struct tuples with options and reference tuples.
+* `queue { }` builds a queue.
### Changed
@@ -124,6 +140,19 @@ 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.
+* `Queue` works on `Queue<'T>` (`ImmutableQueue<'T>`) instead of `IQueue<'T>`, and `enqueue`, `tail` and `filter` return a
+ `Queue<'T>`; the functions for `IImmutableQueue<'T>` are in the new `IQueue` module.
+* `Queue`: `tryFind`, `tryFindIndex` and `tryPick` return `voption`, and `pick` and `tryPick` take functions that return
+ `voption`. The complete active pattern `Cons | Nil`, whose `Cons` case matched a reference tuple of the head and the
+ tail, is replaced by the struct partial active patterns `Cons`, which matches a struct tuple of the head and the tail,
+ and `Nil`.
+* `Queue`: every function throws what the FSharp.Core `List` function of the same name throws. `head` and `tail` of an
+ empty queue throw `ArgumentException`, where they threw `InvalidOperationException`, and `iter2` of queues of
+ different lengths throws `ArgumentException`, where it stopped at the shorter queue. A default `FlatList` given as a
+ sequence to `ofSeq` throws `ArgumentNullException`, where it threw `InvalidOperationException`.
+* `Queue.forall` and `Queue.averageBy` apply to the queue; they returned a function of a sequence before.
+* `Queue`: `enqueue`, `head` and `tail` compile to `Enqueue`, `Peek` and `Dequeue`, and the other functions to
+ PascalCase names such as `IsEmpty`, where 1.0.0 compiled all of them under their F# names, such as `isEmpty`.
### Removed
@@ -161,6 +190,7 @@ 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.
+* `Queue.clear`: use `Queue.empty`, or `IQueue.clear` to keep the implementation of an interface value.
## [1.0.0] - 2020-02-23
diff --git a/README.md b/README.md
index 239ef746..46712352 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,8 @@ Please also join the [F# Open Source Group](http://fsharp.github.com)
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
| `Stack` | `ImmutableStack<'T>` | – |
-| `Queue` | `ImmutableQueue<'T>` | – |
+| `Queue` | `ImmutableQueue<'T>` | `queue { }` |
+| `IQueue` | `IImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | – |
diff --git a/docsSrc/Explanations/Background.md b/docsSrc/Explanations/Background.md
index 21f275db..57231e47 100644
--- a/docsSrc/Explanations/Background.md
+++ b/docsSrc/Explanations/Background.md
@@ -30,7 +30,8 @@ While F# has its own built-in immutable collections, System.Collections.Immutabl
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
| `Stack` | `ImmutableStack<'T>` | – |
-| `Queue` | `ImmutableQueue<'T>` | – |
+| `Queue` | `ImmutableQueue<'T>` | `queue { }` |
+| `IQueue` | `IImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | – |
diff --git a/docsSrc/index.md b/docsSrc/index.md
index 8fd0c476..6cc7d1f3 100644
--- a/docsSrc/index.md
+++ b/docsSrc/index.md
@@ -12,7 +12,8 @@ F# bindings for [System.Collections.Immutable](https://learn.microsoft.com/en-us
| `IndexedList` | `ImmutableList<'T>` | `indexedList { }` |
| `IIndexedList` | `IImmutableList<'T>` | – |
| `Stack` | `ImmutableStack<'T>` | – |
-| `Queue` | `ImmutableQueue<'T>` | – |
+| `Queue` | `ImmutableQueue<'T>` | `queue { }` |
+| `IQueue` | `IImmutableQueue<'T>` | – |
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | – |
diff --git a/src/FSharp.Collections.Immutable/ComputationExpressions.fs b/src/FSharp.Collections.Immutable/ComputationExpressions.fs
index 972f77ec..a7aab05c 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 queue = QueueCE ()
diff --git a/src/FSharp.Collections.Immutable/ComputationExpressions.fsi b/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
index e588c81b..c684d123 100644
--- a/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
+++ b/src/FSharp.Collections.Immutable/ComputationExpressions.fsi
@@ -33,3 +33,17 @@ val flatList : FlatListCE
/// Evaluates to an indexed list of 0, 1, 4, 9, 100 and 200.
///
val indexedList : IndexedListCE
+
+/// Builds a queue.
+///
+///
+/// let squares = queue {
+/// yield 0
+/// for i in 1..3 do
+/// yield i * i
+/// yield! [ 100; 200 ]
+/// }
+///
+/// Evaluates to a queue of 0, 1, 4, 9, 100 and 200, from which 0 leaves first.
+///
+val queue : QueueCE
diff --git a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
index f66316ea..226c8ff6 100644
--- a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
+++ b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
@@ -40,6 +40,7 @@
+
@@ -68,8 +69,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/FSharp.Collections.Immutable/Helper/Queues.fs b/src/FSharp.Collections.Immutable/Helper/Queues.fs
new file mode 100644
index 00000000..ca0c691d
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Helper/Queues.fs
@@ -0,0 +1,137 @@
+///
+/// The helpers that the queue modules share (ADR 0001, D5): the queues of a fresh array and of a buffer, and the walks that
+/// read the elements of a queue only up to the ones that a function of
+/// or needs.
+///
+///
+///
+/// The walks take the interface, which both modules pass, and the caller checks the queue for . They
+/// enumerate an with its struct enumerator, which allocates
+/// nothing, also when a function of receives it, and any other queue
+/// with its own enumerator, which they dispose.
+///
+///
+/// Each walk is one generic loop over an enumerator type that implements
+/// . The loop runs with the struct ImmutableQueueEnumerator
+/// or with the enumerator of the other queue. The JIT compiles generic code for each struct type argument and calls its
+/// members without boxing it, so the struct enumerator of the queue advances in place. Member constraints of an inline
+/// loop would need the struct to be public, because they resolve only to members that are accessible from everywhere.
+///
+///
+module internal FSharp.Collections.Immutable.Queues
+
+open System
+open System.Collections.Generic
+open System.Collections.Immutable
+open System.Runtime.InteropServices
+
+/// The struct enumerator of an ImmutableQueue as an IEnumerator, for the loops below, which take it as a type argument.
+[]
+type ImmutableQueueEnumerator<'T> =
+ val mutable private enumerator : ImmutableQueue<'T>.Enumerator
+ new (queue : ImmutableQueue<'T>) = { enumerator = queue.GetEnumerator () }
+
+ interface IEnumerator<'T> with
+ member this.Current = this.enumerator.Current
+
+ interface Collections.IEnumerator with
+ member this.Current = box this.enumerator.Current
+ member this.MoveNext () = this.enumerator.MoveNext ()
+ // The walks never reset an enumerator, and the struct enumerator of the queue cannot be reset
+ member _.Reset () = raise (NotSupportedException ())
+
+ interface IDisposable with
+ member _.Dispose () = ()
+
+/// Runs a loop over the struct enumerator of an ImmutableQueue, or over the enumerator of another queue, which it
+/// disposes. The callers pass the same generic loop twice, once for each type of enumerator.
+let inline private walk
+ ([] immutableQueueLoop : ImmutableQueueEnumerator<'T> -> 'Result)
+ ([] otherQueueLoop : IEnumerator<'T> -> 'Result)
+ (queue : IImmutableQueue<'T>)
+ =
+ match queue with
+ | :? ImmutableQueue<'T> as queue -> immutableQueueLoop (new ImmutableQueueEnumerator<'T> (queue))
+ | _ ->
+ use enumerator = queue.GetEnumerator ()
+ otherQueueLoop enumerator
+
+/// The loop of firstMatch.
+let private firstMatchLoop (predicate : 'T -> bool) (enumerator : 'Enumerator :> IEnumerator<'T>) =
+ // A struct enumerator advances only as a mutable value; a call on the parameter would advance a copy
+ let mutable enumerator = enumerator
+ 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 loop of firstChoice.
+let private firstChoiceLoop (chooser : 'T -> 'U voption) (enumerator : 'Enumerator :> IEnumerator<'T>) =
+ let mutable enumerator = enumerator
+ let mutable result = ValueNone
+
+ while result.IsNone && enumerator.MoveNext () do
+ result <- chooser enumerator.Current
+
+ result
+
+/// The loop of front.
+let private frontLoop (count : int) (enumerator : 'Enumerator :> IEnumerator<'T>) =
+ let mutable enumerator = enumerator
+ let items = ResizeArray<'T>()
+ let mutable more = enumerator.MoveNext ()
+
+ while more && items.Count < count do
+ items.Add enumerator.Current
+ more <- enumerator.MoveNext ()
+
+ struct (items, more)
+
+///
+/// A queue of the elements of a fresh array, without the null check of
+/// .
+///
+///
+/// copies the elements
+/// into the front stack of the queue, from the last one to the first one, so that the queue dequeues them without
+/// reversing a back stack first.
+///
+let inline ofFreshArray (array : 'T array) = ImmutableQueue.Create<'T>(ReadOnlySpan<'T> array)
+
+/// A queue of the elements of a buffer that nobody changes any more, built as ofFreshArray builds it, without a copy of
+/// the buffer.
+let ofBuffer (buffer : ResizeArray<'T>) =
+ ImmutableQueue.Create<'T>(Span<'T>.op_Implicit(CollectionsMarshal.AsSpan buffer))
+
+/// The index and the first element that satisfies the predicate, or ValueNone.
+let firstMatch (predicate : 'T -> bool) (queue : IImmutableQueue<'T>) =
+ walk (firstMatchLoop predicate) (firstMatchLoop predicate) queue
+
+/// The first value that the chooser returns for the elements, in their order, or ValueNone.
+let firstChoice (chooser : 'T -> 'U voption) (queue : IImmutableQueue<'T>) =
+ walk (firstChoiceLoop chooser) (firstChoiceLoop chooser) queue
+
+/// The first elements of a queue, at most the count, and whether the queue has more elements.
+let front (count : int) (queue : IImmutableQueue<'T>) = walk (frontLoop count) (frontLoop count) queue
+
+/// The index and the last element that satisfies the predicate, or ValueNone; List searches a copy from the end too. A
+/// null queue throws ArgumentNullException for "queue", the name of the parameter in both modules.
+let lastMatch (predicate : 'T -> bool) (queue : IImmutableQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ 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]))
diff --git a/src/FSharp.Collections.Immutable/Queue.fs b/src/FSharp.Collections.Immutable/Queue.fs
deleted file mode 100644
index ba9d1f39..00000000
--- a/src/FSharp.Collections.Immutable/Queue.fs
+++ /dev/null
@@ -1,132 +0,0 @@
-namespace FSharp.Collections.Immutable
-
-type IQueue<'T> = System.Collections.Immutable.IImmutableQueue<'T>
-
-type Queue<'T> = System.Collections.Immutable.ImmutableQueue<'T>
-
-[]
-module Queue =
-
- type internal QueueFactory = System.Collections.Immutable.ImmutableQueue
-
- let inline private check (queue : IQueue<_>) = checkNotNull (nameof queue) queue
-
- let inline empty<'T> : Queue<'T> = QueueFactory.Create<'T>()
-
- let inline singleton<'T> (item : 'T) : Queue<'T> = QueueFactory.Create<'T>(item)
-
- let inline ofSeq (source : 'T seq) : Queue<'T> = QueueFactory.CreateRange source
-
- let inline toSeq (queue : Queue<_>) = queue :> seq<_>
-
- let isEmpty queue =
- check queue
- queue.IsEmpty
-
- let clear queue : IQueue<_> =
- check queue
- queue.Clear ()
-
- let enqueue item queue : IQueue<_> =
- check queue
- queue.Enqueue item
-
- let head queue =
- check queue
- queue.Peek ()
-
- let tail queue : IQueue<_> =
- check queue
- queue.Dequeue ()
-
- //////////
-
- let (|Cons|Nil|) queue = // consider renaming
- if isEmpty queue then Nil else Cons (head queue, tail queue)
-
- ////////// Predicate based //////////
-
- let filter predicate queue =
- let rec loop queue result =
- match queue with
- | Cons (head, tail) ->
- loop tail
- <| if predicate head then enqueue head result else result
- | Nil -> result
-
- loop queue <| clear queue
-
- ////////// Seq-based //////////
-
- let find predicate queue =
- check queue
- Seq.find predicate queue
-
- let tryFind predicate queue =
- check queue
- Seq.tryFind predicate queue
-
- let findIndex predicate queue =
- check queue
- Seq.findIndex predicate queue
-
- let tryFindIndex predicate queue =
- check queue
- Seq.tryFindIndex predicate queue
-
- let pick chooser queue =
- check queue
- Seq.pick chooser queue
-
- let tryPick chooser queue =
- check queue
- Seq.tryPick chooser queue
-
- let iter action queue =
- check queue
- Seq.iter action queue
-
- let iteri action queue =
- check queue
- Seq.iteri action queue
-
- let iter2 action (queue1 : IQueue<_>) (queue2 : IQueue<_>) =
- checkNotNull (nameof queue1) queue1
- checkNotNull (nameof queue2) queue2
- Seq.iter2 action queue1 queue2
-
- let fold folder state queue =
- check queue
- Seq.fold folder state queue
-
- let forall predicate queue =
- check queue
- Seq.forall predicate
-
- let exists predicate queue =
- check queue
- Seq.exists predicate queue
-
- let reduce reduction queue =
- check queue
- Seq.reduce reduction queue
-
- let inline sum queue =
- check queue
- Seq.sum queue
-
- let inline sumBy projection queue =
- check queue
- Seq.sumBy projection queue
-
- let inline average queue =
- check queue
- Seq.average queue
-
- let inline averageBy projection queue =
- check queue
- Seq.averageBy projection
-
-module ImmutableQueue = Queue
diff --git a/src/FSharp.Collections.Immutable/Queue/IQueue.fs b/src/FSharp.Collections.Immutable/Queue/IQueue.fs
new file mode 100644
index 00000000..de6393ff
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Queue/IQueue.fs
@@ -0,0 +1,945 @@
+[]
+module FSharp.Collections.Immutable.IQueue
+
+open System
+open System.Collections.Immutable
+open System.Linq
+
+// The interface has no members beyond Clear, Enqueue, Dequeue and Peek, so the functions walk the elements, here or in the
+// walks of Queues that Queue shares, or take a snapshot of them, apply an Array function or a shared algorithm and build the
+// result. A result with the element type of the input keeps the implementation of the input: it comes from Clear and
+// Enqueue, or from Dequeue. A result of another element type is an ImmutableQueue. The checks of ListChecks throw what the
+// List functions throw (ADR 0001, D5 and D6); the lengths that they check come from Lengths.queueUpTo.
+//
+// The module does not open System.Collections.Generic: its Queue would shadow the Queue<'T> of this namespace.
+
+/// A queue of the implementation of the input with the given elements: the empty queue of the input, with the elements
+/// enqueued in their order.
+let private rebuild (queue : IQueue<'T>) (items : seq<'T>) =
+ let mutable result = queue.Clear ()
+
+ for item in items do
+ result <- result.Enqueue item
+
+ result
+
+// ---- Properties ----
+
+[]
+let isEmpty (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ queue.IsEmpty
+
+[]
+let length (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let mutable count = 0
+
+ for _ in queue do
+ count <- count + 1
+
+ count
+
+// ---- Access ----
+
+[]
+let exactlyOne (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ // List.exactlyOne reports its input under the name "source"
+ ListChecks.exactlyOne "source" (Lengths.queueUpTo 2 queue)
+ queue.Peek ()
+
+[]
+let head (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ queue.Peek ()
+
+[]
+let last (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ Enumerable.Last queue
+
+[]
+let tail (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ queue.Dequeue ()
+
+[]
+let tryExactlyOne (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue = 1 then
+ ValueSome (queue.Peek ())
+ else
+ ValueNone
+
+[]
+let tryHead (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if queue.IsEmpty then
+ ValueNone
+ else
+ ValueSome (queue.Peek ())
+
+[]
+let tryLast (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if queue.IsEmpty then
+ ValueNone
+ else
+ ValueSome (Enumerable.Last queue)
+
+[]
+let tryTail (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if queue.IsEmpty then
+ ValueNone
+ else
+ ValueSome (queue.Dequeue ())
+
+[]
+let tryUncons (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if queue.IsEmpty then
+ ValueNone
+ else
+ ValueSome (struct (queue.Peek (), queue.Dequeue ()))
+
+[]
+let uncons (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ // An empty queue throws what List.head throws
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ struct (queue.Peek (), queue.Dequeue ())
+
+[]
+[]
+let (|Cons|_|) (queue : IQueue<'T>) = tryUncons queue
+
+[]
+let (|Nil|_|) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ queue.IsEmpty
+
+// ---- Search ----
+
+[]
+let contains (value : 'T) (source : IQueue<'T>) =
+ checkNotNull (nameof source) source
+ // F# equality with the value on the left, as List.contains compares
+ Seq.contains value source
+
+[]
+let exists (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ Seq.exists predicate queue
+
+[]
+let exists2 predicate (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.exists2 (nameof queue2) predicate (Snapshot.ofSeq (nameof queue1) queue1) (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let find (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ match Queues.firstMatch predicate queue with
+ | ValueSome (struct (_, item)) -> item
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findBack (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ match Queues.lastMatch predicate queue with
+ | ValueSome (struct (_, item)) -> item
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findIndex (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ match Queues.firstMatch predicate queue with
+ | ValueSome (struct (index, _)) -> index
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let findIndexBack (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ match Queues.lastMatch predicate queue with
+ | ValueSome (struct (index, _)) -> index
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let forall (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ Seq.forall predicate queue
+
+[]
+let forall2 predicate (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.forall2
+ (nameof queue1)
+ (nameof queue2)
+ predicate
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let pick (chooser : 'T -> 'U voption) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ match Queues.firstChoice chooser queue with
+ | ValueSome value -> value
+ | ValueNone -> Raise.indexNotFound ()
+
+[]
+let tryFind (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ match Queues.firstMatch predicate queue with
+ | ValueSome (struct (_, item)) -> ValueSome item
+ | ValueNone -> ValueNone
+
+[]
+let tryFindBack (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ match Queues.lastMatch predicate queue with
+ | ValueSome (struct (_, item)) -> ValueSome item
+ | ValueNone -> ValueNone
+
+[]
+let tryFindIndex (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ match Queues.firstMatch predicate queue with
+ | ValueSome (struct (index, _)) -> ValueSome index
+ | ValueNone -> ValueNone
+
+[]
+let tryFindIndexBack (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ match Queues.lastMatch predicate queue with
+ | ValueSome (struct (index, _)) -> ValueSome index
+ | ValueNone -> ValueNone
+
+[]
+let tryPick (chooser : 'T -> 'U voption) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ Queues.firstChoice chooser queue
+
+// ---- Transformation ----
+
+[]
+let allPairs (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ Arrays.allPairs structPair (Snapshot.ofSeq (nameof queue1) queue1) (Snapshot.ofSeq (nameof queue2) queue2)
+ |> Queue.ofArray
+
+[]
+let append (queue1 : IQueue<'T>) (queue2 : IQueue<'T>) =
+ checkNotNull (nameof queue1) queue1
+ checkNotNull (nameof queue2) queue2
+ // Enqueueing into the first queue keeps its implementation, also when it is empty
+ let mutable result = queue1
+
+ for item in queue2 do
+ result <- result.Enqueue item
+
+ result
+
+[]
+let choose (chooser : 'T -> 'U voption) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let chosen = ResizeArray<'U>()
+
+ for item in queue do
+ match chooser item with
+ | ValueSome value -> chosen.Add value
+ | ValueNone -> ()
+
+ Queues.ofBuffer chosen
+
+[]
+let chunkBySize chunkSize (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.positive (nameof chunkSize) chunkSize
+
+ Array.chunkBySize chunkSize (Snapshot.ofSeq (nameof queue) queue)
+ |> Array.map Queue.ofArray
+ |> Queue.ofArray
+
+[]
+let collect (mapping : 'T -> IQueue<'U>) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let collected = ResizeArray<'U>()
+
+ for item in queue do
+ collected.AddRange (mapping item)
+
+ Queues.ofBuffer collected
+
+[]
+let concat (queues : seq>) =
+ checkNotNullOrDefaultSeq (nameof queues) queues
+ let concatenated = ResizeArray<'T>()
+
+ for queue in queues do
+ // A null queue is a null input, as a null queue is for Queue.concat (ADR 0001, D6)
+ checkNotNull (nameof queues) queue
+ concatenated.AddRange queue
+
+ Queues.ofBuffer concatenated
+
+[]
+let enqueue (value : 'T) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ queue.Enqueue value
+
+[]
+let enqueueMany (values : seq<'T>) (queue : IQueue<'T>) =
+ checkNotNullOrDefaultSeq (nameof values) values
+ checkNotNull (nameof queue) queue
+ let mutable result = queue
+
+ for value in values do
+ result <- result.Enqueue value
+
+ result
+
+[]
+let filter (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ rebuild queue (Array.filter predicate array)
+
+[]
+let indexed (queue : IQueue<'T>) =
+ Arrays.indexed structPair (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let map (mapping : 'T -> 'U) (queue : IQueue<'T>) =
+ Array.map mapping (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let map2 (mapping : 'T1 -> 'T2 -> 'U) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.map2
+ (nameof queue1)
+ (nameof queue2)
+ mapping
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+ |> Queue.ofArray
+
+[]
+let map3 (mapping : 'T1 -> 'T2 -> 'T3 -> 'U) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) (queue3 : IQueue<'T3>) =
+ ListArrays.map3
+ (nameof queue1)
+ (nameof queue2)
+ (nameof queue3)
+ mapping
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+ (Snapshot.ofSeq (nameof queue3) queue3)
+ |> Queue.ofArray
+
+[]
+let mapi (mapping : int -> 'T -> 'U) (queue : IQueue<'T>) =
+ Array.mapi mapping (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let mapi2 (mapping : int -> 'T1 -> 'T2 -> 'U) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.mapi2
+ (nameof queue1)
+ (nameof queue2)
+ mapping
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+ |> Queue.ofArray
+
+[]
+let pairwise (queue : IQueue<'T>) =
+ Arrays.pairwise structPair (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let permute indexMap (queue : IQueue<'T>) =
+ // List.permute applies the algorithm of Array.permute to a copy of the list, with the same checks
+ let array = Snapshot.ofSeq (nameof queue) queue
+ rebuild queue (Array.permute indexMap array)
+
+[]
+let rev (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ rebuild queue (Array.rev array)
+
+[]
+let transpose (queues : seq>) =
+ checkNotNullOrDefaultSeq (nameof queues) queues
+
+ let arrays =
+ queues
+ |> Seq.map (fun queue -> Snapshot.ofSeq (nameof queues) queue)
+ |> Seq.toArray
+
+ // List.transpose names the lists "list.[0]", "list.[1]", …; this module names them "queue.[0]", "queue.[1]", …
+ // Once their lengths agree, Array.transpose agrees with List.transpose
+ ListChecks.transpose "queue" (arrays |> Array.map _.Length)
+
+ Array.transpose arrays
+ |> Array.map Queue.ofArray
+ |> Queue.ofArray
+
+[]
+let unzip (queue : IQueue) =
+ let struct (first, second) = Arrays.unzip id (Snapshot.ofSeq (nameof queue) queue)
+ struct (Queue.ofArray first, Queue.ofArray second)
+
+[]
+let unzip3 (queue : IQueue) =
+ let struct (first, second, third) = Arrays.unzip3 id (Snapshot.ofSeq (nameof queue) queue)
+ struct (Queue.ofArray first, Queue.ofArray second, Queue.ofArray third)
+
+[]
+let where (predicate : 'T -> bool) (queue : IQueue<'T>) = filter predicate queue
+
+[]
+let windowed windowSize (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.positive (nameof windowSize) windowSize
+
+ Array.windowed windowSize (Snapshot.ofSeq (nameof queue) queue)
+ |> Array.map Queue.ofArray
+ |> Queue.ofArray
+
+[]
+let zip (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ let array1 = Snapshot.ofSeq (nameof queue1) queue1
+ let array2 = Snapshot.ofSeq (nameof queue2) queue2
+ ListChecks.sameLengths (nameof queue1) array1.Length (nameof queue2) array2.Length
+ Arrays.zip structPair array1 array2 |> Queue.ofArray
+
+[]
+let zip3 (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) (queue3 : IQueue<'T3>) =
+ let array1 = Snapshot.ofSeq (nameof queue1) queue1
+ let array2 = Snapshot.ofSeq (nameof queue2) queue2
+ let array3 = Snapshot.ofSeq (nameof queue3) queue3
+ ListChecks.zip3 (nameof queue1) array1.Length (nameof queue2) array2.Length (nameof queue3) array3.Length
+ Arrays.zip3 structTriple array1 array2 array3
+ |> Queue.ofArray
+
+// ---- Folding ----
+
+// The inline functions can use public members only, so they check the queue themselves and count its elements while
+// they add them; the empty queue throws what List.average and List.averageBy throw, with the name "source"
+[]
+let inline average (queue : IQueue< ^T >) : ^T =
+ if obj.ReferenceEquals (queue, null) then
+ nullArg (nameof queue)
+
+ if queue.IsEmpty then
+ invalidArg "source" ErrorStrings.InputSequenceEmpty
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^T>
+ let mutable count = 0
+
+ for item in queue do
+ sum <- Checked.(+) sum item
+ count <- count + 1
+
+ LanguagePrimitives.DivideByInt< ^T> sum count
+
+[]
+let inline averageBy ([] projection : 'T -> ^U) (queue : IQueue<'T>) : ^U =
+ if obj.ReferenceEquals (queue, null) then
+ nullArg (nameof queue)
+
+ if queue.IsEmpty then
+ invalidArg "source" ErrorStrings.InputSequenceEmpty
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^U>
+ let mutable count = 0
+
+ for item in queue do
+ sum <- Checked.(+) sum (projection item)
+ count <- count + 1
+
+ LanguagePrimitives.DivideByInt< ^U> sum count
+
+[]
+let compareWith (comparer : 'T -> 'T -> int) (queue1 : IQueue<'T>) (queue2 : IQueue<'T>) =
+ // Array.compareWith compares as List.compareWith does: the first difference, then the lengths
+ Array.compareWith comparer (Snapshot.ofSeq (nameof queue1) queue1) (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let fold (folder : 'State -> 'T -> 'State) (state : 'State) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let mutable state = state
+
+ for item in queue do
+ state <- folder state item
+
+ state
+
+[]
+let fold2 (folder : 'State -> 'T1 -> 'T2 -> 'State) (state : 'State) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.fold2
+ (nameof queue1)
+ (nameof queue2)
+ folder
+ state
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let foldBack (folder : 'T -> 'State -> 'State) (queue : IQueue<'T>) (state : 'State) =
+ Array.foldBack folder (Snapshot.ofSeq (nameof queue) queue) state
+
+[]
+let foldBack2 (folder : 'T1 -> 'T2 -> 'State -> 'State) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) (state : 'State) =
+ let array1 = Snapshot.ofSeq (nameof queue1) queue1
+ let array2 = Snapshot.ofSeq (nameof queue2) queue2
+ // List.foldBack2 checks the lengths before it calls the folder
+ ListChecks.foldBack2 (nameof queue1) array1.Length (nameof queue2) array2.Length
+ Array.foldBack2 folder array1 array2 state
+
+[]
+let iter (action : 'T -> unit) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ for item in queue do
+ action item
+
+[]
+let iter2 (action : 'T1 -> 'T2 -> unit) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.iter2
+ (nameof queue1)
+ (nameof queue2)
+ action
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let iteri (action : int -> 'T -> unit) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let mutable index = 0
+
+ for item in queue do
+ action index item
+ index <- index + 1
+
+[]
+let iteri2 (action : int -> 'T1 -> 'T2 -> unit) (queue1 : IQueue<'T1>) (queue2 : IQueue<'T2>) =
+ ListArrays.iteri2
+ (nameof queue1)
+ (nameof queue2)
+ action
+ (Snapshot.ofSeq (nameof queue1) queue1)
+ (Snapshot.ofSeq (nameof queue2) queue2)
+
+[]
+let mapFold (mapping : 'State -> 'T -> struct ('Result * 'State)) (state : 'State) (queue : IQueue<'T>) =
+ let source = Snapshot.ofSeq (nameof queue) queue
+ 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 (Queue.ofArray results, state)
+
+[]
+let mapFoldBack (mapping : 'T -> 'State -> struct ('Result * 'State)) (queue : IQueue<'T>) (state : 'State) =
+ let source = Snapshot.ofSeq (nameof queue) queue
+ 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 (Queue.ofArray results, state)
+
+// Seq.max and the others compare as the List functions do once the queue has elements, so the List checks come first
+
+[]
+let max (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmptySequence (nameof queue) (Lengths.queueUpTo 1 queue)
+ Seq.max queue
+
+[]
+let maxBy (projection : 'T -> 'U) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmptySequence (nameof queue) (Lengths.queueUpTo 1 queue)
+ Seq.maxBy projection queue
+
+[]
+let min (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmptySequence (nameof queue) (Lengths.queueUpTo 1 queue)
+ Seq.min queue
+
+[]
+let minBy (projection : 'T -> 'U) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmptySequence (nameof queue) (Lengths.queueUpTo 1 queue)
+ Seq.minBy projection queue
+
+[]
+let reduce (reduction : 'T -> 'T -> 'T) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ Seq.reduce reduction queue
+
+[]
+let reduceBack (reduction : 'T -> 'T -> 'T) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.notEmpty (nameof queue) (Lengths.queueUpTo 1 queue)
+ Array.reduceBack reduction (Snapshot.ofSeq (nameof queue) queue)
+
+[]
+let scan (folder : 'State -> 'T -> 'State) (state : 'State) (queue : IQueue<'T>) =
+ Array.scan folder state (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let scanBack (folder : 'T -> 'State -> 'State) (queue : IQueue<'T>) (state : 'State) =
+ Array.scanBack folder (Snapshot.ofSeq (nameof queue) queue) state
+ |> Queue.ofArray
+
+[]
+let inline sum (queue : IQueue< ^T >) : ^T =
+ if obj.ReferenceEquals (queue, null) then
+ nullArg (nameof queue)
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^T>
+
+ for item in queue do
+ sum <- Checked.(+) sum item
+
+ sum
+
+[]
+let inline sumBy ([] projection : 'T -> ^U) (queue : IQueue<'T>) : ^U =
+ if obj.ReferenceEquals (queue, null) then
+ nullArg (nameof queue)
+
+ let mutable sum = LanguagePrimitives.GenericZero< ^U>
+
+ for item in queue do
+ sum <- Checked.(+) sum (projection item)
+
+ sum
+
+// ---- Partitioning and grouping ----
+
+[]
+let countBy (projection : 'T -> 'Key) (queue : IQueue<'T>) =
+ Arrays.countBy structPair projection (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let distinct (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ rebuild queue (Array.distinct array)
+
+[]
+let distinctBy (projection : 'T -> 'Key) (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ rebuild queue (Array.distinctBy projection array)
+
+[]
+let except (itemsToExclude : seq<'T>) (queue : IQueue<'T>) =
+ // List.except checks the items to exclude first and does not read them for an empty list
+ checkNotNullOrDefaultSeq (nameof itemsToExclude) itemsToExclude
+ checkNotNull (nameof queue) queue
+
+ if queue.IsEmpty then
+ queue
+ else
+ rebuild queue (Array.except itemsToExclude (Snapshot.ofSeq (nameof queue) queue))
+
+[]
+let groupBy (projection : 'T -> 'Key) (queue : IQueue<'T>) =
+ Arrays.groupBy (fun key items -> struct (key, Queue.ofArray items)) projection (Snapshot.ofSeq (nameof queue) queue)
+ |> Queue.ofArray
+
+[]
+let partition (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ let accepted, rejected = Array.partition predicate (Snapshot.ofSeq (nameof queue) queue)
+ struct (rebuild queue accepted, rebuild queue rejected)
+
+[]
+let partitionWith (partitioner : 'T -> Choice<'T1, 'T2>) (queue : IQueue<'T>) =
+ let first, second = Array.partitionWith partitioner (Snapshot.ofSeq (nameof queue) queue)
+ struct (Queue.ofArray first, Queue.ofArray second)
+
+[]
+let skip count (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let mutable rest = queue
+ let mutable skipped = 0
+
+ while skipped < count && not rest.IsEmpty do
+ rest <- rest.Dequeue ()
+ skipped <- skipped + 1
+
+ // The loop stops at the count or at the end of the queue, where the number of skipped elements is the length; a
+ // count beyond the length throws what List.skip throws
+ ListChecks.skip (nameof count) count skipped
+ rest
+
+[]
+let skipLast count (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ // The checks of List.skip
+ ListChecks.skip (nameof count) count array.Length
+
+ if count <= 0 then
+ queue
+ else
+ rebuild queue (ArraySegment<'T>(array, 0, array.Length - count))
+
+[]
+let skipWhile (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let mutable rest = queue
+
+ while not rest.IsEmpty && predicate (rest.Peek ()) do
+ rest <- rest.Dequeue ()
+
+ rest
+
+[]
+let splitAt index (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let before = ResizeArray<'T>()
+ let mutable rest = queue
+
+ while before.Count < index && not rest.IsEmpty do
+ before.Add (rest.Peek ())
+ rest <- rest.Dequeue ()
+
+ // The loop stops at the index or at the end of the queue, where the number of taken elements is the length
+ ListChecks.splitAt (nameof index) index before.Count
+ struct ((if rest.IsEmpty then queue else rebuild queue before), rest)
+
+[]
+let splitInto count (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ ListChecks.positive (nameof count) count
+
+ Array.splitInto count (Snapshot.ofSeq (nameof queue) queue)
+ |> Array.map Queue.ofArray
+ |> Queue.ofArray
+
+[]
+let take count (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let struct (items, more) = Queues.front count queue
+ // The front stops at the count or at the end of the queue, where the number of taken elements is the length
+ ListChecks.take (nameof count) count items.Count
+ if more then rebuild queue items else queue
+
+[]
+let takeLast count (queue : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof queue) queue
+ // The checks of List.take
+ ListChecks.take (nameof count) count array.Length
+
+ if count = array.Length then
+ queue
+ else
+ rebuild queue (ArraySegment<'T>(array, array.Length - count, count))
+
+[]
+let takeWhile (predicate : 'T -> bool) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let items = ResizeArray<'T>()
+ use enumerator = queue.GetEnumerator ()
+ let mutable more = enumerator.MoveNext ()
+
+ while more && predicate enumerator.Current do
+ items.Add enumerator.Current
+ more <- enumerator.MoveNext ()
+
+ if more then rebuild queue items else queue
+
+[]
+let truncate count (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ let struct (items, more) = Queues.front count queue
+ if more then rebuild queue items else queue
+
+// ---- 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. A queue of one element is returned
+// without a call of the projection or the comparer, as List returns a list of one element.
+
+[]
+let sort (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (Seq.sort queue)
+
+[]
+let sortBy (projection : 'T -> 'Key) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (queue.OrderBy(Func<'T, 'Key> projection, DefaultComparer<'Key>.Instance).ToArray())
+
+[]
+let sortByDescending (projection : 'T -> 'Key) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (DescendingSorts.sortByDescending projection queue)
+
+[]
+let sortDescending (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (DescendingSorts.sortDescending queue)
+
+[]
+let sortWith (comparer : 'T -> 'T -> int) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (Seq.sortWith comparer queue)
+
+[]
+let sortWithComparer (comparer : System.Collections.Generic.IComparer<'T>) (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+
+ if Lengths.queueUpTo 2 queue <= 1 then
+ queue
+ else
+ rebuild queue (queue.Order(comparer).ToArray())
+
+// ---- Random ----
+
+// The functions draw the random numbers as the List functions do: they take a snapshot, run the List checks with its
+// length and choose from it, or apply the Array function to it
+
+[]
+let randomChoice (source : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'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 : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlace array
+ rebuild source array
+
+[]
+let randomShuffleBy (randomizer : unit -> float) (source : IQueue<'T>) =
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlaceBy randomizer array
+ rebuild source array
+
+[]
+let randomShuffleWith (random : Random) (source : IQueue<'T>) =
+ checkNotNull (nameof random) random
+ let array = Snapshot.ofSeq (nameof source) source
+ Array.randomShuffleInPlaceWith random array
+ rebuild source array
+
+// ---- Conversion ----
+
+[]
+let toArray (queue : IQueue<'T>) = Snapshot.ofSeq (nameof queue) queue
+
+[]
+let toList (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ List.ofSeq queue
+
+[]
+let toSeq (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ queue :> seq<'T>
+
+// ---- Type-specific ----
+
+[]
+let clear (queue : IQueue<'T>) =
+ checkNotNull (nameof queue) queue
+ queue.Clear ()
diff --git a/src/FSharp.Collections.Immutable/Queue/IQueue.fsi b/src/FSharp.Collections.Immutable/Queue/IQueue.fsi
new file mode 100644
index 00000000..62c65b1c
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/Queue/IQueue.fsi
@@ -0,0 +1,1432 @@
+///
+/// Functions for , the interface of the immutable queues.
+///
+///
+///
+/// The module mirrors as
+/// does, over the elements in the order in which they leave the queue:
+/// each function has the semantics, the argument checks and the exceptions of the function with the same name in that
+/// module (ADR 0001), and its sorting functions are stable. The module has no creation functions, because an interface
+/// cannot create a queue.
+///
+///
+/// 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, followed by
+/// , or
+/// . A function that changes the element type, or
+/// that combines several queues into one, returns an .
+///
+///
+/// Every function throws for a queue. The
+/// complexities in the remarks are the ones of an , which
+/// does not store its length.
+///
+///
+/// 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.IQueue
+
+open System
+open System.Collections.Immutable
+
+// ---- Properties ----
+
+/// Returns whether the queue has no elements.
+/// The input queue.
+/// when the queue is empty.
+/// O(1).
+[]
+val isEmpty : queue : IQueue<'T> -> bool
+
+///
+/// Returns the number of elements of the queue.
+///
+/// The input queue.
+/// The length of the queue.
+///
+/// O(n): the interface has no length, so the function counts the elements.
+///
+[]
+val length : queue : IQueue<'T> -> int
+
+// ---- Access ----
+
+/// Returns the only element of the queue.
+/// The input queue.
+/// The only element.
+/// Thrown when the queue does not have exactly one element.
+/// Amortized O(1).
+[]
+val exactlyOne : queue : IQueue<'T> -> 'T
+
+///
+/// Returns the first element of the queue, the one that leaves it next.
+///
+/// The input queue.
+/// The first element.
+/// Thrown when the queue is empty.
+///
+/// O(1). The function wraps .
+///
+[]
+val head : queue : IQueue<'T> -> 'T
+
+///
+/// Returns the last element of the queue, the one that joined it last.
+///
+/// The input queue.
+/// The last element.
+/// Thrown when the queue is empty.
+///
+/// O(n). The function wraps .
+///
+[]
+val last : queue : IQueue<'T> -> 'T
+
+///
+/// Returns the queue without its first element.
+///
+/// The input queue.
+/// The elements after the first one, in a queue of the implementation of the input.
+/// Thrown when the queue is empty.
+///
+/// Amortized O(1). The function wraps .
+///
+[]
+val tail : queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the only element of the queue, or ValueNone when it does not have exactly one element.
+///
+/// The input queue.
+/// The only element, or ValueNone.
+///
+/// Amortized O(1).
+///
+[]
+val tryExactlyOne : queue : IQueue<'T> -> 'T voption
+
+///
+/// Returns the first element of the queue, or ValueNone when it is empty.
+///
+/// The input queue.
+/// The first element, or ValueNone.
+///
+/// O(1).
+///
+[]
+val tryHead : queue : IQueue<'T> -> 'T voption
+
+///
+/// Returns the last element of the queue, or ValueNone when it is empty.
+///
+/// The input queue.
+/// The last element, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryLast : queue : IQueue<'T> -> 'T voption
+
+///
+/// Returns the queue without its first element, or ValueNone when it is empty.
+///
+/// The input queue.
+/// The elements after the first one, in a queue of the implementation of the input, or ValueNone.
+///
+/// Amortized O(1).
+///
+[]
+val tryTail : queue : IQueue<'T> -> IQueue<'T> voption
+
+///
+/// Returns the first element and the other elements, or ValueNone when the queue is empty.
+///
+/// The input queue.
+/// The first element and the elements after it, or ValueNone.
+///
+/// Amortized O(1).
+///
+[]
+val tryUncons : queue : IQueue<'T> -> struct ('T * IQueue<'T>) voption
+
+///
+/// Returns the first element and the other elements.
+///
+/// The input queue.
+/// The first element and the elements after it, in a queue of the implementation of the input.
+/// Thrown when the queue is empty.
+///
+/// Amortized O(1). The function throws what
+/// throws.
+///
+[]
+val uncons : queue : IQueue<'T> -> struct ('T * IQueue<'T>)
+
+///
+/// Matches a queue with a first element, and returns that element and the other elements.
+///
+/// The input queue.
+/// The first element and the elements after it, or ValueNone for an empty queue.
+///
+/// Amortized O(1). A struct partial pattern, so that a match allocates no option.
+///
+[]
+[]
+val (|Cons|_|) : queue : IQueue<'T> -> struct ('T * IQueue<'T>) voption
+
+/// Matches an empty queue.
+/// The input queue.
+/// when the queue is empty.
+/// O(1).
+[]
+val (|Nil|_|) : queue : IQueue<'T> -> bool
+
+// ---- Search ----
+
+///
+/// Returns whether the queue contains an element equal to a value, in F# structural equality.
+///
+/// The value to find.
+/// The input queue.
+/// when an element equals the value.
+///
+/// O(n).
+///
+[]
+val contains : value : 'T -> source : IQueue<'T> -> bool when 'T : equality
+
+/// Returns whether any element satisfies the predicate.
+/// The function to test the elements.
+/// The input queue.
+/// when an element satisfies the predicate.
+/// O(n).
+[]
+val exists : predicate : ('T -> bool) -> queue : IQueue<'T> -> bool
+
+///
+/// Returns whether any pair of elements at the same index satisfies the predicate.
+///
+/// The function to test the pairs.
+/// The first queue.
+/// The second queue.
+/// when a pair satisfies the predicate.
+/// Thrown when no pair satisfies the predicate and the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val exists2 : predicate : ('T1 -> 'T2 -> bool) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> bool
+
+/// Returns the first element that satisfies the predicate.
+/// The function to test the elements.
+/// The input queue.
+/// The first element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+/// O(n).
+[]
+val find : predicate : ('T -> bool) -> queue : IQueue<'T> -> 'T
+
+/// Returns the last element that satisfies the predicate.
+/// The function to test the elements.
+/// The input queue.
+/// The last element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+/// O(n).
+[]
+val findBack : predicate : ('T -> bool) -> queue : IQueue<'T> -> 'T
+
+///
+/// Returns the index of the first element that satisfies the predicate.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The index of the first element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+///
+/// O(n).
+///
+[]
+val findIndex : predicate : ('T -> bool) -> queue : IQueue<'T> -> int
+
+///
+/// Returns the index of the last element that satisfies the predicate.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The index of the last element that satisfies the predicate.
+/// Thrown when no element satisfies the predicate.
+///
+/// O(n).
+///
+[]
+val findIndexBack : predicate : ('T -> bool) -> queue : IQueue<'T> -> int
+
+/// Returns whether all elements satisfy the predicate.
+/// The function to test the elements.
+/// The input queue.
+/// when every element satisfies the predicate.
+/// O(n).
+[]
+val forall : predicate : ('T -> bool) -> queue : IQueue<'T> -> bool
+
+///
+/// Returns whether all pairs of elements at the same index satisfy the predicate.
+///
+/// The function to test the pairs.
+/// The first queue.
+/// The second queue.
+/// when every pair satisfies the predicate.
+/// Thrown when every pair satisfies the predicate and the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val forall2 : predicate : ('T1 -> 'T2 -> bool) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'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 queue.
+/// The first chosen value.
+/// Thrown when the chooser returns ValueNone for every element.
+///
+/// O(n).
+///
+[]
+val pick : chooser : ('T -> 'U voption) -> queue : IQueue<'T> -> 'U
+
+///
+/// Returns the first element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The first element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFind : predicate : ('T -> bool) -> queue : IQueue<'T> -> 'T voption
+
+///
+/// Returns the last element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The last element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindBack : predicate : ('T -> bool) -> queue : IQueue<'T> -> 'T voption
+
+///
+/// Returns the index of the first element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The index of the first element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindIndex : predicate : ('T -> bool) -> queue : IQueue<'T> -> int voption
+
+///
+/// Returns the index of the last element that satisfies the predicate, or ValueNone.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The index of the last element that satisfies the predicate, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryFindIndexBack : predicate : ('T -> bool) -> queue : IQueue<'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 queue.
+/// The first chosen value, or ValueNone.
+///
+/// O(n).
+///
+[]
+val tryPick : chooser : ('T -> 'U voption) -> queue : IQueue<'T> -> 'U voption
+
+// ---- Transformation ----
+
+///
+/// Returns every element of the first queue paired with every element of the second one.
+///
+/// The first queue.
+/// The second queue.
+/// The pairs, in the order of the first queue and then of the second one.
+///
+/// O(n × m).
+///
+[]
+val allPairs : queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> Queue
+
+///
+/// Returns a queue of the elements of the first queue followed by the elements of the second one.
+///
+/// The first queue.
+/// The second queue.
+/// The concatenation, in a queue of the implementation of the first queue.
+///
+///
+/// O(m), where m is the length of the second queue: the function enqueues its elements into the first queue with
+/// .
+///
+///
+/// The result keeps the implementation of the first queue, which receives the elements, as
+/// keeps it (ADR 0001, D14). It does so also
+/// when the first queue is empty: the function does not return the second queue then, as
+///
+/// does, because the second queue can have another implementation.
+///
+///
+[]
+val append : queue1 : IQueue<'T> -> queue2 : IQueue<'T> -> IQueue<'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 queue.
+/// The chosen values.
+///
+/// O(n).
+///
+[]
+val choose : chooser : ('T -> 'U voption) -> queue : IQueue<'T> -> Queue<'U>
+
+/// Divides the queue into chunks of a given size.
+/// The maximum size of a chunk.
+/// The input queue.
+/// The chunks; the last one can be smaller.
+/// Thrown when is not positive.
+/// O(n).
+[]
+val chunkBySize : chunkSize : int -> queue : IQueue<'T> -> Queue>
+
+///
+/// Concatenates the queues that a function returns for the elements.
+///
+/// The function that returns a queue for each element.
+/// The input queue.
+/// The concatenation of the returned queues.
+///
+/// O(n + the total length of the returned queues).
+///
+[]
+val collect : mapping : ('T -> IQueue<'U>) -> queue : IQueue<'T> -> Queue<'U>
+
+/// Concatenates a sequence of queues.
+/// The queues.
+/// The concatenation, in an immutable queue, because the queues can have different implementations.
+/// Thrown when is or a default flat list, or one of its queues is .
+/// O(the total length).
+[]
+val concat : queues : seq> -> Queue<'T>
+
+///
+/// Returns a queue with a value added after the last element.
+///
+/// The value to add.
+/// The input queue.
+/// A queue of the implementation of the input with the elements and the value, which leaves it last.
+///
+/// O(1). The function wraps .
+///
+[]
+val enqueue : value : 'T -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns a queue with values added after the last element, in their order.
+///
+/// The values to add.
+/// The input queue.
+/// A queue of the implementation of the input with the elements and then the values.
+/// Thrown when is or a default flat list.
+///
+/// O(m), where m is the number of values; without values, the function returns the input. The function calls
+/// for each value.
+///
+[]
+val enqueueMany : values : seq<'T> -> queue : IQueue<'T> -> IQueue<'T>
+
+/// Returns the elements that satisfy the predicate.
+/// The function to test the elements.
+/// The input queue.
+/// The elements that satisfy the predicate, in their order, in a queue of the implementation of the input.
+/// O(n).
+[]
+val filter : predicate : ('T -> bool) -> queue : IQueue<'T> -> IQueue<'T>
+
+/// Returns every element with its index.
+/// The input queue.
+/// The pairs of index and element.
+/// O(n).
+[]
+val indexed : queue : IQueue<'T> -> Queue
+
+/// 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 queue.
+/// The results, in the order of the elements.
+/// O(n).
+[]
+val map : mapping : ('T -> 'U) -> queue : IQueue<'T> -> Queue<'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 queue.
+/// The second queue.
+/// The results.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val map2 : mapping : ('T1 -> 'T2 -> 'U) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> Queue<'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 queue.
+/// The second queue.
+/// The third queue.
+/// The results.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val map3 :
+ mapping : ('T1 -> 'T2 -> 'T3 -> 'U) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> queue3 : IQueue<'T3> -> Queue<'U>
+
+///
+/// Returns the results of a function applied to each element and its index.
+///
+/// The function to transform the elements.
+/// The input queue.
+/// The results.
+///
+/// O(n).
+///
+[]
+val mapi : mapping : (int -> 'T -> 'U) -> queue : IQueue<'T> -> Queue<'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 queue.
+/// The second queue.
+/// The results.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val mapi2 : mapping : (int -> 'T1 -> 'T2 -> 'U) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> Queue<'U>
+
+/// Returns every element with its successor.
+/// The input queue.
+/// The pairs of neighbours; empty for fewer than two elements.
+/// O(n).
+[]
+val pairwise : queue : IQueue<'T> -> Queue
+
+///
+/// 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 queue.
+/// The permuted elements, in a queue of the implementation of the input.
+/// Thrown when the function does not compute a permutation.
+///
+/// O(n).
+///
+[]
+val permute : indexMap : (int -> int) -> queue : IQueue<'T> -> IQueue<'T>
+
+/// Returns the elements in reverse order.
+/// The input queue.
+/// The reversed elements, in a queue of the implementation of the input.
+/// O(n).
+[]
+val rev : queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the transpose of a sequence of queues of the same length.
+///
+/// The queues.
+/// An immutable queue whose element i holds the element i of every input queue.
+/// Thrown when is or a default flat list, or one of its queues is .
+/// Thrown when the queues have different lengths.
+///
+/// O(the total length).
+///
+[]
+val transpose : queues : seq> -> Queue>
+
+/// Splits a queue of pairs into two immutable queues.
+/// The input queue.
+/// The first components and the second components.
+/// O(n).
+[]
+val unzip : queue : IQueue -> struct (Queue<'T1> * Queue<'T2>)
+
+/// Splits a queue of triples into three immutable queues.
+/// The input queue.
+/// The first, the second and the third components.
+/// O(n).
+[]
+val unzip3 : queue : IQueue -> struct (Queue<'T1> * Queue<'T2> * Queue<'T3>)
+
+///
+/// Returns the elements that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The elements that satisfy the predicate, in their order, in a queue of the implementation of the input.
+///
+/// O(n). A synonym of .
+///
+[]
+val where : predicate : ('T -> bool) -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the windows of a given size that slide over the queue.
+///
+/// The size of a window.
+/// The input queue.
+/// The windows; empty when the queue is shorter than a window.
+/// Thrown when is not positive.
+///
+/// O(n × windowSize).
+///
+[]
+val windowed : windowSize : int -> queue : IQueue<'T> -> Queue>
+
+/// Combines two queues into an immutable queue of pairs.
+/// The first queue.
+/// The second queue.
+/// The pairs of elements at the same index.
+/// Thrown when the queues have different lengths.
+/// O(n).
+[]
+val zip : queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> Queue
+
+/// Combines three queues into an immutable queue of triples.
+/// The first queue.
+/// The second queue.
+/// The third queue.
+/// The triples of elements at the same index.
+/// Thrown when the queues have different lengths.
+/// O(n).
+[]
+val zip3 : queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> queue3 : IQueue<'T3> -> Queue
+
+// ---- Folding ----
+
+/// Returns the average of the elements.
+/// The input queue.
+/// The average.
+/// Thrown when the queue is empty.
+/// O(n).
+[]
+val inline average :
+ queue : IQueue< ^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 queue.
+/// The average of the results.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val inline averageBy :
+ projection : ('T -> ^U) -> queue : IQueue<'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 queues element by element with a comparer.
+/// The function that compares two elements.
+/// The first queue.
+/// The second queue.
+///
+/// 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) -> queue1 : IQueue<'T> -> queue2 : IQueue<'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 queue.
+/// The final state.
+///
+/// O(n).
+///
+[]
+val fold : folder : ('State -> 'T -> 'State) -> state : 'State -> queue : IQueue<'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 queue.
+/// The second queue.
+/// The final state.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val fold2 : folder : ('State -> 'T1 -> 'T2 -> 'State) -> state : 'State -> queue1 : IQueue<'T1> -> queue2 : IQueue<'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 queue.
+/// The initial state.
+/// The final state.
+///
+/// O(n).
+///
+[]
+val foldBack : folder : ('T -> 'State -> 'State) -> queue : IQueue<'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 queue.
+/// The second queue.
+/// The initial state.
+/// The final state.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val foldBack2 :
+ folder : ('T1 -> 'T2 -> 'State -> 'State) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> state : 'State -> 'State
+
+/// Applies an action to each element.
+/// The action.
+/// The input queue.
+/// O(n).
+[]
+val iter : action : ('T -> unit) -> queue : IQueue<'T> -> unit
+
+///
+/// Applies an action to each pair of elements at the same index.
+///
+/// The action.
+/// The first queue.
+/// The second queue.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val iter2 : action : ('T1 -> 'T2 -> unit) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'T2> -> unit
+
+/// Applies an action to each element and its index.
+/// The action.
+/// The input queue.
+/// O(n).
+[]
+val iteri : action : (int -> 'T -> unit) -> queue : IQueue<'T> -> unit
+
+///
+/// Applies an action to the index and the pair of elements at that index.
+///
+/// The action.
+/// The first queue.
+/// The second queue.
+/// Thrown when the queues have different lengths.
+///
+/// O(n).
+///
+[]
+val iteri2 : action : (int -> 'T1 -> 'T2 -> unit) -> queue1 : IQueue<'T1> -> queue2 : IQueue<'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 queue.
+/// The results and the final state.
+///
+/// O(n).
+///
+[]
+val mapFold :
+ mapping : ('State -> 'T -> struct ('Result * 'State)) ->
+ state : 'State ->
+ queue : IQueue<'T> ->
+ struct (Queue<'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 queue.
+/// 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)) ->
+ queue : IQueue<'T> ->
+ state : 'State ->
+ struct (Queue<'Result> * 'State)
+
+/// Returns the greatest element, in F# generic comparison.
+/// The input queue.
+/// The greatest element.
+/// Thrown when the queue is empty.
+/// O(n).
+[]
+val max : queue : IQueue<'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 queue.
+/// The first element with the greatest result.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val maxBy : projection : ('T -> 'U) -> queue : IQueue<'T> -> 'T when 'U : comparison
+
+/// Returns the smallest element, in F# generic comparison.
+/// The input queue.
+/// The smallest element.
+/// Thrown when the queue is empty.
+/// O(n).
+[]
+val min : queue : IQueue<'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 queue.
+/// The first element with the smallest result.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val minBy : projection : ('T -> 'U) -> queue : IQueue<'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 queue.
+/// The combined value.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val reduce : reduction : ('T -> 'T -> 'T) -> queue : IQueue<'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 queue.
+/// The combined value.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val reduceBack : reduction : ('T -> 'T -> 'T) -> queue : IQueue<'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 queue.
+/// The states; one more than the elements.
+///
+/// O(n).
+///
+[]
+val scan : folder : ('State -> 'T -> 'State) -> state : 'State -> queue : IQueue<'T> -> Queue<'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 queue.
+/// The initial state.
+/// The states; one more than the elements.
+///
+/// O(n).
+///
+[]
+val scanBack : folder : ('T -> 'State -> 'State) -> queue : IQueue<'T> -> state : 'State -> Queue<'State>
+
+/// Returns the sum of the elements.
+/// The input queue.
+/// The sum; zero for an empty queue.
+/// O(n).
+[]
+val inline sum : queue : IQueue< ^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 queue.
+/// The sum of the results; zero for an empty queue.
+///
+/// O(n).
+///
+[]
+val inline sumBy :
+ projection : ('T -> ^U) -> queue : IQueue<'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 queue.
+/// 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) -> queue : IQueue<'T> -> Queue when 'Key : equality
+
+///
+/// Returns the elements without duplicates, in F# structural equality.
+///
+/// The input queue.
+/// The first occurrence of each element, in their order, in a queue of the implementation of the input.
+///
+/// O(n).
+///
+[]
+val distinct : queue : IQueue<'T> -> IQueue<'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 queue.
+/// The first element of each key, in their order, in a queue of the implementation of the input.
+///
+/// O(n). The keys compare in F# structural equality.
+///
+[]
+val distinctBy : projection : ('T -> 'Key) -> queue : IQueue<'T> -> IQueue<'T> when 'Key : equality
+
+///
+/// Returns the distinct elements that do not equal any of the excluded items.
+///
+/// The items to remove.
+/// The input queue.
+/// The distinct remaining elements, in their order, in a queue of the implementation of the input.
+/// Thrown when is or a default flat list.
+///
+/// O(n + m). An empty queue is returned without a look at the items to exclude.
+///
+[]
+val except : itemsToExclude : seq<'T> -> queue : IQueue<'T> -> IQueue<'T> when 'T : equality
+
+/// Groups the elements by the key of a projection.
+/// The function that returns the key of an element.
+/// The input queue.
+/// 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) -> queue : IQueue<'T> -> Queue)> when 'Key : equality
+
+/// Splits the elements by a predicate.
+/// The function to test the elements.
+/// The input queue.
+///
+/// The elements that satisfy the predicate and the other ones, both in their order, in queues of the implementation of
+/// the input.
+///
+/// O(n).
+[]
+val partition : predicate : ('T -> bool) -> queue : IQueue<'T> -> struct (IQueue<'T> * IQueue<'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 queue.
+/// 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>) -> queue : IQueue<'T> -> struct (Queue<'T1> * Queue<'T2>)
+
+///
+/// Returns the queue without its first elements.
+///
+/// The number of elements to skip; a negative count skips nothing.
+/// The input queue.
+/// The remaining elements, in a queue of the implementation of the input.
+/// Thrown when the count exceeds the length.
+///
+/// Amortized O(count). The function dequeues the elements with
+/// ; a count that is not positive returns the input.
+///
+[]
+val skip : count : int -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the queue without its last elements.
+///
+/// The number of elements to skip; a negative count skips nothing.
+/// The input queue.
+/// The remaining elements, in a queue of the implementation of the input.
+/// Thrown when the count exceeds the length.
+///
+/// O(n). A count that is not positive returns the input. The checks follow
+/// .
+///
+[]
+val skipLast : count : int -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the queue without its first elements that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The elements from the first one that does not satisfy the predicate, in a queue of the implementation of the input.
+///
+/// Amortized O(k), where k is the number of skipped elements. The function dequeues the elements with
+/// .
+///
+[]
+val skipWhile : predicate : ('T -> bool) -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Splits the queue at an index.
+///
+/// The index of the first element of the second part.
+/// The input queue.
+/// The elements before the index and the elements from the index, in queues of the implementation of the input.
+/// Thrown when the index is negative.
+/// Thrown when the index exceeds the length.
+///
+/// Amortized O(index). The function dequeues the elements of the first part from the input, which gives the second part;
+/// the first part is the input itself when the index is the length.
+///
+[]
+val splitAt : index : int -> queue : IQueue<'T> -> struct (IQueue<'T> * IQueue<'T>)
+
+///
+/// Splits the queue into at most a given number of parts of nearly equal length.
+///
+/// The maximum number of parts.
+/// The input queue.
+/// 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 -> queue : IQueue<'T> -> Queue>
+
+///
+/// Returns the first elements of the queue.
+///
+/// The number of elements to take.
+/// The input queue.
+/// The first elements, in a queue of the implementation of the input.
+/// Thrown when the count is negative.
+/// Thrown when the count exceeds the length.
+///
+/// O(count). The input itself is returned when the count is the length.
+///
+[]
+val take : count : int -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the last elements of the queue.
+///
+/// The number of elements to take.
+/// The input queue.
+/// The last elements, in a queue of the implementation of the input.
+/// Thrown when the count is negative.
+/// Thrown when the count exceeds the length.
+///
+/// O(n). The input itself is returned when the count is the length. The checks follow
+/// .
+///
+[]
+val takeLast : count : int -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the first elements of the queue that satisfy the predicate.
+///
+/// The function to test the elements.
+/// The input queue.
+/// The elements before the first one that does not satisfy the predicate, in a queue of the implementation of the input.
+///
+/// O(k), where k is the length of the result. The input itself is returned when every element satisfies the predicate.
+///
+[]
+val takeWhile : predicate : ('T -> bool) -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns at most a given number of first elements.
+///
+/// The maximum number of elements; a count that is not positive returns an empty queue.
+/// The input queue.
+/// The first elements, in a queue of the implementation of the input.
+///
+/// O(count). The input itself is returned when it is not longer than the count.
+///
+[]
+val truncate : count : int -> queue : IQueue<'T> -> IQueue<'T>
+
+// ---- Sorting ----
+
+///
+/// Sorts the elements in the order of F# generic comparison.
+///
+/// The input queue.
+/// The sorted elements, in a queue 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 : queue : IQueue<'T> -> IQueue<'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 queue.
+/// The sorted elements, in a queue 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) -> queue : IQueue<'T> -> IQueue<'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 queue.
+/// The sorted elements, in a queue 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) -> queue : IQueue<'T> -> IQueue<'T> when 'Key : comparison
+
+///
+/// Sorts the elements in descending order of F# generic comparison.
+///
+/// The input queue.
+/// The sorted elements, in a queue 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 : queue : IQueue<'T> -> IQueue<'T> when 'T : comparison
+
+///
+/// Sorts the elements with a comparison function.
+///
+/// The function that compares two elements.
+/// The input queue.
+/// The sorted elements, in a queue 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) -> queue : IQueue<'T> -> IQueue<'T>
+
+///
+/// Sorts the elements with a comparer.
+///
+/// The comparer.
+/// The input queue.
+/// The sorted elements, in a queue 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 : System.Collections.Generic.IComparer<'T> -> queue : IQueue<'T> -> IQueue<'T>
+
+// ---- Random ----
+
+///
+/// Returns a random element, chosen with .
+///
+/// The input queue.
+/// A random element.
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val randomChoice : source : IQueue<'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 queue.
+/// A random element.
+/// Thrown when the queue is empty.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n).
+///
+[]
+val randomChoiceBy : randomizer : (unit -> float) -> source : IQueue<'T> -> 'T
+
+///
+/// Returns a random element, chosen with a given random number generator.
+///
+/// The random number generator.
+/// The input queue.
+/// A random element.
+/// Thrown when is .
+/// Thrown when the queue is empty.
+///
+/// O(n).
+///
+[]
+val randomChoiceWith : random : Random -> source : IQueue<'T> -> 'T
+
+///
+/// Returns random elements, chosen with ; an element can repeat.
+///
+/// The number of elements to choose.
+/// The input queue.
+/// The chosen elements, in a queue of the implementation of the input.
+/// Thrown when the count is negative, or positive for an empty queue.
+///
+/// O(n + count).
+///
+[]
+val randomChoices : count : int -> source : IQueue<'T> -> IQueue<'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 queue.
+/// The chosen elements, in a queue of the implementation of the input.
+/// Thrown when the count is negative, or positive for an empty queue.
+/// Thrown when the randomizer returns a number outside [0, 1).
+///
+/// O(n + count).
+///
+[]
+val randomChoicesBy : randomizer : (unit -> float) -> count : int -> source : IQueue<'T> -> IQueue<'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 queue.
+/// The chosen elements, in a queue of the implementation of the input.
+/// Thrown when is .
+/// Thrown when the count is negative, or positive for an empty queue.
+///
+/// O(n + count).
+///
+[]
+val randomChoicesWith : random : Random -> count : int -> source : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns random elements at distinct indices, chosen with .
+///
+/// The number of elements to choose.
+/// The input queue.
+/// The chosen elements, in a queue of the implementation of the input.
+/// Thrown when the count is negative or exceeds the length.
+///
+/// O(n + count).
+///
+[]
+val randomSample : count : int -> source : IQueue<'T> -> IQueue<'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 queue.
+/// The chosen elements, in a queue 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 : IQueue<'T> -> IQueue<'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 queue.
+/// The chosen elements, in a queue 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 : IQueue<'T> -> IQueue<'T>
+
+///
+/// Returns the elements in a random order, chosen with