├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── dotnet.yml ├── .gitignore ├── .gitpod.yml ├── FSharpx.Collections.ncrunchsolution ├── FSharpx.Collections.sln ├── LICENSE.txt ├── README.md ├── RELEASE_NOTES.md ├── build.cmd ├── build.fsx ├── build.fsx.lock ├── build.sh ├── docs ├── PersistentHashMap.fsx ├── PersistentVector.fsx ├── content │ └── fsdocs-custom.css ├── csharp │ ├── PersistentHashMap.cs │ ├── PersistentVector.cs │ ├── Program.cs │ ├── csharp.csproj │ └── packages.config ├── img │ ├── logo.pdn │ └── logo.png └── index.md ├── global.json ├── paket.dependencies ├── paket.lock ├── paket.references ├── src ├── Directory.Build.props ├── FSharpx.Collections.Experimental │ ├── AltBinaryRandomAccessList.fs │ ├── AssemblyInfo.fs │ ├── BKTree.fs │ ├── BankersDeque.fs │ ├── BankersQueue.fs │ ├── BatchedDeque.fs │ ├── BatchedQueue.fs │ ├── BinaryRandomAccessList.fs │ ├── BinaryRoseTree.fs │ ├── BinaryTreeZipper.fs │ ├── BinomialHeap.fs │ ├── BlockResizeArray.fs │ ├── BootstrappedQueue.fs │ ├── BottomUpMergeSort.fs │ ├── CSharpCompat.fs │ ├── ChampHashMap.fs │ ├── DList.fs │ ├── Deque.fs │ ├── EagerRoseTree.fs │ ├── FSharpx.Collections.Experimental.fsproj │ ├── FlatList.fs │ ├── FlatList.fsi │ ├── HeapPriorityQueue.fs │ ├── HoodMelvilleQueue.fs │ ├── ImplicitQueue.fs │ ├── IndexedRoseTree.fs │ ├── Infrastructure.fs │ ├── IntMap.fs │ ├── LeftistHeap.fs │ ├── ListZipper.fs │ ├── PairingHeap.fs │ ├── PhysicistQueue.fs │ ├── RealTimeDeque.fs │ ├── RealTimeQueue.fs │ ├── RingBuffer.fs │ ├── RoseTree.fs │ ├── SkewBinaryRandomAccessList.fs │ ├── SkewBinomialHeap.fs │ ├── TimeSeries.fs │ ├── paket.references │ └── paket.template └── FSharpx.Collections │ ├── AssemblyInfo.fs │ ├── ByteString.fs │ ├── CircularBuffer.fs │ ├── CircularBuffer.fsi │ ├── Collections.fs │ ├── DList.fs │ ├── DList.fsi │ ├── Deque.fs │ ├── Deque.fsi │ ├── Exceptions.fs │ ├── FSharpx.Collections.fsproj │ ├── Infrastructure.fs │ ├── Interfaces.fs │ ├── LazyList.fs │ ├── LazyList.fsi │ ├── Literals.fs │ ├── NonEmptyList.fs │ ├── PersistentHashMap.fs │ ├── PersistentVector.fs │ ├── PersistentVector.fsi │ ├── PriorityQueue.fs │ ├── Queue.fs │ ├── Queue.fsi │ ├── RandomAccessList.fs │ ├── RandomAccessList.fsi │ ├── ResizeArray.fs │ ├── ResizeArray.fsi │ ├── TaggedCollections.fs │ ├── paket.references │ ├── paket.template │ └── readme.md └── tests ├── FSharpx.Collections.Experimental.Tests ├── AltBinaryRandomAccessListTest.fs ├── BKTreeTest.fs ├── BankersDequeTest.fs ├── BatchedDequeTest.fs ├── BinaryRandomAccessListTest.fs ├── BinaryRoseTreeTest.fs ├── BinaryTreeZipperTest.fs ├── BinomialHeapTest.fs ├── BlockResizeArrayTest.fs ├── BootstrappedQueueTest.fs ├── BottomUpMergeSortTest.fs ├── ChampHashMapTest.fs ├── DListTest.fs ├── DequeTest.fs ├── EagerRoseTreeTest.fs ├── EditDistanceTest.fs ├── FSharpx.Collections.Experimental.Tests.fsproj ├── FileSystemZipperTest.fs ├── FlatListTest.fs ├── FsCheckProperties.fs ├── HeapGen.fs ├── HeapPriorityQueueTest.fs ├── IQueueTest.fs ├── ImplicitQueueTest.fs ├── IndexedRoseTreeTest.fs ├── IntMapTest.fs ├── LeftistHeapTest.fs ├── ListZipperTest.fs ├── PairingHeapTest.fs ├── QueueGen.fs ├── RealTimeDequeTest.fs ├── RealTimeQueueTest.fs ├── RingBufferTest.fs ├── RoseTreeTest.fs ├── RunTests.fs ├── SkewBinaryRandomAccessListTest.fs ├── SkewBinomialHeapTest.fs ├── TimeSeriesTest.fs └── paket.references ├── FSharpx.Collections.Tests ├── ArrayTests.fs ├── ByteStringTest.fs ├── CircularBufferTests.fs ├── DListTest.fs ├── DequeTest.fs ├── DictionaryExtensionsTests.fs ├── FSharpx.Collections.Tests.fsproj ├── FsCheckProperties.fs ├── HeapTest.fs ├── LazyListTests.fs ├── ListExtensionsTest.fs ├── MapExtensionsTest.fs ├── MapTests.fs ├── NameValueCollectionTests.fs ├── NonEmptyListTests.fs ├── PersistentHashMapTest.fs ├── PersistentVectorTest.fs ├── PriorityQueueTest.fs ├── QueueTest.fs ├── RandomAccessListTest.fs ├── ResizeArrayTests.fs ├── RunTests.fs ├── SeqTests.fs ├── TransientHashMapTest.fs └── paket.references └── fable ├── .gitignore ├── FSharpx.Collections.Tests ├── Deque.test.fs ├── FSharpx.Collections.Tests.fsproj ├── LazyList.test.fs ├── NonEmptyList.test.fs ├── PersistentVector.test.fs ├── Queue.test.fs └── paket.references ├── babel.config.js ├── package.json └── yarn.lock /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fake-cli": { 6 | "version": "5.23.0", 7 | "commands": [ 8 | "fake" 9 | ] 10 | }, 11 | "paket": { 12 | "version": "7.1.5", 13 | "commands": [ 14 | "paket" 15 | ] 16 | }, 17 | "fsdocs-tool": { 18 | "version": "14.0.1", 19 | "commands": [ 20 | "fsdocs" 21 | ] 22 | }, 23 | "fable": { 24 | "version": "3.4.4", 25 | "commands": [ 26 | "fable" 27 | ] 28 | }, 29 | "fantomas": { 30 | "version": "5.0.3", 31 | "commands": [ 32 | "fantomas" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; EditorConfig helps developers define and maintain consistent 2 | ; coding styles between different editors and IDEs. 3 | 4 | ; For more visit http://editorconfig.org. 5 | root = true 6 | 7 | ; Choose between lf or rf on "end_of_line" property 8 | [*] 9 | indent_style = space 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [{*.fs,*.fsx}] 16 | indent_size = 4 17 | indent_style = space 18 | max_line_length=150 19 | fsharp_max_function_binding_width=10 20 | fsharp_max_infix_operator_expression=70 21 | fsharp_space_before_parameter=false 22 | fsharp_space_before_lowercase_invocation=false 23 | fsharp_multiline_block_brackets_on_same_column=true 24 | fsharp_experimental_stroustrup_style=true 25 | fsharp_bar_before_discriminated_union_declaration=true 26 | fsharp_keep_max_number_of_blank_lines=3 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Please provide a succinct description of your issue. 4 | 5 | ### Repro steps 6 | 7 | Please provide the steps required to reproduce the problem 8 | 9 | 1. Step A 10 | 11 | 2. Step B 12 | 13 | ### Expected behavior 14 | 15 | Please provide a description of the behavior you expect. 16 | 17 | ### Actual behavior 18 | 19 | Please provide a description of the actual behavior you observe. 20 | 21 | ### Known workarounds 22 | 23 | Please provide a description of any known workarounds. 24 | 25 | ### Related information 26 | 27 | * Operating system 28 | * Branch 29 | * .NET Runtime, CoreCLR or Mono Version 30 | * Performance information, links to performance testing scripts 31 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | inputs: 8 | release: 9 | description: 'Release NuGet package' 10 | required: true 11 | default: false 12 | type: boolean 13 | 14 | jobs: 15 | build: 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, windows-latest] 21 | dotnet: [6.0.401] 22 | node: ['14'] 23 | runs-on: ${{ matrix.os }} 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: setup git config 28 | run: | 29 | git config user.name "GitHub Actions Bot" 30 | git config user.email "<>" 31 | - name: Setup node 32 | uses: actions/setup-node@v2 33 | with: 34 | node-version: ${{ matrix.node }} 35 | - name: Setup .NET Core 36 | uses: actions/setup-dotnet@v1 37 | with: 38 | dotnet-version: ${{ matrix.dotnet }} 39 | - name: Install local tools 40 | run: dotnet tool restore 41 | - name: Paket Restore 42 | run: dotnet paket restore 43 | - name: Build and Test 44 | run: dotnet fake run build.fsx 45 | 46 | - name: Check secrets presence 47 | id: checksecrets 48 | shell: bash 49 | run: | 50 | echo "secretspresent=YES" >> $GITHUB_OUTPUT 51 | if [ "$GITHUB_TOKEN" == "" ]; then 52 | echo "secretspresent=NO" >> $GITHUB_OUTPUT 53 | fi 54 | if [ "$NUGET_KEY" == "" ]; then 55 | echo "secretspresent=NO" >> $GITHUB_OUTPUT 56 | fi 57 | env: 58 | NUGET_KEY: ${{ secrets.NUGETKEY }} 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | 61 | - name: Publish prerelease NuGet to GitHub 62 | if: (steps.checksecrets.outputs.secretspresent != 'NO') && matrix.os == 'windows-latest' && github.ref == 'refs/heads/master' && github.event.inputs.release != 'true' 63 | run: dotnet fake run build.fsx -t PublishCINuGet 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | - name: Publish NuGet to NuGet.org 67 | if: (steps.checksecrets.outputs.secretspresent != 'NO') && matrix.os == 'windows-latest' && github.ref == 'refs/heads/master' && github.event.inputs.release == 'true' 68 | run: dotnet fake run build.fsx -t Release 69 | env: 70 | NUGET_KEY: ${{ secrets.NUGETKEY }} 71 | 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | .vs/ 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | *.userprefs 11 | 12 | # Build results 13 | 14 | [Dd]ebug/ 15 | [Rr]elease/ 16 | x64/ 17 | build/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | .vs/ 21 | 22 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 23 | !packages/*/build/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | *_i.c 30 | *_p.c 31 | *.ilk 32 | *.meta 33 | *.obj 34 | *.pch 35 | *.pdb 36 | *.pgc 37 | *.pgd 38 | *.rsp 39 | *.sbr 40 | *.tlb 41 | *.tli 42 | *.tlh 43 | *.tmp 44 | *.tmp_proj 45 | *.log 46 | *.vspscc 47 | *.vssscc 48 | .builds 49 | *.pidb 50 | *.log 51 | *.scc 52 | 53 | # Visual C++ cache files 54 | ipch/ 55 | *.aps 56 | *.ncb 57 | *.opensdf 58 | *.sdf 59 | *.cachefile 60 | 61 | # Visual Studio profiler 62 | *.psess 63 | *.vsp 64 | *.vspx 65 | 66 | # Guidance Automation Toolkit 67 | *.gpState 68 | 69 | # ReSharper is a .NET coding add-in 70 | _ReSharper*/ 71 | *.[Rr]e[Ss]harper 72 | 73 | # TeamCity is a build add-in 74 | _TeamCity* 75 | 76 | # DotCover is a Code Coverage Tool 77 | *.dotCover 78 | 79 | # NCrunch 80 | *.ncrunch* 81 | .*crunch*.local.xml 82 | 83 | # Installshield output folder 84 | [Ee]xpress/ 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish/ 98 | 99 | # Publish Web Output 100 | *.Publish.xml 101 | 102 | # Enable nuget.exe in the .nuget folder (though normally executables are not tracked) 103 | !.nuget/NuGet.exe 104 | 105 | # Windows Azure Build Output 106 | csx 107 | *.build.csdef 108 | 109 | # Windows Store app package directory 110 | AppPackages/ 111 | 112 | # Others 113 | sql/ 114 | *.Cache 115 | ClientBin/ 116 | [Ss]tyle[Cc]op.* 117 | ~$* 118 | *~ 119 | *.dbmdl 120 | *.[Pp]ublish.xml 121 | *.pfx 122 | *.publishsettings 123 | 124 | # RIA/Silverlight projects 125 | Generated_Code/ 126 | 127 | # Backup & report files from converting an old project file to a newer 128 | # Visual Studio version. Backup files are not needed, because we have git ;-) 129 | _UpgradeReport_Files/ 130 | Backup*/ 131 | UpgradeLog*.XML 132 | UpgradeLog*.htm 133 | 134 | # SQL Server files 135 | App_Data/*.mdf 136 | App_Data/*.ldf 137 | 138 | 139 | #LightSwitch generated files 140 | GeneratedArtifacts/ 141 | _Pvt_Extensions/ 142 | ModelManifest.xml 143 | 144 | # ========================= 145 | # Windows detritus 146 | # ========================= 147 | 148 | # Windows image file caches 149 | Thumbs.db 150 | ehthumbs.db 151 | 152 | # Folder config file 153 | Desktop.ini 154 | 155 | # Recycle Bin used on file shares 156 | $RECYCLE.BIN/ 157 | 158 | # Mac desktop service store files 159 | .DS_Store 160 | 161 | # =================================================== 162 | # Exclude F# project specific directories and files 163 | # =================================================== 164 | 165 | # NuGet Packages Directory 166 | packages/ 167 | 168 | # Generated documentation folder 169 | output/ 170 | 171 | # Temp folder used for publishing docs 172 | temp/ 173 | tmp/ 174 | 175 | # Test results produced by build 176 | TestResults.xml 177 | release.cmd 178 | nuget/ 179 | 180 | # Nuget outputs 181 | nuget/*.nupkg 182 | paket-files 183 | docs/content/release-notes.md 184 | docs/content/license.md 185 | _NCrunch* 186 | .fake 187 | RELEASE_NOTES.md.orig 188 | 189 | .paket/ 190 | .ionide 191 | .fsdocs/ 192 | *.blob 193 | 194 | *.fs.js 195 | fable_modules 196 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | 2 | image: gitpod/workspace-dotnet 3 | 4 | vscode: 5 | extensions: 6 | - ionide.ionide-fsharp 7 | - christian-kohler.path-intellisense 8 | - PKief.material-icon-theme 9 | - zhuangtongfa.material-theme 10 | - eamodio.gitlens 11 | 12 | tasks: 13 | - init: ./build.sh 14 | -------------------------------------------------------------------------------- /FSharpx.Collections.ncrunchsolution: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | True 4 | false 5 | true 6 | UseDynamicAnalysis 7 | UseStaticAnalysis 8 | UseStaticAnalysis 9 | UseStaticAnalysis 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSharpx.Collections [![NuGet Badge](https://buildstats.info/nuget/FSharpx.Collections)](https://www.nuget.org/packages/FSharpx.Collections) 2 | 3 | **FSharpx.Collections** is a collection of data structures for use with F# and C#. 4 | 5 | See [the home page](http://fsprojects.github.io/FSharpx.Collections/) and [API docs](http://fsprojects.github.io/FSharpx.Collections/reference/index.html) for details. 6 | 7 | Please contribute to this project. Don't ask for permission, just fork the repository and send pull requests. 8 | 9 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/fsprojects/fsharpx.collections) 10 | 11 | ## Build Status 12 | 13 | [![Build Status](https://github.com/fsprojects/FSharpx.Collections/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/fsprojects/FSharpx.Collections/actions?query=branch%3Amaster) 14 | 15 | Pre-release nuget packages automatically built from master branch are published to [GitHub fsproject NuGet feed](https://github.com/orgs/fsprojects/packages?repo_name=FSharpx.Collections) (`https://nuget.pkg.github.com/fsprojects/index.json`) 16 | 17 | ## Maintainer(s) 18 | 19 | For git history prior to commit 169ced2 on October 19, 2013 refer to [FSharpx.Extras](https://github.com/fsprojects/FSharpx.Extras). 20 | 21 | - [@forki](https://github.com/forki) 22 | - [@gdziadkiewicz](https://github.com/gdziadkiewicz) 23 | 24 | The default maintainer account for projects under "fsprojects" is [@fsprojectsgit](https://github.com/fsprojectsgit) - F# Community Project Incubation Space (repo management) 25 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | #### 3.1.0 - 2022-11-13 2 | * Gitpod support by @cartermp in https://github.com/fsprojects/FSharpx.Collections/pull/172 3 | * Fix a typo in a comment by @viebel in https://github.com/fsprojects/FSharpx.Collections/pull/190 4 | * Testing on Net6 by @sergey-tihon in https://github.com/fsprojects/FSharpx.Collections/pull/193 5 | * Add `PersistentVector.mapi` by @njlr in https://github.com/fsprojects/FSharpx.Collections/pull/186 6 | * RoseTree. Fix a broken link in the comments. by @ScottHutchinson in https://github.com/fsprojects/FSharpx.Collections/pull/195 7 | * Fantomas5 formatting by @sergey-tihon in https://github.com/fsprojects/FSharpx.Collections/pull/194 8 | * Make the NuGet package consumable from Fable by @njlr in https://github.com/fsprojects/FSharpx.Collections/pull/187 9 | * Add release NuGet publish from CI by @gdziadkiewicz in https://github.com/fsprojects/FSharpx.Collections/pull/199 10 | 11 | #### 3.0.1 - 2021-10-23 12 | * Fix choice2s #184. Thanks @sideeffffect 13 | 14 | #### 3.0.0 - 2021-10-22 15 | * Deque.Tail performance tweaks 16 | * Fast path for single back element. Avoid traversing list for length multiple times. Use Unchecked.defaultOf<_> rather than an actual element. Thanks @simendsjo 17 | * Expose PersistentVector.rangedIterator. Thanks @simendsjo 18 | * Seq extensions: add tryHeadTail #176. Thanks @knocte 19 | * some helper functions #92. Thanks @sideeffffect 20 | * Enable PersistentVector in Fable #182. Thanks @njlr 21 | * Dependencies update 22 | * Build and test with .NET 5 & Node 14 23 | 24 | #### 2.1.3 - 2020-05-30 25 | * RandomAccessLists zip, reduce, map2 26 | * doc projects netcoreapp3.1 build, thanks Grzegorz Dziadkiewicz 27 | 28 | #### 2.1.2 - 2019-12-24 29 | * Fix FSharp.Core assembly reference version 30 | 31 | #### 2.1.1 - 2019-11-17 32 | * Fix FSharp.Core dependency version 33 | 34 | #### 2.1.0 - 2019-11-12 35 | * target net45 and netstandard2.0, thanks Grzegorz Dziadkiewicz 36 | 37 | #### 2.0.0 - 2019-05-15 38 | * BREAKING CHANGE: only netstandard2.0 supported 39 | * PersistentHashMap implements Count (issues/12) 40 | * add compareWith and areEqual to LazyList (issues/114) thanks teo-tsirpanis 41 | * fix experimental RoseTree equality (issues/114) thanks teo-tsirpanis 42 | * fix Nop thread comparison in PersistentHashMap (issues/66) 43 | * make RandomAccessList serializable (issue 127) thanks teo-tsirpanis 44 | * Implemented CHAMP algorithm for a persistent hash map, thanks bsomes 45 | * Implement IReadOnlyList and IReadOnlyCollection for the RandomAccessList (issue 130) thanks teo-tsirpanis 46 | * LazyListbased on the BCL's Lazy type, thanks teo-tsirpanis 47 | 48 | #### 2.0.0-beta3 - 2018-06-19 49 | * pairwise DList, thanks Brendan Fahy 50 | 51 | #### 2.0.0-beta2 - 2018-05-26 52 | * target net45 and netstandard2.0 (beta1 incorrectly targetted net461) 53 | 54 | #### 2.0.0-beta1 - 2018-05-26 55 | * Microsoft.NET.Sdk projects 56 | * target net45 and netstandard2.0 57 | * BREAKING CHANGE: RequireQualifiedAccess 58 | * BREAKING CHANGE: type RealTimeQueue under Experimental namespace 59 | * BREAKING CHANGE: type BootstrappedQueue under Experimental namespace 60 | * BREAKING CHANGE: type ListZipper under Experimental namespace 61 | * BREAKING CHANGE: types BinaryTree, TreeDirection, BinaryTreeZipper under Experimental namespace 62 | * BREAKING CHANGE: type ImplicitQueue under Experimental namespace 63 | * BREAKING CHANGE: type BinaryRandomAccessList under Experimental namespace 64 | * BREAKING CHANGE: type Digit for BinaryRandomAccessList renamed TreeBRALDigit 65 | 66 | #### 1.17.0 - 26.06.2017 67 | * PERFORMANCE: NonEmptyList Collect had poor performance - https://github.com/fsprojects/FSharpx.Collections/pull/75 68 | 69 | #### 1.16.0 - 25.05.2017 70 | * New tagged structures - https://github.com/fsprojects/FSharpx.Collections/pull/69 71 | * Use FSharp.Core 4.0 72 | * Couple of new helper functions - https://github.com/fsprojects/FSharpx.Collections/pull/64 https://github.com/fsprojects/FSharpx.Collections/pull/63 73 | * Improved RandomAccessList API with some functions from PersistentVector - https://github.com/fsprojects/FSharpx.Collections/pull/54 74 | * Faster NonEmptyList implementation - https://github.com/fsprojects/FSharpx.Collections/pull/62 75 | 76 | #### 1.14.0 - 13.02.2016 77 | * Allow 4.0 FSharp.Core 78 | * Distinct and DistinctBy functions for ResizeArray - https://github.com/fsprojects/FSharpx.Collections/pull/56 79 | * Add zip to NonEmptyList - https://github.com/fsprojects/FSharpx.Collections/pull/46 80 | 81 | #### 1.12.4 - 13.09.2015 82 | * Fixed enumerating empty RandomAccessList - https://github.com/fsprojects/FSharpx.Collections/pull/45 83 | 84 | #### 1.12.3 - 12.09.2015 85 | * ResizeArray.collect made to take seq as an input - https://github.com/fsprojects/FSharpx.Collections/pull/41 86 | * Made conflict resolution explicit in variable names - https://github.com/fsprojects/FSharpx.Collections/pull/39 87 | 88 | #### 1.12.1 - 17.07.2015 89 | * LazyList.fold - https://github.com/fsprojects/FSharpx.Collections/pull/34 90 | 91 | #### 1.12.0 - 17.07.2015 92 | * New SkewBinomialHeap - https://github.com/fsprojects/FSharpx.Collections/pull/36 93 | 94 | #### 1.11.1 - 17.07.2015 95 | * New Block resize array functions - https://github.com/fsprojects/FSharpx.Collections/pull/37 96 | 97 | #### 1.11.0 - 25.05.2015 98 | * New Block resize array - https://github.com/fsprojects/FSharpx.Collections/pull/33 99 | * BUGFIX: Prevent ArrayNode's tryFind from returning Some null - https://github.com/fsprojects/FSharpx.Collections/pull/22 100 | 101 | #### 1.10.0 - 26.02.2015 102 | * Added profile 259 and fixed profile 47 folder name - https://github.com/fsprojects/FSharpx.Collections/pull/26 103 | 104 | #### 1.9.6 - 13.01.2015 105 | * Bump version due to broken logo link - https://github.com/fsprojects/FSharpx.Collections/issues/21 106 | 107 | #### 1.9.5 - 13.01.2015 108 | * Bump version due to broken package meta data - https://github.com/fsprojects/FSharpx.Collections/issues/21 109 | 110 | #### 1.9.4 - 07.08.2014 111 | * Add Profile47 112 | 113 | #### 1.9.3 - 07.08.2014 114 | * Tail recursive implementation of Heap.Tail - https://github.com/fsprojects/FSharpx.Collections/pull/17 115 | 116 | #### 1.9.2 - 14.01.2013 117 | * Fixing nuget package 118 | 119 | #### 1.9.1 - 14.01.2013 120 | * Initial release from new location; previous contributor history lost 121 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | 4 | dotnet tool restore 5 | dotnet paket restore 6 | dotnet fake run build.fsx %* 7 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # to properly set Travis permissions: https://stackoverflow.com/questions/33820638/travis-yml-gradlew-permission-denied 3 | # git update-index --chmod=+x fake.sh 4 | # git commit -m "permission access for travis" 5 | 6 | set -eu 7 | set -o pipefail 8 | 9 | dotnet tool restore 10 | dotnet paket restore 11 | dotnet fake run build.fsx $@ -------------------------------------------------------------------------------- /docs/PersistentVector.fsx: -------------------------------------------------------------------------------- 1 | (** 2 | --- 3 | category: Collections 4 | categoryindex: 1 5 | index: 1 6 | --- 7 | *) 8 | 9 | (*** hide ***) 10 | #r "../src/FSharpx.Collections/bin/Debug/netstandard2.0/FSharpx.Collections.dll" 11 | 12 | open System 13 | 14 | (** 15 | # PersistentVector 16 | 17 | A Vector is a collection of values indexed by contiguous integers. Vectors support access to items by index in log32N hops. count is O(1). conj puts the item at the end of the vector. 18 | More details can be found in the [API docs](reference/fsharpx-collections-persistentvector-1.html). 19 | *) 20 | 21 | open FSharpx.Collections 22 | 23 | // Create a new PersistentVector and add some items to it 24 | let v = 25 | PersistentVector.empty 26 | |> PersistentVector.conj "hello" 27 | |> PersistentVector.conj "world" 28 | |> PersistentVector.conj "!" 29 | (*** include-value: v ***) 30 | 31 | // lookup some items 32 | PersistentVector.nth 0 v 33 | (*** include-it ***) 34 | 35 | PersistentVector.nth 1 v 36 | (*** include-it ***) 37 | 38 | v.[2] 39 | (*** include-it ***) 40 | 41 | // Check no. of elements in the PersistentVector 42 | PersistentVector.length v 43 | (*** include-it ***) 44 | 45 | (** 46 | PersistentVectors are immutable and therefor allow to create new version without destruction of the old ones. 47 | *) 48 | 49 | let v' = v |> PersistentVector.conj "!!!" |> PersistentVector.update 0 "hi" // replace existing value 50 | 51 | PersistentVector.nth 0 v' 52 | (*** include-it ***) 53 | 54 | PersistentVector.nth 3 v' 55 | (*** include-it ***) 56 | 57 | PersistentVector.nth 0 v 58 | (*** include-it ***) 59 | 60 | PersistentVector.length v 61 | (*** include-it ***) 62 | 63 | PersistentVector.length v' 64 | (*** include-it ***) 65 | 66 | // remove the last element from a PersistentVector 67 | let v'' = PersistentVector.initial v' 68 | 69 | PersistentVector.length v'' 70 | (*** include-it ***) 71 | 72 | 73 | (** 74 | There a couple of interesting operations on PersistentVectors: 75 | *) 76 | 77 | // Convert a sequence of values to a PersistentVectors 78 | let intVector = PersistentVector.ofSeq [ 1..10 ] 79 | (*** include-value: intVector ***) 80 | 81 | // Square all values in a PersistentVector 82 | let intVector' = PersistentVector.map (fun x -> x * x) intVector 83 | intVector'.[3] 84 | (*** include-it ***) 85 | 86 | (** 87 | ## Using from C# 88 | 89 | A Vector is a collection of values indexed by contiguous integers. Vectors support access to items by index in log32N hops. Count is O(1). Conj puts the item at the end of the vector. 90 | 91 | [lang=csharp,file=csharp/PersistentVector.cs,key=create-vector] 92 | 93 | PersistentVectors are immutable and therefor allow to create new version without destruction of the old ones. 94 | 95 | [lang=csharp,file=csharp/PersistentVector.cs,key=modify-vector] 96 | 97 | *) 98 | 99 | (*** hide ***) 100 | 101 | /// Stops the runtime for a given function 102 | let stopTime f = 103 | let sw = new System.Diagnostics.Stopwatch() 104 | sw.Start() 105 | let result = f() 106 | sw.Stop() 107 | result, float sw.ElapsedMilliseconds 108 | 109 | /// Stops the average runtime for a given function and applies it the given count 110 | let stopAverageTime count f = 111 | System.GC.Collect() // force garbage collector before testing 112 | let sw = new System.Diagnostics.Stopwatch() 113 | sw.Start() 114 | 115 | for _ in 1..count do 116 | f() |> ignore 117 | 118 | sw.Stop() 119 | float sw.ElapsedMilliseconds / float count 120 | 121 | let printInFsiTags s = 122 | printfn " [fsi:%s]" s 123 | 124 | /// Stops the average runtime for a given function and applies it the given count 125 | /// Afterwards it reports it with the given description 126 | let averageTime count desc f = 127 | let time = stopAverageTime count f 128 | sprintf "%s %Ams" desc time |> printInFsiTags 129 | 130 | (** 131 | ## Performance Tests 132 | 133 | Bulk operations on PersistentVector use an internal TransientVector in order to get much better performance. The following scripts shows this: 134 | *) 135 | 136 | open FSharpx.Collections 137 | 138 | let trials = 5 139 | let r = new System.Random() 140 | 141 | let initArrayAndVectorFromList n = 142 | sprintf "Init with n = %d" n |> printInFsiTags 143 | let list = [ for i in 1..n -> r.Next() ] 144 | 145 | let initvector list = 146 | let v = ref PersistentVector.empty 147 | 148 | for x in list do 149 | v := PersistentVector.conj x !v 150 | 151 | !v 152 | 153 | averageTime trials " Array.ofSeq" (fun () -> Array.ofSeq list) 154 | 155 | averageTime trials " Multiple PersistentVector.conj" (fun () -> initvector list) 156 | 157 | averageTime trials " PersistentVector.ofSeq" (fun () -> PersistentVector.ofSeq list) 158 | 159 | let lookupInArrayAndVector n count = 160 | sprintf "%d Lookups in size n = %d" count n |> printInFsiTags 161 | let list = [ for i in 1..n -> r.Next() ] 162 | let array = Array.ofSeq list 163 | let vector = PersistentVector.ofSeq list 164 | 165 | averageTime trials " Array" (fun () -> 166 | for i in 1..count do 167 | array.[r.Next n]) 168 | 169 | averageTime trials " PersistentVector" (fun () -> 170 | for i in 1..count do 171 | PersistentVector.nth (r.Next n) vector) 172 | 173 | 174 | let replaceInArrayAndVector n count = 175 | sprintf "%d writes in size n = %d" count n |> printInFsiTags 176 | let list = [ for i in 1..n -> r.Next() ] 177 | let array = Array.ofSeq list 178 | let vector = PersistentVector.ofSeq list 179 | 180 | averageTime trials " Array" (fun () -> 181 | for i in 1..count do 182 | array.[r.Next n] <- r.Next()) 183 | 184 | averageTime trials " PersistentVector" (fun () -> 185 | for i in 1..count do 186 | PersistentVector.update (r.Next n) (r.Next()) vector) 187 | 188 | initArrayAndVectorFromList 10000 189 | initArrayAndVectorFromList 100000 190 | initArrayAndVectorFromList 1000000 191 | (*** include-output ***) 192 | 193 | lookupInArrayAndVector 10000 10000 194 | lookupInArrayAndVector 100000 10000 195 | lookupInArrayAndVector 1000000 10000 196 | (*** include-output ***) 197 | 198 | replaceInArrayAndVector 10000 10000 199 | replaceInArrayAndVector 100000 10000 200 | replaceInArrayAndVector 1000000 10000 201 | (*** include-output ***) 202 | -------------------------------------------------------------------------------- /docs/content/fsdocs-custom.css: -------------------------------------------------------------------------------- 1 | #nuget { 2 | margin-top:20px; 3 | font-size: 11pt; 4 | padding:20px; 5 | background: #f5f5f5; 6 | } 7 | 8 | #nuget pre { 9 | font-size:11pt; 10 | -moz-border-radius: 0px; 11 | -webkit-border-radius: 0px; 12 | border-radius: 0px; 13 | background: #404040; 14 | border-style:none; 15 | color: #e0e0e0; 16 | margin-top:15px; 17 | } 18 | -------------------------------------------------------------------------------- /docs/csharp/PersistentHashMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FSharpx.Collections; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace CSharp 6 | { 7 | class PersistentHashMapSamples 8 | { 9 | public static void Samples() 10 | { 11 | // ------------------------------------------------------------ 12 | // Creating PersistentVectors 13 | // ------------------------------------------------------------ 14 | 15 | // [create-hashmap] 16 | // Create an empty PersistentHashMap and add some elements 17 | PersistentHashMap map = 18 | PersistentHashMap.Empty() 19 | .Add(42,"hello") 20 | .Add(99, "world"); 21 | 22 | Console.WriteLine(map[42]); // hello 23 | Console.WriteLine(map[99]); // world 24 | 25 | // Check no. of elements in the PersistentHashMap 26 | Console.WriteLine(map.Length); // 2 27 | 28 | // [/create-hashmap] 29 | 30 | // [modify-hashmap] 31 | PersistentHashMap map2 = 32 | map 33 | .Add(104, "!") 34 | .Add(42, "hi"); // replace existing value 35 | 36 | Console.WriteLine(map2[42]); // hi 37 | Console.WriteLine(map[42]); // hello 38 | 39 | Console.WriteLine(map.Length); // 2 40 | Console.WriteLine(map2.Length); // 3 41 | 42 | // remove the last element from a PersistentHashMap 43 | PersistentHashMap map3 = map2.Remove(104); 44 | 45 | Console.WriteLine(map3.Length); // 2 46 | 47 | // [/modify-hashmap] 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/csharp/PersistentVector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FSharpx.Collections; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace CSharp 6 | { 7 | class PersistentVectorSamples 8 | { 9 | public static void Samples() 10 | { 11 | // ------------------------------------------------------------ 12 | // Creating PersistentVectors 13 | // ------------------------------------------------------------ 14 | 15 | // [create-vector] 16 | // Create an empty PersistentVector and add some elements 17 | PersistentVector vector = 18 | PersistentVector.Empty() 19 | .Conj("hello") 20 | .Conj("world") 21 | .Conj("!"); 22 | 23 | Console.WriteLine(vector[0]); // hello 24 | Console.WriteLine(vector[2]); // ! 25 | 26 | // Check no. of elements in the PersistentVector 27 | Console.WriteLine(vector.Length); // 3 28 | 29 | // [/create-vector] 30 | 31 | // [modify-vector] 32 | PersistentVector vector2 = vector.Conj("!!!").Update(0,"hi"); 33 | 34 | Console.WriteLine(vector2[0]); // hi 35 | Console.WriteLine(vector[0]); // hello 36 | Console.WriteLine(vector2[3]); // !!! 37 | 38 | Console.WriteLine(vector.Length); // 3 39 | Console.WriteLine(vector2.Length); // 4 40 | 41 | // remove the last element from a PersistentVector 42 | PersistentVector vector3 = vector2.Initial; 43 | 44 | Console.WriteLine(vector3.Length); // 3 45 | 46 | // [/modify-vector] 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/csharp/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CSharp 2 | { 3 | class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | PersistentVectorSamples.Samples(); 8 | PersistentHashMapSamples.Samples(); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/csharp/csharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | Properties 6 | csharp 7 | csharp 8 | net6.0 9 | 512 10 | true 11 | true 12 | true 13 | snupkg 14 | 15 | 16 | 17 | 18 | 19 | 20 | {ad3c8e86-f4cf-426f-a31d-015056227777} 21 | FSharpx.Collections.Experimental 22 | 23 | 24 | {1e95a279-c2a9-498b-bc72-6e7a0d6854ce} 25 | FSharpx.Collections 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/csharp/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/img/logo.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/6bf37798ac373b7b08659d29cfd0ee10961f2838/docs/img/logo.pdn -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/6bf37798ac373b7b08659d29cfd0ee10961f2838/docs/img/logo.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # FSharpx.Collections 2 | 3 | **FSharpx.Collections** is a collection of datastructures for use with F# and C#. 4 | 5 | Please contribute to this project. Don't ask for permission, just fork the repository and send pull requests. 6 | 7 | Please also join the [F# Open Source Group](http://fsharp.github.com) 8 | 9 |
10 |
11 |
12 |
13 |

