diff --git a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
index 9f5941f..187ea7c 100644
--- a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
+++ b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0;net8.0;net9.0
$(AssemblyBaseName)
true
true
@@ -21,7 +21,8 @@
-
+
+
@@ -40,4 +41,10 @@
+
+
+ <_Parameter1>FSharp.Collections.Immutable.Tests
+
+
+
diff --git a/src/FSharp.Collections.Immutable/FlatList.fs b/src/FSharp.Collections.Immutable/FlatList.fs
index 82bcce7..a98405f 100644
--- a/src/FSharp.Collections.Immutable/FlatList.fs
+++ b/src/FSharp.Collections.Immutable/FlatList.fs
@@ -1,9 +1,19 @@
+// This file contains F# bindings to ImmutableArray from System.Collections.Immutable.
+// It provides a flat list implementation that is optimized for performance and memory usage.
+// The FlatList type is a wrapper around ImmutableArray, providing a more convenient API for working with immutable lists.
+// FlatList code is designed to perform operations without allocating new arrays unnecessarily, making it suitable for high-performance applications.
#if INTERACTIVE
namespace global
#else
namespace FSharp.Collections.Immutable
#endif
+open System
+open System.Buffers
+open System.Collections.Generic
+open System.Collections.Immutable
+open System.Linq
+
// The FlatList name comes from a similar construct seen in the official F# source code
type FlatList<'T> = System.Collections.Immutable.ImmutableArray<'T>
@@ -19,575 +29,1256 @@ module FlatList =
if list.IsDefault then
invalidArg argName "Uninstantiated ImmutableArray/FlatList"
- let inline internal check (list : FlatList<'T>) = checkNotDefault (nameof list) list
+ let inline internal indexNotFound () =
+ raise
+ <| System.Collections.Generic.KeyNotFoundException ("An item with the specified key was not found.")
+
+ let inline internal sequenceNotFound () =
+ raise
+ <| System.InvalidOperationException ("Sequence contains no matching element.")
////////// Creating //////////
- let inline empty<'T> : FlatList<_> = FlatListFactory.Create<'T> ()
- let inline singleton<'T> (item : 'T) : FlatList<'T> = FlatListFactory.Create<'T> (item)
+ []
+ let inline builderWith capacity : FlatList<'T>.Builder = FlatListFactory.CreateBuilder (capacity)
- let inline ofSeq source = FlatListFactory.CreateRange source
+ []
+ let moveFromBuilder (builder : FlatList<_>.Builder) : FlatList<_> =
+ checkNotNull (nameof builder) builder // Keep check for null builder, not default FlatList
+ builder.MoveToImmutable ()
+
+ []
+ let inline empty<'T> : FlatList<'T> = FlatListFactory.Create<'T> ()
+
+ []
let inline ofArray (source : _ array) = FlatListFactory.CreateRange source
+ []
+ let inline ofSeq source = FlatListFactory.CreateRange source
+
+ []
+ let inline ofList (source : 'T list) = FlatListFactory.CreateRange source
+
+ []
+ let inline singleton<'T> (item : 'T) : FlatList<'T> = FlatListFactory.Create<'T> (item)
+
+ []
+ let ofOption (option : 'T option) : FlatList<'T> =
+ match option with
+ | Some x -> singleton x
+ | None -> empty
+
+ []
+ let ofValueOption (voption : 'T voption) : FlatList<'T> =
+ match voption with
+ | ValueSome x -> singleton x
+ | ValueNone -> empty
+
+ []
+ let init count (initializer : int -> 'T) =
+ if count < 0 then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+
+ if count = 0 then
+ empty
+ else
+ // Create a builder with exact capacity needed
+ let builder = FlatListFactory.CreateBuilder<'T> count
+ // Resize the internal array to ensure all indices are valid
+ builder.Count <- count
+
+ // Use Parallel.For to initialize elements in parallel
+ System.Threading.Tasks.Parallel.For (0, count, fun i -> builder.[i] <- initializer i)
+ |> ignore
+
+ builder.MoveToImmutable ()
+
+ []
+ let create count (value : 'T) = init count (fun _ -> value)
+
+ []
+ let replicate count initial = create count initial
+
+ []
+ let zeroCreate<'T> (count : int) : FlatList<'T> =
+ if count < 0 then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ if count = 0 then
+ empty
+ else
+ let arr = Array.zeroCreate<'T> count
+ FlatListFactory.CreateRange (arr)
+
+ []
let inline toSeq (flatList : FlatList<_>) = flatList :> seq<_>
- let inline toArray (list : FlatList<_>) =
- check list
- Seq.toArray list
+ []
+ let inline toArray (list : FlatList<_>) = list.ToArray ()
- ////////// Building //////////
+ []
+ let toList (list : FlatList<'T>) : 'T list =
+ if list.IsDefaultOrEmpty then
+ []
+ else
+ let len = list.Length
+ let mutable result = []
+ for i = len - 1 downto 0 do
+ result <- list.[i] :: result
+ result
- let moveFromBuilder (builder : FlatList<_>.Builder) : FlatList<_> =
- checkNotNull (nameof builder) builder
- builder.MoveToImmutable ()
+ []
+ let toOption (list : FlatList<'T>) : 'T option = if list.Length = 1 then Some list.[0] else None
+
+ []
+ let toValueOption (list : FlatList<'T>) : 'T voption = if list.Length = 1 then ValueSome list.[0] else ValueNone
+
+ []
+ let copy (list : FlatList<'T>) : FlatList<'T> = list.ToImmutableArray ()
+
+ ////////// Building //////////
+ []
let ofBuilder (builder : FlatList<_>.Builder) : FlatList<_> =
- checkNotNull (nameof builder) builder
+ checkNotNull (nameof builder) builder // Keep check for null builder
builder.ToImmutable ()
+ []
let inline builder () : FlatList<'T>.Builder = FlatListFactory.CreateBuilder ()
- let inline builderWith capacity : FlatList<'T>.Builder = FlatListFactory.CreateBuilder (capacity)
- let toBuilder list : FlatList<_>.Builder =
- check list
- list.ToBuilder ()
+ []
+ let toBuilder (list : FlatList<'T>) : FlatList<'T>.Builder = list.ToBuilder ()
module Builder =
+
let inline private check (builder : FlatList<'T>.Builder) = checkNotNull (nameof builder) builder
- let add item builder =
+ []
+ let add (item : 'T) (builder : FlatList<'T>.Builder) : FlatList<'T>.Builder =
check builder
- builder.Add (item)
-
- let inline internal indexNotFound () = raise <| System.Collections.Generic.KeyNotFoundException ()
+ builder.Add item
+ builder
+ []
let isEmpty (list : FlatList<_>) = list.IsEmpty
+
+ []
let isDefault (list : FlatList<_>) = list.IsDefault
+
+ []
let isDefaultOrEmpty (list : FlatList<_>) = list.IsDefaultOrEmpty
////////// IReadOnly* //////////
- let length list =
- check list
- list.Length
+ []
+ let length (list : FlatList<'T>) = list.Length
- let item index list =
- check list
- list.[index]
+ []
+ let item index (list : FlatList<'T>) = list.[index]
- let append list1 list2 : FlatList<'T> =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- list1.AddRange (list2 : FlatList<_>)
+ []
+ let append (list1 : FlatList<'T>) (list2 : FlatList<'T>) : FlatList<'T> =
+ list1.AddRange (list2 :> System.Collections.Generic.IEnumerable<'T>)
- /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
- /// of elements in the list that starts at the specified index and
- /// contains the specified number of elements.
- let indexRangeWith comparer index count item list =
- check list
- list.IndexOf (item, index, count, comparer)
+ []
+ let indexRangeWith comparer index count item (list : FlatList<'T>) = list.IndexOf (item, index, count, comparer)
+ []
let indexRange index count item list = indexRangeWith HashIdentity.Structural index count item list
- let indexFromWith comparer index item list = indexRangeWith comparer index (length list - index) item
+
+ []
+ let indexFromWith comparer index item list = indexRangeWith comparer index (length list - index) item list
+
+ []
let indexFrom index item list = indexFromWith HashIdentity.Structural index item list
+
+ []
let indexWith comparer item list = indexFromWith comparer 0 item list
- let index item list = indexWith HashIdentity.Structural item list
- /// Searches for the specified object and returns the zero-based index of the last occurrence within the
- /// range of elements in the list that contains the specified number
- /// of elements and ends at the specified index.
- let lastIndexRangeWith comparer index count item list =
- check list
- list.LastIndexOf (item, index, count, comparer)
+ []
+ let index item (list : FlatList<'T>) =
+ let idx = list.IndexOf (item)
+ if idx = -1 then indexNotFound () else idx
+
+ []
+ let lastIndexRangeWith comparer index count item (list : FlatList<'T>) = list.LastIndexOf (item, index, count, comparer)
+ []
let lastIndexRange index count item list = lastIndexRangeWith HashIdentity.Structural index count item list
+
+ []
let lastIndexFromWith comparer index item list = lastIndexRangeWith comparer index (index + 1) item list
+
+ []
let lastIndexFrom index item list = lastIndexFromWith HashIdentity.Structural index item list
+
+ []
let lastIndexWith comparer item list = lastIndexFromWith comparer (length list - 1) item list
- let lastIndex item list = lastIndexWith HashIdentity.Structural item list
- /// Removes the specified objects from the list with the given comparer.
- let removeAllWith (comparer : System.Collections.Generic.IEqualityComparer<'T>) (items : 'T seq) list : FlatList<_> =
- check list
- list.RemoveRange (items, comparer)
+ []
+ let lastIndex item (list : FlatList<'T>) =
+ let idx = list.LastIndexOf (item)
+ if idx = -1 then indexNotFound () else idx
- /// Removes the specified objects from the list.
- let removeAll items list = removeAllWith HashIdentity.Structural items list
+ []
+ let removeAllWith comparer (items : 'T seq) (list : FlatList<'T>) : FlatList<'T> =
+ let itemsToRemove = HashSet (items, comparer)
+ list.RemoveAll (System.Predicate (fun x -> itemsToRemove.Contains x))
- /// Removes all the elements that do not match the conditions defined by the specified predicate.
- let filter predicate list : FlatList<_> =
- check list
- System.Predicate (not << predicate) |> list.RemoveAll
+ []
+ let removeAll items (list : FlatList<'T>) = removeAllWith HashIdentity.Structural items list
- /// Removes all the elements that do not match the conditions defined by the specified predicate.
- let where predicate list = filter predicate list
+ []
+ let filter (predicate : 'T -> bool) (list : FlatList<'T>) : FlatList<'T> =
+ list.RemoveAll (System.Predicate (not << predicate))
- /// Removes a range of elements from the list.
- let removeRange index (count : int) list : FlatList<_> =
- check list
- list.RemoveRange (index, count)
+ []
+ let where (predicate : 'T -> bool) (list : FlatList<'T>) : FlatList<'T> = filter predicate list
- let blit source sourceIndex (destination : 'T[]) destinationIndex count =
- checkNotDefault (nameof source) source
+ []
+ let removeRange index (count : int) (list : FlatList<'T>) : FlatList<'T> = list.RemoveRange (index, count)
- try
- source.CopyTo (sourceIndex, destination, destinationIndex, count)
- with exn ->
- raise exn // throw same exception with the correct stack trace. Update exception code
+ []
+ let blit (source : FlatList<'T>) sourceIndex (destination : 'T[]) destinationIndex count =
+ source.CopyTo (sourceIndex, destination, destinationIndex, count)
- let sortRangeWithComparer comparer index count list =
- check list
- list.Sort (index, count, comparer)
+ []
+ let sortRangeWithComparer comparer index count (list : FlatList<'T>) = list.Sort (index, count, comparer)
+ []
let sortRangeWith comparer index count list =
sortRangeWithComparer (ComparisonIdentity.FromFunction comparer) index count list
+ []
let sortRange index count list = sortRangeWithComparer ComparisonIdentity.Structural index count list
- let sortWithComparer (comparer : System.Collections.Generic.IComparer<_>) list =
- check list
- list.Sort (comparer)
+ []
+ let sortWithComparer (comparer : System.Collections.Generic.IComparer<'T>) (list : FlatList<'T>) = list.Sort (comparer)
+ []
let sortWith comparer list = sortWithComparer (ComparisonIdentity.FromFunction comparer) list
- let sort list =
- check list
- list.Sort ()
-
- ////////// Loop-based //////////
-
- let inline private builderWithLengthOf list = builderWith <| length list
-
- let init count initializer =
- if count < 0 then
- invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ []
+ let sort (list : FlatList<'T>) = list.Sort ()
- let builder = builderWith count
-
- for i = 0 to count - 1 do
- builder.Add <| initializer i
+ []
+ let rev (list : FlatList<'T>) : FlatList<'T> =
+ if list.IsDefaultOrEmpty then
+ list
+ else
+ let len = list.Length
+ let builder = FlatListFactory.CreateBuilder<'T> len
+ for i = len - 1 downto 0 do
+ builder.Add list.[i]
+ builder.MoveToImmutable ()
+
+ []
+ let inline sortDescending (list : FlatList<'T>) : FlatList<'T> when 'T : comparison = sortWith (fun x y -> compare y x) list
+
+ []
+ let inline sortByDescending (projection : 'T -> 'Key) (list : FlatList<'T>) : FlatList<'T> when 'Key : comparison =
+ sortWith (fun x y -> compare (projection y) (projection x)) list
+
+ []
+ let sortBy (projection : 'T -> 'Key) (list : FlatList<'T>) : FlatList<'T> when 'Key : comparison =
+ if list.IsDefaultOrEmpty then
+ list
+ else
+ let items = list.ToArray () // Work with a mutable array for sorting
+ System.Array.Sort (items, fun x y -> compare (projection x) (projection y))
+ FlatListFactory.CreateRange (items)
+
+ ////////// Loop-based (now LINQ-based where applicable) //////////
+
+ []
+ let concat (arrs : FlatList>) =
+ let totalLength = Seq.sumBy (fun (innerList : FlatList<'T>) -> innerList.Length) arrs
+ let builder = FlatListFactory.CreateBuilder<'T> (totalLength)
+ for i = 0 to arrs.Length - 1 do
+ let arr = arrs.[i]
+ for j = 0 to arr.Length - 1 do
+ builder.Add (arr.[j])
+ builder.MoveToImmutable ()
- moveFromBuilder builder
+ []
+ let inline map (mapping : 'T -> 'U) (list : FlatList<'T>) : FlatList<'U> = list.Select(mapping).ToImmutableArray ()
- let rec private concatAddLengths (arrs : FlatList>) i acc =
- if i >= length arrs then
- acc
- else
- concatAddLengths arrs (i + 1) (acc + arrs.[i].Length)
+ []
+ let mapi (mapping : int -> 'T -> 'U) (list : FlatList<'T>) : FlatList<'U> =
+ list.Select(fun x i -> mapping i x).ToImmutableArray ()
- let concat (arrs : FlatList>) = // consider generalizing
- let result : FlatList<'T>.Builder = builderWith <| concatAddLengths arrs 0 0
+ []
+ let mapi2 (mapping : int -> 'T1 -> 'T2 -> 'U) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) : FlatList<'U> =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
- for i = 0 to length arrs - 1 do
- result.AddRange (arrs.[i] : FlatList<'T>)
+ Enumerable.Range(0, len1).Select(fun i -> mapping i list1.[i] list2.[i]).ToImmutableArray ()
- moveFromBuilder result
+ []
+ let mapi3
+ (mapping : int -> 'T1 -> 'T2 -> 'T3 -> 'U)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (list3 : FlatList<'T3>)
+ : FlatList<'U> =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
- let inline map mapping list =
- check list
- let builder = builderWithLengthOf list
+ Enumerable.Range(0, len1).Select(fun i -> mapping i list1.[i] list2.[i] list3.[i]).ToImmutableArray ()
- for i = 0 to length list - 1 do
- builder.Add (mapping list.[i])
+ []
+ let map2 (mapping : 'T1 -> 'T2 -> 'U) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) : FlatList<'U> =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
- moveFromBuilder builder
+ Enumerable.Range(0, len1).Select(fun i -> mapping list1.[i] list2.[i]).ToImmutableArray ()
- let countBy projection list =
- check list
- // need struct box optimization
- let dict = new System.Collections.Generic.Dictionary<'Key, int> (HashIdentity.Structural)
+ []
+ let map3
+ (mapping : 'T1 -> 'T2 -> 'T3 -> 'U)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (list3 : FlatList<'T3>)
+ : FlatList<'U> =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
- // Build the groupings
- for v in list do
- let key = projection v
- let mutable prev = Unchecked.defaultof<_>
+ Enumerable.Range(0, len1).Select(fun i -> mapping list1.[i] list2.[i] list3.[i]).ToImmutableArray ()
- if dict.TryGetValue (key, &prev) then
- dict.[key] <- prev + 1
- else
- dict.[key] <- 1
+ []
+ let mapFold<'T, 'State, 'Result>
+ (mapping : 'State -> 'T -> 'Result * 'State)
+ (state : 'State)
+ (list : FlatList<'T>)
+ : FlatList<'Result> * 'State =
+ checkNotDefault (nameof list) list
+ let len = list.Length
- let res = builderWith dict.Count
- let mutable i = 0
+ if len = 0 then
+ empty, state
+ else
+ let resultBuilder = FlatListFactory.CreateBuilder<'Result> (len)
+ resultBuilder.Count <- len
+
+ let mutable currentState = state
+
+ for i = 0 to len - 1 do
+ let item = list.[i]
+ let result, newState = mapping currentState item
+ resultBuilder.[i] <- result
+ currentState <- newState
+
+ resultBuilder.MoveToImmutable (), currentState
+
+ []
+ let mapFoldBack<'T, 'State, 'Result>
+ (mapping : 'T -> 'State -> 'Result * 'State)
+ (list : FlatList<'T>)
+ (state : 'State)
+ : FlatList<'Result> * 'State =
+ checkNotDefault (nameof list) list
+ let len = list.Length
- for group in dict do
- res.Add (group.Key, group.Value)
- i <- i + 1
+ if len = 0 then
+ empty, state
+ else
+ let resultBuilder = FlatListFactory.CreateBuilder<'Result> (len)
+ resultBuilder.Count <- len
- moveFromBuilder res
+ let mutable currentState = state
- let indexed list =
- check list
- let builder = builderWithLengthOf list
+ for i = len - 1 downto 0 do
+ let item = list.[i]
+ let result, newState = mapping item currentState
+ resultBuilder.[i] <- result
+ currentState <- newState
- for i = 0 to length list - 1 do
- builder.Add (i, list.[i])
+ resultBuilder.MoveToImmutable (), currentState
- moveFromBuilder builder
+ []
+ let countBy (projection : 'T -> 'Key) (list : FlatList<'T>) =
+ list.GroupBy(projection).Select(fun group -> struct (group.Key, Seq.length group)).ToImmutableArray ()
- let inline iter action list =
- check list
+ []
+ let indexed (list : FlatList<'T>) = list.Select(fun item index -> struct (index, item)).ToImmutableArray ()
- for i = 0 to length list - 1 do
- action list.[i]
+ []
+ let inline iter (action : 'T -> unit) (list : FlatList<'T>) =
+ for item in list do
+ action item
- let iter2 action list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<'T, 'U, unit>.Adapt (action)
- let len = length list1
+ []
+ let iteri action (list : FlatList<'T>) =
+ for i = 0 to list.Length - 1 do
+ do action i list.[i]
- if len <> length list2 then
+ []
+ let iter2 (action : 'T1 -> 'T2 -> unit) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) =
+ let len = list1.Length
+ if len <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
for i = 0 to len - 1 do
- f.Invoke (list1.[i], list2.[i])
-
- let distinctBy projection (list : FlatList<'T>) =
- let builder : FlatList<'T>.Builder = builderWith <| length list
- let set = System.Collections.Generic.HashSet<'Key> (HashIdentity.Structural)
- let mutable outputIndex = 0
-
- for i = 0 to length list - 1 do
- let item = list.[i]
-
- if set.Add <| projection item then
- outputIndex <- outputIndex + 1
- Builder.add item builder
-
- ofBuilder builder
+ do action list1.[i] list2.[i]
- let map2 mapping list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (mapping)
+ []
+ let iter3 (action : 'T1 -> 'T2 -> 'T3 -> unit) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) (list3 : FlatList<'T3>) =
let len1 = list1.Length
-
if len1 <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
- let res = builderWith len1
-
- for i = 0 to len1 - 1 do
- res.Add <| f.Invoke (list1.[i], list2.[i])
-
- moveFromBuilder res
-
- let map3 mapping list1 list2 list3 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- checkNotDefault (nameof list3) list3
- let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (mapping)
- let len1 = list1.Length
-
- if not (len1 = list2.Length) then
- invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
- if not (len1 = list3.Length) then
+ if len1 <> list3.Length then
invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
-
- let res = builderWith len1
-
for i = 0 to len1 - 1 do
- res.Add <| f.Invoke (list1.[i], list2.[i], list3.[i])
+ action list1.[i] list2.[i] list3.[i]
- moveFromBuilder res
-
- let mapi2 mapping list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (mapping)
+ []
+ let iteri2 (action : int -> 'T1 -> 'T2 -> unit) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) =
let len1 = list1.Length
-
if len1 <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
- let res = builderWith len1
-
for i = 0 to len1 - 1 do
- res.Add <| f.Invoke (i, list1.[i], list2.[i])
-
- moveFromBuilder res
-
- let iteri action list =
- check list
- let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (action)
- let len = list.Length
-
- for i = 0 to len - 1 do
- f.Invoke (i, list.[i])
-
- let iteri2 action list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (action)
+ action i list1.[i] list2.[i]
+
+ []
+ let iteri3
+ (action : int -> 'T1 -> 'T2 -> 'T3 -> unit)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (list3 : FlatList<'T3>)
+ =
let len1 = list1.Length
-
if len1 <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
for i = 0 to len1 - 1 do
- f.Invoke (i, list1.[i], list2.[i])
-
- let mapi mapping list =
- check list
- let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (mapping)
- let len = list.Length
- let res = builderWithLengthOf list
-
- for i = 0 to len - 1 do
- res.Add <| f.Invoke (i, list.[i])
+ action i list1.[i] list2.[i] list3.[i]
- moveFromBuilder res
+ []
+ let exists (predicate : 'T -> bool) (list : FlatList<'T>) = list.Any (predicate)
- let exists predicate list =
- check list
- let len = list.Length
- let rec loop i = i < len && (predicate list.[i] || loop (i + 1))
+ []
+ let exists2 (predicate : 'T1 -> 'T2 -> bool) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) =
+ let len = list1.Length
+ if len <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let rec loop i = i < len && (predicate list1.[i] list2.[i] || loop (i + 1))
loop 0
- let inline contains e list =
- check list
- let mutable state = false
- let mutable i = 0
-
- while (not state && i < list.Length) do
- state <- e = list.[i]
- i <- i + 1
-
- state
-
- let exists2 predicate list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (predicate)
+ []
+ let exists3 (predicate : 'T1 -> 'T2 -> 'T3 -> bool) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) (list3 : FlatList<'T3>) =
let len1 = list1.Length
-
if len1 <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
-
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
let rec loop i =
i < len1
- && (f.Invoke (list1.[i], list2.[i]) || loop (i + 1))
-
+ && (predicate list1.[i] list2.[i] list3.[i] || loop (i + 1))
loop 0
- let forall predicate list =
- check list
- let len = list.Length
- let rec loop i = i >= len || (predicate list.[i] && loop (i + 1))
- loop 0
+ []
+ let forall (predicate : 'T -> bool) (list : FlatList<'T>) = list.All (predicate)
- let forall2 predicate list1 list2 =
- checkNotDefault (nameof list1) list1
- checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (predicate)
+ []
+ let forall2 (predicate : 'T1 -> 'T2 -> bool) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) =
let len1 = list1.Length
-
if len1 <> list2.Length then
invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let rec loop i = i >= len1 || (predicate list1.[i] list2.[i] && loop (i + 1))
+ loop 0
+ []
+ let forall3 (predicate : 'T1 -> 'T2 -> 'T3 -> bool) (list1 : FlatList<'T1>) (list2 : FlatList<'T2>) (list3 : FlatList<'T3>) =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
let rec loop i =
i >= len1
- || (f.Invoke (list1.[i], list2.[i]) && loop (i + 1))
-
+ || (predicate list1.[i] list2.[i] list3.[i] && loop (i + 1))
loop 0
- let groupBy projection list =
- check list
- let dict = new System.Collections.Generic.Dictionary<'Key, ResizeArray<'T>> (HashIdentity.Structural)
-
- // Build the groupings
- for i = 0 to (list.Length - 1) do
- let v = list.[i]
- let key = projection v
- let ok, prev = dict.TryGetValue (key)
-
- if ok then
- prev.Add (v)
- else
- let prev = new ResizeArray<'T> (1)
- dict.[key] <- prev
- prev.Add (v)
+ []
+ let inline contains item (list : FlatList<'T>) = list.Contains (item)
- // Return the list-of-lists.
- let result = builderWith dict.Count
- let mutable i = 0
+ []
+ let partition (predicate : 'T -> bool) (list : FlatList<'T>) =
+ let res1 = builderWith list.Length
+ let res2 = builderWith list.Length
+ for x in list do // Iteration will cause InvalidOperationException if list is default
+ if predicate x then res1.Add x else res2.Add x
+ (res1.ToImmutable (), res2.ToImmutable ())
- for group in dict do
- result.Add (group.Key, ofSeq group.Value)
- i <- i + 1
+ []
+ let find (predicate : 'T -> bool) (list : FlatList<'T>) = list.First (predicate)
- moveFromBuilder result
+ []
+ let tryFind (predicate : 'T -> bool) (list : FlatList<'T>) : 'T voption = list.Where (predicate) |> Seq.vtryHead
- let pick chooser list =
- check list
+ []
+ let findBack (predicate : 'T -> bool) (list : FlatList<'T>) = list.Last (predicate)
- let rec loop i =
- if i >= list.Length then
- indexNotFound ()
- else
- match chooser list.[i] with
- | None -> loop (i + 1)
- | Some res -> res
+ []
+ let findLast (predicate : 'T -> bool) (list : FlatList<'T>) = list.Last (predicate)
- loop 0
+ []
+ let tryFindBack (predicate : 'T -> bool) (list : FlatList<'T>) : 'T voption =
+ let mutable result = ValueNone
+ for i = 0 to list.Length - 1 do
+ let item = list.[i]
+ if predicate item then
+ result <- ValueSome item
+ result
- let tryPick chooser list =
- check list
+ []
+ let findIndexBack (predicate : 'T -> bool) (list : FlatList<'T>) =
+ let mutable idx = -1
+ for i = 0 to list.Length - 1 do
+ if predicate list.[i] then
+ idx <- i
+ if idx >= 0 then idx else sequenceNotFound ()
+
+ []
+ let tryFindLast (predicate : 'T -> bool) (list : FlatList<'T>) : 'T voption =
+ let mutable result = ValueNone
+ for i = list.Length - 1 downto 0 do
+ let item = list.[i]
+ if predicate item && ValueOption.isNone result then
+ result <- ValueSome item
+ result
+
+ []
+ let tryFindIndexBack (predicate : 'T -> bool) (list : FlatList<'T>) : int voption =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ let mutable i = list.Length - 1
+ let mutable result = ValueNone
+
+ while i >= 0 && ValueOption.isNone result do
+ if predicate list.[i] then
+ result <- ValueSome i
+ i <- i - 1
+
+ result
+
+ []
+ let findLastIndex (predicate : 'T -> bool) (list : FlatList<'T>) : int =
+ let mutable found = false
+ let mutable idx = -1
+ for i = list.Length - 1 downto 0 do
+ if predicate list.[i] && not found then
+ idx <- i
+ found <- true
+ if found then idx else sequenceNotFound ()
+
+ []
+ let tryFindLastIndex (predicate : 'T -> bool) (list : FlatList<'T>) : int voption =
+ let mutable result = ValueNone
+ for i = list.Length - 1 downto 0 do
+ if predicate list.[i] && ValueOption.isNone result then
+ result <- ValueSome i
+ result
+
+ []
+ let pick (chooser : 'T -> 'U voption) (list : FlatList<'T>) =
+ checkNotDefault (nameof list) list
+ let mutable result = ValueNone
+ let mutable i = 0
+ while i < list.Length && ValueOption.isNone result do
+ result <- chooser list.[i]
+ i <- i + 1
+ match result with
+ | ValueSome x -> x
+ | ValueNone -> sequenceNotFound ()
+
+ []
+ let tryPick (chooser : 'T -> 'U voption) (list : FlatList<'T>) : 'U voption =
+ list.Select(chooser).Where(ValueOption.isSome).Select (ValueOption.get)
+ |> Seq.vtryHead
+
+ []
+ let pickBack (chooser : 'T -> 'U voption) (list : FlatList<'T>) : 'U =
+ let mutable result = ValueNone
+ for i = list.Length - 1 downto 0 do
+ let v = chooser list.[i]
+ if ValueOption.isSome v && ValueOption.isNone result then
+ result <- v
+ match result with
+ | ValueSome x -> x
+ | ValueNone -> sequenceNotFound ()
+
+ []
+ let tryPickBack (chooser : 'T -> 'U voption) (list : FlatList<'T>) : 'U voption =
+ let mutable result = ValueNone
+ for i = list.Length - 1 downto 0 do
+ let v = chooser list.[i]
+ if ValueOption.isSome v && ValueOption.isNone result then
+ result <- v
+ result
+
+ []
+ let choose (chooser : 'T -> 'U voption) (list : FlatList<'T>) : FlatList<'U> =
+ list.Select(chooser).Where(ValueOption.isSome).Select(ValueOption.get).ToImmutableArray ()
+
+ []
+ let chooseBack (chooser : 'T -> 'U voption) (list : FlatList<'T>) : FlatList<'U> =
+ let builder = FlatListFactory.CreateBuilder<'U> ()
+ for i = list.Length - 1 downto 0 do
+ match chooser list.[i] with
+ | ValueSome v -> builder.Add v
+ | ValueNone -> ()
+ builder.ToImmutable ()
- let rec loop i =
- if i >= list.Length then
- None
- else
- match chooser list.[i] with
- | None -> loop (i + 1)
- | res -> res
+ []
+ let groupBy (projection : 'T -> 'Key) (list : FlatList<'T>) =
+ list.GroupBy(projection).Select(fun group -> struct (group.Key, group.ToImmutableArray ())).ToImmutableArray ()
- loop 0
-
- let choose chooser list =
- check list
- let res = builderWith list.Length
+ []
+ let distinctBy (projection : 'T -> 'Key) (list : FlatList<'T>) =
+ if list.IsDefaultOrEmpty then
+ list
+ else
+ let setBuilder = ImmutableHashSet.CreateBuilder<'Key> ()
+ let arrayBuilder = ImmutableArray.CreateBuilder<'T> ()
+ for i = 0 to list.Length - 1 do
+ let item = list.[i]
+ if setBuilder.Add (projection item) then
+ arrayBuilder.Add (item)
+ arrayBuilder.ToImmutable ()
+
+ []
+ let findDup (list : FlatList<'T>) =
+ checkNotDefault (nameof list) list
+ let seen = System.Collections.Generic.HashSet<'T> ()
+ let mutable result = ValueNone
+ let mutable i = 0
+ while i < list.Length && ValueOption.isNone result do
+ let item = list.[i]
+ if not (seen.Add (item)) then
+ result <- ValueSome item
+ i <- i + 1
+ match result with
+ | ValueSome x -> x
+ | ValueNone -> indexNotFound ()
+
+ []
+ let findDupBy (projection : 'T -> 'Key) (list : FlatList<'T>) =
+ checkNotDefault (nameof list) list
+ let seen = System.Collections.Generic.HashSet<'Key> ()
+ let mutable result = ValueNone
+ let mutable i = 0
+ while i < list.Length && ValueOption.isNone result do
+ let item = list.[i]
+ let key = projection item
+ if not (seen.Add (key)) then
+ result <- ValueSome item
+ i <- i + 1
+ match result with
+ | ValueSome x -> x
+ | ValueNone -> indexNotFound ()
- for i = 0 to list.Length - 1 do
- match chooser list.[i] with
- | None -> ()
- | Some b -> res.Add (b)
+ []
+ let collect (mapping : 'T -> 'U seq) (list : FlatList<'T>) : FlatList<'U> = list.SelectMany(mapping).ToImmutableArray ()
- ofBuilder res
+ []
+ let tryItem index (list : FlatList<'T>) : voption<'T> =
+ // list.Length or list.[index] will throw if list is default before comparison happens
+ if list.IsDefault then
+ ValueNone // Explicitly handle default case for tryItem to return ValueNone
+ elif index >= 0 && index < list.Length then
+ ValueSome list.[index]
+ else
+ ValueNone
- let partition predicate list =
- check list
- let res1 = builderWith list.Length
- let res2 = builderWith list.Length
+ []
+ let head (list : FlatList<'T>) = list.First ()
- for i = 0 to list.Length - 1 do
- let x = list.[i]
- if predicate x then res1.Add (x) else res2.Add (x)
+ []
+ let tryHead (list : FlatList<'T>) : 'T voption =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ ValueSome list.[0]
- ofBuilder res1, ofBuilder res2
+ []
+ let last (list : FlatList<_>) = list.Last ()
- let find predicate list =
- check list
+ []
+ let tryLast (list : FlatList<'T>) : 'T voption =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ ValueSome list.[list.Length - 1]
+
+ []
+ let tail (list : FlatList<'T>) =
+ if list.IsDefaultOrEmpty then
+ invalidArg (nameof list) "List must not be empty to get tail."
+ list.Slice (1, list.Length - 1)
+
+ []
+ let tryTail (list : FlatList<'T>) : voption> =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ ValueSome (list.Slice (1, list.Length - 1))
- let rec loop i =
- if i >= list.Length then indexNotFound ()
- else if predicate list.[i] then list.[i]
- else loop (i + 1)
+ []
+ let tryHeadAndTail (list : FlatList<'T>) : ('T * FlatList<'T>) voption =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ ValueSome (list.[0], list.Slice (1, list.Length - 1))
- loop 0
+ []
+ let tryLastAndInit (list : FlatList<'T>) : (FlatList<'T> * 'T) voption =
+ if list.IsDefaultOrEmpty then
+ ValueNone
+ else
+ ValueSome (list.Slice (0, list.Length - 1), list.[list.Length - 1])
- let tryFind predicate list =
- check list
+ []
+ let take (count : int) (list : FlatList<'T>) =
+ if count < 0 then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ let len = list.Length
+ if count = 0 then empty
+ elif count >= len then list
+ else list.Slice (0, count)
- let rec loop i =
- if i >= list.Length then None
- else if predicate list.[i] then Some list.[i]
- else loop (i + 1)
+ []
+ let takeEnd (count : int) (list : FlatList<'T>) : FlatList<'T> =
+ if count < 0 || count > list.Length then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ if count = 0 then
+ empty
+ else
+ list.Slice (list.Length - count, count)
- loop 0
+ []
+ let takeWhile (predicate : 'T -> bool) (list : FlatList<'T>) = list.TakeWhile(predicate).ToImmutableArray ()
- let findBack predicate list =
- check list
+ []
+ let skip index (list : FlatList<'T>) =
+ if index < 0 then
+ invalidArg (nameof index) ErrorStrings.InputMustBeNonNegative
+ let len = list.Length
+ if index = 0 then list
+ elif index >= len then empty
+ else list.Slice (index, len - index)
- let rec loop i =
- if i < 0 then indexNotFound ()
- else if predicate list.[i] then list.[i]
- else loop (i - 1)
+ []
+ let skipEnd (count : int) (list : FlatList<'T>) : FlatList<'T> =
+ if count < 0 || count > list.Length then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ if count = 0 then
+ list
+ else
+ list.Slice (0, list.Length - count)
- loop <| length list - 1
+ []
+ let skipWhile (predicate : 'T -> bool) (list : FlatList<'T>) = list.SkipWhile(predicate).ToImmutableArray ()
- let tryFindBack predicate list =
- check list
+ []
+ let sub start count (list : FlatList<'T>) = list.Slice (start, count)
- let rec loop i =
- if i < 0 then None
- else if predicate list.[i] then Some list.[i]
- else loop (i + 1)
+ []
+ let truncate count (list : FlatList<'T>) = if count < list.Length then list.Slice (0, count) else list
- loop <| length list - 1
+ []
+ let splitAt index (list : FlatList<'T>) = (list.Slice (0, index), list.Slice (index, list.Length - index))
- let findIndexBack predicate list =
- check list
+ []
+ let chunkBySize chunkSize (list : FlatList<'T>) =
+ if chunkSize <= 0 then
+ invalidArg (nameof chunkSize) ErrorStrings.InputMustBeNonNegative
+ let len = list.Length
+ if len = 0 then
+ empty
+ else
+ let numChunks = (len + chunkSize - 1) / chunkSize
+ let builder = FlatListFactory.CreateBuilder> (numChunks)
- let rec loop i =
- if i < 0 then indexNotFound ()
- else if predicate list.[i] then i
- else loop (i - 1)
+ for i = 0 to numChunks - 1 do
+ let start = i * chunkSize
+ if start < len then
+ let remaining = len - start
+ let count = min chunkSize remaining
+ builder.Add (list.Slice (start, count))
- loop <| length list - 1
+ builder.ToImmutable ()
- let tryFindIndexBack predicate list =
- check list
+ []
+ let inline build f =
+ let builder = builder ()
+ f builder
+ builder.ToImmutable ()
- let rec loop i =
- if i < 0 then None
- else if predicate list.[i] then Some i
- else loop (i - 1)
+ []
+ let inline update f (list : FlatList<'T>) =
+ let builder = toBuilder list
+ f builder
+ builder.ToImmutable ()
- loop <| length list - 1
- // TODO: windowed
+ []
+ let findIndex (predicate : 'T -> bool) (list : FlatList<'T>) =
+ checkNotDefault (nameof list) list
- ////////// Based on other operations //////////
+ let mutable index = -1
+ let mutable found = false
+ let len = list.Length
- let take count list = removeRange count (length list - count) list
+ if len = 0 then
+ sequenceNotFound ()
- let inline private lengthWhile predicate list =
- check list
- let mutable count = 0
+ let mutable i = 0
+ while i < len && not found do
+ if predicate list.[i] then
+ index <- i
+ found <- true
+ else
+ i <- i + 1
- while count < list.Length && predicate list.[count] do
- count <- count + 1
+ if found then index else sequenceNotFound ()
- count
+ []
+ let tryFindIndex (predicate : 'T -> bool) (list : FlatList<'T>) : int voption =
+ list.Select (fun item i -> struct (item, i))
+ |> Seq.where (fun struct (item, i) -> predicate item)
+ |> Seq.map (fun struct (item, i) -> i)
+ |> Seq.vtryHead
- let takeWhile predicate list = take (lengthWhile predicate list) list
+ []
+ let windowed windowSize (list : FlatList<'T>) =
+ if windowSize < 1 then
+ invalidArg (nameof windowSize) ErrorStrings.InputMustBeNonNegative
+ let len = list.Length
+ if windowSize > len then
+ empty
+ else
+ Enumerable.Range(0, len - windowSize + 1).Select(fun i -> list.Slice (i, windowSize)).ToImmutableArray ()
- let skip index list = removeRange 0 index list
+ []
+ let pairwise (list : FlatList<'T>) =
+ if list.Length < 2 then
+ empty
+ else
+ Enumerable.Zip(list, list.Skip (1), fun first second -> struct (first, second)).ToImmutableArray ()
- let skipWhile predicate list = skip (lengthWhile predicate list) list
+ []
+ let splitInto (count : int) (list : FlatList<'T>) : FlatList> =
+ if count <= 0 then
+ invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
- let sub start stop list = skip start list |> take (stop - start - 1)
+ let len = list.Length
+ if len = 0 then
+ empty
+ else
+ let chunkSize = (len + count - 1) / count
+ chunkBySize chunkSize list
- let truncate count list = if count < length list then take count list else list
+ []
+ let splitIntoN (count : int) (list : FlatList<'T>) : FlatList> = splitInto count list
- let splitAt index list = take index list, skip index list
+ []
+ let distinct (list : FlatList<'T>) =
+ if list.IsDefaultOrEmpty then
+ list
+ else
+ let seen = System.Collections.Generic.HashSet<'T> ()
+ let builder = ImmutableArray.CreateBuilder<'T> ()
+ for item in list do
+ if seen.Add (item) then
+ builder.Add (item)
+ builder.ToImmutable ()
+
+ []
+ let allPairs (xs : FlatList<'T>) (ys : FlatList<'U>) = xs.SelectMany(fun x -> ys.Select (fun y -> (x, y))).ToImmutableArray ()
+
+ []
+ let permute indexMap (list : FlatList<'T>) =
+ let len = list.Length
+ if len = 0 then
+ list
+ else
+ let builder = FlatListFactory.CreateBuilder<'T> len
+ builder.Count <- len
+ let usedSourceIndices = System.Collections.Generic.HashSet ()
+
+ for i = 0 to len - 1 do
+ let sourceIndex = indexMap i
+ if sourceIndex < 0 || sourceIndex >= len then
+ invalidArg (nameof indexMap) "Invalid permutation function, source index out of range"
+ if not (usedSourceIndices.Add (sourceIndex)) then
+ invalidArg (nameof indexMap) "Invalid permutation function, duplicate source indices mapped"
+
+ builder.[i] <- list.[sourceIndex]
+ builder.MoveToImmutable ()
+
+ []
+ let zip (list1 : FlatList<'T>) (list2 : FlatList<'U>) =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ Enumerable.Range(0, len1).Select(fun i -> struct (list1.[i], list2.[i])).ToImmutableArray ()
- let head list = item 0 list
+ []
+ let zip3 (list1 : FlatList<'T>) (list2 : FlatList<'U>) (list3 : FlatList<'V>) =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
+ Enumerable.Range(0, len1).Select(fun i -> struct (list1.[i], list2.[i], list3.[i])).ToImmutableArray ()
- let tryItem index list =
- if index >= length list || index < 0 then
- None
+ []
+ let unzip (list : FlatList) =
+ if list.IsEmpty then
+ struct (empty, empty)
else
- Some (list.[index])
+ struct (list.Select(fstv).ToImmutableArray (), list.Select(sndv).ToImmutableArray ())
- let tryHead list = tryItem 0 list
+ []
+ let unzip3 (list : FlatList) =
+ if list.IsEmpty then
+ struct (empty, empty, empty)
+ else
+ let res1 = list.Select(fun struct (x, _, _) -> x).ToImmutableArray ()
+ let res2 = list.Select(fun struct (_, y, _) -> y).ToImmutableArray ()
+ let res3 = list.Select(fun struct (_, _, z) -> z).ToImmutableArray ()
+ struct (res1, res2, res3)
+
+ []
+ let inline average<'T
+ when 'T : (static member (+) : 'T * 'T -> 'T)
+ and 'T : (static member DivideByInt : 'T * int -> 'T)
+ and 'T : (static member Zero : 'T)>
+ (list : FlatList<'T>)
+ =
+ if list.Length = 0 then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ let sum = list.Aggregate ('T.Zero, fun acc x -> Checked.(+) acc x)
+ 'T.DivideByInt (sum, list.Length)
+
+ []
+ let inline averageBy<'T, 'U
+ when 'U : (static member (+) : 'U * 'U -> 'U)
+ and 'U : (static member DivideByInt : 'U * int -> 'U)
+ and 'U : (static member Zero : 'U)>
+ (projection : 'T -> 'U)
+ (list : FlatList<'T>)
+ =
+ let sum = list.Aggregate ('U.Zero, fun acc x -> Checked.(+) acc (projection x))
+ 'U.DivideByInt (sum, list.Length)
+
+ []
+ let fold<'T, 'State> (folder : 'State -> 'T -> 'State) (state : 'State) (list : FlatList<'T>) = list.Aggregate (state, folder)
+
+ []
+ let fold2<'T1, 'T2, 'State>
+ (folder : 'State -> 'T1 -> 'T2 -> 'State)
+ (state : 'State)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let mutable acc = state
+ for i = 0 to len1 - 1 do
+ acc <- folder acc list1.[i] list2.[i]
+ acc
- let last (list : FlatList<_>) = list.[length list - 1]
+ []
+ let foldi<'T, 'State> (folder : int -> 'State -> 'T -> 'State) (state : 'State) (list : FlatList<'T>) : 'State =
+ let mutable acc = state
+ for i = 0 to list.Length - 1 do
+ acc <- folder i acc list.[i]
+ acc
+
+ []
+ let foldi2<'T1, 'T2, 'State>
+ (folder : int -> 'State -> 'T1 -> 'T2 -> 'State)
+ (state : 'State)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ : 'State =
+ let len = list1.Length
+ if len <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let mutable acc = state
+ for i = 0 to len - 1 do
+ acc <- folder i acc list1.[i] list2.[i]
+ acc
+
+ []
+ let foldBack<'T, 'State> (folder : 'T -> 'State -> 'State) (list : FlatList<'T>) (state : 'State) =
+ checkNotDefault (nameof list) list
+ let mutable acc = state
+ for i = list.Length - 1 downto 0 do
+ acc <- folder list.[i] acc
+ acc
+
+ []
+ let foldBack2<'T1, 'T2, 'State>
+ (folder : 'T1 -> 'T2 -> 'State -> 'State)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (state : 'State)
+ =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let mutable acc = state
+ for i = len1 - 1 downto 0 do
+ acc <- folder list1.[i] list2.[i] acc
+ acc
+
+ []
+ let foldBacki (folder : int -> 'T -> 'State -> 'State) (list : FlatList<'T>) (state : 'State) : 'State =
+ let mutable acc = state
+ for i = list.Length - 1 downto 0 do
+ acc <- folder i list.[i] acc
+ acc
+
+ []
+ let foldBacki2
+ (folder : int -> 'T1 -> 'T2 -> 'State -> 'State)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (state : 'State)
+ : 'State =
+ let len = list1.Length
+ if len <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ let mutable acc = state
+ for i = len - 1 downto 0 do
+ acc <- folder i list1.[i] list2.[i] acc
+ acc
+
+ []
+ let foldBack3<'T1, 'T2, 'T3, 'State>
+ (folder : 'T1 -> 'T2 -> 'T3 -> 'State -> 'State)
+ (list1 : FlatList<'T1>)
+ (list2 : FlatList<'T2>)
+ (list3 : FlatList<'T3>)
+ (state : 'State)
+ =
+ let len1 = list1.Length
+ if len1 <> list2.Length then
+ invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
+ if len1 <> list3.Length then
+ invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths
+ let mutable acc = state
+ for i = len1 - 1 downto 0 do
+ acc <- folder list1.[i] list2.[i] list3.[i] acc
+ acc
+
+ []
+ let reduce (reduction : 'T -> 'T -> 'T) (list : FlatList<'T>) =
+ if list.IsDefaultOrEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ else
+ list.Aggregate (reduction)
- let tryLast list = tryItem (length list - 1) list
+ []
+ let reduceBack (reduction : 'T -> 'T -> 'T) (list : FlatList<'T>) =
+ if list.IsDefaultOrEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ else
+ let len = list.Length
+ let mutable result = list.[len - 1]
+ for i = len - 2 downto 0 do
+ result <- reduction list.[i] result
+ result
+
+ []
+ let scan<'T, 'State> folder (state : 'State) (list : FlatList<'T>) =
+ let builder = FlatListFactory.CreateBuilder<'State> (list.Length + 1)
+ builder.Add state
+ let mutable currentState = state
+ for item in list do
+ currentState <- folder currentState item
+ builder.Add currentState
+ builder.ToImmutable ()
- let tail list = removeRange 1 (length list - 1) list
+ []
+ let scanBack<'T, 'State> folder (list : FlatList<'T>) (state : 'State) =
+ checkNotDefault (nameof list) list
+ let len = list.Length
+ let builder = FlatListFactory.CreateBuilder<'State> (len + 1)
+ builder.Count <- len + 1
- let tryTail list = if isEmpty list then None else Some <| tail list
+ builder.[len] <- state
+ let mutable currentState = state
+ for i = len - 1 downto 0 do
+ currentState <- folder list.[i] currentState
+ builder.[i] <- currentState
- let create count item = init count <| fun _ -> item // optimize
+ builder.MoveToImmutable ()
- let replicate count item = create item count
+ []
+ let exactlyOne (list : FlatList<'T>) = list.Single ()
- let collect mapping list = concat <| map mapping list
+ []
+ let tryExactlyOne (list : FlatList<'T>) : 'T voption =
+ if list.IsDefaultOrEmpty || list.Length <> 1 then
+ ValueNone
+ else
+ ValueSome list.[0]
+
+ []
+ let except (itemsToExclude : 'T seq) (list : FlatList<'T>) : FlatList<'T> =
+ let excludeSet = HashSet (itemsToExclude)
+ filter (fun x -> not (excludeSet.Contains x)) list
+
+ []
+ let inline sum (list : FlatList< ^T >) : ^T when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) =
+ list.Aggregate (LanguagePrimitives.GenericZero< ^T>, fun acc x -> acc + x)
+
+ []
+ let inline sumBy
+ (projection : 'T -> 'U)
+ (list : FlatList<'T>)
+ : 'U when 'U : (static member (+) : 'U * 'U -> 'U) and 'U : (static member Zero : 'U) =
+ list.Aggregate (LanguagePrimitives.GenericZero<'U>, fun acc x -> acc + (projection x))
+
+ []
+ let transpose (lists : FlatList>) : FlatList> =
+ if lists.IsDefaultOrEmpty then
+ empty
+ else
+ let len0 = lists.[0].Length
+ for i = 1 to lists.Length - 1 do
+ if lists.[i].Length <> len0 then
+ invalidArg (nameof lists) "All inner arrays must have the same length."
+
+ Enumerable
+ .Range(0, len0)
+ .Select(fun j -> Enumerable.Range(0, lists.Length).Select(fun i -> lists.[i].[j]).ToImmutableArray ())
+ .ToImmutableArray ()
+
+ []
+ let updateAt (index : int) (value : 'T) (list : FlatList<'T>) : FlatList<'T> = list.SetItem (index, value)
+
+ []
+ let removeAt (index : int) (list : FlatList<'T>) : FlatList<'T> = list.RemoveAt (index)
+
+ []
+ let insertAt (index : int) (value : 'T) (list : FlatList<'T>) : FlatList<'T> = list.Insert (index, value)
+
+ []
+ let insertManyAt (index : int) (values : 'T seq) (list : FlatList<'T>) : FlatList<'T> = list.InsertRange (index, values)
+
+ []
+ let fill (index : int) (count : int) (value : 'T) (list : FlatList<'T>) : FlatList<'T> =
+ if index < 0 || count < 0 || index + count > list.Length then
+ invalidArg (nameof index) ErrorStrings.InputMustBeNonNegative
+ let builder = list.ToBuilder ()
+ for i = index to index + count - 1 do
+ builder.[i] <- value
+ builder.MoveToImmutable ()
- let inline build f =
+ []
+ let unfold<'T, 'State> (generator : 'State -> struct ('T * 'State) voption) (state : 'State) : FlatList<'T> =
let builder = builder ()
- f builder
- moveFromBuilder builder
+ let mutable currentState = state
+ let mutable continuing = true
- let inline update f list =
- let builder = toBuilder list
- f builder
- moveFromBuilder builder
+ while continuing do
+ match generator currentState with
+ | ValueSome (value, newState) ->
+ builder.Add (value)
+ currentState <- newState
+ | ValueNone -> continuing <- false
-//////////
+ builder.ToImmutable ()
-module ImmutableArray = FlatList
+ []
+ let compareWith (comparer : 'T -> 'T -> int) (list1 : FlatList<'T>) (list2 : FlatList<'T>) : int =
+ if list1.IsDefault && list2.IsDefault then
+ 0
+ elif list1.IsDefault then
+ -1
+ elif list2.IsDefault then
+ 1
+ else
+ let len1 = list1.Length
+ let len2 = list2.Length
+ let minLength = min len1 len2
+
+ let mutable i = 0
+ let mutable result = 0
+ let mutable continueComparing = true
+
+ while i < minLength && continueComparing do
+ result <- comparer list1.[i] list2.[i]
+ if result <> 0 then
+ continueComparing <- false
+ else
+ i <- i + 1
+
+ if result = 0 then compare len1 len2 else result
+
+ []
+ let inline max<'T when 'T : comparison> (list : FlatList<'T>) : 'T =
+ checkNotDefault (nameof list) list
+ if list.IsEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ let mutable acc = list.[0]
+ for i = 1 to list.Length - 1 do
+ let curr = list.[i]
+ if curr > acc then
+ acc <- curr
+ acc
+
+ []
+ let inline maxBy<'T, 'Key when 'Key : comparison> (projection : 'T -> 'Key) (list : FlatList<'T>) : 'T =
+ checkNotDefault (nameof list) list
+ if list.IsEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ let mutable maxVal = list.[0]
+ let mutable maxKey = projection maxVal
+ for i = 1 to list.Length - 1 do
+ let currVal = list.[i]
+ let currKey = projection currVal
+ if currKey > maxKey then
+ maxVal <- currVal
+ maxKey <- currKey
+ maxVal
+
+ []
+ let inline min<'T when 'T : comparison> (list : FlatList<'T>) : 'T =
+ checkNotDefault (nameof list) list
+ if list.IsEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ let mutable acc = list.[0]
+ for i = 1 to list.Length - 1 do
+ let curr = list.[i]
+ if curr < acc then
+ acc <- curr
+ acc
+
+ []
+ let inline minBy<'T, 'Key when 'Key : comparison> (projection : 'T -> 'Key) (list : FlatList<'T>) : 'T =
+ checkNotDefault (nameof list) list
+ if list.IsEmpty then
+ invalidArg (nameof list) LanguagePrimitives.ErrorStrings.InputArrayEmptyString
+ let mutable minVal = list.[0]
+ let mutable minKey = projection minVal
+ for i = 1 to list.Length - 1 do
+ let currVal = list.[i]
+ let currKey = projection currVal
+ if currKey < minKey then
+ minVal <- currVal
+ minKey <- currKey
+ minVal
diff --git a/src/FSharp.Collections.Immutable/FlatList.fsi b/src/FSharp.Collections.Immutable/FlatList.fsi
new file mode 100644
index 0000000..d560cb1
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/FlatList.fsi
@@ -0,0 +1,1446 @@
+// This file contains F# bindings to ImmutableArray from System.Collections.Immutable.
+// It provides a flat list implementation that is optimized for performance and memory usage.
+// The FlatList type is a wrapper around ImmutableArray, providing a more convenient API for working with immutable lists.
+// FlatList code is designed to perform operations without allocating new arrays unnecessarily, making it suitable for high-performance applications.
+#if INTERACTIVE
+namespace global
+#else
+namespace FSharp.Collections.Immutable
+#endif
+
+open System
+open System.Collections.Generic
+open System.Collections.Immutable
+
+// The FlatList name comes from a similar construct seen in the official F# source code
+type FlatList<'T> = System.Collections.Immutable.ImmutableArray<'T>
+
+// based on the F# Array module source
+[]
+module FlatList =
+
+ ////////// Creating //////////
+
+ /// Creates a new builder with the specified capacity
+ /// The initial capacity of the builder
+ /// An empty builder with the specified capacity
+ []
+ val inline builderWith<'T> : capacity : int -> FlatList<'T>.Builder
+
+ /// Builds a from a builder, moving the elements and leaving the builder empty
+ /// The builder to build from
+ /// A containing the elements from the builder
+ /// Thrown when builder is null
+ []
+ val moveFromBuilder<'T> : builder : FlatList<'T>.Builder -> FlatList<'T>
+
+ /// Returns an empty
+ /// An empty
+ ///
+ ///
+ /// let emptyList = FlatList.empty<int>
+ /// printfn "Is empty? %b" (FlatList.isEmpty emptyList) // true
+ ///
+ ///
+ []
+ val inline empty<'T> : FlatList<'T>
+
+ /// Builds a from the given
+ /// The to build the from
+ /// A containing the elements of the array
+ []
+ val inline ofArray<'T> : source : 'T array -> FlatList<'T>
+
+ /// Builds a from the given
+ /// The to build the from
+ /// A containing the elements of the sequence
+ []
+ val inline ofSeq<'T> : source : seq<'T> -> FlatList<'T>
+
+ /// Builds a from the given
+ /// The to build the from
+ /// A containing the elements of the sequence
+ []
+ val inline ofList<'T> : source : 'T list -> FlatList<'T>
+
+ /// Creates a list from a value option.
+ /// The input option.
+ /// A list of one element if the option is Some, and an empty list if the option is None.
+ []
+ val ofOption<'T> : option : 'T option -> FlatList<'T>
+
+ /// Creates a list from a value option.
+ /// The input value option.
+ /// A list of one element if the option is ValueSome, and an empty list if the option is ValueNone.
+ []
+ val ofValueOption<'T> : voption : 'T voption -> FlatList<'T>
+
+ /// Returns a with a single element
+ /// The item to put into the
+ /// A containing only the given item
+ []
+ val inline singleton<'T> : item : 'T -> FlatList<'T>
+
+ /// Creates a by initializing each element with the given function
+ /// The number of elements to create
+ /// The function to initialize each element
+ /// A new with the initialized elements
+ /// Thrown when count is negative
+ []
+ val init<'T> : count : int -> initializer : (int -> 'T) -> FlatList<'T>
+
+ /// Creates a of a given length with all elements set to the given value
+ /// The length of the to create
+ /// The value to replicate
+ /// A of the specified length with all elements equal to the given value
+ []
+ val create<'T> : count : int -> value : 'T -> FlatList<'T>
+
+ /// Replicates a value into a of a given length
+ /// The length of the to create
+ /// The value to replicate
+ /// A of the specified length with all elements equal to the given value
+ []
+ val replicate<'T> : count : int -> initial : 'T -> FlatList<'T>
+
+ /// Creates an of a specified length, with all the elements initialized to the default zero value for the type.
+ /// The length of the to create.
+ /// The created .
+ []
+ val zeroCreate<'T> : count : int -> FlatList<'T>
+
+ /// Views the as a
+ /// The input
+ /// The containing the elements of the
+ []
+ val inline toSeq<'T> : flatList : FlatList<'T> -> seq<'T>
+
+ /// Builds an from the given
+ /// The to build the from
+ /// An containing the elements of the
+ []
+ val inline toArray<'T> : list : FlatList<'T> -> 'T array
+
+ /// Builds an from the given
+ /// The to build the from
+ /// An containing the elements of the
+ []
+ val toList<'T> : list : FlatList<'T> -> 'T list
+
+ /// Converts a list to an option. If the list has one element, it returns Some of that element.
+ /// Otherwise, it returns None.
+ /// The input list.
+ /// An option representing the list's single element, or None.
+ []
+ val toOption<'T> : list : FlatList<'T> -> 'T option
+
+ /// Converts a list to a value option. If the list has one element, it returns ValueSome of that element.
+ /// Otherwise, it returns ValueNone.
+ /// The input list.
+ /// A value option representing the list's single element, or ValueNone.
+ []
+ val toValueOption<'T> : list : FlatList<'T> -> 'T voption
+
+ /// Builds a new that contains the elements of the given .
+ /// The input .
+ []
+ val copy<'T> : list : FlatList<'T> -> FlatList<'T>
+
+ ////////// Building //////////
+
+ /// Builds a from a builder, copying the elements
+ /// The builder to build from
+ /// A containing the elements from the builder
+ /// Thrown when builder is null
+ []
+ val ofBuilder<'T> : builder : FlatList<'T>.Builder -> FlatList<'T>
+
+ /// Creates a new builder
+ /// An empty builder
+ []
+ val inline builder<'T> : unit -> FlatList<'T>.Builder
+
+ /// Creates a builder containing the elements of the input
+ /// The to create the builder from
+ /// A builder containing the elements of the
+ []
+ val toBuilder<'T> : list : FlatList<'T> -> FlatList<'T>.Builder
+
+ module Builder =
+
+ /// Adds an item to the builder
+ /// The item to add
+ /// The builder to add to
+ []
+ val add<'T> : item : 'T -> builder : FlatList<'T>.Builder -> FlatList<'T>.Builder
+
+ /// Checks if the is empty
+ /// The to check
+ /// True if the is empty, false otherwise
+ []
+ val isEmpty<'T> : list : FlatList<'T> -> bool
+
+ /// Checks if the is uninstantiated
+ /// The to check
+ /// True if the is uninstantiated, false otherwise
+ []
+ val isDefault<'T> : list : FlatList<'T> -> bool
+
+ /// Checks if the is uninstantiated or empty
+ /// The to check
+ /// True if the is uninstantiated or empty, false otherwise
+ []
+ val isDefaultOrEmpty<'T> : list : FlatList<'T> -> bool
+
+ ////////// IReadOnly* //////////
+
+ /// Returns the number of elements in the
+ /// The input
+ /// The number of elements in the
+ []
+ val length<'T> : list : FlatList<'T> -> int
+
+ /// Gets the element at the specified index in the
+ /// The index to retrieve
+ /// The input
+ /// The element at the specified index
+ /// Thrown when the index is out of range
+ []
+ val item<'T> : index : int -> list : FlatList<'T> -> 'T
+
+ /// Appends two s to create a new containing all elements from both s
+ /// The first
+ /// The second
+ /// A new containing all elements from both input s
+ []
+ val append<'T> : list1 : FlatList<'T> -> list2 : FlatList<'T> -> FlatList<'T>
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The equality comparer to use
+ /// The starting index
+ /// The number of elements to search
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val indexRangeWith<'T> :
+ comparer : IEqualityComparer<'T> -> index : int -> count : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The starting index
+ /// The number of elements to search
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val indexRange<'T when 'T : equality> : index : int -> count : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The equality comparer to use
+ /// The starting index
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val indexFromWith<'T> : comparer : IEqualityComparer<'T> -> index : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The starting index
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val indexFrom<'T when 'T : equality> : index : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The equality comparer to use
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val indexWith<'T> : comparer : IEqualityComparer<'T> -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the first occurrence within the range
+ /// of elements in the that starts at the specified index and
+ /// contains the specified number of elements.
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the first occurrence of the item
+ []
+ val index<'T when 'T : equality> : item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The equality comparer to use
+ /// The ending index
+ /// The number of elements to search
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndexRangeWith<'T> :
+ comparer : IEqualityComparer<'T> -> index : int -> count : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The ending index
+ /// The number of elements to search
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndexRange<'T when 'T : equality> : index : int -> count : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The equality comparer to use
+ /// The ending index
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndexFromWith<'T> : comparer : IEqualityComparer<'T> -> index : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The ending index
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndexFrom<'T when 'T : equality> : index : int -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The equality comparer to use
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndexWith<'T> : comparer : IEqualityComparer<'T> -> item : 'T -> list : FlatList<'T> -> int
+
+ /// Searches for the specified object and returns the zero-based index of the last occurrence within the
+ /// range of elements in the that contains the specified number
+ /// of elements and ends at the specified index.
+ /// The item to search for
+ /// The input
+ /// The zero-based index of the last occurrence of the item
+ []
+ val lastIndex<'T when 'T : equality> : item : 'T -> list : FlatList<'T> -> int
+
+ /// Removes the specified objects from the with the given comparer.
+ /// The equality comparer to use
+ /// The items to remove
+ /// The input
+ /// A new with the specified items removed
+ []
+ val removeAllWith<'T> : comparer : IEqualityComparer<'T> -> items : 'T seq -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Removes the specified objects from the .
+ /// The items to remove
+ /// The input
+ /// A new with the specified items removed
+ []
+ val removeAll<'T when 'T : equality> : items : 'T seq -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Removes all the elements that do not match the conditions defined by the specified predicate.
+ /// The predicate to test elements
+ /// The input
+ /// A new with elements that match the predicate
+ ///
+ ///
+ /// let numbers = FlatList.ofArray [|1; 2; 3; 4; 5; 6|]
+ /// let evens = FlatList.filter (fun x -> x % 2 = 0) numbers
+ /// // evens is [|2; 4; 6|]
+ ///
+ ///
+ []
+ val filter<'T> : predicate : ('T -> bool) -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Removes all the elements that do not match the conditions defined by the specified predicate.
+ /// The predicate to test elements
+ /// The input
+ /// A new with elements that match the predicate
+ []
+ val where<'T> : predicate : ('T -> bool) -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Removes a range of elements from the .
+ /// The starting index
+ /// The number of elements to remove
+ /// The input
+ /// A new with the specified range of elements removed
+ []
+ val removeRange<'T> : index : int -> count : int -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Fills the elements of a list with a specified value.
+ /// The starting index in the target list.
+ /// The number of elements to fill.
+ /// The value to fill with.
+ /// The input list.
+ /// A new list with the specified range filled with the value.
+ []
+ val fill<'T> : index : int -> count : int -> value : 'T -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Copies a range of elements from the source to the destination array
+ /// The source
+ /// The starting index in the source
+ /// The destination array
+ /// The starting index in the destination array
+ /// The number of elements to copy
+ /// Thrown when the range is invalid
+ []
+ val blit<'T> :
+ source : FlatList<'T> -> sourceIndex : int -> destination : 'T[] -> destinationIndex : int -> count : int -> unit
+
+ /// Sorts a range of elements in the using the specified comparer
+ /// The comparer to use
+ /// The starting index
+ /// The number of elements to sort
+ /// The input
+ /// A new with the specified range of elements sorted
+ []
+ val sortRangeWithComparer<'T> : comparer : IComparer<'T> -> index : int -> count : int -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts a range of elements in the using the specified comparison function
+ /// The comparison function to use
+ /// The starting index
+ /// The number of elements to sort
+ /// The input
+ /// A new with the specified range of elements sorted
+ []
+ val sortRangeWith<'T> : comparer : ('T -> 'T -> int) -> index : int -> count : int -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts a range of elements in the using the default comparer
+ /// The starting index
+ /// The number of elements to sort
+ /// The input
+ /// A new with the specified range of elements sorted
+ []
+ val sortRange<'T when 'T : comparison> : index : int -> count : int -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts the elements in the using the specified comparer
+ /// The comparer to use
+ /// The input
+ /// A new with the elements sorted
+ []
+ val sortWithComparer<'T> : comparer : IComparer<'T> -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts the elements in the using the specified comparison function
+ /// The comparison function to use
+ /// The input
+ /// A new with the elements sorted
+ []
+ val sortWith<'T> : comparer : ('T -> 'T -> int) -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts the elements in the using the default comparer
+ /// The input
+ /// A new with the elements sorted
+ []
+ val sort<'T> : list : FlatList<'T> -> FlatList<'T>
+
+ /// Returns a new with the elements in reverse order.
+ /// The input .
+ /// The reversed .
+ []
+ val rev<'T> : list : FlatList<'T> -> FlatList<'T>
+
+ /// Returns a new that contains elements of the original sorted in descending order.
+ /// The input .
+ /// The sorted .
+ []
+ val inline sortDescending<'T when 'T : comparison> : list : FlatList<'T> -> FlatList<'T>
+
+ /// Returns a new that contains elements of the original sorted in descending order using the specified projection.
+ /// The function to transform the elements into a type that supports comparison.
+ /// The input .
+ /// The sorted .
+ []
+ val inline sortByDescending<'T, 'Key when 'Key : comparison> :
+ projection : ('T -> 'Key) -> list : FlatList<'T> -> FlatList<'T>
+
+ /// Sorts the using keys given by the given projection. Keys are compared using Operators.compare.
+ /// The function to transform the elements into a type supporting comparison.
+ /// The input .
+ /// The sorted .
+ []
+ val sortBy<'T, 'Key when 'Key : comparison> : projection : ('T -> 'Key) -> list : FlatList<'T> -> FlatList<'T>
+
+ ////////// Loop-based (now LINQ-based where applicable) //////////
+
+ /// Concatenates a of s into a single
+ /// The of s to concatenate
+ /// A new containing all elements from the input s
+ []
+ val concat<'T> : arrs : FlatList> -> FlatList<'T>
+
+ /// Builds a new from the elements of a by applying a mapping function to each element
+ /// A function to transform elements from the input
+ /// The input
+ /// A containing the transformed elements
+ ///
+ ///
+ /// let numbers = FlatList.ofArray [|1; 2; 3; 4; 5|]
+ /// let squares = FlatList.map (fun x -> x * x) numbers
+ /// // squares is [|1; 4; 9; 16; 25|]
+ ///
+ ///
+ []
+ val inline map<'T, 'U> : mapping : ('T -> 'U) -> list : FlatList<'T> -> FlatList<'U>
+
+ /// Builds a new whose elements are the results of applying the given function
+ /// to each of the elements of the . The integer index passed to the
+ /// function indicates the index of element being transformed.
+ /// A function to transform an element and its index into a result element.
+ /// The input .
+ /// The of transformed elements.
+ []
+ val mapi<'T, 'U> : mapping : (int -> 'T -> 'U) -> list : FlatList<'T> -> FlatList<'U>
+
+ /// Builds a new whose elements are the results of applying the given function
+ /// to the corresponding elements of the two collections pairwise, also passing the index of
+ /// the elements. The two input s must have the same lengths.
+ /// The function to transform pairs of input elements and their indices.
+ /// The first input .
+ /// The second input .
+ /// The of transformed elements.
+ []
+ val mapi2<'T1, 'T2, 'U> :
+ mapping : (int -> 'T1 -> 'T2 -> 'U) -> list1 : FlatList<'T1> -> list2 : FlatList<'T2> -> FlatList<'U>
+
+ /// Builds a new whose elements are the results of applying the given function
+ /// to the corresponding elements of the three collections pairwise, also passing the index of
+ /// the elements. The three input s must have the same lengths.
+ /// The function to transform triples of input elements and their indices.
+ /// The first input .
+ /// The second input .
+ /// The third input .
+ /// The of transformed elements.
+ []
+ val mapi3<'T1, 'T2, 'T3, 'U> :
+ mapping : (int -> 'T1 -> 'T2 -> 'T3 -> 'U) ->
+ list1 : FlatList<'T1> ->
+ list2 : FlatList<'T2> ->
+ list3 : FlatList<'T3> ->
+ FlatList<'U>
+
+ /// Builds a new collection whose elements are the results of applying the given function
+ /// to the corresponding elements of the two collections pairwise. The two input
+ /// s must have the same lengths.
+ /// The function to transform the pairs of the input elements.
+ /// The first input .
+ /// The second input .
+ /// The of transformed elements.
+ []
+ val map2<'T1, 'T2, 'U> : mapping : ('T1 -> 'T2 -> 'U) -> list1 : FlatList<'T1> -> list2 : FlatList<'T2> -> FlatList<'U>
+
+ /// Builds a new collection whose elements are the results of applying the given function
+ /// to the corresponding elements of the three collections pairwise. The three input
+ /// s must have the same lengths.
+ /// The function to transform the triples of the input elements.
+ /// The first input .
+ /// The second input .
+ /// The third input .
+ /// The of transformed elements.
+ []
+ val map3<'T1, 'T2, 'T3, 'U> :
+ mapping : ('T1 -> 'T2 -> 'T3 -> 'U) ->
+ list1 : FlatList<'T1> ->
+ list2 : FlatList<'T2> ->
+ list3 : FlatList<'T3> ->
+ FlatList<'U>
+
+ /// Builds a new whose elements are the results of applying the given function
+ /// to each of the elements of the while threading an accumulator argument
+ /// through the computation.
+ /// The function to transform elements from the input and
+ /// thread an accumulator state.
+ /// The initial state of the accumulator.
+ /// The input .
+ /// A of transformed elements, and the final accumulator value.
+ ///
+ ///
+ /// // Calculate a running sum while squaring each element
+ /// let numbers = FlatList.ofArray [|1; 2; 3; 4|]
+ /// let squares, sum = FlatList.mapFold (fun state x -> x * x, state + x) 0 numbers
+ /// // squares is [|1; 4; 9; 16|]
+ /// // sum is 10
+ ///
+ ///
+ []
+ val mapFold<'T, 'State, 'Result> :
+ mapping : ('State -> 'T -> 'Result * 'State) -> state : 'State -> list : FlatList<'T> -> FlatList<'Result> * 'State
+
+ /// Builds a new whose elements are the results of applying the given function
+ /// to each of the elements of the while threading an accumulator argument
+ /// through the computation, starting from the end of the list.
+ /// The function to transform elements from the input and
+ /// thread an accumulator state, starting from the end.
+ /// The input .
+ /// The initial state of the accumulator.
+ /// A of transformed elements, and the final accumulator value.
+ ///
+ ///
+ /// // Create a reverse-order list of indices while computing sum
+ /// let chars = FlatList.ofArray [|'a'; 'b'; 'c'|]
+ /// let indices, sum = FlatList.mapFoldBack (fun x state -> state, state + 1) chars 0
+ /// // indices is [|2; 1; 0|]
+ /// // sum is 3
+ ///
+ ///
+ []
+ val mapFoldBack<'T, 'State, 'Result> :
+ mapping : ('T -> 'State -> 'Result * 'State) -> list : FlatList<'T> -> state : 'State -> FlatList<'Result> * 'State
+
+ /// Counts the number of elements in the that satisfy the given predicate
+ /// A function to project elements from the input
+ /// The input
+ /// A of key-value pairs where the key is the projected value and the value is the count
+ []
+ val countBy<'T, 'Key> : projection : ('T -> 'Key) -> list : FlatList<'T> -> FlatList
+
+ /// Creates a containing the elements of the original paired with their indices
+ /// The input
+ /// A containing pairs of indices and elements
+ []
+ val indexed<'T> : list : FlatList<'T> -> FlatList
+
+ ///