diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4c4490073..673835b8d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "paket": { - "version": "7.2.1", + "version": "8.0.3", "commands": [ "paket" ] diff --git a/README.md b/README.md index 23df751e2..b332a31ba 100644 --- a/README.md +++ b/README.md @@ -2,35 +2,13 @@ ![MacOS](https://github.com/aardvark-platform/aardvark.base/workflows/MacOS/badge.svg) ![Linux](https://github.com/aardvark-platform/aardvark.base/workflows/Linux/badge.svg) - [![Discord](https://badgen.net/discord/online-members/UyecnhM)](https://discord.gg/UyecnhM) [![license](https://img.shields.io/github/license/aardvark-platform/aardvark.base.svg)](https://github.com/aardvark-platform/aardvark.base/blob/master/LICENSE) [The Aardvark Platform](https://aardvarkians.com/) | -[Platform Wiki](https://github.com/aardvarkplatform/aardvark.docs/wiki) | -[The Platform Walkthrough Repository](https://github.com/aardvark-platform/walkthrough) | -[Gallery](https://github.com/aardvarkplatform/aardvark.docs/wiki/Gallery) | -[Quickstart](https://github.com/aardvarkplatform/aardvark.docs/wiki/Quickstart-Windows) | -[Status](https://github.com/aardvarkplatform/aardvark.docs/wiki/Status) - - -Aardvark.Base consists of multiple platform-independent packages (netstandard2.0) delivering essential tools for visual computing, such as vectors and matrices, as well as many algorithms and data structures. -It is the lowest-level foundation of the open-source [Aardvark Platform](https://github.com/aardvark-platform/aardvark.docs/wiki) for visual computing, real-time graphics and visualization: - -repository | description -:-- | --- | -`aardvark.media` | a unified ELM-style UI framework for both 2D and 3D | -`aardvark.rendering` | powerful incremental rendering engine | -`aardvark.base` | math, geometry, algorithms, data structures | - -The repository `aardvark.base` includes many packages, e.g. - - [Aardvark.Base](https://www.nuget.org/packages/Aardvark.Base/): matrices, vectors, geometry, basic algorithms and data structures. - - [Aardvark.Base.FSharp](https://www.nuget.org/packages/Aardvark.Base.FSharp/): stuff you always need, optimized persistent (e.g. hash maps), ephemeral data structures (e.g. SkipList) as well as spatial data structures (e.g. bounding volume hierarchies). The package also contains an attribute grammar system exposed as an embedded domain specific language. We use it in [aardvark.rendering](https://github.com/aardvark-platform/aardvark.base) for our scene graph system, as described in [Attribute Grammars for Incremental Scene Graph Rendering](https://www.vrvis.at/publications/pdfs/PB-VRVis-2019-004.pdf). - - [Aardvark.Base.Incremental](https://www.nuget.org/packages/Aardvark.Base.Incremental/): incremental data structures similarly but extended to Hammer et al.'s paper [Adapton: Composable, Demand-Driven Incremental Computation](https://www.cs.umd.edu/~hammer/adapton/). Additionally to modifiable cells, we have more sophisticated optimized incremental data structures such as adaptive sets, maps etc. and computation expression builders to conveniently work with. - - [Aardvark.Base.Runtime](https://www.nuget.org/packages/Aardvark.Base.Runtime/): Crazy tools such as an AMD64 assembler used for incremental Just In Time Compilation as used in [aardvark.rendering](https://github.com/aardvark-platform/aardvark.base) - - [Aardvark.Data.Vrml97](https://www.nuget.org/packages/Aardvark.Data.Vrml97/): legacy Vrml97 parser - - [Aardvark.Geometry](https://www.nuget.org/packages/Aardvark.Geometry/): currently a rather small set of F# geometry tools. Most functionality regarding geometry lives in base and [algodat](https://github.com/aardvark-platform/aardvark.algodat) +[Gallery](https://github.com/aardvark-platform/aardvark.docs/wiki/Gallery) | +[Packages&Repositories](https://github.com/aardvark-platform/aardvark.docs/wiki/Packages-and-Repositories) -All packages are distributed under the [Apache 2.0 license](https://github.com/aardvark-platform/aardvark.base/blob/master/LICENSE). +Aardvark.Base is the foundation of the open-source [Aardvark Platform](https://github.com/aardvark-platform) for visual computing, real-time graphics, and visualization. It consists of multiple platform-independent packages (netstandard2.0) that deliver essential tools for visual computing, including vectors, matrices, algorithms, data structures, or image loaders. Supported platforms are windows, linux, macOS. -For support please have a look at [Aardvarkians](https://aardvarkians.com). +You can find demos and code in the Gallery and Packages&Repositories links above. [This repository's wiki](https://github.com/aardvark-platform/aardvark.base/wiki) hosts technical documentation. For more information, please refer to the [aardvark.docs wiki](https://github.com/aardvark-platform/aardvark.docs/wiki). diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2cb2c6926..06a1104f1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,29 @@ +### 5.2.29 +* Fixed color parsing to be independent of the current culture (regression in 5.2.27) +* Added more value variants for Dictionary, Dict, and SymbolDict functions + +### 5.2.28 +* Added Brewer color schemes +* Added String.replace +* Added IDictionary.TryPop overload with output argument + +### 5.2.27 +* Improved and fixed RangeSet implementation +* Added utilities for ValueOption +* Added more ArraySegment utilities +* [Text] Fixed NestedBracketSplitCount +* [Color] Fixed overflow issue in C4ui constructors +* [Color] Improved color parsing, now supports hexadecimal color strings +* [PixImage] Fixed file handle leak in Windows Media loader +* [MapExt] Added value variants of some operations + ### 5.2.26 * LinearRegression3d: made fields public +* Added getLines, normalizeLineEndings, withLineNumbers in String module +* Fixed FGetValueOrDefault dictionary extension +* Added value variants for Dict functions +* Added F# ArraySegment utilities +* [Introspection] Tidied up exception reporting ### 5.2.25 * Fixed issue with PixImageMipMap loading diff --git a/paket.dependencies b/paket.dependencies index 0d8b738c0..639c21276 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -33,13 +33,13 @@ group Test source https://api.nuget.org/v3/index.json nuget NUnit ~> 3.13.3 - nuget FsUnit ~> 5.2.0 + nuget FsUnit ~> 5.4.0 nuget FsCheck ~> 2.16.4 nuget FsCheck.NUnit ~> 2.16.4 - nuget NUnit3TestAdapter ~> 4.4.2 - nuget Microsoft.NET.Test.Sdk ~> 17.5.0 - nuget Expecto ~> 9.0.4 - nuget Expecto.FsCheck ~> 9.0.4 - nuget YoloDev.Expecto.TestSdk ~> 0.13.3 + nuget NUnit3TestAdapter ~> 4.5.0 + nuget Microsoft.NET.Test.Sdk ~> 17.7.2 + nuget Expecto ~> 10.1.0 + nuget Expecto.FsCheck ~> 10.1.0 + nuget YoloDev.Expecto.TestSdk ~> 0.14.2 - nuget BenchmarkDotNet ~> 0.13.1 \ No newline at end of file + nuget BenchmarkDotNet ~> 0.13.9 \ No newline at end of file diff --git a/paket.lock b/paket.lock index f5969f495..834221633 100644 --- a/paket.lock +++ b/paket.lock @@ -201,33 +201,33 @@ GROUP Test RESTRICTION: || (== net6.0) (== net6.0-windows7.0) NUGET remote: https://api.nuget.org/v3/index.json - BenchmarkDotNet (0.13.5) - BenchmarkDotNet.Annotations (>= 0.13.5) - CommandLineParser (>= 2.4.3) + BenchmarkDotNet (0.13.9) + BenchmarkDotNet.Annotations (>= 0.13.9) + CommandLineParser (>= 2.9.1) Gee.External.Capstone (>= 2.3) Iced (>= 1.17) - Microsoft.CodeAnalysis.CSharp (>= 3.0) + Microsoft.CodeAnalysis.CSharp (>= 4.1) Microsoft.Diagnostics.Runtime (>= 2.2.332302) Microsoft.Diagnostics.Tracing.TraceEvent (>= 3.0.2) Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) - Perfolizer (>= 0.2.1) - System.Management (>= 6.0) - BenchmarkDotNet.Annotations (0.13.5) + Perfolizer (0.2.1) + System.Management (>= 5.0) + BenchmarkDotNet.Annotations (0.13.9) CommandLineParser (2.9.1) - Expecto (9.0.4) - FSharp.Core (>= 4.6) - Mono.Cecil (>= 0.11.3) - Expecto.FsCheck (9.0.4) - Expecto (>= 9.0.4) - FsCheck (>= 2.14.3) - FsCheck (2.16.5) + Expecto (10.1) + FSharp.Core (>= 7.0.200) + Mono.Cecil (>= 0.11.4 < 1.0) + Expecto.FsCheck (10.1) + Expecto (>= 10.1) + FsCheck (>= 2.16.5 < 3.0) + FsCheck (2.16.6) FSharp.Core (>= 4.2.3) - FsCheck.NUnit (2.16.5) - FsCheck (2.16.5) + FsCheck.NUnit (2.16.6) + FsCheck (2.16.6) NUnit (>= 3.13.1 < 4.0) FSharp.Core (7.0.200) - FsUnit (5.2) - FSharp.Core (>= 6.0.7) + FsUnit (5.4) + FSharp.Core (>= 5.0.2) NUnit (>= 3.13.3 < 3.14) Gee.External.Capstone (2.3) Iced (1.17) @@ -243,7 +243,7 @@ NUGET System.Threading.Tasks.Extensions (>= 4.5.4) Microsoft.CodeAnalysis.CSharp (4.2) Microsoft.CodeAnalysis.Common (4.2) - Microsoft.CodeCoverage (17.5) + Microsoft.CodeCoverage (17.7.2) Microsoft.Diagnostics.NETCore.Client (0.2.327302) Microsoft.Bcl.AsyncInterfaces (>= 1.1) Microsoft.Extensions.Logging (>= 2.1.1) @@ -270,24 +270,24 @@ NUGET Microsoft.Extensions.Primitives (>= 6.0) Microsoft.Extensions.Primitives (6.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - Microsoft.NET.Test.Sdk (17.5) - Microsoft.CodeCoverage (>= 17.5) - Microsoft.TestPlatform.TestHost (>= 17.5) + Microsoft.NET.Test.Sdk (17.7.2) + Microsoft.CodeCoverage (>= 17.7.2) + Microsoft.TestPlatform.TestHost (>= 17.7.2) Microsoft.NETCore.Platforms (6.0.3) - Microsoft.TestPlatform.ObjectModel (17.5) - NuGet.Frameworks (>= 5.11) + Microsoft.TestPlatform.ObjectModel (17.7.2) + NuGet.Frameworks (>= 6.5) System.Reflection.Metadata (>= 1.6) - Microsoft.TestPlatform.TestHost (17.5) - Microsoft.TestPlatform.ObjectModel (>= 17.5) + Microsoft.TestPlatform.TestHost (17.7.2) + Microsoft.TestPlatform.ObjectModel (>= 17.7.2) Newtonsoft.Json (>= 13.0.1) Mono.Cecil (0.11.4) NETStandard.Library (2.0.3) Microsoft.NETCore.Platforms (>= 1.1) Newtonsoft.Json (13.0.1) - NuGet.Frameworks (6.2) + NuGet.Frameworks (6.7) NUnit (3.13.3) NETStandard.Library (>= 2.0) - NUnit3TestAdapter (4.4.2) + NUnit3TestAdapter (4.5) Perfolizer (0.2.1) System.Memory (>= 4.5.3) System.CodeDom (6.0) @@ -304,7 +304,7 @@ NUGET System.Text.Encoding.CodePages (6.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Threading.Tasks.Extensions (4.5.4) - YoloDev.Expecto.TestSdk (0.13.3) - Expecto (>= 9.0 < 10.0) - FSharp.Core (>= 4.6.2) + YoloDev.Expecto.TestSdk (0.14.2) + Expecto (>= 10.0 < 11.0) + FSharp.Core (>= 7.0.200) System.Collections.Immutable (>= 6.0) diff --git a/src/Aardvark.Base.FSharp/Aardvark.Base.FSharp.fsproj b/src/Aardvark.Base.FSharp/Aardvark.Base.FSharp.fsproj index 24d5d2544..3daf34648 100644 --- a/src/Aardvark.Base.FSharp/Aardvark.Base.FSharp.fsproj +++ b/src/Aardvark.Base.FSharp/Aardvark.Base.FSharp.fsproj @@ -15,23 +15,31 @@ ..\..\bin\Release - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -43,6 +51,8 @@ + + @@ -50,6 +60,9 @@ + + + @@ -67,20 +80,14 @@ - - - - - - - - + + diff --git a/src/Aardvark.Base.FSharp/Color/ColorBrewer.fs b/src/Aardvark.Base.FSharp/Color/ColorBrewer.fs new file mode 100644 index 000000000..d0abca79e --- /dev/null +++ b/src/Aardvark.Base.FSharp/Color/ColorBrewer.fs @@ -0,0 +1,135 @@ +namespace Aardvark.Base + +open System +open System.Collections +open System.Collections.Generic + +/// Brewer color schemes designed for choropleth map visualizations. +module ColorBrewer = + + [] + type PaletteUsage = + | None = 0 + + /// Does not confuse people with red-green color blindness. + | ColorBlind = 1 + + /// Suitable for desktop color printing. + | Print = 2 + + /// Suitable for viewing on a laptop LCD display. + /// Small, portable LCD monitors tend to wash-out colors which results in noticeable differences from computer-to-computer. + | LCD = 4 + + /// Withstands black and white photocopying. + /// Diverging schemes can not be photocopied successfully. + /// Differences in lightness should be preserved with sequential schemes. + | PhotoCopy = 8 + + [] + type Palette = + { + /// The color values of the palette. + Colors : C3b[] + + /// Usage properties of the palette. + Usage : PaletteUsage + } + + member inline x.Length = + x.Colors.Length + + member inline x.Item (index : int) = + x.Colors.[index] + + interface IEnumerable with + member x.GetEnumerator() = x.Colors.GetEnumerator() + + interface IEnumerable with + member x.GetEnumerator() = (x.Colors :> IEnumerable).GetEnumerator() + + + type SchemeType = + + /// Diverging schemes put equal emphasis on mid-range critical values and extremes at both ends of the data range. + /// The critical class or break in the middle of the legend is emphasized with light colors and low and high extremes are + /// emphasized with dark colors that have contrasting hues. + | Diverging = 0 + + /// Qualitative schemes do not imply magnitude differences between legend classes, and hues are used to + /// create the primary visual differences between classes. Qualitative schemes are best suited to representing nominal or categorical data. + | Qualitative = 1 + + /// Sequential schemes are suited to ordered data that progress from low to high. + /// Lightness steps dominate the look of these schemes, with light colors for low data values to dark colors for high data values. + | Sequential = 2 + + /// A color scheme containing palettes of various size. + [] + type Scheme = + { + /// Name of the scheme. + Name : Symbol + + /// Type of the scheme. + Type : SchemeType + + /// The palettes of the scheme according to their size. + Palettes : MapExt + } + + /// Returns whether the scheme is empty (i.e. has no palettes). + member inline x.IsEmpty = + x.Palettes.IsEmpty + + /// Size of the smallest palette. + member inline x.MinSize = + x.Palettes.TryMinKeyV |> ValueOption.defaultValue 0 + + /// Size of the largest palette. + member inline x.MaxSize = + x.Palettes.TryMaxKeyV |> ValueOption.defaultValue 0 + + /// Gets the palette with the given size. + /// If the scheme is not defined for the requested size, gets the next larger palette. + /// Throws an exception if the requested size is greater than the maximum size. + member inline x.Item (requestedSize : int) = + match x.Palettes |> MapExt.neighboursV requestedSize with + | struct (_, ValueSome (struct (_, palette)), _) + | struct (_, _, ValueSome (struct (_, palette))) -> + palette + + | struct (ValueSome (struct (max, _)), _, _) -> + raise <| ArgumentOutOfRangeException("requestedSize", $"Scheme {x.Name} has a maximum palette size of {max} (requested {requestedSize}).") + + | struct (ValueNone, ValueNone, ValueNone) -> + raise <| ArgumentException($"Scheme {x.Name} is empty.") + + [] + module Scheme = + + /// Returns whether the scheme is empty (i.e. has no palettes). + let inline isEmpty (scheme : Scheme) = + scheme.IsEmpty + + /// Return the size of the smallest palette for the given scheme. + let inline minSize (scheme : Scheme) = + scheme.MinSize + + /// Return the size of the largest palette for the given scheme. + let inline maxSize (scheme : Scheme) = + scheme.MaxSize + + /// Gets the palette with the given size. + /// If the scheme is not defined for the requested size, gets the next larger palette. + /// Throws an exception if the requested size is greater than the maximum size. + let inline getPalette (requestedSize : int) (scheme : Scheme) = + scheme.[requestedSize] + + /// Returns a new scheme containing only the palettes for which the predicate returns true. + let inline filter (predicate : Palette -> bool) (scheme : Scheme) = + { scheme with Palettes = scheme.Palettes |> MapExt.filter (fun _ -> predicate) } + + /// Returns a new scheme containing only the palettes with the given usage flags. + let inline filterUsage (usage : PaletteUsage) (scheme : Scheme) = + scheme |> filter (fun p -> p.Usage &&& usage = usage) \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fs b/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fs new file mode 100644 index 000000000..2a22cc682 --- /dev/null +++ b/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fs @@ -0,0 +1,1924 @@ +namespace Aardvark.Base + +[] +module ColorBrewerSchemes = + open ColorBrewer + + /// Brewer color schemes designed for choropleth map visualizations. + module ColorBrewer = + + module Scheme = + + /// Diverging schemes put equal emphasis on mid-range critical values and extremes at both ends of the data range. + /// The critical class or break in the middle of the legend is emphasized with light colors and low and high extremes are + /// emphasized with dark colors that have contrasting hues. + module Diverging = + + let Spectral = + { + Name = Sym.ofString "Spectral" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(252uy,141uy,89uy); C3b(255uy,255uy,191uy); C3b(153uy,213uy,148uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(171uy,221uy,164uy); C3b(43uy,131uy,186uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.PhotoCopy + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(255uy,255uy,191uy); C3b(171uy,221uy,164uy) + C3b(43uy,131uy,186uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(213uy,62uy,79uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,139uy); C3b(230uy,245uy,152uy) + C3b(153uy,213uy,148uy); C3b(50uy,136uy,189uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(213uy,62uy,79uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,139uy); C3b(255uy,255uy,191uy) + C3b(230uy,245uy,152uy); C3b(153uy,213uy,148uy); C3b(50uy,136uy,189uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(213uy,62uy,79uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,139uy) + C3b(230uy,245uy,152uy); C3b(171uy,221uy,164uy); C3b(102uy,194uy,165uy); C3b(50uy,136uy,189uy) |] + } + + 9, { + Usage = PaletteUsage.None + Colors = [| C3b(213uy,62uy,79uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,139uy) + C3b(255uy,255uy,191uy); C3b(230uy,245uy,152uy); C3b(171uy,221uy,164uy); C3b(102uy,194uy,165uy) + C3b(50uy,136uy,189uy) |] + } + + 10, { + Usage = PaletteUsage.None + Colors = [| C3b(158uy,1uy,66uy); C3b(213uy,62uy,79uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,139uy); C3b(230uy,245uy,152uy); C3b(171uy,221uy,164uy); C3b(102uy,194uy,165uy) + C3b(50uy,136uy,189uy); C3b(94uy,79uy,162uy) |] + } + + 11, { + Usage = PaletteUsage.None + Colors = [| C3b(158uy,1uy,66uy); C3b(213uy,62uy,79uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,139uy); C3b(255uy,255uy,191uy); C3b(230uy,245uy,152uy); C3b(171uy,221uy,164uy) + C3b(102uy,194uy,165uy); C3b(50uy,136uy,189uy); C3b(94uy,79uy,162uy) |] + } + + ] + } + + let RdYlGn = + { + Name = Sym.ofString "RdYlGn" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(252uy,141uy,89uy); C3b(255uy,255uy,191uy); C3b(145uy,207uy,96uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(166uy,217uy,106uy); C3b(26uy,150uy,65uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(255uy,255uy,191uy); C3b(166uy,217uy,106uy) + C3b(26uy,150uy,65uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(215uy,48uy,39uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,139uy); C3b(217uy,239uy,139uy) + C3b(145uy,207uy,96uy); C3b(26uy,152uy,80uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(215uy,48uy,39uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,139uy); C3b(255uy,255uy,191uy) + C3b(217uy,239uy,139uy); C3b(145uy,207uy,96uy); C3b(26uy,152uy,80uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,139uy) + C3b(217uy,239uy,139uy); C3b(166uy,217uy,106uy); C3b(102uy,189uy,99uy); C3b(26uy,152uy,80uy) |] + } + + 9, { + Usage = PaletteUsage.None + Colors = [| C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,139uy) + C3b(255uy,255uy,191uy); C3b(217uy,239uy,139uy); C3b(166uy,217uy,106uy); C3b(102uy,189uy,99uy) + C3b(26uy,152uy,80uy) |] + } + + 10, { + Usage = PaletteUsage.None + Colors = [| C3b(165uy,0uy,38uy); C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,139uy); C3b(217uy,239uy,139uy); C3b(166uy,217uy,106uy); C3b(102uy,189uy,99uy) + C3b(26uy,152uy,80uy); C3b(0uy,104uy,55uy) |] + } + + 11, { + Usage = PaletteUsage.None + Colors = [| C3b(165uy,0uy,38uy); C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,139uy); C3b(255uy,255uy,191uy); C3b(217uy,239uy,139uy); C3b(166uy,217uy,106uy) + C3b(102uy,189uy,99uy); C3b(26uy,152uy,80uy); C3b(0uy,104uy,55uy) |] + } + + ] + } + + let RdBu = + { + Name = Sym.ofString "RdBu" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(239uy,138uy,98uy); C3b(247uy,247uy,247uy); C3b(103uy,169uy,207uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(202uy,0uy,32uy); C3b(244uy,165uy,130uy); C3b(146uy,197uy,222uy); C3b(5uy,113uy,176uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(202uy,0uy,32uy); C3b(244uy,165uy,130uy); C3b(247uy,247uy,247uy); C3b(146uy,197uy,222uy) + C3b(5uy,113uy,176uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(178uy,24uy,43uy); C3b(239uy,138uy,98uy); C3b(253uy,219uy,199uy); C3b(209uy,229uy,240uy) + C3b(103uy,169uy,207uy); C3b(33uy,102uy,172uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(178uy,24uy,43uy); C3b(239uy,138uy,98uy); C3b(253uy,219uy,199uy); C3b(247uy,247uy,247uy) + C3b(209uy,229uy,240uy); C3b(103uy,169uy,207uy); C3b(33uy,102uy,172uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy); C3b(253uy,219uy,199uy) + C3b(209uy,229uy,240uy); C3b(146uy,197uy,222uy); C3b(67uy,147uy,195uy); C3b(33uy,102uy,172uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy); C3b(253uy,219uy,199uy) + C3b(247uy,247uy,247uy); C3b(209uy,229uy,240uy); C3b(146uy,197uy,222uy); C3b(67uy,147uy,195uy) + C3b(33uy,102uy,172uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(103uy,0uy,31uy); C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy) + C3b(253uy,219uy,199uy); C3b(209uy,229uy,240uy); C3b(146uy,197uy,222uy); C3b(67uy,147uy,195uy) + C3b(33uy,102uy,172uy); C3b(5uy,48uy,97uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(103uy,0uy,31uy); C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy) + C3b(253uy,219uy,199uy); C3b(247uy,247uy,247uy); C3b(209uy,229uy,240uy); C3b(146uy,197uy,222uy) + C3b(67uy,147uy,195uy); C3b(33uy,102uy,172uy); C3b(5uy,48uy,97uy) |] + } + + ] + } + + let PiYG = + { + Name = Sym.ofString "PiYG" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(233uy,163uy,201uy); C3b(247uy,247uy,247uy); C3b(161uy,215uy,106uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(208uy,28uy,139uy); C3b(241uy,182uy,218uy); C3b(184uy,225uy,134uy); C3b(77uy,172uy,38uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(208uy,28uy,139uy); C3b(241uy,182uy,218uy); C3b(247uy,247uy,247uy); C3b(184uy,225uy,134uy) + C3b(77uy,172uy,38uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(197uy,27uy,125uy); C3b(233uy,163uy,201uy); C3b(253uy,224uy,239uy); C3b(230uy,245uy,208uy) + C3b(161uy,215uy,106uy); C3b(77uy,146uy,33uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(197uy,27uy,125uy); C3b(233uy,163uy,201uy); C3b(253uy,224uy,239uy); C3b(247uy,247uy,247uy) + C3b(230uy,245uy,208uy); C3b(161uy,215uy,106uy); C3b(77uy,146uy,33uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(197uy,27uy,125uy); C3b(222uy,119uy,174uy); C3b(241uy,182uy,218uy); C3b(253uy,224uy,239uy) + C3b(230uy,245uy,208uy); C3b(184uy,225uy,134uy); C3b(127uy,188uy,65uy); C3b(77uy,146uy,33uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(197uy,27uy,125uy); C3b(222uy,119uy,174uy); C3b(241uy,182uy,218uy); C3b(253uy,224uy,239uy) + C3b(247uy,247uy,247uy); C3b(230uy,245uy,208uy); C3b(184uy,225uy,134uy); C3b(127uy,188uy,65uy) + C3b(77uy,146uy,33uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(142uy,1uy,82uy); C3b(197uy,27uy,125uy); C3b(222uy,119uy,174uy); C3b(241uy,182uy,218uy) + C3b(253uy,224uy,239uy); C3b(230uy,245uy,208uy); C3b(184uy,225uy,134uy); C3b(127uy,188uy,65uy) + C3b(77uy,146uy,33uy); C3b(39uy,100uy,25uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(142uy,1uy,82uy); C3b(197uy,27uy,125uy); C3b(222uy,119uy,174uy); C3b(241uy,182uy,218uy) + C3b(253uy,224uy,239uy); C3b(247uy,247uy,247uy); C3b(230uy,245uy,208uy); C3b(184uy,225uy,134uy) + C3b(127uy,188uy,65uy); C3b(77uy,146uy,33uy); C3b(39uy,100uy,25uy) |] + } + + ] + } + + let PRGn = + { + Name = Sym.ofString "PRGn" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(175uy,141uy,195uy); C3b(247uy,247uy,247uy); C3b(127uy,191uy,123uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(123uy,50uy,148uy); C3b(194uy,165uy,207uy); C3b(166uy,219uy,160uy); C3b(0uy,136uy,55uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(123uy,50uy,148uy); C3b(194uy,165uy,207uy); C3b(247uy,247uy,247uy); C3b(166uy,219uy,160uy) + C3b(0uy,136uy,55uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(118uy,42uy,131uy); C3b(175uy,141uy,195uy); C3b(231uy,212uy,232uy); C3b(217uy,240uy,211uy) + C3b(127uy,191uy,123uy); C3b(27uy,120uy,55uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(118uy,42uy,131uy); C3b(175uy,141uy,195uy); C3b(231uy,212uy,232uy); C3b(247uy,247uy,247uy) + C3b(217uy,240uy,211uy); C3b(127uy,191uy,123uy); C3b(27uy,120uy,55uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(118uy,42uy,131uy); C3b(153uy,112uy,171uy); C3b(194uy,165uy,207uy); C3b(231uy,212uy,232uy) + C3b(217uy,240uy,211uy); C3b(166uy,219uy,160uy); C3b(90uy,174uy,97uy); C3b(27uy,120uy,55uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(118uy,42uy,131uy); C3b(153uy,112uy,171uy); C3b(194uy,165uy,207uy); C3b(231uy,212uy,232uy) + C3b(247uy,247uy,247uy); C3b(217uy,240uy,211uy); C3b(166uy,219uy,160uy); C3b(90uy,174uy,97uy) + C3b(27uy,120uy,55uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(64uy,0uy,75uy); C3b(118uy,42uy,131uy); C3b(153uy,112uy,171uy); C3b(194uy,165uy,207uy) + C3b(231uy,212uy,232uy); C3b(217uy,240uy,211uy); C3b(166uy,219uy,160uy); C3b(90uy,174uy,97uy) + C3b(27uy,120uy,55uy); C3b(0uy,68uy,27uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(64uy,0uy,75uy); C3b(118uy,42uy,131uy); C3b(153uy,112uy,171uy); C3b(194uy,165uy,207uy) + C3b(231uy,212uy,232uy); C3b(247uy,247uy,247uy); C3b(217uy,240uy,211uy); C3b(166uy,219uy,160uy) + C3b(90uy,174uy,97uy); C3b(27uy,120uy,55uy); C3b(0uy,68uy,27uy) |] + } + + ] + } + + let RdYlBu = + { + Name = Sym.ofString "RdYlBu" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(252uy,141uy,89uy); C3b(255uy,255uy,191uy); C3b(145uy,191uy,219uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(171uy,217uy,233uy); C3b(44uy,123uy,182uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(215uy,25uy,28uy); C3b(253uy,174uy,97uy); C3b(255uy,255uy,191uy); C3b(171uy,217uy,233uy) + C3b(44uy,123uy,182uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(215uy,48uy,39uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,144uy); C3b(224uy,243uy,248uy) + C3b(145uy,191uy,219uy); C3b(69uy,117uy,180uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(215uy,48uy,39uy); C3b(252uy,141uy,89uy); C3b(254uy,224uy,144uy); C3b(255uy,255uy,191uy) + C3b(224uy,243uy,248uy); C3b(145uy,191uy,219uy); C3b(69uy,117uy,180uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,144uy) + C3b(224uy,243uy,248uy); C3b(171uy,217uy,233uy); C3b(116uy,173uy,209uy); C3b(69uy,117uy,180uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy); C3b(254uy,224uy,144uy) + C3b(255uy,255uy,191uy); C3b(224uy,243uy,248uy); C3b(171uy,217uy,233uy); C3b(116uy,173uy,209uy) + C3b(69uy,117uy,180uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(165uy,0uy,38uy); C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,144uy); C3b(224uy,243uy,248uy); C3b(171uy,217uy,233uy); C3b(116uy,173uy,209uy) + C3b(69uy,117uy,180uy); C3b(49uy,54uy,149uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(165uy,0uy,38uy); C3b(215uy,48uy,39uy); C3b(244uy,109uy,67uy); C3b(253uy,174uy,97uy) + C3b(254uy,224uy,144uy); C3b(255uy,255uy,191uy); C3b(224uy,243uy,248uy); C3b(171uy,217uy,233uy) + C3b(116uy,173uy,209uy); C3b(69uy,117uy,180uy); C3b(49uy,54uy,149uy) |] + } + + ] + } + + let BrBG = + { + Name = Sym.ofString "BrBG" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(216uy,179uy,101uy); C3b(245uy,245uy,245uy); C3b(90uy,180uy,172uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,97uy,26uy); C3b(223uy,194uy,125uy); C3b(128uy,205uy,193uy); C3b(1uy,133uy,113uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,97uy,26uy); C3b(223uy,194uy,125uy); C3b(245uy,245uy,245uy); C3b(128uy,205uy,193uy) + C3b(1uy,133uy,113uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(140uy,81uy,10uy); C3b(216uy,179uy,101uy); C3b(246uy,232uy,195uy); C3b(199uy,234uy,229uy) + C3b(90uy,180uy,172uy); C3b(1uy,102uy,94uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(140uy,81uy,10uy); C3b(216uy,179uy,101uy); C3b(246uy,232uy,195uy); C3b(245uy,245uy,245uy) + C3b(199uy,234uy,229uy); C3b(90uy,180uy,172uy); C3b(1uy,102uy,94uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(140uy,81uy,10uy); C3b(191uy,129uy,45uy); C3b(223uy,194uy,125uy); C3b(246uy,232uy,195uy) + C3b(199uy,234uy,229uy); C3b(128uy,205uy,193uy); C3b(53uy,151uy,143uy); C3b(1uy,102uy,94uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(140uy,81uy,10uy); C3b(191uy,129uy,45uy); C3b(223uy,194uy,125uy); C3b(246uy,232uy,195uy) + C3b(245uy,245uy,245uy); C3b(199uy,234uy,229uy); C3b(128uy,205uy,193uy); C3b(53uy,151uy,143uy) + C3b(1uy,102uy,94uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(84uy,48uy,5uy); C3b(140uy,81uy,10uy); C3b(191uy,129uy,45uy); C3b(223uy,194uy,125uy) + C3b(246uy,232uy,195uy); C3b(199uy,234uy,229uy); C3b(128uy,205uy,193uy); C3b(53uy,151uy,143uy) + C3b(1uy,102uy,94uy); C3b(0uy,60uy,48uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(84uy,48uy,5uy); C3b(140uy,81uy,10uy); C3b(191uy,129uy,45uy); C3b(223uy,194uy,125uy) + C3b(246uy,232uy,195uy); C3b(245uy,245uy,245uy); C3b(199uy,234uy,229uy); C3b(128uy,205uy,193uy) + C3b(53uy,151uy,143uy); C3b(1uy,102uy,94uy); C3b(0uy,60uy,48uy) |] + } + + ] + } + + let RdGy = + { + Name = Sym.ofString "RdGy" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(239uy,138uy,98uy); C3b(255uy,255uy,255uy); C3b(153uy,153uy,153uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(202uy,0uy,32uy); C3b(244uy,165uy,130uy); C3b(186uy,186uy,186uy); C3b(64uy,64uy,64uy) |] + } + + 5, { + Usage = PaletteUsage.Print + Colors = [| C3b(202uy,0uy,32uy); C3b(244uy,165uy,130uy); C3b(255uy,255uy,255uy); C3b(186uy,186uy,186uy) + C3b(64uy,64uy,64uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(178uy,24uy,43uy); C3b(239uy,138uy,98uy); C3b(253uy,219uy,199uy); C3b(224uy,224uy,224uy) + C3b(153uy,153uy,153uy); C3b(77uy,77uy,77uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(178uy,24uy,43uy); C3b(239uy,138uy,98uy); C3b(253uy,219uy,199uy); C3b(255uy,255uy,255uy) + C3b(224uy,224uy,224uy); C3b(153uy,153uy,153uy); C3b(77uy,77uy,77uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy); C3b(253uy,219uy,199uy) + C3b(224uy,224uy,224uy); C3b(186uy,186uy,186uy); C3b(135uy,135uy,135uy); C3b(77uy,77uy,77uy) |] + } + + 9, { + Usage = PaletteUsage.None + Colors = [| C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy); C3b(253uy,219uy,199uy) + C3b(255uy,255uy,255uy); C3b(224uy,224uy,224uy); C3b(186uy,186uy,186uy); C3b(135uy,135uy,135uy) + C3b(77uy,77uy,77uy) |] + } + + 10, { + Usage = PaletteUsage.None + Colors = [| C3b(103uy,0uy,31uy); C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy) + C3b(253uy,219uy,199uy); C3b(224uy,224uy,224uy); C3b(186uy,186uy,186uy); C3b(135uy,135uy,135uy) + C3b(77uy,77uy,77uy); C3b(26uy,26uy,26uy) |] + } + + 11, { + Usage = PaletteUsage.None + Colors = [| C3b(103uy,0uy,31uy); C3b(178uy,24uy,43uy); C3b(214uy,96uy,77uy); C3b(244uy,165uy,130uy) + C3b(253uy,219uy,199uy); C3b(255uy,255uy,255uy); C3b(224uy,224uy,224uy); C3b(186uy,186uy,186uy) + C3b(135uy,135uy,135uy); C3b(77uy,77uy,77uy); C3b(26uy,26uy,26uy) |] + } + + ] + } + + let PuOr = + { + Name = Sym.ofString "PuOr" + Type = SchemeType.Diverging + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(241uy,163uy,64uy); C3b(247uy,247uy,247uy); C3b(153uy,142uy,195uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(230uy,97uy,1uy); C3b(253uy,184uy,99uy); C3b(178uy,171uy,210uy); C3b(94uy,60uy,153uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(230uy,97uy,1uy); C3b(253uy,184uy,99uy); C3b(247uy,247uy,247uy); C3b(178uy,171uy,210uy) + C3b(94uy,60uy,153uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(179uy,88uy,6uy); C3b(241uy,163uy,64uy); C3b(254uy,224uy,182uy); C3b(216uy,218uy,235uy) + C3b(153uy,142uy,195uy); C3b(84uy,39uy,136uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(179uy,88uy,6uy); C3b(241uy,163uy,64uy); C3b(254uy,224uy,182uy); C3b(247uy,247uy,247uy) + C3b(216uy,218uy,235uy); C3b(153uy,142uy,195uy); C3b(84uy,39uy,136uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(179uy,88uy,6uy); C3b(224uy,130uy,20uy); C3b(253uy,184uy,99uy); C3b(254uy,224uy,182uy) + C3b(216uy,218uy,235uy); C3b(178uy,171uy,210uy); C3b(128uy,115uy,172uy); C3b(84uy,39uy,136uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(179uy,88uy,6uy); C3b(224uy,130uy,20uy); C3b(253uy,184uy,99uy); C3b(254uy,224uy,182uy) + C3b(247uy,247uy,247uy); C3b(216uy,218uy,235uy); C3b(178uy,171uy,210uy); C3b(128uy,115uy,172uy) + C3b(84uy,39uy,136uy) |] + } + + 10, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(127uy,59uy,8uy); C3b(179uy,88uy,6uy); C3b(224uy,130uy,20uy); C3b(253uy,184uy,99uy) + C3b(254uy,224uy,182uy); C3b(216uy,218uy,235uy); C3b(178uy,171uy,210uy); C3b(128uy,115uy,172uy) + C3b(84uy,39uy,136uy); C3b(45uy,0uy,75uy) |] + } + + 11, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(127uy,59uy,8uy); C3b(179uy,88uy,6uy); C3b(224uy,130uy,20uy); C3b(253uy,184uy,99uy) + C3b(254uy,224uy,182uy); C3b(247uy,247uy,247uy); C3b(216uy,218uy,235uy); C3b(178uy,171uy,210uy) + C3b(128uy,115uy,172uy); C3b(84uy,39uy,136uy); C3b(45uy,0uy,75uy) |] + } + + ] + } + + /// Qualitative schemes do not imply magnitude differences between legend classes, and hues are used to + /// create the primary visual differences between classes. Qualitative schemes are best suited to representing nominal or categorical data. + module Qualitative = + + let Set2 = + { + Name = Sym.ofString "Set2" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy); C3b(231uy,138uy,195uy) |] + } + + 5, { + Usage = PaletteUsage.Print + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy); C3b(231uy,138uy,195uy) + C3b(166uy,216uy,84uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy); C3b(231uy,138uy,195uy) + C3b(166uy,216uy,84uy); C3b(255uy,217uy,47uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy); C3b(231uy,138uy,195uy) + C3b(166uy,216uy,84uy); C3b(255uy,217uy,47uy); C3b(229uy,196uy,148uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(102uy,194uy,165uy); C3b(252uy,141uy,98uy); C3b(141uy,160uy,203uy); C3b(231uy,138uy,195uy) + C3b(166uy,216uy,84uy); C3b(255uy,217uy,47uy); C3b(229uy,196uy,148uy); C3b(179uy,179uy,179uy) |] + } + + ] + } + + let Accent = + { + Name = Sym.ofString "Accent" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy); C3b(255uy,255uy,153uy) |] + } + + 5, { + Usage = PaletteUsage.LCD + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy); C3b(255uy,255uy,153uy) + C3b(56uy,108uy,176uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy); C3b(255uy,255uy,153uy) + C3b(56uy,108uy,176uy); C3b(240uy,2uy,127uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy); C3b(255uy,255uy,153uy) + C3b(56uy,108uy,176uy); C3b(240uy,2uy,127uy); C3b(191uy,91uy,23uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(127uy,201uy,127uy); C3b(190uy,174uy,212uy); C3b(253uy,192uy,134uy); C3b(255uy,255uy,153uy) + C3b(56uy,108uy,176uy); C3b(240uy,2uy,127uy); C3b(191uy,91uy,23uy); C3b(102uy,102uy,102uy) |] + } + + ] + } + + let Set1 = + { + Name = Sym.ofString "Set1" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) + C3b(255uy,127uy,0uy) |] + } + + 6, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) + C3b(255uy,127uy,0uy); C3b(255uy,255uy,51uy) |] + } + + 7, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) + C3b(255uy,127uy,0uy); C3b(255uy,255uy,51uy); C3b(166uy,86uy,40uy) |] + } + + 8, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) + C3b(255uy,127uy,0uy); C3b(255uy,255uy,51uy); C3b(166uy,86uy,40uy); C3b(247uy,129uy,191uy) |] + } + + 9, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(228uy,26uy,28uy); C3b(55uy,126uy,184uy); C3b(77uy,175uy,74uy); C3b(152uy,78uy,163uy) + C3b(255uy,127uy,0uy); C3b(255uy,255uy,51uy); C3b(166uy,86uy,40uy); C3b(247uy,129uy,191uy) + C3b(153uy,153uy,153uy) |] + } + + ] + } + + let Set3 = + { + Name = Sym.ofString "Set3" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy) |] + } + + 6, { + Usage = PaletteUsage.Print + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy) |] + } + + 7, { + Usage = PaletteUsage.Print + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy) |] + } + + 8, { + Usage = PaletteUsage.Print + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy); C3b(252uy,205uy,229uy) |] + } + + 9, { + Usage = PaletteUsage.None + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy); C3b(252uy,205uy,229uy) + C3b(217uy,217uy,217uy) |] + } + + 10, { + Usage = PaletteUsage.None + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy); C3b(252uy,205uy,229uy) + C3b(217uy,217uy,217uy); C3b(188uy,128uy,189uy) |] + } + + 11, { + Usage = PaletteUsage.None + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy); C3b(252uy,205uy,229uy) + C3b(217uy,217uy,217uy); C3b(188uy,128uy,189uy); C3b(204uy,235uy,197uy) |] + } + + 12, { + Usage = PaletteUsage.None + Colors = [| C3b(141uy,211uy,199uy); C3b(255uy,255uy,179uy); C3b(190uy,186uy,218uy); C3b(251uy,128uy,114uy) + C3b(128uy,177uy,211uy); C3b(253uy,180uy,98uy); C3b(179uy,222uy,105uy); C3b(252uy,205uy,229uy) + C3b(217uy,217uy,217uy); C3b(188uy,128uy,189uy); C3b(204uy,235uy,197uy); C3b(255uy,237uy,111uy) |] + } + + ] + } + + let Dark2 = + { + Name = Sym.ofString "Dark2" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy) |] + } + + 4, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy); C3b(231uy,41uy,138uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy); C3b(231uy,41uy,138uy) + C3b(102uy,166uy,30uy) |] + } + + 6, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy); C3b(231uy,41uy,138uy) + C3b(102uy,166uy,30uy); C3b(230uy,171uy,2uy) |] + } + + 7, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy); C3b(231uy,41uy,138uy) + C3b(102uy,166uy,30uy); C3b(230uy,171uy,2uy); C3b(166uy,118uy,29uy) |] + } + + 8, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(27uy,158uy,119uy); C3b(217uy,95uy,2uy); C3b(117uy,112uy,179uy); C3b(231uy,41uy,138uy) + C3b(102uy,166uy,30uy); C3b(230uy,171uy,2uy); C3b(166uy,118uy,29uy); C3b(102uy,102uy,102uy) |] + } + + ] + } + + let Paired = + { + Name = Sym.ofString "Paired" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) |] + } + + 5, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy) |] + } + + 6, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy) |] + } + + 7, { + Usage = PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy) |] + } + + 8, { + Usage = PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy); C3b(255uy,127uy,0uy) |] + } + + 9, { + Usage = PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy); C3b(255uy,127uy,0uy) + C3b(202uy,178uy,214uy) |] + } + + 10, { + Usage = PaletteUsage.LCD + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy); C3b(255uy,127uy,0uy) + C3b(202uy,178uy,214uy); C3b(106uy,61uy,154uy) |] + } + + 11, { + Usage = PaletteUsage.None + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy); C3b(255uy,127uy,0uy) + C3b(202uy,178uy,214uy); C3b(106uy,61uy,154uy); C3b(255uy,255uy,153uy) |] + } + + 12, { + Usage = PaletteUsage.None + Colors = [| C3b(166uy,206uy,227uy); C3b(31uy,120uy,180uy); C3b(178uy,223uy,138uy); C3b(51uy,160uy,44uy) + C3b(251uy,154uy,153uy); C3b(227uy,26uy,28uy); C3b(253uy,191uy,111uy); C3b(255uy,127uy,0uy) + C3b(202uy,178uy,214uy); C3b(106uy,61uy,154uy); C3b(255uy,255uy,153uy); C3b(177uy,89uy,40uy) |] + } + + ] + } + + let Pastel2 = + { + Name = Sym.ofString "Pastel2" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy) |] + } + + 4, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy); C3b(244uy,202uy,228uy) |] + } + + 5, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy); C3b(244uy,202uy,228uy) + C3b(230uy,245uy,201uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy); C3b(244uy,202uy,228uy) + C3b(230uy,245uy,201uy); C3b(255uy,242uy,174uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy); C3b(244uy,202uy,228uy) + C3b(230uy,245uy,201uy); C3b(255uy,242uy,174uy); C3b(241uy,226uy,204uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(179uy,226uy,205uy); C3b(253uy,205uy,172uy); C3b(203uy,213uy,232uy); C3b(244uy,202uy,228uy) + C3b(230uy,245uy,201uy); C3b(255uy,242uy,174uy); C3b(241uy,226uy,204uy); C3b(204uy,204uy,204uy) |] + } + + ] + } + + let Pastel1 = + { + Name = Sym.ofString "Pastel1" + Type = SchemeType.Qualitative + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy) |] + } + + 4, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) |] + } + + 5, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) + C3b(254uy,217uy,166uy) |] + } + + 6, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) + C3b(254uy,217uy,166uy); C3b(255uy,255uy,204uy) |] + } + + 7, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) + C3b(254uy,217uy,166uy); C3b(255uy,255uy,204uy); C3b(229uy,216uy,189uy) |] + } + + 8, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) + C3b(254uy,217uy,166uy); C3b(255uy,255uy,204uy); C3b(229uy,216uy,189uy); C3b(253uy,218uy,236uy) |] + } + + 9, { + Usage = PaletteUsage.None + Colors = [| C3b(251uy,180uy,174uy); C3b(179uy,205uy,227uy); C3b(204uy,235uy,197uy); C3b(222uy,203uy,228uy) + C3b(254uy,217uy,166uy); C3b(255uy,255uy,204uy); C3b(229uy,216uy,189uy); C3b(253uy,218uy,236uy) + C3b(242uy,242uy,242uy) |] + } + + ] + } + + /// Sequential schemes are suited to ordered data that progress from low to high. + /// Lightness steps dominate the look of these schemes, with light colors for low data values to dark colors for high data values. + module Sequential = + + let OrRd = + { + Name = Sym.ofString "OrRd" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(254uy,232uy,200uy); C3b(253uy,187uy,132uy); C3b(227uy,74uy,51uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(254uy,240uy,217uy); C3b(253uy,204uy,138uy); C3b(252uy,141uy,89uy); C3b(215uy,48uy,31uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(254uy,240uy,217uy); C3b(253uy,204uy,138uy); C3b(252uy,141uy,89uy); C3b(227uy,74uy,51uy) + C3b(179uy,0uy,0uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,240uy,217uy); C3b(253uy,212uy,158uy); C3b(253uy,187uy,132uy); C3b(252uy,141uy,89uy) + C3b(227uy,74uy,51uy); C3b(179uy,0uy,0uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,240uy,217uy); C3b(253uy,212uy,158uy); C3b(253uy,187uy,132uy); C3b(252uy,141uy,89uy) + C3b(239uy,101uy,72uy); C3b(215uy,48uy,31uy); C3b(153uy,0uy,0uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,236uy); C3b(254uy,232uy,200uy); C3b(253uy,212uy,158uy); C3b(253uy,187uy,132uy) + C3b(252uy,141uy,89uy); C3b(239uy,101uy,72uy); C3b(215uy,48uy,31uy); C3b(153uy,0uy,0uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,236uy); C3b(254uy,232uy,200uy); C3b(253uy,212uy,158uy); C3b(253uy,187uy,132uy) + C3b(252uy,141uy,89uy); C3b(239uy,101uy,72uy); C3b(215uy,48uy,31uy); C3b(179uy,0uy,0uy) + C3b(127uy,0uy,0uy) |] + } + + ] + } + + let PuBu = + { + Name = Sym.ofString "PuBu" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(236uy,231uy,242uy); C3b(166uy,189uy,219uy); C3b(43uy,140uy,190uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(241uy,238uy,246uy); C3b(189uy,201uy,225uy); C3b(116uy,169uy,207uy); C3b(5uy,112uy,176uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(241uy,238uy,246uy); C3b(189uy,201uy,225uy); C3b(116uy,169uy,207uy); C3b(43uy,140uy,190uy) + C3b(4uy,90uy,141uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(241uy,238uy,246uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy); C3b(116uy,169uy,207uy) + C3b(43uy,140uy,190uy); C3b(4uy,90uy,141uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(241uy,238uy,246uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy); C3b(116uy,169uy,207uy) + C3b(54uy,144uy,192uy); C3b(5uy,112uy,176uy); C3b(3uy,78uy,123uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,251uy); C3b(236uy,231uy,242uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy) + C3b(116uy,169uy,207uy); C3b(54uy,144uy,192uy); C3b(5uy,112uy,176uy); C3b(3uy,78uy,123uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,251uy); C3b(236uy,231uy,242uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy) + C3b(116uy,169uy,207uy); C3b(54uy,144uy,192uy); C3b(5uy,112uy,176uy); C3b(4uy,90uy,141uy) + C3b(2uy,56uy,88uy) |] + } + + ] + } + + let BuPu = + { + Name = Sym.ofString "BuPu" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(224uy,236uy,244uy); C3b(158uy,188uy,218uy); C3b(136uy,86uy,167uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(237uy,248uy,251uy); C3b(179uy,205uy,227uy); C3b(140uy,150uy,198uy); C3b(136uy,65uy,157uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(237uy,248uy,251uy); C3b(179uy,205uy,227uy); C3b(140uy,150uy,198uy); C3b(136uy,86uy,167uy) + C3b(129uy,15uy,124uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,251uy); C3b(191uy,211uy,230uy); C3b(158uy,188uy,218uy); C3b(140uy,150uy,198uy) + C3b(136uy,86uy,167uy); C3b(129uy,15uy,124uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,251uy); C3b(191uy,211uy,230uy); C3b(158uy,188uy,218uy); C3b(140uy,150uy,198uy) + C3b(140uy,107uy,177uy); C3b(136uy,65uy,157uy); C3b(110uy,1uy,107uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,253uy); C3b(224uy,236uy,244uy); C3b(191uy,211uy,230uy); C3b(158uy,188uy,218uy) + C3b(140uy,150uy,198uy); C3b(140uy,107uy,177uy); C3b(136uy,65uy,157uy); C3b(110uy,1uy,107uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,253uy); C3b(224uy,236uy,244uy); C3b(191uy,211uy,230uy); C3b(158uy,188uy,218uy) + C3b(140uy,150uy,198uy); C3b(140uy,107uy,177uy); C3b(136uy,65uy,157uy); C3b(129uy,15uy,124uy) + C3b(77uy,0uy,75uy) |] + } + + ] + } + + let Oranges = + { + Name = Sym.ofString "Oranges" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(254uy,230uy,206uy); C3b(253uy,174uy,107uy); C3b(230uy,85uy,13uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(254uy,237uy,222uy); C3b(253uy,190uy,133uy); C3b(253uy,141uy,60uy); C3b(217uy,71uy,1uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(254uy,237uy,222uy); C3b(253uy,190uy,133uy); C3b(253uy,141uy,60uy); C3b(230uy,85uy,13uy) + C3b(166uy,54uy,3uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,237uy,222uy); C3b(253uy,208uy,162uy); C3b(253uy,174uy,107uy); C3b(253uy,141uy,60uy) + C3b(230uy,85uy,13uy); C3b(166uy,54uy,3uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,237uy,222uy); C3b(253uy,208uy,162uy); C3b(253uy,174uy,107uy); C3b(253uy,141uy,60uy) + C3b(241uy,105uy,19uy); C3b(217uy,72uy,1uy); C3b(140uy,45uy,4uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,245uy,235uy); C3b(254uy,230uy,206uy); C3b(253uy,208uy,162uy); C3b(253uy,174uy,107uy) + C3b(253uy,141uy,60uy); C3b(241uy,105uy,19uy); C3b(217uy,72uy,1uy); C3b(140uy,45uy,4uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,245uy,235uy); C3b(254uy,230uy,206uy); C3b(253uy,208uy,162uy); C3b(253uy,174uy,107uy) + C3b(253uy,141uy,60uy); C3b(241uy,105uy,19uy); C3b(217uy,72uy,1uy); C3b(166uy,54uy,3uy) + C3b(127uy,39uy,4uy) |] + } + + ] + } + + let BuGn = + { + Name = Sym.ofString "BuGn" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(229uy,245uy,249uy); C3b(153uy,216uy,201uy); C3b(44uy,162uy,95uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(237uy,248uy,251uy); C3b(178uy,226uy,226uy); C3b(102uy,194uy,164uy); C3b(35uy,139uy,69uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,251uy); C3b(178uy,226uy,226uy); C3b(102uy,194uy,164uy); C3b(44uy,162uy,95uy) + C3b(0uy,109uy,44uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,251uy); C3b(204uy,236uy,230uy); C3b(153uy,216uy,201uy); C3b(102uy,194uy,164uy) + C3b(44uy,162uy,95uy); C3b(0uy,109uy,44uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,251uy); C3b(204uy,236uy,230uy); C3b(153uy,216uy,201uy); C3b(102uy,194uy,164uy) + C3b(65uy,174uy,118uy); C3b(35uy,139uy,69uy); C3b(0uy,88uy,36uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,253uy); C3b(229uy,245uy,249uy); C3b(204uy,236uy,230uy); C3b(153uy,216uy,201uy) + C3b(102uy,194uy,164uy); C3b(65uy,174uy,118uy); C3b(35uy,139uy,69uy); C3b(0uy,88uy,36uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,253uy); C3b(229uy,245uy,249uy); C3b(204uy,236uy,230uy); C3b(153uy,216uy,201uy) + C3b(102uy,194uy,164uy); C3b(65uy,174uy,118uy); C3b(35uy,139uy,69uy); C3b(0uy,109uy,44uy) + C3b(0uy,68uy,27uy) |] + } + + ] + } + + let YlOrBr = + { + Name = Sym.ofString "YlOrBr" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(255uy,247uy,188uy); C3b(254uy,196uy,79uy); C3b(217uy,95uy,14uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(255uy,255uy,212uy); C3b(254uy,217uy,142uy); C3b(254uy,153uy,41uy); C3b(204uy,76uy,2uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,212uy); C3b(254uy,217uy,142uy); C3b(254uy,153uy,41uy); C3b(217uy,95uy,14uy) + C3b(153uy,52uy,4uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,212uy); C3b(254uy,227uy,145uy); C3b(254uy,196uy,79uy); C3b(254uy,153uy,41uy) + C3b(217uy,95uy,14uy); C3b(153uy,52uy,4uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,212uy); C3b(254uy,227uy,145uy); C3b(254uy,196uy,79uy); C3b(254uy,153uy,41uy) + C3b(236uy,112uy,20uy); C3b(204uy,76uy,2uy); C3b(140uy,45uy,4uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,229uy); C3b(255uy,247uy,188uy); C3b(254uy,227uy,145uy); C3b(254uy,196uy,79uy) + C3b(254uy,153uy,41uy); C3b(236uy,112uy,20uy); C3b(204uy,76uy,2uy); C3b(140uy,45uy,4uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,229uy); C3b(255uy,247uy,188uy); C3b(254uy,227uy,145uy); C3b(254uy,196uy,79uy) + C3b(254uy,153uy,41uy); C3b(236uy,112uy,20uy); C3b(204uy,76uy,2uy); C3b(153uy,52uy,4uy) + C3b(102uy,37uy,6uy) |] + } + + ] + } + + let YlGn = + { + Name = Sym.ofString "YlGn" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(247uy,252uy,185uy); C3b(173uy,221uy,142uy); C3b(49uy,163uy,84uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(255uy,255uy,204uy); C3b(194uy,230uy,153uy); C3b(120uy,198uy,121uy); C3b(35uy,132uy,67uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(255uy,255uy,204uy); C3b(194uy,230uy,153uy); C3b(120uy,198uy,121uy); C3b(49uy,163uy,84uy) + C3b(0uy,104uy,55uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(217uy,240uy,163uy); C3b(173uy,221uy,142uy); C3b(120uy,198uy,121uy) + C3b(49uy,163uy,84uy); C3b(0uy,104uy,55uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(217uy,240uy,163uy); C3b(173uy,221uy,142uy); C3b(120uy,198uy,121uy) + C3b(65uy,171uy,93uy); C3b(35uy,132uy,67uy); C3b(0uy,90uy,50uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,229uy); C3b(247uy,252uy,185uy); C3b(217uy,240uy,163uy); C3b(173uy,221uy,142uy) + C3b(120uy,198uy,121uy); C3b(65uy,171uy,93uy); C3b(35uy,132uy,67uy); C3b(0uy,90uy,50uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,229uy); C3b(247uy,252uy,185uy); C3b(217uy,240uy,163uy); C3b(173uy,221uy,142uy) + C3b(120uy,198uy,121uy); C3b(65uy,171uy,93uy); C3b(35uy,132uy,67uy); C3b(0uy,104uy,55uy) + C3b(0uy,69uy,41uy) |] + } + + ] + } + + let Reds = + { + Name = Sym.ofString "Reds" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(254uy,224uy,210uy); C3b(252uy,146uy,114uy); C3b(222uy,45uy,38uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,229uy,217uy); C3b(252uy,174uy,145uy); C3b(251uy,106uy,74uy); C3b(203uy,24uy,29uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,229uy,217uy); C3b(252uy,174uy,145uy); C3b(251uy,106uy,74uy); C3b(222uy,45uy,38uy) + C3b(165uy,15uy,21uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,229uy,217uy); C3b(252uy,187uy,161uy); C3b(252uy,146uy,114uy); C3b(251uy,106uy,74uy) + C3b(222uy,45uy,38uy); C3b(165uy,15uy,21uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,229uy,217uy); C3b(252uy,187uy,161uy); C3b(252uy,146uy,114uy); C3b(251uy,106uy,74uy) + C3b(239uy,59uy,44uy); C3b(203uy,24uy,29uy); C3b(153uy,0uy,13uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,245uy,240uy); C3b(254uy,224uy,210uy); C3b(252uy,187uy,161uy); C3b(252uy,146uy,114uy) + C3b(251uy,106uy,74uy); C3b(239uy,59uy,44uy); C3b(203uy,24uy,29uy); C3b(153uy,0uy,13uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,245uy,240uy); C3b(254uy,224uy,210uy); C3b(252uy,187uy,161uy); C3b(252uy,146uy,114uy) + C3b(251uy,106uy,74uy); C3b(239uy,59uy,44uy); C3b(203uy,24uy,29uy); C3b(165uy,15uy,21uy) + C3b(103uy,0uy,13uy) |] + } + + ] + } + + let RdPu = + { + Name = Sym.ofString "RdPu" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(253uy,224uy,221uy); C3b(250uy,159uy,181uy); C3b(197uy,27uy,138uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(254uy,235uy,226uy); C3b(251uy,180uy,185uy); C3b(247uy,104uy,161uy); C3b(174uy,1uy,126uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(254uy,235uy,226uy); C3b(251uy,180uy,185uy); C3b(247uy,104uy,161uy); C3b(197uy,27uy,138uy) + C3b(122uy,1uy,119uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,235uy,226uy); C3b(252uy,197uy,192uy); C3b(250uy,159uy,181uy); C3b(247uy,104uy,161uy) + C3b(197uy,27uy,138uy); C3b(122uy,1uy,119uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(254uy,235uy,226uy); C3b(252uy,197uy,192uy); C3b(250uy,159uy,181uy); C3b(247uy,104uy,161uy) + C3b(221uy,52uy,151uy); C3b(174uy,1uy,126uy); C3b(122uy,1uy,119uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,243uy); C3b(253uy,224uy,221uy); C3b(252uy,197uy,192uy); C3b(250uy,159uy,181uy) + C3b(247uy,104uy,161uy); C3b(221uy,52uy,151uy); C3b(174uy,1uy,126uy); C3b(122uy,1uy,119uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,243uy); C3b(253uy,224uy,221uy); C3b(252uy,197uy,192uy); C3b(250uy,159uy,181uy) + C3b(247uy,104uy,161uy); C3b(221uy,52uy,151uy); C3b(174uy,1uy,126uy); C3b(122uy,1uy,119uy) + C3b(73uy,0uy,106uy) |] + } + + ] + } + + let Greens = + { + Name = Sym.ofString "Greens" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(229uy,245uy,224uy); C3b(161uy,217uy,155uy); C3b(49uy,163uy,84uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,233uy); C3b(186uy,228uy,179uy); C3b(116uy,196uy,118uy); C3b(35uy,139uy,69uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,233uy); C3b(186uy,228uy,179uy); C3b(116uy,196uy,118uy); C3b(49uy,163uy,84uy) + C3b(0uy,109uy,44uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,233uy); C3b(199uy,233uy,192uy); C3b(161uy,217uy,155uy); C3b(116uy,196uy,118uy) + C3b(49uy,163uy,84uy); C3b(0uy,109uy,44uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(237uy,248uy,233uy); C3b(199uy,233uy,192uy); C3b(161uy,217uy,155uy); C3b(116uy,196uy,118uy) + C3b(65uy,171uy,93uy); C3b(35uy,139uy,69uy); C3b(0uy,90uy,50uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,245uy); C3b(229uy,245uy,224uy); C3b(199uy,233uy,192uy); C3b(161uy,217uy,155uy) + C3b(116uy,196uy,118uy); C3b(65uy,171uy,93uy); C3b(35uy,139uy,69uy); C3b(0uy,90uy,50uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,245uy); C3b(229uy,245uy,224uy); C3b(199uy,233uy,192uy); C3b(161uy,217uy,155uy) + C3b(116uy,196uy,118uy); C3b(65uy,171uy,93uy); C3b(35uy,139uy,69uy); C3b(0uy,109uy,44uy) + C3b(0uy,68uy,27uy) |] + } + + ] + } + + let YlGnBu = + { + Name = Sym.ofString "YlGnBu" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(237uy,248uy,177uy); C3b(127uy,205uy,187uy); C3b(44uy,127uy,184uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(255uy,255uy,204uy); C3b(161uy,218uy,180uy); C3b(65uy,182uy,196uy); C3b(34uy,94uy,168uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(255uy,255uy,204uy); C3b(161uy,218uy,180uy); C3b(65uy,182uy,196uy); C3b(44uy,127uy,184uy) + C3b(37uy,52uy,148uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(199uy,233uy,180uy); C3b(127uy,205uy,187uy); C3b(65uy,182uy,196uy) + C3b(44uy,127uy,184uy); C3b(37uy,52uy,148uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(199uy,233uy,180uy); C3b(127uy,205uy,187uy); C3b(65uy,182uy,196uy) + C3b(29uy,145uy,192uy); C3b(34uy,94uy,168uy); C3b(12uy,44uy,132uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,217uy); C3b(237uy,248uy,177uy); C3b(199uy,233uy,180uy); C3b(127uy,205uy,187uy) + C3b(65uy,182uy,196uy); C3b(29uy,145uy,192uy); C3b(34uy,94uy,168uy); C3b(12uy,44uy,132uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,217uy); C3b(237uy,248uy,177uy); C3b(199uy,233uy,180uy); C3b(127uy,205uy,187uy) + C3b(65uy,182uy,196uy); C3b(29uy,145uy,192uy); C3b(34uy,94uy,168uy); C3b(37uy,52uy,148uy) + C3b(8uy,29uy,88uy) |] + } + + ] + } + + let Purples = + { + Name = Sym.ofString "Purples" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(239uy,237uy,245uy); C3b(188uy,189uy,220uy); C3b(117uy,107uy,177uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(242uy,240uy,247uy); C3b(203uy,201uy,226uy); C3b(158uy,154uy,200uy); C3b(106uy,81uy,163uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(242uy,240uy,247uy); C3b(203uy,201uy,226uy); C3b(158uy,154uy,200uy); C3b(117uy,107uy,177uy) + C3b(84uy,39uy,143uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(242uy,240uy,247uy); C3b(218uy,218uy,235uy); C3b(188uy,189uy,220uy); C3b(158uy,154uy,200uy) + C3b(117uy,107uy,177uy); C3b(84uy,39uy,143uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(242uy,240uy,247uy); C3b(218uy,218uy,235uy); C3b(188uy,189uy,220uy); C3b(158uy,154uy,200uy) + C3b(128uy,125uy,186uy); C3b(106uy,81uy,163uy); C3b(74uy,20uy,134uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(252uy,251uy,253uy); C3b(239uy,237uy,245uy); C3b(218uy,218uy,235uy); C3b(188uy,189uy,220uy) + C3b(158uy,154uy,200uy); C3b(128uy,125uy,186uy); C3b(106uy,81uy,163uy); C3b(74uy,20uy,134uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(252uy,251uy,253uy); C3b(239uy,237uy,245uy); C3b(218uy,218uy,235uy); C3b(188uy,189uy,220uy) + C3b(158uy,154uy,200uy); C3b(128uy,125uy,186uy); C3b(106uy,81uy,163uy); C3b(84uy,39uy,143uy) + C3b(63uy,0uy,125uy) |] + } + + ] + } + + let GnBu = + { + Name = Sym.ofString "GnBu" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(224uy,243uy,219uy); C3b(168uy,221uy,181uy); C3b(67uy,162uy,202uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(240uy,249uy,232uy); C3b(186uy,228uy,188uy); C3b(123uy,204uy,196uy); C3b(43uy,140uy,190uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(240uy,249uy,232uy); C3b(186uy,228uy,188uy); C3b(123uy,204uy,196uy); C3b(67uy,162uy,202uy) + C3b(8uy,104uy,172uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(240uy,249uy,232uy); C3b(204uy,235uy,197uy); C3b(168uy,221uy,181uy); C3b(123uy,204uy,196uy) + C3b(67uy,162uy,202uy); C3b(8uy,104uy,172uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(240uy,249uy,232uy); C3b(204uy,235uy,197uy); C3b(168uy,221uy,181uy); C3b(123uy,204uy,196uy) + C3b(78uy,179uy,211uy); C3b(43uy,140uy,190uy); C3b(8uy,88uy,158uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,240uy); C3b(224uy,243uy,219uy); C3b(204uy,235uy,197uy); C3b(168uy,221uy,181uy) + C3b(123uy,204uy,196uy); C3b(78uy,179uy,211uy); C3b(43uy,140uy,190uy); C3b(8uy,88uy,158uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,252uy,240uy); C3b(224uy,243uy,219uy); C3b(204uy,235uy,197uy); C3b(168uy,221uy,181uy) + C3b(123uy,204uy,196uy); C3b(78uy,179uy,211uy); C3b(43uy,140uy,190uy); C3b(8uy,104uy,172uy) + C3b(8uy,64uy,129uy) |] + } + + ] + } + + let Greys = + { + Name = Sym.ofString "Greys" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(240uy,240uy,240uy); C3b(189uy,189uy,189uy); C3b(99uy,99uy,99uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(247uy,247uy,247uy); C3b(204uy,204uy,204uy); C3b(150uy,150uy,150uy); C3b(82uy,82uy,82uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,247uy,247uy); C3b(204uy,204uy,204uy); C3b(150uy,150uy,150uy); C3b(99uy,99uy,99uy) + C3b(37uy,37uy,37uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,247uy,247uy); C3b(217uy,217uy,217uy); C3b(189uy,189uy,189uy); C3b(150uy,150uy,150uy) + C3b(99uy,99uy,99uy); C3b(37uy,37uy,37uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,247uy,247uy); C3b(217uy,217uy,217uy); C3b(189uy,189uy,189uy); C3b(150uy,150uy,150uy) + C3b(115uy,115uy,115uy); C3b(82uy,82uy,82uy); C3b(37uy,37uy,37uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,255uy); C3b(240uy,240uy,240uy); C3b(217uy,217uy,217uy); C3b(189uy,189uy,189uy) + C3b(150uy,150uy,150uy); C3b(115uy,115uy,115uy); C3b(82uy,82uy,82uy); C3b(37uy,37uy,37uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,255uy); C3b(240uy,240uy,240uy); C3b(217uy,217uy,217uy); C3b(189uy,189uy,189uy) + C3b(150uy,150uy,150uy); C3b(115uy,115uy,115uy); C3b(82uy,82uy,82uy); C3b(37uy,37uy,37uy) + C3b(0uy,0uy,0uy) |] + } + + ] + } + + let YlOrRd = + { + Name = Sym.ofString "YlOrRd" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(255uy,237uy,160uy); C3b(254uy,178uy,76uy); C3b(240uy,59uy,32uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print + Colors = [| C3b(255uy,255uy,178uy); C3b(254uy,204uy,92uy); C3b(253uy,141uy,60uy); C3b(227uy,26uy,28uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,178uy); C3b(254uy,204uy,92uy); C3b(253uy,141uy,60uy); C3b(240uy,59uy,32uy) + C3b(189uy,0uy,38uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,178uy); C3b(254uy,217uy,118uy); C3b(254uy,178uy,76uy); C3b(253uy,141uy,60uy) + C3b(240uy,59uy,32uy); C3b(189uy,0uy,38uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,178uy); C3b(254uy,217uy,118uy); C3b(254uy,178uy,76uy); C3b(253uy,141uy,60uy) + C3b(252uy,78uy,42uy); C3b(227uy,26uy,28uy); C3b(177uy,0uy,38uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(255uy,237uy,160uy); C3b(254uy,217uy,118uy); C3b(254uy,178uy,76uy) + C3b(253uy,141uy,60uy); C3b(252uy,78uy,42uy); C3b(227uy,26uy,28uy); C3b(177uy,0uy,38uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,255uy,204uy); C3b(255uy,237uy,160uy); C3b(254uy,217uy,118uy); C3b(254uy,178uy,76uy) + C3b(253uy,141uy,60uy); C3b(252uy,78uy,42uy); C3b(227uy,26uy,28uy); C3b(189uy,0uy,38uy) + C3b(128uy,0uy,38uy) |] + } + + ] + } + + let PuRd = + { + Name = Sym.ofString "PuRd" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(231uy,225uy,239uy); C3b(201uy,148uy,199uy); C3b(221uy,28uy,119uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(241uy,238uy,246uy); C3b(215uy,181uy,216uy); C3b(223uy,101uy,176uy); C3b(206uy,18uy,86uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD + Colors = [| C3b(241uy,238uy,246uy); C3b(215uy,181uy,216uy); C3b(223uy,101uy,176uy); C3b(221uy,28uy,119uy) + C3b(152uy,0uy,67uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(241uy,238uy,246uy); C3b(212uy,185uy,218uy); C3b(201uy,148uy,199uy); C3b(223uy,101uy,176uy) + C3b(221uy,28uy,119uy); C3b(152uy,0uy,67uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(241uy,238uy,246uy); C3b(212uy,185uy,218uy); C3b(201uy,148uy,199uy); C3b(223uy,101uy,176uy) + C3b(231uy,41uy,138uy); C3b(206uy,18uy,86uy); C3b(145uy,0uy,63uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,244uy,249uy); C3b(231uy,225uy,239uy); C3b(212uy,185uy,218uy); C3b(201uy,148uy,199uy) + C3b(223uy,101uy,176uy); C3b(231uy,41uy,138uy); C3b(206uy,18uy,86uy); C3b(145uy,0uy,63uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,244uy,249uy); C3b(231uy,225uy,239uy); C3b(212uy,185uy,218uy); C3b(201uy,148uy,199uy) + C3b(223uy,101uy,176uy); C3b(231uy,41uy,138uy); C3b(206uy,18uy,86uy); C3b(152uy,0uy,67uy) + C3b(103uy,0uy,31uy) |] + } + + ] + } + + let Blues = + { + Name = Sym.ofString "Blues" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(222uy,235uy,247uy); C3b(158uy,202uy,225uy); C3b(49uy,130uy,189uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(239uy,243uy,255uy); C3b(189uy,215uy,231uy); C3b(107uy,174uy,214uy); C3b(33uy,113uy,181uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(239uy,243uy,255uy); C3b(189uy,215uy,231uy); C3b(107uy,174uy,214uy); C3b(49uy,130uy,189uy) + C3b(8uy,81uy,156uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(239uy,243uy,255uy); C3b(198uy,219uy,239uy); C3b(158uy,202uy,225uy); C3b(107uy,174uy,214uy) + C3b(49uy,130uy,189uy); C3b(8uy,81uy,156uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(239uy,243uy,255uy); C3b(198uy,219uy,239uy); C3b(158uy,202uy,225uy); C3b(107uy,174uy,214uy) + C3b(66uy,146uy,198uy); C3b(33uy,113uy,181uy); C3b(8uy,69uy,148uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,251uy,255uy); C3b(222uy,235uy,247uy); C3b(198uy,219uy,239uy); C3b(158uy,202uy,225uy) + C3b(107uy,174uy,214uy); C3b(66uy,146uy,198uy); C3b(33uy,113uy,181uy); C3b(8uy,69uy,148uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(247uy,251uy,255uy); C3b(222uy,235uy,247uy); C3b(198uy,219uy,239uy); C3b(158uy,202uy,225uy) + C3b(107uy,174uy,214uy); C3b(66uy,146uy,198uy); C3b(33uy,113uy,181uy); C3b(8uy,81uy,156uy) + C3b(8uy,48uy,107uy) |] + } + + ] + } + + let PuBuGn = + { + Name = Sym.ofString "PuBuGn" + Type = SchemeType.Sequential + Palettes = + MapExt.ofList [ + 3, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.Print ||| PaletteUsage.LCD ||| PaletteUsage.PhotoCopy + Colors = [| C3b(236uy,226uy,240uy); C3b(166uy,189uy,219uy); C3b(28uy,144uy,153uy) |] + } + + 4, { + Usage = PaletteUsage.ColorBlind ||| PaletteUsage.LCD + Colors = [| C3b(246uy,239uy,247uy); C3b(189uy,201uy,225uy); C3b(103uy,169uy,207uy); C3b(2uy,129uy,138uy) |] + } + + 5, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(246uy,239uy,247uy); C3b(189uy,201uy,225uy); C3b(103uy,169uy,207uy); C3b(28uy,144uy,153uy) + C3b(1uy,108uy,89uy) |] + } + + 6, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(246uy,239uy,247uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy); C3b(103uy,169uy,207uy) + C3b(28uy,144uy,153uy); C3b(1uy,108uy,89uy) |] + } + + 7, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(246uy,239uy,247uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy); C3b(103uy,169uy,207uy) + C3b(54uy,144uy,192uy); C3b(2uy,129uy,138uy); C3b(1uy,100uy,80uy) |] + } + + 8, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,251uy); C3b(236uy,226uy,240uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy) + C3b(103uy,169uy,207uy); C3b(54uy,144uy,192uy); C3b(2uy,129uy,138uy); C3b(1uy,100uy,80uy) |] + } + + 9, { + Usage = PaletteUsage.ColorBlind + Colors = [| C3b(255uy,247uy,251uy); C3b(236uy,226uy,240uy); C3b(208uy,209uy,230uy); C3b(166uy,189uy,219uy) + C3b(103uy,169uy,207uy); C3b(54uy,144uy,192uy); C3b(2uy,129uy,138uy); C3b(1uy,108uy,89uy) + C3b(1uy,70uy,54uy) |] + } + + ] + } + + /// Array of all available color schemes. + let All = + [| + Diverging.Spectral; Diverging.RdYlGn; Diverging.RdBu; Diverging.PiYG; Diverging.PRGn; Diverging.RdYlBu + Diverging.BrBG; Diverging.RdGy; Diverging.PuOr; Qualitative.Set2; Qualitative.Accent; Qualitative.Set1 + Qualitative.Set3; Qualitative.Dark2; Qualitative.Paired; Qualitative.Pastel2; Qualitative.Pastel1; Sequential.OrRd + Sequential.PuBu; Sequential.BuPu; Sequential.Oranges; Sequential.BuGn; Sequential.YlOrBr; Sequential.YlGn + Sequential.Reds; Sequential.RdPu; Sequential.Greens; Sequential.YlGnBu; Sequential.Purples; Sequential.GnBu + Sequential.Greys; Sequential.YlOrRd; Sequential.PuRd; Sequential.Blues; Sequential.PuBuGn + |] diff --git a/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fsx b/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fsx new file mode 100644 index 000000000..85fbbe0a2 --- /dev/null +++ b/src/Aardvark.Base.FSharp/Color/ColorBrewerSchemes.fsx @@ -0,0 +1,247 @@ +#r "../../../bin/Debug/netstandard2.0/Aardvark.Base.dll" +#r "../../../bin/Debug/netstandard2.0/Aardvark.Base.FSharp.dll" +#r "../../../bin/Debug/net6.0/FSharp.Data.Adaptive.dll" +#r "System.Net.Http.dll" + +open Aardvark.Base +open System +open System.IO +open System.Text +open System.Text.RegularExpressions +open System.Net.Http + +fsi.ShowDeclarationValues <- false + +let source = + let ws = Regex(@"\s+", RegexOptions.Compiled) + + use client = new HttpClient() + use task = client.GetStringAsync "https://raw.githubusercontent.com/axismaps/colorbrewer/master/colorbrewer_schemes.js" + task.Wait() + + task.Result + |> String.getLines + |> Array.map (fun str -> ws.Replace(str, "")) + +module Regex = + + let Scheme = + Regex( + @"^(?[A-Za-z0-9]+):\{" + + @"(?(?:[0-9]:\[.+?\],?)+)" + + @"'properties':\{(?.+)\}\},?$", + RegexOptions.Multiline + ); + + let Palette = + Regex(@"(?[0-9]+):\[(?[0-9'rgb\(\),]+)\]", RegexOptions.Compiled); + + let Color = + Regex(@"'(?rgb\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\))'", RegexOptions.Compiled) + + let RGB = + Regex(@"^rgb\((?[0-9]{1,3}),(?[0-9]{1,3}),(?[0-9]{1,3})\)$", RegexOptions.Compiled) + + let Property = + Regex(@"'(?[a-zA-Z]+)':(?:'(?[a-zA-Z]+)'|\[(?[0-9,]+)\])", RegexOptions.Compiled); + + +type Severity = + | Warning = 0 + | Error = 1 + +let messages = ResizeArray() + +let log severity fmt = + Printf.kprintf (fun str -> messages.Add (severity, str)) fmt + +let schemes = + source |> Array.choose (fun str -> + let m = Regex.Scheme.Match str + + if m.Success then + let schemeName = m.Groups.["Name"].Value + + try + let properties = + Regex.Property.Matches m.Groups.["Properties"].Value + |> Seq.map (fun p -> + let value = + if p.Groups.["Value"].Success then p.Groups.["Value"].Value + else p.Groups.["PerPaletteValue"].Value + + p.Groups.["Key"].Value, value + ) + |> Map.ofSeq + + let typ = + match properties.["type"] with + | "div" -> ColorBrewer.SchemeType.Diverging + | "qual" -> ColorBrewer.SchemeType.Qualitative + | "seq" -> ColorBrewer.SchemeType.Sequential + | t -> failwithf "Unknown scheme type '%s'" t + + let colors = + Regex.Palette.Matches m.Groups.["Palettes"].Value + |> Seq.map (fun p -> + let size = Int32.Parse p.Groups.["Size"].Value + + let colors = + Regex.Color.Matches p.Groups.["Colors"].Value + |> Seq.map (fun m -> + let m = Regex.RGB.Match m.Groups.["Color"].Value + let r = m.Groups.["R"].Value + let g = m.Groups.["G"].Value + let b = m.Groups.["B"].Value + $"C3b({r}uy,{g}uy,{b}uy)" + ) + |> Array.ofSeq + + if colors.Length <> size then + failwithf "Size mismatch (expected %d but got %d)" size colors.Length + + colors + ) + |> Array.ofSeq + + let getFlags (propertyName : string) = + let values = + properties.[propertyName] |> String.split "," |> Array.map (fun str -> + Int32.Parse str = 1 + ) + + if values.Length >= colors.Length then values + elif values.Length = 1 then Array.replicate colors.Length values.[0] + else + let missing = Array.replicate (colors.Length - values.Length) false + log Severity.Warning "Property '%s' in scheme '%s' has %d entries but expected %d." propertyName schemeName values.Length colors.Length + Array.concat [values; missing] + + let blind = getFlags "blind" + let print = getFlags "print" + let copy = getFlags "copy" + let screen = getFlags "screen" + + let palettes = + colors |> Array.mapi (fun i c -> + let mutable usage = Set.empty + if blind.[i] then usage <- usage |> Set.add ColorBrewer.PaletteUsage.ColorBlind + if print.[i] then usage <- usage |> Set.add ColorBrewer.PaletteUsage.Print + if copy.[i] then usage <- usage |> Set.add ColorBrewer.PaletteUsage.PhotoCopy + if screen.[i] then usage <- usage |> Set.add ColorBrewer.PaletteUsage.LCD + + c.Length, {| Colors = c; Usage = usage |} + ) + |> Map.ofArray + + Some {| Name = schemeName; Type = typ; Palettes = palettes |} + + with e -> + log Severity.Error "Failed to parse scheme %s: %s" schemeName e.Message + None + else + None + ) + +printfn "Found %d schemes" schemes.Length + +let writeToFile() = + let builder = StringBuilder() + + let writeln indent fmt = + Printf.kprintf (fun str -> builder.AppendLine(str |> String.indent indent) |> ignore) fmt + + let schemeTypeComments = + Map.ofList [ + ColorBrewer.SchemeType.Diverging, "/// Diverging schemes put equal emphasis on mid-range critical values and extremes at both ends of the data range.\n/// The critical class or break in the middle of the legend is emphasized with light colors and low and high extremes are\n/// emphasized with dark colors that have contrasting hues." + + ColorBrewer.SchemeType.Qualitative, "/// Qualitative schemes do not imply magnitude differences between legend classes, and hues are used to\n/// create the primary visual differences between classes. Qualitative schemes are best suited to representing nominal or categorical data." + + ColorBrewer.SchemeType.Sequential, "/// Sequential schemes are suited to ordered data that progress from low to high.\n/// Lightness steps dominate the look of these schemes, with light colors for low data values to dark colors for high data values." + ] + + writeln 0 "namespace Aardvark.Base" + writeln 0 "" + writeln 0 "[]" + writeln 0 "module ColorBrewerSchemes =" + writeln 1 "open ColorBrewer" + writeln 0 "" + writeln 1 "/// Brewer color schemes designed for choropleth map visualizations." + writeln 1 "module ColorBrewer =" + writeln 0 "" + writeln 2 "module Scheme =" + writeln 0 "" + + let grouped = + schemes |> Array.groupBy (fun s -> s.Type) + + for (typ, schemes) in grouped do + + for c in schemeTypeComments.[typ] |> String.getLines do + writeln 3 "%s" c + + writeln 3 "module %A =" typ + writeln 0 "" + + for s in schemes do + writeln 4 "let %s =" s.Name + writeln 5 "{" + writeln 6 "Name = Sym.ofString \"%s\"" s.Name + writeln 6 "Type = SchemeType.%A" s.Type + writeln 6 "Palettes =" + writeln 7 "MapExt.ofList [" + + for (KeyValue(n, p)) in s.Palettes do + let usage = + if Set.isEmpty p.Usage then "PaletteUsage.None" + else p.Usage |> Seq.map (fun u -> $"PaletteUsage.{u}") |> String.concat " ||| " + + let colors = + Array.chunkBySize 4 p.Colors + |> Array.map (String.concat "; ") + + colors.[0] <- $"Colors = [| {colors.[0]}" + colors.[colors.Length - 1] <- $"{colors.[colors.Length - 1]} |]" + + writeln 8 "%d, {" n + writeln 9 "Usage = %s" usage + + writeln 9 "%s" colors.[0] + for i = 1 to colors.Length - 1 do + writeln 12 "%s" colors.[i] + + writeln 8 "}" + writeln 0 "" + + writeln 7 "]" + writeln 5 "}" + writeln 0 "" + + writeln 3 "/// Array of all available color schemes." + writeln 3 "let All =" + writeln 4 "[|" + + let all = + grouped |> Array.collect (fun (typ, schemes) -> + schemes |> Array.map (fun s -> $"{typ}.{s.Name}") + ) + |> Array.chunkBySize 6 + |> Array.map (String.concat "; ") + + for schemes in all do + writeln 5 "%s" schemes + + writeln 4 "|]" + + + let file = Path.Combine(__SOURCE_DIRECTORY__, "ColorBrewerSchemes.fs") + File.writeAllText file (builder.ToString()) + + for s in schemes do + printfn "%s" s.Name + printfn "%A" s.Palettes + + for (s, e) in messages do + printfn "[%A] %s" s e + +writeToFile() \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Datastructures/Immutable/MapExt.fs b/src/Aardvark.Base.FSharp/Datastructures/Immutable/MapExt.fs index be9a614a6..f8eea6f1c 100644 --- a/src/Aardvark.Base.FSharp/Datastructures/Immutable/MapExt.fs +++ b/src/Aardvark.Base.FSharp/Datastructures/Immutable/MapExt.fs @@ -455,6 +455,40 @@ module internal MapExtImplementation = | ValueSome v -> join left k v right | ValueNone -> merge left right + let rec range (comparer : IComparer<'Value>) lk rk m = + match m with + | _ when comparer.Compare(lk, rk) > 0 -> + MapEmpty + + | MapEmpty -> + MapEmpty + + | MapOne(k, _) as n -> + if comparer.Compare(lk, k) <= 0 && comparer.Compare(k, rk) <= 0 then n + else MapEmpty + + | MapNode(k, v, l, r, _, _) -> + let cl = comparer.Compare(lk, k) + + if cl = 0 then + if comparer.Compare(k, rk) = 0 then + MapOne(k, v) + else + join MapEmpty k v (range comparer lk rk r) + + elif cl < 0 then + let cr = comparer.Compare(k, rk) + + if cr = 0 then + join (range comparer lk rk l) k v MapEmpty + elif cr < 0 then + join (range comparer lk rk l) k v (range comparer lk rk r) + else + range comparer lk rk l + + else + range comparer lk rk r + let rec split (comparer: IComparer<'Value>) k m = match m with | MapEmpty -> @@ -1681,12 +1715,19 @@ module internal MapExtImplementation = let ofList comparer l = List.fold (fun acc (k,v) -> add comparer k v acc) empty l + let ofListV comparer l = List.fold (fun acc (struct (k,v)) -> add comparer k v acc) empty l let rec mkFromEnumerator comparer acc (e : IEnumerator<_>) = if e.MoveNext() then let (x,y) = e.Current mkFromEnumerator comparer (add comparer x y acc) e else acc + + let rec mkFromEnumeratorV comparer acc (e : IEnumerator<_>) = + if e.MoveNext() then + let struct (x,y) = e.Current + mkFromEnumeratorV comparer (add comparer x y acc) e + else acc let ofArray comparer (arr : array<_>) = let mutable res = empty @@ -1694,13 +1735,27 @@ module internal MapExtImplementation = res <- add comparer x y res res + let ofArrayV comparer (arr : array<_>) = + let mutable res = empty + for struct (x,y) in arr do + res <- add comparer x y res + res + let ofSeq comparer (c : seq<'Key * 'T>) = match c with | :? array<'Key * 'T> as xs -> ofArray comparer xs | :? list<'Key * 'T> as xs -> ofList comparer xs | _ -> use ie = c.GetEnumerator() - mkFromEnumerator comparer empty ie + mkFromEnumerator comparer empty ie + + let ofSeqV comparer (c : seq) = + match c with + | :? array as xs -> ofArrayV comparer xs + | :? list as xs -> ofListV comparer xs + | _ -> + use ie = c.GetEnumerator() + mkFromEnumeratorV comparer empty ie let copyToArray s (arr: _[]) i = @@ -1948,6 +2003,10 @@ type MapExt<[]'Key,[) : MapExt<'Key,'Value> = let comparer = LanguagePrimitives.FastGenericComparer<'Key> new MapExt<_,_>(comparer,MapTree.ofSeq comparer ie) + + static member CreateV(ie : IEnumerable<_>) : MapExt<'Key,'Value> = + let comparer = LanguagePrimitives.FastGenericComparer<'Key> + new MapExt<_,_>(comparer,MapTree.ofSeqV comparer ie) static member Create() : MapExt<'Key,'Value> = empty @@ -2085,6 +2144,9 @@ type MapExt<[]'Key,[ ValueOption.map (fun struct(_,v) -> v) member x.TryMaxValueV = MapTree.tryMaxV tree |> ValueOption.map (fun struct(_,v) -> v) + member x.Range(min, max) = + MapExt<'Key, 'Value>(comparer, MapTree.range comparer min max tree) + member x.Split (k) = let l, self, r = MapTree.split comparer k tree MapExt<'Key, 'Value>(comparer, l), self, MapExt<'Key, 'Value>(comparer, r) @@ -2153,6 +2215,10 @@ type MapExt<[]'Key,[ = let comparer = LanguagePrimitives.FastGenericComparer<'Key> new MapExt<_,_>(comparer,MapTree.ofList comparer l) + + static member ofListV(l) : MapExt<'Key,'Value> = + let comparer = LanguagePrimitives.FastGenericComparer<'Key> + new MapExt<_,_>(comparer,MapTree.ofListV comparer l) member this.ComputeHashCode() = let combineHash x y = (x <<< 1) + y + 631 @@ -2351,9 +2417,15 @@ module MapExt = [] let ofList (l: ('Key * 'Value) list) = MapExt<_,_>.ofList(l) + [] + let ofListV (l: (struct ('Key * 'Value)) list) = MapExt<_,_>.ofListV(l) + [] let ofSeq l = MapExt<_,_>.Create(l) - + + [] + let ofSeqV l = MapExt<_,_>.CreateV(l) + [] let singleton k v = MapExt<_,_>(LanguagePrimitives.FastGenericComparer<_>,MapOne(k,v)) @@ -2362,6 +2434,11 @@ module MapExt = let comparer = LanguagePrimitives.FastGenericComparer<'Key> new MapExt<_,_>(comparer,MapTree.ofArray comparer array) + [] + let ofArrayV (array: (struct ('Key * 'Value)) array) = + let comparer = LanguagePrimitives.FastGenericComparer<'Key> + new MapExt<_,_>(comparer,MapTree.ofArrayV comparer array) + [] let toList (m:MapExt<_,_>) = m.ToList() @@ -2388,6 +2465,9 @@ module MapExt = [] let tryMin (m:MapExt<_,_>) = m.TryMinKey + + [] + let tryMinV (m:MapExt<_,_>) = m.TryMinKeyV [] let min (m:MapExt<_,_>) = @@ -2398,6 +2478,9 @@ module MapExt = [] let tryMax (m:MapExt<_,_>) = m.TryMaxKey + [] + let tryMaxV (m:MapExt<_,_>) = m.TryMaxKeyV + [] let max (m:MapExt<_,_>) = match m.TryMaxKey with @@ -2426,9 +2509,15 @@ module MapExt = [] let chooseMonotonic f (m:MapExt<_,_>) = m.ChooseMonotonic(f) + [] + let range min max (m:MapExt<_,_>) = m.Range(min, max) + [] let split k (m:MapExt<_,_>) = m.Split k + [] + let splitV k (m:MapExt<_,_>) = m.SplitV k + [] let tryIndexOf i (m:MapExt<_,_>) = m.TryIndexOf i diff --git a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_auto.fs b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_auto.fs new file mode 100644 index 000000000..2d1137f3d --- /dev/null +++ b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_auto.fs @@ -0,0 +1,455 @@ +namespace Aardvark.Base + +open System +open System.Collections +open System.Collections.Generic +open FingerTreeImplementation + +#nowarn "44" + +[] +type private HalfRange = + struct + val mutable public IsMax : bool + val mutable public Value : int32 + + new(m,v) = { IsMax = m; Value = v } + + override x.GetHashCode() = + if x.IsMax then 0 + else x.Value.GetHashCode() + + override x.Equals o = + match o with + | :? HalfRange as o -> + x.IsMax = o.IsMax && x.Value = o.Value + | _ -> + false + + member x.CompareTo (o : HalfRange) = + let c = x.Value.CompareTo o.Value + if c = 0 then + if x.IsMax = o.IsMax then 0 + else (if x.IsMax then 1 else -1) + else + c + + interface IComparable with + member x.CompareTo o = + match o with + | :? HalfRange as o -> x.CompareTo(o) + | _ -> failwith "uncomparable" + end + + +[] +[] +type RangeSet = private { root : FingerTreeNode } with + + member private x.AsString = + x |> Seq.map (sprintf "%A") + |> String.concat "; " + |> sprintf "set [%s]" + + member x.Min = + match x.root |> FingerTreeNode.firstOpt with + | Some f -> f.Value + | _ -> Int32.MaxValue + + member x.Max = + match x.root |> FingerTreeNode.lastOpt with + | Some f -> f.Value + | _ -> Int32.MinValue + + member x.Range = + match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with + | Some min, Some max -> Range1i(min.Value, max.Value) + | _ -> Range1i.Invalid + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ + + +and private RangeSetEnumerator(i : IEnumerator) = + + let mutable last = HalfRange() + let mutable current = HalfRange() + + member x.Current = Range1i(last.Value, current.Value - 1) + + interface IEnumerator with + member x.MoveNext() = + if i.MoveNext() then + last <- i.Current + if i.MoveNext() then + current <- i.Current + true + else + false + else + false + + member x.Current = x.Current :> obj + + member x.Reset() = + i.Reset() + + interface IEnumerator with + member x.Current = x.Current + member x.Dispose() = i.Dispose() + +[] +[] +module RangeSet = + let private mm = + { + quantify = fun (r : HalfRange) -> r + mempty = HalfRange(false, Int32.MinValue) + mappend = fun l r -> if l.CompareTo r > 0 then l else r + } + + let private minRange = HalfRange(false, Int32.MinValue) + + let inline private leq v = HalfRange(true, v) + let inline private geq v = HalfRange(false, v) + + let inline private (|Leq|Geq|) (r : HalfRange) = + if r.IsMax then Leq r.Value + else Geq r.Value + + let empty = { root = Empty } + + let insert (range : Range1i) (t : RangeSet) = + let rangeMax = range.Max + 1 + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = leq rangeMax + let min = geq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Deep(max, One(min), Empty, One(max)) } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } + | Geq _ -> { root = l |> FingerTreeNode.append mm max } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } + | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + let remove (range : Range1i) (t : RangeSet) = + let rangeMax = range.Max + 1 + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = geq rangeMax + let min = leq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Empty } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l } + | Geq _ -> { root = l |> FingerTreeNode.append mm min } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } + | Geq _ -> { root = r } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + let ofSeq (s : seq) = + let mutable res = empty + for e in s do res <- insert e res + res + + let inline ofList (l : list) = ofSeq l + let inline ofArray (l : Range1i[]) = ofSeq l + + let toSeq (r : RangeSet) = r :> seq<_> + let toList (r : RangeSet) = r |> Seq.toList + let toArray (r : RangeSet) = r |> Seq.toArray + + let inline min (t : RangeSet) = t.Min + let inline max (t : RangeSet) = t.Max + let inline range (t : RangeSet) = t.Range + + let window (window : Range1i) (set : RangeSet) = + let rangeMax = window.Max + 1 + + let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange + let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + + let inner = + match FingerTreeNode.lastOpt l with + | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner + | _ -> inner + + let inner = + match FingerTreeNode.firstOpt r with + | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner + | _ -> inner + + { root = inner } + +[] +type private HalfRange64 = + struct + val mutable public IsMax : bool + val mutable public Value : int64 + + new(m,v) = { IsMax = m; Value = v } + + override x.GetHashCode() = + if x.IsMax then 0 + else x.Value.GetHashCode() + + override x.Equals o = + match o with + | :? HalfRange64 as o -> + x.IsMax = o.IsMax && x.Value = o.Value + | _ -> + false + + member x.CompareTo (o : HalfRange64) = + let c = x.Value.CompareTo o.Value + if c = 0 then + if x.IsMax = o.IsMax then 0 + else (if x.IsMax then 1 else -1) + else + c + + interface IComparable with + member x.CompareTo o = + match o with + | :? HalfRange64 as o -> x.CompareTo(o) + | _ -> failwith "uncomparable" + end + + +[] +[] +type RangeSet64 = private { root : FingerTreeNode } with + + member private x.AsString = + x |> Seq.map (sprintf "%A") + |> String.concat "; " + |> sprintf "set [%s]" + + member x.Min = + match x.root |> FingerTreeNode.firstOpt with + | Some f -> f.Value + | _ -> Int64.MaxValue + + member x.Max = + match x.root |> FingerTreeNode.lastOpt with + | Some f -> f.Value + | _ -> Int64.MinValue + + member x.Range = + match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with + | Some min, Some max -> Range1l(min.Value, max.Value) + | _ -> Range1l.Invalid + + interface IEnumerable with + member x.GetEnumerator() = new RangeSet64Enumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + + interface IEnumerable with + member x.GetEnumerator() = new RangeSet64Enumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ + + +and private RangeSet64Enumerator(i : IEnumerator) = + + let mutable last = HalfRange64() + let mutable current = HalfRange64() + + member x.Current = Range1l(last.Value, current.Value - 1L) + + interface IEnumerator with + member x.MoveNext() = + if i.MoveNext() then + last <- i.Current + if i.MoveNext() then + current <- i.Current + true + else + false + else + false + + member x.Current = x.Current :> obj + + member x.Reset() = + i.Reset() + + interface IEnumerator with + member x.Current = x.Current + member x.Dispose() = i.Dispose() + +[] +[] +module RangeSet64 = + let private mm = + { + quantify = fun (r : HalfRange64) -> r + mempty = HalfRange64(false, Int64.MinValue) + mappend = fun l r -> if l.CompareTo r > 0 then l else r + } + + let private minRange = HalfRange64(false, Int64.MinValue) + + let inline private leq v = HalfRange64(true, v) + let inline private geq v = HalfRange64(false, v) + + let inline private (|Leq|Geq|) (r : HalfRange64) = + if r.IsMax then Leq r.Value + else Geq r.Value + + let empty = { root = Empty } + + let insert (range : Range1l) (t : RangeSet64) = + let rangeMax = range.Max + 1L + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = leq rangeMax + let min = geq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Deep(max, One(min), Empty, One(max)) } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } + | Geq _ -> { root = l |> FingerTreeNode.append mm max } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } + | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + let remove (range : Range1l) (t : RangeSet64) = + let rangeMax = range.Max + 1L + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = geq rangeMax + let min = leq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Empty } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l } + | Geq _ -> { root = l |> FingerTreeNode.append mm min } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } + | Geq _ -> { root = r } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + let ofSeq (s : seq) = + let mutable res = empty + for e in s do res <- insert e res + res + + let inline ofList (l : list) = ofSeq l + let inline ofArray (l : Range1l[]) = ofSeq l + + let toSeq (r : RangeSet64) = r :> seq<_> + let toList (r : RangeSet64) = r |> Seq.toList + let toArray (r : RangeSet64) = r |> Seq.toArray + + let inline min (t : RangeSet64) = t.Min + let inline max (t : RangeSet64) = t.Max + let inline range (t : RangeSet64) = t.Range + + let window (window : Range1l) (set : RangeSet64) = + let rangeMax = window.Max + 1L + + let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange + let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + + let inner = + match FingerTreeNode.lastOpt l with + | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner + | _ -> inner + + let inner = + match FingerTreeNode.firstOpt r with + | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner + | _ -> inner + + { root = inner } + diff --git a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_template.fs b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_template.fs new file mode 100644 index 000000000..759b6e7f8 --- /dev/null +++ b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSetOld_template.fs @@ -0,0 +1,242 @@ +namespace Aardvark.Base + +open System +open System.Collections +open System.Collections.Generic +open FingerTreeImplementation + +#nowarn "44" + +//# foreach (var isLong in new[] { false, true }) { +//# var halfrange = isLong ? "HalfRange64" : "HalfRange"; +//# var rangeset = isLong ? "RangeSet64" : "RangeSet"; +//# var rangesetenumerator = isLong ? "RangeSet64Enumerator" : "RangeSetEnumerator"; +//# var range = isLong ? "Range1l" : "Range1i"; +//# var systype = isLong ? "Int64" : "Int32"; +//# var ft = isLong ? "int64" : "int32"; +//# var one = isLong ? "1L" : "1"; +//# var replacement = isLong ? "RangeSet1l" : "RangeSet1i"; +[] +type private __halfrange__ = + struct + val mutable public IsMax : bool + val mutable public Value : __ft__ + + new(m,v) = { IsMax = m; Value = v } + + override x.GetHashCode() = + if x.IsMax then 0 + else x.Value.GetHashCode() + + override x.Equals o = + match o with + | :? __halfrange__ as o -> + x.IsMax = o.IsMax && x.Value = o.Value + | _ -> + false + + member x.CompareTo (o : __halfrange__) = + let c = x.Value.CompareTo o.Value + if c = 0 then + if x.IsMax = o.IsMax then 0 + else (if x.IsMax then 1 else -1) + else + c + + interface IComparable with + member x.CompareTo o = + match o with + | :? __halfrange__ as o -> x.CompareTo(o) + | _ -> failwith "uncomparable" + end + + +[] +[] +type __rangeset__ = private { root : FingerTreeNode<__halfrange__, __halfrange__> } with + + member private x.AsString = + x |> Seq.map (sprintf "%A") + |> String.concat "; " + |> sprintf "set [%s]" + + member x.Min = + match x.root |> FingerTreeNode.firstOpt with + | Some f -> f.Value + | _ -> __systype__.MaxValue + + member x.Max = + match x.root |> FingerTreeNode.lastOpt with + | Some f -> f.Value + | _ -> __systype__.MinValue + + member x.Range = + match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with + | Some min, Some max -> __range__(min.Value, max.Value) + | _ -> __range__.Invalid + + interface IEnumerable with + member x.GetEnumerator() = new __rangesetenumerator__(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + + interface IEnumerable<__range__> with + member x.GetEnumerator() = new __rangesetenumerator__(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ + + +and private __rangesetenumerator__(i : IEnumerator<__halfrange__>) = + + let mutable last = __halfrange__() + let mutable current = __halfrange__() + + member x.Current = __range__(last.Value, current.Value - __one__) + + interface IEnumerator with + member x.MoveNext() = + if i.MoveNext() then + last <- i.Current + if i.MoveNext() then + current <- i.Current + true + else + false + else + false + + member x.Current = x.Current :> obj + + member x.Reset() = + i.Reset() + + interface IEnumerator<__range__> with + member x.Current = x.Current + member x.Dispose() = i.Dispose() + +[] +[] +module __rangeset__ = + let private mm = + { + quantify = fun (r : __halfrange__) -> r + mempty = __halfrange__(false, __systype__.MinValue) + mappend = fun l r -> if l.CompareTo r > 0 then l else r + } + + let private minRange = __halfrange__(false, __systype__.MinValue) + + let inline private leq v = __halfrange__(true, v) + let inline private geq v = __halfrange__(false, v) + + let inline private (|Leq|Geq|) (r : __halfrange__) = + if r.IsMax then Leq r.Value + else Geq r.Value + + let empty = { root = Empty } + + let insert (range : __range__) (t : __rangeset__) = + let rangeMax = range.Max + __one__ + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = leq rangeMax + let min = geq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Deep(max, One(min), Empty, One(max)) } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } + | Geq _ -> { root = l |> FingerTreeNode.append mm max } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } + | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + let remove (range : __range__) (t : __rangeset__) = + let rangeMax = range.Max + __one__ + + let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange + let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + + let max = geq rangeMax + let min = leq range.Min + + match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with + | None, None -> + { root = Empty } + + | Some lmax, None -> + match lmax with + | Leq _ -> { root = l } + | Geq _ -> { root = l |> FingerTreeNode.append mm min } + + | None, Some rmin -> + match rmin with + | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } + | Geq _ -> { root = r } + + | Some lmax, Some rmin -> + match lmax, rmin with + | Leq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [] r } + + | Geq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + + | Leq _, Leq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [max] r } + + | Geq _, Geq _ -> + { root = FingerTreeNode.concatWithMiddle mm l [min] r } + + let ofSeq (s : seq<__range__>) = + let mutable res = empty + for e in s do res <- insert e res + res + + let inline ofList (l : list<__range__>) = ofSeq l + let inline ofArray (l : __range__[]) = ofSeq l + + let toSeq (r : __rangeset__) = r :> seq<_> + let toList (r : __rangeset__) = r |> Seq.toList + let toArray (r : __rangeset__) = r |> Seq.toArray + + let inline min (t : __rangeset__) = t.Min + let inline max (t : __rangeset__) = t.Max + let inline range (t : __rangeset__) = t.Range + + let window (window : __range__) (set : __rangeset__) = + let rangeMax = window.Max + __one__ + + let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange + let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + + let inner = + match FingerTreeNode.lastOpt l with + | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner + | _ -> inner + + let inner = + match FingerTreeNode.firstOpt r with + | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner + | _ -> inner + + { root = inner } + +//# } \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_auto.fs b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_auto.fs index 0eff6f368..20320d66b 100644 --- a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_auto.fs +++ b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_auto.fs @@ -3,447 +3,1844 @@ namespace Aardvark.Base open System open System.Collections open System.Collections.Generic -open FingerTreeImplementation +open Aardvark.Base -[] -type private HalfRange = - struct - val mutable public IsMax : bool - val mutable public Value : int32 +type internal HalfRangeKind = + | Left = 0 + | Right = 1 - new(m,v) = { IsMax = m; Value = v } +module private RangeSetUtils = - override x.GetHashCode() = - if x.IsMax then 0 - else x.Value.GetHashCode() + let inline inc (value : 'T) = + let res = value + LanguagePrimitives.GenericOne<'T> + if res = Constant<'T>.ParseableMinValue then struct (Constant<'T>.ParseableMaxValue, true) + else struct (res, false) - override x.Equals o = - match o with - | :? HalfRange as o -> - x.IsMax = o.IsMax && x.Value = o.Value - | _ -> - false + module MapExt = + let inline splitAt (key : 'K) (map : MapExt<'K, 'V>) = + let struct (l, _, _, _, r) = MapExt.splitV key map + struct (l, r) - member x.CompareTo (o : HalfRange) = - let c = x.Value.CompareTo o.Value - if c = 0 then - if x.IsMax = o.IsMax then 0 - else (if x.IsMax then 1 else -1) - else - c + let inline tryMinValue (map : MapExt<'K, 'V>) = + MapExt.tryMinV map |> ValueOption.map (fun mk -> map.[mk]) - interface IComparable with - member x.CompareTo o = - match o with - | :? HalfRange as o -> x.CompareTo(o) - | _ -> failwith "uncomparable" - end + let inline tryMaxValue (map : MapExt<'K, 'V>) = + MapExt.tryMaxV map |> ValueOption.map (fun mk -> map.[mk]) + + let inline maxValue (map : MapExt<'K, 'V>) = + map.[MapExt.max map] +open RangeSetUtils +/// Set of ranges where overlapping and neighboring ranges are coalesced. +/// Note that ranges describe closed intervals. [] -type RangeSet = private { root : FingerTreeNode } with - - member private x.AsString = - x |> Seq.map (sprintf "%A") - |> String.concat "; " - |> sprintf "set [%s]" +type RangeSet1i internal (store : MapExt) = + static let empty = RangeSet1i(MapExt.empty) + /// Empty range set. + static member Empty = empty + + member inline private x.Store = store + + // We cannot directly describe a range that ends at Int32.MaxValue since the right half-range is inserted + // at max + 1. In that case the right-half range will be missing and the total count is odd. + member inline private x.HasMaxValue = store.Count % 2 = 1 + + /// Returns the minimum value in the range set or Int32.MaxValue if the range is empty. member x.Min = - match x.root |> FingerTreeNode.firstOpt with - | Some f -> f.Value + match store.TryMinKeyV with + | ValueSome min -> min | _ -> Int32.MaxValue + /// Returns the maximum value in the range set or Int32.MinValue if the range is empty. member x.Max = - match x.root |> FingerTreeNode.lastOpt with - | Some f -> f.Value - | _ -> Int32.MinValue + if x.HasMaxValue then Int32.MaxValue + else + match store.TryMaxKeyV with + | ValueSome max -> max - 1 + | _ -> Int32.MinValue + + /// Returns the total range spanned by the range set, i.e. [min, max]. + member inline x.Range = + Range1i(x.Min, x.Max) + + /// Adds the given range to the set. + member x.Add(r : Range1i) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained Int32.MaxValue or we have overflown, we must not add an explicit right half-range. + // Int32.MaxValue is stored implicitly. + let fixRightBoundary = + if x.HasMaxValue || overflow then + id + else + MapExt.add max HalfRangeKind.Right + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.ofListV [ + struct (min, HalfRangeKind.Left) + ] + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Left + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + |> MapExt.add min HalfRangeKind.Left + |> MapExt.add max HalfRangeKind.Right + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add min HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left); struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + MapExt.union lm rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1i(newStore) + + /// Removes the given range from the set. + member x.Remove(r : Range1i) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained Int32.MaxValue and we have not overflown, there is still a range [max, Int32.MaxValue] + let fixRightBoundary = + if x.HasMaxValue && not overflow then + MapExt.add max HalfRangeKind.Left + else + id + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.empty + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Right + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add max HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + MapExt.union lm rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right); struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1i(newStore) + + /// Returns the union of the set with the given set. + member inline x.Union(other : RangeSet1i) = + let mutable res = x + for r in other do + res <- res.Add r + res + + /// Returns the intersection of the set with the given range. + member x.Intersect(r : Range1i) = + if r.Max < r.Min then + empty + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let inner = + store + |> MapExt.splitAt min |> sndv + |> if not overflow then MapExt.splitAt max >> fstv else id + + let newStore = + inner + |> if x.Contains r.Min then MapExt.add min HalfRangeKind.Left else id + |> if x.Contains r.Max && not overflow then MapExt.add max HalfRangeKind.Right else id + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1i(newStore) + + member private x.TryFindLeftBoundary(v : int32) = + let struct (l, s, _) = MapExt.neighboursV v store + match s with + | ValueSome (i, k) -> if k = HalfRangeKind.Left then ValueSome i else ValueNone + | _ -> + match l with + | ValueSome (i, HalfRangeKind.Left) -> ValueSome i + | _ -> ValueNone + + /// Returns whether the given value is contained in the range set. + member x.Contains(v : int32) = + x.TryFindLeftBoundary v |> ValueOption.isSome + + /// Returns whether the given range is contained in the set. + member x.Contains(r : Range1i) = + if r.Max < r.Min then false + elif r.Min = r.Max then x.Contains r.Min + else + match x.TryFindLeftBoundary r.Min, x.TryFindLeftBoundary r.Max with + | ValueSome l, ValueSome r -> l = r + | _ -> false + + /// Returns the number of disjoint ranges in the set. + member x.Count = + (store.Count + 1) / 2 + + /// Returns whether the set is empty. + member inline x.IsEmpty = + x.Count = 0 + + /// Builds an array from the range set. + member x.ToArray() = + let arr = Array.zeroCreate x.Count + + let rec write (i : int) (l : struct (int32 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + arr.[i] <- Range1i(l, r - 1) + write (i + 1) rest + + | [struct (l, HalfRangeKind.Left)] when i = x.Count - 1 -> + arr.[i] <- Range1i(l, Int32.MaxValue) + + | [_] -> failwith "bad RangeSet" - member x.Range = - match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with - | Some min, Some max -> Range1i(min.Value, max.Value) - | _ -> Range1i.Invalid + | [] -> () + + store |> MapExt.toListV |> write 0 + arr + + /// Builds a list from the range set. + member x.ToList() = + let rec build (accum : Range1i list) (l : struct (int32 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + build (Range1i(l, r - 1) :: accum) rest + + | [struct (l, HalfRangeKind.Left)] -> + build (Range1i(l, Int32.MaxValue) :: accum) [] + + | [_] -> failwith "bad RangeSet" + + | [] -> List.rev accum + + store |> MapExt.toListV |> build [] + + /// Views the range set as a sequence. + member x.ToSeq() = + x :> seq<_> + + member inline private x.Equals(other : RangeSet1i) = + store = other.Store + + override x.Equals(other : obj) = + match other with + | :? RangeSet1i as o -> x.Equals o + | _ -> false + + override x.GetHashCode() = + store.GetHashCode() + + member private x.AsString = x.ToString() + + override x.ToString() = + let content = + x |> Seq.map (fun r -> + $"[{r.Min}, {r.Max}]" + ) + |> String.concat "; " + + $"ranges [{content}]" + + member x.GetEnumerator() = + new RangeSetEnumerator1i((store :> seq<_>).GetEnumerator()) + + interface IEquatable with + member x.Equals(other) = x.Equals(other) interface IEnumerable with - member x.GetEnumerator() = new RangeSetEnumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + member x.GetEnumerator() = new RangeSetEnumerator1i((store :> seq<_>).GetEnumerator()) :> _ interface IEnumerable with - member x.GetEnumerator() = new RangeSetEnumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ - + member x.GetEnumerator() = new RangeSetEnumerator1i((store :> seq<_>).GetEnumerator()) :> _ -and private RangeSetEnumerator(i : IEnumerator) = - - let mutable last = HalfRange() - let mutable current = HalfRange() +// TODO: MapExt should use a struct enumerator and return it directly. +// That way we could get rid of allocations. +and RangeSetEnumerator1i = + struct + val private Inner : IEnumerator> + val mutable private Left : KeyValuePair + val mutable private Right : KeyValuePair - member x.Current = Range1i(last.Value, current.Value - 1) + internal new (inner : IEnumerator>) = + { Inner = inner + Left = Unchecked.defaultof<_> + Right = Unchecked.defaultof<_> } - interface IEnumerator with member x.MoveNext() = - if i.MoveNext() then - last <- i.Current - if i.MoveNext() then - current <- i.Current + if x.Inner.MoveNext() then + x.Left <- x.Inner.Current + if x.Inner.MoveNext() then + x.Right <- x.Inner.Current true else - false + if x.Left.Value = HalfRangeKind.Left then + x.Right <- KeyValuePair(Int32.MinValue, HalfRangeKind.Right) // MaxValue + 1 + true + else + failwithf "bad RangeSet" else false - member x.Current = x.Current :> obj - member x.Reset() = - i.Reset() - - interface IEnumerator with - member x.Current = x.Current - member x.Dispose() = i.Dispose() + x.Inner.Reset() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + member x.Current = + assert (x.Left.Value = HalfRangeKind.Left && x.Right.Value = HalfRangeKind.Right) + Range1i(x.Left.Key, x.Right.Key - 1) + + member x.Dispose() = + x.Inner.Dispose() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + interface IEnumerator with + member x.MoveNext() = x.MoveNext() + member x.Current = x.Current :> obj + member x.Reset() = x.Reset() + + interface IEnumerator with + member x.Dispose() = x.Dispose() + member x.Current = x.Current + end [] -module RangeSet = - let private mm = - { - quantify = fun (r : HalfRange) -> r - mempty = HalfRange(false, Int32.MinValue) - mappend = fun l r -> if l.CompareTo r > 0 then l else r - } +module RangeSet1i = + + /// Empty range set. + let empty = RangeSet1i.Empty + + /// Returns the minimum value in the range set or Int32.MaxValue if the range is empty. + let inline min (set : RangeSet1i) = set.Min + + /// Returns the maximum value in the range set or Int32.MinValue if the range is empty. + let inline max (set : RangeSet1i) = set.Max + + /// Returns the total range spanned by the range set, i.e. [min, max]. + let inline range (set : RangeSet1i) = set.Range + + let inline private getHalfRanges (r : Range1i) = + [ struct (r.Min, HalfRangeKind.Left) + if r.Max < Int32.MaxValue then struct (r.Max + 1, HalfRangeKind.Right) ] + + let inline private ofRange (r : Range1i) = + RangeSet1i(MapExt.ofListV <| getHalfRanges r) + + let private ofRanges (ranges : seq) = + let halves = + ranges + |> Seq.toList + |> List.collect getHalfRanges + |> List.sortBy fstv + + let mutable level = 0 + let result = ResizeArray() + + for (struct (i, k) as h) in halves do + if k = HalfRangeKind.Left then + if level = 0 then result.Add h + level <- level + 1 + else + level <- level - 1 + if level = 0 then result.Add h + + RangeSet1i(MapExt.ofSeqV result) + + /// Builds a range set of the given list of ranges. + let ofList (ranges : Range1i list) = + match ranges with + | [] -> empty + | [r] -> ofRange r + | _ -> ofRanges ranges + + /// Builds a range set of the given array of ranges. + let ofArray (ranges : Range1i[]) = + if ranges.Length = 0 then empty + elif ranges.Length = 1 then ofRange ranges.[0] + else ofRanges ranges + + /// Builds a range set of the given sequence of ranges. + let inline ofSeq (ranges : seq) = + ofList <| Seq.toList ranges + + /// Adds the given range to the set. + let inline add (range : Range1i) (set : RangeSet1i) = set.Add range - let private minRange = HalfRange(false, Int32.MinValue) + [] + let inline insert (range : Range1i) (set : RangeSet1i) = set.Add range - let inline private leq v = HalfRange(true, v) - let inline private geq v = HalfRange(false, v) + /// Removes the given range from the set. + let inline remove (range : Range1i) (set : RangeSet1i) = set.Remove range - let inline private (|Leq|Geq|) (r : HalfRange) = - if r.IsMax then Leq r.Value - else Geq r.Value + /// Returns the union of two sets. + let inline union (l : RangeSet1i) (r : RangeSet1i) = l.Union r - let empty = { root = Empty } + /// Returns the intersection of the set with the given range. + let inline intersect (range : Range1i) (set : RangeSet1i) = set.Intersect range - let insert (range : Range1i) (t : RangeSet) = - let rangeMax = range.Max + 1 + [] + let inline window (range : Range1i) (set : RangeSet1i) = intersect range set - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + /// Returns whether the given value is contained in the range set. + let inline contains (value : int32) (set : RangeSet1i) = set.Contains value - let max = leq rangeMax - let min = geq range.Min + /// Returns whether the given range is contained in the set. + let inline containsRange (range : Range1i) (set : RangeSet1i) = set.Contains range - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Deep(max, One(min), Empty, One(max)) } + /// Returns the number of disjoint ranges in the set. + let inline count (set : RangeSet1i) = set.Count - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } - | Geq _ -> { root = l |> FingerTreeNode.append mm max } + /// Returns whether the set is empty. + let inline isEmpty (set : RangeSet1i) = set.IsEmpty - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } - | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } + /// Views the range set as a sequence. + let inline toSeq (set : RangeSet1i) = set :> seq<_> - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } + /// Builds a list from the range set. + let inline toList (set : RangeSet1i) = set.ToList() - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } + /// Builds an array from the range set. + let inline toArray (set : RangeSet1i) = set.ToArray() - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } +/// Set of ranges where overlapping and neighboring ranges are coalesced. +/// Note that ranges describe closed intervals. +[] +type RangeSet1ui internal (store : MapExt) = + static let empty = RangeSet1ui(MapExt.empty) + + /// Empty range set. + static member Empty = empty + + member inline private x.Store = store + + // We cannot directly describe a range that ends at UInt32.MaxValue since the right half-range is inserted + // at max + 1. In that case the right-half range will be missing and the total count is odd. + member inline private x.HasMaxValue = store.Count % 2 = 1 + + /// Returns the minimum value in the range set or UInt32.MaxValue if the range is empty. + member x.Min = + match store.TryMinKeyV with + | ValueSome min -> min + | _ -> UInt32.MaxValue + + /// Returns the maximum value in the range set or UInt32.MinValue if the range is empty. + member x.Max = + if x.HasMaxValue then UInt32.MaxValue + else + match store.TryMaxKeyV with + | ValueSome max -> max - 1u + | _ -> UInt32.MinValue + + /// Returns the total range spanned by the range set, i.e. [min, max]. + member inline x.Range = + Range1ui(x.Min, x.Max) + + /// Adds the given range to the set. + member x.Add(r : Range1ui) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained UInt32.MaxValue or we have overflown, we must not add an explicit right half-range. + // UInt32.MaxValue is stored implicitly. + let fixRightBoundary = + if x.HasMaxValue || overflow then + id + else + MapExt.add max HalfRangeKind.Right + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.ofListV [ + struct (min, HalfRangeKind.Left) + ] + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Left + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + |> MapExt.add min HalfRangeKind.Left + |> MapExt.add max HalfRangeKind.Right + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add min HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left); struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + MapExt.union lm rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ui(newStore) + + /// Removes the given range from the set. + member x.Remove(r : Range1ui) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained UInt32.MaxValue and we have not overflown, there is still a range [max, UInt32.MaxValue] + let fixRightBoundary = + if x.HasMaxValue && not overflow then + MapExt.add max HalfRangeKind.Left + else + id + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.empty + |> fixRightBoundary - let remove (range : Range1i) (t : RangeSet) = - let rangeMax = range.Max + 1 + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> fixRightBoundary - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Right + |> fixRightBoundary - let max = geq rangeMax - let min = leq range.Min + | ValueNone, ValueSome HalfRangeKind.Left -> + rm - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Empty } + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add max HalfRangeKind.Left - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l } - | Geq _ -> { root = l |> FingerTreeNode.append mm min } + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + MapExt.union lm rm - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } - | Geq _ -> { root = r } + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right); struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } + | _ -> + failwithf "impossible" - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ui(newStore) - let ofSeq (s : seq) = - let mutable res = empty - for e in s do res <- insert e res + /// Returns the union of the set with the given set. + member inline x.Union(other : RangeSet1ui) = + let mutable res = x + for r in other do + res <- res.Add r res - let inline ofList (l : list) = ofSeq l - let inline ofArray (l : Range1i[]) = ofSeq l + /// Returns the intersection of the set with the given range. + member x.Intersect(r : Range1ui) = + if r.Max < r.Min then + empty + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let inner = + store + |> MapExt.splitAt min |> sndv + |> if not overflow then MapExt.splitAt max >> fstv else id - let toSeq (r : RangeSet) = r :> seq<_> - let toList (r : RangeSet) = r |> Seq.toList - let toArray (r : RangeSet) = r |> Seq.toArray + let newStore = + inner + |> if x.Contains r.Min then MapExt.add min HalfRangeKind.Left else id + |> if x.Contains r.Max && not overflow then MapExt.add max HalfRangeKind.Right else id - let inline min (t : RangeSet) = t.Min - let inline max (t : RangeSet) = t.Max - let inline range (t : RangeSet) = t.Range + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ui(newStore) - let window (window : Range1i) (set : RangeSet) = - let rangeMax = window.Max + 1 + member private x.TryFindLeftBoundary(v : uint32) = + let struct (l, s, _) = MapExt.neighboursV v store + match s with + | ValueSome (i, k) -> if k = HalfRangeKind.Left then ValueSome i else ValueNone + | _ -> + match l with + | ValueSome (i, HalfRangeKind.Left) -> ValueSome i + | _ -> ValueNone - let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange - let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + /// Returns whether the given value is contained in the range set. + member x.Contains(v : uint32) = + x.TryFindLeftBoundary v |> ValueOption.isSome - let inner = - match FingerTreeNode.lastOpt l with - | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner - | _ -> inner + /// Returns whether the given range is contained in the set. + member x.Contains(r : Range1ui) = + if r.Max < r.Min then false + elif r.Min = r.Max then x.Contains r.Min + else + match x.TryFindLeftBoundary r.Min, x.TryFindLeftBoundary r.Max with + | ValueSome l, ValueSome r -> l = r + | _ -> false - let inner = - match FingerTreeNode.firstOpt r with - | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner - | _ -> inner + /// Returns the number of disjoint ranges in the set. + member x.Count = + (store.Count + 1) / 2 - { root = inner } + /// Returns whether the set is empty. + member inline x.IsEmpty = + x.Count = 0 -[] -type private HalfRange64 = - struct - val mutable public IsMax : bool - val mutable public Value : int64 + /// Builds an array from the range set. + member x.ToArray() = + let arr = Array.zeroCreate x.Count - new(m,v) = { IsMax = m; Value = v } + let rec write (i : int) (l : struct (uint32 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + arr.[i] <- Range1ui(l, r - 1u) + write (i + 1) rest - override x.GetHashCode() = - if x.IsMax then 0 - else x.Value.GetHashCode() + | [struct (l, HalfRangeKind.Left)] when i = x.Count - 1 -> + arr.[i] <- Range1ui(l, UInt32.MaxValue) - override x.Equals o = - match o with - | :? HalfRange64 as o -> - x.IsMax = o.IsMax && x.Value = o.Value - | _ -> - false + | [_] -> failwith "bad RangeSet" + + | [] -> () + + store |> MapExt.toListV |> write 0 + arr + + /// Builds a list from the range set. + member x.ToList() = + let rec build (accum : Range1ui list) (l : struct (uint32 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + build (Range1ui(l, r - 1u) :: accum) rest + + | [struct (l, HalfRangeKind.Left)] -> + build (Range1ui(l, UInt32.MaxValue) :: accum) [] + + | [_] -> failwith "bad RangeSet" + + | [] -> List.rev accum + + store |> MapExt.toListV |> build [] + + /// Views the range set as a sequence. + member x.ToSeq() = + x :> seq<_> + + member inline private x.Equals(other : RangeSet1ui) = + store = other.Store + + override x.Equals(other : obj) = + match other with + | :? RangeSet1ui as o -> x.Equals o + | _ -> false + + override x.GetHashCode() = + store.GetHashCode() + + member private x.AsString = x.ToString() + + override x.ToString() = + let content = + x |> Seq.map (fun r -> + $"[{r.Min}, {r.Max}]" + ) + |> String.concat "; " + + $"ranges [{content}]" + + member x.GetEnumerator() = + new RangeSetEnumerator1ui((store :> seq<_>).GetEnumerator()) + + interface IEquatable with + member x.Equals(other) = x.Equals(other) + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator1ui((store :> seq<_>).GetEnumerator()) :> _ + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator1ui((store :> seq<_>).GetEnumerator()) :> _ + +// TODO: MapExt should use a struct enumerator and return it directly. +// That way we could get rid of allocations. +and RangeSetEnumerator1ui = + struct + val private Inner : IEnumerator> + val mutable private Left : KeyValuePair + val mutable private Right : KeyValuePair + + internal new (inner : IEnumerator>) = + { Inner = inner + Left = Unchecked.defaultof<_> + Right = Unchecked.defaultof<_> } - member x.CompareTo (o : HalfRange64) = - let c = x.Value.CompareTo o.Value - if c = 0 then - if x.IsMax = o.IsMax then 0 - else (if x.IsMax then 1 else -1) + member x.MoveNext() = + if x.Inner.MoveNext() then + x.Left <- x.Inner.Current + if x.Inner.MoveNext() then + x.Right <- x.Inner.Current + true + else + if x.Left.Value = HalfRangeKind.Left then + x.Right <- KeyValuePair(UInt32.MinValue, HalfRangeKind.Right) // MaxValue + 1 + true + else + failwithf "bad RangeSet" else - c + false - interface IComparable with - member x.CompareTo o = - match o with - | :? HalfRange64 as o -> x.CompareTo(o) - | _ -> failwith "uncomparable" + member x.Reset() = + x.Inner.Reset() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + member x.Current = + assert (x.Left.Value = HalfRangeKind.Left && x.Right.Value = HalfRangeKind.Right) + Range1ui(x.Left.Key, x.Right.Key - 1u) + + member x.Dispose() = + x.Inner.Dispose() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + interface IEnumerator with + member x.MoveNext() = x.MoveNext() + member x.Current = x.Current :> obj + member x.Reset() = x.Reset() + + interface IEnumerator with + member x.Dispose() = x.Dispose() + member x.Current = x.Current end +[] +module RangeSet1ui = + + /// Empty range set. + let empty = RangeSet1ui.Empty + + /// Returns the minimum value in the range set or UInt32.MaxValue if the range is empty. + let inline min (set : RangeSet1ui) = set.Min + + /// Returns the maximum value in the range set or UInt32.MinValue if the range is empty. + let inline max (set : RangeSet1ui) = set.Max + + /// Returns the total range spanned by the range set, i.e. [min, max]. + let inline range (set : RangeSet1ui) = set.Range + + let inline private getHalfRanges (r : Range1ui) = + [ struct (r.Min, HalfRangeKind.Left) + if r.Max < UInt32.MaxValue then struct (r.Max + 1u, HalfRangeKind.Right) ] + + let inline private ofRange (r : Range1ui) = + RangeSet1ui(MapExt.ofListV <| getHalfRanges r) + + let private ofRanges (ranges : seq) = + let halves = + ranges + |> Seq.toList + |> List.collect getHalfRanges + |> List.sortBy fstv + + let mutable level = 0 + let result = ResizeArray() + + for (struct (i, k) as h) in halves do + if k = HalfRangeKind.Left then + if level = 0 then result.Add h + level <- level + 1 + else + level <- level - 1 + if level = 0 then result.Add h + + RangeSet1ui(MapExt.ofSeqV result) + + /// Builds a range set of the given list of ranges. + let ofList (ranges : Range1ui list) = + match ranges with + | [] -> empty + | [r] -> ofRange r + | _ -> ofRanges ranges + /// Builds a range set of the given array of ranges. + let ofArray (ranges : Range1ui[]) = + if ranges.Length = 0 then empty + elif ranges.Length = 1 then ofRange ranges.[0] + else ofRanges ranges + + /// Builds a range set of the given sequence of ranges. + let inline ofSeq (ranges : seq) = + ofList <| Seq.toList ranges + + /// Adds the given range to the set. + let inline add (range : Range1ui) (set : RangeSet1ui) = set.Add range + + [] + let inline insert (range : Range1ui) (set : RangeSet1ui) = set.Add range + + /// Removes the given range from the set. + let inline remove (range : Range1ui) (set : RangeSet1ui) = set.Remove range + + /// Returns the union of two sets. + let inline union (l : RangeSet1ui) (r : RangeSet1ui) = l.Union r + + /// Returns the intersection of the set with the given range. + let inline intersect (range : Range1ui) (set : RangeSet1ui) = set.Intersect range + + [] + let inline window (range : Range1ui) (set : RangeSet1ui) = intersect range set + + /// Returns whether the given value is contained in the range set. + let inline contains (value : uint32) (set : RangeSet1ui) = set.Contains value + + /// Returns whether the given range is contained in the set. + let inline containsRange (range : Range1ui) (set : RangeSet1ui) = set.Contains range + + /// Returns the number of disjoint ranges in the set. + let inline count (set : RangeSet1ui) = set.Count + + /// Returns whether the set is empty. + let inline isEmpty (set : RangeSet1ui) = set.IsEmpty + + /// Views the range set as a sequence. + let inline toSeq (set : RangeSet1ui) = set :> seq<_> + + /// Builds a list from the range set. + let inline toList (set : RangeSet1ui) = set.ToList() + + /// Builds an array from the range set. + let inline toArray (set : RangeSet1ui) = set.ToArray() + + +/// Set of ranges where overlapping and neighboring ranges are coalesced. +/// Note that ranges describe closed intervals. [] -type RangeSet64 = private { root : FingerTreeNode } with - - member private x.AsString = - x |> Seq.map (sprintf "%A") - |> String.concat "; " - |> sprintf "set [%s]" +type RangeSet1l internal (store : MapExt) = + static let empty = RangeSet1l(MapExt.empty) + + /// Empty range set. + static member Empty = empty + member inline private x.Store = store + + // We cannot directly describe a range that ends at Int64.MaxValue since the right half-range is inserted + // at max + 1. In that case the right-half range will be missing and the total count is odd. + member inline private x.HasMaxValue = store.Count % 2 = 1 + + /// Returns the minimum value in the range set or Int64.MaxValue if the range is empty. member x.Min = - match x.root |> FingerTreeNode.firstOpt with - | Some f -> f.Value + match store.TryMinKeyV with + | ValueSome min -> min | _ -> Int64.MaxValue + /// Returns the maximum value in the range set or Int64.MinValue if the range is empty. member x.Max = - match x.root |> FingerTreeNode.lastOpt with - | Some f -> f.Value - | _ -> Int64.MinValue + if x.HasMaxValue then Int64.MaxValue + else + match store.TryMaxKeyV with + | ValueSome max -> max - 1L + | _ -> Int64.MinValue + + /// Returns the total range spanned by the range set, i.e. [min, max]. + member inline x.Range = + Range1l(x.Min, x.Max) + + /// Adds the given range to the set. + member x.Add(r : Range1l) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained Int64.MaxValue or we have overflown, we must not add an explicit right half-range. + // Int64.MaxValue is stored implicitly. + let fixRightBoundary = + if x.HasMaxValue || overflow then + id + else + MapExt.add max HalfRangeKind.Right + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.ofListV [ + struct (min, HalfRangeKind.Left) + ] + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Left + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + |> MapExt.add min HalfRangeKind.Left + |> MapExt.add max HalfRangeKind.Right + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add min HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left); struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + MapExt.union lm rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1l(newStore) + + /// Removes the given range from the set. + member x.Remove(r : Range1l) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained Int64.MaxValue and we have not overflown, there is still a range [max, Int64.MaxValue] + let fixRightBoundary = + if x.HasMaxValue && not overflow then + MapExt.add max HalfRangeKind.Left + else + id + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.empty + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Right + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add max HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + MapExt.union lm rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right); struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1l(newStore) + + /// Returns the union of the set with the given set. + member inline x.Union(other : RangeSet1l) = + let mutable res = x + for r in other do + res <- res.Add r + res + + /// Returns the intersection of the set with the given range. + member x.Intersect(r : Range1l) = + if r.Max < r.Min then + empty + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let inner = + store + |> MapExt.splitAt min |> sndv + |> if not overflow then MapExt.splitAt max >> fstv else id + + let newStore = + inner + |> if x.Contains r.Min then MapExt.add min HalfRangeKind.Left else id + |> if x.Contains r.Max && not overflow then MapExt.add max HalfRangeKind.Right else id + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1l(newStore) + + member private x.TryFindLeftBoundary(v : int64) = + let struct (l, s, _) = MapExt.neighboursV v store + match s with + | ValueSome (i, k) -> if k = HalfRangeKind.Left then ValueSome i else ValueNone + | _ -> + match l with + | ValueSome (i, HalfRangeKind.Left) -> ValueSome i + | _ -> ValueNone + + /// Returns whether the given value is contained in the range set. + member x.Contains(v : int64) = + x.TryFindLeftBoundary v |> ValueOption.isSome + + /// Returns whether the given range is contained in the set. + member x.Contains(r : Range1l) = + if r.Max < r.Min then false + elif r.Min = r.Max then x.Contains r.Min + else + match x.TryFindLeftBoundary r.Min, x.TryFindLeftBoundary r.Max with + | ValueSome l, ValueSome r -> l = r + | _ -> false + + /// Returns the number of disjoint ranges in the set. + member x.Count = + (store.Count + 1) / 2 + + /// Returns whether the set is empty. + member inline x.IsEmpty = + x.Count = 0 + + /// Builds an array from the range set. + member x.ToArray() = + let arr = Array.zeroCreate x.Count + + let rec write (i : int) (l : struct (int64 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + arr.[i] <- Range1l(l, r - 1L) + write (i + 1) rest + + | [struct (l, HalfRangeKind.Left)] when i = x.Count - 1 -> + arr.[i] <- Range1l(l, Int64.MaxValue) + + | [_] -> failwith "bad RangeSet" + + | [] -> () + + store |> MapExt.toListV |> write 0 + arr + + /// Builds a list from the range set. + member x.ToList() = + let rec build (accum : Range1l list) (l : struct (int64 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + build (Range1l(l, r - 1L) :: accum) rest + + | [struct (l, HalfRangeKind.Left)] -> + build (Range1l(l, Int64.MaxValue) :: accum) [] + + | [_] -> failwith "bad RangeSet" + + | [] -> List.rev accum - member x.Range = - match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with - | Some min, Some max -> Range1l(min.Value, max.Value) - | _ -> Range1l.Invalid + store |> MapExt.toListV |> build [] + + /// Views the range set as a sequence. + member x.ToSeq() = + x :> seq<_> + + member inline private x.Equals(other : RangeSet1l) = + store = other.Store + + override x.Equals(other : obj) = + match other with + | :? RangeSet1l as o -> x.Equals o + | _ -> false + + override x.GetHashCode() = + store.GetHashCode() + + member private x.AsString = x.ToString() + + override x.ToString() = + let content = + x |> Seq.map (fun r -> + $"[{r.Min}, {r.Max}]" + ) + |> String.concat "; " + + $"ranges [{content}]" + + member x.GetEnumerator() = + new RangeSetEnumerator1l((store :> seq<_>).GetEnumerator()) + + interface IEquatable with + member x.Equals(other) = x.Equals(other) interface IEnumerable with - member x.GetEnumerator() = new RangeSet64Enumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + member x.GetEnumerator() = new RangeSetEnumerator1l((store :> seq<_>).GetEnumerator()) :> _ interface IEnumerable with - member x.GetEnumerator() = new RangeSet64Enumerator(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ + member x.GetEnumerator() = new RangeSetEnumerator1l((store :> seq<_>).GetEnumerator()) :> _ +// TODO: MapExt should use a struct enumerator and return it directly. +// That way we could get rid of allocations. +and RangeSetEnumerator1l = + struct + val private Inner : IEnumerator> + val mutable private Left : KeyValuePair + val mutable private Right : KeyValuePair -and private RangeSet64Enumerator(i : IEnumerator) = - - let mutable last = HalfRange64() - let mutable current = HalfRange64() - - member x.Current = Range1l(last.Value, current.Value - 1L) + internal new (inner : IEnumerator>) = + { Inner = inner + Left = Unchecked.defaultof<_> + Right = Unchecked.defaultof<_> } - interface IEnumerator with member x.MoveNext() = - if i.MoveNext() then - last <- i.Current - if i.MoveNext() then - current <- i.Current + if x.Inner.MoveNext() then + x.Left <- x.Inner.Current + if x.Inner.MoveNext() then + x.Right <- x.Inner.Current true else - false + if x.Left.Value = HalfRangeKind.Left then + x.Right <- KeyValuePair(Int64.MinValue, HalfRangeKind.Right) // MaxValue + 1 + true + else + failwithf "bad RangeSet" else false - member x.Current = x.Current :> obj - member x.Reset() = - i.Reset() - - interface IEnumerator with - member x.Current = x.Current - member x.Dispose() = i.Dispose() + x.Inner.Reset() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + member x.Current = + assert (x.Left.Value = HalfRangeKind.Left && x.Right.Value = HalfRangeKind.Right) + Range1l(x.Left.Key, x.Right.Key - 1L) + + member x.Dispose() = + x.Inner.Dispose() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + interface IEnumerator with + member x.MoveNext() = x.MoveNext() + member x.Current = x.Current :> obj + member x.Reset() = x.Reset() + + interface IEnumerator with + member x.Dispose() = x.Dispose() + member x.Current = x.Current + end [] -module RangeSet64 = - let private mm = - { - quantify = fun (r : HalfRange64) -> r - mempty = HalfRange64(false, Int64.MinValue) - mappend = fun l r -> if l.CompareTo r > 0 then l else r - } +module RangeSet1l = + + /// Empty range set. + let empty = RangeSet1l.Empty + + /// Returns the minimum value in the range set or Int64.MaxValue if the range is empty. + let inline min (set : RangeSet1l) = set.Min + + /// Returns the maximum value in the range set or Int64.MinValue if the range is empty. + let inline max (set : RangeSet1l) = set.Max + + /// Returns the total range spanned by the range set, i.e. [min, max]. + let inline range (set : RangeSet1l) = set.Range + + let inline private getHalfRanges (r : Range1l) = + [ struct (r.Min, HalfRangeKind.Left) + if r.Max < Int64.MaxValue then struct (r.Max + 1L, HalfRangeKind.Right) ] + + let inline private ofRange (r : Range1l) = + RangeSet1l(MapExt.ofListV <| getHalfRanges r) + + let private ofRanges (ranges : seq) = + let halves = + ranges + |> Seq.toList + |> List.collect getHalfRanges + |> List.sortBy fstv + + let mutable level = 0 + let result = ResizeArray() + + for (struct (i, k) as h) in halves do + if k = HalfRangeKind.Left then + if level = 0 then result.Add h + level <- level + 1 + else + level <- level - 1 + if level = 0 then result.Add h + + RangeSet1l(MapExt.ofSeqV result) + + /// Builds a range set of the given list of ranges. + let ofList (ranges : Range1l list) = + match ranges with + | [] -> empty + | [r] -> ofRange r + | _ -> ofRanges ranges + + /// Builds a range set of the given array of ranges. + let ofArray (ranges : Range1l[]) = + if ranges.Length = 0 then empty + elif ranges.Length = 1 then ofRange ranges.[0] + else ofRanges ranges + + /// Builds a range set of the given sequence of ranges. + let inline ofSeq (ranges : seq) = + ofList <| Seq.toList ranges + + /// Adds the given range to the set. + let inline add (range : Range1l) (set : RangeSet1l) = set.Add range + + [] + let inline insert (range : Range1l) (set : RangeSet1l) = set.Add range + + /// Removes the given range from the set. + let inline remove (range : Range1l) (set : RangeSet1l) = set.Remove range + + /// Returns the union of two sets. + let inline union (l : RangeSet1l) (r : RangeSet1l) = l.Union r + + /// Returns the intersection of the set with the given range. + let inline intersect (range : Range1l) (set : RangeSet1l) = set.Intersect range + + [] + let inline window (range : Range1l) (set : RangeSet1l) = intersect range set + + /// Returns whether the given value is contained in the range set. + let inline contains (value : int64) (set : RangeSet1l) = set.Contains value - let private minRange = HalfRange64(false, Int64.MinValue) + /// Returns whether the given range is contained in the set. + let inline containsRange (range : Range1l) (set : RangeSet1l) = set.Contains range - let inline private leq v = HalfRange64(true, v) - let inline private geq v = HalfRange64(false, v) + /// Returns the number of disjoint ranges in the set. + let inline count (set : RangeSet1l) = set.Count - let inline private (|Leq|Geq|) (r : HalfRange64) = - if r.IsMax then Leq r.Value - else Geq r.Value + /// Returns whether the set is empty. + let inline isEmpty (set : RangeSet1l) = set.IsEmpty - let empty = { root = Empty } + /// Views the range set as a sequence. + let inline toSeq (set : RangeSet1l) = set :> seq<_> - let insert (range : Range1l) (t : RangeSet64) = - let rangeMax = range.Max + 1L + /// Builds a list from the range set. + let inline toList (set : RangeSet1l) = set.ToList() - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + /// Builds an array from the range set. + let inline toArray (set : RangeSet1l) = set.ToArray() - let max = leq rangeMax - let min = geq range.Min - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Deep(max, One(min), Empty, One(max)) } +/// Set of ranges where overlapping and neighboring ranges are coalesced. +/// Note that ranges describe closed intervals. +[] +type RangeSet1ul internal (store : MapExt) = + static let empty = RangeSet1ul(MapExt.empty) - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } - | Geq _ -> { root = l |> FingerTreeNode.append mm max } + /// Empty range set. + static member Empty = empty - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } - | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } + member inline private x.Store = store - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } + // We cannot directly describe a range that ends at UInt64.MaxValue since the right half-range is inserted + // at max + 1. In that case the right-half range will be missing and the total count is odd. + member inline private x.HasMaxValue = store.Count % 2 = 1 - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } + /// Returns the minimum value in the range set or UInt64.MaxValue if the range is empty. + member x.Min = + match store.TryMinKeyV with + | ValueSome min -> min + | _ -> UInt64.MaxValue - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } + /// Returns the maximum value in the range set or UInt64.MinValue if the range is empty. + member x.Max = + if x.HasMaxValue then UInt64.MaxValue + else + match store.TryMaxKeyV with + | ValueSome max -> max - 1UL + | _ -> UInt64.MinValue + + /// Returns the total range spanned by the range set, i.e. [min, max]. + member inline x.Range = + Range1ul(x.Min, x.Max) + + /// Adds the given range to the set. + member x.Add(r : Range1ul) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained UInt64.MaxValue or we have overflown, we must not add an explicit right half-range. + // UInt64.MaxValue is stored implicitly. + let fixRightBoundary = + if x.HasMaxValue || overflow then + id + else + MapExt.add max HalfRangeKind.Right + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.ofListV [ + struct (min, HalfRangeKind.Left) + ] + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Left + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + |> MapExt.add min HalfRangeKind.Left + |> MapExt.add max HalfRangeKind.Right + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add min HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left); struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + MapExt.union lm rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ul(newStore) + + /// Removes the given range from the set. + member x.Remove(r : Range1ul) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained UInt64.MaxValue and we have not overflown, there is still a range [max, UInt64.MaxValue] + let fixRightBoundary = + if x.HasMaxValue && not overflow then + MapExt.add max HalfRangeKind.Left + else + id - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.empty + |> fixRightBoundary - let remove (range : Range1l) (t : RangeSet64) = - let rangeMax = range.Max + 1L + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> fixRightBoundary - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Right + |> fixRightBoundary - let max = geq rangeMax - let min = leq range.Min + | ValueNone, ValueSome HalfRangeKind.Left -> + rm - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Empty } + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add max HalfRangeKind.Left - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l } - | Geq _ -> { root = l |> FingerTreeNode.append mm min } + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + MapExt.union lm rm - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } - | Geq _ -> { root = r } + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right); struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } + | _ -> + failwithf "impossible" - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ul(newStore) - let ofSeq (s : seq) = - let mutable res = empty - for e in s do res <- insert e res + /// Returns the union of the set with the given set. + member inline x.Union(other : RangeSet1ul) = + let mutable res = x + for r in other do + res <- res.Add r res - let inline ofList (l : list) = ofSeq l - let inline ofArray (l : Range1l[]) = ofSeq l + /// Returns the intersection of the set with the given range. + member x.Intersect(r : Range1ul) = + if r.Max < r.Min then + empty + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let inner = + store + |> MapExt.splitAt min |> sndv + |> if not overflow then MapExt.splitAt max >> fstv else id + + let newStore = + inner + |> if x.Contains r.Min then MapExt.add min HalfRangeKind.Left else id + |> if x.Contains r.Max && not overflow then MapExt.add max HalfRangeKind.Right else id + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + RangeSet1ul(newStore) + + member private x.TryFindLeftBoundary(v : uint64) = + let struct (l, s, _) = MapExt.neighboursV v store + match s with + | ValueSome (i, k) -> if k = HalfRangeKind.Left then ValueSome i else ValueNone + | _ -> + match l with + | ValueSome (i, HalfRangeKind.Left) -> ValueSome i + | _ -> ValueNone + + /// Returns whether the given value is contained in the range set. + member x.Contains(v : uint64) = + x.TryFindLeftBoundary v |> ValueOption.isSome + + /// Returns whether the given range is contained in the set. + member x.Contains(r : Range1ul) = + if r.Max < r.Min then false + elif r.Min = r.Max then x.Contains r.Min + else + match x.TryFindLeftBoundary r.Min, x.TryFindLeftBoundary r.Max with + | ValueSome l, ValueSome r -> l = r + | _ -> false + + /// Returns the number of disjoint ranges in the set. + member x.Count = + (store.Count + 1) / 2 + + /// Returns whether the set is empty. + member inline x.IsEmpty = + x.Count = 0 + + /// Builds an array from the range set. + member x.ToArray() = + let arr = Array.zeroCreate x.Count + + let rec write (i : int) (l : struct (uint64 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + arr.[i] <- Range1ul(l, r - 1UL) + write (i + 1) rest + + | [struct (l, HalfRangeKind.Left)] when i = x.Count - 1 -> + arr.[i] <- Range1ul(l, UInt64.MaxValue) + + | [_] -> failwith "bad RangeSet" + + | [] -> () + + store |> MapExt.toListV |> write 0 + arr + + /// Builds a list from the range set. + member x.ToList() = + let rec build (accum : Range1ul list) (l : struct (uint64 * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + build (Range1ul(l, r - 1UL) :: accum) rest + + | [struct (l, HalfRangeKind.Left)] -> + build (Range1ul(l, UInt64.MaxValue) :: accum) [] + + | [_] -> failwith "bad RangeSet" + + | [] -> List.rev accum + + store |> MapExt.toListV |> build [] + + /// Views the range set as a sequence. + member x.ToSeq() = + x :> seq<_> + + member inline private x.Equals(other : RangeSet1ul) = + store = other.Store + + override x.Equals(other : obj) = + match other with + | :? RangeSet1ul as o -> x.Equals o + | _ -> false + + override x.GetHashCode() = + store.GetHashCode() + + member private x.AsString = x.ToString() + + override x.ToString() = + let content = + x |> Seq.map (fun r -> + $"[{r.Min}, {r.Max}]" + ) + |> String.concat "; " + + $"ranges [{content}]" + + member x.GetEnumerator() = + new RangeSetEnumerator1ul((store :> seq<_>).GetEnumerator()) + + interface IEquatable with + member x.Equals(other) = x.Equals(other) + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator1ul((store :> seq<_>).GetEnumerator()) :> _ + + interface IEnumerable with + member x.GetEnumerator() = new RangeSetEnumerator1ul((store :> seq<_>).GetEnumerator()) :> _ + +// TODO: MapExt should use a struct enumerator and return it directly. +// That way we could get rid of allocations. +and RangeSetEnumerator1ul = + struct + val private Inner : IEnumerator> + val mutable private Left : KeyValuePair + val mutable private Right : KeyValuePair + + internal new (inner : IEnumerator>) = + { Inner = inner + Left = Unchecked.defaultof<_> + Right = Unchecked.defaultof<_> } + + member x.MoveNext() = + if x.Inner.MoveNext() then + x.Left <- x.Inner.Current + if x.Inner.MoveNext() then + x.Right <- x.Inner.Current + true + else + if x.Left.Value = HalfRangeKind.Left then + x.Right <- KeyValuePair(UInt64.MinValue, HalfRangeKind.Right) // MaxValue + 1 + true + else + failwithf "bad RangeSet" + else + false + + member x.Reset() = + x.Inner.Reset() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + member x.Current = + assert (x.Left.Value = HalfRangeKind.Left && x.Right.Value = HalfRangeKind.Right) + Range1ul(x.Left.Key, x.Right.Key - 1UL) + + member x.Dispose() = + x.Inner.Dispose() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + interface IEnumerator with + member x.MoveNext() = x.MoveNext() + member x.Current = x.Current :> obj + member x.Reset() = x.Reset() + + interface IEnumerator with + member x.Dispose() = x.Dispose() + member x.Current = x.Current + end + +[] +module RangeSet1ul = + + /// Empty range set. + let empty = RangeSet1ul.Empty + + /// Returns the minimum value in the range set or UInt64.MaxValue if the range is empty. + let inline min (set : RangeSet1ul) = set.Min + + /// Returns the maximum value in the range set or UInt64.MinValue if the range is empty. + let inline max (set : RangeSet1ul) = set.Max + + /// Returns the total range spanned by the range set, i.e. [min, max]. + let inline range (set : RangeSet1ul) = set.Range + + let inline private getHalfRanges (r : Range1ul) = + [ struct (r.Min, HalfRangeKind.Left) + if r.Max < UInt64.MaxValue then struct (r.Max + 1UL, HalfRangeKind.Right) ] + + let inline private ofRange (r : Range1ul) = + RangeSet1ul(MapExt.ofListV <| getHalfRanges r) + + let private ofRanges (ranges : seq) = + let halves = + ranges + |> Seq.toList + |> List.collect getHalfRanges + |> List.sortBy fstv + + let mutable level = 0 + let result = ResizeArray() + + for (struct (i, k) as h) in halves do + if k = HalfRangeKind.Left then + if level = 0 then result.Add h + level <- level + 1 + else + level <- level - 1 + if level = 0 then result.Add h + + RangeSet1ul(MapExt.ofSeqV result) + + /// Builds a range set of the given list of ranges. + let ofList (ranges : Range1ul list) = + match ranges with + | [] -> empty + | [r] -> ofRange r + | _ -> ofRanges ranges + + /// Builds a range set of the given array of ranges. + let ofArray (ranges : Range1ul[]) = + if ranges.Length = 0 then empty + elif ranges.Length = 1 then ofRange ranges.[0] + else ofRanges ranges + + /// Builds a range set of the given sequence of ranges. + let inline ofSeq (ranges : seq) = + ofList <| Seq.toList ranges + + /// Adds the given range to the set. + let inline add (range : Range1ul) (set : RangeSet1ul) = set.Add range + + [] + let inline insert (range : Range1ul) (set : RangeSet1ul) = set.Add range + + /// Removes the given range from the set. + let inline remove (range : Range1ul) (set : RangeSet1ul) = set.Remove range + + /// Returns the union of two sets. + let inline union (l : RangeSet1ul) (r : RangeSet1ul) = l.Union r + + /// Returns the intersection of the set with the given range. + let inline intersect (range : Range1ul) (set : RangeSet1ul) = set.Intersect range + + [] + let inline window (range : Range1ul) (set : RangeSet1ul) = intersect range set + + /// Returns whether the given value is contained in the range set. + let inline contains (value : uint64) (set : RangeSet1ul) = set.Contains value - let toSeq (r : RangeSet64) = r :> seq<_> - let toList (r : RangeSet64) = r |> Seq.toList - let toArray (r : RangeSet64) = r |> Seq.toArray + /// Returns whether the given range is contained in the set. + let inline containsRange (range : Range1ul) (set : RangeSet1ul) = set.Contains range - let inline min (t : RangeSet64) = t.Min - let inline max (t : RangeSet64) = t.Max - let inline range (t : RangeSet64) = t.Range + /// Returns the number of disjoint ranges in the set. + let inline count (set : RangeSet1ul) = set.Count - let window (window : Range1l) (set : RangeSet64) = - let rangeMax = window.Max + 1L + /// Returns whether the set is empty. + let inline isEmpty (set : RangeSet1ul) = set.IsEmpty - let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange - let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + /// Views the range set as a sequence. + let inline toSeq (set : RangeSet1ul) = set :> seq<_> - let inner = - match FingerTreeNode.lastOpt l with - | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner - | _ -> inner + /// Builds a list from the range set. + let inline toList (set : RangeSet1ul) = set.ToList() - let inner = - match FingerTreeNode.firstOpt r with - | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner - | _ -> inner + /// Builds an array from the range set. + let inline toArray (set : RangeSet1ul) = set.ToArray() - { root = inner } diff --git a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_template.fs b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_template.fs index 87c8143c9..412f4e753 100644 --- a/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_template.fs +++ b/src/Aardvark.Base.FSharp/Datastructures/Immutable/RangeSet_template.fs @@ -3,235 +3,500 @@ open System open System.Collections open System.Collections.Generic -open FingerTreeImplementation - -//# foreach (var isLong in new[] { false, true }) { -//# var halfrange = isLong ? "HalfRange64" : "HalfRange"; -//# var rangeset = isLong ? "RangeSet64" : "RangeSet"; -//# var rangesetenumerator = isLong ? "RangeSet64Enumerator" : "RangeSetEnumerator"; -//# var range = isLong ? "Range1l" : "Range1i"; -//# var systype = isLong ? "Int64" : "Int32"; -//# var ft = isLong ? "int64" : "int32"; -//# var one = isLong ? "1L" : "1"; -[] -type private __halfrange__ = - struct - val mutable public IsMax : bool - val mutable public Value : __ft__ +open Aardvark.Base + +type internal HalfRangeKind = + | Left = 0 + | Right = 1 + +module private RangeSetUtils = + + let inline inc (value : 'T) = + let res = value + LanguagePrimitives.GenericOne<'T> + if res = Constant<'T>.ParseableMinValue then struct (Constant<'T>.ParseableMaxValue, true) + else struct (res, false) + + module MapExt = + let inline splitAt (key : 'K) (map : MapExt<'K, 'V>) = + let struct (l, _, _, _, r) = MapExt.splitV key map + struct (l, r) + + let inline tryMinValue (map : MapExt<'K, 'V>) = + MapExt.tryMinV map |> ValueOption.map (fun mk -> map.[mk]) + + let inline tryMaxValue (map : MapExt<'K, 'V>) = + MapExt.tryMaxV map |> ValueOption.map (fun mk -> map.[mk]) + + let inline maxValue (map : MapExt<'K, 'V>) = + map.[MapExt.max map] + +open RangeSetUtils + +//# var ltypes = new[] { "int32", "uint32", "int64", "uint64" }; +//# var sltypes = new[] { "Int32", "UInt32", "Int64", "UInt64" }; +//# var suffixes = new[] { "i", "ui", "l", "ul" }; +//# var literalSuffixes = new[] { "", "u", "L", "UL" }; +//# +//# for (int i = 0; i < ltypes.Length; i++) { +//# var ltype = ltypes[i]; +//# var suffix = suffixes[i]; +//# var range = "Range1" + suffix; +//# var rangeset = "RangeSet1" + suffix; +//# var enumerator = "RangeSetEnumerator1" + suffix; +//# var one = "1" + literalSuffixes[i]; +//# var minvalue = sltypes[i] + ".MinValue"; +//# var maxvalue = sltypes[i] + ".MaxValue"; +/// Set of ranges where overlapping and neighboring ranges are coalesced. +/// Note that ranges describe closed intervals. +[] +type __rangeset__ internal (store : MapExt<__ltype__, HalfRangeKind>) = + static let empty = __rangeset__(MapExt.empty) - new(m,v) = { IsMax = m; Value = v } + /// Empty range set. + static member Empty = empty - override x.GetHashCode() = - if x.IsMax then 0 - else x.Value.GetHashCode() + member inline private x.Store = store - override x.Equals o = - match o with - | :? __halfrange__ as o -> - x.IsMax = o.IsMax && x.Value = o.Value - | _ -> - false + // We cannot directly describe a range that ends at __maxvalue__ since the right half-range is inserted + // at max + 1. In that case the right-half range will be missing and the total count is odd. + member inline private x.HasMaxValue = store.Count % 2 = 1 - member x.CompareTo (o : __halfrange__) = - let c = x.Value.CompareTo o.Value - if c = 0 then - if x.IsMax = o.IsMax then 0 - else (if x.IsMax then 1 else -1) - else - c + /// Returns the minimum value in the range set or __maxvalue__ if the range is empty. + member x.Min = + match store.TryMinKeyV with + | ValueSome min -> min + | _ -> __maxvalue__ - interface IComparable with - member x.CompareTo o = - match o with - | :? __halfrange__ as o -> x.CompareTo(o) - | _ -> failwith "uncomparable" - end + /// Returns the maximum value in the range set or __minvalue__ if the range is empty. + member x.Max = + if x.HasMaxValue then __maxvalue__ + else + match store.TryMaxKeyV with + | ValueSome max -> max - __one__ + | _ -> __minvalue__ + + /// Returns the total range spanned by the range set, i.e. [min, max]. + member inline x.Range = + __range__(x.Min, x.Max) + + /// Adds the given range to the set. + member x.Add(r : __range__) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained __maxvalue__ or we have overflown, we must not add an explicit right half-range. + // __maxvalue__ is stored implicitly. + let fixRightBoundary = + if x.HasMaxValue || overflow then + id + else + MapExt.add max HalfRangeKind.Right + + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.ofListV [ + struct (min, HalfRangeKind.Left) + ] + |> fixRightBoundary + + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Left + |> fixRightBoundary + + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> fixRightBoundary + + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + |> MapExt.add min HalfRangeKind.Left + |> MapExt.add max HalfRangeKind.Right + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add min HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left); struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + MapExt.union lm rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + __rangeset__(newStore) + + /// Removes the given range from the set. + member x.Remove(r : __range__) = + if r.Max < r.Min then + x + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let struct (lm, inner) = MapExt.splitAt min store + let rm = + if overflow then MapExt.empty + else sndv <| MapExt.splitAt max inner + + let before = MapExt.tryMaxValue lm + let after = MapExt.tryMinValue rm + + // If the set contained __maxvalue__ and we have not overflown, there is still a range [max, __maxvalue__] + let fixRightBoundary = + if x.HasMaxValue && not overflow then + MapExt.add max HalfRangeKind.Left + else + id + let newStore = + match before, after with + | ValueNone, ValueNone -> + MapExt.empty + |> fixRightBoundary -[] -type __rangeset__ = private { root : FingerTreeNode<__halfrange__, __halfrange__> } with - - member private x.AsString = - x |> Seq.map (sprintf "%A") - |> String.concat "; " - |> sprintf "set [%s]" + | ValueSome HalfRangeKind.Right, ValueNone -> + lm + |> fixRightBoundary - member x.Min = - match x.root |> FingerTreeNode.firstOpt with - | Some f -> f.Value - | _ -> __systype__.MaxValue + | ValueSome HalfRangeKind.Left, ValueNone -> + lm + |> MapExt.add min HalfRangeKind.Right + |> fixRightBoundary - member x.Max = - match x.root |> FingerTreeNode.lastOpt with - | Some f -> f.Value - | _ -> __systype__.MinValue + | ValueNone, ValueSome HalfRangeKind.Left -> + rm + + | ValueNone, ValueSome HalfRangeKind.Right -> + rm + |> MapExt.add max HalfRangeKind.Left + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Left -> + MapExt.union lm rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Left -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Right, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | ValueSome HalfRangeKind.Left, ValueSome HalfRangeKind.Right -> + let self = MapExt.ofListV [ struct (min, HalfRangeKind.Right); struct (max, HalfRangeKind.Left) ] + MapExt.union (MapExt.union lm self) rm + + | _ -> + failwithf "impossible" + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + __rangeset__(newStore) + + /// Returns the union of the set with the given set. + member inline x.Union(other : __rangeset__) = + let mutable res = x + for r in other do + res <- res.Add r + res + + /// Returns the intersection of the set with the given range. + member x.Intersect(r : __range__) = + if r.Max < r.Min then + empty + else + let min = r.Min + let struct (max, overflow) = inc r.Max + + let inner = + store + |> MapExt.splitAt min |> sndv + |> if not overflow then MapExt.splitAt max >> fstv else id + + let newStore = + inner + |> if x.Contains r.Min then MapExt.add min HalfRangeKind.Left else id + |> if x.Contains r.Max && not overflow then MapExt.add max HalfRangeKind.Right else id + + assert (newStore.Count % 2 = 0 || MapExt.maxValue newStore = HalfRangeKind.Left) + __rangeset__(newStore) + + member private x.TryFindLeftBoundary(v : __ltype__) = + let struct (l, s, _) = MapExt.neighboursV v store + match s with + | ValueSome (i, k) -> if k = HalfRangeKind.Left then ValueSome i else ValueNone + | _ -> + match l with + | ValueSome (i, HalfRangeKind.Left) -> ValueSome i + | _ -> ValueNone + + /// Returns whether the given value is contained in the range set. + member x.Contains(v : __ltype__) = + x.TryFindLeftBoundary v |> ValueOption.isSome + + /// Returns whether the given range is contained in the set. + member x.Contains(r : __range__) = + if r.Max < r.Min then false + elif r.Min = r.Max then x.Contains r.Min + else + match x.TryFindLeftBoundary r.Min, x.TryFindLeftBoundary r.Max with + | ValueSome l, ValueSome r -> l = r + | _ -> false + + /// Returns the number of disjoint ranges in the set. + member x.Count = + (store.Count + 1) / 2 + + /// Returns whether the set is empty. + member inline x.IsEmpty = + x.Count = 0 + + /// Builds an array from the range set. + member x.ToArray() = + let arr = Array.zeroCreate x.Count + + let rec write (i : int) (l : struct (__ltype__ * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + arr.[i] <- __range__(l, r - __one__) + write (i + 1) rest - member x.Range = - match FingerTreeNode.firstOpt x.root, FingerTreeNode.lastOpt x.root with - | Some min, Some max -> __range__(min.Value, max.Value) - | _ -> __range__.Invalid + | [struct (l, HalfRangeKind.Left)] when i = x.Count - 1 -> + arr.[i] <- __range__(l, __maxvalue__) + + | [_] -> failwith "bad RangeSet" + + | [] -> () + + store |> MapExt.toListV |> write 0 + arr + + /// Builds a list from the range set. + member x.ToList() = + let rec build (accum : __range__ list) (l : struct (__ltype__ * HalfRangeKind) list) = + match l with + | struct (l, _) :: struct (r, _) :: rest -> + build (__range__(l, r - __one__) :: accum) rest + + | [struct (l, HalfRangeKind.Left)] -> + build (__range__(l, __maxvalue__) :: accum) [] + + | [_] -> failwith "bad RangeSet" + + | [] -> List.rev accum + + store |> MapExt.toListV |> build [] + + /// Views the range set as a sequence. + member x.ToSeq() = + x :> seq<_> + + member inline private x.Equals(other : __rangeset__) = + store = other.Store + + override x.Equals(other : obj) = + match other with + | :? __rangeset__ as o -> x.Equals o + | _ -> false + + override x.GetHashCode() = + store.GetHashCode() + + member private x.AsString = x.ToString() + + override x.ToString() = + let content = + x |> Seq.map (fun r -> + $"[{r.Min}, {r.Max}]" + ) + |> String.concat "; " + + $"ranges [{content}]" + + member x.GetEnumerator() = + new __enumerator__((store :> seq<_>).GetEnumerator()) + + interface IEquatable<__rangeset__> with + member x.Equals(other) = x.Equals(other) interface IEnumerable with - member x.GetEnumerator() = new __rangesetenumerator__(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> IEnumerator + member x.GetEnumerator() = new __enumerator__((store :> seq<_>).GetEnumerator()) :> _ interface IEnumerable<__range__> with - member x.GetEnumerator() = new __rangesetenumerator__(FingerTreeImplementation.FingerTreeNode.getEnumeratorFw x.root) :> _ + member x.GetEnumerator() = new __enumerator__((store :> seq<_>).GetEnumerator()) :> _ +// TODO: MapExt should use a struct enumerator and return it directly. +// That way we could get rid of allocations. +and __enumerator__ = + struct + val private Inner : IEnumerator> + val mutable private Left : KeyValuePair<__ltype__, HalfRangeKind> + val mutable private Right : KeyValuePair<__ltype__, HalfRangeKind> -and private __rangesetenumerator__(i : IEnumerator<__halfrange__>) = - - let mutable last = __halfrange__() - let mutable current = __halfrange__() - - member x.Current = __range__(last.Value, current.Value - __one__) + internal new (inner : IEnumerator>) = + { Inner = inner + Left = Unchecked.defaultof<_> + Right = Unchecked.defaultof<_> } - interface IEnumerator with member x.MoveNext() = - if i.MoveNext() then - last <- i.Current - if i.MoveNext() then - current <- i.Current + if x.Inner.MoveNext() then + x.Left <- x.Inner.Current + if x.Inner.MoveNext() then + x.Right <- x.Inner.Current true else - false + if x.Left.Value = HalfRangeKind.Left then + x.Right <- KeyValuePair(__minvalue__, HalfRangeKind.Right) // MaxValue + 1 + true + else + failwithf "bad RangeSet" else false - member x.Current = x.Current :> obj - member x.Reset() = - i.Reset() - - interface IEnumerator<__range__> with - member x.Current = x.Current - member x.Dispose() = i.Dispose() + x.Inner.Reset() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + member x.Current = + assert (x.Left.Value = HalfRangeKind.Left && x.Right.Value = HalfRangeKind.Right) + __range__(x.Left.Key, x.Right.Key - __one__) + + member x.Dispose() = + x.Inner.Dispose() + x.Left <- Unchecked.defaultof<_> + x.Right <- Unchecked.defaultof<_> + + interface IEnumerator with + member x.MoveNext() = x.MoveNext() + member x.Current = x.Current :> obj + member x.Reset() = x.Reset() + + interface IEnumerator<__range__> with + member x.Dispose() = x.Dispose() + member x.Current = x.Current + end [] module __rangeset__ = - let private mm = - { - quantify = fun (r : __halfrange__) -> r - mempty = __halfrange__(false, __systype__.MinValue) - mappend = fun l r -> if l.CompareTo r > 0 then l else r - } - - let private minRange = __halfrange__(false, __systype__.MinValue) - let inline private leq v = __halfrange__(true, v) - let inline private geq v = __halfrange__(false, v) + /// Empty range set. + let empty = __rangeset__.Empty - let inline private (|Leq|Geq|) (r : __halfrange__) = - if r.IsMax then Leq r.Value - else Geq r.Value + /// Returns the minimum value in the range set or __maxvalue__ if the range is empty. + let inline min (set : __rangeset__) = set.Min - let empty = { root = Empty } + /// Returns the maximum value in the range set or __minvalue__ if the range is empty. + let inline max (set : __rangeset__) = set.Max - let insert (range : __range__) (t : __rangeset__) = - let rangeMax = range.Max + __one__ + /// Returns the total range spanned by the range set, i.e. [min, max]. + let inline range (set : __rangeset__) = set.Range - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + let inline private getHalfRanges (r : __range__) = + [ struct (r.Min, HalfRangeKind.Left) + if r.Max < __maxvalue__ then struct (r.Max + __one__, HalfRangeKind.Right) ] - let max = leq rangeMax - let min = geq range.Min + let inline private ofRange (r : __range__) = + __rangeset__(MapExt.ofListV <| getHalfRanges r) - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Deep(max, One(min), Empty, One(max)) } + let private ofRanges (ranges : seq<__range__>) = + let halves = + ranges + |> Seq.toList + |> List.collect getHalfRanges + |> List.sortBy fstv - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l |> FingerTreeNode.append mm min |> FingerTreeNode.append mm max } - | Geq _ -> { root = l |> FingerTreeNode.append mm max } + let mutable level = 0 + let result = ResizeArray() - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm min } - | Geq _ -> { root = r |> FingerTreeNode.prepend mm max |> FingerTreeNode.prepend mm min } - - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min;max] r } - - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } - - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } - - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } - - let remove (range : __range__) (t : __rangeset__) = - let rangeMax = range.Max + __one__ + for (struct (i, k) as h) in halves do + if k = HalfRangeKind.Left then + if level = 0 then result.Add h + level <- level + 1 + else + level <- level - 1 + if level = 0 then result.Add h - let (l,rest) = t.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo range.Min >= 0) minRange - let (_,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax > 0) minRange + __rangeset__(MapExt.ofSeqV result) - let max = geq rangeMax - let min = leq range.Min + /// Builds a range set of the given list of ranges. + let ofList (ranges : __range__ list) = + match ranges with + | [] -> empty + | [r] -> ofRange r + | _ -> ofRanges ranges - match FingerTreeNode.lastOpt l, FingerTreeNode.firstOpt r with - | None, None -> - { root = Empty } + /// Builds a range set of the given array of ranges. + let ofArray (ranges : __range__[]) = + if ranges.Length = 0 then empty + elif ranges.Length = 1 then ofRange ranges.[0] + else ofRanges ranges - | Some lmax, None -> - match lmax with - | Leq _ -> { root = l } - | Geq _ -> { root = l |> FingerTreeNode.append mm min } + /// Builds a range set of the given sequence of ranges. + let inline ofSeq (ranges : seq<__range__>) = + ofList <| Seq.toList ranges - | None, Some rmin -> - match rmin with - | Leq _ -> { root = r |> FingerTreeNode.prepend mm max } - | Geq _ -> { root = r } + /// Adds the given range to the set. + let inline add (range : __range__) (set : __rangeset__) = set.Add range - | Some lmax, Some rmin -> - match lmax, rmin with - | Leq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [] r } + [] + let inline insert (range : __range__) (set : __rangeset__) = set.Add range - | Geq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min; max] r } + /// Removes the given range from the set. + let inline remove (range : __range__) (set : __rangeset__) = set.Remove range - | Leq _, Leq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [max] r } + /// Returns the union of two sets. + let inline union (l : __rangeset__) (r : __rangeset__) = l.Union r - | Geq _, Geq _ -> - { root = FingerTreeNode.concatWithMiddle mm l [min] r } + /// Returns the intersection of the set with the given range. + let inline intersect (range : __range__) (set : __rangeset__) = set.Intersect range - let ofSeq (s : seq<__range__>) = - let mutable res = empty - for e in s do res <- insert e res - res + [] + let inline window (range : __range__) (set : __rangeset__) = intersect range set - let inline ofList (l : list<__range__>) = ofSeq l - let inline ofArray (l : __range__[]) = ofSeq l + /// Returns whether the given value is contained in the range set. + let inline contains (value : __ltype__) (set : __rangeset__) = set.Contains value - let toSeq (r : __rangeset__) = r :> seq<_> - let toList (r : __rangeset__) = r |> Seq.toList - let toArray (r : __rangeset__) = r |> Seq.toArray + /// Returns whether the given range is contained in the set. + let inline containsRange (range : __range__) (set : __rangeset__) = set.Contains range - let inline min (t : __rangeset__) = t.Min - let inline max (t : __rangeset__) = t.Max - let inline range (t : __rangeset__) = t.Range + /// Returns the number of disjoint ranges in the set. + let inline count (set : __rangeset__) = set.Count - let window (window : __range__) (set : __rangeset__) = - let rangeMax = window.Max + __one__ + /// Returns whether the set is empty. + let inline isEmpty (set : __rangeset__) = set.IsEmpty - let (l,rest) = set.root |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo window.Min > 0) minRange - let (inner,r) = rest |> FingerTreeNode.splitFirstRight mm (fun v -> v.Value.CompareTo rangeMax >= 0) minRange + /// Views the range set as a sequence. + let inline toSeq (set : __rangeset__) = set :> seq<_> - let inner = - match FingerTreeNode.lastOpt l with - | Some (Geq _) -> FingerTreeNode.prepend mm (geq window.Min) inner - | _ -> inner + /// Builds a list from the range set. + let inline toList (set : __rangeset__) = set.ToList() - let inner = - match FingerTreeNode.firstOpt r with - | Some (Leq _) -> FingerTreeNode.append mm (leq rangeMax) inner - | _ -> inner + /// Builds an array from the range set. + let inline toArray (set : __rangeset__) = set.ToArray() - { root = inner } //# } \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Interop/String.fs b/src/Aardvark.Base.FSharp/Interop/String.fs deleted file mode 100644 index 40353c6de..000000000 --- a/src/Aardvark.Base.FSharp/Interop/String.fs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Aardvark.Base - -open System - -[] -[] -module Strings = - let partRx = System.Text.RegularExpressions.Regex @"([A-Z][a-z0-9]*)[_]*" - - /// checks whether pattern is contained in str - let contains pattern (str : string) = str.Contains pattern - - let toLower (str : string) = str.ToLower() - let toUpper (str : string) = str.ToUpper() - - let inline split (sep : string) (str : string) = str.Split([| sep |], StringSplitOptions.None) - let inline startsWith (s : string) (str : string) = str.StartsWith s - let inline endsWith (s : string) (str : string) = str.EndsWith s - let inline trim (str : string) = str.Trim() - - - diff --git a/src/Aardvark.Base.FSharp/Prelude/AverageWindow.fs b/src/Aardvark.Base.FSharp/Math/AverageWindow.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Prelude/AverageWindow.fs rename to src/Aardvark.Base.FSharp/Math/AverageWindow.fs diff --git a/src/Aardvark.Base.FSharp/Interop/Converters.fs b/src/Aardvark.Base.FSharp/Math/Converters.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/Converters.fs rename to src/Aardvark.Base.FSharp/Math/Converters.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/Math.fs b/src/Aardvark.Base.FSharp/Math/Math.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Prelude/Math.fs rename to src/Aardvark.Base.FSharp/Math/Math.fs diff --git a/src/Aardvark.Base.FSharp/Interop/Matrix.fs b/src/Aardvark.Base.FSharp/Math/Matrix.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/Matrix.fs rename to src/Aardvark.Base.FSharp/Math/Matrix.fs diff --git a/src/Aardvark.Base.FSharp/SVDM33f.fs b/src/Aardvark.Base.FSharp/Math/SVDM33f.fs similarity index 100% rename from src/Aardvark.Base.FSharp/SVDM33f.fs rename to src/Aardvark.Base.FSharp/Math/SVDM33f.fs diff --git a/src/Aardvark.Base.FSharp/Interop/Vectors.fs b/src/Aardvark.Base.FSharp/Math/Vectors.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/Vectors.fs rename to src/Aardvark.Base.FSharp/Math/Vectors.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/FSLibExtensions.fs b/src/Aardvark.Base.FSharp/Prelude/FSLibExtensions.fs deleted file mode 100644 index c532e3e09..000000000 --- a/src/Aardvark.Base.FSharp/Prelude/FSLibExtensions.fs +++ /dev/null @@ -1,831 +0,0 @@ -namespace Aardvark.Base -#nowarn "9" -#nowarn "51" -#nowarn "44" - -open System -open FSharp.NativeInterop - -[] -module Prelude = - - let inc (a:byref) = a <- a + 1 - let dec (a:byref) = a <- a - 1 - - let inline isNull (a : 'a) = - match a with - | null -> true - | _ -> false - - module Map = - let union (l : Map<'k, 'v>) (r : Map<'k, 'v>) = - let mutable result = l - for KeyValue(k,v) in r do - result <- Map.add k v result - result - - let unionMany (input : seq>) = - (Map.empty, input) ||> Seq.fold union - - module Seq = - let iter' (f : 'a -> 'b) (s : seq<'a>) = - for i in s do - f i |> ignore - - let repeat (n : int) (f : unit -> unit) = - for i in 1..n do - f() - - let repeat' (n : int) (f : unit -> 'a) = - for i in 1..n do - f() |> ignore - - let partition (f : 'a -> bool) (xs : seq<'a>) = - let xs = xs |> Seq.map (fun a -> f a, a) |> Seq.cache - - (xs |> Seq.filter (fun (r,v) -> not r) |> Seq.map snd, xs |> Seq.filter (fun (r,v) -> r) |> Seq.map snd) - - let chooseOption (f : 'a -> Option<'b>) (xs : seq>) : seq> = - seq { - for x in xs do - match x with - | None -> () - | Some x -> yield f x - } - - let forany (f : 'a -> bool) (s : seq<'a>) = - let e = s.GetEnumerator() - let mutable r = false - while not r && e.MoveNext() do - if f e.Current then - r <- true - e.Dispose() - r - - let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (source : 'T seq) = - ((0, state), source) ||> Seq.fold (fun (i, state) x -> - (i + 1), folder i state x - ) |> snd - - open System.Collections - open System.Collections.Generic - - let atMost (n : int) (s : seq<'a>) : seq<'a> = - let newEnumerator() = - let input = s.GetEnumerator() - let remaining = ref n - { new IEnumerator<'a> with - member x.MoveNext() = - remaining := !remaining - 1 - !remaining >= 0 && input.MoveNext() - member x.Current : obj = input.Current :> obj - member x.Dispose() = input.Dispose() - member x.Reset() = input.Reset(); remaining := n - member x.Current : 'a = input.Current - } - - { new IEnumerable<'a> with - member x.GetEnumerator() : IEnumerator = newEnumerator() :> IEnumerator - member x.GetEnumerator() : IEnumerator<'a> = newEnumerator() - } - - - module List = - - //Experimental Results show that this implementation is faster than all other ones maintaining the list's order - let rec private partitionAcc (f : 'a -> bool) (source : list<'a>) (l : System.Collections.Generic.List<'a>) (r : System.Collections.Generic.List<'a>) = - match source with - | [] -> - let mutable ll = [] - let mutable rr = [] - - for i in 1..l.Count do - let i = l.Count - i - ll <- l.[i]::ll - - for i in 1..r.Count do - let i = r.Count - i - rr <- r.[i]::rr - - (ll,rr) - - | x::xs -> - if f x then - l.Add(x) - partitionAcc f xs l r - else - r.Add(x) - partitionAcc f xs l r - - let partition (f : 'a -> bool) (source : list<'a>) = - partitionAcc f source (System.Collections.Generic.List()) (System.Collections.Generic.List()) - - let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (list : 'T list) = - ((0, state), list) ||> List.fold (fun (i, state) x -> - (i + 1), folder i state x - ) |> snd - - module Array = - let rec private forany' (f : 'a -> bool) (index : int) (a : 'a[]) = - if index >= a.Length then false - else - if f a.[index] then true - else forany' f (index + 1) a - - let forany (f : 'a -> bool) (a : 'a[]) = - forany' f 0 a - - let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (array : 'T[]) = - ((0, state), array) ||> Array.fold (fun (i, state) x -> - (i + 1), folder i state x - ) |> snd - - let binarySearch (compare : 'T -> int) (array : 'T[]) = - let rec search (a : int) (b : int) = - if a <= b then - let i = (a + b) / 2 - let cmp = compare array.[i] - - if cmp = 0 then ValueSome i - elif cmp < 0 then search a (i - 1) - else search (i + 1) b - else - ValueNone - - search 0 (array.Length - 1) - - module Disposable = - - let empty = { new IDisposable with member x.Dispose() = () } - - let inline dispose v = (^a : (member Dispose : unit -> unit) v) - - module Option = - - let inline defaultValue (fallback : 'a) (option : Option<'a>) = - match option with - | Some value -> value - | None -> fallback - - type Async with - static member AwaitTask(t : System.Threading.Tasks.Task) = - t |> Async.AwaitIAsyncResult |> Async.Ignore - - type nativeptr<'T when 'T : unmanaged> with - member ptr.Value - with inline get () = NativePtr.read ptr - and inline set (value : 'T) = NativePtr.write ptr value - - member ptr.Item - with inline get (index : int) = NativePtr.get ptr index - and inline set (index : int) (value : 'T) = NativePtr.set ptr index value - - module NativePtr = - open System.Runtime.InteropServices - open System.IO - - [] - let inline zero<'a when 'a : unmanaged> : nativeptr<'a> = NativePtr.ofNativeInt 0n - - let inline cast (ptr : nativeptr<'a>) : nativeptr<'b> = - ptr |> NativePtr.toNativeInt |> NativePtr.ofNativeInt - - let inline step (count : int) (ptr : nativeptr<'a>) = - NativePtr.add ptr count - - let inline toArray (cnt : int) (ptr : nativeptr<'a>) = - Array.init cnt (NativePtr.get ptr) - - let inline toSeq (cnt : int) (ptr : nativeptr<'a>) = - Seq.init cnt (NativePtr.get ptr) - - let inline toList (cnt : int) (ptr : nativeptr<'a>) = - List.init cnt (NativePtr.get ptr) - - let inline isNull (ptr : nativeptr<'a>) = - ptr |> NativePtr.toNativeInt = 0n - - let alloc<'a when 'a: unmanaged> (size : int) = - size * sizeof<'a> |> Marshal.AllocHGlobal |> NativePtr.ofNativeInt<'a> - - let free (ptr : nativeptr<'a>) = - ptr |> NativePtr.toNativeInt |> Marshal.FreeHGlobal - - let inline stackUse (elements : seq<'a>) = - let arr = elements |> Seq.toArray - let ptr = NativePtr.stackalloc arr.Length - for i in 0..arr.Length-1 do - NativePtr.set ptr i arr.[i] - ptr - - let toStream (count : int) (ptr : nativeptr<'a>) : Stream = - let l = int64 <| sizeof<'a> * count - new System.IO.UnmanagedMemoryStream(cast ptr, l,l, FileAccess.ReadWrite) :> _ - - module Operators = - - let ( &+ ) (ptr : nativeptr<'a>) (count : int) = - ptr |> step count - - let ( &- ) (ptr : nativeptr<'a>) (count : int) = - ptr |> step (-count) - - let ( !* ) (ptr : nativeptr<'a>) = - NativePtr.read ptr - - type Buffer with - - static member inline MemoryCopy(source : nativeint, destination : nativeint, destinationSizeInBytes : uint64, sourceBytesToCopy : uint64) = - Buffer.MemoryCopy(source.ToPointer(), destination.ToPointer(), destinationSizeInBytes, sourceBytesToCopy) - - static member inline MemoryCopy(source : nativeint, destination : nativeint, destinationSizeInBytes : int64, sourceBytesToCopy : int64) = - Buffer.MemoryCopy(source.ToPointer(), destination.ToPointer(), destinationSizeInBytes, sourceBytesToCopy) - - (* Error datastructure *) - type Error<'a> = Success of 'a - | Error of string - - (* Either left or right *) - type Either<'a,'b> = Left of 'a - | Right of 'b - - - let toFunc (f : 'a -> 'b) : Func<'a, 'b> = - Func<'a, 'b>(f) - - let fromFunc (f : Func<'a, 'b>) : 'a -> 'b = - fun x -> f.Invoke(x) - - let dowhile (f : unit -> bool) = - while f() do () - - //VERY IMPORTANT CODE - let uncurry (f : 'a -> 'b -> 'c) = fun (a,b) -> f a b - let curry (f : 'a * 'b -> 'c) = fun a b -> f (a,b) - - let schönfinkel = curry - let deschönfinkel = uncurry - - let frege = curry - let unfrege = uncurry - - let ಠ_ಠ str = failwith str - - let inline private refequals<'a when 'a : not struct> (a : 'a) (b : 'a) = Object.ReferenceEquals(a,b) - - let inline (==) (a : 'a) (b : 'a) = refequals a b - let inline (!=) (a : 'a) (b : 'a) = refequals a b |> not - - - let inline flip f a b = f b a - - let inline constF v = fun _ -> v - -[] -module CSharpInterop = - - open System.Runtime.CompilerServices - - type public FSharpFuncUtil = - - [] - static member ToFSharpFunc<'a,'b> (func:System.Converter<'a,'b>) = fun x -> func.Invoke(x) - - [] - static member ToFSharpFunc<'a,'b> (func:System.Func<'a,'b>) = fun x -> func.Invoke(x) - - [] - static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) = fun x y -> func.Invoke(x,y) - - [] - static member ToFSharpFunc<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = fun x y z -> func.Invoke(x,y,z) - - static member Create<'a,'b> (func:System.Func<'a,'b>) = FSharpFuncUtil.ToFSharpFunc func - - static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) = FSharpFuncUtil.ToFSharpFunc func - - static member Create<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = FSharpFuncUtil.ToFSharpFunc func - -[] -module CSharpCollectionExtensions = - - open System.Runtime.CompilerServices - open System.Collections.Generic - open System.Runtime.InteropServices - - type public DictionaryExtensions = - - [] - static member TryRemove(x : Dictionary<'a,'b>, k,[] r: byref<'b>) = - match x.TryGetValue k with - | (true,v) -> r <- v; true - | _ -> false - - [] - static member GetOrAdd(x : Dictionary<'a,'b>, k : 'a, creator : 'a -> 'b) = - match x.TryGetValue k with - | (true,v) -> v - | _ -> - let v = creator k - x.Add(k,v) |> ignore - v - -[] -module Threading = - open System.Threading - - /// Please note that Aardvark.Base.FSharp's MVar implementation is different from Haskell's MVar introduced in - /// "Concurrent Haskell" by Simon Peyton Jones, Andrew Gordon and Sigbjorn Finne. - /// see also: http://hackage.haskell.org/package/base-4.11.1.0/docs/Control-Concurrent-MVar.html - /// In our 'wrong' implementation put does not block but overrides the old value. - /// We use it typically for synchronized sampling use cases. - type MVar<'a>() = - let l = obj() - - let mutable hasValue = false - let mutable content = Unchecked.defaultof<'a> - - member x.Put v = - lock l (fun () -> - content <- v - if not hasValue then - hasValue <- true - Monitor.PulseAll l - ) - - member x.Take () = - lock l (fun () -> - while not hasValue do - Monitor.Wait l |> ignore - let v = content - content <- Unchecked.defaultof<_> - hasValue <- false - v - ) - - [] - member x.TakeAsync () = - async { - let! ct = Async.CancellationToken - do! Async.SwitchToThreadPool() - return x.Take() - } - - - let startThread (f : unit -> unit) = - let t = new Thread(ThreadStart f) - t.IsBackground <- true - t.Start() - t - - - [] - module MVar = - let empty () = MVar<'a>() - let create a = - let v = empty() - v.Put a - v - let put (m : MVar<'a>) v = m.Put v - let take (m : MVar<'a>) = m.Take() - [] - let takeAsync (m : MVar<'a>) = m.TakeAsync () - - type Interlocked with - static member Change(location : byref<'a>, f : 'a -> 'a) = - let mutable initial = location - let mutable computed = f initial - - while Interlocked.CompareExchange(&location, computed, initial) != initial do - initial <- location - computed <- f initial - - computed - - static member Change(location : byref<'a>, f : 'a -> 'a * 'b) = - let mutable initial = location - let (n,r) = f initial - let mutable computed = n - let mutable result = r - - while Interlocked.CompareExchange(&location, computed, initial) != initial do - initial <- location - let (n,r) = f initial - computed <- n - result <- r - - result - - - static member Change(location : byref, f : int -> int) = - let mutable initial = location - let mutable computed = f initial - - while Interlocked.CompareExchange(&location, computed, initial) <> initial do - initial <- location - computed <- f initial - - computed - - static member Change(location : byref, f : int -> int * 'b) = - let mutable initial = location - let (n,r) = f initial - let mutable computed = n - let mutable result = r - - while Interlocked.CompareExchange(&location, computed, initial) <> initial do - initial <- location - let (n,r) = f initial - computed <- n - result <- r - - result - - static member Change(location : byref, f : int64 -> int64) = - let mutable initial = location - let mutable computed = f initial - - while Interlocked.CompareExchange(&location, computed, initial) <> initial do - initial <- location - computed <- f initial - - computed - - static member Change(location : byref, f : int64 -> int64 * 'b) = - let mutable initial = location - let (n,r) = f initial - let mutable computed = n - let mutable result = r - - while Interlocked.CompareExchange(&location, computed, initial) <> initial do - initial <- location - let (n,r) = f initial - computed <- n - result <- r - - result - -module GenericValues = - open System.Reflection - - let inline zero< ^a when ^a : (static member (-) : ^a -> ^a -> ^a)> : ^a = - let t = typeof< ^a> - let pi = t.GetProperty("Zero", BindingFlags.Static ||| BindingFlags.Public) - let fi = t.GetField("Zero", BindingFlags.Static ||| BindingFlags.Public) - - if not (isNull pi) then pi.GetValue(null) |> unbox - elif not (isNull fi) then fi.GetValue(null) |> unbox - else Unchecked.defaultof< ^a> - -module Caching = - - let memoTable = System.Collections.Concurrent.ConcurrentDictionary() - let cacheFunction (f : 'a -> 'b) (a : 'a) : 'b = - memoTable.GetOrAdd((a,f) :> obj, fun (o:obj) -> f a :> obj) |> unbox<'b> - -[] -module NiceUtilities = - - module LookupTable = - open System.Collections.Generic - - let lookupTable (l : list<'a * 'b>) = - let d = Dictionary() - for (k,v) in l do - - match d.TryGetValue k with - | (true, vo) -> failwithf "duplicated lookup-entry: %A (%A vs %A)" k vo v - | _ -> () - - d.[k] <- v - - fun (key : 'a) -> - match d.TryGetValue key with - | (true, v) -> v - | _ -> failwithf "unsupported %A: %A" typeof<'a> key - - - let lookupTable' (l : list<'a * 'b>) = - let d = Dictionary() - for (k,v) in l do - match d.TryGetValue k with - | (true, vo) -> failwithf "duplicated lookup-entry: %A (%A vs %A)" k vo v - | _ -> () - - d.[k] <- v - - fun (key : 'a) -> - match d.TryGetValue key with - | (true, v) -> Some v - | _ -> None - - -[] -module IO = - open System.IO - - let alterFileName str f = Path.Combine (Path.GetDirectoryName str, f (Path.GetFileName str)) - - let createFileStream path = - if File.Exists path - then File.Delete path - new FileStream(path, FileMode.CreateNew) - - -[] -module Path = - open System.IO - - let combine (paths : seq) = Path.Combine(paths |> Seq.toArray) - - let andPath first second = combine [| first; second |] - -[] -module File = - open System.IO - - /// - /// Creates the parent directory of the given file path, if it does not exist. - /// - /// The path of the file, whose parent directory is to be created. - let createParentDirectory (path : string) = - let info = FileInfo(path) - if not info.Directory.Exists then - info.Directory.Create() - - /// - /// Creates a new file, writes the specified string array to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - let writeAllLines (path : string) (lines : string[]) = - File.WriteAllLines(path, lines) - - /// - /// Creates a new file, writes the specified string array to the file, and then closes the file. - /// If the parent directory does not exist, it is created first. - /// - /// The file to write to. - /// The lines to write to the file. - let writeAllLinesSafe (path : string) (lines : string[]) = - createParentDirectory path - File.WriteAllLines(path, lines) - - /// - /// Creates a new file, writes the specified string to the file, and then closes the file. - /// - /// The file to write to. - /// The string to write to the file. - let writeAllText (path : string) (text : string) = - File.WriteAllText(path, text) - - /// - /// Creates a new file, writes the specified string to the file, and then closes the file. - /// If the parent directory does not exist, it is created first. - /// - /// The file to write to. - /// The string to write to the file. - let writeAllTextSafe (path : string) (text : string) = - createParentDirectory path - File.WriteAllText(path, text) - - /// - /// Creates a new file, writes the specified byte array to the file, and then closes the file. - /// - /// The file to write to. - /// The bytes to write to the file. - let writeAllBytes (path : string) (bytes : uint8[]) = - File.WriteAllBytes(path, bytes) - - /// - /// Creates a new file, writes the specified byte array to the file, and then closes the file. - /// If the parent directory does not exist, it is created first. - /// - /// The file to write to. - /// The bytes to write to the file. - let writeAllBytesSafe (path : string) (bytes : uint8[]) = - createParentDirectory path - File.WriteAllBytes(path, bytes) - - /// - /// Opens a text file, reads all lines of the file into a string array, and then closes the file. - /// - /// The file to open for reading. - /// A string array containing all lines of the file. - let readAllLines (path : string) = - File.ReadAllLines path - - /// - /// Opens a text file, reads all the text in the file into a string, and then closes the file. - /// - /// The file to open for reading. - /// A string containing all the text in the file. - let readAllText (path : string) = - File.ReadAllText path - - /// - /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. - /// - /// The file to open for reading. - /// A byte array containing the contents of the file. - let readAllBytes (path : string) = - File.ReadAllBytes path - -[] -module NativeUtilities = - open System.Runtime.InteropServices - open Microsoft.FSharp.NativeInterop - - let private os = System.Environment.OSVersion - let private notimp() = raise <| NotImplementedException() - - - /// - /// MSVCRT wraps memory-functions provided by msvcrt.dll on windows systems. - /// - module internal MSVCRT = - open System - open System.Runtime.InteropServices - - [] - extern nativeint private memcpy_internal(nativeint dest, nativeint src, UIntPtr size); - - [] - extern int private memcmp_internal(nativeint ptr1, nativeint ptr2, UIntPtr size); - - [] - extern nativeint private memset_internal(nativeint ptr, int value, UIntPtr size); - - [] - extern nativeint private memmove_internal(nativeint dest, nativeint src, UIntPtr size); - - - let memcpy(target : nativeint, source : nativeint, size : unativeint) = - memcpy_internal(target, source, size) |> ignore - - let memcmp(ptr1 : nativeint, ptr2 : nativeint, size : unativeint) = - memcmp_internal(ptr1, ptr2, size) - - let memset(ptr : nativeint, value : int, size : unativeint) = - memset_internal(ptr, value, size) |> ignore - - let memmove(target : nativeint, source : nativeint, size : unativeint) = - memmove_internal(target, source, size) |> ignore - - /// - /// LibC wraps memory-functions provided by libc on linux systems. - /// - module internal LibC = - open System - open System.Runtime.InteropServices - - - [] - extern nativeint private memcpy_internal(nativeint dest, nativeint src, UIntPtr size); - - [] - extern int private memcmp_internal(nativeint ptr1, nativeint ptr2, UIntPtr size); - - [] - extern nativeint private memset_internal(nativeint ptr, int value, UIntPtr size); - - [] - extern nativeint private memmove_internal(nativeint dest, nativeint src, UIntPtr size); - - [] - extern int private uname_intern(nativeint buf); - - - let mutable osname = null - let uname() = - if isNull osname then - let ptr : nativeptr = NativePtr.stackalloc 8192 - if uname_intern(NativePtr.toNativeInt ptr) = 0 then - osname <- Marshal.PtrToStringAnsi(NativePtr.toNativeInt ptr) - else - failwith "could not get os-name" - osname - - - - let memcpy(target : nativeint, source : nativeint, size : unativeint) = - memcpy_internal(target, source, size) |> ignore - - let memcmp(ptr1 : nativeint, ptr2 : nativeint, size : unativeint) = - memcmp_internal(ptr1, ptr2, size) - - let memset(ptr : nativeint, value : int, size : unativeint) = - memset_internal(ptr, value, size) |> ignore - - let memmove(target : nativeint, source : nativeint, size : unativeint) = - memmove_internal(target, source, size) |> ignore - - [] - module PlatformStuff = - - - let (|Windows|Linux|Mac|) (p : System.OperatingSystem) = - match p.Platform with - | System.PlatformID.Unix -> - if LibC.uname() = "Darwin" then Mac - else Linux - | System.PlatformID.MacOSX -> Mac - | _ -> Windows - - [] - module NativeInt = - let memcpy (src : nativeint) (dst : nativeint) (size : int) = - match os with - | Windows -> MSVCRT.memcpy(dst, src, unativeint size) - | _ -> LibC.memcpy(dst, src, unativeint size) - - let memmove (src : nativeint) (dst : nativeint) (size : int) = - match os with - | Windows -> MSVCRT.memmove(dst, src, unativeint size) - | _ -> LibC.memmove(dst, src, unativeint size) - - let memset (dst : nativeint) (value : int) (size : int) = - match os with - | Windows -> MSVCRT.memset(dst, value, unativeint size) - | _ -> LibC.memset(dst, value, unativeint size) - - let memcmp (src : nativeint) (dst : nativeint) (size : int) = - match os with - | Windows -> MSVCRT.memcmp(dst, src, unativeint size) - | _ -> LibC.memcmp(dst, src, unativeint size) - - let inline read<'a when 'a : unmanaged> (ptr : nativeint) = - NativePtr.read (NativePtr.ofNativeInt<'a> ptr) - - let inline write<'a when 'a : unmanaged> (ptr : nativeint) (value : 'a) = - NativePtr.write (NativePtr.ofNativeInt<'a> ptr) value - - let inline get<'a when 'a : unmanaged> (ptr : nativeint) (index : int) = - NativePtr.get (NativePtr.ofNativeInt<'a> ptr) index - - let inline set<'a when 'a : unmanaged> (ptr : nativeint) (index : int) (value : 'a)= - NativePtr.set (NativePtr.ofNativeInt<'a> ptr) index value - - type Marshal with - static member Copy(source : nativeint, destination : nativeint, length : unativeint) = - match os with - | Windows -> MSVCRT.memcpy(destination, source, length) - | _ -> LibC.memcpy(destination, source, length) - - static member Move(source : nativeint, destination : nativeint, length : unativeint) = - match os with - | Windows -> MSVCRT.memmove(destination, source, length) - | _ -> LibC.memmove(destination, source, length) - - static member Set(memory : nativeint, value : int, length : unativeint) = - match os with - | Windows -> MSVCRT.memset(memory, value, length) - | _ -> LibC.memset(memory, value, length) - - static member Compare(source : nativeint, destination : nativeint, length : unativeint) = - match os with - | Windows -> MSVCRT.memcmp(destination, source, length) - | _ -> LibC.memcmp(destination, source, length) - - - - static member Copy(source : nativeint, destination : nativeint, length : int) = - Marshal.Copy(source, destination, unativeint length) - - static member Move(source : nativeint, destination : nativeint, length : int) = - Marshal.Move(source, destination, unativeint length) - - static member Set(memory : nativeint, value : int, length : int) = - Marshal.Set(memory, value, unativeint length) - - static member Compare(source : nativeint, destination : nativeint, length : int) = - Marshal.Compare(source, destination, unativeint length) - - - - - static member inline Copy(source : nativeint, destination : nativeint, length : 'a) = - Marshal.Copy(source, destination, unativeint length) - - static member inline Move(source : nativeint, destination : nativeint, length : 'a) = - Marshal.Move(source, destination, unativeint length) - - static member inline Set(memory : nativeint, value : int, length : 'a) = - Marshal.Set(memory, value, unativeint length) - - static member inline Compare(source : nativeint, destination : nativeint, length : 'a) = - Marshal.Compare(source, destination, unativeint length) - - - - - - let pinned (a : obj) f = - let gc = GCHandle.Alloc(a, GCHandleType.Pinned) - try - f ( gc.AddrOfPinnedObject() ) - finally - gc.Free() - - -type float16 = Aardvark.Base.Half \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Reflection/ReflectionExtensions.fs b/src/Aardvark.Base.FSharp/Reflection/ReflectionExtensions.fs index 10267f5a4..aa94982bd 100644 --- a/src/Aardvark.Base.FSharp/Reflection/ReflectionExtensions.fs +++ b/src/Aardvark.Base.FSharp/Reflection/ReflectionExtensions.fs @@ -336,5 +336,153 @@ type MethodInfoGenericExtensions private() = match MethodResolver.tryMakeApplicable args ret this with | Some m -> m | None -> null - + +[] +module ReflectionHelpers = + open Microsoft.FSharp.Reflection + + let private lockObj = obj() + + let private prettyNames = + Dict.ofList [ + typeof, "sbyte" + typeof, "byte" + typeof, "int16" + typeof, "uint16" + typeof, "int" + typeof, "uint32" + typeof, "int64" + typeof, "uint64" + typeof, "nativeint" + typeof, "unativeint" + + typeof, "char" + typeof, "string" + + + typeof, "float32" + typeof, "float" + typeof, "decimal" + + typeof, "obj" + typeof, "unit" + typeof, "void" + + ] + + let private genericPrettyNames = + Dict.ofList [ + typedefof>, "list" + typedefof>, "Option" + typedefof>, "Set" + typedefof>, "Map" + typedefof>, "seq" + + ] + + let private idRx = System.Text.RegularExpressions.Regex @"[a-zA-Z_][a-zA-Z_0-9]*" + + let rec private getPrettyNameInternal (t : Type) = + let res = + match prettyNames.TryGetValue t with + | (true, n) -> n + | _ -> + if t.IsArray then + t.GetElementType() |> getPrettyNameInternal |> sprintf "%s[]" + + elif FSharpType.IsTuple t then + FSharpType.GetTupleElements t |> Seq.map getPrettyNameInternal |> String.concat " * " + + elif FSharpType.IsFunction t then + let (arg, res) = FSharpType.GetFunctionElements t + + sprintf "%s -> %s" (getPrettyNameInternal arg) (getPrettyNameInternal res) + + elif typeof.IsAssignableFrom t then + let s = Aardvark.Base.Peano.getSize t + sprintf "N%d" s + + elif t.IsGenericType then + let args = t.GetGenericArguments() |> Seq.map getPrettyNameInternal |> String.concat ", " + let bt = t.GetGenericTypeDefinition() + match genericPrettyNames.TryGetValue bt with + | (true, gen) -> + sprintf "%s<%s>" gen args + | _ -> + let gen = idRx.Match bt.Name + sprintf "%s<%s>" gen.Value args + + + else + t.Name + + prettyNames.[t] <- res + res + + [] + let getPrettyName(t : Type) = + lock lockObj (fun () -> + getPrettyNameInternal t + ) + + type Type with + member x.PrettyName = + lock lockObj (fun () -> + getPrettyNameInternal x + ) + + +/// +/// Defines a number of active patterns for matching expressions. Includes some +/// functionality missing in F#. +/// +[] +module ReflectionPatterns = + open Microsoft.FSharp.Quotations + open QuotationReflectionHelpers + + let private typePrefixPattern = System.Text.RegularExpressions.Regex @"^.*\.(?.*)$" + let (|Method|_|) (mi : MethodInfo) = + let args = mi.GetParameters() |> Seq.map(fun p -> p.ParameterType) + let parameters = if mi.IsStatic then + args + else + seq { yield mi.DeclaringType; yield! args } + + let m = typePrefixPattern.Match mi.Name + let name = + if m.Success then m.Groups.["methodName"].Value + else mi.Name + + Method (name, parameters |> Seq.toList) |> Some + + let private compareMethods (template : MethodInfo) (m : MethodInfo) = + if template.IsGenericMethod && m.IsGenericMethod then + if template.GetGenericMethodDefinition() = m.GetGenericMethodDefinition() then + let targs = template.GetGenericArguments() |> Array.toList + let margs = m.GetGenericArguments() |> Array.toList + + let zip = List.zip targs margs + + let args = zip |> List.filter(fun (l,r) -> l.IsGenericParameter) |> List.map (fun (_,a) -> a) + + Some args + else + None + elif template = m then + Some [] + else + None + + let (|MethodQuote|_|) (e : Expr) (mi : MethodInfo) = + let m = tryGetMethodInfo e + match m with + | Some m -> match compareMethods m mi with + | Some a -> MethodQuote(a) |> Some + | None -> None + | _ -> None + + + let (|Create|_|) (c : ConstructorInfo) = + Create(c.DeclaringType, c.GetParameters() |> Seq.toList) |> Some \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Utilities/IO.fs b/src/Aardvark.Base.FSharp/Utilities/IO.fs new file mode 100644 index 000000000..0dd855eff --- /dev/null +++ b/src/Aardvark.Base.FSharp/Utilities/IO.fs @@ -0,0 +1,109 @@ +namespace Aardvark.Base + +open System.IO + +[] +module IO = + let alterFileName str f = Path.Combine (Path.GetDirectoryName str, f (Path.GetFileName str)) + + let createFileStream path = + if File.Exists path + then File.Delete path + new FileStream(path, FileMode.CreateNew) + + +[] +module Path = + let combine (paths : seq) = Path.Combine(paths |> Seq.toArray) + + let andPath first second = combine [| first; second |] + +[] +module File = + + /// + /// Creates the parent directory of the given file path, if it does not exist. + /// + /// The path of the file, whose parent directory is to be created. + let createParentDirectory (path : string) = + let info = FileInfo(path) + if not info.Directory.Exists then + info.Directory.Create() + + /// + /// Creates a new file, writes the specified string array to the file, and then closes the file. + /// + /// The file to write to. + /// The lines to write to the file. + let writeAllLines (path : string) (lines : string[]) = + File.WriteAllLines(path, lines) + + /// + /// Creates a new file, writes the specified string array to the file, and then closes the file. + /// If the parent directory does not exist, it is created first. + /// + /// The file to write to. + /// The lines to write to the file. + let writeAllLinesSafe (path : string) (lines : string[]) = + createParentDirectory path + File.WriteAllLines(path, lines) + + /// + /// Creates a new file, writes the specified string to the file, and then closes the file. + /// + /// The file to write to. + /// The string to write to the file. + let writeAllText (path : string) (text : string) = + File.WriteAllText(path, text) + + /// + /// Creates a new file, writes the specified string to the file, and then closes the file. + /// If the parent directory does not exist, it is created first. + /// + /// The file to write to. + /// The string to write to the file. + let writeAllTextSafe (path : string) (text : string) = + createParentDirectory path + File.WriteAllText(path, text) + + /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// + /// The file to write to. + /// The bytes to write to the file. + let writeAllBytes (path : string) (bytes : uint8[]) = + File.WriteAllBytes(path, bytes) + + /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the parent directory does not exist, it is created first. + /// + /// The file to write to. + /// The bytes to write to the file. + let writeAllBytesSafe (path : string) (bytes : uint8[]) = + createParentDirectory path + File.WriteAllBytes(path, bytes) + + /// + /// Opens a text file, reads all lines of the file into a string array, and then closes the file. + /// + /// The file to open for reading. + /// A string array containing all lines of the file. + let readAllLines (path : string) = + File.ReadAllLines path + + /// + /// Opens a text file, reads all the text in the file into a string, and then closes the file. + /// + /// The file to open for reading. + /// A string containing all the text in the file. + let readAllText (path : string) = + File.ReadAllText path + + /// + /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. + /// + /// The file to open for reading. + /// A byte array containing the contents of the file. + let readAllBytes (path : string) = + File.ReadAllBytes path \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Interop/ArraySegment.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/ArraySegment.fs similarity index 65% rename from src/Aardvark.Base.FSharp/Interop/ArraySegment.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/ArraySegment.fs index 204a1d630..c982a1aa1 100644 --- a/src/Aardvark.Base.FSharp/Interop/ArraySegment.fs +++ b/src/Aardvark.Base.FSharp/Utilities/Interop/ArraySegment.fs @@ -12,6 +12,18 @@ module ArraySegment = member inline x.Item(index : int) = x.Array.[x.Offset + index] #endif + let inline tryHeadV (segment : ArraySegment<'T>) = + if segment.Count > 0 then ValueSome segment.[0] + else ValueNone + + let inline copyArray (segment : ArraySegment<'T>) = + let arr = Array.zeroCreate<'T> segment.Count + Array.Copy(segment.Array, segment.Offset, arr, 0, arr.Length) + arr + + let inline copy (segment : ArraySegment<'T1>) = + ArraySegment (copyArray segment) + let inline mapArray (mapping : 'T1 -> 'T2) (segment : ArraySegment<'T1>) = Array.init segment.Count (fun i -> mapping segment.[i]) @@ -29,6 +41,18 @@ module ArraySegment = state + let inline reduce (reduction : 'T -> 'T -> 'T) (segment : ArraySegment<'T>) = + if segment.Count = 0 then + invalidArg "segment" "Cannot reduce an empty ArraySegment." + else + let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (reduction) + let mutable res = segment.[0] + + for i = 1 to segment.Count - 1 do + res <- f.Invoke(res, segment.[i]) + + res + /// Forms a slice of the specified length out of the array segment starting at the specified index. let inline slice (start : int) (count : int) (segment : ArraySegment<'T>) = #if NETSTANDARD2_1_OR_GREATER diff --git a/src/Aardvark.Base.FSharp/Interop/CSharpList.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/CSharpList.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/CSharpList.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/CSharpList.fs diff --git a/src/Aardvark.Base.FSharp/Interop/Dictionary.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/Dictionary.fs similarity index 61% rename from src/Aardvark.Base.FSharp/Interop/Dictionary.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/Dictionary.fs index 4f12f2561..ebee3d53f 100644 --- a/src/Aardvark.Base.FSharp/Interop/Dictionary.fs +++ b/src/Aardvark.Base.FSharp/Utilities/Interop/Dictionary.fs @@ -5,11 +5,11 @@ module Dictionary = open System.Collections.Generic let empty<'k, 'v when 'k : equality> = Dictionary<'k, 'v>() - let emptyNoEquality<'k,'v> = + let emptyNoEquality<'k,'v> = Dictionary<'k,'v>( - { new IEqualityComparer<'k> with + { new IEqualityComparer<'k> with member x.Equals(a,b) = Unchecked.equals a b - member x.GetHashCode(t) = Unchecked.hash t + member x.GetHashCode(t) = Unchecked.hash t }) let inline add (key : 'k) (value : 'v) (d : Dictionary<'k, 'v>) = @@ -24,15 +24,14 @@ module Dictionary = let inline clear (d : Dictionary<'k, 'v>) = d.Clear() - let inline map (f : 'k -> 'a -> 'b) (d : Dictionary<'k, 'a>) = - let result = Dictionary() + let result = Dictionary(capacity = d.Count) for (KeyValue(k,v)) in d do result.[k] <- f k v result let inline mapKeys (f : 'k -> 'a -> 'b) (d : Dictionary<'k, 'a>) = - let result = Dictionary() + let result = Dictionary(capacity = d.Count) for (KeyValue(k,v)) in d do result.[f k v] <- v result @@ -49,8 +48,13 @@ module Dictionary = let inline tryFind (key : 'k) (d : Dictionary<'k, 'v>) = match d.TryGetValue key with - | (true, v) -> Some v - | _ -> None + | (true, v) -> Some v + | _ -> None + + let inline tryFindV (key : 'k) (d : Dictionary<'k, 'v>) = + let mutable value = Unchecked.defaultof<_> + if d.TryGetValue(key, &value) then ValueSome value + else ValueNone let inline ofSeq (elements : seq<'k * 'v>) = let result = Dictionary() @@ -58,11 +62,29 @@ module Dictionary = result.[k] <- v result + let inline ofSeqV (elements : seq) = + let result = Dictionary() + for (k,v) in elements do + result.[k] <- v + result + let inline ofList (elements : list<'k * 'v>) = ofSeq elements + let inline ofListV (elements : list) = + ofSeqV elements + let inline ofArray (elements : ('k * 'v)[]) = - ofSeq elements + let result = Dictionary(capacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result + + let inline ofArrayV (elements : (struct('k * 'v))[]) = + let result = Dictionary(capacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result let inline ofMap (elements : Map<'k, 'v>) = elements |> Map.toSeq |> ofSeq @@ -70,12 +92,21 @@ module Dictionary = let inline toSeq (d : Dictionary<'k, 'v>) = d |> Seq.map (fun (KeyValue(k,v)) -> k,v) + let inline toSeqV (d : Dictionary<'k, 'v>) = + d |> Seq.map (fun (KeyValue(k,v)) -> struct(k,v)) + let inline toList (d : Dictionary<'k, 'v>) = d |> toSeq |> Seq.toList + let inline toListV (d : Dictionary<'k, 'v>) = + d |> toSeqV |> Seq.toList + let inline toArray (d : Dictionary<'k, 'v>) = d |> toSeq |> Seq.toArray + let inline toArrayV (d : Dictionary<'k, 'v>) = + d |> toSeqV |> Seq.toArray + let inline toMap (d : Dictionary<'k, 'v>) = d |> toSeq |> Map.ofSeq @@ -120,13 +151,13 @@ module Dict = #endif let inline map (f : 'k -> 'a -> 'b) (d : Dict<'k, 'a>) = - let result = Dict() + let result = Dict(initialCapacity = d.Count) for (KeyValue(k,v)) in d do result.[k] <- f k v result let inline mapKeys (f : 'k -> 'a -> 'b) (d : Dict<'k, 'a>) = - let result = Dict() + let result = Dict(initialCapacity = d.Count) for (KeyValue(k,v)) in d do result.[f k v] <- v result @@ -170,10 +201,16 @@ module Dict = ofSeqV elements let inline ofArray (elements : ('k * 'v)[]) = - ofSeq elements + let result = Dict(initialCapacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result let inline ofArrayV (elements : struct('k * 'v)[]) = - ofSeqV elements + let result = Dict(initialCapacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result let inline ofMap (elements : Map<'k, 'v>) = elements |> Map.toSeq |> ofSeq @@ -216,15 +253,14 @@ module SymDict = let inline clear (d : SymbolDict<'v>) = d.Clear() - let inline map (f : Symbol -> 'a -> 'b) (d : SymbolDict<'a>) = - let result = SymbolDict() + let result = SymbolDict(initialCapacity = d.Count) for (KeyValue(k,v)) in d do result.[k] <- f k v result let inline mapKeys (f : Symbol -> 'a -> Symbol) (d : SymbolDict<'a>) = - let result = SymbolDict() + let result = SymbolDict(initialCapacity = d.Count) for (KeyValue(k,v)) in d do result.[f k v] <- v result @@ -241,20 +277,43 @@ module SymDict = let inline tryFind (key : Symbol) (d : SymbolDict<'v>) = match d.TryGetValue key with - | (true, v) -> Some v - | _ -> None + | (true, v) -> Some v + | _ -> None + + let inline tryFindV (key : Symbol) (d : SymbolDict<'v>) = + let mutable value = Unchecked.defaultof<_> + if d.TryGetValue(key, &value) then ValueSome value + else ValueNone let inline ofSeq (elements : seq) = let result = SymbolDict() for (k,v) in elements do - result.Add(k,v) + result.[k] <- v + result + + let inline ofSeqV (elements : seq) = + let result = SymbolDict() + for (k,v) in elements do + result.[k] <- v result let inline ofList (elements : list) = ofSeq elements + let inline ofListV (elements : list) = + ofSeqV elements + let inline ofArray (elements : (Symbol * 'v)[]) = - ofSeq elements + let result = SymbolDict(initialCapacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result + + let inline ofArrayV (elements : (struct(Symbol * 'v))[]) = + let result = SymbolDict(initialCapacity = elements.Length) + for (k,v) in elements do + result.[k] <- v + result let inline ofMap (elements : Map) = elements |> Map.toSeq |> ofSeq @@ -262,11 +321,59 @@ module SymDict = let inline toSeq (d : SymbolDict<'v>) = d |> Seq.map (fun (KeyValue(k,v)) -> k,v) + let inline toSeqV (d : SymbolDict<'v>) = + d |> Seq.map (fun (KeyValue(k,v)) -> struct (k,v)) + let inline toList (d : SymbolDict<'v>) = d |> toSeq |> Seq.toList + let inline toListV (d : SymbolDict<'v>) = + d |> toSeqV |> Seq.toList + let inline toArray (d : SymbolDict<'v>) = - d |> toSeq |> Seq.toArray + d |> toSeq |> Seq.toArray + + let inline toArrayV (d : SymbolDict<'v>) = + d |> toSeqV |> Seq.toArray let inline toMap (elements : SymbolDict<'v>) = elements |> toSeq |> Map.ofSeq + + +[] +module CSharpCollectionExtensions = + open System + open System.Runtime.CompilerServices + open System.Collections.Generic + open System.Runtime.InteropServices + + type Dictionary<'Key, 'Value> with + member inline x.TryFind(key : 'Key) : 'Value option = Dictionary.tryFind key x + member inline x.TryFindV(key : 'Key) : 'Value voption = Dictionary.tryFindV key x + + type Dict<'Key, 'Value> with + member inline x.TryFind(key : 'Key) : 'Value option = Dict.tryFind key x + member inline x.TryFindV(key : 'Key) : 'Value voption = Dict.tryFindV key x + + type SymbolDict<'Value> with + member inline x.TryFind(key : Symbol) : 'Value option = SymDict.tryFind key x + member inline x.TryFindV(key : Symbol) : 'Value voption = SymDict.tryFindV key x + + type public DictionaryExtensions = + + [] + [] + static member TryRemove(x : Dictionary<'a,'b>, k,[] r: byref<'b>) = + match x.TryGetValue k with + | (true,v) -> r <- v; true + | _ -> false + + [] + [] + static member GetOrAdd(x : Dictionary<'a,'b>, k : 'a, creator : 'a -> 'b) = + match x.TryGetValue k with + | (true,v) -> v + | _ -> + let v = creator k + x.Add(k,v) |> ignore + v \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Utilities/Interop/FSLibExtensions.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/FSLibExtensions.fs new file mode 100644 index 000000000..0086c027c --- /dev/null +++ b/src/Aardvark.Base.FSharp/Utilities/Interop/FSLibExtensions.fs @@ -0,0 +1,410 @@ +namespace Aardvark.Base + +#nowarn "9" + +open System +open FSharp.NativeInterop + +[] +module Prelude = + + let inc (a:byref) = a <- a + 1 + let dec (a:byref) = a <- a - 1 + + let inline isNull (a : 'a) = + match a with + | null -> true + | _ -> false + + module Map = + let union (l : Map<'k, 'v>) (r : Map<'k, 'v>) = + let mutable result = l + for KeyValue(k,v) in r do + result <- Map.add k v result + result + + let unionMany (input : seq>) = + (Map.empty, input) ||> Seq.fold union + + module Seq = + let iter' (f : 'a -> 'b) (s : seq<'a>) = + for i in s do + f i |> ignore + + let repeat (n : int) (f : unit -> unit) = + for i in 1..n do + f() + + let repeat' (n : int) (f : unit -> 'a) = + for i in 1..n do + f() |> ignore + + let partition (f : 'a -> bool) (xs : seq<'a>) = + let xs = xs |> Seq.map (fun a -> f a, a) |> Seq.cache + + (xs |> Seq.filter (fun (r,v) -> not r) |> Seq.map snd, xs |> Seq.filter (fun (r,v) -> r) |> Seq.map snd) + + let chooseOption (f : 'a -> Option<'b>) (xs : seq>) : seq> = + seq { + for x in xs do + match x with + | None -> () + | Some x -> yield f x + } + + let forany (f : 'a -> bool) (s : seq<'a>) = + let e = s.GetEnumerator() + let mutable r = false + while not r && e.MoveNext() do + if f e.Current then + r <- true + e.Dispose() + r + + let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (source : 'T seq) = + ((0, state), source) ||> Seq.fold (fun (i, state) x -> + (i + 1), folder i state x + ) |> snd + + let inline tryPickV chooser (source : seq<'T>) = + use e = source.GetEnumerator() + let mutable res = ValueNone + + while (ValueOption.isNone res && e.MoveNext()) do + res <- chooser e.Current + + res + + open System.Collections + open System.Collections.Generic + + let atMost (n : int) (s : seq<'a>) : seq<'a> = + let newEnumerator() = + let input = s.GetEnumerator() + let remaining = ref n + { new IEnumerator<'a> with + member x.MoveNext() = + remaining := !remaining - 1 + !remaining >= 0 && input.MoveNext() + member x.Current : obj = input.Current :> obj + member x.Dispose() = input.Dispose() + member x.Reset() = input.Reset(); remaining := n + member x.Current : 'a = input.Current + } + + { new IEnumerable<'a> with + member x.GetEnumerator() : IEnumerator = newEnumerator() :> IEnumerator + member x.GetEnumerator() : IEnumerator<'a> = newEnumerator() + } + + + module List = + + //Experimental Results show that this implementation is faster than all other ones maintaining the list's order + let rec private partitionAcc (f : 'a -> bool) (source : list<'a>) (l : System.Collections.Generic.List<'a>) (r : System.Collections.Generic.List<'a>) = + match source with + | [] -> + let mutable ll = [] + let mutable rr = [] + + for i in 1..l.Count do + let i = l.Count - i + ll <- l.[i]::ll + + for i in 1..r.Count do + let i = r.Count - i + rr <- r.[i]::rr + + (ll,rr) + + | x::xs -> + if f x then + l.Add(x) + partitionAcc f xs l r + else + r.Add(x) + partitionAcc f xs l r + + let partition (f : 'a -> bool) (source : list<'a>) = + partitionAcc f source (System.Collections.Generic.List()) (System.Collections.Generic.List()) + + let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (list : 'T list) = + ((0, state), list) ||> List.fold (fun (i, state) x -> + (i + 1), folder i state x + ) |> snd + + module Array = + let rec private forany' (f : 'a -> bool) (index : int) (a : 'a[]) = + if index >= a.Length then false + else + if f a.[index] then true + else forany' f (index + 1) a + + let forany (f : 'a -> bool) (a : 'a[]) = + forany' f 0 a + + let foldi (folder : int -> 'State -> 'T -> 'State) (state : 'State) (array : 'T[]) = + ((0, state), array) ||> Array.fold (fun (i, state) x -> + (i + 1), folder i state x + ) |> snd + + let binarySearch (compare : 'T -> int) (array : 'T[]) = + let rec search (a : int) (b : int) = + if a <= b then + let i = (a + b) / 2 + let cmp = compare array.[i] + + if cmp = 0 then ValueSome i + elif cmp < 0 then search a (i - 1) + else search (i + 1) b + else + ValueNone + + search 0 (array.Length - 1) + + let inline tryPickV chooser (array : _[]) = + let rec loop i = + if i >= array.Length then + ValueNone + else + match chooser array.[i] with + | ValueNone -> loop (i + 1) + | res -> res + + loop 0 + + module Disposable = + + let empty = { new IDisposable with member x.Dispose() = () } + + let inline dispose v = (^a : (member Dispose : unit -> unit) v) + + module Option = + let inline defaultValue (fallback : 'a) (option : Option<'a>) = + match option with + | Some value -> value + | None -> fallback + + let inline toValueOption (value : 'T option) = + match value with Some x -> ValueSome x | _ -> ValueNone + + module ValueOption = + let inline toOption (value : 'T voption) = + match value with ValueSome x -> Some x | _ -> None + + [] + module ValueOptionOperators = + let inline fstv (struct (x, _)) = x + let inline sndv (struct (_, y)) = y + + type Async with + static member AwaitTask(t : System.Threading.Tasks.Task) = + t |> Async.AwaitIAsyncResult |> Async.Ignore + + type nativeptr<'T when 'T : unmanaged> with + member ptr.Value + with inline get () = NativePtr.read ptr + and inline set (value : 'T) = NativePtr.write ptr value + + member ptr.Item + with inline get (index : int) = NativePtr.get ptr index + and inline set (index : int) (value : 'T) = NativePtr.set ptr index value + + module NativePtr = + open System.Runtime.InteropServices + open System.IO + + [] + let inline zero<'a when 'a : unmanaged> : nativeptr<'a> = NativePtr.ofNativeInt 0n + + let inline cast (ptr : nativeptr<'a>) : nativeptr<'b> = + ptr |> NativePtr.toNativeInt |> NativePtr.ofNativeInt + + let inline step (count : int) (ptr : nativeptr<'a>) = + NativePtr.add ptr count + + let inline toArray (cnt : int) (ptr : nativeptr<'a>) = + Array.init cnt (NativePtr.get ptr) + + let inline toSeq (cnt : int) (ptr : nativeptr<'a>) = + Seq.init cnt (NativePtr.get ptr) + + let inline toList (cnt : int) (ptr : nativeptr<'a>) = + List.init cnt (NativePtr.get ptr) + + let inline isNull (ptr : nativeptr<'a>) = + ptr |> NativePtr.toNativeInt = 0n + + let alloc<'a when 'a: unmanaged> (size : int) = + size * sizeof<'a> |> Marshal.AllocHGlobal |> NativePtr.ofNativeInt<'a> + + let free (ptr : nativeptr<'a>) = + ptr |> NativePtr.toNativeInt |> Marshal.FreeHGlobal + + let inline stackUse (elements : seq<'a>) = + let arr = elements |> Seq.toArray + let ptr = NativePtr.stackalloc arr.Length + for i in 0..arr.Length-1 do + NativePtr.set ptr i arr.[i] + ptr + + let toStream (count : int) (ptr : nativeptr<'a>) : Stream = + let l = int64 <| sizeof<'a> * count + new System.IO.UnmanagedMemoryStream(cast ptr, l,l, FileAccess.ReadWrite) :> _ + + module Operators = + + let ( &+ ) (ptr : nativeptr<'a>) (count : int) = + ptr |> step count + + let ( &- ) (ptr : nativeptr<'a>) (count : int) = + ptr |> step (-count) + + let ( !* ) (ptr : nativeptr<'a>) = + NativePtr.read ptr + + type Buffer with + + static member inline MemoryCopy(source : nativeint, destination : nativeint, destinationSizeInBytes : uint64, sourceBytesToCopy : uint64) = + Buffer.MemoryCopy(source.ToPointer(), destination.ToPointer(), destinationSizeInBytes, sourceBytesToCopy) + + static member inline MemoryCopy(source : nativeint, destination : nativeint, destinationSizeInBytes : int64, sourceBytesToCopy : int64) = + Buffer.MemoryCopy(source.ToPointer(), destination.ToPointer(), destinationSizeInBytes, sourceBytesToCopy) + + (* Error datastructure *) + type Error<'a> = Success of 'a + | Error of string + + (* Either left or right *) + type Either<'a,'b> = Left of 'a + | Right of 'b + + + let toFunc (f : 'a -> 'b) : Func<'a, 'b> = + Func<'a, 'b>(f) + + let fromFunc (f : Func<'a, 'b>) : 'a -> 'b = + fun x -> f.Invoke(x) + + let dowhile (f : unit -> bool) = + while f() do () + + //VERY IMPORTANT CODE + let uncurry (f : 'a -> 'b -> 'c) = fun (a,b) -> f a b + let curry (f : 'a * 'b -> 'c) = fun a b -> f (a,b) + + let schönfinkel = curry + let deschönfinkel = uncurry + + let frege = curry + let unfrege = uncurry + + let ಠ_ಠ str = failwith str + + let inline private refequals<'a when 'a : not struct> (a : 'a) (b : 'a) = Object.ReferenceEquals(a,b) + + let inline (==) (a : 'a) (b : 'a) = refequals a b + let inline (!=) (a : 'a) (b : 'a) = refequals a b |> not + + + let inline flip f a b = f b a + + let inline constF v = fun _ -> v + +[] +module CSharpInterop = + + open System.Runtime.CompilerServices + + type public FSharpFuncUtil = + + [] + static member ToFSharpFunc<'a,'b> (func:System.Converter<'a,'b>) = fun x -> func.Invoke(x) + + [] + static member ToFSharpFunc<'a,'b> (func:System.Func<'a,'b>) = fun x -> func.Invoke(x) + + [] + static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) = fun x y -> func.Invoke(x,y) + + [] + static member ToFSharpFunc<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = fun x y z -> func.Invoke(x,y,z) + + static member Create<'a,'b> (func:System.Func<'a,'b>) = FSharpFuncUtil.ToFSharpFunc func + + static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) = FSharpFuncUtil.ToFSharpFunc func + + static member Create<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = FSharpFuncUtil.ToFSharpFunc func + +module GenericValues = + open System.Reflection + + let inline zero< ^a when ^a : (static member (-) : ^a -> ^a -> ^a)> : ^a = + let t = typeof< ^a> + let pi = t.GetProperty("Zero", BindingFlags.Static ||| BindingFlags.Public) + let fi = t.GetField("Zero", BindingFlags.Static ||| BindingFlags.Public) + + if not (isNull pi) then pi.GetValue(null) |> unbox + elif not (isNull fi) then fi.GetValue(null) |> unbox + else Unchecked.defaultof< ^a> + +module Caching = + + let memoTable = System.Collections.Concurrent.ConcurrentDictionary() + let cacheFunction (f : 'a -> 'b) (a : 'a) : 'b = + memoTable.GetOrAdd((a,f) :> obj, fun (o:obj) -> f a :> obj) |> unbox<'b> + +[] +module NiceUtilities = + + module LookupTable = + open System.Collections.Generic + + let lookupTable (l : list<'a * 'b>) = + let d = Dictionary() + for (k,v) in l do + + match d.TryGetValue k with + | (true, vo) -> failwithf "duplicated lookup-entry: %A (%A vs %A)" k vo v + | _ -> () + + d.[k] <- v + + fun (key : 'a) -> + match d.TryGetValue key with + | (true, v) -> v + | _ -> failwithf "unsupported %A: %A" typeof<'a> key + + + let lookupTable' (l : list<'a * 'b>) = + let d = Dictionary() + for (k,v) in l do + match d.TryGetValue k with + | (true, vo) -> failwithf "duplicated lookup-entry: %A (%A vs %A)" k vo v + | _ -> () + + d.[k] <- v + + fun (key : 'a) -> + match d.TryGetValue key with + | (true, v) -> Some v + | _ -> None + +module ConversionHelpers = + + [] + let lookupTableOption (l : list<'a * 'b>) : ('a -> 'b option) = + LookupTable.lookupTable' l + + [] + let lookupTable (l : list<'a * 'b>) : ('a -> 'b) = + LookupTable.lookupTable l + + let inline convertEnum< ^a, ^b when ^a : (static member op_Explicit : ^a -> int)> (fmt : ^a) : ^b = + let v = int fmt + if Enum.IsDefined(typeof< ^b >, v) then + unbox< ^b > v + else + failwithf "cannot convert %s %A to %s" typeof< ^a >.Name fmt typeof< ^b >.Name + +type float16 = Aardvark.Base.Half \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Interop/HashSet.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/HashSet.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/HashSet.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/HashSet.fs diff --git a/src/Aardvark.Base.FSharp/Interop/SortedSet.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/SortedSet.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/SortedSet.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/SortedSet.fs diff --git a/src/Aardvark.Base.FSharp/String.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/String.fs similarity index 79% rename from src/Aardvark.Base.FSharp/String.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/String.fs index 5f3d6aaf4..0ab294b14 100644 --- a/src/Aardvark.Base.FSharp/String.fs +++ b/src/Aardvark.Base.FSharp/Utilities/Interop/String.fs @@ -9,6 +9,22 @@ open System open System.Text.RegularExpressions open System.Runtime.CompilerServices +[] +[] +module Strings = + let partRx = Regex @"([A-Z][a-z0-9]*)[_]*" + + /// checks whether pattern is contained in str + let contains pattern (str : string) = str.Contains pattern + + let toLower (str : string) = str.ToLower() + let toUpper (str : string) = str.ToUpper() + + let inline split (sep : string) (str : string) = str.Split([| sep |], StringSplitOptions.None) + let inline startsWith (s : string) (str : string) = str.StartsWith s + let inline endsWith (s : string) (str : string) = str.EndsWith s + let inline trim (str : string) = str.Trim() + [] type StringExtensions private() = @@ -95,4 +111,8 @@ module ``String Extensions`` = /// Removes all leading and trailing white-space characters from the string. let inline trim (str : string) = - str.Trim() \ No newline at end of file + str.Trim() + + /// Replaces all occurrences of the pattern with the replacement string. + let inline replace (pattern : string) (replacement : string) (str : string) = + str.Replace(pattern, replacement) \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Interop/Symbol.fs b/src/Aardvark.Base.FSharp/Utilities/Interop/Symbol.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Interop/Symbol.fs rename to src/Aardvark.Base.FSharp/Utilities/Interop/Symbol.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/Lens.fs b/src/Aardvark.Base.FSharp/Utilities/Lens.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Prelude/Lens.fs rename to src/Aardvark.Base.FSharp/Utilities/Lens.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/Logging.fs b/src/Aardvark.Base.FSharp/Utilities/Logging.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Prelude/Logging.fs rename to src/Aardvark.Base.FSharp/Utilities/Logging.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/Measures.fs b/src/Aardvark.Base.FSharp/Utilities/Measures.fs similarity index 99% rename from src/Aardvark.Base.FSharp/Prelude/Measures.fs rename to src/Aardvark.Base.FSharp/Utilities/Measures.fs index bd3fe7c9b..279c524b7 100644 --- a/src/Aardvark.Base.FSharp/Prelude/Measures.fs +++ b/src/Aardvark.Base.FSharp/Utilities/Measures.fs @@ -135,8 +135,8 @@ type MicroTime = new (ts : TimeSpan) = { TotalNanoseconds = ts.Ticks * (1000000000L / TimeSpan.TicksPerSecond) } new (ns : float) = { TotalNanoseconds = - if isFinite ns then int64 ns - elif isPositiveInfinity ns then Int64.MaxValue + if Fun.IsFinite ns then int64 ns + elif Fun.IsPositiveInfinity ns then Int64.MaxValue else Int64.MinValue } diff --git a/src/Aardvark.Base.FSharp/Control.fs b/src/Aardvark.Base.FSharp/Utilities/Monads.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Control.fs rename to src/Aardvark.Base.FSharp/Utilities/Monads.fs diff --git a/src/Aardvark.Base.FSharp/Prelude/Monoid.fs b/src/Aardvark.Base.FSharp/Utilities/Monoid.fs similarity index 100% rename from src/Aardvark.Base.FSharp/Prelude/Monoid.fs rename to src/Aardvark.Base.FSharp/Utilities/Monoid.fs diff --git a/src/Aardvark.Base.FSharp/Utilities/Native.fs b/src/Aardvark.Base.FSharp/Utilities/Native.fs new file mode 100644 index 000000000..30a225e23 --- /dev/null +++ b/src/Aardvark.Base.FSharp/Utilities/Native.fs @@ -0,0 +1,209 @@ +namespace Aardvark.Base + +#nowarn "9" + +open System +open System.Runtime.InteropServices +open FSharp.NativeInterop + +[] +module NativeUtilities = + + let private os = System.Environment.OSVersion + + /// + /// MSVCRT wraps memory-functions provided by msvcrt.dll on windows systems. + /// + module internal MSVCRT = + + [] + extern nativeint private memcpy_internal(nativeint dest, nativeint src, UIntPtr size); + + [] + extern int private memcmp_internal(nativeint ptr1, nativeint ptr2, UIntPtr size); + + [] + extern nativeint private memset_internal(nativeint ptr, int value, UIntPtr size); + + [] + extern nativeint private memmove_internal(nativeint dest, nativeint src, UIntPtr size); + + let memcpy(target : nativeint, source : nativeint, size : unativeint) = + memcpy_internal(target, source, size) |> ignore + + let memcmp(ptr1 : nativeint, ptr2 : nativeint, size : unativeint) = + memcmp_internal(ptr1, ptr2, size) + + let memset(ptr : nativeint, value : int, size : unativeint) = + memset_internal(ptr, value, size) |> ignore + + let memmove(target : nativeint, source : nativeint, size : unativeint) = + memmove_internal(target, source, size) |> ignore + + /// + /// LibC wraps memory-functions provided by libc on linux systems. + /// + module internal LibC = + + [] + extern nativeint private memcpy_internal(nativeint dest, nativeint src, UIntPtr size); + + [] + extern int private memcmp_internal(nativeint ptr1, nativeint ptr2, UIntPtr size); + + [] + extern nativeint private memset_internal(nativeint ptr, int value, UIntPtr size); + + [] + extern nativeint private memmove_internal(nativeint dest, nativeint src, UIntPtr size); + + [] + extern int private uname_intern(nativeint buf); + + let mutable osname = null + let uname() = + if isNull osname then + let ptr : nativeptr = NativePtr.stackalloc 8192 + if uname_intern(NativePtr.toNativeInt ptr) = 0 then + osname <- Marshal.PtrToStringAnsi(NativePtr.toNativeInt ptr) + else + failwith "could not get os-name" + osname + + let memcpy(target : nativeint, source : nativeint, size : unativeint) = + memcpy_internal(target, source, size) |> ignore + + let memcmp(ptr1 : nativeint, ptr2 : nativeint, size : unativeint) = + memcmp_internal(ptr1, ptr2, size) + + let memset(ptr : nativeint, value : int, size : unativeint) = + memset_internal(ptr, value, size) |> ignore + + let memmove(target : nativeint, source : nativeint, size : unativeint) = + memmove_internal(target, source, size) |> ignore + + [] + module PlatformStuff = + + let (|Windows|Linux|Mac|) (p : System.OperatingSystem) = + match p.Platform with + | System.PlatformID.Unix -> + if LibC.uname() = "Darwin" then Mac + else Linux + | System.PlatformID.MacOSX -> Mac + | _ -> Windows + + [] + module NativeInt = + let memcpy (src : nativeint) (dst : nativeint) (size : int) = + match os with + | Windows -> MSVCRT.memcpy(dst, src, unativeint size) + | _ -> LibC.memcpy(dst, src, unativeint size) + + let memmove (src : nativeint) (dst : nativeint) (size : int) = + match os with + | Windows -> MSVCRT.memmove(dst, src, unativeint size) + | _ -> LibC.memmove(dst, src, unativeint size) + + let memset (dst : nativeint) (value : int) (size : int) = + match os with + | Windows -> MSVCRT.memset(dst, value, unativeint size) + | _ -> LibC.memset(dst, value, unativeint size) + + let memcmp (src : nativeint) (dst : nativeint) (size : int) = + match os with + | Windows -> MSVCRT.memcmp(dst, src, unativeint size) + | _ -> LibC.memcmp(dst, src, unativeint size) + + let inline read<'a when 'a : unmanaged> (ptr : nativeint) = + NativePtr.read (NativePtr.ofNativeInt<'a> ptr) + + let inline write<'a when 'a : unmanaged> (ptr : nativeint) (value : 'a) = + NativePtr.write (NativePtr.ofNativeInt<'a> ptr) value + + let inline get<'a when 'a : unmanaged> (ptr : nativeint) (index : int) = + NativePtr.get (NativePtr.ofNativeInt<'a> ptr) index + + let inline set<'a when 'a : unmanaged> (ptr : nativeint) (index : int) (value : 'a)= + NativePtr.set (NativePtr.ofNativeInt<'a> ptr) index value + + type Marshal with + static member Copy(source : nativeint, destination : nativeint, length : unativeint) = + match os with + | Windows -> MSVCRT.memcpy(destination, source, length) + | _ -> LibC.memcpy(destination, source, length) + + static member Move(source : nativeint, destination : nativeint, length : unativeint) = + match os with + | Windows -> MSVCRT.memmove(destination, source, length) + | _ -> LibC.memmove(destination, source, length) + + static member Set(memory : nativeint, value : int, length : unativeint) = + match os with + | Windows -> MSVCRT.memset(memory, value, length) + | _ -> LibC.memset(memory, value, length) + + static member Compare(source : nativeint, destination : nativeint, length : unativeint) = + match os with + | Windows -> MSVCRT.memcmp(destination, source, length) + | _ -> LibC.memcmp(destination, source, length) + + + static member Copy(source : nativeint, destination : nativeint, length : int) = + Marshal.Copy(source, destination, unativeint length) + + static member Move(source : nativeint, destination : nativeint, length : int) = + Marshal.Move(source, destination, unativeint length) + + static member Set(memory : nativeint, value : int, length : int) = + Marshal.Set(memory, value, unativeint length) + + static member Compare(source : nativeint, destination : nativeint, length : int) = + Marshal.Compare(source, destination, unativeint length) + + + static member inline Copy(source : nativeint, destination : nativeint, length : 'a) = + Marshal.Copy(source, destination, unativeint length) + + static member inline Move(source : nativeint, destination : nativeint, length : 'a) = + Marshal.Move(source, destination, unativeint length) + + static member inline Set(memory : nativeint, value : int, length : 'a) = + Marshal.Set(memory, value, unativeint length) + + static member inline Compare(source : nativeint, destination : nativeint, length : 'a) = + Marshal.Compare(source, destination, unativeint length) + + + let pinned (a : obj) f = + let gc = GCHandle.Alloc(a, GCHandleType.Pinned) + try + f ( gc.AddrOfPinnedObject() ) + finally + gc.Free() + +[] +module MarshalDelegateExtensions = + open System.Collections.Concurrent + + let private pinnedDelegates = ConcurrentDictionary() + type PinnedDelegate internal(d : Delegate, ptr : nativeint) = + member x.Pointer = ptr + member x.Dispose() = pinnedDelegates.TryRemove d |> ignore + + interface IDisposable with + member x.Dispose() = x.Dispose() + + type Marshal with + static member PinDelegate(d : Delegate) = + let ptr = pinnedDelegates.GetOrAdd(d, fun _ -> Marshal.GetFunctionPointerForDelegate d) + new PinnedDelegate(d, ptr) + + static member PinFunction(f : 'a -> 'b) = + Marshal.PinDelegate(Func<'a, 'b>(f)) + + static member PinFunction(f : 'a -> 'b -> 'c) = + Marshal.PinDelegate(Func<'a, 'b, 'c>(f)) + + static member PinFunction(f : 'a -> 'b -> 'c -> 'd) = + Marshal.PinDelegate(Func<'a, 'b, 'c, 'd>(f)) \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/AdaptivePicklers.fs b/src/Aardvark.Base.FSharp/Utilities/Pickler/AdaptivePicklers.fs similarity index 100% rename from src/Aardvark.Base.FSharp/AdaptivePicklers.fs rename to src/Aardvark.Base.FSharp/Utilities/Pickler/AdaptivePicklers.fs diff --git a/src/Aardvark.Base.FSharp/FsPicklerExtensions.fs b/src/Aardvark.Base.FSharp/Utilities/Pickler/FsPicklerExtensions.fs similarity index 100% rename from src/Aardvark.Base.FSharp/FsPicklerExtensions.fs rename to src/Aardvark.Base.FSharp/Utilities/Pickler/FsPicklerExtensions.fs diff --git a/src/Aardvark.Base.FSharp/Utilities/Threading.fs b/src/Aardvark.Base.FSharp/Utilities/Threading.fs new file mode 100644 index 000000000..7a5ebf6ad --- /dev/null +++ b/src/Aardvark.Base.FSharp/Utilities/Threading.fs @@ -0,0 +1,140 @@ +namespace Aardvark.Base + +#nowarn "44" + +open System + +[] +module Threading = + open System.Threading + + /// Please note that Aardvark.Base.FSharp's MVar implementation is different from Haskell's MVar introduced in + /// "Concurrent Haskell" by Simon Peyton Jones, Andrew Gordon and Sigbjorn Finne. + /// see also: http://hackage.haskell.org/package/base-4.11.1.0/docs/Control-Concurrent-MVar.html + /// In our 'wrong' implementation put does not block but overrides the old value. + /// We use it typically for synchronized sampling use cases. + type MVar<'a>() = + let l = obj() + + let mutable hasValue = false + let mutable content = Unchecked.defaultof<'a> + + member x.Put v = + lock l (fun () -> + content <- v + if not hasValue then + hasValue <- true + Monitor.PulseAll l + ) + + member x.Take () = + lock l (fun () -> + while not hasValue do + Monitor.Wait l |> ignore + let v = content + content <- Unchecked.defaultof<_> + hasValue <- false + v + ) + + [] + member x.TakeAsync () = + async { + let! ct = Async.CancellationToken + do! Async.SwitchToThreadPool() + return x.Take() + } + + + let startThread (f : unit -> unit) = + let t = new Thread(ThreadStart f) + t.IsBackground <- true + t.Start() + t + + + [] + module MVar = + let empty () = MVar<'a>() + let create a = + let v = empty() + v.Put a + v + let put (m : MVar<'a>) v = m.Put v + let take (m : MVar<'a>) = m.Take() + [] + let takeAsync (m : MVar<'a>) = m.TakeAsync () + + type Interlocked with + static member Change(location : byref<'a>, f : 'a -> 'a) = + let mutable initial = location + let mutable computed = f initial + + while Interlocked.CompareExchange(&location, computed, initial) != initial do + initial <- location + computed <- f initial + + computed + + static member Change(location : byref<'a>, f : 'a -> 'a * 'b) = + let mutable initial = location + let (n,r) = f initial + let mutable computed = n + let mutable result = r + + while Interlocked.CompareExchange(&location, computed, initial) != initial do + initial <- location + let (n,r) = f initial + computed <- n + result <- r + + result + + + static member Change(location : byref, f : int -> int) = + let mutable initial = location + let mutable computed = f initial + + while Interlocked.CompareExchange(&location, computed, initial) <> initial do + initial <- location + computed <- f initial + + computed + + static member Change(location : byref, f : int -> int * 'b) = + let mutable initial = location + let (n,r) = f initial + let mutable computed = n + let mutable result = r + + while Interlocked.CompareExchange(&location, computed, initial) <> initial do + initial <- location + let (n,r) = f initial + computed <- n + result <- r + + result + + static member Change(location : byref, f : int64 -> int64) = + let mutable initial = location + let mutable computed = f initial + + while Interlocked.CompareExchange(&location, computed, initial) <> initial do + initial <- location + computed <- f initial + + computed + + static member Change(location : byref, f : int64 -> int64 * 'b) = + let mutable initial = location + let (n,r) = f initial + let mutable computed = n + let mutable result = r + + while Interlocked.CompareExchange(&location, computed, initial) <> initial do + initial <- location + let (n,r) = f initial + computed <- n + result <- r + + result \ No newline at end of file diff --git a/src/Aardvark.Base.FSharp/Runtime.fs b/src/Aardvark.Base.FSharp/Utilities/Weak.fs similarity index 59% rename from src/Aardvark.Base.FSharp/Runtime.fs rename to src/Aardvark.Base.FSharp/Utilities/Weak.fs index 12388cb5d..7703c660f 100644 --- a/src/Aardvark.Base.FSharp/Runtime.fs +++ b/src/Aardvark.Base.FSharp/Utilities/Weak.fs @@ -280,213 +280,4 @@ module Weak = false else List.fold2 (fun b l r -> b && (l.Equals(r))) true m_elements other.Target - | _ -> false - -[] -module ReflectionHelpers = - open Microsoft.FSharp.Reflection - - let private lockObj = obj() - - let private prettyNames = - Dict.ofList [ - typeof, "sbyte" - typeof, "byte" - typeof, "int16" - typeof, "uint16" - typeof, "int" - typeof, "uint32" - typeof, "int64" - typeof, "uint64" - typeof, "nativeint" - typeof, "unativeint" - - typeof, "char" - typeof, "string" - - - typeof, "float32" - typeof, "float" - typeof, "decimal" - - typeof, "obj" - typeof, "unit" - typeof, "void" - - ] - - let private genericPrettyNames = - Dict.ofList [ - typedefof>, "list" - typedefof>, "Option" - typedefof>, "Set" - typedefof>, "Map" - typedefof>, "seq" - - ] - - let private idRx = System.Text.RegularExpressions.Regex @"[a-zA-Z_][a-zA-Z_0-9]*" - - let rec private getPrettyNameInternal (t : Type) = - let res = - match prettyNames.TryGetValue t with - | (true, n) -> n - | _ -> - if t.IsArray then - t.GetElementType() |> getPrettyNameInternal |> sprintf "%s[]" - - elif FSharpType.IsTuple t then - FSharpType.GetTupleElements t |> Seq.map getPrettyNameInternal |> String.concat " * " - - elif FSharpType.IsFunction t then - let (arg, res) = FSharpType.GetFunctionElements t - - sprintf "%s -> %s" (getPrettyNameInternal arg) (getPrettyNameInternal res) - - elif typeof.IsAssignableFrom t then - let s = Aardvark.Base.Peano.getSize t - sprintf "N%d" s - - elif t.IsGenericType then - let args = t.GetGenericArguments() |> Seq.map getPrettyNameInternal |> String.concat ", " - let bt = t.GetGenericTypeDefinition() - match genericPrettyNames.TryGetValue bt with - | (true, gen) -> - sprintf "%s<%s>" gen args - | _ -> - let gen = idRx.Match bt.Name - sprintf "%s<%s>" gen.Value args - - - else - t.Name - - prettyNames.[t] <- res - res - - [] - let getPrettyName(t : Type) = - lock lockObj (fun () -> - getPrettyNameInternal t - ) - - type Type with - member x.PrettyName = - lock lockObj (fun () -> - getPrettyNameInternal x - ) - -/// -/// Defines a number of active patterns for matching expressions. Includes some -/// functionality missing in F#. -/// -[] -module ReflectionPatterns = - open System.Reflection - open Microsoft.FSharp.Quotations - open QuotationReflectionHelpers - - let private typePrefixPattern = System.Text.RegularExpressions.Regex @"^.*\.(?.*)$" - let (|Method|_|) (mi : MethodInfo) = - let args = mi.GetParameters() |> Seq.map(fun p -> p.ParameterType) - let parameters = if mi.IsStatic then - args - else - seq { yield mi.DeclaringType; yield! args } - - let m = typePrefixPattern.Match mi.Name - let name = - if m.Success then m.Groups.["methodName"].Value - else mi.Name - - Method (name, parameters |> Seq.toList) |> Some - - let private compareMethods (template : MethodInfo) (m : MethodInfo) = - if template.IsGenericMethod && m.IsGenericMethod then - if template.GetGenericMethodDefinition() = m.GetGenericMethodDefinition() then - let targs = template.GetGenericArguments() |> Array.toList - let margs = m.GetGenericArguments() |> Array.toList - - let zip = List.zip targs margs - - let args = zip |> List.filter(fun (l,r) -> l.IsGenericParameter) |> List.map (fun (_,a) -> a) - - Some args - else - None - elif template = m then - Some [] - else - None - - let (|MethodQuote|_|) (e : Expr) (mi : MethodInfo) = - let m = tryGetMethodInfo e - match m with - | Some m -> match compareMethods m mi with - | Some a -> MethodQuote(a) |> Some - | None -> None - | _ -> None - - - let (|Create|_|) (c : ConstructorInfo) = - Create(c.DeclaringType, c.GetParameters() |> Seq.toList) |> Some - -[] -module MarshalDelegateExtensions = - open System.Runtime.InteropServices - open System.Collections.Concurrent - - let private pinnedDelegates = ConcurrentDictionary() - type PinnedDelegate internal(d : Delegate, ptr : nativeint) = - member x.Pointer = ptr - member x.Dispose() = pinnedDelegates.TryRemove d |> ignore - - interface IDisposable with - member x.Dispose() = x.Dispose() - - type Marshal with - static member PinDelegate(d : Delegate) = - let ptr = pinnedDelegates.GetOrAdd(d, fun _ -> Marshal.GetFunctionPointerForDelegate d) - new PinnedDelegate(d, ptr) - - static member PinFunction(f : 'a -> 'b) = - Marshal.PinDelegate(Func<'a, 'b>(f)) - - static member PinFunction(f : 'a -> 'b -> 'c) = - Marshal.PinDelegate(Func<'a, 'b, 'c>(f)) - - static member PinFunction(f : 'a -> 'b -> 'c -> 'd) = - Marshal.PinDelegate(Func<'a, 'b, 'c, 'd>(f)) - - -module ConversionHelpers = - open System.Collections.Generic - - let lookupTableOption (l : list<'a * 'b>) = - let d = Dictionary() - for (k,v) in l do - - match d.TryGetValue k with - | (true, vo) -> failwithf "duplicated lookup-entry: %A (%A vs %A)" k vo v - | _ -> () - - d.[k] <- v - - fun (key : 'a) -> - match d.TryGetValue key with - | (true, v) -> Some v - | _ -> None - - let lookupTable (l : list<'a * 'b>) = - let tryLookup = lookupTableOption l - fun (key : 'a) -> - match tryLookup key with - | Some v -> v - | _ -> failwithf "unsupported %A: %A" typeof<'a> key - - let inline convertEnum< ^a, ^b when ^a : (static member op_Explicit : ^a -> int)> (fmt : ^a) : ^b = - let v = int fmt - if Enum.IsDefined(typeof< ^b >, v) then - unbox< ^b > v - else - failwithf "cannot convert %s %A to %s" typeof< ^a >.Name fmt typeof< ^b >.Name \ No newline at end of file + | _ -> false \ No newline at end of file diff --git a/src/Aardvark.Base.IO/Aardvark.Base.IO.csproj b/src/Aardvark.Base.IO/Aardvark.Base.IO.csproj index 6f540866c..a77a9a50a 100644 --- a/src/Aardvark.Base.IO/Aardvark.Base.IO.csproj +++ b/src/Aardvark.Base.IO/Aardvark.Base.IO.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net6.0;netstandard2.0 true 612;1591 true diff --git a/src/Aardvark.Base.IO/StreamCodeReader.cs b/src/Aardvark.Base.IO/StreamCodeReader.cs index adc11982f..f65ecf412 100644 --- a/src/Aardvark.Base.IO/StreamCodeReader.cs +++ b/src/Aardvark.Base.IO/StreamCodeReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -9,8 +10,6 @@ namespace Aardvark.Base.Coder { public partial class StreamCodeReader : BinaryReader { - private const int c_bufferSize = 262144; - // private byte[] m_buffer = new byte[c_bufferSize]; private byte[] m_guidBuffer = new byte[16]; // own buffer since creator requires 16-byte length #region Constructors @@ -100,6 +99,7 @@ public long ReadArray(byte[] array, long index, long count) return (long)Read(array, (int)index, (int)count); } +#if !NET6_0_OR_GREATER [StructLayout(LayoutKind.Explicit)] struct ByteArrayUnion { @@ -118,14 +118,34 @@ private static IntPtr GetTypeIdUncached() where T : struct gcHandle.Free(); return typeId; } +#endif public long ReadArray(T[] array, long index, long count) where T : struct { if (count < 1) return 0; + +#if NET6_0_OR_GREATER + var span = MemoryMarshal.AsBytes(array.AsSpan((int)index, (int)count)); + + var sizeOfT = span.Length / array.Length; + var bytesToRead = span.Length; + var offset = 0; + + do + { + int finished = base.Read(span); + if (finished == 0) break; + offset += finished; bytesToRead -= finished; + span = span.Slice(offset, bytesToRead); + } + while (bytesToRead > 0); + + return offset / sizeOfT; +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; @@ -152,24 +172,42 @@ public long ReadArray(T[] array, long index, long count) } return ((long)(offset / sizeOfT) - index); } +#endif } public long ReadArray(T[,] array, long count) where T : struct { if (count < 1) return 0; + +#if NET6_0_OR_GREATER + var sizeOfT = Unsafe.SizeOf(); + var span = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetArrayDataReference(array), (int)count * sizeOfT); + + var bytesToRead = span.Length; + var offset = 0; + + do + { + int finished = base.Read(span); + if (finished == 0) break; + offset += finished; bytesToRead -= finished; + span = span.Slice(offset, bytesToRead); + } + while (bytesToRead > 0); + + return offset / sizeOfT; +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; var bytesToRead = (int)(sizeOfT * count); - #if __MonoCS__ - var skip = 0; - #else + var skip = 2 * 2 * sizeof(int); - #endif + IntPtr byteLen = (IntPtr)(array.Length * sizeOfT + skip); var offset = skip; @@ -192,24 +230,41 @@ public long ReadArray(T[,] array, long count) } return (long)((offset - skip) / sizeOfT); } +#endif } public long ReadArray(T[, ,] array, long count) where T : struct { if (count < 1) return 0; +#if NET6_0_OR_GREATER + var sizeOfT = Unsafe.SizeOf(); + var span = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetArrayDataReference(array), (int)count * sizeOfT); + + var bytesToRead = span.Length; + var offset = 0; + + do + { + int finished = base.Read(span); + if (finished == 0) break; + offset += finished; bytesToRead -= finished; + span = span.Slice(offset, bytesToRead); + } + while (bytesToRead > 0); + + return offset / sizeOfT; +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; var bytesToRead = (int)(sizeOfT * count); - #if __MonoCS__ - var skip = 0; - #else + var skip = 3 * 2 * sizeof(int); - #endif + IntPtr byteLen = (IntPtr)(array.Length * sizeOfT + skip); var offset = skip; @@ -232,6 +287,7 @@ public long ReadArray(T[, ,] array, long count) } return (long)((offset - skip) / sizeOfT); } +#endif } public int ReadList(List buffer, int index, int count) @@ -244,14 +300,13 @@ public int ReadList(List buffer, int index, int count) return (int)ReadArray(arrayValue, (long)index, (long)count); } - #endregion +#endregion #region Close public override void Close() { base.Close(); - // m_buffer = null; m_guidBuffer = null; } diff --git a/src/Aardvark.Base.IO/StreamCodeWriter.cs b/src/Aardvark.Base.IO/StreamCodeWriter.cs index 67ce5795d..0d06ff346 100644 --- a/src/Aardvark.Base.IO/StreamCodeWriter.cs +++ b/src/Aardvark.Base.IO/StreamCodeWriter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -9,8 +10,10 @@ namespace Aardvark.Base.Coder { public partial class StreamCodeWriter : BinaryWriter { +#if !NET6_0_OR_GREATER private const int c_bufferSize = 262144; private byte[] m_buffer = new byte[c_bufferSize]; +#endif #region Constructors @@ -105,6 +108,7 @@ public void Write(CameraIntrinsics c) #region Write Arrays and Lists +#if !NET6_0_OR_GREATER [StructLayout(LayoutKind.Explicit)] struct ByteArrayUnion { @@ -113,6 +117,7 @@ struct ByteArrayUnion [FieldOffset(0)] public Array structs; } +#endif public void WriteArray(byte[] array, long index, long count) { @@ -123,9 +128,15 @@ public void WriteArray(T[] array, long index, long count) where T : struct { if (count < 1) return; + +#if NET6_0_OR_GREATER + var arrSpan = array.AsSpan((int)index, (int)count); + var byteSpan = MemoryMarshal.AsBytes(arrSpan); + base.Write(byteSpan); +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; @@ -152,26 +163,28 @@ public void WriteArray(T[] array, long index, long count) while (count > 0); } } +#endif } public void WriteArray(T[,] array, long count) where T : struct { if (count < 1) return; +#if NET6_0_OR_GREATER + var sizeOfT = Unsafe.SizeOf(); + var byteSpan = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetArrayDataReference(array), (int)count * sizeOfT); + base.Write(byteSpan); +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; fixed (byte* pBytes = hack.bytes) { - #if __MonoCS__ - long offset = 0; - #else long offset = 2 * 2 * sizeof(int); - #endif int itemsPerBlock = c_bufferSize / sizeOfT; do @@ -191,27 +204,29 @@ public void WriteArray(T[,] array, long count) while (count > 0); } } +#endif } public void WriteArray(T[, ,] array, long count) where T : struct { if (count < 1) return; +#if NET6_0_OR_GREATER + var sizeOfT = Unsafe.SizeOf(); + var byteSpan = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetArrayDataReference(array), (int)count * sizeOfT); + base.Write(byteSpan); +#else unsafe { - var sizeOfT = Marshal.SizeOf(typeof(T)); + var sizeOfT = Unsafe.SizeOf(); var hack = new ByteArrayUnion(); hack.structs = array; fixed (byte* pBytes = hack.bytes) { - #if __MonoCS__ - long offset = 0; - #else long offset = 3 * 2 * sizeof(int); - #endif - + int itemsPerBlock = c_bufferSize / sizeOfT; do { @@ -230,6 +245,7 @@ public void WriteArray(T[, ,] array, long count) while (count > 0); } } +#endif } public void WriteList(List buffer, int index, int count) @@ -240,16 +256,18 @@ public void WriteList(List buffer, int index, int count) WriteArray(arrayValue, (long)index, (long)count); } - #endregion +#endregion #region Close public override void Close() { base.Close(); +#if! NET6_0_OR_GREATER m_buffer = null; +#endif } - #endregion +#endregion } } diff --git a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs index 513491f33..4f96066ed 100644 --- a/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs +++ b/src/Aardvark.Base.Tensors.CSharp/PixImage/PixImage.cs @@ -205,6 +205,9 @@ public PixImageInfo GetInfoFromStream(Stream stream) #region PgmPixLoader + /// + /// Loader for saving PGM images. Reading is not supported. + /// private class PgmPixLoader : IPixLoader { public string Name { get; } @@ -214,18 +217,14 @@ public PgmPixLoader() Name = "Aardvark PGM"; } - public PixImage LoadFromFile(string filename) - => throw new ImageLoadException($"{Name} loader does not support loading from files."); + public PixImage LoadFromFile(string filename) => null; - public PixImage LoadFromStream(Stream stream) - => throw new ImageLoadException($"{Name} loader does not support loading from streams."); + public PixImage LoadFromStream(Stream stream) => null; public void SaveToFile(string filename, PixImage image, PixSaveParams saveParams) { - using (var stream = File.OpenWrite(filename)) - { - SaveToStream(stream, image, saveParams); - } + using var stream = File.OpenWrite(filename); + SaveToStream(stream, image, saveParams); } public void SaveToStream(Stream stream, PixImage image, PixSaveParams saveParams) @@ -245,11 +244,9 @@ public void SaveToStream(Stream stream, PixImage image, PixSaveParams saveParams stream.Write(img.Volume.Data, 0, img.Volume.Data.Length); } - public PixImageInfo GetInfoFromFile(string filename) - => throw new ImageLoadException($"{Name} loader does not support retrieving info from files."); + public PixImageInfo GetInfoFromFile(string filename) => null; - public PixImageInfo GetInfoFromStream(Stream stream) - => throw new ImageLoadException($"{Name} loader does not support retrieving info from streams."); + public PixImageInfo GetInfoFromStream(Stream stream) => null; } #endregion @@ -275,7 +272,7 @@ public static void SetLoader(IPixLoader loader, int priority) /// /// Adds a PixImage loader. /// Assigns a priority that is greater than the highest priority among existing loaders, resulting in a LIFO order. - /// If the loader already exists, the priority is not modified. + /// If the loader already exists, the priority is modified. /// /// The loader to add. public static void AddLoader(IPixLoader loader) diff --git a/src/Aardvark.Base/Extensions/DictionaryExtensions.cs b/src/Aardvark.Base/Extensions/DictionaryExtensions.cs index c53cce0d1..7e4aff4e5 100644 --- a/src/Aardvark.Base/Extensions/DictionaryExtensions.cs +++ b/src/Aardvark.Base/Extensions/DictionaryExtensions.cs @@ -116,18 +116,23 @@ public static TV Pop(this IDictionary self, TK key) throw new KeyNotFoundException(); } + /// + /// Removes the value with the given key from the dictionary and passes it as out argument. + /// In case the value is not found default(T) will be returned. + /// + /// true if the value was removed from the dictionary, false otherwise. + public static bool TryPop(this IDictionary self, TK key, out TV value) + => self.TryGetValue(key, out value) && self.Remove(key); + /// /// Removes the value with the given key from the dictionary and passes it as return argument. - /// In case the value is not found default(T) will be return. + /// In case the value is not found default(T) will be returned. /// /// The value removed from the dictionary or default(T) public static TV TryPop(this IDictionary self, TK key) - { - if (self.TryGetValue(key, out TV value)) - self.Remove(key); - return value; - } - + => self.TryPop(key, out TV value) ? value : default; + + public static T1v[] CopyToArray(this IDictionary self, Func, T1v> fun) { var r = new T1v[self.Count]; diff --git a/src/Aardvark.Base/Math/Colors/Color.cs b/src/Aardvark.Base/Math/Colors/Color.cs index de86b1bf8..3b31efd1e 100644 --- a/src/Aardvark.Base/Math/Colors/Color.cs +++ b/src/Aardvark.Base/Math/Colors/Color.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Text.RegularExpressions; namespace Aardvark.Base { @@ -1900,6 +1902,97 @@ public static C4f Average(this IEnumerable items) } #endregion + + #region Hex Parsing + + // See: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color + private static readonly Regex regexHexSgl = new Regex("^(?:#|0x)?(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])(?[0-9a-fA-F])?$", RegexOptions.Compiled); + private static readonly Regex regexHexDbl = new Regex("^(?:#|0x)?(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})(?[0-9a-fA-F]{2})?$", RegexOptions.Compiled); + + private static bool TryParseHex(Regex regex, string input, out C4b result) + { + var m = regex.Match(input); + if (m.Success) + { + string[] values = new string[4]; + values[0] = m.Groups["R"].Value; + values[1] = m.Groups["G"].Value; + values[2] = m.Groups["B"].Value; + values[3] = m.Groups["A"].Success ? m.Groups["A"].Value : "FF"; + + result = + new C4b(values.Map((x) => { + var value = x.Length == 1 ? new string(x[0], 2) : x; + return byte.Parse(value, NumberStyles.AllowHexSpecifier); + })); + + return true; + } + + result = C4b.Zero; + return false; + } + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseHex(Text input, out C4b result) + { + var str = input.WhiteSpaceTrimmed.ToString(); + return TryParseHex(regexHexDbl, str, out result) || TryParseHex(regexHexSgl, str, out result); + } + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseHex(string input, out C4b result) + => TryParseHex(new Text(input), out result); + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid hexadecimal color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b ParseHex(Text input) + => TryParseHex(input, out C4b result) ? result : throw new FormatException($"{input} is not a valid hexadecimal color."); + + /// + /// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional. + /// + /// + /// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// The color string may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid hexadecimal color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b ParseHex(string input) + => ParseHex(new Text(input)); + + #endregion } #endregion diff --git a/src/Aardvark.Base/Math/Colors/Color_auto.cs b/src/Aardvark.Base/Math/Colors/Color_auto.cs index f84f4fd84..1617c3059 100644 --- a/src/Aardvark.Base/Math/Colors/Color_auto.cs +++ b/src/Aardvark.Base/Math/Colors/Color_auto.cs @@ -1594,30 +1594,101 @@ public static C3b DivideByInt(C3b c, int x) #region Parsing - public static C3b Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3b.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3b result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3b(); + return true; + } + else + { + bool success = true; + byte[] values = new byte[4] { 255, 255, 255, 255 }; + + byte parse(Text t) + { + if (!byte.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out byte value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3b(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3b result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3b Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3b( - byte.Parse(x[0], CultureInfo.InvariantCulture), - byte.Parse(x[1], CultureInfo.InvariantCulture), - byte.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3b.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3b.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3b Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3b.Setter); - } + => TryParse(t, out C3b result) ? result : throw new FormatException($"{t} is not a valid C3b color."); #endregion @@ -1724,6 +1795,17 @@ public static bool IsTiny(this C3b c, byte epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3b c) + => $"{c.R:X2}{c.G:X2}{c.B:X2}"; + + #endregion + #region Comparisons /// @@ -3786,30 +3868,101 @@ public static C3us DivideByInt(C3us c, int x) #region Parsing - public static C3us Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3us.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3us result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3us(); + return true; + } + else + { + bool success = true; + ushort[] values = new ushort[4] { 65535, 65535, 65535, 65535 }; + + ushort parse(Text t) + { + if (!ushort.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out ushort value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3us(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3us.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3us result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3us Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3us( - ushort.Parse(x[0], CultureInfo.InvariantCulture), - ushort.Parse(x[1], CultureInfo.InvariantCulture), - ushort.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3us.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3us.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3us Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3us.Setter); - } + => TryParse(t, out C3us result) ? result : throw new FormatException($"{t} is not a valid C3us color."); #endregion @@ -3916,6 +4069,17 @@ public static bool IsTiny(this C3us c, ushort epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3us c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -5901,30 +6065,101 @@ public static C3ui DivideByInt(C3ui c, int x) #region Parsing - public static C3ui Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3ui.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3ui result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3ui(); + return true; + } + else + { + bool success = true; + uint[] values = new uint[4] { UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue }; + + uint parse(Text t) + { + if (!uint.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out uint value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3ui(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3ui.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3ui result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3ui Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3ui( - uint.Parse(x[0], CultureInfo.InvariantCulture), - uint.Parse(x[1], CultureInfo.InvariantCulture), - uint.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3ui.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3ui.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3ui Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3ui.Setter); - } + => TryParse(t, out C3ui result) ? result : throw new FormatException($"{t} is not a valid C3ui color."); #endregion @@ -6031,6 +6266,17 @@ public static bool IsTiny(this C3ui c, uint epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3ui c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -7891,30 +8137,101 @@ public static C3f DivideByInt(C3f c, int x) #region Parsing - public static C3f Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3f.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3f result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3f(); + return true; + } + else + { + bool success = true; + float[] values = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f }; + + float parse(Text t) + { + if (!float.TryParse(t.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3f(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3f.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3f result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3f Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3f( - float.Parse(x[0], CultureInfo.InvariantCulture), - float.Parse(x[1], CultureInfo.InvariantCulture), - float.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3f.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3f.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3f Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3f.Setter); - } + => TryParse(t, out C3f result) ? result : throw new FormatException($"{t} is not a valid C3f color."); #endregion @@ -8064,6 +8381,17 @@ public static bool IsFinite(C3f c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3f c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -9918,30 +10246,101 @@ public static C3d DivideByInt(C3d c, int x) #region Parsing - public static C3d Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3d.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C3d result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC3d(); + return true; + } + else + { + bool success = true; + double[] values = new double[4] { 1.0, 1.0, 1.0, 1.0 }; + + double parse(Text t) + { + if (!double.TryParse(t.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C3d(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C3d.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C3d result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C3d Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C3d( - double.Parse(x[0], CultureInfo.InvariantCulture), - double.Parse(x[1], CultureInfo.InvariantCulture), - double.Parse(x[2], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C3d.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C3d.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional and discarded. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C3d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C3d Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C3d.Setter); - } + => TryParse(t, out C3d result) ? result : throw new FormatException($"{t} is not a valid C3d color."); #endregion @@ -10091,6 +10490,17 @@ public static bool IsFinite(C3d c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C3d c) + => c.ToC3b().ToHexString(); + + #endregion + #region Comparisons /// @@ -12466,31 +12876,100 @@ public static C4b DivideByInt(C4b c, int x) #region Parsing - public static C4b Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4b result) { - return Parse(s); + if (Col.TryParseHex(t, out result)) + { + return true; + } + else + { + bool success = true; + byte[] values = new byte[4] { 255, 255, 255, 255 }; + + byte parse(Text t) + { + if (!byte.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out byte value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4b(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4b.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4b result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4b Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4b( - byte.Parse(x[0], CultureInfo.InvariantCulture), - byte.Parse(x[1], CultureInfo.InvariantCulture), - byte.Parse(x[2], CultureInfo.InvariantCulture), - byte.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4b.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4b.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4b color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4b Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4b.Setter); - } + => TryParse(t, out C4b result) ? result : throw new FormatException($"{t} is not a valid C4b color."); #endregion @@ -12608,6 +13087,17 @@ public static bool IsTiny(this C4b c, byte epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4b c) + => $"{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}"; + + #endregion + #region Comparisons /// @@ -13457,7 +13947,7 @@ public C4us(V3i vec) R = (ushort)(vec.X); G = (ushort)(vec.Y); B = (ushort)(vec.Z); - A = 2^16 - 1; + A = 65535; } /// @@ -13471,7 +13961,7 @@ public C4us(V3ui vec) R = (ushort)(vec.X); G = (ushort)(vec.Y); B = (ushort)(vec.Z); - A = 2^16 - 1; + A = 65535; } /// @@ -13485,7 +13975,7 @@ public C4us(V3l vec) R = (ushort)(vec.X); G = (ushort)(vec.Y); B = (ushort)(vec.Z); - A = 2^16 - 1; + A = 65535; } /// @@ -13499,7 +13989,7 @@ public C4us(V3f vec) R = (ushort)(vec.X); G = (ushort)(vec.Y); B = (ushort)(vec.Z); - A = 2^16 - 1; + A = 65535; } /// @@ -13513,7 +14003,7 @@ public C4us(V3d vec) R = (ushort)(vec.X); G = (ushort)(vec.Y); B = (ushort)(vec.Z); - A = 2^16 - 1; + A = 65535; } /// @@ -14943,31 +15433,101 @@ public static C4us DivideByInt(C4us c, int x) #region Parsing - public static C4us Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4us.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4us result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4us(); + return true; + } + else + { + bool success = true; + ushort[] values = new ushort[4] { 65535, 65535, 65535, 65535 }; + + ushort parse(Text t) + { + if (!ushort.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out ushort value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4us(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4us.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4us result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4us Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4us( - ushort.Parse(x[0], CultureInfo.InvariantCulture), - ushort.Parse(x[1], CultureInfo.InvariantCulture), - ushort.Parse(x[2], CultureInfo.InvariantCulture), - ushort.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4us.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4us.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4us color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4us Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4us.Setter); - } + => TryParse(t, out C4us result) ? result : throw new FormatException($"{t} is not a valid C4us color."); #endregion @@ -15085,6 +15645,17 @@ public static bool IsTiny(this C4us c, ushort epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4us c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -15933,7 +16504,7 @@ public C4ui(V3ui vec) R = (vec.X); G = (vec.Y); B = (vec.Z); - A = 2^32 - 1; + A = UInt32.MaxValue; } /// @@ -15947,7 +16518,7 @@ public C4ui(V3l vec) R = (uint)(vec.X); G = (uint)(vec.Y); B = (uint)(vec.Z); - A = 2^32 - 1; + A = UInt32.MaxValue; } /// @@ -15961,7 +16532,7 @@ public C4ui(V3f vec) R = (uint)(vec.X); G = (uint)(vec.Y); B = (uint)(vec.Z); - A = 2^32 - 1; + A = UInt32.MaxValue; } /// @@ -15975,7 +16546,7 @@ public C4ui(V3d vec) R = (uint)(vec.X); G = (uint)(vec.Y); B = (uint)(vec.Z); - A = 2^32 - 1; + A = UInt32.MaxValue; } /// @@ -17325,31 +17896,101 @@ public static C4ui DivideByInt(C4ui c, int x) #region Parsing - public static C4ui Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4ui.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4ui result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4ui(); + return true; + } + else + { + bool success = true; + uint[] values = new uint[4] { UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue, UInt32.MaxValue }; + + uint parse(Text t) + { + if (!uint.TryParse(t.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out uint value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4ui(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4ui.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4ui result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4ui Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4ui( - uint.Parse(x[0], CultureInfo.InvariantCulture), - uint.Parse(x[1], CultureInfo.InvariantCulture), - uint.Parse(x[2], CultureInfo.InvariantCulture), - uint.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4ui.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4ui.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4ui color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4ui Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4ui.Setter); - } + => TryParse(t, out C4ui result) ? result : throw new FormatException($"{t} is not a valid C4ui color."); #endregion @@ -17467,6 +18108,17 @@ public static bool IsTiny(this C4ui c, uint epsilon) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4ui c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -18241,7 +18893,7 @@ public C4f(V3f vec) R = (vec.X); G = (vec.Y); B = (vec.Z); - A = 1; + A = 1.0f; } /// @@ -18254,7 +18906,7 @@ public C4f(V3d vec) R = (float)(vec.X); G = (float)(vec.Y); B = (float)(vec.Z); - A = 1; + A = 1.0f; } /// @@ -19497,31 +20149,101 @@ public static C4f DivideByInt(C4f c, int x) #region Parsing - public static C4f Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4f.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4f result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4f(); + return true; + } + else + { + bool success = true; + float[] values = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f }; + + float parse(Text t) + { + if (!float.TryParse(t.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4f(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4f.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4f result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4f Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4f( - float.Parse(x[0], CultureInfo.InvariantCulture), - float.Parse(x[1], CultureInfo.InvariantCulture), - float.Parse(x[2], CultureInfo.InvariantCulture), - float.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4f.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4f.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4f color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4f Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4f.Setter); - } + => TryParse(t, out C4f result) ? result : throw new FormatException($"{t} is not a valid C4f color."); #endregion @@ -19682,6 +20404,17 @@ public static bool IsFinite(C4f c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4f c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// @@ -20453,7 +21186,7 @@ public C4d(V3f vec) R = (double)(vec.X); G = (double)(vec.Y); B = (double)(vec.Z); - A = 1; + A = 1.0; } /// @@ -20466,7 +21199,7 @@ public C4d(V3d vec) R = (vec.X); G = (vec.Y); B = (vec.Z); - A = 1; + A = 1.0; } /// @@ -21709,31 +22442,101 @@ public static C4d DivideByInt(C4d c, int x) #region Parsing - public static C4d Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4d.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out C4d result) { - return Parse(s); + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.ToC4d(); + return true; + } + else + { + bool success = true; + double[] values = new double[4] { 1.0, 1.0, 1.0, 1.0 }; + + double parse(Text t) + { + if (!double.TryParse(t.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new C4d(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, C4d.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out C4d result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static C4d Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new C4d( - double.Parse(x[0], CultureInfo.InvariantCulture), - double.Parse(x[1], CultureInfo.InvariantCulture), - double.Parse(x[2], CultureInfo.InvariantCulture), - double.Parse(x[3], CultureInfo.InvariantCulture) - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text.Parse, C4d.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text.Parse, C4d.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid C4d color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static C4d Parse(Text t) - { - return t.NestedBracketSplit(1, Text.Parse, C4d.Setter); - } + => TryParse(t, out C4d result) ? result : throw new FormatException($"{t} is not a valid C4d color."); #endregion @@ -21894,6 +22697,17 @@ public static bool IsFinite(C4d c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBBAA. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this C4d c) + => c.ToC4b().ToHexString(); + + #endregion + #region Comparisons /// diff --git a/src/Aardvark.Base/Math/Colors/Color_template.cs b/src/Aardvark.Base/Math/Colors/Color_template.cs index 3a84f8713..2413e211b 100644 --- a/src/Aardvark.Base/Math/Colors/Color_template.cs +++ b/src/Aardvark.Base/Math/Colors/Color_template.cs @@ -259,6 +259,7 @@ namespace Aardvark.Base //# var getptr = "&" + ((ft == Meta.ByteType) ? fields[2] : fields[0]); //# var rgba = t.HasAlpha ? "RGBA" : "RGB"; //# var maxval = maxvalmap[ft]; + //# var parseNumberStyle = isReal ? "NumberStyles.Float | NumberStyles.AllowThousands" : "NumberStyles.Integer"; #region __type__ /// @@ -510,7 +511,7 @@ public __type__(__t1.Name__ color, __ftype__ alpha) public __type__(__vt.Name__ vec) { //# fields.ForEach(Meta.VecFields, (c, vf, i) => { - __c__ = /*# if (i < d) { */__convert__(vec.__vf__);/*# } else {*/__maxval__;/*# }*/ + __c__ = /*# if (i < d) { */__convert__(vec.__vf__);/*# } else {*/__t.MaxValue__;/*# }*/ //# }); } @@ -1133,28 +1134,108 @@ public static __type__ DivideByInt(__type__ c, int x) #region Parsing - public static __type__ Parse(string s, IFormatProvider provider) + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, __type__.Zero otherwise. + /// True on success, false otherwise. + public static bool TryParse(Text t, out __type__ result) { - return Parse(s); + //# if (type == "C4b") { + if (Col.TryParseHex(t, out result)) + { + return true; + } + //# } else { + if (Col.TryParseHex(t, out C4b tmp)) + { + result = tmp.To__type__(); + return true; + } + //# } + else + { + bool success = true; + __ftype__[] values = new __ftype__[4] { /*# 4.ForEach(p => { */__t.MaxValue__/*# }, comma);*/ }; + + __ftype__ parse(Text t) + { + if (!__ftype__.TryParse(t.ToString(), __parseNumberStyle__, CultureInfo.InvariantCulture, out __ftype__ value)) + success = false; + + return value; + }; + + var count = t.NestedBracketSplitCount2(1); + if (count == 3 || count == 4) + t.NestedBracketSplit(1, parse, () => values); + else + success = false; + + result = success ? new __type__(values) : Zero; + return success; + } } + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// Contains the parsed color on success, __type__.Zero otherwise. + /// True on success, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParse(string s, out __type__ result) + => TryParse(new Text(s), out result); + + [Obsolete("Parameter provider is unused.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static __type__ Parse(string s, IFormatProvider provider) + => Parse(s); + + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid __type__ color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(string s) - { - var x = s.NestedBracketSplitLevelOne().ToArray(); - return new __type__(/*# t.Len.ForEach(p => { */ - __ftype__.Parse(x[__p__], CultureInfo.InvariantCulture)/*# }, comma); */ - ); - } + => Parse(new Text(s)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(Text t, int bracketLevel = 1) - { - return t.NestedBracketSplit(bracketLevel, Text<__ftype__>.Parse, __type__.Setter); - } + => t.NestedBracketSplit(bracketLevel, Text<__ftype__>.Parse, __type__.Setter); + /// + /// Parses a color string with decimal format [R, G, B, A], or hexadecimal formats RRGGBBAA or RGBA. + /// + /// + /// The alpha component in any format is optional/*# if (!t.HasAlpha) {*/ and discarded/*#}*/. + /// For the single digit hexadecimal RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF"). + /// Color strings in a hexadecimal format may be prefixed by "#" or "0x". + /// + /// The string to be parsed. + /// The parsed color. + /// the input does not represent a valid __type__ color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static __type__ Parse(Text t) - { - return t.NestedBracketSplit(1, Text<__ftype__>.Parse, __type__.Setter); - } + => TryParse(t, out __type__ result) ? result : throw new FormatException($"{t} is not a valid __type__ color."); #endregion @@ -1336,6 +1417,21 @@ public static bool IsFinite(__type__ c) public static partial class Col { + #region ToHexString + + /// + /// Returns the hexadecimal representation with format RRGGBB/*# if (t.HasAlpha) {*/AA/*#}*/. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToHexString(this __type__ c) + //# if (ft == Meta.ByteType) { + => /*# if (t.HasAlpha) {*/$"{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}"/*# } else {*/$"{c.R:X2}{c.G:X2}{c.B:X2}"/*#}*/; + //# } else { + => c.ToC__t.Len__b().ToHexString(); + //# } + + #endregion + #region Comparisons //# var bops = new[,] { { "<", "Smaller" }, { ">" , "Greater"}, diff --git a/src/Aardvark.Base/Text/Text.cs b/src/Aardvark.Base/Text/Text.cs index a06077985..677facd61 100644 --- a/src/Aardvark.Base/Text/Text.cs +++ b/src/Aardvark.Base/Text/Text.cs @@ -1254,7 +1254,8 @@ public static string[] ToStringArray(this Text[] textArray) public static List ToListOfString(this Text[] textArray) => textArray.MapToList(t => t.ToString()); - + + [Obsolete("Does not always return the same count as NestedBracketSplit() result length. Use NestedBracketSplitCount2 instead.")] public static int NestedBracketSplitCount(this Text text, int splitLevel) { int count = 0; @@ -1274,6 +1275,38 @@ public static int NestedBracketSplitCount(this Text text, int splitLevel) return count; } + public static int NestedBracketSplitCount2(this Text text, int splitLevel) + { + int count = 0; + int level = 0; + int begin = text.Start; + int end = text.End; + for (int pos = text.Start; pos < end; pos++) + { + switch (text.String[pos]) + { + case '[': + ++level; + if (level == splitLevel) begin = pos + 1; + break; + case ']': + if (level == splitLevel) ++count; + --level; + break; + case ',': + if (level == splitLevel) + { + ++count; + begin = pos + 1; + } + break; + } + } + if (level == splitLevel && begin < end) + ++count; + return count; + } + #endregion #region Text Extensions diff --git a/src/Aardvark.PixImage.WindowsMedia/PixImageWindowsMedia.cs b/src/Aardvark.PixImage.WindowsMedia/PixImageWindowsMedia.cs index 3325f3fca..e65bcd9dc 100644 --- a/src/Aardvark.PixImage.WindowsMedia/PixImageWindowsMedia.cs +++ b/src/Aardvark.PixImage.WindowsMedia/PixImageWindowsMedia.cs @@ -240,12 +240,13 @@ private class PixLoader : IPixLoader /// public PixImage LoadFromFile(string filename) { + using var stream = new MemoryStream(File.ReadAllBytes(filename)); + var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); - bitmapImage.UriSource = new Uri(filename, UriKind.RelativeOrAbsolute); + bitmapImage.StreamSource = stream; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; - bitmapImage.CreateOptions = BitmapCreateOptions.None - | BitmapCreateOptions.PreservePixelFormat; + bitmapImage.CreateOptions = BitmapCreateOptions.PreservePixelFormat; bitmapImage.EndInit(); return CreateFromBitmapSource(bitmapImage); } diff --git a/src/Tests/Aardvark.Base.Benchmarks/Aardvark.Base.Benchmarks.csproj b/src/Tests/Aardvark.Base.Benchmarks/Aardvark.Base.Benchmarks.csproj index 9038b6aaf..ac034b9c5 100644 --- a/src/Tests/Aardvark.Base.Benchmarks/Aardvark.Base.Benchmarks.csproj +++ b/src/Tests/Aardvark.Base.Benchmarks/Aardvark.Base.Benchmarks.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Tests/Aardvark.Base.Benchmarks/Program.cs b/src/Tests/Aardvark.Base.Benchmarks/Program.cs index a1fb1e237..70ed7aa13 100644 --- a/src/Tests/Aardvark.Base.Benchmarks/Program.cs +++ b/src/Tests/Aardvark.Base.Benchmarks/Program.cs @@ -1,6 +1,5 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Jobs; namespace Aardvark.Base.Benchmarks { @@ -9,7 +8,9 @@ public class Program public static void Main(string[] args) { var cfg = ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator); - BenchmarkSwitcher.FromAssembly(typeof(IntegerPowerFloat).Assembly).Run(args, cfg); + //BenchmarkSwitcher.FromAssembly(typeof(IntegerPowerFloat).Assembly).Run(args, cfg); + + BenchmarkRunner.Run(cfg); //BenchmarkRunner.Run(); //BenchmarkRunner.Run i); + _writer = new StreamCodeWriter(new MemoryStream(_data.Length * 4)); + + var tmpWriter = new StreamCodeWriter(new MemoryStream(_data.Length * 4)); + tmpWriter.WriteArray(_data, 0, _data.Length); + _reader = new StreamCodeReader(tmpWriter.BaseStream); + } + + [Benchmark] + public void WriteArray() + { + _writer.BaseStream.Position = 0; + _writer.WriteArray(_data, 0, _data.Length); + } + + [Benchmark] + public void ReadArray() + { + _reader.BaseStream.Position = 0; + _reader.ReadArray(_data, 0, _data.Length); + } + } +} diff --git a/src/Tests/Aardvark.Base.Benchmarks/TensorMathBenchmark.cs b/src/Tests/Aardvark.Base.Benchmarks/TensorMathBenchmark.cs new file mode 100644 index 000000000..660b179a2 --- /dev/null +++ b/src/Tests/Aardvark.Base.Benchmarks/TensorMathBenchmark.cs @@ -0,0 +1,108 @@ +using Aardvark.Base.IL; +using BenchmarkDotNet.Attributes; +using System; +using System.Runtime.CompilerServices; +using static Aardvark.Base.Monads.Optics; +using System.Runtime.Intrinsics.X86; + +namespace Aardvark.Base.Benchmarks +{ + /* + * Benchmark indicates that inlined implementations are noticeably more efficient than + * simply using Norm or InnerProduct and passing lambda functions. Therefore it may be wise to implement + * common tensor operations in a dumb inline way for optimization purposes. The InlineIfLambda attribute + * in F# might mitigate this but we are dealing with C# here. + * + * BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 10 (10.0.19045.4170/22H2/2022Update) + * AMD Ryzen 5 5600X, 1 CPU, 12 logical and 6 physical cores + * .NET SDK 6.0.420 + * [Host] : .NET 6.0.28 (6.0.2824.12007), X64 RyuJIT AVX2 + * + * Job = InProcess Toolchain=InProcessEmitToolchain + * + * | Method | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio | + * |--------------------------- |-----------:|---------:|---------:|------:|--------:|----------:|------------:| + * | NormSquared | 1,377.5 us | 24.30 us | 23.87 us | 1.00 | 0.00 | 6 B | 1.00 | + * | 'NormSquared (Predefined)' | 1,771.9 us | 35.15 us | 40.48 us | 1.28 | 0.04 | 1 B | 0.17 | + * | 'NormSquared (Inline)' | 340.1 us | 1.77 us | 1.48 us | 0.25 | 0.00 | - | 0.00 | + */ + + [InProcess] + [MemoryDiagnoser] + public class VectorNormSquared + { + private static readonly Func SquareFunc = Fun.Square; + private static readonly Func AddFunc = (x, y) => x + y; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float NormSquared(Vector vector) + => vector.Norm(x => x * x, 0.0f, (x, y) => x + y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float NormSquaredPredefined(Vector vector) + => vector.Norm(SquareFunc, 0.0f, AddFunc); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float NormSquaredInline(Vector v) + { + float result = 0.0f; + long i = v.FirstIndex; + if (v.Info.DX == 1) + { + long xs = v.Info.SX; + for (long xe = i + xs; i != xe; i++) + { + result += v.Data[i] * v.Data[i]; + } + } + else + { + long xs = v.Info.DSX, xj = v.Info.JX; + for (long xe = i + xs; i != xe; i += xj) + { + result += v.Data[i] * v.Data[i]; + } + } + return result; + } + + private static Vector GenerateVector(RandomSystem rnd) + => Vector.Create(new float[4 + rnd.UniformInt(1000)].SetByIndex(i => rnd.UniformFloat())); + + Vector[] _data; + + [GlobalSetup] + public void Init() + { + var rnd = new RandomSystem(0); + _data = new Vector[1000].SetByIndex(i => GenerateVector(rnd)); + } + + [Benchmark(Baseline = true, Description = "NormSquared")] + public float NormSquared() + { + float sum = 0; + foreach (var x in _data) + sum += NormSquared(x); + return sum; + } + + [Benchmark(Description = "NormSquared (Predefined)")] + public float NormSquaredPredfined() + { + float sum = 0; + foreach (var x in _data) + sum += NormSquaredPredefined(x); + return sum; + } + + [Benchmark(Description = "NormSquared (Inline)")] + public float NormSquaredInline() + { + float sum = 0; + foreach (var x in _data) + sum += NormSquaredInline(x); + return sum; + } + } +} diff --git a/src/Tests/Aardvark.Base.FSharp.Benchmarks/Aardvark.Base.FSharp.Benchmarks.fsproj b/src/Tests/Aardvark.Base.FSharp.Benchmarks/Aardvark.Base.FSharp.Benchmarks.fsproj index 291f4317d..4f7b23db6 100644 --- a/src/Tests/Aardvark.Base.FSharp.Benchmarks/Aardvark.Base.FSharp.Benchmarks.fsproj +++ b/src/Tests/Aardvark.Base.FSharp.Benchmarks/Aardvark.Base.FSharp.Benchmarks.fsproj @@ -1,9 +1,10 @@ - + + Exe net6.0 - true false + true 3389;3390;3395 false @@ -20,6 +21,8 @@ true + + diff --git a/src/Tests/Aardvark.Base.FSharp.Benchmarks/RangeSet.fs b/src/Tests/Aardvark.Base.FSharp.Benchmarks/RangeSet.fs new file mode 100644 index 000000000..a8475a98f --- /dev/null +++ b/src/Tests/Aardvark.Base.FSharp.Benchmarks/RangeSet.fs @@ -0,0 +1,319 @@ +namespace Aardvark.Base.FSharp.Benchmarks + +#nowarn "44" + +open System +open Aardvark.Base + +module RangeSetTests = + open FsUnit + open NUnit.Framework + + [] + let ``[RangeSet] Insert`` (maxValue : bool) (mergeWithMaxValue : bool) = + let mutable s1 = RangeSet64.empty + let mutable s2 = RangeSet1l.empty + + let add (r : Range1l) = + s1 <- s1 |> RangeSet64.insert r + s2 <- s2 |> RangeSet1l.add r + + let equal (expected : Range1l list) = + // Old implementation broken for MaxValue! + if not maxValue then + Seq.toList s1 |> should equal (Seq.toList s2) + + Seq.toList s2 |> should equal expected + + add <| Range1l(0L, 1L) + add <| Range1l(1L, 2L) + equal [ Range1l(0L, 2L) ] + + add <| Range1l(3L, 4L) + equal [ Range1l(0L, 4L) ] + + add <| Range1l(6L, 8L) + equal [ Range1l(0L, 4L); Range1l(6L, 8L) ] + + if maxValue then + add <| Range1l Int64.MaxValue + equal [ Range1l(0L, 4L); Range1l(6L, 8L); Range1l(Int64.MaxValue) ] + + add <| Range1l(9L, 14L) + equal [ Range1l(0L, 4L); Range1l(6L, 14L); Range1l(Int64.MaxValue) ] + + add <| Range1l(42L, if mergeWithMaxValue then Int64.MaxValue - 1L else Int64.MaxValue) + equal [ Range1l(0L, 4L); Range1l(6L, 14L); Range1l(42L, Int64.MaxValue) ] + + [] + let ``[RangeSet] Remove`` (maxValue : bool) = + let init = [ Range1l(0L, 4L); Range1l(6L, 8L); if maxValue then Range1l(42L, Int64.MaxValue) ] + let mutable s1 = RangeSet64.ofList init + let mutable s2 = RangeSet1l.ofList init + + let rem (r : Range1l) = + s1 <- s1 |> RangeSet64.remove r + s2 <- s2 |> RangeSet1l.remove r + + let equal (expected : Range1l list) = + // Old implementation broken for MaxValue! + if not maxValue then + Seq.toList s1 |> should equal (Seq.toList s2) + + Seq.toList s2 |> should equal expected + + rem <| Range1l(3L, 3L) + equal [ Range1l(0L, 2L); Range1l(4L, 4L); Range1l(6L, 8L); if maxValue then Range1l(42L, Int64.MaxValue) ] + + rem <| Range1l(4L, 4L) + equal [ Range1l(0L, 2L); Range1l(6L, 8L); if maxValue then Range1l(42L, Int64.MaxValue) ] + + rem <| Range1l(2L, 7L) + equal [ Range1l(0L, 1L); Range1l(8L, 8L); if maxValue then Range1l(42L, Int64.MaxValue) ] + + rem <| Range1l(-12L, 7L) + equal [ Range1l(8L, 8L); if maxValue then Range1l(42L, Int64.MaxValue) ] + + if maxValue then + rem <| Range1l(0L, Int64.MaxValue - 1L) + equal [ Range1l(Int64.MaxValue) ] + + rem <| Range1l(Int64.MaxValue) + equal [] + + s2 <- RangeSet1l.ofList [ Range1l(0L, Int64.MaxValue) ] + rem <| Range1l(Int64.MaxValue) + equal [ Range1l(0L, Int64.MaxValue - 1L) ] + + [] + let ``[RangeSet] ToList`` (maxValue : bool) = + let init = [ Range1l(0L, 4L); Range1l(6L, 8L); if maxValue then Range1l(10L, Int64.MaxValue) ] + let mutable s1 = RangeSet64.ofList init + let mutable s2 = RangeSet1l.ofList init + + RangeSet64.toList s1 |> should equal init + RangeSet1l.toList s2 |> should equal init + + [] + let ``[RangeSet] ToArray`` (maxValue : bool) = + let init = [ Range1l(0L, 4L); Range1l(6L, 8L); if maxValue then Range1l(10L, Int64.MaxValue) ] + let mutable s1 = RangeSet64.ofList init + let mutable s2 = RangeSet1l.ofList init + + RangeSet64.toArray s1 |> should equal (Array.ofList init) + RangeSet1l.toArray s2 |> should equal (Array.ofList init) + + [] + let ``[RangeSet] ToSeq`` (maxValue : bool) = + let init = [ Range1l(0L, 4L); Range1l(6L, 8L); if maxValue then Range1l(10L, Int64.MaxValue) ] + let mutable s1 = RangeSet64.ofList init + let mutable s2 = RangeSet1l.ofList init + + RangeSet64.toSeq s1 |> Seq.toList |> should equal init + RangeSet1l.toSeq s2 |> Seq.toList |> should equal init + + [] + let ``[RangeSet] Min / Max / Range`` (maxValue : bool) = + let init = [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L); if maxValue then Range1l(10L, Int64.MaxValue) ] + let min = init |> List.map (fun r -> r.Min) |> List.min + let max = init |> List.map (fun r -> r.Max) |> List.max + + let mutable s1 = RangeSet64.ofList init + let mutable s2 = RangeSet1l.ofList init + + s1.Min |> should equal s2.Min + s2.Min |> should equal min + + s1.Max |> should equal (max + 1L) // Old implementation is inconsistent! + s2.Max |> should equal max + + s1.Range |> should equal (Range1l(min, max + 1L)) + s2.Range |> should equal (Range1l(min, max)) + + [] + let ``[RangeSet] Intersect`` (hasMaxValue : bool) (testMaxValue : bool) = + let init = [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L); if hasMaxValue then Range1l(10L, Int64.MaxValue)] + let s1 = RangeSet64.ofList init + let s2 = RangeSet1l.ofList init + + let intersect (r : Range1l) = + let r1 = s1 |> RangeSet64.window r + let r2 = s2 |> RangeSet1l.intersect r + + // Old implementation broken for MaxValue! + if not (hasMaxValue || testMaxValue) then + (RangeSet64.toList r1) |> should equal (RangeSet1l.toList r2) + + RangeSet1l.toList r2 + + if testMaxValue then + Range1l(2L, Int64.MaxValue) |> intersect |> should equal [ Range1l(2L, 4L); Range1l(6L, 8L); if hasMaxValue then Range1l(10L, Int64.MaxValue) ] + Range1l(5L, Int64.MaxValue) |> intersect |> should equal [ Range1l(6L, 8L); if hasMaxValue then Range1l(10L, Int64.MaxValue) ] + Range1l(1L, Int64.MaxValue) |> intersect |> should equal [ Range1l(1L, 4L); Range1l(6L, 8L); if hasMaxValue then Range1l(10L, Int64.MaxValue) ] + Range1l(4L, Int64.MaxValue) |> intersect |> should equal [ Range1l(4L, 4L); Range1l(6L, 8L); if hasMaxValue then Range1l(10L, Int64.MaxValue) ] + Range1l(-3L, Int64.MaxValue) |> intersect |> should equal init + Range1l(-42L, Int64.MaxValue) |> intersect |> should equal init + else + Range1l(2L, 3L) |> intersect |> should equal [ Range1l(2L, 3L) ] + Range1l(5L, 6L) |> intersect |> should equal [ Range1l(6L, 6L) ] + Range1l(-3L, 8L) |> intersect |> should equal [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L) ] + Range1l(-42L, 42L) |> intersect |> should equal [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L); if hasMaxValue then Range1l(10L, 42L) ] + Range1l(-3L, -2L) |> intersect |> should equal [ Range1l(-3L, -2L) ] + Range1l(-2L, -1L) |> intersect |> should equal [ Range1l(-2L, -2L); ] + Range1l(-2L, 1L) |> intersect |> should equal [ Range1l(-2L, -2L); Range1l(1L, 1L) ] + Range1l(-3L, 3L) |> intersect |> should equal [ Range1l(-3L, -2L); Range1l(1L, 3L) ] + Range1l(-2L, 8L) |> intersect |> should equal [ Range1l(-2L, -2L); Range1l(1L, 4L); Range1l(6L, 8L) ] + + [] + let ``[RangeSet] Enumerator`` (maxValue : bool) = + let init = [ Range1l(-3L, -2L); Range1l(1L, 4L); if maxValue then Range1l(6L, Int64.MaxValue) ] + let s1 = RangeSet64.ofList init + let s2 = RangeSet1l.ofList init + + let expected = Seq.ofList init + s1 :> seq<_> |> should equal (s2 :> seq<_>) + s2 :> seq<_> |> should equal expected + + [] + let ``[RangeSet] Equality`` (maxValue : bool) = + let init = [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L); if maxValue then Range1l(34L, Int64.MaxValue) ] + let mutable a1 = RangeSet64.ofList init + let mutable b1 = RangeSet64.ofList init + let mutable a2 = RangeSet1l.ofList init + let mutable b2 = RangeSet1l.ofList init + + (a1 = b1) |> should be True + (a2 = b2) |> should be True + + [] + let ``[RangeSet] ofList`` () = + let rnd = RandomSystem(0) + + let ranges = + List.init 5 (fun _ -> + let mutable l = Int64.MaxValue + let mutable r = Int64.MinValue + + while l > r do + l <- rnd.UniformLong() + r <- l + rnd.UniformLong(100000L) + + Range1l(l, r) + ) + + let expected = (RangeSet1l.empty, ranges) ||> List.fold (fun s r -> RangeSet1l.add r s) + let actual = RangeSet1l.ofList ranges + actual |> should equal expected + + [] + let ``[RangeSet] Contains Range`` (maxValue : bool) = + let set = RangeSet1l.ofList [ Range1l(-3L, -2L); Range1l(1L, 4L); Range1l(6L, 8L); Range1l(10L, if maxValue then Int64.MaxValue else 30L)] + + set |> RangeSet1l.containsRange (Range1l(-3L, -2L)) |> should be True + set |> RangeSet1l.containsRange (Range1l(-3L, -3L)) |> should be True + set |> RangeSet1l.containsRange (Range1l(-3L, -2L)) |> should be True + set |> RangeSet1l.containsRange (Range1l(-2L, -2L)) |> should be True + + set |> RangeSet1l.containsRange (Range1l(2L, 3L)) |> should be True + set |> RangeSet1l.containsRange (Range1l(3L, 3L)) |> should be True + + set |> RangeSet1l.containsRange (Range1l(-2L, 3L)) |> should be False + + set |> RangeSet1l.containsRange (Range1l(20L, Int64.MaxValue)) |> should equal maxValue + +module RangeSetBenchmarks = + open BenchmarkDotNet.Attributes; + + [] + type ``RangeSets``() = + let rnd = RandomSystem(0) + + let mutable setOld = RangeSet64.empty + let mutable setNew = RangeSet1l.empty + let mutable ranges = List.empty + let mutable ranges2 = List.empty + + [] + val mutable Count : int + + member private x.RandomRange() = + let mutable l = Int64.MaxValue + let mutable r = Int64.MinValue + + while l > r do + l <- rnd.UniformLong() + r <- l + rnd.UniformLong(100000L) + + Range1l(l, r) + + member private x.Generate(count : int) = + List.init count (ignore >> x.RandomRange) + + [] + member x.Init() = + ranges <- x.Generate x.Count + ranges2 <- x.Generate x.Count + setOld <- RangeSet64.ofList ranges + setNew <- RangeSet1l.ofList ranges + + + [] + member x.OfListOld() = + RangeSet64.ofList ranges + + [] + member x.OfListNew() = + RangeSet1l.ofList ranges + + + [] + member x.InsertOld() = + let mutable set = RangeSet64.empty + + for r in ranges do + set <- set |> RangeSet64.insert r + + set + + [] + member x.InsertNew() = + let mutable set = RangeSet1l.empty + + for r in ranges do + set <- set |> RangeSet1l.add r + + set + + + [] + member x.RemoveOld() = + for r in ranges2 do + setOld <- setOld |> RangeSet64.remove r + + setOld + + [] + member x.RemoveNew() = + for r in ranges2 do + setNew <- setNew |> RangeSet1l.remove r + + setNew + + + [] + member x.IterateOld() = + let mutable cnt = Int64.MinValue + + for r in setOld do + cnt <- cnt + r.Min + + cnt + + [] + member x.IterateNew() = + let mutable cnt = Int64.MinValue + + for r in setNew do + cnt <- cnt + r.Min + + cnt \ No newline at end of file diff --git a/src/Tests/Aardvark.Base.FSharp.Benchmarks/paket.references b/src/Tests/Aardvark.Base.FSharp.Benchmarks/paket.references index 556724027..d54763f39 100644 --- a/src/Tests/Aardvark.Base.FSharp.Benchmarks/paket.references +++ b/src/Tests/Aardvark.Base.FSharp.Benchmarks/paket.references @@ -3,5 +3,6 @@ group Test FSharp.Core BenchmarkDotNet NUnit +FsUnit NUnit3TestAdapter Microsoft.NET.Test.Sdk \ No newline at end of file diff --git a/src/Tests/Aardvark.Base.FSharp.Tests/MapExt.fs b/src/Tests/Aardvark.Base.FSharp.Tests/MapExt.fs index 786519bb1..0ff7e4730 100644 --- a/src/Tests/Aardvark.Base.FSharp.Tests/MapExt.fs +++ b/src/Tests/Aardvark.Base.FSharp.Tests/MapExt.fs @@ -46,5 +46,11 @@ let ``[MapExt] choose`` (m : Map) (f : int -> int -> Option) = MapExt.toList (MapExt.choose f me) = Map.toList (choose f m) ] - - +[] +let ``[MapExt] range`` (m : Map) (min : int) (max : int) = + (min <= max) ==> lazy ( + let me = MapExt.ofSeq (Map.toSeq m) + let expected = me |> MapExt.filter (fun k _ -> k >= min && k <= max) + let actual = me |> MapExt.range min max + expected = actual + ) \ No newline at end of file diff --git a/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs b/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs index 25eb4616c..e4b062e97 100644 --- a/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs +++ b/src/Tests/Aardvark.Base.Tests/Math/ColorTests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Linq; using Aardvark.Base; using NUnit.Framework; @@ -171,5 +168,91 @@ public void ScalarOperators() Assert.AreEqual(new C4b(scalar, scalar, scalar, scalar) / color, scalar / color); } } + + [Test] + public void Parse() + { + var rnd = new RandomSystem(0); + + for (int i = 0; i < 100; i++) + { + var c = rnd.UniformC4ui(); + var s1 = $"[{c.R}, {c.G}, {c.B}, {c.A}]"; + var s2 = $"[{c.R}, {c.G}, {c.B}]"; + + Assert.AreEqual(c.RGB, C3ui.Parse(s1)); + Assert.AreEqual(c, C4ui.Parse(s1)); + Assert.AreEqual(c.RGB, C3ui.Parse(s2)); + Assert.AreEqual(new C4ui(c.RGB, uint.MaxValue), C4ui.Parse(s2)); + } + } + + [Test] + public void ParseTooManyComponents() + { + var t1 = new Text("[1,2,3"); + var c1 = t1.NestedBracketSplitCount2(1); + var r1 = t1.NestedBracketSplit(1).ToArray(); + + var t2 = new Text("[42]"); + var c2 = t2.NestedBracketSplitCount2(1); + var r2 = t2.NestedBracketSplit(1).ToArray(); + + var t3 = new Text("[42,43]"); + var c3 = t3.NestedBracketSplitCount2(1); + var r3 = t3.NestedBracketSplit(1).ToArray(); + + Assert.AreEqual(r1.Length, c1); + Assert.AreEqual(r2.Length, c2); + Assert.AreEqual(r3.Length, c3); + + Assert.IsFalse(C4d.TryParse("[1, 2, 3, 4, 5]", out C4d _)); + } + + [Test] + public void ParseHex() + { + var rnd = new RandomSystem(1); + + for (int i = 0; i < 100; i++) + { + var color = rnd.UniformC4d().ToC4b(); + var str = color.ToHexString(); + + Assert.IsTrue(Col.TryParseHex(str, out C4b result)); + Assert.IsTrue(C4b.TryParse(str, out C4b result2)); + Assert.AreEqual(color, result); + Assert.AreEqual(result, result2); + } + } + + [Test] + public void ParseHexSingleDigit() + { + var rnd = new RandomSystem(1); + + for (int i = 0; i < 100; i++) + { + var str = rnd.UniformC4d().ToHexString(); + var dbl = $"#{str[0]}{str[0]}{str[2]}{str[2]}{str[4]}{str[4]}"; + var sgl = $"0x{str[0]}{str[2]}{str[4]}F"; + + Assert.IsTrue(Col.TryParseHex(dbl, out C4b a)); + Assert.IsTrue(C4b.TryParse(sgl, out C4b b)); + Assert.AreEqual(a, b); + } + } + + [Test] + [SetCulture("de-DE")] + public void ParseWithBadCulture() + { + var rnd = new RandomSystem(1); + var col = rnd.UniformC4d(); + var str = col.ToString(); + var res = C4d.Parse(str); + + Assert.True(Fun.ApproximateEquals(col, res)); + } } } diff --git a/src/Tests/Aardvark.Base.Windows.Tests/PixLoaderTests.fs b/src/Tests/Aardvark.Base.Windows.Tests/PixLoaderTests.fs index 4842b97ae..0e641a66a 100644 --- a/src/Tests/Aardvark.Base.Windows.Tests/PixLoaderTests.fs +++ b/src/Tests/Aardvark.Base.Windows.Tests/PixLoaderTests.fs @@ -285,6 +285,10 @@ module PixLoaderTests = priorities.Count |> should equal count priorities.Get(PixImageDevil.Loader) |> should equal 1337 + PixImage.AddLoader(PixImageDevil.Loader) + let priorities = PixImage.GetLoadersWithPriority() + priorities.Get(PixImageDevil.Loader) |> should equal 1338 + PixImage.RemoveLoader(PixImageSharp.Loader) PixImage.RemoveLoader(PixImageDevil.Loader) PixImage.GetLoaders() |> Seq.length |> should equal (count - 2)