diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..b26cc9a
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,27 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "shell",
+ "args": [
+ "build",
+ // Ask dotnet build to generate full paths for file names.
+ "/property:GenerateFullPaths=true",
+ // Do not generate summary otherwise it leads to duplicate errors in Problems panel
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "presentation": {
+ "reveal": "silent"
+ },
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj
new file mode 100644
index 0000000..3401609
--- /dev/null
+++ b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs
new file mode 100644
index 0000000..9951fb1
--- /dev/null
+++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs
@@ -0,0 +1,156 @@
+namespace FSharp.Collections.Immutable.Tests
+
+open NUnit.Framework
+open FSharp.Collections.Immutable
+
+[]
+module FlatListTestsUtils =
+ let nullList<'a> () = new FlatList<'a>()
+ let emptyList<'a> () = FlatList<'a>.Empty
+ let list0to9 () = FlatList.init 10 id
+ let list9to0 () = FlatList.init 10 ((-) 9)
+ let listOf count list =
+ let builder = FlatList.builderWith count
+ for i = 1 to count do
+ builder.Add list
+ FlatList.ofBuilder builder
+ let throwsOnlyOnNullLists category func = [
+ (func |> appliedToFunc nullList |> throws) |> testCase "throws on null list" category
+ (func |> appliedToFunc emptyList |> doesNotThrow) |> testCase "does not throw on empty list" category
+ (func |> appliedToFunc list0to9 |> doesNotThrow) |> testCase "does not throw on non-empty list" category
+ ]
+ let doesNotThrowOnlyOnFilledList category func = [
+ (func |> appliedToFunc nullList |> throws) |> testCase "throws on null list" category
+ (func |> appliedToFunc emptyList |> throws) |> testCase "throws on empty list" category
+ (func |> appliedToFunc list0to9 |> doesNotThrow) |> testCase "does not throw on non-empty list" category
+ ]
+
+type FlatListFixture () =
+ static member validationCases = Seq.concat [
+ FlatList.toSeq |> throwsOnlyOnNullLists (nameof(FlatList.toSeq))
+ FlatList.toArray |> throwsOnlyOnNullLists (nameof(FlatList.toArray))
+ FlatList.toList |> throwsOnlyOnNullLists (nameof(FlatList.toList))
+ FlatList.length |> throwsOnlyOnNullLists (nameof(FlatList.length))
+ (FlatList.append |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (first arg)")
+ (FlatList.append <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (second arg)")
+ (FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFromWith))
+ (FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFrom))
+ (FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexWith))
+ (FlatList.index 1) |> throwsOnlyOnNullLists (nameof(FlatList.index))
+ (FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.removeAllWith))
+ (FlatList.removeAll <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.removeAll))
+ (FlatList.filter noopPredicate) |> throwsOnlyOnNullLists (nameof(FlatList.filter))
+ (FlatList.where noopPredicate) |> throwsOnlyOnNullLists (nameof(FlatList.where))
+ (FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists (nameof(FlatList.sortWithComparer))
+ (FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists (nameof(FlatList.sortWith))
+ FlatList.sort |> throwsOnlyOnNullLists (nameof(FlatList.sort))
+ FlatList.map noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.map))
+ FlatList.countBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.countBy))
+ FlatList.indexed |> throwsOnlyOnNullLists (nameof(FlatList.indexed))
+ FlatList.iter ignore |> throwsOnlyOnNullLists (nameof(FlatList.iter))
+ (FlatList.iter2 ignore2 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (first arg)")
+ (FlatList.iter2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (second arg)")
+ FlatList.distinct |> throwsOnlyOnNullLists (nameof(FlatList.distinct))
+ FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.distinctBy))
+ (FlatList.map2 ignore2 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (first arg)")
+ (FlatList.map2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (second arg)")
+ (fun a -> FlatList.map3 ignore3 a (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (first arg)")
+ (fun a -> FlatList.map3 ignore3 (emptyList ()) a (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (second arg)")
+ (FlatList.map3 ignore3 (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (third arg)")
+ (FlatList.mapi2 ignore3 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (first arg)")
+ (FlatList.mapi2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (second arg)")
+ (FlatList.iteri ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.iteri))
+ (FlatList.iteri2 ignore3 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (first arg)")
+ (FlatList.iteri2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (second arg)")
+ (FlatList.mapi ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.mapi))
+
+ (FlatList.item 0) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.item))
+ (FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.indexRangeWith))
+ (FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.indexRange))
+ (FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexRangeWith))
+ (FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexRange))
+ (FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexFromWith))
+ (FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexFrom))
+ (FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexWith))
+ (FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndex))
+ (FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.removeRange))
+ (fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.blit))
+ (FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRangeWithComparer))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRange))
+ ]
+
+ static member operationCases = [
+ (FlatList.init 5 |> appliedTo (id) |> producesEquivalentOf [0..4]) |> testCase "for 5 elements yield valid list" (nameof(FlatList.init))
+ (FlatList.init 0 |> appliedTo (id) |> producesEquivalentOf []) |> testCase "for 0 elements yields empty list" (nameof(FlatList.init))
+ (FlatList.isEmpty |> appliedToFunc emptyList |> produces true) |> testCase "empty list is" (nameof(FlatList.isEmpty))
+ (FlatList.isEmpty |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isEmpty))
+ (FlatList.isDefault |> appliedToFunc nullList |> produces true) |> testCase "null list is" (nameof(FlatList.isDefault))
+ (FlatList.isDefault |> appliedToFunc emptyList |> produces false) |> testCase "empty list is not" (nameof(FlatList.isDefault))
+ (FlatList.isDefault |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefault))
+ (FlatList.isDefaultOrEmpty |> appliedToFunc nullList |> produces true) |> testCase "null list is" (nameof(FlatList.isDefaultOrEmpty))
+ (FlatList.isDefaultOrEmpty |> appliedToFunc emptyList |> produces true) |> testCase "empty list is" (nameof(FlatList.isDefaultOrEmpty))
+ (FlatList.isDefaultOrEmpty |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefaultOrEmpty))
+ (FlatList.length |> appliedToFunc emptyList |> produces 0) |> testCase "for empty list is 0" (nameof(FlatList.length))
+ (FlatList.length |> appliedToFunc list0to9 |> produces 10) |> testCase "for non-empty list is .length" (nameof(FlatList.length))
+ (FlatList.item 0 |> appliedToFunc emptyList |> throws) |> testCase "throws for empty list" (nameof(FlatList.item))
+ (FlatList.item 0 |> appliedToFunc list0to9 |> produces 0) |> testCase "[0] for non-empty list equals to [0]" (nameof(FlatList.item))
+ (FlatList.item 5 |> appliedToFunc list0to9 |> produces 5) |> testCase "[5] for non-empty list equals to [5]" (nameof(FlatList.item))
+ (FlatList.item -1 |> appliedToFunc list0to9 |> throws) |> testCase "[-1] for non-empty list throws" (nameof(FlatList.item))
+ (FlatList.item 25 |> appliedToFunc list0to9 |> throws) |> testCase "[out of bounds] for non-empty list throws" (nameof(FlatList.item))
+ (FlatList.append (emptyList ()) |> appliedToFunc list0to9 |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" (nameof(FlatList.append))
+ (FlatList.append (list0to9 ()) |> appliedToFunc emptyList |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" (nameof(FlatList.append))
+ (FlatList.append (list0to9 ()) |> appliedToFunc list0to9 |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" (nameof(FlatList.append))
+ (FlatList.indexRangeWith HashIdentity.Structural 3 5 6 |> appliedToFunc list0to9 |> produces 6) |> testCase "returns index for valid args" (nameof(FlatList.indexRangeWith))
+ (FlatList.indexRangeWith HashIdentity.Structural 3 15 6 |> appliedToFunc list0to9 |> throws) |> testCase "throws for invalid args (count)" (nameof(FlatList.indexRangeWith))
+ (FlatList.indexRangeWith HashIdentity.Structural -2 5 6 |> appliedToFunc list0to9 |> throws) |> testCase "throws for invalid args (index)" (nameof(FlatList.indexRangeWith))
+ (FlatList.indexRangeWith HashIdentity.Structural 3 5 0 |> appliedToFunc list0to9 |> produces -1) |> testCase "returns -1 for non-present item" (nameof(FlatList.indexRangeWith))
+ (FlatList.removeAll ([] |> FlatList.ofList) |> appliedToFunc list0to9 |> producesEquivalentOf [0..9]) |> testCase "with empty list - returns source" (nameof(FlatList.removeAll))
+ (FlatList.removeAll ([] |> FlatList.ofList) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "with empty list - returns source (even if empty)" (nameof(FlatList.removeAll))
+ (FlatList.removeAll (list0to9 ()) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "takes no action for not present values" (nameof(FlatList.removeAll))
+ (FlatList.removeAll (list0to9 ()) |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "removes all present items" (nameof(FlatList.removeAll))
+ (FlatList.filter ((>) 4) |> appliedToFunc list0to9 |> producesEquivalentOf [0..3]) |> testCase "yields only valid items" (nameof(FlatList.filter))
+ (FlatList.filter ((<) 100) |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "returns empty list if predicate matches no items" (nameof(FlatList.filter))
+ (FlatList.removeRange 0 0 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 0 items from 0" (nameof(FlatList.removeRange))
+ (FlatList.removeRange 0 -1 |> appliedToFunc emptyList |> throws) |> testCase "empty list - -1 items from 0" (nameof(FlatList.removeRange))
+ (FlatList.removeRange -1 0 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 0 items from -1" (nameof(FlatList.removeRange))
+ (FlatList.removeRange 0 3 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 3 items from 0" (nameof(FlatList.removeRange))
+ (FlatList.removeRange 0 3 |> appliedToFunc list0to9 |> producesEquivalentOf [3..9]) |> testCase "non-empty list - 3 items from 0" (nameof(FlatList.removeRange))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 3 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 3 items from 0" (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 -1 |> appliedToFunc emptyList |> throws) |> testCase "empty list - -1 items from 0" (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison -1 0 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 0 items from -1" (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 0 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 3 items from 0" (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 3 |> appliedToFunc list9to0 |> producesSameAs [7;8;9;6;5;4;3;2;1;0]) |> testCase "9 to 0 list - 3 items from 0" (nameof(FlatList.sortRangeWith))
+ (FlatList.sortRangeWith (fun x y -> y - x) 0 3 |> appliedToFunc list0to9 |> producesSameAs [2;1;0;3;4;5;6;7;8;9]) |> testCase "0 to 9 list - 3 items from 0" (nameof(FlatList.sortRangeWith))
+ (FlatList.initWithValue -1 |> appliedTo 1 |> throws) |> testCase "-1 items" (nameof(FlatList.initWithValue))
+ (FlatList.initWithValue 0 |> appliedTo 1 |> producesEquivalentOf []) |> testCase "0 items" (nameof(FlatList.initWithValue))
+ (FlatList.initWithValue 3 |> appliedTo 1 |> producesEquivalentOf [1;1;1]) |> testCase "3 items" (nameof(FlatList.initWithValue))
+ (FlatList.concat |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "no items" (nameof(FlatList.concat))
+ (FlatList.concat |> appliedTo (listOf 4 <| emptyList ()) |> producesEquivalentOf []) |> testCase "no items 4 times" (nameof(FlatList.concat))
+ (FlatList.concat |> appliedTo ([emptyList ();list0to9 ()] |> FlatList.ofList) |> producesEquivalentOf [0..9]) |> testCase "no items & 0 to 9" (nameof(FlatList.concat))
+ (FlatList.concat |> appliedTo ([list0to9 ();list0to9 ()] |> FlatList.ofList) |> producesEquivalentOf [0;1;2;3;4;5;6;7;8;9;0;1;2;3;4;5;6;7;8;9]) |> testCase "0 to 9 times 2" (nameof(FlatList.concat))
+ (FlatList.map ((+) 3) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.map))
+ (FlatList.map ((+) 3) |> appliedToFunc list0to9 |> producesEquivalentOf [3..12]) |> testCase "non-empty list" (nameof(FlatList.map))
+ (FlatList.countBy (fun x -> x % 2 = 0) |> appliedToFunc list0to9 |> producesEquivalentOf [true,5;false,5]) |> testCase "non-empty list" (nameof(FlatList.countBy))
+ (FlatList.countBy (fun x -> x % 2 = 0) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.countBy))
+ (FlatList.distinct |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.distinct))
+ (FlatList.distinct |> appliedTo ([1;1;1;1] |> FlatList.ofList) |> producesEquivalentOf [1]) |> testCase "duplicates list" (nameof(FlatList.distinct))
+ (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.distinctBy))
+ (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc list0to9 |> producesEquivalentOf [0;1]) |> testCase "0 to 9 list, oddity check" (nameof(FlatList.distinctBy))
+ (FlatList.map2 (+) |> withSecondArgFrom emptyList |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - both" (nameof(FlatList.map2))
+ (FlatList.map2 (+) |> withSecondArgFrom emptyList |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "empty list - 1st" (nameof(FlatList.map2))
+ (FlatList.map2 (+) |> withSecondArgFrom list0to9 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 2nd" (nameof(FlatList.map2))
+ (FlatList.map2 (+) |> withSecondArgFrom list0to9 |> appliedToFunc list9to0 |> producesEquivalentOf (listOf 10 9)) |> testCase "non-empty" (nameof(FlatList.map2))
+ (FlatList.exists ((>) 5) |> appliedToFunc emptyList |> produces false) |> testCase "empty list" (nameof(FlatList.exists))
+ (FlatList.exists ((>) 5) |> appliedToFunc list0to9 |> produces true) |> testCase "non-empty list" (nameof(FlatList.exists))
+ (FlatList.exists ((>) -1) |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list, invalid condition" (nameof(FlatList.exists))
+ (FlatList.contains 5 |> appliedToFunc emptyList |> produces false) |> testCase "empty list" (nameof(FlatList.contains))
+ (FlatList.contains 5 |> appliedToFunc list0to9 |> produces true) |> testCase "non-empty list" (nameof(FlatList.contains))
+ (FlatList.contains -1 |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list, invalid element" (nameof(FlatList.contains))
+ ]
+
+ []
+ member this.testParameterValidation (code:unit->unit) = code ()
+
+ []
+ member this.testOperationResult (code:unit->unit) = code ()
diff --git a/FSharp.Collections.Immutable.Tests/test-helpers.fs b/FSharp.Collections.Immutable.Tests/test-helpers.fs
new file mode 100644
index 0000000..f0a6754
--- /dev/null
+++ b/FSharp.Collections.Immutable.Tests/test-helpers.fs
@@ -0,0 +1,20 @@
+namespace FSharp.Collections.Immutable.Tests
+
+open FsUnit
+open NUnit.Framework
+
+[]
+module TestHelpers =
+ let appliedTo value func = fun () -> func value
+ let appliedToFunc value func = fun () -> func (value ())
+ let produces value func = fun () -> () |> func |> should equal value
+ let producesEquivalentOf value func = fun () -> () |> func |> should equivalent value
+ let producesSameAs value func = fun () -> () |> func |> Seq.fold2 (fun acc x y -> acc && (x = y)) true value |> should equal true
+ let throws func = fun () -> Assert.Catch (func >> ignore) |> ignore
+ let doesNotThrow func = fun () -> func () |> ignore |> should equal ()
+ let noopPredicate _ = true
+ let ignore2 _ _ = ()
+ let ignore3 _ _ _ = ()
+ let testCase name category (code:unit->unit) = ((TestCaseData code).SetName (category + " - " + name)).SetCategory category
+ let withSecondArg a f = fun b -> f b a
+ let withSecondArgFrom a f = fun b -> f b (a ())
diff --git a/FSharp.Collections.Immutable.sln b/FSharp.Collections.Immutable.sln
index a9324bb..f053b66 100644
--- a/FSharp.Collections.Immutable.sln
+++ b/FSharp.Collections.Immutable.sln
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\publish_release.yml = .github\workflows\publish_release.yml
EndProjectSection
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Collections.Immutable.Tests", "FSharp.Collections.Immutable.Tests\FSharp.Collections.Immutable.Tests.fsproj", "{2272CACE-DBCE-4BC8-BADD-11802BAF30EC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
index 2b3fbfb..a9177f1 100644
--- a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
+++ b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj
@@ -1,45 +1,41 @@
-
-
-
- netstandard2.0
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
- F# bindings for System.Collections.Immutable
- Copyright © XperiAndri 2016
- FSharp.Collections.Immutable
- XperiAndri
- FSharp.Collections.Immutable
- 2.0.0
- XperiAndri;EventHelix;vilinski;anthony-mi;dim-37
- true
- FSharp.Collections.Immutable
- System;Immutable;Collections;FSharp;F#
- git
- embedded
- https://github.com/fsprojects/FSharp.Collections.Immutable/
- https://github.com/fsprojects/FSharp.Collections.Immutable/
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
-
+
+
+ netstandard2.0
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+ F# bindings for System.Collections.Immutable
+ Copyright © XperiAndri 2016
+ FSharp.Collections.Immutable
+ XperiAndri
+ FSharp.Collections.Immutable
+ 2.0.0
+ XperiAndri;EventHelix;vilinski;anthony-mi;dim-37
+ true
+ FSharp.Collections.Immutable
+ System;Immutable;Collections;FSharp;F#
+ git
+ embedded
+ https://github.com/fsprojects/FSharp.Collections.Immutable/
+ https://github.com/fsprojects/FSharp.Collections.Immutable/
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs
index 5f86c40..6739494 100644
--- a/src/FSharp.Collections.Immutable/flat-list.fs
+++ b/src/FSharp.Collections.Immutable/flat-list.fs
@@ -2,6 +2,9 @@
namespace global
#else
namespace FSharp.Collections.Immutable
+
+open FSharp.Collections.Immutable
+
#endif
// The FlatList name comes from a similar construct seen in the official F# source code
@@ -16,49 +19,75 @@ module FlatList =
let inline internal checkNotDefault argName (list : FlatList<'T>) =
if list.IsDefault then invalidArg argName "Uninstantiated ImmutableArray/FlatList"
let inline internal check (list : FlatList<'T>) = checkNotDefault (nameof list) list
+ let inline internal checkEmpty (list : FlatList<_>) = check list; if list.Length = 0 then invalidArg (nameof list) "Source is empty" else ()
+ let inline internal raiseOrReturn list = check list; list
////////// Creating //////////
+ []
let inline empty<'T> : FlatList<_> = FlatListFactory.Create<'T>()
+ []
let inline singleton<'T> (item : 'T) : FlatList<'T> = FlatListFactory.Create<'T> (item)
+ []
+ let copy (list:FlatList<_>) = FlatListFactory.CreateRange list
+ []
let inline ofSeq source = FlatListFactory.CreateRange source
+ []
let inline ofArray (source : _ array) = FlatListFactory.CreateRange source
+ []
+ let inline ofList (source: _ list) = FlatListFactory.CreateRange source
- let inline toSeq (flatList: FlatList<_>) = flatList :> seq<_>
+ []
+ let inline toSeq (flatList: FlatList<_>) = check flatList; flatList :> seq<_>
+ []
let inline toArray (list : FlatList<_>) = check list; Seq.toArray list
+ []
+ let inline toList list = check list; Seq.toList list
////////// Building //////////
+ []
let moveFromBuilder (builder : FlatList<_>.Builder) : FlatList<_> =
checkNotNull (nameof builder) builder
builder.MoveToImmutable()
+ []
let ofBuilder (builder : FlatList<_>.Builder) : FlatList<_> =
checkNotNull (nameof builder) 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()
module Builder =
let inline private check (builder: FlatList<'T>.Builder) = checkNotNull (nameof builder) builder
+ []
let add item builder = check builder; builder.Add(item)
let inline internal indexNotFound() = raise <| System.Collections.Generic.KeyNotFoundException()
+ []
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 item index list = check list; list.[index]
+ []
let append list1 list2 : FlatList<'T> =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
@@ -67,74 +96,99 @@ module FlatList =
/// 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 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 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 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<_>) items list: FlatList<_> =
check list
list.RemoveRange(items, comparer)
/// Removes the specified objects from the list.
+ []
let removeAll items list = removeAllWith HashIdentity.Structural items list
/// 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
/// Removes all the elements that do not match the conditions defined by the specified predicate.
+ []
let where predicate list = filter predicate list
/// Removes a range of elements from the list.
+ []
let removeRange index (count: int) list: FlatList<_> = check list; list.RemoveRange(index, count)
+ []
let blit source sourceIndex (destination: 'T[]) destinationIndex count =
checkNotDefault (nameof source) source
try source.CopyTo(sourceIndex, destination, destinationIndex, count)
with exn -> raise exn // throw same exception with the correct stack trace. Update exception code
+ []
let sortRangeWithComparer comparer index count list =
check list
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 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 builder = builderWith count
@@ -142,230 +196,115 @@ module FlatList =
builder.Add <| initializer i
moveFromBuilder builder
+ []
+ let initWithValue count value =
+ if count < 0 then invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative
+ let builder = builderWith count
+ for i = 0 to count - 1 do
+ builder.Add value
+ ofBuilder builder
+
let rec private concatAddLengths (arrs: FlatList>) i acc =
if i >= length arrs then acc
else concatAddLengths arrs (i+1) (acc + arrs.[i].Length)
- let concat (arrs : FlatList>) = // consider generalizing
- let result: FlatList<'T>.Builder = builderWith <| concatAddLengths arrs 0 0
- for i = 0 to length arrs - 1 do
- result.AddRange(arrs.[i]: FlatList<'T>)
- moveFromBuilder result
+ []
+ let concat (seqs:FlatList>) =
+ let builder = builderWith <| concatAddLengths seqs 0 0
+ for seq in seqs do
+ builder.AddRange seq
+ ofBuilder builder
- let inline map mapping list =
- check list
- let builder = builderWithLengthOf list
- for i = 0 to length list - 1 do
- builder.Add(mapping list.[i])
- moveFromBuilder builder
+ []
+ let map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq
- let countBy projection list =
- check list
- // need struct box optimization
- let dict = new System.Collections.Generic.Dictionary<'Key, int>(HashIdentity.Structural)
-
- // Build the groupings
- for v in list do
- let key = projection v
- let mutable prev = Unchecked.defaultof<_>
- if dict.TryGetValue(key, &prev) then dict.[key] <- prev + 1 else dict.[key] <- 1
-
- let res = builderWith dict.Count
- let mutable i = 0
- for group in dict do
- res.Add(group.Key, group.Value)
- i <- i + 1
- moveFromBuilder res
-
- let indexed list =
- check list
- let builder = builderWithLengthOf list
- for i = 0 to length list - 1 do
- builder.Add(i, list.[i])
- moveFromBuilder builder
+ []
+ let countBy projection = raiseOrReturn >> Seq.countBy projection >> ofSeq
- let inline iter action list =
- check list
- for i = 0 to length list - 1 do
- action list.[i]
+ []
+ let indexed list = list |> raiseOrReturn |> Seq.indexed |> ofSeq
+ []
+ let iter action = raiseOrReturn >> Seq.iter action
+
+ []
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
- if len <> length list2 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
+ Seq.iter2 action list1 list2
- ofBuilder builder
+ []
+ let distinct (list: FlatList<'T>) = list |> Seq.distinct |> ofSeq
+ []
+ let distinctBy projection = raiseOrReturn >> Seq.distinctBy projection >> ofSeq
+
+ []
let map2 mapping list1 list2 =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(mapping)
- 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
+ Seq.map2 mapping list1 list2 |> ofSeq
+ []
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 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])
- moveFromBuilder res
+ Seq.map3 mapping list1 list2 list3 |> ofSeq
+
+
+ []
let mapi2 mapping list1 list2 =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(mapping)
- 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])
+ Seq.mapi2 mapping list1 list2 |> ofSeq
+
+ []
+ let iteri action = raiseOrReturn >> Seq.iteri action
+ []
let iteri2 action list1 list2 =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(action)
- let len1 = list1.Length
- if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
- for i = 0 to len1 - 1 do
- f.Invoke(i,list1.[i], list2.[i])
+ Seq.iteri2 action list1 list2
- 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])
- moveFromBuilder res
-
- let exists predicate list =
- check list
- let len = list.Length
- let rec loop i = i < len && (predicate list.[i] || loop (i+1))
- loop 0
+ []
+ let mapi mapping = raiseOrReturn >> Seq.mapi mapping >> ofSeq
- 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 exists predicate = raiseOrReturn >> Seq.exists predicate
+ []
+ let contains e = raiseOrReturn >> Seq.contains e
+
+ []
let exists2 predicate list1 list2 =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(predicate)
- let len1 = list1.Length
- if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
- let rec loop i = i < len1 && (f.Invoke(list1.[i], list2.[i]) || loop (i+1))
- loop 0
+ Seq.exists2 predicate list1 list2
- 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 = raiseOrReturn >> Seq.forall predicate
+ []
let forall2 predicate list1 list2 =
checkNotDefault (nameof list1) list1
checkNotDefault (nameof list2) list2
- let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(predicate)
- let len1 = list1.Length
- if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths
- let rec loop i = i >= len1 || (f.Invoke(list1.[i], list2.[i]) && loop (i+1))
- loop 0
+ Seq.forall2 predicate list1 list2
- 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)
-
- // Return the list-of-lists.
- let result = builderWith dict.Count
- let mutable i = 0
- for group in dict do
- result.Add(group.Key, ofSeq group.Value)
- i <- i + 1
-
- moveFromBuilder result
-
- let pick chooser list =
- check list
- let rec loop i =
- if i >= list.Length then
- indexNotFound()
- else
- match chooser list.[i] with
- | None -> loop(i+1)
- | Some res -> res
- loop 0
-
- let tryPick chooser list =
- check list
- let rec loop i =
- if i >= list.Length then None else
- match chooser list.[i] with
- | None -> loop(i+1)
- | res -> res
- loop 0
-
- let choose chooser list =
- check list
- let res = builderWith list.Length
- for i = 0 to list.Length - 1 do
- match chooser list.[i] with
- | None -> ()
- | Some b -> res.Add(b)
- ofBuilder res
+ []
+ let groupBy projection = raiseOrReturn >> Seq.groupBy projection >> Seq.map (fun (k, i) -> k, ofSeq i) >> ofSeq
+
+ []
+ let pick chooser = raiseOrReturn >> Seq.pick chooser
+ []
+ let tryPick chooser = raiseOrReturn >> Seq.tryPick chooser
+
+ []
+ let choose chooser = raiseOrReturn >> Seq.choose chooser >> ofSeq
+
+ []
let partition predicate list =
check list
let res1 = builderWith list.Length
@@ -375,48 +314,126 @@ module FlatList =
if predicate x then res1.Add(x) else res2.Add(x)
ofBuilder res1, ofBuilder res2
- let find predicate list =
- check list
- let rec loop i =
- if i >= list.Length then indexNotFound() else
- if predicate list.[i] then list.[i] else loop (i+1)
- loop 0
- let tryFind predicate list =
+ let rec private tryFindWithCustomStride (list:FlatList<_>) index predicate indexPredicate indexTransform =
+ if indexPredicate index then
+ if predicate list.[index] then
+ Some (index, list.[index])
+ else tryFindWithCustomStride list (indexTransform index) predicate indexPredicate indexTransform
+ else None
+
+ []
+ let tryFindItem predicate direction list =
check list
- let rec loop i =
- if i >= list.Length then None else
- if predicate list.[i] then Some list.[i] else loop (i+1)
- loop 0
- let findBack predicate list =
+ let startIndex = if direction then 0 else length list - 1
+ let indexPredicate = if direction then ((>) (length list)) else ((<=) 0)
+ let transform = if direction then ((+) 1) else ((+) -1) // because section is not available
+ tryFindWithCustomStride list startIndex predicate indexPredicate transform
+
+ []
+ let find predicate = raiseOrReturn >> Seq.find predicate
+ []
+ let tryFind predicate = raiseOrReturn >> Seq.tryFind predicate
+ []
+ let findBack predicate = raiseOrReturn >> Seq.findBack predicate
+ []
+ let tryFindBack predicate = raiseOrReturn >> Seq.tryFindBack predicate
+ []
+ let findIndex predicate = raiseOrReturn >> Seq.findIndex predicate
+ []
+ let findIndexBack predicate = raiseOrReturn >> Seq.findIndexBack predicate
+ []
+ let tryFindIndex predicate = raiseOrReturn >> Seq.tryFindIndex predicate
+ []
+ let tryFindIndexBack predicate = raiseOrReturn >> Seq.tryFindIndexBack predicate
+
+ []
+ let fold folder (state: 'state) = raiseOrReturn >> Seq.fold folder state
+
+ []
+ let scan folder (state: 'state) = raiseOrReturn >> Seq.scan folder state >> ofSeq
+
+ []
+ let fold2 folder (state: 'state) (left:FlatList<'a>) (right:FlatList<'b>) =
+ check left; check right
+ Seq.fold2 folder state left right
+
+ []
+ let foldBack2 folder (left:FlatList<'a>) (right:FlatList<'b>) (state:'state) =
+ check left; check right
+ Seq.foldBack2 folder left right state
+
+ []
+ let foldBack folder (list:FlatList<'a>) (state: 'state) =
check list
- let rec loop i =
- if i < 0 then indexNotFound() else
- if predicate list.[i] then list.[i] else loop (i - 1)
- loop <| length list - 1
- let tryFindBack predicate list =
+ Seq.foldBack folder list state
+
+ []
+ let scanBack folder (list:FlatList<'a>) (state:'state) =
check list
- let rec loop i =
- if i < 0 then None else
- if predicate list.[i] then Some list.[i] else loop (i+1)
- loop <| length list - 1
+ Seq.scanBack folder list state |> ofSeq
- let findIndexBack predicate list =
+ []
+ let unfold (generator: 'state -> ('a * 'state) option) state =
+ Seq.unfold generator state |> ofSeq
+
+ []
+ let reduce reduction = raiseOrReturn >> Seq.reduce reduction
+
+ []
+ let reduceBack reduction = raiseOrReturn >> Seq.reduceBack reduction
+
+ []
+ let mapFold mapping (state:'State) (list:FlatList<'T>) =
check list
- let rec loop i =
- if i < 0 then indexNotFound() else
- if predicate list.[i] then i else loop (i - 1)
- loop <| length list - 1
+ let (items, s) = Seq.mapFold mapping state list
+ ofSeq items, s
- let tryFindIndexBack predicate list =
+ []
+ let mapFoldBack mapping (list:FlatList<'T>) (state:'State) =
check list
- let rec loop i =
- if i < 0 then None else
- if predicate list.[i] then Some i else loop (i - 1)
- loop <| length list - 1
- // TODO: windowed
+ let (i, s) = Seq.mapFoldBack mapping list state
+ ofSeq i, s
+
+ []
+ let zip (left:FlatList<_>) (right:FlatList<_>) =
+ check left; check right
+ Seq.zip left right |> ofSeq
+
+ []
+ let zip3 (left:FlatList<_>) (middle:FlatList<_>) (right:FlatList<_>) =
+ check left; check middle; check right
+ Seq.zip3 left middle right |> ofSeq
+
+ []
+ let unzip list =
+ let left = builderWithLengthOf list
+ let right = builderWithLengthOf list
+ for item in list do
+ left.Add <| fst item
+ right.Add <| snd item
+ ofBuilder left, ofBuilder right
+
+ []
+ let unzip3 list =
+ let left = builderWithLengthOf list
+ let right = builderWithLengthOf list
+ let middle = builderWithLengthOf list
+ for item in list do
+ left.Add <| fst3 item
+ middle.Add <| snd3 item
+ right.Add <| thd3 item
+ ofBuilder left, ofBuilder middle, ofBuilder right
+
+ []
+ let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq
+
+ []
+ let fill target targetIndex count value =
+ mapi (fun i a -> if targetIndex <= i && i < targetIndex + count then value else a) target
////////// Based on other operations //////////
+ []
let take count list = removeRange count (length list - count) list
let inline private lengthWhile predicate list =
@@ -425,50 +442,133 @@ module FlatList =
while count < list.Length && predicate list.[count] do
count <- count + 1
count
+
+ []
let takeWhile predicate list = take (lengthWhile predicate list) list
+ []
let skip index list = removeRange 0 index list
+ []
let skipWhile predicate list = skip (lengthWhile predicate list) list
+ []
let sub start stop list = skip start list |> take (stop - start - 1)
+ []
let truncate count list = if count < length list then take count list else list
+ []
let splitAt index list = take index list, skip index list
+ []
let head list = item 0 list
+ []
let tryItem index list =
if index >= length list || index < 0 then None
else Some(list.[index])
+ []
let tryHead list = tryItem 0 list
+ []
let last (list : FlatList<_>) = list.[length list - 1]
+ []
let tryLast list = tryItem (length list - 1) list
- let tail list = removeRange 1 (length list - 1) list
+ []
+ let tail list = skip 1 list
+ []
let tryTail list = if isEmpty list then None else Some <| tail list
- let create count item = init count <| fun _ -> item // optimize
+ []
+ let create = initWithValue
- let replicate count item = create item count
+ []
+ let replicate item = item |> flip initWithValue
+ []
let collect mapping list = concat <| map mapping list
+ []
let inline build f =
let builder = builder()
f builder
moveFromBuilder builder
+ []
let inline update f list =
let builder = toBuilder list
f builder
moveFromBuilder builder
+ []
+ let inline sum ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) =
+ list |> raiseOrReturn |> reduce (+)
+
+ []
+ let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) =
+ list |> raiseOrReturn |> map projection |> reduce (+)
+
+ []
+ let inline average ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member DivideByInt : ^T*int -> ^T) and ^T : (static member Zero : ^T) ) =
+ list |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt sum length
+
+ []
+ let inline averageBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member DivideByInt : ^T*int -> ^T) and ^T : (static member Zero : ^T) ) =
+ list |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt ((map projection) >> sum) length
+
+ let private minMaxReduction projection comparison a b =
+ let pa = projection a
+ let pb = projection b
+ if comparison pa pb then a else b
+
+ []
+ let maxBy projection (list:FlatList<'a>) = list |> raiseOrReturn |> reduce (minMaxReduction projection (>))
+
+ []
+ let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce (minMaxReduction projection (<))
+
+ []
+ let max (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce max
+ []
+ let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce min
+
+ []
+ let sortBy projection = sortWith (applyOverArgs LanguagePrimitives.GenericComparison projection)
+ []
+ let sortDescending (list:FlatList<'a>) = sortWith (flip LanguagePrimitives.GenericComparison) list
+ []
+ let sortByDescending projection = sortWith (flip (applyOverArgs LanguagePrimitives.GenericComparison projection))
+
+ []
+ let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> Seq.skipWhile ((uncurry comparer) >> ((=) 0)) |> Seq.head |> (uncurry comparer)
+
+ []
+ let tryExactlyOne (list:FlatList<_>) = Seq.tryExactlyOne list
+ []
+ let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list
+
+ []
+ let rev (list:FlatList<_>) = list |> raiseOrReturn |> Seq.rev |> ofSeq
+ []
+ let transpose (list:FlatList<_>) = list |> raiseOrReturn |> Seq.transpose |> Seq.map ofSeq |> ofSeq
+ []
+ let permute indexMap (list:FlatList<_>) = list |> raiseOrReturn |> Seq.permute indexMap |> ofSeq
+ []
+ let pairwise (list:FlatList<_>) = list |> raiseOrReturn |> Seq.pairwise |> ofSeq
+ []
+ let except itemsToExclude (list:FlatList<_>) = list |> raiseOrReturn |> Seq.except itemsToExclude |> ofSeq
+ []
+ let splitInto count (list:FlatList<_>) = list |> raiseOrReturn |> Seq.splitInto count |> Seq.map ofSeq |> ofSeq
+ []
+ let chunkBySize chunkSize (list:FlatList<_>) = list |> raiseOrReturn |> Seq.chunkBySize chunkSize |> Seq.map ofSeq |> ofSeq
+ []
+ let allPairs (left:FlatList<'a>) (right:FlatList<'b>) = Seq.allPairs (raiseOrReturn left) (raiseOrReturn right) |> ofSeq
+
//////////
module ImmutableArray = FlatList
diff --git a/src/FSharp.Collections.Immutable/functional-utils.fs b/src/FSharp.Collections.Immutable/functional-utils.fs
new file mode 100644
index 0000000..e05821b
--- /dev/null
+++ b/src/FSharp.Collections.Immutable/functional-utils.fs
@@ -0,0 +1,17 @@
+#if INTERACTIVE
+namespace global
+#else
+namespace FSharp.Collections.Immutable
+#endif
+
+[]
+module FunctionalUtils =
+ let inline flip f a b = f b a
+ let inline uncurry f (a, b) = f a b
+
+ let inline applyOverFuncs f g h x = f (g x) (h x)
+ let inline applyOverArgs f g x y = f (g x) (g y)
+
+ let inline fst3 (a, _, _) = a
+ let inline snd3 (_, a, _) = a
+ let inline thd3 (_, _, a) = a