The F# DataFrame library can be installed from NuGet:

14 |
PM> Install-Package FSharpx.Collections
15 |
16 |
17 |
18 |
19 | 20 | ## Contributing and copyright 21 | 22 | The project is hosted on [GitHub][gh] where you can [report issues][issues], fork 23 | the project and submit pull requests. If you're adding new public API, please also 24 | consider adding [samples][content] that can be turned into a documentation. You might 25 | also want to read [library design notes][readme] to understand how it works. 26 | 27 | The library is available under Public Domain license, which allows modification and 28 | redistribution for both commercial and non-commercial purposes. For more information see the 29 | [License file][license] in the GitHub repository. 30 | 31 | [content]: https://github.com/fsprojects/FSharpx.Collections/tree/master/docs/content 32 | [gh]: https://github.com/fsprojects/FSharpx.Collections 33 | [issues]: https://github.com/fsprojects/FSharpx.Collections/issues 34 | [readme]: https://github.com/fsprojects/FSharpx.Collections/blob/master/README.md 35 | [license]: https://github.com/fsprojects/FSharpx.Collections/blob/master/LICENSE.txt 36 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.401", 4 | "rollForward": "minor" 5 | } 6 | } -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | framework: netstandard2.0, net6.0 2 | source https://api.nuget.org/v3/index.json 3 | 4 | nuget FSharp.Core >= 4.3.4 lowest_matching:true 5 | nuget Expecto 6 | nuget Expecto.FsCheck 7 | nuget FsCheck 8 | nuget Microsoft.NET.Test.Sdk 9 | nuget YoloDev.Expecto.TestSdk 10 | 11 | group Test.Fable 12 | framework: netstandard2.0 13 | source https://api.nuget.org/v3/index.json 14 | 15 | nuget Fable.Core 16 | nuget Fable.FastCheck 17 | nuget Fable.FastCheck.Jest 18 | nuget Fable.Jester 19 | nuget FSharp.Core 20 | -------------------------------------------------------------------------------- /paket.lock: -------------------------------------------------------------------------------- 1 | RESTRICTION: || (== net6.0) (== netstandard2.0) 2 | NUGET 3 | remote: https://api.nuget.org/v3/index.json 4 | Expecto (8.13.1) 5 | FSharp.Core (>= 4.3.4) 6 | Mono.Cecil (>= 0.11) 7 | Expecto.FsCheck (8.13.1) 8 | Expecto (>= 8.13.1) 9 | FsCheck (>= 2.14) 10 | FsCheck (2.16.5) 11 | FSharp.Core (>= 4.2.3) 12 | FSharp.Core (4.3.4) 13 | Microsoft.CodeCoverage (17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) 14 | Microsoft.NET.Test.Sdk (17.3.1) 15 | Microsoft.CodeCoverage (>= 17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) 16 | Microsoft.TestPlatform.TestHost (>= 17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) 17 | Microsoft.TestPlatform.ObjectModel (17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) 18 | NuGet.Frameworks (>= 5.11) 19 | System.Reflection.Metadata (>= 1.6) 20 | Microsoft.TestPlatform.TestHost (17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) 21 | Microsoft.TestPlatform.ObjectModel (>= 17.3.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) 22 | Newtonsoft.Json (>= 9.0.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) 23 | Mono.Cecil (0.11.4) 24 | Newtonsoft.Json (13.0.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) 25 | NuGet.Frameworks (6.3) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= netcoreapp2.1)) 26 | System.Collections.Immutable (6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net461)) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= netcoreapp2.0)) (&& (== netstandard2.0) (>= netcoreapp2.1)) 27 | System.Runtime.CompilerServices.Unsafe (>= 6.0) 28 | System.Reflection.Metadata (6.0.1) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= netcoreapp2.1)) 29 | System.Collections.Immutable (>= 6.0) 30 | System.Runtime.CompilerServices.Unsafe (6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net461)) (&& (== netstandard2.0) (>= netcoreapp2.0)) 31 | YoloDev.Expecto.TestSdk (0.8) 32 | Expecto (>= 8.10 < 9.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net461)) (&& (== netstandard2.0) (>= netcoreapp2.0)) 33 | FSharp.Core (>= 4.3.4) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net461)) (&& (== netstandard2.0) (>= netcoreapp2.0)) 34 | System.Collections.Immutable (>= 1.4) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net461)) (&& (== netstandard2.0) (>= netcoreapp2.0)) 35 | 36 | GROUP Test.Fable 37 | RESTRICTION: == netstandard2.0 38 | NUGET 39 | remote: https://api.nuget.org/v3/index.json 40 | Fable.AST (4.2) 41 | Fable.Browser.Blob (1.2) 42 | Fable.Core (>= 3.0) 43 | FSharp.Core (>= 4.7.2) 44 | Fable.Browser.Dom (2.10) 45 | Fable.Browser.Blob (>= 1.2) 46 | Fable.Browser.Event (>= 1.5) 47 | Fable.Browser.WebStorage (>= 1.1) 48 | Fable.Core (>= 3.2.8) 49 | FSharp.Core (>= 4.7.2) 50 | Fable.Browser.Event (1.5) 51 | Fable.Browser.Gamepad (>= 1.1) 52 | Fable.Core (>= 3.0) 53 | FSharp.Core (>= 4.7.2) 54 | Fable.Browser.Gamepad (1.1) 55 | Fable.Core (>= 3.0) 56 | FSharp.Core (>= 4.7.2) 57 | Fable.Browser.WebStorage (1.1) 58 | Fable.Browser.Event (>= 1.5) 59 | Fable.Core (>= 3.0) 60 | FSharp.Core (>= 4.7.2) 61 | Fable.Core (3.7.1) 62 | Fable.Elmish (3.1) 63 | Fable.Core (>= 3.0) 64 | FSharp.Core (>= 4.6.2) 65 | Fable.FastCheck (0.33) 66 | Fable.Core (>= 3.2.8 < 4.0) 67 | Fable.Elmish (>= 3.1 < 4.0) 68 | Fable.Promise (>= 2.2.2 < 3.0) 69 | FSharp.Core (>= 4.7.2) 70 | Fable.FastCheck.Jest (0.33) 71 | Fable.Core (>= 3.2.8 < 4.0) 72 | Fable.FastCheck (>= 0.33) 73 | Fable.Jester (>= 0.33) 74 | Fable.Promise (>= 2.2.2 < 3.0) 75 | FSharp.Core (>= 4.7.2) 76 | Fable.Jester (0.33) 77 | Fable.Browser.Dom (>= 2.4.4 < 3.0) 78 | Fable.Core (>= 3.2.8 < 4.0) 79 | Fable.Node (>= 1.0.2 < 2.0) 80 | Fable.Promise (>= 2.2.2 < 3.0) 81 | Feliz (>= 1.47 < 2.0) 82 | FSharp.Core (>= 4.7.2) 83 | Fable.Node (1.2) 84 | Fable.Core (>= 3.1.2) 85 | FSharp.Core (>= 5.0) 86 | Fable.Promise (2.2.2) 87 | Fable.Core (>= 3.1.5) 88 | FSharp.Core (>= 4.7.2) 89 | Fable.React (9.0.1) 90 | Fable.Core (>= 3.2.7) 91 | Fable.React.Types (>= 18.0) 92 | FSharp.Core (>= 4.7.2) 93 | Fable.React.Types (18.0) 94 | Fable.Browser.Dom (>= 2.4.4) 95 | Fable.Core (>= 3.2.7) 96 | FSharp.Core (>= 4.7.2) 97 | Feliz (1.68) 98 | Fable.Core (>= 3.1.5) 99 | Fable.React (>= 7.4) 100 | Feliz.CompilerPlugins (>= 1.10) 101 | FSharp.Core (>= 4.7.2) 102 | Feliz.CompilerPlugins (1.10) 103 | Fable.AST (>= 3.0) 104 | FSharp.Core (>= 4.7.2) 105 | FSharp.Core (6.0.6) 106 | -------------------------------------------------------------------------------- /paket.references: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/6bf37798ac373b7b08659d29cfd0ee10961f2838/paket.references -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | .pdb; $(AllowedOutputExtensionsInPackageBuildOutputFolder) 6 | 7 | true 8 | true 9 | 10 | 11 | 12 | 13 | 14 | false 15 | 16 | FSharpx.Collections is a collection of datastructures for use with F# and C#. 17 | 18 | Steffen Forkmann, Daniel Mohl, Tomas Petricek, Ryan Riley, Mauricio Scheffer, Phil Trelford, JackFox 19 | Copyright 2013-2021 20 | F# fsharp fsharpx collections datastructures 21 | https://github.com/fsprojects/FSharpx.Collections 22 | https://fsprojects.github.io/FSharpx.Collections/ 23 | https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/master/docs/img/logo.png 24 | Apache-2.0 25 | git 26 | 27 | https://github.com/fsprojects/FSharpx.Collections/blob/master/LICENSE.txt 28 | http://fsprojects.github.io/FSharpx.Collections/ 29 | https://github.com/fsprojects/FSharpx.Collections/blob/master/LICENSE.txt 30 | https://github.com/fsprojects/FSharpx.Collections/blob/master/RELEASE_NOTES.md 31 | fixed-left 32 | true 33 | default 34 | 35 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // Auto-Generated by FAKE; do not edit 2 | namespace System 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | [] 13 | [] 14 | do () 15 | 16 | module internal AssemblyVersionInformation = 17 | let [] AssemblyTitle = "FSharpx.Collections.Experimental" 18 | let [] AssemblyProduct = "FSharpx.Collections" 19 | let [] AssemblyDescription = "FSharpx.Collections is a collection of datastructures for use with F# and C#." 20 | let [] InternalsVisibleTo = "FSharpx.Collections.Tests" 21 | let [] InternalsVisibleTo_1 = "FSharpx.Collections.Experimental.Tests" 22 | let [] AssemblyVersion = "3.0.1" 23 | let [] AssemblyFileVersion = "3.0.1" 24 | let [] AssemblyConfiguration = "Release" 25 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/BinaryRoseTree.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx 4 | open FSharpx.Collections 5 | open System 6 | open System.Collections 7 | open System.Collections.Generic 8 | open System.Linq 9 | open System.Runtime.CompilerServices 10 | 11 | /// Multi-way tree, also known as rose tree. 12 | /// This RoseTree uses a Vector for the children RoseTree forest. 13 | /// Adapted from @mausch F# adaptation of Experimental.RoseTree. 14 | // Ported from http://hackage.haskell.org/packages/archive/containers/latest/doc/html/src/Data-Tree.html 15 | [] 16 | type BinaryRoseTree<'T> = //{ Root: 'T; Children: 'T BinaryRoseTree Vector } 17 | | Nil 18 | | Node of 'T * BinaryRoseTree<'T> * BinaryRoseTree<'T> 19 | 20 | override x.Equals y = 21 | match y with 22 | | :? BinaryRoseTree<'T> as y -> (x :> _ IEquatable).Equals y 23 | | _ -> false 24 | 25 | override x.GetHashCode() = 26 | let mutable hash = 1 27 | 28 | for v in x do 29 | hash <- 31 * hash + Unchecked.hash v 30 | 31 | hash 32 | 33 | interface IEquatable> with 34 | member x.Equals y = 35 | x.SequenceEqual y 36 | 37 | interface IEnumerable<'T> with 38 | member x.GetEnumerator() = 39 | (let rec loop x = seq { 40 | match x with 41 | | Node(a, Nil, Nil) -> yield a 42 | | Node(a, children, siblings) -> 43 | yield a 44 | yield! loop children 45 | yield! loop siblings 46 | | Nil -> () 47 | } 48 | 49 | loop x) 50 | .GetEnumerator() 51 | 52 | interface System.Collections.IEnumerable with 53 | member x.GetEnumerator() = 54 | (x :> seq<'T>).GetEnumerator() :> IEnumerator 55 | 56 | [] 57 | module BinaryRoseTree = 58 | 59 | let inline createTree root (children: BinaryRoseTree<'T>) = 60 | Node(root, children, Nil) 61 | 62 | let inline createForest root (children: BinaryRoseTree<'T>) (sibling: BinaryRoseTree<'T>) = 63 | Node(root, children, sibling) 64 | 65 | let empty = Nil 66 | 67 | let inline singleton root = 68 | createTree root Nil 69 | 70 | /// loads from sequences of objects and sequences of objects, assumed to be well-constructed 71 | /// elements in final tree will be objects, regardless of original type 72 | let ofSeq(xs: seq) = 73 | 74 | let rec outerLoop(xs': seq) = 75 | let lazyXs = LazyList.ofSeq xs' 76 | 77 | let rec loop(xs'': LazyList) = 78 | match xs'' with 79 | | LazyList.Nil -> Nil 80 | | LazyList.Cons(x, LazyList.Nil) -> 81 | match x with 82 | | :? seq as x' -> outerLoop x' 83 | | _ -> Node(x, Nil, Nil) 84 | 85 | | LazyList.Cons(x, ys) -> 86 | match x with 87 | | :? seq as x' -> outerLoop x' 88 | | _ -> 89 | match ys with 90 | | LazyList.Nil -> Node(x, Nil, Nil) 91 | | LazyList.Cons(y, ys') -> 92 | match y with 93 | | :? seq as y' -> Node(x, (outerLoop y'), (loop ys')) 94 | | _ -> Node(x, Nil, (loop ys)) 95 | 96 | loop lazyXs 97 | 98 | outerLoop xs 99 | 100 | let private map2 nodeF leafV (x: _ BinaryRoseTree) = 101 | 102 | let rec loop (x: _ BinaryRoseTree) cont = 103 | match x with 104 | | Node(a, children, siblings) -> loop children (fun lacc -> loop siblings (fun racc -> cont(nodeF a lacc racc))) 105 | | Nil -> cont leafV 106 | 107 | loop x (fun x -> x) 108 | 109 | let map f (tree: _ BinaryRoseTree) = 110 | (map2 (fun x (l: BinaryRoseTree<_>) (r: BinaryRoseTree<_>) -> Node(f x, l, r)) Nil tree) 111 | 112 | let rec preOrder(x: _ BinaryRoseTree) = seq { 113 | match x with 114 | | Node(a, Nil, Nil) -> yield a 115 | | Node(a, children, siblings) -> 116 | yield a 117 | yield! preOrder children 118 | yield! preOrder siblings 119 | | Nil -> () 120 | } 121 | 122 | let rec postOrder(x: _ BinaryRoseTree) = seq { 123 | match x with 124 | | Node(a, Nil, Nil) -> yield a 125 | | Node(a, children, siblings) -> 126 | yield! postOrder children 127 | yield a 128 | yield! postOrder siblings 129 | | Nil -> () 130 | } 131 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/BinaryTreeZipper.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | /// A simple binary tree 4 | type BinaryTree<'T> = 5 | | Leaf 6 | | Branch of 'T * BinaryTree<'T> * BinaryTree<'T> 7 | 8 | type TreeDirection = 9 | | Left 10 | | Right 11 | 12 | /// The zipper data structure for binary trees 13 | type BinaryTreeZipper<'T> = { 14 | Focus: BinaryTree<'T> 15 | Path: (TreeDirection * 'T * BinaryTree<'T>) list 16 | } 17 | 18 | /// TreeZipper 19 | /// original implementation taken from http://blog.xquant.net/?p=156 20 | [] 21 | module BinaryTreeZipper = 22 | 23 | /// Creates a new branch with the label x and two leaves as subbranches 24 | let branch x = 25 | Branch(x, Leaf, Leaf) 26 | 27 | /// Moves the zipper one level up 28 | let up z = 29 | match z.Path with 30 | | (Left, v, other) :: ep -> { 31 | Focus = Branch(v, z.Focus, other) 32 | Path = ep 33 | } 34 | | (Right, v, other) :: ep -> { 35 | Focus = Branch(v, other, z.Focus) 36 | Path = ep 37 | } 38 | | [] -> failwith "can't go up" // because ep only goes down and is empty 39 | 40 | /// Moves the zipper to the top 41 | let rec top z = 42 | match z.Path with 43 | | [] -> z 44 | | _ -> top(up z) 45 | 46 | /// Moves the zipper to the left 47 | let left z = 48 | match z.Focus with 49 | | (Branch(v, explored, other)) -> { 50 | Focus = explored 51 | Path = (Left, v, other) :: z.Path 52 | } 53 | | Leaf -> failwith "can't go down on leaf" 54 | 55 | /// Moves the zipper to the right 56 | let right z = 57 | match z.Focus with 58 | | (Branch(v, other, explored)) -> { 59 | Focus = explored 60 | Path = (Right, v, other) :: z.Path 61 | } 62 | | Leaf -> failwith "can't go down on leaf" 63 | 64 | /// Modifies the current focus inside the zipper 65 | let setFocus newFocus zipper = 66 | { zipper with Focus = newFocus } 67 | 68 | /// Creates a zipper from a tree 69 | let zipper t = { Focus = t; Path = [] } 70 | 71 | type TreeZipperDirection = 72 | | Up 73 | | Left 74 | | Right 75 | 76 | let inline getMove direction = 77 | match direction with 78 | | Up -> up 79 | | Left -> left 80 | | Right -> right 81 | 82 | /// Moves the zipper in the directions of the given list 83 | let rec move directions (z: BinaryTreeZipper<'T>) = 84 | directions |> Seq.map getMove |> Seq.fold (|>) z 85 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/BootstrappedQueue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | 5 | type NonEmptyBootstrappedQueue<'T> = 6 | { 7 | FrontAndSuspensionsLength: int 8 | Front: list<'T> 9 | Suspensions: BootstrappedQueue>> 10 | RBackLength: int 11 | RBack: list<'T> 12 | } 13 | 14 | static member create lenfm f m lenr r = { 15 | FrontAndSuspensionsLength = lenfm 16 | Front = f 17 | Suspensions = m 18 | RBackLength = lenr 19 | RBack = r 20 | } 21 | 22 | and BootstrappedQueue<'T> = 23 | | Empty 24 | | NonEmpty of NonEmptyBootstrappedQueue<'T> 25 | 26 | // polymorphic recursion cannot be achieved through let-bound functions 27 | // hence we use static member methods 28 | 29 | static member checkQ(q: NonEmptyBootstrappedQueue<'T>) = 30 | if q.RBackLength <= q.FrontAndSuspensionsLength then 31 | BootstrappedQueue.checkF q 32 | else 33 | let susp = BootstrappedQueue.snoc (lazy List.rev q.RBack) q.Suspensions 34 | 35 | NonEmptyBootstrappedQueue<'T>.create (q.FrontAndSuspensionsLength + q.RBackLength) q.Front susp 0 [] 36 | |> BootstrappedQueue.checkF 37 | 38 | static member checkF(q: NonEmptyBootstrappedQueue<'T>) : BootstrappedQueue<'T> = 39 | match q.Front, q.Suspensions with 40 | | [], Empty -> Empty 41 | | [], m -> 42 | let f = (BootstrappedQueue.head m).Force() 43 | let susp = BootstrappedQueue.tail m 44 | 45 | NonEmpty 46 | <| NonEmptyBootstrappedQueue<'T>.create q.FrontAndSuspensionsLength f susp q.RBackLength q.RBack 47 | | _ -> NonEmpty q 48 | 49 | static member snoc(x: 'T) : BootstrappedQueue<'T> -> BootstrappedQueue<'T> = 50 | function 51 | | Empty -> NonEmpty <| NonEmptyBootstrappedQueue<'T>.create 1 [ x ] Empty 0 [] 52 | | NonEmpty q -> 53 | let lenr = q.RBackLength + 1 54 | let r = x :: q.RBack 55 | 56 | NonEmptyBootstrappedQueue<'T>.create q.FrontAndSuspensionsLength q.Front q.Suspensions lenr r 57 | |> BootstrappedQueue<'T>.checkQ 58 | 59 | static member head: BootstrappedQueue<'T> -> 'T = 60 | function 61 | | Empty -> raise Exceptions.Empty 62 | | NonEmpty q -> List.head q.Front 63 | 64 | static member tryGetHead: BootstrappedQueue<'T> -> 'T option = 65 | function 66 | | Empty -> None 67 | | NonEmpty q -> Some(List.head q.Front) 68 | 69 | static member tail: BootstrappedQueue<'T> -> BootstrappedQueue<'T> = 70 | function 71 | | Empty -> raise Exceptions.Empty 72 | | NonEmpty q -> 73 | let lenfm = q.FrontAndSuspensionsLength - 1 74 | let f' = List.tail q.Front 75 | 76 | NonEmptyBootstrappedQueue<'T>.create lenfm f' q.Suspensions q.RBackLength q.RBack 77 | |> BootstrappedQueue<'T>.checkQ 78 | 79 | static member tryGetTail: BootstrappedQueue<'T> -> BootstrappedQueue<'T> option = 80 | function 81 | | Empty -> None 82 | | NonEmpty q -> 83 | let lenfm = q.FrontAndSuspensionsLength - 1 84 | let f' = List.tail q.Front 85 | 86 | NonEmptyBootstrappedQueue<'T>.create lenfm f' q.Suspensions q.RBackLength q.RBack 87 | |> BootstrappedQueue<'T>.checkQ 88 | |> Some 89 | 90 | static member length: BootstrappedQueue<'T> -> int = 91 | function 92 | | Empty -> 0 93 | | NonEmpty q -> q.FrontAndSuspensionsLength + q.RBackLength 94 | 95 | static member ofList(l: List<'T>) : BootstrappedQueue<'T> = 96 | let b0 = BootstrappedQueue.Empty 97 | NonEmptyBootstrappedQueue<'T>.create (l.Length) l b0 0 [] |> NonEmpty 98 | 99 | /// bootstrapped queue from Chris Okasaki's "Purely functional data structures" 100 | /// original implementation taken from http://lepensemoi.free.fr/index.php/2010/02/18/bootstrapped-queue 101 | [] 102 | module BootstrappedQueue = 103 | 104 | ///O(1). Returns queue of no elements. 105 | let empty = Empty 106 | 107 | ///O(1). Returns true if the queue has no elements 108 | let isEmpty = 109 | function 110 | | Empty -> true 111 | | _ -> false 112 | 113 | ///O(log* n). Returns a new queue with the element added to the end. 114 | let inline snoc x queue = 115 | BootstrappedQueue.snoc x queue 116 | 117 | ///O(1), worst case. Returns the first element. 118 | let inline head queue = 119 | BootstrappedQueue<'T>.head queue 120 | 121 | ///O(1), worst case. Returns option first element. 122 | let inline tryGetHead queue = 123 | BootstrappedQueue<'T>.tryGetHead queue 124 | 125 | ///O(log* n), worst case. Returns a new queue of the elements trailing the first element. 126 | let inline tail queue = 127 | BootstrappedQueue<'T>.tail queue 128 | 129 | ///O(log* n), worst case. Returns option queue of the elements trailing the first element. 130 | let inline tryGetTail queue = 131 | BootstrappedQueue<'T>.tryGetTail queue 132 | 133 | ///O(1). Returns the count of elements. 134 | let inline length queue = 135 | BootstrappedQueue<'T>.length queue 136 | 137 | ///O(1). Returns a queue of the list. 138 | let inline ofList list = 139 | BootstrappedQueue<'T>.ofList list 140 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/BottomUpMergeSort.fs: -------------------------------------------------------------------------------- 1 | // BottomUp merge sort from Chris Okasaki's "Purely functional data structures" 2 | // original implementation taken from http://lepensemoi.free.fr/index.php/2010/01/07/bottom-up-merge-sort 3 | 4 | namespace FSharpx.Collections.Experimental 5 | 6 | [] 7 | module BottomUpMergeSort = 8 | 9 | type Sortable<'T> = { 10 | Size: int 11 | Segments: Lazy>> 12 | } 13 | 14 | let rec merge xs ys = 15 | match xs, ys with 16 | | [], ys -> ys 17 | | xs, [] -> xs 18 | | x :: tlx, y :: tly -> if x <= y then x :: merge tlx ys else y :: merge xs tly 19 | 20 | let empty<'T> : Sortable<'T> = { Size = 0; Segments = lazy [] } 21 | 22 | let isEmpty x = 23 | x.Size = 0 24 | 25 | let singleton x = { Size = 1; Segments = lazy [ [ x ] ] } 26 | 27 | let rec addSeg seg segs size = 28 | if size % 2 = 0 then 29 | seg :: segs 30 | else 31 | addSeg (merge seg (List.head segs)) (List.tail segs) (size / 2) 32 | 33 | let add x y = { 34 | Size = y.Size + 1 35 | Segments = lazy addSeg [ x ] (y.Segments.Force()) y.Size 36 | } 37 | 38 | let rec mergeAll xs ys = 39 | match xs, ys with 40 | | xs, [] -> xs 41 | | xs, seg :: segs -> mergeAll (merge xs seg) segs 42 | 43 | let sort x = 44 | mergeAll [] (x.Segments.Force()) 45 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/CSharpCompat.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open FSharpx 6 | open FSharpx.Collections 7 | 8 | [] 9 | type RoseTree = 10 | static member Singleton x = 11 | RoseTree.singleton x 12 | 13 | static member Create(root, [] children) = 14 | RoseTree.create root (LazyList.ofArray children) 15 | 16 | static member Create(root, children) = 17 | RoseTree.create root children 18 | 19 | [] 20 | static member Select(tree, f: Func<_, _>) = 21 | RoseTree.map f.Invoke tree 22 | 23 | [] 24 | static member SelectMany(o, f: Func<_, _>) = 25 | RoseTree.bind f.Invoke o 26 | 27 | [] 28 | static member SelectMany(o, f: Func<_, _>, mapper: Func<_, _, _>) = 29 | let mapper = RoseTree.lift2(fun a b -> mapper.Invoke(a, b)) 30 | let v = RoseTree.bind f.Invoke o 31 | mapper o v 32 | 33 | [] 34 | static member SelectAccum(o, state, f: Func<_, _, _>) = 35 | RoseTree.mapAccum f.Invoke state o 36 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/DList.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx 4 | open System.Collections 5 | open System.Collections.Generic 6 | 7 | /// The DList is an implementation of John Hughes' append list. 8 | /// See http://dl.acm.org/citation.cfm?id=8475 for more information. 9 | /// This implementation adds an additional parameter to allow a more 10 | /// efficient calculation of the list length. 11 | /// Note that an alternate form would represent the DList as: 12 | /// type DList<'T> = DList of ('T list -> 'T list) 13 | /// An example can be found at http://stackoverflow.com/questions/5324623/functional-o1-append-and-on-iteration-from-first-element-list-data-structure/5327209#5327209 14 | type DList<'T> = 15 | | Nil 16 | | Unit of 'T 17 | | Join of DList<'T> * DList<'T> * int (* the total length of the DList *) 18 | 19 | static member op_Equality(left, right) = 20 | match left with 21 | | Nil -> 22 | match right with 23 | | Nil -> true 24 | | _ -> false 25 | | Unit x -> 26 | match right with 27 | | Unit y -> x = y 28 | | _ -> false 29 | | Join(x, y, l) -> 30 | match right with 31 | | Join(x', y', l') -> l = l' && x = x' && y = y' // TODO: || iterate each and compare the values. 32 | | _ -> false 33 | 34 | static member op_Nil() = Nil 35 | 36 | ///O(1). Returns the count of elements. 37 | member x.Length = 38 | match x with 39 | | Nil -> 0 40 | | Unit _ -> 1 41 | | Join(_, _, l) -> l 42 | 43 | ///O(1). Returns true if the DList has no elements. 44 | member x.IsEmpty = 45 | match x with 46 | | Nil -> true 47 | | _ -> false 48 | 49 | ///O(log n). Returns the first element. 50 | member x.Head = 51 | match x with 52 | | Unit x' -> x' 53 | | Join(x', y, _) -> x'.Head 54 | | _ -> failwith "DList.head: empty list" 55 | 56 | ///O(1). Returns a new DList with the element added to the end. 57 | member x.snoc(a: 'T) = 58 | //DList<_>.op_Append(x, Unit a) 59 | match x with 60 | | Nil -> Unit a 61 | | _ -> 62 | match Unit a with 63 | | Nil -> x 64 | | _ -> Join(x, (Unit a), x.Length + (Unit a).Length) 65 | 66 | ///O(log n). Returns a new DList of the elements trailing the first element. 67 | member x.Tail = 68 | let rec step (xs: DList<'T>) (acc: DList<'T>) : DList<'T> = 69 | match xs with 70 | | Nil -> acc 71 | | Unit _ -> acc 72 | | Join(x, y, _) -> 73 | step 74 | x 75 | (match y with 76 | | Nil -> acc 77 | | _ -> 78 | match acc with 79 | | Nil -> y 80 | | _ -> Join(y, acc, y.Length + acc.Length)) 81 | 82 | if x.IsEmpty then Nil else step x Nil 83 | 84 | interface IEnumerable<'T> with 85 | member x.GetEnumerator() = 86 | let enumerable = seq { 87 | match x with 88 | | Nil -> () 89 | | Unit x -> yield x 90 | | Join(x, y, _) -> 91 | yield! x :> seq<'T> 92 | yield! y :> seq<'T> 93 | } 94 | 95 | enumerable.GetEnumerator() 96 | 97 | member x.GetEnumerator() = 98 | let enumerable = seq { 99 | match x with 100 | | Nil -> () 101 | | Unit x -> yield x 102 | | Join(x, y, _) -> 103 | yield! x :> seq<'T> 104 | yield! y :> seq<'T> 105 | } 106 | 107 | enumerable.GetEnumerator() :> IEnumerator 108 | 109 | [] 110 | module DList = 111 | ///O(1). Returns DList of no elements. 112 | let empty<'T> : DList<'T> = Nil 113 | 114 | ///O(1). Returns true if the DList has no elements. 115 | let isEmpty(l: DList<_>) = l.IsEmpty 116 | 117 | ///O(1). Returns the count of elements. 118 | let length(l: DList<_>) = l.Length 119 | 120 | ///O(1). Returns DList of one elements. 121 | let singleton x = Unit x 122 | 123 | ///O(n). Returns a DList of the seq. 124 | let ofSeq s = 125 | Seq.fold 126 | (fun xs x -> 127 | match xs with 128 | | Nil -> Unit x 129 | | Unit _ -> Join(xs, Unit x, 2) 130 | | Join(_, _, l) -> Join(xs, Unit x, l + 1)) 131 | Nil 132 | s 133 | 134 | ///O(n). Returns a seq of the DList elements. 135 | let toSeq(l: DList<_>) = 136 | l :> _ seq 137 | 138 | ///O(1). Returns a new DList with the element added to the beginning. 139 | let cons hd tl = 140 | match tl with 141 | | Nil -> Unit hd 142 | | _ -> Join(Unit hd, tl, tl.Length + 1) 143 | 144 | ///O(1). Returns a new DList of two lists. 145 | let append left right = 146 | match left with 147 | | Nil -> right 148 | | _ -> 149 | match right with 150 | | Nil -> left 151 | | _ -> Join(left, right, left.Length + right.Length) 152 | 153 | ///O(log n). Returns the first element. 154 | let head(l: DList<_>) = l.Head 155 | 156 | ///O(1). Returns a new DList with the element added to the end. 157 | let snoc (l: DList<_>) x = l.snoc x 158 | 159 | ///O(log n). Returns a new DList of the elements trailing the first element. 160 | let tail(l: DList<_>) = l.Tail 161 | 162 | /// Fold walks the DList using constant stack space. Implementation is from Norman Ramsey. 163 | /// See http://stackoverflow.com/questions/5324623/functional-o1-append-and-on-iteration-from-first-element-list-data-structure/5334068#5334068 164 | let fold f seed l = 165 | let rec walk lefts l xs = 166 | match l with 167 | | Nil -> finish lefts xs 168 | | Unit x -> finish lefts <| f xs x 169 | | Join(x, y, _) -> walk (x :: lefts) y xs 170 | 171 | and finish lefts xs = 172 | match lefts with 173 | | [] -> xs 174 | | t :: ts -> walk ts t xs in 175 | 176 | walk [] l seed 177 | 178 | let toList l = 179 | fold (fun a b -> FSharpx.Collections.List.cons b a) [] l 180 | 181 | let toArray l = 182 | l |> toList |> Array.ofList 183 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/EagerRoseTree.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | open System 5 | open System.Linq 6 | open System.Runtime.CompilerServices 7 | 8 | /// Multi-way tree, also known as rose tree. 9 | /// This RoseTree uses a List for the children RoseTree forest. 10 | /// Adapted from @mausch F# adaptation of Experimental.RoseTree. 11 | // Ported from http://hackage.haskell.org/packages/archive/containers/latest/doc/html/src/Data-Tree.html 12 | [] 13 | type EagerRoseTree<'T> = 14 | { 15 | Root: 'T 16 | Children: EagerRoseForest<'T> 17 | } 18 | 19 | override x.Equals y = 20 | match y with 21 | | :? EagerRoseTree<'T> as y -> (x :> _ IEquatable).Equals y 22 | | _ -> false 23 | 24 | override x.GetHashCode() = 25 | 391 + (box x.Root).GetHashCode() * 23 + x.Children.GetHashCode() 26 | 27 | interface IEquatable> with 28 | 29 | member x.Equals y = 30 | obj.Equals(x.Root, y.Root) 31 | && (x.Children :> _ seq).SequenceEqual y.Children 32 | 33 | and EagerRoseForest<'T> = EagerRoseTree<'T> list 34 | 35 | [] 36 | [] 37 | module EagerRoseTree = 38 | open FSharpx 39 | 40 | let inline create root children = { Root = root; Children = children } 41 | 42 | let inline singleton x = 43 | create x List.empty 44 | 45 | let rec map f (x: _ EagerRoseTree) = { 46 | EagerRoseTree.Root = f x.Root 47 | Children = List.map (map f) x.Children 48 | } 49 | 50 | let rec ap x f = { 51 | EagerRoseTree.Root = f.Root x.Root 52 | Children = 53 | let a = List.map (map f.Root) x.Children 54 | let b = List.map (fun c -> ap x c) f.Children 55 | List.append a b 56 | } 57 | 58 | let inline lift2 f a b = 59 | singleton f |> ap a |> ap b 60 | 61 | let rec bind f x = 62 | let a = f x.Root 63 | 64 | { 65 | EagerRoseTree.Root = a.Root 66 | Children = List.append a.Children (List.map (bind f) x.Children) 67 | } 68 | 69 | [] 70 | [] 71 | let rec dfsPre(x: _ EagerRoseTree) = seq { 72 | yield x.Root 73 | yield! Seq.collect dfsPre x.Children 74 | } 75 | 76 | [] 77 | [] 78 | let rec dfsPost(x: _ EagerRoseTree) = seq { 79 | yield! Seq.collect dfsPost x.Children 80 | yield x.Root 81 | } 82 | 83 | let rec unfold f seed = 84 | let root, bs = f seed 85 | create root (unfoldForest f bs) 86 | 87 | and unfoldForest f = 88 | List.map(unfold f) 89 | 90 | /// Behaves like a combination of map and fold; 91 | /// it applies a function to each element of a tree, 92 | /// passing an accumulating parameter, 93 | /// and returning a final value of this accumulator together with the new tree. 94 | let rec mapAccum f state tree = 95 | let nstate, root = f state tree.Root 96 | let nstate, children = List.mapAccum (mapAccum f) nstate tree.Children 97 | nstate, create root children 98 | 99 | // TODO: 100 | // bfs: http://pdf.aminer.org/000/309/950/the_under_appreciated_unfold.pdf 101 | // sequence / mapM / filterM 102 | // zipper: http://hackage.haskell.org/package/rosezipper-0.2 103 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/FSharpx.Collections.Experimental.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library 5 | FSharpx.Collections.Experimental 6 | FSharpx.Collections.Experimental 7 | FSharpx.Collections.Experimental 8 | netstandard2.0 9 | 10 | true 11 | 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | False 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/HeapPriorityQueue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | 5 | [] 6 | module HeapPriorityQueue = 7 | 8 | let empty<'T when 'T: comparison> maxQueue = 9 | PairingHeap.empty maxQueue :> IPriorityQueue<'T> 10 | 11 | let inline isEmpty(pq: IPriorityQueue<'T>) = 12 | pq.IsEmpty 13 | 14 | let inline insert element (pq: IPriorityQueue<'T>) = 15 | pq.Insert element 16 | 17 | let inline tryPeek(pq: IPriorityQueue<'T>) = 18 | pq.TryPeek() 19 | 20 | let inline peek(pq: IPriorityQueue<'T>) = pq.Peek() 21 | 22 | let inline tryPop(pq: IPriorityQueue<'T>) = 23 | pq.TryPop() 24 | 25 | let inline pop(pq: IPriorityQueue<'T>) = pq.Pop() 26 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/ImplicitQueue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | 5 | type Digit<'T> = 6 | | Zero 7 | | One of 'T 8 | | Two of 'T * 'T 9 | 10 | type ImplicitQueue<'T> = 11 | | Shallow of Digit<'T> 12 | | Deep of Digit<'T> * Lazy> * Digit<'T> 13 | 14 | type ImplicitQueue<'T> with 15 | //polymorphic recursion cannot be achieved through let-bound functions 16 | //hence we use static member methods 17 | static member snoc(x: 'T) : ImplicitQueue<'T> -> ImplicitQueue<'T> = 18 | function 19 | | Shallow Zero -> Shallow(One x) 20 | | Shallow(One y) -> Deep(Two(y, x), lazy Shallow Zero, Zero) 21 | | Deep(f, m, Zero) -> Deep(f, m, One x) 22 | | Deep(f, m, One y) -> Deep(f, lazy ImplicitQueue.snoc (y, x) (m.Force()), Zero) 23 | | _ -> failwith "should not get there" 24 | 25 | static member head: ImplicitQueue<'T> -> 'T = 26 | function 27 | | Shallow Zero -> raise Exceptions.Empty 28 | | Shallow(One x) -> x 29 | | Deep(One x, m, r) -> x 30 | | Deep(Two(x, y), m, r) -> x 31 | | _ -> failwith "should not get there" 32 | 33 | static member tryGetHead: ImplicitQueue<'T> -> 'T option = 34 | function 35 | | Shallow Zero -> None 36 | | Shallow(One x) -> Some x 37 | | Deep(One x, m, r) -> Some x 38 | | Deep(Two(x, y), m, r) -> Some x 39 | | _ -> failwith "should not get there" 40 | 41 | static member tail: ImplicitQueue<'T> -> ImplicitQueue<'T> = 42 | function 43 | | Shallow Zero -> raise Exceptions.Empty 44 | | Shallow(One x) -> Shallow Zero 45 | | Deep(Two(x, y), m, r) -> Deep(One y, m, r) 46 | | Deep(One x, q, r) -> 47 | let isEmpty = 48 | function 49 | | Shallow Zero -> true 50 | | _ -> false 51 | 52 | let q' = q.Force() 53 | 54 | if isEmpty q' then 55 | Shallow r 56 | else 57 | let y, z = ImplicitQueue.head q' 58 | Deep(Two(y, z), lazy ImplicitQueue.tail q', r) 59 | | _ -> failwith "should not get there" 60 | 61 | static member tryGetTail: ImplicitQueue<'T> -> ImplicitQueue<'T> option = 62 | function 63 | | Shallow Zero -> None 64 | | Shallow(One x) -> Some <| Shallow Zero 65 | | Deep(Two(x, y), m, r) -> Some(Deep(One y, m, r)) 66 | | Deep(One x, q, r) -> 67 | let isEmpty = 68 | function 69 | | Shallow Zero -> true 70 | | _ -> false 71 | 72 | let q' = q.Force() 73 | 74 | if isEmpty q' then 75 | Some(Shallow r) 76 | else 77 | let y, z = ImplicitQueue.head q' 78 | Some(Deep(Two(y, z), lazy ImplicitQueue.tail q', r)) 79 | | _ -> failwith "should not get there" 80 | 81 | /// implicit queue from Chris Okasaki's "Purely functional data structures" 82 | /// original implementation taken from http://lepensemoi.free.fr/index.php/2010/02/18/implicit-queue 83 | [] 84 | module ImplicitQueue = 85 | 86 | ///O(1). Returns queue of no elements. 87 | let empty = Shallow Zero 88 | 89 | ///O(1). Returns true if the queue has no elements 90 | let isEmpty = 91 | function 92 | | Shallow Zero -> true 93 | | _ -> false 94 | 95 | ///O(1), amortized. Returns a new queue with the element added to the end. 96 | let inline snoc x queue = 97 | ImplicitQueue.snoc x queue 98 | 99 | ///O(1), amortized. Returns the first element. 100 | let inline head queue = 101 | ImplicitQueue<'T>.head queue 102 | 103 | ///O(1), amortized. Returns option first element. 104 | let inline tryGetHead queue = 105 | ImplicitQueue<'T>.tryGetHead queue 106 | 107 | ///O(1), amortized. Returns a new queue of the elements trailing the first element. 108 | let inline tail queue = 109 | ImplicitQueue<'T>.tail queue 110 | 111 | ///O(1), amortized. Returns option queue of the elements trailing the first element. 112 | let inline tryGetTail queue = 113 | ImplicitQueue<'T>.tryGetTail queue 114 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/IndexedRoseTree.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx 4 | open FSharpx.Collections 5 | open System 6 | open System.Linq 7 | open System.Runtime.CompilerServices 8 | 9 | /// Multi-way tree, also known as rose tree. 10 | /// This RoseTree uses a Vector for the children RoseTree forest. 11 | /// Adapted from @mausch F# adaptation of Experimental.RoseTree. 12 | // Ported from http://hackage.haskell.org/packages/archive/containers/latest/doc/html/src/Data-Tree.html 13 | [] 14 | type IndexedRoseTree<'T> = 15 | { 16 | Root: 'T 17 | Children: PersistentVector> 18 | } 19 | 20 | override x.Equals y = 21 | match y with 22 | | :? IndexedRoseTree<'T> as y -> (x :> IEquatable<_>).Equals y 23 | | _ -> false 24 | 25 | override x.GetHashCode() = 26 | 391 + (box x.Root).GetHashCode() * 23 + x.Children.GetHashCode() 27 | 28 | interface IEquatable> with 29 | member x.Equals y = 30 | obj.Equals(x.Root, y.Root) 31 | && (x.Children :> _ seq).SequenceEqual y.Children 32 | 33 | [] 34 | module IndexedRoseTree = 35 | 36 | let inline create root children = { Root = root; Children = children } 37 | 38 | let inline singleton x = 39 | create x PersistentVector.empty 40 | 41 | let rec map f (x: _ IndexedRoseTree) = { 42 | IndexedRoseTree.Root = f x.Root 43 | Children = PersistentVector.map (map f) x.Children 44 | } 45 | 46 | let rec ap x f = { 47 | IndexedRoseTree.Root = f.Root x.Root 48 | Children = 49 | let a = PersistentVector.map (map f.Root) x.Children 50 | let b = PersistentVector.map (fun c -> ap x c) f.Children 51 | PersistentVector.append a b 52 | } 53 | 54 | let inline lift2 f a b = 55 | singleton f |> ap a |> ap b 56 | 57 | let rec bind f x = 58 | let a = f x.Root 59 | 60 | { 61 | IndexedRoseTree.Root = a.Root 62 | Children = PersistentVector.append a.Children (PersistentVector.map (bind f) x.Children) 63 | } 64 | 65 | let rec preOrder(x: _ IndexedRoseTree) = seq { 66 | yield x.Root 67 | yield! Seq.collect preOrder x.Children 68 | } 69 | 70 | let rec postOrder(x: _ IndexedRoseTree) = seq { 71 | yield! Seq.collect postOrder x.Children 72 | yield x.Root 73 | } 74 | 75 | let rec unfold f seed = 76 | let root, bs = f seed 77 | create root (unfoldForest f bs) 78 | 79 | and unfoldForest f = 80 | PersistentVector.map(unfold f) 81 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/Infrastructure.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2010-2013, as indicated in README.md in the root directory of this distribution. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License") 4 | 5 | namespace FSharpx.Collections.Experimental 6 | 7 | module internal ListHelpr = 8 | 9 | let rec loop2Array (left: 'T array) right = 10 | function 11 | | x when x < 0 -> left, (List.tail right) 12 | | x -> 13 | Array.set left x (List.head right) 14 | loop2Array left (List.tail right) (x - 1) 15 | 16 | let rec loopFromArray frontLen (left: 'T array) right = 17 | function 18 | | x when x > frontLen -> right 19 | | x -> loopFromArray frontLen left (left.[x] :: right) (x + 1) 20 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/ListZipper.fs: -------------------------------------------------------------------------------- 1 | /// ListZipper 2 | /// original implementation taken from http://learnyouahaskell.com/zippers 3 | namespace FSharpx.Collections.Experimental 4 | 5 | #nowarn "25" 6 | /// A zipper for lists 7 | type ListZipper<'T> = { Focus: 'T list; Path: 'T list } 8 | 9 | [] 10 | module ListZipper = 11 | 12 | /// Returns the head element from the list under focus 13 | let focus zipper = 14 | match zipper.Focus with 15 | | x :: _ -> x 16 | 17 | /// Changes the element under the focus 18 | let modify newElement zipper = 19 | match zipper.Focus with 20 | | x :: xs -> { zipper with Focus = newElement :: xs } 21 | 22 | /// Moves the zipper forward 23 | let forward zipper = 24 | match zipper.Focus with 25 | | x :: xs -> { Focus = xs; Path = x :: zipper.Path } 26 | 27 | /// Moves the zipper backwards 28 | let back zipper = 29 | match zipper.Path with 30 | | b :: bs -> { Focus = b :: zipper.Focus; Path = bs } 31 | 32 | /// Moves the zipper to the front 33 | let rec front zipper = 34 | match zipper.Path with 35 | | [] -> zipper 36 | | _ -> back zipper |> front 37 | 38 | /// Creates a list zipper 39 | let zipper list = { Focus = list; Path = [] } 40 | 41 | /// Returns the whole list from the zipper 42 | let getList zipper = 43 | (front zipper).Focus 44 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/RealTimeQueue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | 5 | type RealTimeQueue<'T> = { 6 | F: LazyList<'T> 7 | R: list<'T> 8 | S: LazyList<'T> 9 | } 10 | 11 | /// RealTime queue from Chris Okasaki's "Purely functional data structures" 12 | /// original implementation taken from http://lepensemoi.free.fr/index.php/2010/01/07/real-time-queue 13 | [] 14 | module RealTimeQueue = 15 | 16 | ///O(1). Returns queue of no elements. 17 | let empty<'T> : RealTimeQueue<'T> = { 18 | F = LazyList.empty 19 | R = [] 20 | S = LazyList.empty 21 | } 22 | 23 | ///O(1). Returns true if the queue has no elements 24 | let isEmpty queue = 25 | LazyList.isEmpty queue.F 26 | 27 | let rec rotate queue = 28 | match queue.F with 29 | | LazyList.Nil -> LazyList.cons (queue.R |> List.head) queue.S 30 | | LazyList.Cons(hd, tl) -> 31 | let x = queue.R 32 | let y = List.head x 33 | let ys = List.tail x 34 | let right = LazyList.cons y queue.S 35 | LazyList.cons hd (rotate { F = tl; R = ys; S = right }) 36 | 37 | let rec exec queue = 38 | match queue.S with 39 | | LazyList.Nil -> 40 | let f' = rotate { queue with S = LazyList.empty } 41 | { F = f'; R = []; S = f' } 42 | | LazyList.Cons(hd, tl) -> { queue with S = tl } 43 | 44 | ///O(1), worst case. Returns a new queue with the element added to the end. 45 | let snoc x queue = 46 | exec { queue with R = (x :: queue.R) } 47 | 48 | ///O(1), worst case. Returns the first element. 49 | let head queue = 50 | match queue.F with 51 | | LazyList.Nil -> raise Exceptions.Empty 52 | | LazyList.Cons(hd, tl) -> hd 53 | 54 | ///O(1), worst case. Returns option first element. 55 | let tryGetHead queue = 56 | match queue.F with 57 | | LazyList.Nil -> None 58 | | LazyList.Cons(hd, tl) -> Some hd 59 | 60 | ///O(1), worst case. Returns a new queue of the elements trailing the first element. 61 | let tail queue = 62 | match queue.F with 63 | | LazyList.Nil -> raise Exceptions.Empty 64 | | LazyList.Cons(hd, tl) -> exec { queue with F = tl } 65 | 66 | ///O(1), worst case. Returns option queue of the elements trailing the first element. 67 | let tryGetTail queue = 68 | match queue.F with 69 | | LazyList.Nil -> None 70 | | LazyList.Cons(hd, tl) -> Some(exec { queue with F = tl }) 71 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/RingBuffer.fs: -------------------------------------------------------------------------------- 1 | /// Originally from https://bitbucket.org/colinbul/fsharpent 2 | namespace FSharpx.Collections.Experimental 3 | 4 | open System 5 | open System.Collections 6 | open System.Collections.Generic 7 | open FSharpx 8 | 9 | type RingBuffer<'T>(position: int, values: seq<'T>) = 10 | let buffer = values |> Seq.toArray 11 | let mutable position = position 12 | 13 | member x.Buffer = buffer 14 | 15 | member x.Position 16 | with get () = position 17 | and set (value) = position <- value 18 | 19 | new(size) = RingBuffer(0, Array.zeroCreate size) 20 | new(values) = RingBuffer(0, values) 21 | 22 | member private x.IndexOffset(i, offset) = 23 | (i + offset) % x.Buffer.Length 24 | 25 | member x.ToArray() = [| 26 | for i in 0 .. x.Buffer.Length - 1 do 27 | yield x.Buffer.[x.IndexOffset(x.Position, i)] 28 | |] 29 | 30 | member x.Insert(op, offset, items) = 31 | if offset >= 0 && offset < x.Buffer.Length then 32 | let values = Seq.toArray items 33 | let startIndex = x.IndexOffset(x.Position, offset) 34 | 35 | for i in 0 .. (min (x.Buffer.Length - offset) values.Length) - 1 do 36 | let insetIndex = x.IndexOffset(startIndex, i) 37 | x.Buffer.[insetIndex] <- op x.Buffer.[insetIndex] values.[i] 38 | 39 | member x.Insert(offset, items) = 40 | x.Insert((fun _ b -> b), offset, items) 41 | 42 | /// Tries to advance the position of the RingBuffer by the offset. 43 | /// Returns None if offset is negative, otherwise Some containing 44 | /// the position of the RingBuffer. 45 | member x.TryAdvance(offset) = 46 | if offset >= 0 then 47 | for i in 0 .. offset - 1 do 48 | x.Buffer.[x.IndexOffset(x.Position, i)] <- Unchecked.defaultof<'T> 49 | 50 | x.Position <- x.IndexOffset(x.Position, offset) 51 | Some(x.Position) 52 | else 53 | None 54 | 55 | /// Advances the position of the RingBuffer by the offset. 56 | /// Returns the position of the RingBuffer. Throws an ArgumentException if 57 | /// the offset is negative. 58 | member x.Advance(offset) = 59 | match x.TryAdvance(offset) with 60 | | Some(position) -> position 61 | | None -> invalidArg "offset" "the offset must be greater than or equal to zero" 62 | 63 | member x.Normalize() = 64 | x.Buffer.[0 .. x.Buffer.Length - 1] <- x.ToArray() 65 | x.Position <- 0 66 | 67 | member x.Clone() = 68 | RingBuffer<'T>(x.Position, x.ToArray()) 69 | 70 | [] 71 | module RingBuffer = 72 | let create(seq: 'T seq) = 73 | new RingBuffer<'T>(seq) 74 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/RoseTree.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental 2 | 3 | open FSharpx.Collections 4 | open System 5 | open System.Runtime.CompilerServices 6 | 7 | /// Multi-way tree, also known as rose tree. 8 | // Ported from https://hackage.haskell.org/package/containers-0.6.6/docs/Data-Tree.html 9 | [] 10 | type RoseTree<[] 'T> = 11 | { 12 | Root: 'T 13 | Children: LazyList> 14 | } 15 | 16 | override x.Equals y = 17 | match y with 18 | | :? RoseTree<'T> as y -> (x :> _ IEquatable).Equals y 19 | | _ -> false 20 | 21 | override x.GetHashCode() = 22 | 391 23 | + (box x.Root).GetHashCode() * 23 24 | + (x.Children :> _ seq).GetHashCode() 25 | 26 | interface IEquatable> with 27 | member x.Equals y = 28 | Unchecked.equals x.Root y.Root 29 | && LazyList.equalsWith Unchecked.equals x.Children y.Children 30 | 31 | module L = LazyList 32 | 33 | [] 34 | [] 35 | [] 36 | module RoseTree = 37 | open FSharpx 38 | 39 | let inline create root children = { Root = root; Children = children } 40 | 41 | let inline singleton x = 42 | create x L.empty 43 | 44 | let rec map f (x: _ RoseTree) = { 45 | RoseTree.Root = f x.Root 46 | Children = L.map (map f) x.Children 47 | } 48 | 49 | let rec ap x f = { 50 | RoseTree.Root = f.Root x.Root 51 | Children = 52 | let a = L.map (map f.Root) x.Children 53 | let b = L.map (fun c -> ap x c) f.Children 54 | L.append a b 55 | } 56 | 57 | let inline lift2 f a b = 58 | singleton f |> ap a |> ap b 59 | 60 | let rec bind f x = 61 | let a = f x.Root 62 | 63 | { 64 | RoseTree.Root = a.Root 65 | Children = L.append a.Children (L.map (bind f) x.Children) 66 | } 67 | 68 | [] 69 | [] 70 | let rec dfsPre(x: _ RoseTree) = seq { 71 | yield x.Root 72 | yield! Seq.collect dfsPre x.Children 73 | } 74 | 75 | [] 76 | [] 77 | let rec dfsPost(x: _ RoseTree) = seq { 78 | yield! Seq.collect dfsPost x.Children 79 | yield x.Root 80 | } 81 | 82 | let rec unfold f seed = 83 | let root, bs = f seed 84 | create root (unfoldForest f bs) 85 | 86 | and unfoldForest f = 87 | L.map(unfold f) 88 | 89 | /// Behaves like a combination of map and fold; 90 | /// it applies a function to each element of a tree, 91 | /// passing an accumulating parameter, 92 | /// and returning a final value of this accumulator together with the new tree. 93 | let rec mapAccum f state tree = 94 | let nstate, root = f state tree.Root 95 | let nstate, children = LazyList.mapAccum (mapAccum f) nstate tree.Children 96 | nstate, create root children 97 | 98 | // TODO: 99 | // bfs: http://pdf.aminer.org/000/309/950/the_under_appreciated_unfold.pdf 100 | // sequence / mapM / filterM 101 | // zipper: http://hackage.haskell.org/package/rosezipper-0.2 102 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/TimeSeries.fs: -------------------------------------------------------------------------------- 1 | /// Originally from https://bitbucket.org/colinbul/fsharpent 2 | namespace FSharpx.Collections.Experimental 3 | 4 | open System 5 | open System.Collections 6 | open System.Collections.Generic 7 | open FSharpx.Collections 8 | 9 | type Timeseries<'T>(startDate: DateTimeOffset, granularity: TimeSpan, position: int, values: seq<'T>) = 10 | 11 | let buffer = RingBuffer<'T>(position, values) 12 | 13 | let mutable startDate = startDate 14 | 15 | let convertToGranularity (targetGran: TimeSpan) (currGran: TimeSpan) (values: seq<'T>) = 16 | if currGran = targetGran then 17 | values 18 | elif currGran > targetGran then 19 | let ratio = currGran.TotalMinutes / targetGran.TotalMinutes |> int 20 | values |> Seq.grow ratio 21 | else 22 | let ratio = targetGran.TotalMinutes / currGran.TotalMinutes |> int 23 | values |> Seq.contract ratio 24 | 25 | let convertToSeries (startDate: DateTimeOffset) (gran: TimeSpan) (seq: 'T[]) = 26 | Array.mapi (fun i x -> startDate.AddMinutes((i |> float) * gran.TotalMinutes), x) seq 27 | 28 | let offset (a: DateTimeOffset) (b: DateTimeOffset) = 29 | (b.Subtract(a).TotalMinutes / granularity.TotalMinutes) 30 | |> floor 31 | |> int 32 | 33 | new(startDate, granularity, values) = Timeseries(startDate, granularity, 0, values) 34 | 35 | new(startDate, granularity, size) = Timeseries(startDate, granularity, Array.zeroCreate size) 36 | 37 | static member Empty(startDate, granularity, size) = 38 | Timeseries(startDate, granularity, Array.zeroCreate size) 39 | 40 | member x.Buffer = buffer 41 | 42 | member x.StartDate 43 | with get () = startDate 44 | and set (value) = startDate <- value 45 | 46 | member x.Granularity = granularity 47 | 48 | member x.AsTimeseries(?startDate: DateTimeOffset) = 49 | let date = defaultArg startDate x.StartDate 50 | 51 | x.Buffer.ToArray() 52 | |> convertToSeries x.StartDate x.Granularity 53 | |> Array.filter(fun (d, _) -> d >= date) 54 | 55 | member x.ToGranularity(granularity: TimeSpan) = 56 | if granularity = x.Granularity then 57 | x 58 | else 59 | Timeseries(x.StartDate, granularity, x.Buffer.ToArray() |> convertToGranularity granularity x.Granularity) 60 | 61 | member x.Series = x.Buffer.ToArray() |> convertToSeries x.StartDate x.Granularity 62 | 63 | /// Tries to advance the start date of the Timeseries to toDate. 64 | /// Returns None if toDate is before the start date of the Timeseries, 65 | /// otherwise Some containing the start date of the Timeseries. 66 | member x.TryAdvance(toDate: DateTimeOffset) = 67 | let offsetIndex = offset x.StartDate toDate 68 | 69 | match x.Buffer.TryAdvance(offsetIndex) with 70 | | Some(_) -> 71 | if offsetIndex > 0 then 72 | x.Buffer.Normalize() 73 | x.StartDate <- toDate 74 | 75 | Some(x.StartDate) 76 | | None -> None 77 | 78 | /// Advances the start date of the Timeseries to toDate. Throws an 79 | /// ArgumentException if toDate is before the Timeseries start date. 80 | member x.Advance(toDate: DateTimeOffset) = 81 | match x.TryAdvance(toDate) with 82 | | Some(startDate) -> startDate 83 | | None -> invalidArg "toDate" "the toDate must be greater than or equal to the start date of the Timeseries" 84 | 85 | member x.Insert(op: ('T -> 'T -> 'T), startDate: DateTimeOffset, gran: TimeSpan, dataToInsert: seq<'T>) = 86 | let data = dataToInsert |> convertToGranularity x.Granularity gran |> Seq.toArray 87 | let offsetIndex = offset x.StartDate startDate 88 | 89 | if offsetIndex < 0 then 90 | if abs offsetIndex < data.Length then 91 | x.Buffer.Insert(op, 0, data.[abs offsetIndex ..]) 92 | else 93 | x.Buffer.Insert(op, offsetIndex, data) 94 | 95 | member x.Merge(f: ('T -> 'T -> 'T), ts: Timeseries<'T>) = 96 | x.Insert(f, ts.StartDate, ts.Granularity, ts.Buffer.ToArray()) 97 | 98 | member x.Clone() = 99 | Timeseries<'T>(x.StartDate, x.Granularity, x.Buffer.Position, x.Buffer.ToArray()) 100 | -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /src/FSharpx.Collections.Experimental/paket.template: -------------------------------------------------------------------------------- 1 | type file 2 | id FSharpx.Collections.Experimental 3 | owners 4 | Steffen Forkmann, Mauricio Scheffer, Jack Fox 5 | authors 6 | Steffen Forkmann, Daniel Mohl, Tomas Petricek, Ryan Riley,Mauricio Scheffer, Phil Trelford, Jack Fox 7 | projectUrl 8 | https://github.com/fsprojects/FSharpx.Collections 9 | iconUrl 10 | https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/master/docs/img/logo.png 11 | licenseUrl 12 | http://github.com/fsprojects/FSharpx.Collections/blob/master/LICENSE.txt 13 | requireLicenseAcceptance 14 | false 15 | repositoryType 16 | git 17 | repositoryUrl 18 | https://github.com/fsprojects/FSharpx.Collections 19 | language 20 | F# 21 | copyright 22 | Copyright 2013-2021 23 | tags 24 | F# fsharp fsharpx collections datastructures 25 | summary 26 | FSharpx.Collections is a collection of datastructures for use with F# and C#. 27 | description 28 | FSharpx.Collections is a collection of datastructures for use with F# and C#. 29 | dependencies 30 | framework: netstandard20 31 | FSharp.Core >= LOCKEDVERSION 32 | files 33 | ./bin/Release/netstandard2.0/FSharpx.Collections.Experimental.dll ==> lib/netstandard2.0 34 | ./bin/Release/netstandard2.0/FSharpx.Collections.Experimental.pdb ==> lib/netstandard2.0 35 | ./bin/Release/netstandard2.0/FSharpx.Collections.Experimental.xml ==> lib/netstandard2.0 -------------------------------------------------------------------------------- /src/FSharpx.Collections/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // Auto-Generated by FAKE; do not edit 2 | namespace System 3 | open System.Reflection 4 | open System.Runtime.CompilerServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | [] 13 | [] 14 | do () 15 | 16 | module internal AssemblyVersionInformation = 17 | let [] AssemblyTitle = "FSharpx.Collections" 18 | let [] AssemblyProduct = "FSharpx.Collections" 19 | let [] AssemblyDescription = "FSharpx.Collections is a collection of datastructures for use with F# and C#." 20 | let [] InternalsVisibleTo = "FSharpx.Collections.Tests" 21 | let [] InternalsVisibleTo_1 = "FSharpx.Collections.Experimental.Tests" 22 | let [] AssemblyVersion = "3.0.1" 23 | let [] AssemblyFileVersion = "3.0.1" 24 | let [] AssemblyConfiguration = "Release" 25 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/CircularBuffer.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Mutable 2 | 3 | open System 4 | open System.Collections 5 | open System.Collections.Generic 6 | 7 | // NOTE: A special version for primitives would increase 8 | // performance for primitive types, especially for I/O, 9 | // byte-based operations. 10 | type CircularBuffer<'T>(bufferSize: int) = 11 | do 12 | if bufferSize <= 0 then 13 | invalidArg "bufferSize" "The bufferSize must be greater than 0." 14 | 15 | let buffer = Array.zeroCreate<'T> bufferSize 16 | let mutable head = bufferSize - 1 17 | let mutable tail = 0 18 | let mutable length = 0 19 | 20 | let rec nextBuffer offset count = seq { 21 | let overflow = count + offset - bufferSize 22 | 23 | if overflow > 0 then 24 | yield (offset, bufferSize - offset) 25 | yield! nextBuffer 0 overflow 26 | else 27 | yield (offset, count) 28 | } 29 | 30 | member this.Count = length 31 | 32 | member this.Dequeue(count) = 33 | if length = 0 then 34 | invalidOp "Queue exhausted." 35 | 36 | if count > bufferSize then 37 | raise 38 | <| new ArgumentOutOfRangeException("Requested count exceeds the buffer size.") 39 | 40 | let count = min count length 41 | 42 | let dequeued = 43 | Array.concat [| for o, c in nextBuffer tail count -> buffer.[o .. o + c - 1] |] 44 | 45 | tail <- (tail + count) % bufferSize 46 | length <- length - count 47 | dequeued 48 | 49 | member this.Enqueue(value: _[], offset, count) = 50 | if count > bufferSize then 51 | invalidOp "Requested count is too large." 52 | 53 | let mutable offset = offset 54 | 55 | head <- (head + 1) % bufferSize 56 | 57 | for x, y in nextBuffer head count do 58 | Array.blit value offset buffer x y 59 | offset <- offset + y 60 | 61 | if length = bufferSize then 62 | tail <- (tail + count) % bufferSize 63 | else 64 | let overflow = length + count - bufferSize 65 | 66 | if overflow > 0 then 67 | tail <- (tail + overflow) % bufferSize 68 | 69 | length <- min (length + count) bufferSize 70 | 71 | member this.Enqueue(value: _[]) = 72 | this.Enqueue(value, 0, value.Length) 73 | 74 | member this.Enqueue(value: _[], offset) = 75 | this.Enqueue(value, offset, value.Length - offset) 76 | 77 | #if !FABLE_COMPILER 78 | member this.Enqueue(value: ArraySegment<_>) = 79 | this.Enqueue(value.Array, value.Offset, value.Count) 80 | #endif 81 | 82 | member this.Enqueue(value) = 83 | this.Enqueue([| value |], 0, 1) 84 | 85 | member this.GetEnumerator() = 86 | let rec loop() = seq { 87 | if length > 0 then 88 | yield this.Dequeue(1).[0] 89 | 90 | yield! loop() 91 | } 92 | 93 | loop().GetEnumerator() 94 | 95 | interface IEnumerable<'T> with 96 | member this.GetEnumerator() = 97 | (this :> IEnumerable<'T>).GetEnumerator() 98 | 99 | interface IEnumerable with 100 | member this.GetEnumerator() = 101 | (this :> IEnumerable).GetEnumerator() 102 | 103 | interface IReadOnlyCollection<'T> with 104 | member this.Count = this.Count 105 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/CircularBuffer.fsi: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Mutable 2 | 3 | open System 4 | 5 | /// needs doc 6 | type CircularBuffer<'T> = 7 | interface System.Collections.IEnumerable 8 | interface System.Collections.Generic.IEnumerable<'T> 9 | interface System.Collections.Generic.IReadOnlyCollection<'T> 10 | 11 | /// needs doc 12 | new : bufferSize:int -> CircularBuffer<'T> 13 | /// needs doc 14 | member Dequeue : count:int -> 'T [] 15 | /// needs doc 16 | member Enqueue : value:'T [] -> unit 17 | #if !FABLE_COMPILER 18 | /// needs doc 19 | member Enqueue : value:ArraySegment<'T> -> unit 20 | #endif 21 | /// needs doc 22 | member Enqueue : value:'T -> unit 23 | /// needs doc 24 | member Enqueue : value:'T [] * offset:int -> unit 25 | /// needs doc 26 | member Enqueue : value:'T [] * offset:int * count:int -> unit 27 | /// needs doc 28 | member GetEnumerator : unit -> System.Collections.Generic.IEnumerator<'T> 29 | /// needs doc 30 | member Count : int 31 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/DList.fsi: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections 2 | 3 | /// DList is an ordered linear structure implementing the List signature (head, tail, cons), 4 | /// end-insertion (conj), and O(1) append. Ordering is by insertion history. 5 | /// DList is an implementation of [John Hughes' append list](http://dl.acm.org/citation.cfm?id=8475). 6 | 7 | [] 8 | type DList<'T> = 9 | interface System.IEquatable> 10 | interface System.Collections.IEnumerable 11 | interface System.Collections.Generic.IEnumerable<'T> 12 | interface System.Collections.Generic.IReadOnlyCollection<'T> 13 | 14 | ///O(1). Returns the count of elememts. 15 | member Length : int 16 | 17 | ///O(1). Returns a new DList with the element added to the front. 18 | member Cons : 'T -> DList<'T> 19 | 20 | ///O(log n). Returns the first element. 21 | member Head : 'T 22 | 23 | ///O(log n). Returns option first element 24 | member TryHead : 'T option 25 | 26 | ///O(1). Returns true if the DList has no elements. 27 | member IsEmpty : bool 28 | 29 | ///O(1). Returns a new DList with the element added to the end. 30 | member Conj : 'T -> DList<'T> 31 | 32 | ///O(log n). Returns a new DList of the elements trailing the first element. 33 | member Tail : DList<'T> 34 | 35 | ///O(log n). Returns option DList of the elements trailing the first element. 36 | member TryTail : DList<'T> option 37 | 38 | ///O(log n). Returns the first element and tail. 39 | member Uncons : 'T * DList<'T> 40 | 41 | ///O(log n). Returns option first element and tail. 42 | member TryUncons : ('T * DList<'T>) option 43 | 44 | [] 45 | module DList = 46 | //pattern discriminators (active pattern) 47 | val (|Cons|Nil|) : DList<'T> -> Choice<('T * DList<'T>),unit> 48 | 49 | ///O(1). Returns a new DList of two lists. 50 | val append : DList<'T> -> DList<'T> -> DList<'T> 51 | 52 | ///O(1). Returns a new DList with the element added to the beginning. 53 | val cons : 'T -> DList<'T> -> DList<'T> 54 | 55 | ///O(1). Returns DList of no elements. 56 | [] 57 | val empty<'T> : DList<'T> 58 | 59 | ///O(n). Fold walks the DList using constant stack space. Implementation is from Norman Ramsey. 60 | /// See http://stackoverflow.com/questions/5324623/functional-o1-append-and-on-iteration-from-first-element-list-data-structure/5334068#5334068 61 | val foldBack : ('T -> 'State -> 'State) -> DList<'T> -> 'State -> 'State 62 | 63 | val fold : ('State -> 'T -> 'State) -> 'State -> DList<'T> -> 'State 64 | 65 | ///O(log n). Returns the first element. 66 | val inline head : DList<'T> -> 'T 67 | 68 | ///O(log n). Returns option first element. 69 | val inline tryHead : DList<'T> -> 'T option 70 | 71 | ///O(1). Returns true if the DList has no elements. 72 | val inline isEmpty : DList<'T> -> bool 73 | 74 | ///O(1). Returns the count of elememts. 75 | val inline length : DList<'T> -> int 76 | 77 | ///O(1). Returns DList of one elements. 78 | val singleton : 'T -> DList<'T> 79 | 80 | ///O(1). Returns a new DList with the element added to the end. 81 | val inline conj : 'T -> DList<'T> -> DList<'T> 82 | 83 | ///O(log n). Returns a new DList of the elements trailing the first element. 84 | val inline tail : DList<'T> -> DList<'T> 85 | 86 | ///O(log n). Returns option DList of the elements trailing the first element. 87 | val inline tryTail : DList<'T> -> DList<'T> option 88 | 89 | ///O(log n). Returns the first element and tail. 90 | val inline uncons : DList<'T> -> 'T * DList<'T> 91 | 92 | ///O(log n). Returns option first element and tail. 93 | val inline tryUncons : DList<'T> -> ('T * DList<'T>) option 94 | 95 | ///O(n). Returns a DList of the seq. 96 | val ofSeq : seq<'T> -> DList<'T> 97 | 98 | ///O(n). Returns a list of the DList elements. 99 | val inline toList : DList<'T> -> list<'T> 100 | 101 | ///O(n). Returns a seq of the DList elements. 102 | val inline toSeq : DList<'T> -> seq<'T> 103 | 104 | ///O(n). Returns a pairwise DList of elements. 105 | val pairwise : DList<'T> -> DList<'T*'T> 106 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Deque.fsi: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections 2 | 3 | /// Double-ended queue is an ordered linear linear structure implementing the signature of List 4 | /// (head, tail, cons) as well as the mirror-image Vector signature (last, initial, conj). "head" inspects 5 | /// the first or left-most element in the structure, while "last" inspects the last or 6 | /// right-most element. "rev" (reverse) has time complexity O(1). Ordering is by insertion history. 7 | [] 8 | type Deque<'T> = 9 | interface System.IEquatable> 10 | interface System.Collections.IEnumerable 11 | interface System.Collections.Generic.IEnumerable<'T> 12 | interface System.Collections.Generic.IReadOnlyCollection<'T> 13 | 14 | ///O(1). Returns a new deque with the element added to the end. 15 | member Conj : 'T -> Deque<'T> 16 | 17 | ///O(1). Returns a new deque with the element added to the beginning. 18 | member Cons : 'T -> Deque<'T> 19 | 20 | ///O(1) amortized, O(n), worst case. Returns the first element. 21 | member Head : 'T 22 | 23 | ///O(1) amortized, O(n), worst case. Returns option first element. 24 | member TryHead : 'T option 25 | 26 | ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. 27 | member Initial : Deque<'T> 28 | 29 | ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. 30 | member TryInitial : Deque<'T> option 31 | 32 | ///O(1). Returns true if the deque has no elements. 33 | member IsEmpty : bool 34 | 35 | ///O(1) amortized, O(n), worst case. Returns the last element. 36 | member Last : 'T 37 | 38 | ///O(1) amortized, O(n), worst case. Returns option last element. 39 | member TryLast : 'T option 40 | 41 | ///O(1). Returns the count of elememts. 42 | member Length : int 43 | 44 | ///O(1). Returns deque reversed. 45 | member Rev : Deque<'T> 46 | 47 | ///O(1) amortized, O(n), worst case. Returns a new deque of the elements trailing the first element. 48 | member Tail : Deque<'T> 49 | 50 | ///O(1) amortized, O(n), worst case. Returns option deque of the elements trailing the first element. 51 | member TryTail : Deque<'T> option 52 | 53 | ///O(1) amortized, O(n), worst case. Returns init and the last element. 54 | member Unconj : Deque<'T> * 'T 55 | 56 | ///O(1) amortized, O(n), worst case. Returns option init and the last element. 57 | member TryUnconj : (Deque<'T> * 'T) option 58 | 59 | ///O(1) amortized, O(n), worst case. Returns the first element and tail. 60 | member Uncons : 'T * Deque<'T> 61 | 62 | ///O(1) amortized, O(n), worst case. Returns option first element and tail. 63 | member TryUncons : ('T * Deque<'T>) option 64 | 65 | [] 66 | module Deque = 67 | 68 | //pattern discriminators 69 | val (|Cons|Nil|) : Deque<'T> -> Choice<('T * Deque<'T>),unit> 70 | 71 | val (|Conj|Nil|) : Deque<'T> -> Choice<(Deque<'T> * 'T),unit> 72 | 73 | ///O(1). Returns a new deque with the element added to the end. 74 | val inline conj : 'T -> Deque<'T> -> Deque<'T> 75 | 76 | ///O(1). Returns a new deque with the element added to the beginning. 77 | val inline cons : 'T -> Deque<'T> -> Deque<'T> 78 | 79 | ///O(1). Returns deque of no elements. 80 | [] 81 | val empty<'T> : Deque<'T> 82 | 83 | ///O(n). Applies a function to each element of the deque, threading an accumulator argument through the computation, left to right 84 | val fold : ('State -> 'T -> 'State) -> 'State -> Deque<'T> -> 'State 85 | 86 | ///O(n). Applies a function to each element of the deque, threading an accumulator argument through the computation, right to left 87 | val foldBack : ('T -> 'State -> 'State) -> Deque<'T> -> 'State -> 'State 88 | 89 | ///O(1) amortized, O(n), worst case. Returns the first element. 90 | val inline head : Deque<'T> -> 'T 91 | 92 | ///O(1) amortized, O(n), worst case. Returns option first element. 93 | val inline tryHead : Deque<'T> -> 'T option 94 | 95 | ///O(1) amortized, O(n), worst case. Returns a new deque of the elements before the last element. 96 | val inline initial : Deque<'T> -> Deque<'T> 97 | 98 | ///O(1) amortized, O(n), worst case. Returns option deque of the elements before the last element. 99 | val inline tryInitial : Deque<'T> -> Deque<'T> option 100 | 101 | ///O(1). Returns true if the deque has no elements. 102 | val inline isEmpty : Deque<'T> -> bool 103 | 104 | ///O(1) amortized, O(n), worst case. Returns the last element. 105 | val inline last : Deque<'T> -> 'T 106 | 107 | ///O(1) amortized, O(n), worst case. Returns option last element. 108 | val inline tryLast : Deque<'T> -> 'T option 109 | 110 | ///O(1). Returns the count of elememts. 111 | val inline length : Deque<'T> -> int 112 | 113 | ///O(n), worst case. Returns a deque of the two lists concatenated. 114 | val ofCatLists : 'T list -> 'T list -> Deque<'T> 115 | 116 | ///O(n), worst case. Returns a deque of the list. 117 | val ofList : 'T list -> Deque<'T> 118 | 119 | ///O(n), worst case. Returns a deque of the seq. 120 | val ofSeq : seq<'T> -> Deque<'T> 121 | 122 | ///O(1). Returns deque reversed. 123 | val inline rev : Deque<'T> -> Deque<'T> 124 | 125 | ///O(1). Returns a deque of one element. 126 | val singleton : 'T -> Deque<'T> 127 | 128 | ///O(1) amortized, O(n), worst case. Returns a new deque of the elements trailing the first element. 129 | val inline tail : Deque<'T> -> Deque<'T> 130 | 131 | ///O(1) amortized, O(n), worst case. Returns option deque of the elements trailing the first element. 132 | val inline tryTail : Deque<'T> -> Deque<'T> option 133 | 134 | ///O(1) amortized, O(n), worst case. Returns init and the last element. 135 | val inline unconj : Deque<'T> -> Deque<'T> * 'T 136 | 137 | ///O(1) amortized, O(n), worst case. Returns option init and the last element. 138 | val inline tryUnconj : Deque<'T> -> (Deque<'T> * 'T) option 139 | 140 | ///O(1) amortized, O(n), worst case. Returns the first element and tail. 141 | val inline uncons : Deque<'T> -> 'T * Deque<'T> 142 | 143 | ///O(n). Views the given deque as a sequence. 144 | val inline toSeq : Deque<'T> -> seq<'T> 145 | 146 | ///O(1) amortized, O(n), worst case. Returns option first element and tail. 147 | val inline tryUncons : Deque<'T> -> ('T * Deque<'T>) option 148 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Exceptions.fs: -------------------------------------------------------------------------------- 1 | // Copyright 2010-2013, as indicated in README.md in the root directory of this distribution. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License") 4 | 5 | namespace FSharpx.Collections 6 | 7 | module Exceptions = 8 | let Empty = new System.Exception("Queue is empty") // TODO: make this a better exception 9 | 10 | let OutOfBounds = new System.IndexOutOfRangeException() // TODO: make this a better exception 11 | 12 | let KeyNotFound key = 13 | new System.Collections.Generic.KeyNotFoundException("The key " + string key + " was not present in the collection") 14 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/FSharpx.Collections.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 8.0.30703 5 | Library 6 | FSharpx.Collections 7 | FSharpx.Collections 8 | FSharpx.Collections 9 | netstandard2.0 10 | 11 | true 12 | 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Infrastructure.fs: -------------------------------------------------------------------------------- 1 | module internal FSharpx.Collections.TimeMeasurement 2 | 3 | /// Stops the runtime for a given function 4 | let stopTime f = 5 | #if FABLE_COMPILER 6 | failwith "Not implement" 7 | #else 8 | let sw = new System.Diagnostics.Stopwatch() 9 | sw.Start() 10 | let result = f() 11 | sw.Stop() 12 | result, float sw.ElapsedMilliseconds 13 | #endif 14 | 15 | /// Stops the average runtime for a given function and applies it the given count 16 | let stopAverageTime count f = 17 | #if FABLE_COMPILER 18 | failwith "Not implement" 19 | #else 20 | System.GC.Collect() // force garbage collector before testing 21 | let sw = new System.Diagnostics.Stopwatch() 22 | sw.Start() 23 | 24 | for _ in 1..count do 25 | f() |> ignore 26 | 27 | sw.Stop() 28 | float sw.ElapsedMilliseconds / float count 29 | #endif 30 | 31 | let printInFsiTags s = 32 | printfn " [fsi:%s]" s 33 | 34 | /// Stops the average runtime for a given function and applies it the given count 35 | /// Afterwards it reports it with the given description 36 | let averageTime count desc f = 37 | let time = stopAverageTime count f 38 | sprintf "%s %Ams" desc time |> printInFsiTags 39 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Literals.fs: -------------------------------------------------------------------------------- 1 | module FSharpx.Collections.Literals 2 | 3 | [] 4 | let internal blockSizeShift = 5 // TODO: what can we do in 64Bit case? 5 | 6 | [] 7 | let internal blockSize = 32 8 | 9 | [] 10 | let internal blockIndexMask = 0x01f 11 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/NonEmptyList.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections 2 | 3 | open System 4 | open System.Collections 5 | open System.Collections.Generic 6 | open System.Runtime.CompilerServices 7 | 8 | type NonEmptyList<'T> = 9 | private 10 | { 11 | List: 'T list 12 | } 13 | 14 | member this.Head = this.List.Head 15 | member this.Tail = this.List.Tail 16 | member this.Length = this.List.Length 17 | 18 | interface IEnumerable<'T> with 19 | member this.GetEnumerator() = 20 | (this.List :> _ seq).GetEnumerator() 21 | 22 | interface IEnumerable with 23 | member this.GetEnumerator() = 24 | (this.List :> _ seq).GetEnumerator() :> IEnumerator 25 | 26 | interface IReadOnlyCollection<'T> with 27 | member this.Count = this.Length 28 | 29 | [] 30 | [] 31 | module NonEmptyList = 32 | [] 33 | let create head tail = { List = head :: tail } 34 | 35 | #if !FABLE_COMPILER 36 | [] 37 | let createParamsArray(head, [] tail) = { List = head :: List.ofArray tail } 38 | #endif 39 | 40 | [] 41 | let inline singleton value = 42 | create value [] 43 | 44 | [] 45 | let inline head(x: NonEmptyList<_>) = x.Head 46 | 47 | [] 48 | let inline tail(x: NonEmptyList<_>) = x.Tail 49 | 50 | [] 51 | [] 52 | let toList(x: NonEmptyList<_>) = x.List 53 | 54 | [] 55 | let inline length(x: NonEmptyList<_>) = x.Length 56 | 57 | [] 58 | [] 59 | let toArray list = 60 | Array.ofList list.List 61 | 62 | [] 63 | [] 64 | let inline toSeq(list: NonEmptyList<_>) = 65 | list :> _ seq 66 | 67 | [] 68 | let ofArray(arr: _ array) = 69 | match arr.Length with 70 | | 0 -> invalidArg "arr" "Array is empty" 71 | | _ -> { List = List.ofArray arr } 72 | 73 | [] 74 | let ofList(l: _ list) = 75 | match l with 76 | | head :: tail -> create head tail 77 | | _ -> invalidArg "l" "List is empty" 78 | 79 | [] 80 | let ofSeq(e: _ seq) = 81 | if Seq.isEmpty e then 82 | invalidArg "e" "Sequence is empty" 83 | else 84 | { List = List.ofSeq e } 85 | 86 | [] 87 | let map f list = { List = List.map f list.List } 88 | 89 | [] 90 | let cons head tail = { List = head :: tail.List } 91 | 92 | #if !FABLE_COMPILER 93 | [] 94 | #endif 95 | let appendList list1 list2 = { List = list1.List @ list2 } 96 | 97 | [] 98 | let append list1 list2 = { List = list1.List @ list2.List } 99 | 100 | [] 101 | let reduce reduction list = 102 | List.reduce reduction list.List 103 | 104 | [] 105 | let last list = 106 | List.last list.List 107 | 108 | [] 109 | [] 110 | let rev list = { List = List.rev list.List } 111 | 112 | [] 113 | let collect (mapping: 'a -> NonEmptyList<'b>) (list: NonEmptyList<'a>) = 114 | list.List |> List.collect(fun x -> (mapping x).List) |> ofList 115 | 116 | [] 117 | let zip list1 list2 = { 118 | List = List.zip list1.List list2.List 119 | } 120 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Queue.fs: -------------------------------------------------------------------------------- 1 | // XML docs in fsi 2 | 3 | namespace FSharpx.Collections 4 | 5 | type Queue<'T>(front: list<'T>, rBack: list<'T>) = 6 | let mutable hashCode = None 7 | member internal this.front = front 8 | member internal this.rBack = rBack 9 | 10 | override this.GetHashCode() = 11 | match hashCode with 12 | | None -> 13 | let mutable hash = 1 14 | 15 | for x in this do 16 | hash <- 31 * hash + Unchecked.hash x 17 | 18 | hashCode <- Some hash 19 | hash 20 | | Some hash -> hash 21 | 22 | override this.Equals(other) = 23 | match other with 24 | | :? Queue<'T> as y -> (this :> System.IEquatable>).Equals y 25 | | _ -> false 26 | 27 | member this.Conj x = 28 | match front, x :: rBack with 29 | | [], r -> Queue((List.rev r), []) 30 | | f, r -> Queue(f, r) 31 | 32 | member this.Head = 33 | match front with 34 | | hd :: _ -> hd 35 | | _ -> raise(new System.Exception("Queue is empty")) 36 | 37 | member this.TryHead = 38 | match front with 39 | | hd :: _ -> Some(hd) 40 | | _ -> None 41 | 42 | member this.IsEmpty = front.IsEmpty 43 | 44 | member this.Length = front.Length + rBack.Length 45 | 46 | member this.Rev() = 47 | match rBack, front with 48 | | [], r -> Queue((List.rev r), []) 49 | | f, r -> Queue(f, r) 50 | 51 | member this.Tail = 52 | match front with 53 | | hd :: tl -> 54 | match tl, rBack with 55 | | [], r -> Queue((List.rev r), []) 56 | | f, r -> Queue(f, r) 57 | | _ -> raise(new System.Exception("Queue is empty")) 58 | 59 | member this.TryTail = 60 | match front with 61 | | hd :: tl -> 62 | match tl, rBack with 63 | | [], r -> Some(Queue((List.rev r), [])) 64 | | f, r -> Some(Queue(f, r)) 65 | | _ -> None 66 | 67 | member this.Uncons = 68 | match front with 69 | | hd :: tl -> 70 | hd, 71 | (match tl, rBack with 72 | | [], r -> Queue((List.rev r), []) 73 | | f, r -> Queue(f, r)) 74 | | _ -> raise(new System.Exception("Queue is empty")) 75 | 76 | member this.TryUncons = 77 | match front with 78 | | hd :: tl -> 79 | match tl, rBack with 80 | | [], r -> Some(hd, Queue((List.rev r), [])) 81 | | f, r -> Some(hd, Queue(f, r)) 82 | | _ -> None 83 | 84 | interface System.IEquatable> with 85 | member this.Equals(y: Queue<'T>) = 86 | if this.Length <> y.Length then false 87 | else if this.GetHashCode() <> y.GetHashCode() then false 88 | else Seq.forall2 (Unchecked.equals) this y 89 | 90 | interface System.Collections.Generic.IEnumerable<'T> with 91 | override this.GetEnumerator() : System.Collections.Generic.IEnumerator<'T> = 92 | let e = seq { 93 | yield! front 94 | yield! (List.rev rBack) 95 | } 96 | 97 | e.GetEnumerator() 98 | 99 | interface System.Collections.IEnumerable with 100 | override this.GetEnumerator() = 101 | (this :> System.Collections.Generic.IEnumerable<'T>).GetEnumerator() :> System.Collections.IEnumerator 102 | 103 | interface System.Collections.Generic.IReadOnlyCollection<'T> with 104 | member this.Count = this.Length 105 | 106 | [] 107 | module Queue = 108 | //pattern discriminators (active pattern) 109 | let (|Cons|Nil|)(q: Queue<'T>) = 110 | match q.TryUncons with 111 | | Some(a, b) -> Cons(a, b) 112 | | None -> Nil 113 | 114 | let inline conj (x: 'T) (q: Queue<'T>) = 115 | (q.Conj x) 116 | 117 | let empty<'T> : Queue<'T> = Queue<_>([], []) 118 | 119 | let fold (f: ('State -> 'T -> 'State)) (state: 'State) (q: Queue<'T>) = 120 | let s = List.fold f state q.front 121 | List.fold f s (List.rev q.rBack) 122 | 123 | let foldBack (f: ('T -> 'State -> 'State)) (q: Queue<'T>) (state: 'State) = 124 | let s = List.foldBack f (List.rev q.rBack) state 125 | (List.foldBack f q.front s) 126 | 127 | let inline head(q: Queue<'T>) = q.Head 128 | 129 | let inline tryHead(q: Queue<'T>) = q.TryHead 130 | 131 | let inline isEmpty(q: Queue<'T>) = q.IsEmpty 132 | 133 | let inline length(q: Queue<'T>) = q.Length 134 | 135 | let ofList xs = 136 | Queue<'T>(xs, []) 137 | 138 | let ofSeq xs = 139 | Queue<'T>((List.ofSeq xs), []) 140 | 141 | let inline rev(q: Queue<'T>) = q.Rev() 142 | 143 | let inline tail(q: Queue<'T>) = q.Tail 144 | 145 | let inline tryTail(q: Queue<'T>) = q.TryTail 146 | 147 | let inline toSeq(q: Queue<'T>) = 148 | q :> seq<'T> 149 | 150 | let inline uncons(q: Queue<'T>) = q.Uncons 151 | 152 | let inline tryUncons(q: Queue<'T>) = 153 | q.TryUncons 154 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/Queue.fsi: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections 2 | 3 | /// Queue is an ordered linear data structure where elements are added at the end (right) 4 | /// and inspected and removed at the beginning (left). Ordering is by insertion history. 5 | /// The qualities of the Queue structure make elements first in, first out (fifo). 6 | /// "head" inspects the first or left-most element in the structure, while "conj" 7 | /// inserts an element at the end, or right of the structure. 8 | /// Purely functional (immutable) Queue based on Okasaki's batched queue. 9 | [] 10 | type Queue<'T> = 11 | interface System.IEquatable> 12 | interface System.Collections.IEnumerable 13 | interface System.Collections.Generic.IEnumerable<'T> 14 | interface System.Collections.Generic.IReadOnlyCollection<'T> 15 | 16 | ///O(1). Returns a new queue with the element added to the end. (Enqueue) 17 | member Conj : 'T -> Queue<'T> 18 | 19 | ///O(1). Returns the first element. (Peek) 20 | member Head : 'T 21 | 22 | ///O(1). Returns option first element 23 | member TryHead : 'T option 24 | 25 | ///O(1). Returns true if the queue has no elements. 26 | member IsEmpty : bool 27 | 28 | ///O(1). Returns the count of elememts. 29 | member Length : int 30 | 31 | ///O(n). Returns queue reversed. 32 | member Rev : unit -> Queue<'T> 33 | 34 | ///O(1) amortized, O(n) worst-case. Returns a new queue of the elements trailing the first element. (Dequeue) 35 | member Tail : Queue<'T> 36 | 37 | ///O(1) amortized, O(n) worst-case. Returns option queue of the elements trailing the first element. 38 | member TryTail : Queue<'T> option 39 | 40 | ///O(1) amortized, O(n) worst-case. Returns the first element and tail. 41 | member Uncons : 'T * Queue<'T> 42 | 43 | ///O(1) amortized, O(n) worst-case. Returns option first element and tail. 44 | member TryUncons : ('T * Queue<'T>) option 45 | 46 | [] 47 | module Queue = 48 | //pattern discriminators (active pattern) 49 | val (|Cons|Nil|) : Queue<'T> -> Choice<('T * Queue<'T>),unit> 50 | 51 | ///O(1). Returns a new queue with the element added to the end. (enqueue) 52 | val inline conj : 'T -> Queue<'T> -> Queue<'T> 53 | 54 | ///O(1). Returns queue of no elements. 55 | [] 56 | val empty<'T> : Queue<'T> 57 | 58 | ///O(n). Applies a function to each element of the queue, threading an accumulator argument through the computation, left to right. 59 | val fold : ('State -> 'T -> 'State) -> 'State -> Queue<'T> -> 'State 60 | 61 | ///O(n). Applies a function to each element of the queue, threading an accumulator argument through the computation, right to left. 62 | val foldBack : ('T -> 'State -> 'State) -> Queue<'T> -> 'State -> 'State 63 | 64 | ///O(1). Returns the first element. (peek) 65 | val inline head : Queue<'T> -> 'T 66 | 67 | ///O(1). Returns option first element. 68 | val inline tryHead : Queue<'T> -> 'T option 69 | 70 | ///O(1). Returns true if the queue has no elements. 71 | val inline isEmpty : Queue<'T> -> bool 72 | 73 | ///O(1). Returns the count of elememts. 74 | val inline length : Queue<'T> -> int 75 | 76 | ///O(1). Returns a queue of the list 77 | val ofList : list<'T> -> Queue<'T> 78 | 79 | ///O(n). Returns a queue of the seq. 80 | val ofSeq : seq<'T> -> Queue<'T> 81 | 82 | ///O(n). Returns queue reversed. 83 | val inline rev : Queue<'T> -> Queue<'T> 84 | 85 | ///O(1) amortized, O(n) worst-case. Returns a new queue of the elements trailing the first element. (dequeue) 86 | val inline tail : Queue<'T> -> Queue<'T> 87 | 88 | ///O(1) amortized, O(n) worst-case. Returns option queue of the elements trailing the first element 89 | val inline tryTail : Queue<'T> -> Queue<'T> option 90 | 91 | ///O(n). Views the given queue as a sequence. 92 | val inline toSeq : Queue<'T> -> seq<'T> 93 | 94 | ///O(1) amortized, O(n) worst-case. Returns the first element and tail. 95 | val inline uncons : Queue<'T> -> 'T * Queue<'T> 96 | 97 | ///O(1) amortized, O(n) worst-case. Returns option first element and tail. 98 | val inline tryUncons : Queue<'T> -> ('T * Queue<'T>) option 99 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /src/FSharpx.Collections/paket.template: -------------------------------------------------------------------------------- 1 | type file 2 | id FSharpx.Collections 3 | owners 4 | Steffen Forkmann, Mauricio Scheffer, Jack Fox 5 | authors 6 | Steffen Forkmann, Daniel Mohl, Tomas Petricek, Ryan Riley,Mauricio Scheffer, Phil Trelford, Jack Fox 7 | projectUrl 8 | https://github.com/fsprojects/FSharpx.Collections 9 | iconUrl 10 | https://raw.githubusercontent.com/fsprojects/FSharpx.Collections/master/docs/img/logo.png 11 | licenseUrl 12 | http://github.com/fsprojects/FSharpx.Collections/blob/master/LICENSE.txt 13 | requireLicenseAcceptance 14 | false 15 | repositoryType 16 | git 17 | repositoryUrl 18 | https://github.com/fsprojects/FSharpx.Collections 19 | language 20 | F# 21 | copyright 22 | Copyright 2013-2021 23 | tags 24 | F# fsharp fsharpx collections datastructures 25 | summary 26 | FSharpx.Collections is a collection of datastructures for use with F# and C#. 27 | description 28 | FSharpx.Collections is a collection of datastructures for use with F# and C#. 29 | dependencies 30 | framework: netstandard20 31 | FSharp.Core >= LOCKEDVERSION 32 | files 33 | ./bin/Release/netstandard2.0/FSharpx.Collections.dll ==> lib/netstandard2.0 34 | ./bin/Release/netstandard2.0/FSharpx.Collections.pdb ==> lib/netstandard2.0 35 | ./bin/Release/netstandard2.0/FSharpx.Collections.xml ==> lib/netstandard2.0 36 | *.fs ==> fable/ 37 | *.fsi ==> fable/ 38 | *.fsproj ==> fable/ 39 | 40 | -------------------------------------------------------------------------------- /src/FSharpx.Collections/readme.md: -------------------------------------------------------------------------------- 1 | # FSharpx.Collections 2 | 3 | ## FSharpx.Collections Namespace is Purely Functional 4 | 5 | This namespace is for Purely Functional (immutable) Data Structures. Mutable structures belong in FSharpx.Collections.Mutable. FSharpx.Collections and FSharpx.Collections.Mutable auto-open in client projects opening FSharpx. 6 | 7 | ## Structure Type and Module 8 | 9 | The structure’s type should exist directly in the namespace, as well as a module with the same name as the type, but not the type under the module. The type should have all the members that operate on an instantiated type. The module should have let bindings to all the the type members as well as let bindings for all other "utility" functions on the type, like ofSeq and empty. 10 | 11 | ## Understand the difference between a property and a method 12 | 13 | [Property Usage Guidelines](http://msdn.microsoft.com/en-us/library/bzwdh01d.aspx) Properties are usually O(1), sometimes O(log n), never O(n). 14 | 15 | ## Pattern Discriminator 16 | 17 | Include any relevant pattern discriminators in the module. 18 | 19 | ## Try to Avoid Exceptions 20 | 21 | If a structure’s method can throw an exception, include a TryMethodName method that returns option. 22 | 23 | ## Necessary Members 24 | 25 | Every data structure should have a length or count member (even if length is O(n)) and an IsEmpty member in the type and an ofSeq let binding in the module. 26 | 27 | ## Additional Members 28 | 29 | Of course implement all the members central to the structure, but there can be some nice O(1) or O(log n) values just waiting to be implemented that perhaps were not directly addressed in the research paper because they were not interesting. Examples: the internal structure of the type allows O(1) rev or ofList. 30 | 31 | ## Necessary Interface 32 | 33 | Implement IEnumerable in the type. It should sequence elements in the inherent order of the data structure. For instance if a structure implements the head/tail paradigm, IEnumerable should sequence elements in the order of unconsing the structure down to empty. Most every structure has some logical, if not physical, order to its elements when flattened. 34 | 35 | Implementing ofSeq and IEnumerable makes practically every Data Structure composable into every other. 36 | 37 | ## Hide unsafe underlying structures 38 | 39 | Any publicly accessible interface to the type or module should withstand any conceivable test case without throwing exceptions, or have a corresponding try -> option interface. 40 | 41 | ## XML docs / Intellisense 42 | 43 | Every public member and let binding should have XML documentation to make it user friendly and not require a literature search or inspection of the code to determine functionality. At the type level there should be intellisense documenting the purpose and use of the structure. Include URLs to additional literature where appropriate. 44 | 45 | Data structures should give developers useful tools, not research projects. 46 | 47 | ## Time Complexity 48 | 49 | Adopting a practice from Haskell documentation, put the time complexity for members and let bindings at the beginning of their XML documentation. It is OK to put O(1) on empty and singleton let bindings to accustom users to seeing the time complexity in the intellisense, even though strictly speaking time complexity for these bindings has no meaning. 50 | 51 | ## Take time to evaluate the naming standards of types and members 52 | 53 | While it’s a good practice to keep the name a structure is best known as, don’t just unthinkingly adopt method and property names from a research paper or the implementation in another language. Consider renaming properties and methods if it makes sense to fit in with F# and .NET paradigms. Use ofList and ofArray not fromList or fromArray. Use the FSharp.Collections and FSharpx.Collections namepaces as models Example: the Heap members findMin and deleteMin renamed to head and tail because that conveys the head/tail paradigm like several other data structures (and Heap is implemented as ascending or descending, so “min” is inappropriate). 54 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/BinaryRoseTreeTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module BinaryRoseTreeTest = 8 | 9 | let atree = 10 | BinaryRoseTree.createTree 11 | 1 12 | (BinaryRoseTree.createTree 13 | 2 14 | (BinaryRoseTree.createForest 15 | 3 16 | BinaryRoseTree.empty 17 | (BinaryRoseTree.createTree 4 (BinaryRoseTree.createTree 5 (BinaryRoseTree.singleton 6))))) 18 | 19 | let expected = 20 | BinaryRoseTree.createTree 21 | 2 22 | (BinaryRoseTree.createTree 23 | 3 24 | (BinaryRoseTree.createForest 25 | 4 26 | BinaryRoseTree.empty 27 | (BinaryRoseTree.createTree 5 (BinaryRoseTree.createTree 6 (BinaryRoseTree.singleton 7))))) 28 | 29 | let ctree = 30 | BinaryRoseTree.createTree 31 | "f" 32 | (BinaryRoseTree.createForest 33 | "b" 34 | (BinaryRoseTree.createForest 35 | "a" 36 | BinaryRoseTree.empty 37 | (BinaryRoseTree.createTree "d" (BinaryRoseTree.createForest "c" BinaryRoseTree.empty (BinaryRoseTree.singleton "e")))) 38 | 39 | (BinaryRoseTree.createTree "g" (BinaryRoseTree.createTree "i" (BinaryRoseTree.singleton "h")))) 40 | 41 | [] 42 | let testBinaryRoseTree = 43 | 44 | testList "Experimental BinaryRoseTree" [ 45 | test "preOrder works" { 46 | let actual = BinaryRoseTree.preOrder ctree |> Seq.toList 47 | Expect.equal "" [ "f"; "b"; "a"; "d"; "c"; "e"; "g"; "i"; "h" ] actual 48 | } 49 | 50 | test "postOrder works" { 51 | let actual = BinaryRoseTree.postOrder ctree |> Seq.toList 52 | Expect.equal "" [ "a"; "c"; "e"; "d"; "b"; "h"; "i"; "g"; "f" ] actual 53 | } 54 | 55 | test "map" { 56 | let actual = BinaryRoseTree.map ((+) 1) atree 57 | Expect.equal "" expected actual 58 | } 59 | 60 | test "fold via preOrder" { 61 | let actual = BinaryRoseTree.preOrder atree |> Seq.fold (*) 1 62 | Expect.equal "" 720 actual 63 | } 64 | 65 | test "functor laws" { 66 | let iRT = BinaryRoseTree.createTree 1 (BinaryRoseTree.createForest 2 atree expected) 67 | let singleRT = BinaryRoseTree.singleton 1 68 | 69 | //fsCheck version of functor and monad laws stackoverflows 70 | let map = BinaryRoseTree.map 71 | 72 | //preserves identity 73 | ((map id iRT) = iRT) |> Expect.isTrue "" 74 | ((map id singleRT) = singleRT) |> Expect.isTrue "" 75 | 76 | let f = (fun x -> x + 5) 77 | let g = (fun x -> x - 2) 78 | 79 | //preserves composition 80 | map (f << g) iRT = (map f << map g) iRT |> Expect.isTrue "" 81 | map (f << g) singleRT = (map f << map g) singleRT |> Expect.isTrue "" 82 | } 83 | ] 84 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/BinaryTreeZipperTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module BinaryTreeZipperTest = 8 | let tree = 9 | Branch("a", Branch("b", BinaryTree.Leaf, Branch("c", BinaryTree.Leaf, BinaryTree.Leaf)), Branch("d", BinaryTree.Leaf, BinaryTree.Leaf)) 10 | 11 | [] 12 | let testBinaryTreeZipper = 13 | 14 | testList "Experimental BinaryTreeZipper" [ 15 | 16 | test "Can create BinaryTreeZipper.zipper from tree" { 17 | let z1 = tree |> BinaryTreeZipper.zipper 18 | Expect.equal "" tree z1.Focus 19 | } 20 | 21 | test "Can BinaryTreeZipper.move down to the BinaryTreeZipper.left inside the BinaryTreeZipper.zipper" { 22 | let z1 = tree |> BinaryTreeZipper.zipper |> BinaryTreeZipper.left 23 | 24 | Expect.equal "" (Branch("b", BinaryTree.Leaf, Branch("c", BinaryTree.Leaf, BinaryTree.Leaf))) z1.Focus 25 | } 26 | 27 | test "Can BinaryTreeZipper.move down to the BinaryTreeZipper.right inside the BinaryTreeZipper.zipper" { 28 | let z1 = tree |> BinaryTreeZipper.zipper |> BinaryTreeZipper.right 29 | Expect.equal "" (Branch("d", BinaryTree.Leaf, BinaryTree.Leaf)) z1.Focus 30 | } 31 | 32 | test "Can BinaryTreeZipper.move down to the BinaryTreeZipper.left and the BinaryTreeZipper.right inside the BinaryTreeZipper.zipper" { 33 | let z1 = 34 | tree 35 | |> BinaryTreeZipper.zipper 36 | |> BinaryTreeZipper.move [ 37 | BinaryTreeZipper.TreeZipperDirection.Left 38 | BinaryTreeZipper.TreeZipperDirection.Right 39 | ] 40 | 41 | Expect.equal "" (Branch("c", BinaryTree.Leaf, BinaryTree.Leaf)) z1.Focus 42 | } 43 | 44 | test "Can BinaryTreeZipper.move up inside the BinaryTreeZipper.zipper" { 45 | let z1 = 46 | tree 47 | |> BinaryTreeZipper.zipper 48 | |> BinaryTreeZipper.move [ 49 | BinaryTreeZipper.TreeZipperDirection.Left 50 | BinaryTreeZipper.TreeZipperDirection.Right 51 | BinaryTreeZipper.TreeZipperDirection.Right 52 | BinaryTreeZipper.Up 53 | BinaryTreeZipper.Up 54 | BinaryTreeZipper.Up 55 | ] 56 | 57 | Expect.equal "" tree z1.Focus 58 | } 59 | 60 | test "Can BinaryTreeZipper.move to the top from inside the BinaryTreeZipper.zipper" { 61 | let z1 = 62 | tree 63 | |> BinaryTreeZipper.zipper 64 | |> BinaryTreeZipper.move [ 65 | BinaryTreeZipper.TreeZipperDirection.Left 66 | BinaryTreeZipper.TreeZipperDirection.Right 67 | BinaryTreeZipper.TreeZipperDirection.Right 68 | ] 69 | |> BinaryTreeZipper.top 70 | 71 | Expect.equal "" tree z1.Focus 72 | } 73 | 74 | test "Can modify inside the BinaryTreeZipper.zipper" { 75 | let z1 = 76 | tree 77 | |> BinaryTreeZipper.zipper 78 | |> BinaryTreeZipper.right 79 | |> BinaryTreeZipper.setFocus(BinaryTreeZipper.branch "e") 80 | |> BinaryTreeZipper.top 81 | 82 | Expect.equal 83 | "" 84 | (Branch( 85 | "a", 86 | Branch("b", BinaryTree.Leaf, Branch("c", BinaryTree.Leaf, BinaryTree.Leaf)), 87 | Branch("e", BinaryTree.Leaf, BinaryTree.Leaf) 88 | )) 89 | z1.Focus 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/BootstrappedQueueTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections 4 | open FSharpx.Collections.Experimental 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module BootstrappedQueueTest = 9 | 10 | [] 11 | let testBootstrappedQueue = 12 | 13 | testList "Experimental BootstrappedQueue" [ 14 | test "BootstrappedQueue.empty queue should be BootstrappedQueue.empty" { 15 | BootstrappedQueue.isEmpty BootstrappedQueue.empty |> Expect.isTrue "" 16 | } 17 | 18 | test "it should allow to enqueue" { 19 | BootstrappedQueue.empty 20 | |> BootstrappedQueue.snoc 1 21 | |> BootstrappedQueue.snoc 2 22 | |> BootstrappedQueue.isEmpty 23 | |> Expect.isFalse "" 24 | } 25 | 26 | test "it should allow to dequeue" { 27 | BootstrappedQueue.empty 28 | |> BootstrappedQueue.snoc 1 29 | |> BootstrappedQueue.tail 30 | |> BootstrappedQueue.isEmpty 31 | |> Expect.isTrue "" 32 | } 33 | 34 | test "it should fail if there is no BootstrappedQueue.head in the queue" { 35 | let ok = ref false 36 | 37 | try 38 | BootstrappedQueue.empty |> BootstrappedQueue.head |> ignore 39 | with x when x = Exceptions.Empty -> 40 | ok := true 41 | 42 | !ok |> Expect.isTrue "" 43 | } 44 | 45 | test "it should give None if there is no BootstrappedQueue.head in the queue" { 46 | BootstrappedQueue.empty 47 | |> BootstrappedQueue.tryGetHead 48 | |> Expect.isNone "" 49 | } 50 | 51 | test "it should fail if there is no BootstrappedQueue.tail the queue" { 52 | let ok = ref false 53 | 54 | try 55 | BootstrappedQueue.empty |> BootstrappedQueue.tail |> ignore 56 | with x when x = Exceptions.Empty -> 57 | ok := true 58 | 59 | !ok |> Expect.isTrue "" 60 | } 61 | 62 | test "it should give None if there is no BootstrappedQueue.tail in the queue" { 63 | BootstrappedQueue.empty 64 | |> BootstrappedQueue.tryGetTail 65 | |> Expect.isNone "" 66 | } 67 | 68 | test "it should allow to get the BootstrappedQueue.head from a queue" { 69 | BootstrappedQueue.empty 70 | |> BootstrappedQueue.snoc 1 71 | |> BootstrappedQueue.snoc 2 72 | |> BootstrappedQueue.head 73 | |> Expect.equal "" 1 74 | } 75 | 76 | test "it should allow to get the BootstrappedQueue.head from a queue safely" { 77 | BootstrappedQueue.empty 78 | |> BootstrappedQueue.snoc 1 79 | |> BootstrappedQueue.snoc 2 80 | |> BootstrappedQueue.tryGetHead 81 | |> Expect.equal "" (Some 1) 82 | } 83 | 84 | test "it should allow to get the BootstrappedQueue.tail from the queue" { 85 | BootstrappedQueue.empty 86 | |> BootstrappedQueue.snoc "a" 87 | |> BootstrappedQueue.snoc "b" 88 | |> BootstrappedQueue.snoc "c" 89 | |> BootstrappedQueue.tail 90 | |> BootstrappedQueue.head 91 | |> Expect.equal "" "b" 92 | } 93 | 94 | test "it should allow to get the BootstrappedQueue.tail from a queue safely" { 95 | let value = 96 | BootstrappedQueue.empty 97 | |> BootstrappedQueue.snoc 1 98 | |> BootstrappedQueue.snoc 2 99 | |> BootstrappedQueue.tryGetTail 100 | 101 | value.Value |> BootstrappedQueue.head |> Expect.equal "" 2 102 | } 103 | 104 | test "it should initialize from a list" { 105 | BootstrappedQueue.ofList [ 1..10 ] 106 | |> BootstrappedQueue.snoc 11 107 | |> BootstrappedQueue.snoc 12 108 | |> BootstrappedQueue.tail 109 | |> BootstrappedQueue.length 110 | |> Expect.equal "" 11 111 | } 112 | ] 113 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/BottomUpMergeSortTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module BottomUpMergeSortTest = 8 | 9 | [] 10 | let testBottomUpMergeSort = 11 | 12 | testList "Experimental BottomUpMergeSort" [ 13 | test "empty list should be empty" { 14 | BottomUpMergeSort.empty 15 | |> BottomUpMergeSort.isEmpty 16 | |> Expect.isTrue "" 17 | } 18 | 19 | test "empty list should be empty after sort" { BottomUpMergeSort.sort BottomUpMergeSort.empty |> Expect.equal "" [] } 20 | 21 | test "singleton list should be the same after sort" { 22 | BottomUpMergeSort.sort(BottomUpMergeSort.singleton 1) 23 | |> Expect.equal "" [ 1 ] 24 | } 25 | 26 | test "adding a element to an empty list" { 27 | BottomUpMergeSort.empty 28 | |> BottomUpMergeSort.add 1 29 | |> BottomUpMergeSort.sort 30 | |> Expect.equal "" [ 1 ] 31 | } 32 | 33 | test "adding multiple elements to an empty list" { 34 | BottomUpMergeSort.empty 35 | |> BottomUpMergeSort.add 100 36 | |> BottomUpMergeSort.add 1 37 | |> BottomUpMergeSort.add 3 38 | |> BottomUpMergeSort.add 42 39 | |> BottomUpMergeSort.sort 40 | |> Expect.equal "" [ 1; 3; 42; 100 ] 41 | } 42 | 43 | test "adding multiple strings to an empty list" { 44 | BottomUpMergeSort.empty 45 | |> BottomUpMergeSort.add "100" 46 | |> BottomUpMergeSort.add "1" 47 | |> BottomUpMergeSort.add "3" 48 | |> BottomUpMergeSort.add "42" 49 | |> BottomUpMergeSort.sort 50 | |> Expect.equal "" [ "1"; "100"; "3"; "42" ] 51 | } 52 | ] 53 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/DListTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module DListTest = 8 | 9 | [] 10 | let testDList = 11 | 12 | let expected = 13 | Join(Unit 0, Join(Unit 1, Join(Unit 2, Join(Unit 3, Unit 4, 2), 3), 4), 5) 14 | 15 | let expected2 = Join(Unit 1, Join(Unit 2, Join(Unit 3, Unit 4, 2), 3), 4) 16 | 17 | testList "Experimental DList" [ 18 | test "test should verify DList.empty is Nil" { DList.empty<_> |> Expect.equal "" DList.Nil } 19 | 20 | test "test DList.length should return 5" { DList.length expected |> Expect.equal "" 5 } 21 | 22 | ptest "test ofSeq should create a DList from a seq" { 23 | let test = seq { for i in 0..4 -> i } 24 | DList.ofSeq test |> Expect.equal "" expected 25 | } 26 | 27 | ptest "test ofSeq should create a DList from a list" { 28 | let test = [ for i in 0..4 -> i ] 29 | DList.ofSeq test |> Expect.equal "" expected 30 | } 31 | 32 | ptest "test ofSeq should create a DList from an array" { 33 | let test = [| for i in 0..4 -> i |] 34 | DList.ofSeq test |> Expect.equal "" expected 35 | } 36 | 37 | test "test DList.cons should prepend 10 to the front of the original list" { 38 | DList.cons 10 expected |> Expect.equal "" (Join(Unit 10, expected, 6)) 39 | } 40 | 41 | test "test DList.singleton should return a Unit containing the solo value" { DList.singleton 1 |> Expect.equal "" (Unit 1) } 42 | 43 | test "test DList.cons should return a Unit when the tail is Nil" { DList.cons 1 DList.empty |> Expect.equal "" (Unit 1) } 44 | 45 | test "test subsequent DList.cons should create a DList just as the constructor functions" { 46 | DList.cons 0 (DList.cons 1 (DList.cons 2 (DList.cons 3 (DList.cons 4 DList.empty)))) 47 | |> Expect.equal "" expected 48 | } 49 | 50 | test "test DList.append should join two DLists together" { 51 | DList.append expected expected2 52 | |> Expect.equal "" (Join(expected, expected2, 9)) 53 | } 54 | 55 | test "test DList.snoc should join DList and one element together" { DList.snoc expected 5 |> Expect.equal "" (Join(expected, Unit 5, 6)) } 56 | 57 | test "test head should return the first item in the DList" { DList.head(DList.append expected expected) |> Expect.equal "" 0 } 58 | 59 | test "test tail should return all items except the head" { 60 | DList.tail(DList.append expected expected) 61 | |> Expect.equal "" (Join(DList.cons 1 (DList.cons 2 (DList.cons 3 (DList.cons 4 DList.empty))), expected, 9)) 62 | } 63 | 64 | test "test DList should respond to Seq functions such as map" { 65 | let testmap x = x * x 66 | let actual = Seq.map testmap (DList.append expected expected) 67 | 68 | let expected = 69 | seq { 70 | yield 0 71 | yield 1 72 | yield 2 73 | yield 3 74 | yield 4 75 | yield 0 76 | yield 1 77 | yield 2 78 | yield 3 79 | yield 4 80 | } 81 | |> Seq.map testmap 82 | 83 | Expect.sequenceEqual "" expected actual 84 | } 85 | ] 86 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/EditDistanceTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections 4 | open FSharpx.Collections.Experimental 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module EditDistanceTest = 9 | 10 | type BS = ByteString 11 | 12 | [] 13 | let testEditDistance = 14 | 15 | testList "Experimental EditDistance" [ 16 | test "distance example" { 17 | BKTree.ByteString.distance (BS "kitten"B) (BS "sitting"B) 18 | |> Expect.equal "" 3 19 | } 20 | 21 | test "toListDistance example" { 22 | [ BS "kitten"B; BS "setting"B; BS "getting"B ] 23 | |> BKTree.ByteString.ofList 24 | |> BKTree.ByteString.toListDistance 2 (BS "sitting"B) 25 | |> Expect.equal "" [ BS "setting"B; BS "getting"B ] 26 | } 27 | 28 | test "String distance example" { 29 | let inline toBS(text: string) = 30 | ByteString(System.Text.Encoding.ASCII.GetBytes text) 31 | 32 | let calcEditDistance text1 text2 = 33 | BKTree.ByteString.distance (toBS text1) (toBS text2) 34 | 35 | calcEditDistance "meilenstein" "levenshtein" |> Expect.equal "" 4 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/FSharpx.Collections.Experimental.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | True 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/FsCheckProperties.fs: -------------------------------------------------------------------------------- 1 | module FSharpx.Collections.Experimental.Tests.Properties 2 | 3 | open Expecto 4 | open FSharpx.Collections 5 | open FsCheck 6 | 7 | let configReplay = 8 | { FsCheckConfig.defaultConfig with 9 | maxTest = 10000 10 | replay = Some <| (1940624926, 296296394) 11 | } 12 | 13 | let config10k = 14 | { FsCheckConfig.defaultConfig with 15 | maxTest = 10000 16 | } 17 | 18 | let fsCheck name testable = 19 | FsCheck.Check.One(name, FsCheck.Config.Default, testable) 20 | 21 | let checkEquality<'a when 'a: equality> name = 22 | let n = sprintf "%s : equality %s" name 23 | fsCheck(n "identity") <| fun (x: 'a) -> x = x 24 | 25 | fsCheck(n "predicate") 26 | <| 27 | // depends a lot on the quality of the generated predicates, i.e. a constant predicate isn't useful. 28 | fun (f: 'a -> bool) x y -> f x = f y 29 | 30 | fsCheck(n "transitive") 31 | <| 32 | // not very useful, it's not likely that relevant values will be generated 33 | fun (x: 'a) (y: 'a) (z: 'a) -> if x = y && y = z then x = z else true 34 | 35 | let classifyCollect xs (count: int) (y: bool) = 36 | y 37 | |> Prop.collect count 38 | |> Prop.classify (xs.GetType().FullName.Contains("System.Int32")) "int" 39 | |> Prop.classify (xs.GetType().FullName.Contains("System.String")) "string" 40 | |> Prop.classify (xs.GetType().FullName.Contains("System.Boolean")) "bool" 41 | |> Prop.classify (xs.GetType().FullName.Contains("System.Object")) "object" 42 | 43 | module Gen = 44 | let rec infiniteSeq() = gen { 45 | let! x = Arb.generate 46 | let! xs = infiniteSeq() 47 | return Seq.append (Seq.singleton x) xs 48 | } 49 | 50 | let infiniteLazyList() = 51 | Gen.map LazyList.ofSeq (infiniteSeq()) 52 | 53 | let finiteLazyList() = 54 | Gen.map LazyList.ofList Arb.generate 55 | 56 | let ArbitrarySeqGen() = gen { 57 | let! len = Gen.choose(0, 10) 58 | let! l = Gen.listOfLength len Arb.generate 59 | 60 | return seq { 61 | for i = 0 to len - 1 do 62 | yield l.[i] 63 | } 64 | } 65 | 66 | let ArbitrarySeq() = 67 | ArbitrarySeqGen() |> Arb.fromGen 68 | 69 | (* 70 | Recommend a range of size 1 - 12 for lists used to build test data structures: 71 | 72 | Several data structures, especially those where the internal data representation is either binary or skew binary, have "distinct failure modes 73 | across the low range of sizes". What I mean by this is "it is possible to have bugs specific to certain small sizes in these data structures". So it is 74 | important that every structure size up to a certain value (let us say "8" for arguments sake) needs to be tested every time. By default FsCheck generates 75 | 100 (pseudo-random) lists. The larger the size range you allow for generation, the higher the chance these crucial small sizes will be skipped. 76 | *) 77 | let listBool n = 78 | Gen.listOfLength n Arb.generate 79 | 80 | let listInt n = 81 | Gen.listOfLength n Arb.generate 82 | 83 | let listObj n = 84 | Gen.listOfLength n Arb.generate 85 | 86 | let listString n = 87 | Gen.listOfLength n Arb.generate 88 | 89 | let length1thru n = 90 | Gen.choose(1, n) 91 | 92 | let length1thru12 = Gen.choose(1, 12) 93 | 94 | let length2thru12 = Gen.choose(2, 12) 95 | 96 | let length1thru100 = Gen.choose(1, 100) 97 | 98 | let length2thru100 = Gen.choose(2, 100) 99 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/HeapPriorityQueueTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module HeapPriorityQueueTest = 8 | 9 | [] 10 | let testHeapPriorityQueue = 11 | 12 | testList "Experimental HeapPriorityQueue" [ 13 | test "HeapPriorityQueue.empty queue should be HeapPriorityQueue.empty" { 14 | let pq = HeapPriorityQueue.empty false 15 | 16 | HeapPriorityQueue.isEmpty pq |> Expect.isTrue "" 17 | HeapPriorityQueue.tryPeek pq |> Expect.isNone "" 18 | HeapPriorityQueue.tryPop pq |> Expect.isNone "" 19 | } 20 | 21 | test "After adding an element to the PQ it shouldn't be HeapPriorityQueue.empty" { 22 | let pq = HeapPriorityQueue.empty false |> HeapPriorityQueue.insert 1 23 | 24 | HeapPriorityQueue.isEmpty pq |> Expect.isFalse "" 25 | } 26 | 27 | 28 | test "After adding an element to the PQ the element should be the smallest" { 29 | let pq = HeapPriorityQueue.empty false |> HeapPriorityQueue.insert 1 30 | 31 | HeapPriorityQueue.tryPeek pq |> Expect.equal "" (Some 1) 32 | HeapPriorityQueue.peek pq |> Expect.equal "" 1 33 | } 34 | 35 | test "After adding an element to the PQ and popping it the PQ should be HeapPriorityQueue.empty" { 36 | let pq = HeapPriorityQueue.empty false |> HeapPriorityQueue.insert 1 37 | 38 | let element, newPQ = HeapPriorityQueue.pop pq 39 | element |> Expect.equal "" 1 40 | HeapPriorityQueue.isEmpty newPQ |> Expect.isTrue "" 41 | 42 | let element, newPQ = (HeapPriorityQueue.tryPop pq).Value 43 | element |> Expect.equal "" 1 44 | HeapPriorityQueue.isEmpty newPQ |> Expect.isTrue "" 45 | } 46 | 47 | test "Adding multiple elements to the PQ should allow to HeapPriorityQueue.pop the smallest" { 48 | let pq = 49 | HeapPriorityQueue.empty false 50 | |> HeapPriorityQueue.insert 1 51 | |> HeapPriorityQueue.insert 3 52 | |> HeapPriorityQueue.insert 0 53 | |> HeapPriorityQueue.insert 4 54 | |> HeapPriorityQueue.insert -3 55 | 56 | let element, newPQ = HeapPriorityQueue.pop pq 57 | element |> Expect.equal "" -3 58 | 59 | let element, newPQ = HeapPriorityQueue.pop newPQ 60 | element |> Expect.equal "" 0 61 | 62 | let element, newPQ = HeapPriorityQueue.pop newPQ 63 | element |> Expect.equal "" 1 64 | 65 | let element, newPQ = HeapPriorityQueue.pop newPQ 66 | element |> Expect.equal "" 3 67 | 68 | let element, newPQ = HeapPriorityQueue.pop newPQ 69 | element |> Expect.equal "" 4 70 | 71 | HeapPriorityQueue.isEmpty newPQ |> Expect.isTrue "" 72 | } 73 | 74 | test "Adding multiple elements to a MaxPriorityQueue should allow to HeapPriorityQueue.pop the smallest" { 75 | let pq = 76 | HeapPriorityQueue.empty true 77 | |> HeapPriorityQueue.insert 1 78 | |> HeapPriorityQueue.insert 3 79 | |> HeapPriorityQueue.insert 0 80 | |> HeapPriorityQueue.insert 4 81 | |> HeapPriorityQueue.insert -3 82 | 83 | let element, newPQ = HeapPriorityQueue.pop pq 84 | element |> Expect.equal "" 4 85 | 86 | let element, newPQ = HeapPriorityQueue.pop newPQ 87 | element |> Expect.equal "" 3 88 | 89 | let element, newPQ = HeapPriorityQueue.pop newPQ 90 | element |> Expect.equal "" 1 91 | 92 | let element, newPQ = HeapPriorityQueue.pop newPQ 93 | element |> Expect.equal "" 0 94 | 95 | let element, newPQ = HeapPriorityQueue.pop newPQ 96 | element |> Expect.equal "" -3 97 | } 98 | 99 | test "Can use a PQ as a seq" { 100 | let pq = 101 | HeapPriorityQueue.empty false 102 | |> HeapPriorityQueue.insert 15 103 | |> HeapPriorityQueue.insert 3 104 | |> HeapPriorityQueue.insert 0 105 | |> HeapPriorityQueue.insert 4 106 | |> HeapPriorityQueue.insert -3 107 | 108 | pq |> Seq.toList |> Expect.equal "" [ -3; 0; 3; 4; 15 ] 109 | } 110 | ] 111 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/ImplicitQueueTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections 4 | open FSharpx.Collections.Experimental 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module ImplicitQueueTest = 9 | 10 | [] 11 | let testImplicitQueue = 12 | 13 | testList "Experimental ImplicitQueue" [ 14 | test "ImplicitQueue.empty queue should be ImplicitQueue.empty" { ImplicitQueue.isEmpty ImplicitQueue.empty |> Expect.isTrue "" } 15 | 16 | test "it should allow to enqueue" { 17 | ImplicitQueue.empty 18 | |> ImplicitQueue.snoc 1 19 | |> ImplicitQueue.snoc 2 20 | |> ImplicitQueue.isEmpty 21 | |> Expect.isFalse "" 22 | } 23 | 24 | test "it should allow to dequeue" { 25 | ImplicitQueue.empty 26 | |> ImplicitQueue.snoc 1 27 | |> ImplicitQueue.tail 28 | |> ImplicitQueue.isEmpty 29 | |> Expect.isTrue "" 30 | } 31 | 32 | test "it should fail if there is no ImplicitQueue.head in the queue" { 33 | let ok = ref false 34 | 35 | try 36 | ImplicitQueue.empty |> ImplicitQueue.head |> ignore 37 | with x when x = Exceptions.Empty -> 38 | ok := true 39 | 40 | !ok |> Expect.isTrue "" 41 | } 42 | 43 | test "it should give None if there is no ImplicitQueue.head in the queue" { 44 | ImplicitQueue.empty |> ImplicitQueue.tryGetHead |> Expect.isNone "" 45 | } 46 | 47 | test "it should fail if there is no ImplicitQueue.tail the queue" { 48 | let ok = ref false 49 | 50 | try 51 | ImplicitQueue.empty |> ImplicitQueue.tail |> ignore 52 | with x when x = Exceptions.Empty -> 53 | ok := true 54 | 55 | !ok |> Expect.isTrue "" 56 | } 57 | 58 | test "it should give None if there is no ImplicitQueue.tail in the queue" { 59 | ImplicitQueue.empty |> ImplicitQueue.tryGetTail |> Expect.isNone "" 60 | } 61 | 62 | test "it should allow to get the ImplicitQueue.head from a queue" { 63 | ImplicitQueue.empty 64 | |> ImplicitQueue.snoc 1 65 | |> ImplicitQueue.snoc 2 66 | |> ImplicitQueue.head 67 | |> Expect.equal "" 1 68 | } 69 | 70 | test "it should allow to get the ImplicitQueue.head from a queue safely" { 71 | ImplicitQueue.empty 72 | |> ImplicitQueue.snoc 1 73 | |> ImplicitQueue.snoc 2 74 | |> ImplicitQueue.tryGetHead 75 | |> Expect.equal "" (Some 1) 76 | } 77 | 78 | test "it should allow to get the ImplicitQueue.tail from the queue" { 79 | ImplicitQueue.empty 80 | |> ImplicitQueue.snoc "a" 81 | |> ImplicitQueue.snoc "b" 82 | |> ImplicitQueue.snoc "c" 83 | |> ImplicitQueue.tail 84 | |> ImplicitQueue.head 85 | |> Expect.equal "" "b" 86 | } 87 | 88 | test "it should allow to get the ImplicitQueue.tail from a queue safely" { 89 | let value = 90 | ImplicitQueue.empty 91 | |> ImplicitQueue.snoc 1 92 | |> ImplicitQueue.snoc 2 93 | |> ImplicitQueue.tryGetTail 94 | 95 | value.Value |> ImplicitQueue.head |> Expect.equal "" 2 96 | } 97 | ] 98 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/IndexedRoseTreeTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open FSharpx.Collections 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module IndexedRoseTreeTest = 9 | 10 | let tree a b = 11 | IndexedRoseTree.create a (PersistentVector.ofSeq b) 12 | 13 | let atree = tree 1 [ tree 2 [ tree 3 [] ]; tree 4 [ tree 5 [ tree 6 [] ] ] ] 14 | 15 | let ctree = 16 | tree "f" [ 17 | tree "b" [ tree "a" []; tree "d" [ tree "c" []; tree "e" [] ] ] 18 | tree "g" [ tree "i" [ tree "h" [] ] ] 19 | ] 20 | 21 | type HtmlElement = { 22 | TagName: string 23 | Attributes: (string * string) list 24 | } 25 | 26 | type HtmlNode = 27 | | Element of HtmlElement 28 | | Text of string 29 | 30 | type Html = HtmlNode IndexedRoseTree 31 | 32 | let iRTF l = 33 | List.fold (fun (s: list>) (t: int) -> (IndexedRoseTree.singleton t) :: s) [] l 34 | |> PersistentVector.ofSeq 35 | 36 | let iRTF2 = 37 | let rec loop (v: PersistentVector>) dec = 38 | match dec with 39 | | 0 -> v 40 | | _ -> loop (v.Conj(IndexedRoseTree.create 1 (iRTF [ 1..5 ]))) (dec - 1) 41 | 42 | loop PersistentVector.empty 5 43 | 44 | let iRT = IndexedRoseTree.create 1 iRTF2 45 | let singleRT = IndexedRoseTree.singleton 1 46 | 47 | [] 48 | let testIndexedRoseTree = 49 | // not the best example, as text nodes cannot have children 50 | 51 | let elemA tag attr = 52 | HtmlNode.Element { TagName = tag; Attributes = attr } 53 | 54 | let elem tag = 55 | elemA tag [] 56 | 57 | let text t = 58 | tree (HtmlNode.Text t) [] 59 | 60 | 61 | let htmldoc = tree (elem "body") [ tree (elem "div") [ text "hello world" ] ] 62 | 63 | testList "Experimental IndexedRoseTree" [ 64 | test "preOrder works" { 65 | let actual = IndexedRoseTree.preOrder ctree |> Seq.toList 66 | Expect.equal "" [ "f"; "b"; "a"; "d"; "c"; "e"; "g"; "i"; "h" ] actual 67 | } 68 | 69 | test "postOrder works" { 70 | let actual = IndexedRoseTree.postOrder ctree |> Seq.toList 71 | Expect.equal "" [ "a"; "c"; "e"; "d"; "b"; "h"; "i"; "g"; "f" ] actual 72 | } 73 | 74 | test "map" { 75 | let actual = IndexedRoseTree.map ((+) 1) atree 76 | let expected = tree 2 [ tree 3 [ tree 4 [] ]; tree 5 [ tree 6 [ tree 7 [] ] ] ] 77 | Expect.equal "" expected actual 78 | } 79 | 80 | test "fold via preOrder" { 81 | let actual = IndexedRoseTree.preOrder atree |> Seq.fold (*) 1 82 | Expect.equal "" 720 actual 83 | } 84 | 85 | test "bind" { 86 | let wrapText = 87 | function 88 | | Text t -> tree (elem "span") [ text t ] 89 | | x -> IndexedRoseTree.singleton x 90 | 91 | let newDoc = htmldoc |> IndexedRoseTree.bind wrapText 92 | 93 | let expected = 94 | tree (elem "body") [ tree (elem "div") [ tree (elem "span") [ text "hello world" ] ] ] 95 | 96 | Expect.equal "" expected newDoc 97 | } 98 | 99 | test "unfold" { 100 | let a = IndexedRoseTree.unfold (fun i -> i, PersistentVector.ofSeq { i + 1 .. 3 }) 0 101 | 102 | let expected = 103 | tree 0 [ tree 1 [ tree 2 [ tree 3 [] ]; tree 3 [] ]; tree 2 [ tree 3 [] ]; tree 3 [] ] 104 | 105 | Expect.equal "" expected a 106 | } 107 | 108 | test "functor laws" { 109 | //fsCheck version of functor and monad laws stackoverflows 110 | let map = IndexedRoseTree.map 111 | 112 | //preserves identity 113 | ((map id iRT) = iRT) |> Expect.isTrue "" 114 | ((map id singleRT) = singleRT) |> Expect.isTrue "" 115 | 116 | let f = (fun x -> x + 5) 117 | let g = (fun x -> x - 2) 118 | 119 | //preserves composition 120 | map (f << g) iRT = (map f << map g) iRT |> Expect.isTrue "" 121 | 122 | map (f << g) singleRT = (map f << map g) singleRT |> Expect.isTrue "" 123 | } 124 | 125 | test "monad laws" { 126 | //fsCheck version of functor and monad laws stackoverflows 127 | let inline (>>=) m f = 128 | IndexedRoseTree.bind f m 129 | 130 | let ret = IndexedRoseTree.singleton 131 | 132 | let myF x = 133 | IndexedRoseTree.create 134 | x 135 | (PersistentVector.empty 136 | |> PersistentVector.conj(IndexedRoseTree.singleton x) 137 | |> PersistentVector.conj(IndexedRoseTree.singleton x)) 138 | 139 | let a = 1 140 | 141 | //left identity 142 | ret a >>= myF = myF a |> Expect.isTrue "" 143 | 144 | //right identity 145 | iRT >>= ret = iRT |> Expect.isTrue "" 146 | singleRT >>= ret = singleRT |> Expect.isTrue "" 147 | 148 | //associativity 149 | let myG x = 150 | IndexedRoseTree.create 151 | (x = x) 152 | (PersistentVector.empty 153 | |> PersistentVector.conj(IndexedRoseTree.singleton(x = x)) 154 | |> PersistentVector.conj(IndexedRoseTree.singleton(x = x))) 155 | 156 | let a' = (iRT >>= myF) >>= myG 157 | let b' = iRT >>= (fun x -> myF x >>= myG) 158 | a' = b' |> Expect.isTrue "" 159 | 160 | let a'' = (singleRT >>= myF) >>= myG 161 | let b'' = singleRT >>= (fun x -> myF x >>= myG) 162 | a'' = b'' |> Expect.isTrue "" 163 | } 164 | ] 165 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/ListZipperTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module ListZipperTest = 8 | 9 | let chars = [ 'a' .. 'z' ] 10 | let digits = [ '0' .. '9' ] 11 | 12 | [] 13 | let testListZipper = 14 | 15 | testList "Experimental ListZipper" [ 16 | test "Can move forward" { 17 | let z = chars |> ListZipper.zipper |> ListZipper.forward |> ListZipper.forward 18 | Expect.equal "" 'c' <| ListZipper.focus z 19 | } 20 | 21 | test "Can move back" { 22 | let z = 23 | chars 24 | |> ListZipper.zipper 25 | |> ListZipper.forward 26 | |> ListZipper.forward 27 | |> ListZipper.back 28 | 29 | Expect.equal "" 'b' <| ListZipper.focus z 30 | } 31 | 32 | test "Can move to the front" { 33 | let z = 34 | chars 35 | |> ListZipper.zipper 36 | |> ListZipper.forward 37 | |> ListZipper.forward 38 | |> ListZipper.front 39 | 40 | Expect.equal "" 'a' <| ListZipper.focus z 41 | } 42 | 43 | test "Can modify an element" { 44 | let z = 45 | chars 46 | |> ListZipper.zipper 47 | |> ListZipper.forward 48 | |> ListZipper.forward 49 | |> ListZipper.modify 'e' 50 | |> ListZipper.back 51 | |> ListZipper.forward 52 | 53 | Expect.equal "" 'e' <| ListZipper.focus z 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/RealTimeQueueTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections 4 | open FSharpx.Collections.Experimental 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module RealTimeQueueTest = 9 | 10 | [] 11 | let testRealTimeQueue = 12 | 13 | testList "Experimental RealTimeQueue" [ 14 | test "RealTimeQueue.empty queue should be RealTimeQueue.empty" { RealTimeQueue.isEmpty RealTimeQueue.empty |> Expect.isTrue "" } 15 | 16 | test "it should allow to enqueue" { 17 | RealTimeQueue.empty 18 | |> RealTimeQueue.snoc 1 19 | |> RealTimeQueue.snoc 2 20 | |> RealTimeQueue.isEmpty 21 | |> Expect.isFalse "" 22 | } 23 | 24 | test "it should allow to dequeue" { 25 | RealTimeQueue.empty 26 | |> RealTimeQueue.snoc 1 27 | |> RealTimeQueue.tail 28 | |> RealTimeQueue.isEmpty 29 | |> Expect.isTrue "" 30 | } 31 | 32 | test "it should fail if there is no RealTimeQueue.head in the queue" { 33 | let ok = ref false 34 | 35 | try 36 | RealTimeQueue.empty |> RealTimeQueue.head |> ignore 37 | with x when x = Exceptions.Empty -> 38 | ok := true 39 | 40 | !ok |> Expect.isTrue "" 41 | } 42 | 43 | test "it should give None if there is no RealTimeQueue.head in the queue" { 44 | RealTimeQueue.empty |> RealTimeQueue.tryGetHead |> Expect.isNone "" 45 | } 46 | 47 | test "it should fail if there is no RealTimeQueue.tail the queue" { 48 | let ok = ref false 49 | 50 | try 51 | RealTimeQueue.empty |> RealTimeQueue.tail |> ignore 52 | with x when x = Exceptions.Empty -> 53 | ok := true 54 | 55 | !ok |> Expect.isTrue "" 56 | } 57 | 58 | test "it should give None if there is no RealTimeQueue.tail in the queue" { 59 | RealTimeQueue.empty |> RealTimeQueue.tryGetTail |> Expect.isNone "" 60 | } 61 | 62 | test "it should allow to get the RealTimeQueue.head from a queue" { 63 | RealTimeQueue.empty 64 | |> RealTimeQueue.snoc 1 65 | |> RealTimeQueue.snoc 2 66 | |> RealTimeQueue.head 67 | |> Expect.equal "" 1 68 | } 69 | 70 | test "it should allow to get the RealTimeQueue.head from a queue safely" { 71 | RealTimeQueue.empty 72 | |> RealTimeQueue.snoc 1 73 | |> RealTimeQueue.snoc 2 74 | |> RealTimeQueue.tryGetHead 75 | |> Expect.equal "" (Some 1) 76 | } 77 | 78 | test "it should allow to get the RealTimeQueue.tail from the queue" { 79 | RealTimeQueue.empty 80 | |> RealTimeQueue.snoc "a" 81 | |> RealTimeQueue.snoc "b" 82 | |> RealTimeQueue.snoc "c" 83 | |> RealTimeQueue.tail 84 | |> RealTimeQueue.head 85 | |> Expect.equal "" "b" 86 | } 87 | 88 | test "it should allow to get the RealTimeQueue.tail from a queue safely" { 89 | let value = 90 | RealTimeQueue.empty 91 | |> RealTimeQueue.snoc 1 92 | |> RealTimeQueue.snoc 2 93 | |> RealTimeQueue.tryGetTail 94 | 95 | value.Value |> RealTimeQueue.head |> Expect.equal "" 2 96 | } 97 | ] 98 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/RingBufferTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open FSharpx.Collections.Experimental 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module RingBufferTest = 8 | 9 | [] 10 | let testRingBuffer = 11 | 12 | testList "Experimental RingBuffer" [ 13 | test "I can create a ring buffer with a given size" { 14 | let actual = new RingBuffer(10) 15 | let expected = Array.init 10 (fun _ -> 0) 16 | actual.ToArray() |> Expect.equal "" expected 17 | } 18 | 19 | test "I can create a ring buffer with a given set of values" { 20 | let actual = new RingBuffer([| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |]) 21 | let expected = Array.init 10 (fun i -> i + 1) 22 | actual.ToArray() |> Expect.equal "" expected 23 | } 24 | 25 | test "I can normalise a ring buffer" { 26 | let actual = new RingBuffer([| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |]) 27 | actual.Advance(5) |> ignore 28 | actual.Position |> Expect.equal "" 5 29 | actual.Normalize() 30 | actual.Position |> Expect.equal "" 0 31 | 32 | actual.ToArray() 33 | |> Expect.equal "" [| 6; 7; 8; 9; 10; 0; 0; 0; 0; 0 |] 34 | } 35 | 36 | test "I can insert values at 0 offset" { 37 | let buffer = new RingBuffer(10) 38 | buffer.Insert(0, [| 1; 2; 3; 4; 5 |]) 39 | buffer.ToArray() |> Expect.equal "" [| 1; 2; 3; 4; 5; 0; 0; 0; 0; 0 |] 40 | } 41 | 42 | test "I should not be able to insert more values than the size of the buffer" { 43 | let buffer = new RingBuffer(10) 44 | buffer.Insert(0, [| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11 |]) 45 | 46 | buffer.ToArray() 47 | |> Expect.equal "" [| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |] 48 | } 49 | 50 | test "Insert should not fail if an empty sequence is inserted" { 51 | let actual = new RingBuffer(10) 52 | actual.Insert(0, [||]) 53 | let expected = Array.init 10 (fun _ -> 0) 54 | actual.ToArray() |> Expect.equal "" expected 55 | } 56 | 57 | test "I should be able to move the start index of the buffer" { 58 | let buffer = new RingBuffer([| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |]) 59 | buffer.Advance(5) |> ignore 60 | 61 | buffer.ToArray() 62 | |> Expect.equal "" [| 6; 7; 8; 9; 10; 0; 0; 0; 0; 0 |] 63 | } 64 | 65 | test "Advancing past the end of the buffer should cause it to wrap" { 66 | let buffer = new RingBuffer([| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |]) 67 | buffer.Advance(11) |> ignore 68 | buffer.ToArray() |> Expect.equal "" [| 0; 0; 0; 0; 0; 0; 0; 0; 0; 0 |] 69 | buffer.Position |> Expect.equal "" 1 70 | } 71 | 72 | test "I should be able to clone a ring buffer" { 73 | let buffer = new RingBuffer([| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 |]) 74 | let clone = buffer.Clone() 75 | buffer.ToArray() |> Expect.equal "" <| clone.ToArray() 76 | clone.Advance(2) |> ignore 77 | Expect.notEqual "" (buffer.ToArray()) <| clone.ToArray() 78 | buffer.Position |> Expect.equal "" 0 79 | clone.Position |> Expect.equal "" 2 80 | } 81 | 82 | test "I can insert with an operation into the buffer" { 83 | let buffer = new RingBuffer(10) 84 | buffer.Insert(0, [| 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11 |]) 85 | buffer.Insert((+), 5, [ 100; 100; 100; 100; 100; 100 ]) 86 | 87 | buffer.ToArray() 88 | |> Expect.equal "" [| 1; 2; 3; 4; 5; 106; 107; 108; 109; 110 |] 89 | } 90 | ] 91 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/RunTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Experimental.Tests 2 | 3 | open Expecto 4 | open Expecto.Impl 5 | 6 | module RunTests = 7 | 8 | [] 9 | let main args = 10 | Tests.runTestsInAssembly Tests.defaultConfig args 11 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Experimental.Tests/paket.references: -------------------------------------------------------------------------------- 1 | FsCheck 2 | Expecto 3 | Expecto.FsCheck 4 | Microsoft.NET.Test.Sdk 5 | YoloDev.Expecto.TestSdk -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/ArrayTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open FSharpx.Collections 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module ArrayTests = 8 | 9 | let data = [| 1.; 2.; 3.; 4.; 5.; 6.; 7.; 8.; 9.; 10. |] 10 | 11 | [] 12 | let testArray = 13 | testList "Array" [ 14 | test "I should be able to part of an array to a target array" { 15 | let a, b = [| 1; 2; 3; 4; 5 |], [| 10; 11; 12; 13; 14 |] 16 | Array.copyTo 0 2 a b 17 | Expect.equal "expect arrays equal" [| 10; 11; 1; 2; 3 |] b 18 | } 19 | 20 | test "I should be able to convert a tuple to an array" { 21 | (1, 2) 22 | |> Array.ofTuple 23 | |> (Expect.equal "expect arrays equal" [| 1; 2 |]) 24 | } 25 | 26 | test "I should be able to convert an array to a tuple" { 27 | let result: (int * int) = [| 1; 2 |] |> Array.toTuple 28 | Expect.equal "expect tuples equal" (1, 2) result 29 | } 30 | 31 | test "I should be able to create a centered window from a seq" { 32 | let expected = [| 33 | [| 1.; 2.; 3.; 4. |] 34 | [| 1.; 2.; 3.; 4.; 5. |] 35 | [| 1.; 2.; 3.; 4.; 5.; 6. |] 36 | [| 1.; 2.; 3.; 4.; 5.; 6.; 7. |] 37 | [| 2.; 3.; 4.; 5.; 6.; 7.; 8. |] 38 | [| 3.; 4.; 5.; 6.; 7.; 8.; 9. |] 39 | [| 4.; 5.; 6.; 7.; 8.; 9.; 10. |] 40 | [| 5.; 6.; 7.; 8.; 9.; 10. |] 41 | [| 6.; 7.; 8.; 9.; 10. |] 42 | [| 7.; 8.; 9.; 10. |] 43 | |] 44 | 45 | Expect.equal "expect arrays equal" expected 46 | <| Array.centeredWindow 3 data 47 | } 48 | 49 | test "I should be able to compute the central moving average of a seq" { 50 | let expected = [| 2.5; 3.; 3.5; 4.; 5.; 6.; 7.; 7.5; 8.; 8.5 |] 51 | 52 | Expect.equal "expect arrays equal" expected 53 | <| Array.centralMovingAverage 3 data 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/DictionaryExtensionsTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open FSharpx.Collections 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module DictionaryExtensionsTests = 8 | 9 | [] 10 | let testDictionaryExtensions = 11 | testList "DictionaryExtensions" [ 12 | 13 | test "dictionary tryfind with some" { 14 | let a = dict [ 1, "one"; 2, "two" ] 15 | Expect.equal "tryfind" (Some "one") (a |> Dictionary.tryFind 1) 16 | } 17 | 18 | test "dictionary tryfind with none" { 19 | let a = dict [ 1, "one"; 2, "two" ] 20 | Expect.isNone "tryfind" (a |> Dictionary.tryFind 3) 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/FSharpx.Collections.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | True 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/FsCheckProperties.fs: -------------------------------------------------------------------------------- 1 | module FSharpx.Collections.Tests.Properties 2 | 3 | open Expecto 4 | open FSharpx.Collections 5 | open FsCheck 6 | 7 | let configReplay = 8 | { FsCheckConfig.defaultConfig with 9 | maxTest = 10000 10 | replay = Some <| (1940624926, 296296394) 11 | } 12 | 13 | let config10k = 14 | { FsCheckConfig.defaultConfig with 15 | maxTest = 10000 16 | } 17 | 18 | let classifyCollect xs (count: int) (y: bool) = 19 | y 20 | |> Prop.collect count 21 | |> Prop.classify (xs.GetType().FullName.Contains("System.Int32")) "int" 22 | |> Prop.classify (xs.GetType().FullName.Contains("System.String")) "string" 23 | |> Prop.classify (xs.GetType().FullName.Contains("System.Boolean")) "bool" 24 | |> Prop.classify (xs.GetType().FullName.Contains("System.Object")) "object" 25 | 26 | module Gen = 27 | let rec infiniteSeq() = gen { 28 | let! x = Arb.generate 29 | let! xs = infiniteSeq() 30 | return Seq.append (Seq.singleton x) xs 31 | } 32 | 33 | let infiniteLazyList() = 34 | Gen.map LazyList.ofSeq (infiniteSeq()) 35 | 36 | let finiteLazyList() = 37 | Gen.map LazyList.ofList Arb.generate 38 | 39 | let ArbitrarySeqGen() = gen { 40 | let! len = Gen.choose(0, 10) 41 | let! l = Gen.listOfLength len Arb.generate 42 | 43 | return seq { 44 | for i = 0 to len - 1 do 45 | yield l.[i] 46 | } 47 | } 48 | 49 | let ArbitrarySeq() = 50 | ArbitrarySeqGen() |> Arb.fromGen 51 | 52 | (* 53 | Recommend a range of size 1 - 12 for lists used to build test data structures: 54 | 55 | Several data structures, especially those where the internal data representation is either binary or skew binary, have "distinct failure modes 56 | across the low range of sizes". What I mean by this is "it is possible to have bugs specific to certain small sizes in these data structures". So it is 57 | important that every structure size up to a certain value (let us say "8" for arguments sake) needs to be tested every time. By default FsCheck generates 58 | 100 (pseudo-random) lists. The larger the size range you allow for generation, the higher the chance these crucial small sizes will be skipped. 59 | *) 60 | let listBool n = 61 | Gen.listOfLength n Arb.generate 62 | 63 | let listInt n = 64 | Gen.listOfLength n Arb.generate 65 | 66 | let listObj n = 67 | Gen.listOfLength n Arb.generate 68 | 69 | let listString n = 70 | Gen.listOfLength n Arb.generate 71 | 72 | let length1thru n = 73 | Gen.choose(1, n) 74 | 75 | let length1thru12 = Gen.choose(1, 12) 76 | 77 | let length2thru12 = Gen.choose(2, 12) 78 | 79 | let length1thru100 = Gen.choose(1, 100) 80 | 81 | let length2thru100 = Gen.choose(2, 100) 82 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/ListExtensionsTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open FSharpx.Collections 4 | open FSharpx.Collections.Tests.Properties 5 | open Expecto 6 | open Expecto.Flip 7 | 8 | module ListExtensionsTests = 9 | 10 | [] 11 | let testListExtensions = 12 | testList "ListExtensions" [ 13 | 14 | test "test List_split correctly breaks the list on the specified predicate" { 15 | let str = List.ofSeq "Howdy! Want to play?" 16 | let expected = (List.ofSeq "Howdy!", List.ofSeq " Want to play?") 17 | Expect.equal "split" expected <| List.split (fun c -> c = ' ') str 18 | } 19 | 20 | test "test List_splitAt correctly breaks the list on the specified index" { 21 | let str = List.ofSeq "Howdy! Want to play?" 22 | let expected = (List.ofSeq "Howdy!", List.ofSeq " Want to play?") 23 | Expect.equal "splitAt" expected <| List.splitAt 6 str 24 | } 25 | 26 | test "Can splitAt 3" { 27 | let list = [ 1..5 ] 28 | let expected = [ 1; 2; 3 ], [ 4; 5 ] 29 | Expect.equal "splitAt" expected <| List.splitAt 3 list 30 | } 31 | 32 | test "Can split at 3" { 33 | let list = [ 1..5 ] 34 | let expected = [ 1; 2 ], [ 3; 4; 5 ] 35 | Expect.equal "split" expected <| List.split ((=) 3) list 36 | } 37 | 38 | test "Can split at 0" { 39 | let l1, l2 = List.split ((=) 0) [ 1..5 ] 40 | Expect.equal "split" [ 1..5 ] l1 41 | Expect.equal "split" [] l2 42 | } 43 | 44 | test "test List_span correctly breaks the list on the specified predicate" { 45 | let str = List.ofSeq "Howdy! Want to play?" 46 | let expected = (List.ofSeq "Howdy!", List.ofSeq " Want to play?") 47 | Expect.equal "span" expected <| List.span (fun c -> c <> ' ') str 48 | } 49 | 50 | test "lift2" { 51 | Expect.equal "lift2" [ 0; 2; 1; 3 ] 52 | <| List.lift2 (+) [ 0; 1 ] [ 0; 2 ] 53 | } 54 | 55 | test "mapAccum" { 56 | let list = [ -5 .. -1 ] 57 | let expected = (15, [ 5; 4; 3; 2; 1 ]) 58 | 59 | Expect.equal "mapAccum" expected 60 | <| List.mapAccum (fun a b -> let c = abs b in (a + c, c)) 0 list 61 | } 62 | 63 | test "Should be able to merge to lists" { 64 | let a, b = [ 1; 2; 3; 4; 5 ], [ 6; 7; 8; 9; 10 ] 65 | 66 | Expect.equal "merge" [ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 ] 67 | <| List.mergeBy id a b 68 | } 69 | 70 | test "Should be able to merge two lists II" { 71 | let a, b = [ 1; 2; 3; 4; 5 ], [ 6; 7; 8; 9; 10 ] 72 | 73 | Expect.equal "merge" [ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 ] 74 | <| List.merge a b 75 | } 76 | 77 | test "I should be able to transpose a list" { 78 | let a = [ [ 1; 2; 3 ]; [ 4; 5; 6 ] ] 79 | let expected = [ [ 1; 4 ]; [ 2; 5 ]; [ 3; 6 ] ] 80 | Expect.equal "transpose" expected (a |> List.transpose) 81 | } 82 | ] 83 | 84 | [] 85 | let propertyTestListExtensions = 86 | let fill (total: int) (elem: 'a) (list: 'a list) = 87 | let padded = List.fill total elem list 88 | 89 | if total > 0 && total > List.length list then 90 | List.length padded = total 91 | else 92 | List.length padded = List.length list 93 | 94 | let pad (total: int) (elem: 'a) (list: 'a list) = 95 | let padded = List.pad total elem list 96 | 97 | if total > 0 then 98 | List.length padded = total + List.length list 99 | else 100 | List.length padded = List.length list 101 | 102 | testList "ListExtensions property tests" [ 103 | testPropertyWithConfig config10k "fill a list" fill 104 | 105 | testPropertyWithConfig config10k "pad a list" pad 106 | ] 107 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/MapExtensionsTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open FSharpx.Collections 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module MapExtensionsTests = 8 | [] 9 | let testMapExtensions = 10 | testList "MapExtensions" [ 11 | test "map insertWith" { 12 | let a = Map.ofList [ 1, "one"; 2, "two" ] 13 | Expect.equal "insertWith" [ 1, "new one"; 2, "two" ] (a |> Map.insertWith (+) 1 "new " |> Map.toList) 14 | } 15 | 16 | test "map updateWith should update value if (f x) is Some" { 17 | let f x = 18 | if x = "one" then Some "new one" else None 19 | 20 | let a = Map.ofList [ 1, "one"; 2, "two" ] 21 | Expect.equal "updateWith" [ 1, "new one"; 2, "two" ] (a |> Map.updateWith f 1 |> Map.toList) 22 | } 23 | 24 | test "map updateWith should delete element if (f x) is None" { 25 | let f x = 26 | if x = "one" then Some "new one" else None 27 | 28 | let a = Map.ofList [ 1, "one"; 2, "two" ] 29 | Expect.equal "updateWith" [ 1, "one" ] (a |> Map.updateWith f 2 |> Map.toList) 30 | } 31 | 32 | test "test Map_splitWithKey correctly breaks the dictionary on the specified predicate" { 33 | let a = Map.ofList [ 0, "zero"; 1, "one"; 2, "two"; 3, "three"; 4, "four" ] 34 | let v1, v2 = a |> Map.splitWithKey((>=) 2) 35 | 36 | Expect.equal "splitWithKey" [ 0, "zero"; 1, "one"; 2, "two" ] 37 | <| Map.toList v1 38 | 39 | Expect.equal "splitWithKey" [ 3, "three"; 4, "four" ] <| Map.toList v2 40 | } 41 | 42 | test "test Map_spanWithKey correctly breaks the dictionary on the specified predicate" { 43 | let a = Map.ofList [ 0, "zero"; 1, "one"; 2, "two"; 3, "three"; 4, "four" ] 44 | let v1, v2 = a |> Map.spanWithKey((<) 2) 45 | 46 | Expect.equal "spanWithKey" [ 0, "zero"; 1, "one"; 2, "two" ] 47 | <| Map.toList v1 48 | 49 | Expect.equal "spanWithKey" [ 3, "three"; 4, "four" ] <| Map.toList v2 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/MapTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | // originally from https://bitbucket.org/colinbul/fsharpent 4 | 5 | open FSharpx.Collections 6 | open Expecto 7 | open Expecto.Flip 8 | 9 | module MapTests = 10 | let data = 11 | [ 12 | (1, 1) 13 | (2, 2) 14 | (3, 3) 15 | (4, 4) 16 | (5, 5) 17 | (6, 6) 18 | (7, 7) 19 | (8, 8) 20 | (9, 9) 21 | (10, 10) 22 | ] 23 | |> Map.ofList 24 | 25 | [] 26 | let testMap = 27 | testList "Map" [ 28 | 29 | test "I should be able to choose elements from a map" { 30 | let expected = [ (2, 2); (4, 4); (6, 6); (8, 8); (10, 10) ] |> Map.ofList 31 | 32 | Expect.equal "choose" expected 33 | <| Map.choose (fun k _ -> if k % 2 = 0 then Some k else None) data 34 | } 35 | 36 | test "I should be able to remove elements by key" { 37 | let toRemove = [ 1; 2; 3; 4; 5 ] 38 | let expected = [ (6, 6); (7, 7); (8, 8); (9, 9); (10, 10) ] |> Map.ofList 39 | Expect.equal "removeMany" expected <| Map.removeMany toRemove data 40 | } 41 | 42 | test "I should be able to extract values from a map" { 43 | let expected = [ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 ] |> Seq.ofList 44 | Expect.sequenceEqual "values" expected <| Map.values data 45 | } 46 | 47 | test "I should be able to extract keys from a map" { 48 | let expected = [ 1; 2; 3; 4; 5; 6; 7; 8; 9; 10 ] |> Seq.ofList 49 | Expect.sequenceEqual "keys" expected <| Map.keys data 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/NameValueCollectionTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open System.Collections.Generic 4 | open System.Collections.Specialized 5 | open System.Linq 6 | open FSharpx.Collections 7 | open Expecto 8 | open Expecto.Flip 9 | 10 | module NameValueCollectionTests = 11 | 12 | [] 13 | let testNameValueCollection = 14 | 15 | let assertKeyIs (l: ILookup<_, _>) a key = 16 | Expect.equal "assertKeyIs" a (l.[key] |> Seq.toList) 17 | 18 | testList "NameValueCollection" [ 19 | 20 | test "ofSeq" { 21 | let n1 = NameValueCollection.ofSeq [ "1", "uno"; "1", "one"; "2", "two" ] 22 | let n2 = NameValueCollection() 23 | n2.Add("1", "uno") 24 | n2.Add("1", "one") 25 | n2.Add("2", "two") 26 | 27 | Expect.equal "ofSeq" n1.AllKeys n2.AllKeys 28 | Expect.equal "ofSeq" (n1.GetValues("1")) (n2.GetValues("1")) 29 | } 30 | 31 | test "toSeq" { 32 | let r = [ "1", "uno"; "1", "one"; "2", "two" ] 33 | let a = NameValueCollection.ofSeq r 34 | let s = NameValueCollection.toSeq a 35 | Expect.sequenceEqual "toSeq" (List.toSeq r) s 36 | } 37 | 38 | test "toArray" { 39 | let r = [| "1", "uno"; "1", "one"; "2", "two" |] 40 | let a = NameValueCollection.ofSeq r 41 | let s = NameValueCollection.toArray a 42 | Expect.equal "toArray" r s 43 | } 44 | 45 | test "toList" { 46 | let r = [ "1", "uno"; "1", "one"; "2", "two" ] 47 | let a = NameValueCollection.ofSeq r 48 | let s = NameValueCollection.toList a 49 | Expect.equal "toList" r s 50 | } 51 | 52 | test "concat" { 53 | let a = NameValueCollection() 54 | a.Add("1", "uno") 55 | a.Add("2", "dos") 56 | let b = NameValueCollection() 57 | b.Add("1", "one") 58 | b.Add("2", "two") 59 | let c = NameValueCollection.concat a b 60 | 61 | Expect.equal "concat" [ ("1", "uno"); ("1", "one"); ("2", "dos"); ("2", "two") ] 62 | <| NameValueCollection.toList c 63 | 64 | Expect.equal "ofSeq" [| "1"; "2" |] c.AllKeys 65 | } 66 | 67 | test "toLookup" { 68 | let n1 = NameValueCollection.ofSeq [ "1", "uno"; "1", "one"; "2", "two" ] 69 | let l = NameValueCollection.toLookup n1 70 | "1" |> assertKeyIs l [ "uno"; "one" ] 71 | "2" |> assertKeyIs l [ "two" ] 72 | "3" |> assertKeyIs l [] 73 | n1.Add("3", "three") 74 | "3" |> assertKeyIs l [] 75 | } 76 | 77 | test "asLookup" { 78 | let n1 = NameValueCollection.ofSeq [ "1", "uno"; "1", "one"; "2", "two" ] 79 | let l = NameValueCollection.asLookup n1 80 | "1" |> assertKeyIs l [ "uno"; "one" ] 81 | "2" |> assertKeyIs l [ "two" ] 82 | "3" |> assertKeyIs l [] 83 | n1.Add("3", "three") 84 | "3" |> assertKeyIs l [ "three" ] 85 | } 86 | 87 | test "asDictionary" { 88 | let n1 = NameValueCollection.ofSeq [ "1", "uno"; "1", "one"; "2", "two" ] 89 | let d = NameValueCollection.asDictionary n1 90 | Expect.equal "asDictionary" [| "uno"; "one" |] d.["1"] 91 | Expect.equal "asDictionary" [| "two" |] d.["2"] 92 | Expect.throwsT "asDictionary" (fun () -> ignore d.["3"] |> ignore) 93 | n1.Add("3", "three") 94 | Expect.equal "asDictionary" [| "three" |] d.["3"] 95 | } 96 | ] 97 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/PriorityQueueTest.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open FSharpx.Collections 4 | open Expecto 5 | open Expecto.Flip 6 | 7 | module PriorityQueueTests = 8 | [] 9 | let testPriorityQueue = 10 | 11 | testList "PriorityQueue" [ 12 | test "empty queue should be empty" { 13 | let pq = PriorityQueue.empty false 14 | 15 | Expect.isTrue "empty" <| PriorityQueue.isEmpty pq 16 | Expect.isNone "empty" <| PriorityQueue.tryPeek pq 17 | Expect.isNone "empty" <| PriorityQueue.tryPop pq 18 | } 19 | 20 | test "After adding an element to the PQ it shouldn't be empty" { 21 | let pq = PriorityQueue.empty false |> PriorityQueue.insert 1 22 | Expect.isFalse "PriorityQueue.insert" <| PriorityQueue.isEmpty pq 23 | } 24 | 25 | test "After adding an element to the PQ the element should be the smallest" { 26 | let pq = PriorityQueue.empty false |> PriorityQueue.insert 1 27 | 28 | Expect.equal "PriorityQueue.insert" (Some 1) 29 | <| PriorityQueue.tryPeek pq 30 | 31 | Expect.equal "PriorityQueue.insert" 1 <| PriorityQueue.peek pq 32 | } 33 | 34 | test "After adding an element to the PQ and popping it the PQ should be empty" { 35 | let pq = PriorityQueue.empty false |> PriorityQueue.insert 1 36 | 37 | let element, newPQ = PriorityQueue.pop pq 38 | Expect.equal "PriorityQueue.pop" 1 element 39 | Expect.isTrue "PriorityQueue.pop" <| PriorityQueue.isEmpty newPQ 40 | 41 | let element, newPQ = (PriorityQueue.tryPop pq).Value 42 | Expect.equal "PriorityQueue.tryPop" 1 element 43 | Expect.isTrue "PriorityQueue.tryPop" <| PriorityQueue.isEmpty newPQ 44 | } 45 | 46 | test "Adding multiple elements to the PQ should allow to PriorityQueue.pop the smallest" { 47 | let pq = 48 | PriorityQueue.empty false 49 | |> PriorityQueue.insert 1 50 | |> PriorityQueue.insert 3 51 | |> PriorityQueue.insert 0 52 | |> PriorityQueue.insert 4 53 | |> PriorityQueue.insert -3 54 | 55 | let element, newPQ = PriorityQueue.pop pq 56 | Expect.equal "PriorityQueue.pop" -3 element 57 | 58 | let element, newPQ = PriorityQueue.pop newPQ 59 | Expect.equal "PriorityQueue.pop" 0 element 60 | 61 | let element, newPQ = PriorityQueue.pop newPQ 62 | Expect.equal "PriorityQueue.pop" 1 element 63 | 64 | let element, newPQ = PriorityQueue.pop newPQ 65 | Expect.equal "PriorityQueue.pop" 3 element 66 | 67 | let element, newPQ = PriorityQueue.pop newPQ 68 | Expect.equal "PriorityQueue.pop" 4 element 69 | 70 | Expect.isTrue "PriorityQueue.pop" <| PriorityQueue.isEmpty newPQ 71 | } 72 | 73 | test "Adding multiple elements to a MaxPriorityQueue should allow to PriorityQueue.pop the smallest" { 74 | let pq = 75 | PriorityQueue.empty true 76 | |> PriorityQueue.insert 1 77 | |> PriorityQueue.insert 3 78 | |> PriorityQueue.insert 0 79 | |> PriorityQueue.insert 4 80 | |> PriorityQueue.insert -3 81 | 82 | let element, newPQ = PriorityQueue.pop pq 83 | Expect.equal "PriorityQueue.pop" 4 element 84 | 85 | let element, newPQ = PriorityQueue.pop newPQ 86 | Expect.equal "PriorityQueue.pop" 3 element 87 | 88 | let element, newPQ = PriorityQueue.pop newPQ 89 | Expect.equal "PriorityQueue.pop" 1 element 90 | 91 | let element, newPQ = PriorityQueue.pop newPQ 92 | Expect.equal "PriorityQueue.pop" 0 element 93 | 94 | let element, newPQ = PriorityQueue.pop newPQ 95 | Expect.equal "PriorityQueue.pop" -3 element 96 | 97 | Expect.isTrue "PriorityQueue.pop" <| PriorityQueue.isEmpty newPQ 98 | } 99 | 100 | test "Can use a PQ as a seq" { 101 | let pq = 102 | PriorityQueue.empty false 103 | |> PriorityQueue.insert 15 104 | |> PriorityQueue.insert 3 105 | |> PriorityQueue.insert 0 106 | |> PriorityQueue.insert 4 107 | |> PriorityQueue.insert -3 108 | 109 | Expect.equal "" [ -3; 0; 3; 4; 15 ] (pq |> Seq.toList) 110 | } 111 | ] 112 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/RunTests.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpx.Collections.Tests 2 | 3 | open Expecto 4 | open Expecto.Impl 5 | 6 | module RunTests = 7 | 8 | [] 9 | let main args = 10 | Tests.runTestsInAssembly Tests.defaultConfig args 11 | -------------------------------------------------------------------------------- /tests/FSharpx.Collections.Tests/paket.references: -------------------------------------------------------------------------------- 1 | FsCheck 2 | Expecto 3 | Expecto.FsCheck 4 | Microsoft.NET.Test.Sdk 5 | YoloDev.Expecto.TestSdk -------------------------------------------------------------------------------- /tests/fable/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json # Use Yarn 3 | dist 4 | 5 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/Deque.test.fs: -------------------------------------------------------------------------------- 1 | module DequeTests 2 | 3 | open Fable.Jester 4 | open Fable.FastCheck 5 | open Fable.FastCheck.Jest 6 | open FSharpx.Collections 7 | 8 | Jest.test( 9 | "NonEmptyList works", 10 | async { 11 | let d = Deque.empty 12 | 13 | Jest.expect(Deque.isEmpty d).toEqual(true) 14 | 15 | let d = d |> Deque.conj "b" |> Deque.conj "a" 16 | 17 | Jest.expect(Deque.isEmpty d).toEqual(false) 18 | Jest.expect(Deque.head d).toEqual("b") 19 | Jest.expect(Deque.length d).toEqual(2) 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/FSharpx.Collections.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/LazyList.test.fs: -------------------------------------------------------------------------------- 1 | module LazyListTests 2 | 3 | open Fable.Jester 4 | open Fable.FastCheck 5 | open Fable.FastCheck.Jest 6 | open FSharpx.Collections 7 | 8 | Jest.test( 9 | "LazyList works", 10 | async { 11 | let xs = LazyList.empty 12 | 13 | Jest.expect(LazyList.isEmpty xs).toEqual(true) 14 | 15 | let xs = LazyList.ofList [ 1; 2; 3 ] 16 | 17 | Jest.expect(LazyList.isEmpty xs).toEqual(false) 18 | Jest.expect(LazyList.tryHead xs).toEqual(1) 19 | 20 | let xs = xs |> LazyList.tail |> LazyList.tail 21 | 22 | Jest.expect(LazyList.tryHead xs).toEqual(3) 23 | } 24 | ) 25 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/NonEmptyList.test.fs: -------------------------------------------------------------------------------- 1 | module NonEmptyListTests 2 | 3 | open Fable.Jester 4 | open Fable.FastCheck 5 | open Fable.FastCheck.Jest 6 | open FSharpx.Collections 7 | 8 | Jest.test( 9 | "NonEmptyList works", 10 | async { 11 | let xs = NonEmptyList.create "a" [] 12 | 13 | Jest.expect(NonEmptyList.length xs).toEqual(1) 14 | Jest.expect(NonEmptyList.head xs).toEqual("a") 15 | 16 | let xs = xs |> NonEmptyList.cons "b" |> NonEmptyList.cons "c" 17 | 18 | Jest.expect(NonEmptyList.head xs).toEqual("c") 19 | Jest.expect(NonEmptyList.length xs).toEqual(3) 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/PersistentVector.test.fs: -------------------------------------------------------------------------------- 1 | module PersistentVectorTests 2 | 3 | open Fable.Jester 4 | open Fable.FastCheck 5 | open Fable.FastCheck.Jest 6 | open FSharpx.Collections 7 | 8 | Jest.test( 9 | "PersistentVector.empty works as expected", 10 | fun () -> 11 | let v = PersistentVector.empty 12 | 13 | Jest.expect(PersistentVector.isEmpty v).toEqual(true) 14 | Jest.expect(PersistentVector.length v).toEqual(0) 15 | ) 16 | 17 | Jest.test( 18 | "PersistentVector.conj works as expected", 19 | fun () -> 20 | let v = PersistentVector.empty |> PersistentVector.conj "a" 21 | 22 | Jest.expect(PersistentVector.nth 0 v).toEqual("a") 23 | 24 | let v = v |> PersistentVector.conj "b" |> PersistentVector.conj "c" 25 | 26 | Jest.expect(PersistentVector.nth 0 v).toEqual("a") 27 | Jest.expect(PersistentVector.nth 1 v).toEqual("b") 28 | Jest.expect(PersistentVector.nth 2 v).toEqual("c") 29 | ) 30 | 31 | Jest.test( 32 | "PersistentVector implements seq as expected", 33 | fun () -> 34 | let v = 35 | PersistentVector.empty 36 | |> PersistentVector.conj 1 37 | |> PersistentVector.conj 4 38 | |> PersistentVector.conj 25 39 | 40 | Jest.expect(Seq.toList v = [ 1; 4; 25 ]).toBe(true) 41 | ) 42 | 43 | Jest.test( 44 | "PersistentVector.map works as expected", 45 | fun () -> 46 | let v = 47 | PersistentVector.ofSeq [ 1..4 ] 48 | |> PersistentVector.map(fun x -> x * 2) 49 | 50 | Jest.expect(Seq.toList v = [ 2; 4; 6; 8 ]).toBe(true) 51 | ) 52 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/Queue.test.fs: -------------------------------------------------------------------------------- 1 | module QueueTests 2 | 3 | open Fable.Jester 4 | open Fable.FastCheck 5 | open Fable.FastCheck.Jest 6 | open FSharpx.Collections 7 | 8 | Jest.test( 9 | "Queue works", 10 | async { 11 | let q = Queue.empty 12 | 13 | Jest.expect(Queue.isEmpty q).toEqual(true) 14 | 15 | let q = Queue.conj "a" q 16 | 17 | Jest.expect(Queue.isEmpty q).toEqual(false) 18 | Jest.expect(Queue.tryHead q).toEqual(Some "a") 19 | 20 | let q = Queue.conj "b" q 21 | 22 | Jest.expect(Queue.tryHead q).toEqual(Some "a") 23 | 24 | let q = Queue.conj "c" q 25 | 26 | Jest 27 | .expect(Queue.tryTail q) 28 | .toEqual(Queue.empty |> Queue.conj "b" |> Queue.conj "c") 29 | 30 | let popped, q = Queue.uncons q 31 | 32 | Jest.expect(popped).toEqual("a") 33 | 34 | let popped, q = Queue.uncons q 35 | 36 | Jest.expect(popped).toEqual("b") 37 | } 38 | ) 39 | -------------------------------------------------------------------------------- /tests/fable/FSharpx.Collections.Tests/paket.references: -------------------------------------------------------------------------------- 1 | group Test.Fable 2 | Fable.Core 3 | Fable.FastCheck 4 | Fable.FastCheck.Jest 5 | Fable.Jester 6 | FSharp.Core 7 | -------------------------------------------------------------------------------- /tests/fable/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ '@babel/preset-env', { targets: { node: 'current' } } ] 4 | ], 5 | plugins: [ 6 | "@babel/plugin-transform-runtime", 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /tests/fable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsharpx-collections", 3 | "version": "0.1.0", 4 | "license": "MIT", 5 | "author": "fsprojects", 6 | "scripts": { 7 | "pretest": "dotnet fable ./FSharpx.Collections.Tests --sourceMaps --outDir ./dist", 8 | "test": "jest" 9 | }, 10 | "dependencies": {}, 11 | "private": true, 12 | "jest": { 13 | "roots": [ 14 | "./dist" 15 | ] 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.15.8", 19 | "@babel/plugin-transform-runtime": "^7.15.8", 20 | "@babel/preset-env": "^7.15.8", 21 | "babel-jest": "^27.3.1", 22 | "jest": "^27.2" 23 | } 24 | } 25 | --------------------------------------------------------------------------------