Full Code of bcarruthers/garnet for AI

master 11f40d9fe26d cached
140 files
738.1 KB
211.2k tokens
1 requests
Download .txt
Showing preview only (782K chars total). Download the full file or copy to clipboard to get everything.
Repository: bcarruthers/garnet
Branch: master
Commit: 11f40d9fe26d
Files: 140
Total size: 738.1 KB

Directory structure:
gitextract_lshbfeyb/

├── .gitignore
├── Directory.Build.props
├── Garnet.sln
├── LICENSE
├── README.md
├── RELEASE_NOTES.md
├── appveyor.yml
├── build.cmd
├── samples/
│   ├── Garnet.Numerics/
│   │   ├── Garnet.Numerics.fsproj
│   │   ├── Hashing.fs
│   │   ├── Noise.fs
│   │   ├── Numerics.fs
│   │   ├── Random.fs
│   │   ├── Ranges.fs
│   │   └── Vectors.fs
│   ├── Garnet.Processor/
│   │   ├── Args.fs
│   │   ├── Garnet.Processor.fsproj
│   │   ├── PackUtility.fs
│   │   └── Program.fs
│   ├── Garnet.Samples.Assorted/
│   │   ├── Extensions.fs
│   │   ├── Garnet.Samples.Assorted.fsproj
│   │   ├── OffscreenDrawing.fs
│   │   ├── Program.fs
│   │   ├── SpriteDrawing.fs
│   │   ├── TextDrawing.fs
│   │   └── assets/
│   │       ├── fonts/
│   │       │   └── pixel-operator-regular-12.font.json
│   │       └── shaders/
│   │           ├── color.frag
│   │           ├── color.frag.hlsl.bytes
│   │           ├── color.vert
│   │           ├── color.vert.hlsl.bytes
│   │           ├── texture-color.frag
│   │           ├── texture-color.frag.hlsl.bytes
│   │           ├── texture-color.vert
│   │           └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Samples.CSharp/
│   │   ├── Garnet.Samples.CSharp.csproj
│   │   └── Program.cs
│   ├── Garnet.Samples.Flocking/
│   │   ├── Debug.fs
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Garnet.Samples.Flocking.fsproj
│   │   ├── Program.fs
│   │   ├── Resources.fs
│   │   ├── Simulation.fs
│   │   ├── Startup.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-color.frag
│   │       ├── texture-color.frag.hlsl.bytes
│   │       ├── texture-color.vert
│   │       └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Samples.Roguelike/
│   │   ├── ConsoleTest.fsx
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Game.fs
│   │   ├── Garnet.Samples.Roguelike.fsproj
│   │   ├── Program.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-dual-color.frag
│   │       └── texture-dual-color.vert
│   ├── Garnet.Samples.Trixel/
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Game.fs
│   │   ├── Garnet.Samples.Trixel.fsproj
│   │   ├── Gui.fs
│   │   ├── Imaging.fs
│   │   ├── Program.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-color.frag
│   │       ├── texture-color.frag.hlsl.bytes
│   │       ├── texture-color.vert
│   │       └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Toolkit/
│   │   ├── Audio.fs
│   │   ├── Collections.fs
│   │   ├── Colors.fs
│   │   ├── Comparisons.fs
│   │   ├── Events.fs
│   │   ├── Fonts.fs
│   │   ├── Garnet.Toolkit.fsproj
│   │   ├── Input.fs
│   │   ├── Logging.fs
│   │   ├── Looping.fs
│   │   ├── Meshes.fs
│   │   ├── Offscreen.fs
│   │   ├── Particles.fs
│   │   ├── Picking.fs
│   │   ├── Pipelines.fs
│   │   ├── Rendering.fs
│   │   ├── Requests.fs
│   │   ├── Serialization.fs
│   │   ├── Shaders.fs
│   │   ├── Sprites.fs
│   │   ├── Systems.fs
│   │   ├── Textures.fs
│   │   ├── Tiling.fs
│   │   ├── Timing.fs
│   │   ├── Vertices.fs
│   │   └── Window.fs
│   └── README.md
├── src/
│   └── Garnet/
│       ├── Actors.fs
│       ├── Channels.fs
│       ├── Collections.fs
│       ├── Comparisons.fs
│       ├── Components.fs
│       ├── Containers.fs
│       ├── Coroutines.fs
│       ├── Entities.fs
│       ├── Formatting.fs
│       ├── Garnet.fsproj
│       ├── Messaging.fs
│       ├── Queries.fs
│       ├── Registry.fs
│       ├── Resources.fs
│       └── Segments.fs
└── tests/
    └── Garnet.Tests/
        ├── ActorTests.fs
        ├── Assertions.fs
        ├── Benchmarks/
        │   ├── ActorBenchmarks.fs
        │   ├── ActorBenchmarks.fsx
        │   ├── ChannelBenchmarks.fs
        │   ├── ChannelBenchmarks.fsx
        │   ├── ContainerBenchmarks.fs
        │   └── ContainerBenchmarks.fsx
        ├── ChannelTests.fs
        ├── CollectionTests.fs
        ├── ComponentTests.fs
        ├── CoroutineTests.fs
        ├── EntityTests.fs
        ├── Garnet.Tests.fsproj
        ├── Iteration.fs
        ├── IterationTests.fs
        ├── Main.fs
        ├── QueryTests.fs
        ├── ReadmeSamples.fs
        ├── Recording.fs
        ├── RegistryTests.fs
        ├── RingBuffer.fs
        ├── Scratch.fsx
        ├── SegmentTests.fs
        ├── Serialization.fs
        ├── SerializationTests.fs
        ├── StateMachineTests.fs
        └── StrategySample.fs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.suo
*.swp
*.user
*.userprefs
*.pidb
*.nupkg
*.sln.ide
*.orig
*.vsp
*.psess
*.vspx
*.stackdump

.idea/
.fake/
.vs/
.vscode/
.ionide
packages/
build/
publish/
artifacts/
BenchmarkDotNet.Artifacts/
bin
obj

#Paket dependency manager
.paket/
paket-files/


================================================
FILE: Directory.Build.props
================================================
<Project>

  <PropertyGroup>
    <Version>0.5.3</Version>
    <Authors>Ben Carruthers</Authors>
    <Copyright>Copyright © 2021 Ben Carruthers</Copyright>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
    <RepositoryUrl>https://github.com/bcarruthers/garnet</RepositoryUrl>
  </PropertyGroup>

</Project>


================================================
FILE: Garnet.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 15.0.26124.0
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Garnet", "src\Garnet\Garnet.fsproj", "{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Garnet.Tests", "tests\Garnet.Tests\Garnet.Tests.fsproj", "{97D1D0D1-C635-4725-A892-DE1852A0CB4C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{1E82F9DC-427D-46D1-9352-FB1E97724CAF}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Garnet.Toolkit", "samples\Garnet.Toolkit\Garnet.Toolkit.fsproj", "{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Garnet.Samples.Flocking", "samples\Garnet.Samples.Flocking\Garnet.Samples.Flocking.fsproj", "{16290883-031E-408F-B599-5FEE33355629}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Garnet.Samples.Roguelike", "samples\Garnet.Samples.Roguelike\Garnet.Samples.Roguelike.fsproj", "{C7826D8F-8343-440B-BD6D-74F73D6814EC}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Garnet.Samples.Trixel", "samples\Garnet.Samples.Trixel\Garnet.Samples.Trixel.fsproj", "{58AB7F65-F130-4624-9458-A4BC5BF867DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Garnet.Samples.CSharp", "samples\Garnet.Samples.CSharp\Garnet.Samples.CSharp.csproj", "{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Garnet.Numerics", "samples\Garnet.Numerics\Garnet.Numerics.fsproj", "{66F27C8C-B121-426B-BEA2-4379FC217849}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Garnet.Samples.Assorted", "samples\Garnet.Samples.Assorted\Garnet.Samples.Assorted.fsproj", "{957D6CB2-EBFD-42A8-A201-731CBA232FB3}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Garnet.Processor", "samples\Garnet.Processor\Garnet.Processor.fsproj", "{C88FF8AF-962A-4162-8FB8-0D337DA09138}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|Any CPU = Release|Any CPU
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x64.ActiveCfg = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x64.Build.0 = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x86.ActiveCfg = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x86.Build.0 = Debug|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|Any CPU.Build.0 = Release|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x64.ActiveCfg = Release|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x64.Build.0 = Release|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x86.ActiveCfg = Release|Any CPU
		{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x86.Build.0 = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x64.ActiveCfg = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x64.Build.0 = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x86.ActiveCfg = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x86.Build.0 = Debug|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|Any CPU.Build.0 = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x64.ActiveCfg = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x64.Build.0 = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x86.ActiveCfg = Release|Any CPU
		{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x86.Build.0 = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x64.ActiveCfg = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x64.Build.0 = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x86.ActiveCfg = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x86.Build.0 = Debug|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|Any CPU.Build.0 = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x64.ActiveCfg = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x64.Build.0 = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x86.ActiveCfg = Release|Any CPU
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x86.Build.0 = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|x64.ActiveCfg = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|x64.Build.0 = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|x86.ActiveCfg = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Debug|x86.Build.0 = Debug|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|Any CPU.Build.0 = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|x64.ActiveCfg = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|x64.Build.0 = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|x86.ActiveCfg = Release|Any CPU
		{16290883-031E-408F-B599-5FEE33355629}.Release|x86.Build.0 = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x64.ActiveCfg = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x64.Build.0 = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x86.ActiveCfg = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x86.Build.0 = Debug|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|Any CPU.Build.0 = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x64.ActiveCfg = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x64.Build.0 = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x86.ActiveCfg = Release|Any CPU
		{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x86.Build.0 = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x64.ActiveCfg = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x64.Build.0 = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x86.ActiveCfg = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x86.Build.0 = Debug|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|Any CPU.Build.0 = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x64.ActiveCfg = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x64.Build.0 = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x86.ActiveCfg = Release|Any CPU
		{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x86.Build.0 = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x64.ActiveCfg = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x64.Build.0 = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x86.ActiveCfg = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x86.Build.0 = Debug|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|Any CPU.Build.0 = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x64.ActiveCfg = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x64.Build.0 = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x86.ActiveCfg = Release|Any CPU
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x86.Build.0 = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x64.ActiveCfg = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x64.Build.0 = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x86.ActiveCfg = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x86.Build.0 = Debug|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|Any CPU.Build.0 = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x64.ActiveCfg = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x64.Build.0 = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x86.ActiveCfg = Release|Any CPU
		{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x86.Build.0 = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x64.ActiveCfg = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x64.Build.0 = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x86.ActiveCfg = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x86.Build.0 = Debug|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|Any CPU.Build.0 = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x64.ActiveCfg = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x64.Build.0 = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x86.ActiveCfg = Release|Any CPU
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x86.Build.0 = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x64.ActiveCfg = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x64.Build.0 = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x86.ActiveCfg = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x86.Build.0 = Debug|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|Any CPU.Build.0 = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x64.ActiveCfg = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x64.Build.0 = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x86.ActiveCfg = Release|Any CPU
		{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x86.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(NestedProjects) = preSolution
		{E0F6649F-E652-4B34-B0AA-1E6716AB60FF} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{16290883-031E-408F-B599-5FEE33355629} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{C7826D8F-8343-440B-BD6D-74F73D6814EC} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{58AB7F65-F130-4624-9458-A4BC5BF867DC} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{AF60F947-ED94-4B84-BF8E-D327D2CFCE41} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{66F27C8C-B121-426B-BEA2-4379FC217849} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{957D6CB2-EBFD-42A8-A201-731CBA232FB3} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
		{C88FF8AF-962A-4162-8FB8-0D337DA09138} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {D7FA662D-CCF7-4CEF-82D0-82F96A334562}
	EndGlobalSection
	GlobalSection(Performance) = preSolution
		HasPerformanceSessions = true
	EndGlobalSection
EndGlobal


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Ben Carruthers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Garnet

[![Build status](https://ci.appveyor.com/api/projects/status/g82kak7btxp48rnd?svg=true)](https://ci.appveyor.com/project/bcarruthers/garnet)

[NuGet package](https://www.nuget.org/packages/Garnet/)

Garnet is a lightweight game composition library for F# with entity-component-system (ECS) and actor-like messaging features.

```fsharp
open Garnet.Composition

// events
[<Struct>] type Update = { dt : float32 }

// components
[<Struct>] type Position = { x : float32; y : float32 }
[<Struct>] type Velocity = { vx : float32; vy : float32 }

// create a world
let world = Container()

// register a system that updates position
let system =
    world.On<Update> <| fun e ->
        for r in world.Query<Position, Velocity>() do
            let p = &r.Value1
            let v = r.Value2
            p <- {
                x = p.x + v.vx * e.dt
                y = p.y + v.vy * e.dt
                }

// add an entity to world
let entity = 
    world.Create()
        .With({ x = 10.0f; y = 5.0f })
        .With({ vx = 1.0f; vy = 2.0f })

// run updates and print world state
for i = 1 to 10 do
    world.Run <| { dt = 0.1f }
    printfn "%O\n\n%O\n\n" world entity
```

## Table of contents
* Introduction
    * [Getting started](#gettingstarted)
    * [Background](#background)
    * [Goals](#goals)
* Guide
    * [Containers](#containers)
    * [Entities](#entities)
    * [Components](#components)
    * [Systems](#systems)
    * [Actors](#actors)
    * [Integration](#integration)
* [FAQ](#faq)
* [License](#license)
* [Maintainers](#maintainers)

## Getting started

1. Create either a .NET Framework, Core, or 6.0+ application.
2. Reference the [Garnet NuGet package](https://www.nuget.org/packages/Garnet/). 
3. For sample code, see unit tests or [sample projects](https://github.com/bcarruthers/garnet/tree/master/samples).

## Background

ECS is a common architecture for games, often contrasted with OOP inheritance. It focuses on separation of data and behavior and is typically implemented in a data-oriented way to achieve high performance. It's similar to a database, where component tables are related using a common entity ID, allowing systems to query and iterate over entities with specific combinations of components present. EC (entity-component) is a related approach that attaches behavior to components and avoids systems.

While ECS focuses on managing shared state, the actor model isolates state into separate actors which communicate only through messages. Actors can send and receive messages, change their behavior as a result of messages, and create new actors. This approach offers scaleability and an abstraction layer over message delivery, and games can use it at a high level to model independent processes, worlds, or agents.

## Goals

- **Lightweight**: Garnet is essentially a simplified in-memory database and messaging system suitable for games. No inheritance, attributes, or interface implementations are required in your code. It's more of a library than a framework or engine, and most of your code shouldn't depend on it.

- **Fast**: Garbage collection spikes can cause dropped frames and inconsistent performance, so Garnet minimizes allocations and helps library users do so too. Component storage is data-oriented for fast iteration.

- **Minimal**: The core library focuses on events, scheduling, and storage, and anything game-specific like physics, rendering, or update loops should be implemented separately.

## Containers

ECS containers provide a useful bundle of functionality for working with shared game state, including event handling, component storage, entity ID generation, coroutine scheduling, and resource resolution.

```fsharp
// create a container/world
let c = Container()
```

### Registry

Containers store single instances of types such as component lists, ID pools, settings, and any other arbitrary type. You can access instances by type, with optional lazy resolution. This is the service locator (anti-)pattern.

```fsharp
// option 1: add specific instance
c.SetValue(defaultWorldSettings)
// option 2: register a factory
c.SetFactory(fun () -> defaultWorldSettings)
// resolve type
let settings = c.GetValue<WorldSettings>()
```

This works for value types as well:

```fsharp
c.SetValue { zoomLevel = 0.5f }
let zoom = c.GetValue<Zoom>>()
```

### Object pooling

Avoiding GC generally amounts to use of structs, pooling, and avoiding closures. Almost all objects are either pooled within a container or on the stack, so there's little or no GC impact or allocation once maximum load is reached. If needed, warming up or provisioning buffers ahead of time is possible for avoiding GC entirely during gameplay.

### Commits

Certain operations on containers, such as sending events or adding/removing components, are staged until a commit occurs, allowing any running event handlers to observe the original state. Commits occur automatically after all subscribers have completed handling a list of events, so you typically shouldn't need to explicitly commit.

```fsharp
// create an entity
let e = c.Create().With("test")
// not yet visible
c.Commit()
// now visible
```

## Entities

An entity is any identifiable thing in your game which you can attach components to. At minimum, an entity consists only of an entity ID.

### Entity ID

Entity IDs are 32 bits and stored in a component list. This means they can be accessed and iterated over like any other component type without special handling. IDs use a special Eid type rather than a raw int32, which offers better type safety but means you need a direct dependency on Garnet if you want to define types with an Eid (or you can manage converting to your own ID type if this is an issue). 

```fsharp
let entity = c.Create()
printfn "%A" entity.id
```

### Generations

A portion of an ID is dedicated to its generation number. The purpose of a generation is to avoid reusing IDs while still allowing buffer slots to be reused, keeping components stored as densely as possible.

### Partitioning

Component storage could become inefficient if it grows too sparse (i.e. the average number of occupied elements per segment becomes low). If this is a concern (or you just want to organize your entities), you can optionally use partitions to specify a high bit mask in ID generation. For example, if ship and bullet entities shared the same ID space, they may become mixed over time and the ship components would become sparse. Instead, with separate partitions, both entities would remain dense. Note: this will likely be replaced with groups in the future.

### Generic storage

Storage should work well for both sequential and sparse data and support generic key types. Entity IDs are typically used as keys, but other types like grid location should be possible as well.

### Inspecting

You can print the components of an entity at any time, which is useful in REPL scenarios as an alternative to using a debugger.

```fsharp
printfn "%s" <| c.Get(Eid 64).ToString()
```
```
Entity 0x40: 20 bytes
Eid 0x40
Loc {x = 10;
 y = 2;}
UnitType Archer
UnitSize {unitSize = 5;}
```

## Components

Components are any arbitrary data type associated with an entity. Combined with systems that operate on them, components provide a way to specify behavior or capabilities of entities.

### Data types

Components should ideally be pure data rather than classes with behavior and dependencies. They should typically be structs to avoid jumping around in memory or incurring allocations and garbage collection. Structs should almost always be immutable, but mutable structs (with their gotchas) are possible too.

```fsharp
[<Struct>] type Position = { x : float32; y : float32 }
[<Struct>] type Velocity = { vx : float32; vy : float32 }

// create an entity and add two components to it
let entity = 
    c.Create()
        .With({ x = 10.0f; y = 5.0f })
        .With({ vx = 1.0f; vy = 2.0f })
```

### Storage

Components are stored in 64-element segments with a mask, ordered by ID. This provides CPU-friendly iteration over densely stored data while retaining some benefits of sparse storage. Some ECS implementations provide a variety of specialized data structures, but Garnet attempts a middle ground that works moderately well for both sequential entity IDs and sparse keys such as grid locations.

Only a single component of a type is allowed per entity, but there is no hard limit on the total number of different component types used (i.e. there is no fixed-size mask defining which components an entity has).

### Iteration

You can iterate over entities with specific combinations of components using queries. In this way you could define a system that updates all entities with a position and velocity, and iteration would skip over any entities with only a position and not velocity.

```fsharp
let healthSub =
    c.On<DestroyZeroHealth> <| fun e ->
        for r in c.Query<Eid, Position, Health>() do
            let h = r.Value3
            if h.hp <= 0 then
                let eid = r.Value1
                c.Destroy(eid)
```

For batch operations or to improve performance further, you can iterate over segments:

```fsharp
let healthSub =
    c.On<DestroyZeroHealth> <| fun e ->
        for seg, eids, _, hs in c.QuerySegments<Eid, Position, Health>() do
            for i in seg do
                let h = hs.[i]
                if h.hp <= 0 then
                    let eid = eids.[i]
                    c.Destroy(eid)
```

Note that writes to existing components during iteration occur immediately, unlike adding or removing components.

### Adding

Additions are deferred until a commit occurs, so any code dependent on those operations completing needs to be implemented as a coroutine.

```fsharp
let e = c.Get(Eid 100)
e.Add<Position> { x = 1.0f; y = 2.0f }
// change not yet visible
```

### Removing

Like additions, removals are also deferred until commit. Note that you can repeatedly add and remove components for the same entity ID before a commit if needed.

```fsharp
e.Remove<Velocity>()
// change not yet visible
```

### Updating

Unlike additions and removals, updating/replacing an existing component can be done directly at the risk of affecting subsequent subscribers. This way is convenient if the update operation is commutative or there are no other subscribers writing to the same component type during the same event. You can alternately just use addition if you don't know whether a component is already present.

```fsharp
let e = c.Get(Eid 100)
e.Set<Position> { x = 1.0f; y = 2.0f }
// change immediately visible
```

### Markers

You can define empty types for use as flags or markers, in which case only 64-bit masks need to be stored per segment. Markers are an efficient way to define static groups for querying.

```fsharp
type PowerupMarker = struct end
```

## Systems

Systems are essentially event subscribers with an optional name. System event handlers often iterate over entities, such as updating position based on velocity, but they can do any other kind of processing too.

```fsharp
module MovementSystem =     
    // separate methods as needed
    let registerUpdate (c : Container) =
        c.On<UpdatePositions> <| fun e ->
            printfn "%A" e

    // combine all together
    let register (c : Container) =
        Disposable.Create [
            registerUpdate c
            ]
```

Alternately, you can define systems as extension methods. This way is more OOP-centric and avoids some redundancy in declarations.

```fsharp
[<AutoOpen>]
module MovementSystem =
    type Container with
        member c.AddMovementUpdate() =
            c.On<UpdatePositions> <| fun e ->
                printfn "%A" e
                
        member c.AddMovementSystems() =  
            Disposable.Create [
                c.AddMovementUpdate()
                ]
```

### Execution

When any code creates or modifies entities, sends events, or starts coroutines, it's only staging those things. To actually set all of it into motion, you need to run the container, which would typically happen as part of the game loop. Each time you run the container, it commits all changes, publishes events, and advances coroutines, repeating this process until no work remains to do. This means you should avoid introducing cycles like two systems responding to each other unless they are part of a timed coroutine.

```fsharp
// run the container
c.Process()
```

### Events

Like components, you can use any arbitrary type for an event, but structs are generally preferable to avoid GC. When events are published, subscribers receive batches of events with no guaranteed ordering among the subscribers or event types. Any additional events raised during event handling are run after all the original event handlers complete, thereby avoiding any possibility of reentrancy but complicating synchronous behavior. 

```fsharp
[<Struct>] type UpdateTime = { dt : float32 }

// call sub.Dispose() to unsubscribe
let sub =
    c.On<UpdateTime> <| fun e ->
        // [do update here]
        printfn "%A" e

// send event        
c.Send { dt = 0.1f }
```

Events intentionally decouple publishers and subscribers, and since dispatching events is typically not synchronous within the ECS, it can be difficult to trace the source of events when something goes wrong (no callstack).

### Coroutines

Coroutines allow capturing state and continuing processing for longer than the handling of a single event. They are implemented as sequences and can be used to achieve synchronous behavior despite the asynchronous nature of event handling. This is one of the few parts of the code which incurs allocation.

Coroutines run until they encounter a yield statement, which can tell the coroutine scheduler to either wait for a time duration or to wait until all nested processing has completed. Nested processing refers to any coroutines created as a result of events sent by the current coroutine, allowing a stack-like flow and ordering of events.

```fsharp
let system =
    c.On<Msg> <| fun e ->
        printf "2 "

// start a coroutine
c.Start <| seq {
    printf "1 "
    // send message and defer execution until all messages and
    // coroutines created as a result of this have completed
    c.Send <| Msg()
    yield Wait.All
    printf "3 "
    }

// run until completion
// output: 1 2 3
c.Process()
```

Time-based coroutines are useful for animations or delayed effects. You can use any unit of time as long as it's consistent.

```fsharp
// start a coroutine
c.Start <| seq {
    for i = 1 to 5 do
        printf "[%d] " i
        // yield execution until time units pass
        yield Wait.time 3L
    }

// run update loop
// output: [1] 1 2 3 [2] 4 5 6 [3] 7 8 9
for i = 1 to 9 do
    // increment time units and run pending coroutines
    c.Step 1L
    c.Process()
    printf "%d " i
```

### Multithreading

It's often useful to run physics in parallel with other processing that doesn't depend on its output, but the event system currently has no built-in features to facilitate multiple threads reading or writing. Instead, you can use the actor system for parallel execution at a higher level, or you can implement your own multithreading at the container level.

### Event ordering

For systems that subscribe to the same event and access the same resources or components, you need to consider whether one is dependent on the other and should run first.

One way to guarantee ordering is to define individual sub-events for the systems and publish those events in the desired order as part of a coroutine started from the original event (with waits following each event to ensure all subscribers are run before proceeding).

```fsharp
// events
type Update = struct end
type UpdatePhysicsBodies = struct end
type UpdateHashSpace = struct end

// systems
let updateSystem =
    c.On<Update> <| fun e -> 
        c.Start <| seq {
            // sending and suspending execution to 
            // achieve ordering of sub-updates
            c.Send <| UpdatePhysicsBodies()
            yield Wait.All
            c.Send <| UpdateHashSpace()
            yield Wait.All
        }
let system1 = 
    c.On<UpdatePhysicsBodies> <| fun e ->
        // [update positions]
        printfn "%A" e
let system2 = 
    c.On<UpdateHashSpace> <| fun e ->
        // [update hash space from positions]
        printfn "%A" e
```

### Composing systems

Since systems are just named event subscriptions, you can compose them into larger systems. This allows for bundling related functionality.

```fsharp
module CoreSystems =        
    let register (c : Container) =
        Disposable.Create [
            MovementSystem.register c
            HashSpaceSystem.register c
        ]
```

## Actors

While ECS containers provide a simple and fast means of storing and updating shared memory state using a single thread, actors share no common state and communicate only through messages, making them suitable for parallel processing.

### Definitions

Actors are identified by an actor ID. They are statically defined and created on demand when a message is sent to a nonexistent actor ID. At that point, an actor consisting of a message handler is created based on any definitions registered in the actor system that match the actor ID. It's closer to a mailbox processor than a complete actor model since these actors can't dynamically create arbitrary actors or control actor lifetimes.

```fsharp
// message types
type Ping = struct end
type Pong = struct end

// actor definitions
let a = new ActorSystem()
a.Register(ActorId 1, fun (c : Container) ->
    c.On<Ping> <| fun e -> 
        printf "ping "
        c.Respond(Pong())
    )
a.Register(ActorId 2, fun (c : Container) ->
    c.On<Pong> <| fun e -> 
        printf "pong "
    )
    
// send a message and run until all complete
// output: ping pong
a.Send(ActorId 1, Ping(), sourceId = ActorId 2)
a.ProcessAll()
```

### Actor messages versus container events

Containers already have their own internal event system, but the semantics are a bit different from actors because container events are always stored in separate channels by event type rather than a single serialized channel for all actor message types. The use of separate channels within containers allows for efficient batch processing in cases where event types have no ordering dependencies, but ordering by default is preferable in many other cases involving actors.

### Wrapping containers

It's useful to wrap a container within an actor, where incoming messages to the actor automatically dispatched to the container, and systems within the container have access to an outbox for sending messages to other actors. This approach allows keeping isolated worlds, such as a subset of world state for AI forces or UI state.

### Replay debugging

If you can write logic where your game state is fully determined by the sequence of incoming messages, you can log these messages and replay them to diagnose bugs. This works best if you can isolate the problem to a single actor, such as observing incorrect state or incorrect outgoing messages given a correct input sequence.

### Message ordering

Messages sent from one actor to another are guaranteed to arrive in the order they were sent, but they may be interleaved with messages arriving from other actors. In general, multiple actors and parallelism can introduce complexity similar to the use of microservices, which address scaleability but can introduce race conditions and challenges in synchronization.

### Multithreading

You can designate actors to run on either the main thread (for UI if needed) or a background thread. Actors run when a batch of messages is delivered, resembling task-based parallelism. In addition to running designated actors, the main thread also delivers messages among actors, although this could change in the future if it becomes a bottleneck. Background actors currently run using a fixed pool of worker threads.

## Integration

How does Garnet integrate with frameworks or engines like Unity, MonoGame, or Veldrid? You have a few options depending on how much you want to depend on Garnet, your chosen framework, and your own code. This approach also works for integrating narrower libraries like physics or networking.

See [sample projects](https://github.com/bcarruthers/garnet/tree/master/samples) for integration with Veldrid and OpenAL.

### Abstracting framework calls

When you need to call the framework (e.g. MonoGame) from your code, you can choose to insulate your code from the framework with an abstraction layer. This reduces your dependency on it, but it takes more effort and may result in less power to use framework-specific features and more overhead in marshaling data. If you decide to abstract, you have several options for defining the abstraction layer:

- **Services**: Register an interface for a subsystem and provide an implemention for the specific framework, e.g. *ISpriteRenderer* with *MonoGameSpriteRenderer*. This makes sense if you want synchronous calls or an explicit interface.

- **Events**: Define interface event types and framework-specific systems which subscribe to them, e.g. a sprite rendering system subscribing to *DrawSprite* events. This way is more decoupled, but the interface may not be so clear.

- **Components**: Define interface component types and implement framework-specific systems which iterate over them, e.g. a sprite rendering system which iterates over entities with a *Sprite* component. 

### Sending framework events

For the reverse direction, when you want the framework to call your code, you can simply send interface event types and run the container or actors.

```fsharp
type Game() =
    // ...
    let world = Container()
    // [configure container here]
    override c.Update gt = 
        world.Run { deltaTime = gt.ElapsedGameTime }
    override c.Draw gameTime = 
        world.Run <| Draw()
```

## FAQ

- **Why F#?** F# offers conciseness, functional-first defaults like immutability, an algebraic type system, interactive code editing, and pragmatic support for other paradigms like OOP. Strong type safety makes it more likely that code is correct, which is especially helpful for tweaking game code that changes frequently enough to make unit testing impractical.

- **What about performance?** Functional code often involves allocation, which sometimes conflicts with the goal of consistent performance when garbage collection occurs. A goal of this library is to reduce the effort in writing code that minimizes allocation. But for simple games, this is likely a non-issue and you should start with idiomatic code.

- **Why use ECS over MVU?** You probably shouldn't start with ECS for a simple game, at least not when prototyping, unless you already have a good understanding of where it might be beneficial. MVU avoids a lot of complexity and has stronger type safety and immutability guarantees than ECS, but you may encounter issues if your project has demanding performance requirements or needs more flexibility than it allows. 

## License
This project is licensed under the [MIT license](https://github.com/bcarruthers/garnet/blob/master/LICENSE).

## Maintainer(s)

- [@bcarruthers](https://github.com/bcarruthers)

================================================
FILE: RELEASE_NOTES.md
================================================
## 0.5.0 – 2021-10-16

- Revised registry implementation
- Renamed Get() to GetComponents()
- Renamed GetInstance() registry methods to Get()
- Renamed Entity.Contains() to Has()
- Added more toolkit functionality

## 0.4.0 – 2021-08-28

- Added new querying code
- Renamed various members for consistent PascalCase
- Added toolkit project and samples

## 0.3.0 – 2021-04-24

- Performance: Implemented static type ID lookup for components
- Performance: Changed to generic type for segment key mapping
- Added separate recipient to actor message destinations
- Refactored resource loading
- Added more iteration options
- Fixed iteration bugs

## 0.2.0 – 2019-09-01

- Cleaned up public interfaces
- Removed experimental serialization code
- Decoupled entity from container
- Cleaned up event publishers
- Rewrote actor system
- Rewrote entity ID pooling and destruction
- Switched to using buffers instead of lists

## 0.1.0 – 2019-07-09

- Initial version


================================================
FILE: appveyor.yml
================================================
version: 1.0.{build}
image: Visual Studio 2022
build_script:
- cmd: build.cmd
artifacts:
- path: publish\*.nupkg
  name: packages
deploy:
- provider: GitHub
  auth_token:
    secure: qG7eOszX+IfotPE1mnKIg13cJTR1/t4aiI5coav1zcp71CZBXw4BZ6PfAU7jWZ4D
  artifact: packages
  draft: true

================================================
FILE: build.cmd
================================================
dotnet restore
dotnet build --no-restore
dotnet test --no-build --verbosity normal
dotnet pack -c Release -o publish src\Garnet\Garnet.fsproj
dotnet pack -c Release -o publish samples\Garnet.Numerics\Garnet.Numerics.fsproj
dotnet pack -c Release -o publish samples\Garnet.Toolkit\Garnet.Toolkit.fsproj
dotnet pack -c Release -o publish samples\Garnet.Processor\Garnet.Processor.fsproj

================================================
FILE: samples/Garnet.Numerics/Garnet.Numerics.fsproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <PropertyGroup>
    <Description>Numeric primitives and operations suitable for games. Supplements System.Numerics.</Description>
    <PackageTags>math vectors bounds game</PackageTags>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Numerics.fs" />
    <Compile Include="Vectors.fs" />
    <Compile Include="Ranges.fs" />
    <Compile Include="Random.fs" />
    <Compile Include="Hashing.fs" />
    <Compile Include="Noise.fs" />
  </ItemGroup>
</Project>

================================================
FILE: samples/Garnet.Numerics/Hashing.fs
================================================
namespace Garnet.Numerics

open System

module Fnv1a =
    [<Literal>]
    let Prime = 0x1000193u

    [<Literal>]
    let Seed = 0x811c9dc5u

type Fnv1a() =
    static member inline Combine(hash, value) =
        (hash ^^^ value) * Fnv1a.Prime

    static member inline Hash(x1 : uint32) =
        let h = Fnv1a.Seed
        let h = Fnv1a.Combine(h, x1)
        h

    static member inline Hash(x1 : uint32, x2 : uint32) =
        let h = Fnv1a.Seed
        let h = Fnv1a.Combine(h, x1)
        let h = Fnv1a.Combine(h, x2)
        h
        
    static member inline Hash(x1 : uint32, x2 : uint32, x3 : uint32) =
        let h = Fnv1a.Seed
        let h = Fnv1a.Combine(h, x1)
        let h = Fnv1a.Combine(h, x2)
        let h = Fnv1a.Combine(h, x3)
        h

    static member inline Hash(key : string) =
        let mutable hash = Fnv1a.Seed
        for i = 0 to key.Length - 1 do
            let value = uint32 key.[i]
            hash <- Fnv1a.Combine(hash, value)
        hash
        
module Fnv1a64 =
    [<Literal>]
    let Prime = 0x100000001b3UL

    [<Literal>]
    let Seed = 0xcbf29ce484222325UL

type Fnv1a64() =
    static member inline Combine(hash, value) =
        (hash ^^^ value) * Fnv1a64.Prime

    static member inline Hash(value : uint64) =
        let h = Fnv1a64.Seed
        let h = Fnv1a64.Combine(h, value)
        h

    static member inline Hash(x1 : uint64, x2 : uint64) =
        let h = Fnv1a64.Seed
        let h = Fnv1a64.Combine(h, x1)
        let h = Fnv1a64.Combine(h, x2)
        h
        
    static member inline Hash(x1 : uint64, x2 : uint64, x3 : uint64) =
        let h = Fnv1a64.Seed
        let h = Fnv1a64.Combine(h, x1)
        let h = Fnv1a64.Combine(h, x2)
        let h = Fnv1a64.Combine(h, x3)
        h

    static member inline Hash(key : string) =
        let mutable hash = Fnv1a64.Seed
        for i = 0 to key.Length - 1 do
            let value = uint64 key.[i]
            hash <- Fnv1a64.Combine(hash, value)
        hash

module XXHash =
    [<Literal>]
    let Prime32_1 = 2654435761u

    [<Literal>]
    let Prime32_2 = 2246822519u

    [<Literal>]
    let Prime32_3 = 3266489917u

    [<Literal>]
    let Prime32_4 = 668265263u

    [<Literal>]
    let Prime32_5 = 374761393u

type XXHash() =    
    static member inline RotateLeft(value : uint32, count) =
        (value <<< count) ||| (value >>> (32 - count))

    static member inline Finalize(hash : uint32) =
        let h = hash ^^^ (hash >>> 15)
        let h = h * XXHash.Prime32_2
        let h = h ^^^ (h >>> 13)
        let h = h * XXHash.Prime32_3
        let h = h ^^^ (h >>> 16)
        h

    static member inline Combine(hash : uint32, value : uint32) = 
        let h = hash + value * XXHash.Prime32_3
        let h = XXHash.RotateLeft(h, 17) * XXHash.Prime32_4
        h

    static member inline Initialize(seed : uint32) =
        seed + XXHash.Prime32_5

    static member inline Initialize(seed : uint32, size : uint32) =
        let h = seed + XXHash.Prime32_5
        let h = h + size
        h

    static member inline Hash(seed : uint32, value : uint32) =        
        let h = XXHash.Initialize(seed, 4u)
        let h = XXHash.Combine(h, value)
        XXHash.Finalize(h)

    static member inline Hash(seed : uint32, x1 : uint32, x2 : uint32) =
        let h = XXHash.Initialize(seed, 8u)
        let h = XXHash.Combine(h, x1)
        let h = XXHash.Combine(h, x2)
        XXHash.Finalize(h)
        
    static member inline Hash(seed : uint32, x1 : uint32, x2 : uint32, x3 : uint32) =
        let h = XXHash.Initialize(seed, 12u)
        let h = XXHash.Combine(h, x1)
        let h = XXHash.Combine(h, x2)
        let h = XXHash.Combine(h, x3)
        XXHash.Finalize(h)

    static member inline Hash(seed : uint32, span : ReadOnlySpan<int>) =
        let mutable h = XXHash.Initialize(seed, uint32 span.Length)
        for i = 0 to span.Length - 1 do
            h <- XXHash.Combine(h, uint32 span.[i])
        XXHash.Finalize(h)

    static member inline Hash(seed : uint32, span : ReadOnlySpan<uint64>) =
        let mutable h = XXHash.Initialize(seed, uint32 (span.Length * 2))
        for i = 0 to span.Length - 1 do
            h <- XXHash.Combine(h, uint32 (span.[i] &&& 0xffffffffUL))
            h <- XXHash.Combine(h, uint32 (span.[i] >>> 32))
        XXHash.Finalize(h)

    static member inline FinalizeToRange(hash, index, min, max) =
        let h = XXHash.Combine(hash, uint32 index)
        let h = XXHash.Finalize(h)
        int (h % uint32 (max - min)) + min


================================================
FILE: samples/Garnet.Numerics/Noise.fs
================================================
namespace Garnet.Numerics

// Adapted for F# from Stefan Gustavson code
// Simplex noise demystified:
// https://weber.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf

// Original license:
// sdnoise1234, Simplex noise with true analytic
// derivative in 1D to 4D.
//
// Copyright © 2003-2012, Stefan Gustavson
//
// Contact: stefan.gustavson@gmail.com
//
// This library is public domain software, released by the author
// into the public domain in February 2011. You may do anything
// you like with it. You may even remove all attributions,
// but of course I'd appreciate it if you kept my name somewhere.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// This is an implementation of Perlin "simplex noise" over one
// dimension (x), two dimensions (x,y), three dimensions (x,y,z)
// and four dimensions (x,y,z,w). The analytic derivative is
// returned, to make it possible to do lots of fun stuff like
// flow animations, curl noise, analytic antialiasing and such.
//
// Visually, this noise is exactly the same as the plain version of
// simplex noise provided in the file "snoise1234.c". It just returns
// all partial derivatives in addition to the scalar noise value.

open System.Numerics

module private SimplexNoise =
    let private grad3 =
        array2D
            [[1.0f;1.0f;0.0f];[-1.0f;1.0f;0.0f];[1.0f;-1.0f;0.0f];[-1.0f;-1.0f;0.0f];
            [1.0f;0.0f;1.0f];[-1.0f;0.0f;1.0f];[1.0f;0.0f;-1.0f];[-1.0f;0.0f;-1.0f];
            [0.0f;1.0f;1.0f];[0.0f;-1.0f;1.0f];[0.0f;1.0f;-1.0f];[0.0f;-1.0f;-1.0f]];

    let private grad4 =
        array2D
            [[0.0f;1.0f;1.0f;1.0f]; [0.0f;1.0f;1.0f;-1.0f]; [0.0f;1.0f;-1.0f;1.0f]; [0.0f;1.0f;-1.0f;-1.0f];
            [0.0f;-1.0f;1.0f;1.0f]; [0.0f;-1.0f;1.0f;-1.0f]; [0.0f;-1.0f;-1.0f;1.0f]; [0.0f;-1.0f;-1.0f;-1.0f];
            [1.0f;0.0f;1.0f;1.0f]; [1.0f;0.0f;1.0f;-1.0f]; [1.0f;0.0f;-1.0f;1.0f]; [1.0f;0.0f;-1.0f;-1.0f];
            [-1.0f;0.0f;1.0f;1.0f]; [-1.0f;0.0f;1.0f;-1.0f]; [-1.0f;0.0f;-1.0f;1.0f]; [-1.0f;0.0f;-1.0f;-1.0f];
            [1.0f;1.0f;0.0f;1.0f]; [1.0f;1.0f;0.0f;-1.0f]; [1.0f;-1.0f;0.0f;1.0f]; [1.0f;-1.0f;0.0f;-1.0f];
            [-1.0f;1.0f;0.0f;1.0f]; [-1.0f;1.0f;0.0f;-1.0f]; [-1.0f;-1.0f;0.0f;1.0f]; [-1.0f;-1.0f;0.0f;-1.0f];
            [1.0f;1.0f;1.0f;0.0f]; [1.0f;1.0f;-1.0f;0.0f]; [1.0f;-1.0f;1.0f;0.0f]; [1.0f;-1.0f;-1.0f;0.0f];
            [-1.0f;1.0f;1.0f;0.0f]; [-1.0f;1.0f;-1.0f;0.0f]; [-1.0f;-1.0f;1.0f;0.0f]; [-1.0f;-1.0f;-1.0f;0.0f]];

    // A lookup table to traverse the simplex around a given point in 4D.
    // Details can be found where this table is used; in the 4D noise method.
    let private simplex =
        array2D
            [[0;1;2;3];[0;1;3;2];[0;0;0;0];[0;2;3;1];[0;0;0;0];[0;0;0;0];[0;0;0;0];[1;2;3;0];
            [0;2;1;3];[0;0;0;0];[0;3;1;2];[0;3;2;1];[0;0;0;0];[0;0;0;0];[0;0;0;0];[1;3;2;0];
            [0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];
            [1;2;0;3];[0;0;0;0];[1;3;0;2];[0;0;0;0];[0;0;0;0];[0;0;0;0];[2;3;0;1];[2;3;1;0];
            [1;0;2;3];[1;0;3;2];[0;0;0;0];[0;0;0;0];[0;0;0;0];[2;0;3;1];[0;0;0;0];[2;1;3;0];
            [0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];
            [2;0;1;3];[0;0;0;0];[0;0;0;0];[0;0;0;0];[3;0;1;2];[3;0;2;1];[0;0;0;0];[3;1;2;0];
            [2;1;0;3];[0;0;0;0];[0;0;0;0];[0;0;0;0];[3;1;0;2];[0;0;0;0];[3;2;0;1];[3;2;1;0]];

    // Permutation table. This is just a random jumble of all numbers 0-255;
    // repeated twice to avoid wrapping the index at 255 for each lookup.
    let private perm =
        [| 151;160;137;91;90;15;
        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;
        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;
        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;
        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;
        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;
        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;
        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;
        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;
        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;
        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;
        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;
        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180;
        151;160;137;91;90;15;
        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;
        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;
        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;
        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;
        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;
        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;
        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;
        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;
        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;
        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;
        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;
        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180 |]

    let private Sqrt3 = 1.7320508075688772935274463415059f
    let private F2 = 0.5f * (Sqrt3 - 1.0f);
    let private G2 = (3.0f - Sqrt3) / 6.0f;

    let inline private dot2 (g : float32[,]) gi x y = g.[gi, 0] * x + g.[gi, 1] * y

    let sample2 x y =
        //float n0, n1, n2; // Noise contributions from the three corners
        // Skew the input space to determine which simplex cell we're in
        let s = (x + y) * F2; // Hairy factor for 2D

        //int i = Floor(x + s);
        //int j = Floor(y + s);
        let realI = x + s
        let realJ = y + s
        let i = int(if realI > 0.0f then realI else realI - 1.0f)
        let j = int(if realJ > 0.0f then realJ else realJ - 1.0f)
    
        let t = float32(i + j) * G2;
        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y) space
        let Y0 = float32(j) - t;
        let x0 = x - X0; // The x,y distances from the cell origin
        let y0 = y - Y0;
    
        // For the 2D case, the simplex shape is an equilateral triangle.
        // Determine which simplex we are in.
        //let i1, j1 =                // Offsets for second (middle) corner of simplex in (i,j) coords
        //    if (x0 > y0) then 1, 0  // lower triangle, XY order: (0,0)->(1,0)->(1,1)
        //    else 0, 1               // upper triangle, YX order: (0,0)->(0,1)->(1,1)
        let i1 = if x0 > y0 then 1 else 0
        let j1 = 1 - i1

        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
        // c = (3-sqrt(3))/6
        let x1 = x0 - float32(i1) + G2; // Offsets for middle corner in (x,y) unskewed coords
        let y1 = y0 - float32(j1) + G2;
        let x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
        let y2 = y0 - 1.0f + 2.0f * G2;
        // Work out the hashed gradient indices of the three simplex corners
        let ii = i &&& 255;
        let jj = j &&& 255;

        //int gi0 = perm.[ii + perm.[jj]] % 12;
        //int gi1 = perm.[ii + i1 + perm.[jj + j1]] % 12;
        //int gi2 = perm.[ii + 1 + perm.[jj + 1]] % 12;

        let gi0 = perm.[ii + perm.[jj]] % 12;
        let gi1 = perm.[ii + i1 + perm.[jj + j1]] % 12;
        let gi2 = perm.[ii + 1 + perm.[jj + 1]] % 12;

        //(n * (n * n * 15731 + 789221) + 1376312589)

        // Calculate the contribution from the three corners
        let t0 = 0.5f - x0 * x0 - y0 * y0
        let n0 =
            if (t0 < 0.0f) then 0.0f
            else
                let t02 = t0 * t0
                t02 * t02 * (dot2 grad3 gi0 x0 y0) // (x,y) of grad3 used for 2D gradient
    
        let t1 = 0.5f - x1 * x1 - y1 * y1
        let n1 =
            if (t1 < 0.0f) then 0.0f
            else
                let t12 = t1 * t1
                t12 * t12 * (dot2 grad3 gi1 x1 y1)

        let t2 = 0.5f - x2 * x2 - y2 * y2
        let n2 =
            if (t2 < 0.0f) then 0.0f
            else
                let t22 = t2 * t2
                t22 * t22 * (dot2 grad3 gi2 x2 y2)

        // Add contributions from each corner to get the final noise value.
        // The result is scaled to return values in the interval [-1,1].
        70.0f * (n0 + n1 + n2)

    let private F3 = 1.0f / 3.0f
    let private G3 = 1.0f / 6.0f; // Very nice and simple unskew factor, too

    let inline private dot3 (g : float32[,]) gi x y z = g.[gi, 0] * x + g.[gi, 1] * y + g.[gi, 2] * z

    let inline private floor (x : float32) = if x > 0.0f then int(x) else int(x) - 1

    let sample3 x y z =
        // Skew the input space to determine which simplex cell we're in
        let s = (x + y + z) * F3 // Very nice and simple skew factor for 3D
        let i = floor(x + s)
        let j = floor(y + s)
        let k = floor(z + s)
        let t = float32(i + j + k) * G3
        let X0 = float32(i) - t // Unskew the cell origin back to (x,y,z) space
        let Y0 = float32(j) - t
        let Z0 = float32(k) - t
        let x0 = x - X0 // The x,y,z distances from the cell origin
        let y0 = y - Y0
        let z0 = z - Z0
        // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
        // Determine which simplex we are in.
        // Offsets for second corner of simplex in (i,j,k) coords
        // Offsets for third corner of simplex in (i,j,k) coords
        let struct(i1, j1, k1, i2, j2, k2) =
            if (x0 >= y0) then
                if (y0 >= z0) then 1, 0, 0, 1, 1, 0 // X Y Z order
                else if (x0 >= z0) then 1, 0, 0, 1, 0, 1 // X Z Y order
                else 0, 0, 1, 1, 0, 1 // Z X Y order
            else 
                if (y0 < z0) then 0, 0, 1, 0, 1, 1 // Z Y X order
                else if (x0 < z0) then 0, 1, 0, 0, 1, 1 // Y Z X order
                else 0, 1, 0, 1, 1, 0 // Y X Z order

        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
        // c = 1/6.
        let x1 = x0 - float32(i1) + G3 // Offsets for second corner in (x,y,z) coords
        let y1 = y0 - float32(j1) + G3
        let z1 = z0 - float32(k1) + G3
        let x2 = x0 - float32(i2) + 2.0f * G3 // Offsets for third corner in (x,y,z) coords
        let y2 = y0 - float32(j2) + 2.0f * G3
        let z2 = z0 - float32(k2) + 2.0f * G3
        let x3 = x0 - 1.0f + 3.0f * G3 // Offsets for last corner in (x,y,z) coords
        let y3 = y0 - 1.0f + 3.0f * G3
        let z3 = z0 - 1.0f + 3.0f * G3
        // Work out the hashed gradient indices of the four simplex corners
        let ii = i &&& 255
        let jj = j &&& 255
        let kk = k &&& 255
        let gi0 = perm.[ii + perm.[jj + perm.[kk]]] % 12
        let gi1 = perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1]]] % 12
        let gi2 = perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2]]] % 12
        let gi3 = perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1]]] % 12
        // Calculate the contribution from the four corners
        let t0 = 0.5f - x0 * x0 - y0 * y0 - z0 * z0
        let n0 =
            if (t0 < 0.0f) then 0.0f
            else
                let t02 = t0 * t0
                t02 * t02 * (dot3 grad3 gi0 x0 y0 z0)

        let t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1
        let n1 =
            if (t1 < 0.0f) then 0.0f
            else
                let t12 = t1 * t1
                t12 * t12 * (dot3 grad3 gi1 x1 y1 z1)

        let t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2
        let n2 =
            if (t2 < 0.0f) then 0.0f
            else
                let t22 = t2 * t2
                t22 * t22 * (dot3 grad3 gi2 x2 y2 z2)

        let t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3
        let n3 =
            if (t3 < 0.0f) then 0.0f
            else
                let t32 = t3 * t3
                t32 * t32 * (dot3 grad3 gi3 x3 y3 z3)

        // Add contributions from each corner to get the final noise value.
        // The result is scaled to stay just inside [-1,1]
        32.0f * (n0 + n1 + n2 + n3)

    let private F4 = 0.309016994f // F4 = (Math.sqrt(5.0)-1.0)/4.0
    let private G4 = 0.138196601f // G4 = (5.0-Math.sqrt(5.0))/20.0

    let private dot4 (g : float32[,]) gi x y z w = g.[gi, 0] * x + g.[gi, 1] * y + g.[gi, 2] * z + g.[gi, 3] * w

    let sample4 x y z w =
        // The skewing and unskewing factors are hairy again for the 4D case
        //double n0, n1, n2, n3, n4; // Noise contributions from the five corners
        // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
        let s = (x + y + z + w) * F4; // Factor for 4D skewing
        let i = floor(x + s);
        let j = floor(y + s);
        let k = floor(z + s);
        let l = floor(w + s);
        let t = float32(i + j + k + l) * G4; // Factor for 4D unskewing
        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z,w) space
        let Y0 = float32(j) - t;
        let Z0 = float32(k) - t;
        let W0 = float32(l) - t;
        let x0 = x - X0; // The x,y,z,w distances from the cell origin
        let y0 = y - Y0;
        let z0 = z - Z0;
        let w0 = w - W0;
        // For the 4D case, the simplex is a 4D shape I won't even try to describe.
        // To find out which of the 24 possible simplices we're in, we need to
        // determine the magnitude ordering of x0, y0, z0 and w0.
        // The method below is a good way of finding the ordering of x,y,z,w and
        // then find the correct traversal order for the simplex we’re in.
        // First, six pair-wise comparisons are performed between each possible pair
        // of the four coordinates, and the results are used to add up binary bits
        // for an integer index.
        let c1 = if (x0 > y0) then 32 else 0
        let c2 = if (x0 > z0) then 16 else 0
        let c3 = if (y0 > z0) then 8 else 0
        let c4 = if (x0 > w0) then 4 else 0
        let c5 = if (y0 > w0) then 2 else 0
        let c6 = if (z0 > w0) then 1 else 0
        let c = c1 + c2 + c3 + c4 + c5 + c6;
    //    int i1, j1, k1, l1; // The integer offsets for the second simplex corner
    //    int i2, j2, k2, l2; // The integer offsets for the third simplex corner
    //    int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
        // simplex.[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
        // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
        // impossible. Only the 24 indices which have non-zero entries make any sense.
        // We use a thresholding to set the coordinates in turn from the largest magnitude.
        // The number 3 in the "simplex" array is at the position of the largest coordinate.
        let i1 = if simplex.[c, 0] >= 3 then 1 else 0;
        let j1 = if simplex.[c, 1] >= 3 then 1 else 0;
        let k1 = if simplex.[c, 2] >= 3 then 1 else 0;
        let l1 = if simplex.[c, 3] >= 3 then 1 else 0;

        // The number 2 in the "simplex" array is at the second largest coordinate.
        let i2 = if simplex.[c, 0] >= 2 then 1 else 0;
        let j2 = if simplex.[c, 1] >= 2 then 1 else 0;
        let k2 = if simplex.[c, 2] >= 2 then 1 else 0;
        let l2 = if simplex.[c, 3] >= 2 then 1 else 0;

        // The number 1 in the "simplex" array is at the second smallest coordinate.
        let i3 = if simplex.[c, 0] >= 1 then 1 else 0;
        let j3 = if simplex.[c, 1] >= 1 then 1 else 0;
        let k3 = if simplex.[c, 2] >= 1 then 1 else 0;
        let l3 = if simplex.[c, 3] >= 1 then 1 else 0;

        // The fifth corner has all coordinate offsets = 1, so no need to look that up.
        let x1 = x0 - float32(i1) + G4; // Offsets for second corner in (x,y,z,w) coords
        let y1 = y0 - float32(j1) + G4;
        let z1 = z0 - float32(k1) + G4;
        let w1 = w0 - float32(l1) + G4;
        let x2 = x0 - float32(i2) + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords
        let y2 = y0 - float32(j2) + 2.0f * G4;
        let z2 = z0 - float32(k2) + 2.0f * G4;
        let w2 = w0 - float32(l2) + 2.0f * G4;
        let x3 = x0 - float32(i3) + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords
        let y3 = y0 - float32(j3) + 3.0f * G4;
        let z3 = z0 - float32(k3) + 3.0f * G4;
        let w3 = w0 - float32(l3) + 3.0f * G4;
        let x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords
        let y4 = y0 - 1.0f + 4.0f * G4;
        let z4 = z0 - 1.0f + 4.0f * G4;
        let w4 = w0 - 1.0f + 4.0f * G4;

        // Work out the hashed gradient indices of the five simplex corners
        let ii = i &&& 255;
        let jj = j &&& 255;
        let kk = k &&& 255;
        let ll = l &&& 255;
        let gi0 = perm.[ii + perm.[jj + perm.[kk + perm.[ll]]]] % 32;
        let gi1 = perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1 + perm.[ll + l1]]]] % 32;
        let gi2 = perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2 + perm.[ll + l2]]]] % 32;
        let gi3 = perm.[ii + i3 + perm.[jj + j3 + perm.[kk + k3 + perm.[ll + l3]]]] % 32;
        let gi4 = perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1 + perm.[ll + 1]]]] % 32;

        // Calculate the contribution from the five corners
        let t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
        let n0 =
            if (t0 < 0.0f) then 0.0f
            else
                let t02 = t0 * t0;
                t02 * t02 * (dot4 grad4 gi0 x0 y0 z0 w0)
    
        let t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
        let n1 =
            if (t1 < 0.0f) then 0.0f
            else
                let t12 = t1 * t1;
                t12 * t12 * (dot4 grad4 gi1 x1 y1 z1 w1)

        let t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
        let n2 =
            if (t2 < 0.0f) then 0.0f
            else
                let t22 = t2 * t2;
                t22 * t22 * (dot4 grad4 gi2 x2 y2 z2 w2);

        let t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
        let n3 =
            if (t3 < 0.0f) then 0.0f
            else
                let t32 = t3 * t3;
                t32 * t32 * (dot4 grad4 gi3 x3 y3 z3 w3);

        let t4 = 0.6f - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
        let n4 =
            if (t4 < 0.0f) then 0.0f
            else
                let t42 = t4 * t4;
                t42 * t42 * (dot4 grad4 gi4 x4 y4 z4 w4);

        // Sum up and scale the result to cover the range [-1,1]
        27.0f * (n0 + n1 + n2 + n3 + n4);

// This code overlaps a lot with above, but also includes gradient calc
module private GradientNoise =
    let private floor (x : float32) = if x > 0.0f then int(x) else int(x) - 1

    // Permutation table. This is just a random jumble of all numbers 0-255,
    // repeated twice to avoid wrapping the index at 255 for each lookup.
    let private perm = [| 151;160;137;91;90;15;
        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;
        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;
        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;
        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;
        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;
        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;
        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;
        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;
        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;
        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;
        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;
        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180;
        151;160;137;91;90;15;
        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;
        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;
        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;
        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;
        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;
        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;
        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;
        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;
        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;
        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;
        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;
        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180 
    |]
    
    // Gradient tables. These could be programmed the Ken Perlin way with
    // some clever bit-twiddling, but this is more clear, and not really slower.
    let private grad2lut =
        [ -1.0f; -1.0f;   1.0f; 0.0f;  -1.0f; 0.0f;  1.0f; 1.0f;
            -1.0f; 1.0f;   0.0f; -1.0f;   0.0f; 1.0f;  1.0f; -1.0f ]

    // Gradient directions for 3D.
    // These vectors are based on the midpoints of the 12 edges of a cube.
    // A larger array of random unit length vectors would also do the job,
    // but these 12 (including 4 repeats to make the array length a power
    // of two) work better. They are not random, they are carefully chosen
    // to represent a small, isotropic set of directions.
    let private grad3lut =
        array2D [
            [ 1.0f; 0.0f; 1.0f ]; [ 0.0f; 1.0f; 1.0f ]; // 12 cube edges
            [ -1.0f; 0.0f; 1.0f ]; [ 0.0f; -1.0f; 1.0f ];
            [ 1.0f; 0.0f; -1.0f ]; [ 0.0f; 1.0f; -1.0f ];
            [ -1.0f; 0.0f; -1.0f ]; [ 0.0f; -1.0f; -1.0f ];
            [ 1.0f; -1.0f; 0.0f ]; [ 1.0f; 1.0f; 0.0f ];
            [ -1.0f; 1.0f; 0.0f ]; [ -1.0f; -1.0f; 0.0f ];
            [ 1.0f; 0.0f; 1.0f ]; [ -1.0f; 0.0f; 1.0f ]; // 4 repeats to make 16
            [ 0.0f; 1.0f; -1.0f ]; [ 0.0f; -1.0f; -1.0f ] ]

    let private grad4lut =
        array2D [
            [ 0.0f; 1.0f; 1.0f; 1.0f ]; [ 0.0f; 1.0f; 1.0f; -1.0f ]; [ 0.0f; 1.0f; -1.0f; 1.0f ]; [ 0.0f; 1.0f; -1.0f; -1.0f ]; // 32 tesseract edges
            [ 0.0f; -1.0f; 1.0f; 1.0f ]; [ 0.0f; -1.0f; 1.0f; -1.0f ]; [ 0.0f; -1.0f; -1.0f; 1.0f ]; [ 0.0f; -1.0f; -1.0f; -1.0f ];
            [ 1.0f; 0.0f; 1.0f; 1.0f ]; [ 1.0f; 0.0f; 1.0f; -1.0f ]; [ 1.0f; 0.0f; -1.0f; 1.0f ]; [ 1.0f; 0.0f; -1.0f; -1.0f ];
            [ -1.0f; 0.0f; 1.0f; 1.0f ]; [ -1.0f; 0.0f; 1.0f; -1.0f ]; [ -1.0f; 0.0f; -1.0f; 1.0f ]; [ -1.0f; 0.0f; -1.0f; -1.0f ];
            [ 1.0f; 1.0f; 0.0f; 1.0f ]; [ 1.0f; 1.0f; 0.0f; -1.0f ]; [ 1.0f; -1.0f; 0.0f; 1.0f ]; [ 1.0f; -1.0f; 0.0f; -1.0f ];
            [ -1.0f; 1.0f; 0.0f; 1.0f ]; [ -1.0f; 1.0f; 0.0f; -1.0f ]; [ -1.0f; -1.0f; 0.0f; 1.0f ]; [ -1.0f; -1.0f; 0.0f; -1.0f ];
            [ 1.0f; 1.0f; 1.0f; 0.0f ]; [ 1.0f; 1.0f; -1.0f; 0.0f ]; [ 1.0f; -1.0f; 1.0f; 0.0f ]; [ 1.0f; -1.0f; -1.0f; 0.0f ];
            [ -1.0f; 1.0f; 1.0f; 0.0f ]; [ -1.0f; 1.0f; -1.0f; 0.0f ]; [ -1.0f; -1.0f; 1.0f; 0.0f ]; [ -1.0f; -1.0f; -1.0f; 0.0f ] ]

    // A lookup table to traverse the simplex around a given point in 4D.
    // Details can be found where this table is used; in the 4D noise method.
    let private simplex =
        array2D [
            [0;1;2;3];[0;1;3;2];[0;0;0;0];[0;2;3;1];[0;0;0;0];[0;0;0;0];[0;0;0;0];[1;2;3;0];
            [0;2;1;3];[0;0;0;0];[0;3;1;2];[0;3;2;1];[0;0;0;0];[0;0;0;0];[0;0;0;0];[1;3;2;0];
            [0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];
            [1;2;0;3];[0;0;0;0];[1;3;0;2];[0;0;0;0];[0;0;0;0];[0;0;0;0];[2;3;0;1];[2;3;1;0];
            [1;0;2;3];[1;0;3;2];[0;0;0;0];[0;0;0;0];[0;0;0;0];[2;0;3;1];[0;0;0;0];[2;1;3;0];
            [0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];[0;0;0;0];
            [2;0;1;3];[0;0;0;0];[0;0;0;0];[0;0;0;0];[3;0;1;2];[3;0;2;1];[0;0;0;0];[3;1;2;0];
            [2;1;0;3];[0;0;0;0];[0;0;0;0];[0;0;0;0];[3;1;0;2];[0;0;0;0];[3;2;0;1];[3;2;1;0]];

    // Helper functions to compute gradients in 1D to 4D and gradients-dot-residualvectors in 2D to 4D.

    let private grad1 (hash: int) =
        let h = hash &&& 15
        let gx = 1.0f + float32(h &&& 7)   // Gradient value is one of 1.0, 2.0, ..., 8.0
        if ((h &&& 8) <> 0) then -gx else gx   // Make half of the gradients negative

    //    let grad2 (hash : int) =
    //        let h = hash &&& 7
    //        grad2lut.[h, 0], grad2lut.[h, 1]

    let private grad3 (hash : int) =
        let h = hash &&& 15
        grad3lut.[h, 0], grad3lut.[h, 1], grad3lut.[h, 2]

    let private grad4 (hash : int) =
        let h = hash &&& 31;
        grad4lut.[h, 0], grad4lut.[h, 1], grad4lut.[h, 2], grad4lut.[h, 3]

    // 1D simplex noise with derivative. If the last argument is not null, the analytic derivative is also calculated.
    let sample1 calcGrad (x : float32) =
        let i0 = floor(x)
        let i1 = i0 + 1
        let x0 = x - float32(i0)
        let x1 = x0 - 1.0f

        let x20 = x0 * x0
        let t0 = 1.0f - x20
        //  if(t0 < 0.0f) t0 = 0.0f; // Never happens for 1D: x0<=1 always
        let t20 = t0 * t0
        let t40 = t20 * t20
        let gx0 = grad1(perm.[i0 &&& 0xff])
        let n0 = t40 * gx0 * x0

        let x21 = x1 * x1
        let t1 = 1.0f - x21
        //  if(t1 < 0.0f) t1 = 0.0f; // Never happens for 1D: |x1|<=1 always
        let t21 = t1 * t1
        let t41 = t21 * t21
        let gx1 = grad1(perm.[i1 &&& 0xff])
        let n1 = t41 * gx1 * x1

        // Compute derivative, if requested by supplying non-null pointer
        // for the last argument
        // Compute derivative according to:
        //  *dnoise_dx = -8.0f * t20 * t0 * x0 * (gx0 * x0) + t40 * gx0;
        //  *dnoise_dx += -8.0f * t21 * t1 * x1 * (gx1 * x1) + t41 * gx1;

        // The maximum value of this noise is 8*(3/4)^4 = 2.53125
        // A factor of 0.395 would scale to fit exactly within [-1,1], but
        // to better match classic Perlin noise, we scale it down some more.
        let value = 0.25f * (n0 + n1)
        
        if calcGrad then
            let dx0 = t20 * t0 * gx0 * x20
            let dx1 = t21 * t1 * gx1 * x21
            let dx = (dx0 + dx1) * -8.0f + t40 * gx0 + t41 * gx1
            struct(value, dx * 0.25f) // Scale derivative to match the noise scaling
        else
            struct(value, 0.0f)

    // Skewing factors for 2D simplex grid:
    // F2 = 0.5*(sqrt(3.0)-1.0)
    // G2 = (3.0-Math.sqrt(3.0))/6.0
    let private F2 = 0.366025403f
    let private G2 = 0.211324865f
    let private Scaling2 = 40.0f

    let sample2 calcGrad (x: float32) (y: float32) =
        // Skew the input space to determine which simplex cell we're in
        let s = (x + y) * F2; // Hairy factor for 2D
        let xs = x + s;
        let ys = y + s;
        let i = floor(xs);
        let j = floor(ys);

        let t = float32(i + j) * G2;
        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y) space */
        let Y0 = float32(j) - t;
        let x0 = x - X0; // The x,y distances from the cell origin */
        let y0 = y - Y0;

        // For the 2D case, the simplex shape is an equilateral triangle.
        // Determine which simplex we are in.
        let i1, j1 =                    // Offsets for second (middle) corner of simplex in (i,j) coords */
            if x0 > y0 then 1, 0  // lower triangle, XY order: (0,0)->(1,0)->(1,1) */
            else 0, 1             // upper triangle, YX order: (0,0)->(0,1)->(1,1) */

        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
        // c = (3-sqrt(3))/6
        let x1 = x0 - float32(i1) + G2; // Offsets for middle corner in (x,y) unskewed coords */
        let y1 = y0 - float32(j1) + G2;
        let x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords */
        let y2 = y0 - 1.0f + 2.0f * G2;

        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds */
        let ii = i &&& 0xff;
        let jj = j &&& 0xff;

        // Calculate the contribution from the three corners */
        let t0c = 0.5f - x0 * x0 - y0 * y0
        let struct(t0, t20, t40, n0, gx0, gy0) =
            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let h = (perm.[ii + perm.[jj]] &&& 7) <<< 1
                let gx0, gy0 = grad2lut.[h], grad2lut.[h + 1]
                //let gx0, gy0 = grad2(perm.[ii + perm.[jj]])
                let t20 = t0c * t0c
                let t40 = t20 * t20
                let n0 = t40 * (gx0 * x0 + gy0 * y0)
                struct(t0c, t20, t40, n0, gx0, gy0)

        let t1c = 0.5f - x1 * x1 - y1 * y1
        let struct(t1, t21, t41, n1, gx1, gy1) =
            if t1c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let h = (perm.[ii + i1 + perm.[jj + j1]] &&& 7) <<< 1
                let gx1, gy1 = grad2lut.[h], grad2lut.[h + 1]
                //let gx1, gy1 = grad2(perm.[ii + i1 + perm.[jj + j1]])
                let t21 = t1c * t1c
                let t41 = t21 * t21
                let n1 = t41 * (gx1 * x1 + gy1 * y1)
                struct(t1c, t21, t41, n1, gx1, gy1)

        let t2c = 0.5f - x2 * x2 - y2 * y2
        let struct(t2, t22, t42, n2, gx2, gy2) =
            if (t2c < 0.0f) then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let h = (perm.[ii + 1 + perm.[jj + 1]] &&& 7) <<< 1
                let gx2, gy2 = grad2lut.[h], grad2lut.[h + 1]
                //let gx2, gy2 = grad2(perm.[ii + 1 + perm.[jj + 1]])
                let t22 = t2c * t2c
                let t42 = t22 * t22
                let n2 = t42 * (gx2 * x2 + gy2 * y2)
                struct(t2c, t22, t42, n2, gx2, gy2)

        // Add contributions from each corner to get the final noise value.
        // The result is scaled to return values in the interval [-1,1].
        let value = Scaling2 * (n0 + n1 + n2);
        
        if calcGrad then
            //  A straight, unoptimised calculation would be like:
            //    *dnoise_dx = -8.0f * t20 * t0 * x0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gx0;
            //    *dnoise_dy = -8.0f * t20 * t0 * y0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gy0;
            //    *dnoise_dx += -8.0f * t21 * t1 * x1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gx1;
            //    *dnoise_dy += -8.0f * t21 * t1 * y1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gy1;
            //    *dnoise_dx += -8.0f * t22 * t2 * x2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gx2;
            //    *dnoise_dy += -8.0f * t22 * t2 * y2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gy2;
            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0);
            let dnoise_dx = temp0 * x0;
            let dnoise_dy = temp0 * y0;
            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1);
            let dnoise_dx1 = dnoise_dx + temp1 * x1;
            let dnoise_dy1 = dnoise_dy + temp1 * y1;
            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2);
            let dnoise_dx2 = (dnoise_dx1 + temp2 * x2) * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2
            let dnoise_dy2 = (dnoise_dy1 + temp2 * y2) * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2
            struct(value, dnoise_dx2 * Scaling2, dnoise_dy2 * Scaling2)
        else
            struct(value, 0.0f, 0.0f)

    // Skewing factors for 3D simplex grid:
    // F3 = 1/3
    // G3 = 1/6 */
    let private F3 = 0.333333333f
    let private G3 = 0.166666667f
    let private Scaling3 = 28.0f

    let sample3 calcGrad (x: float32) (y: float32) (z: float32) =
        // Skew the input space to determine which simplex cell we're in */
        let s = (x + y + z) * F3; // Very nice and simple skew factor for 3D
        let xs = x + s;
        let ys = y + s;
        let zs = z + s;
        let i = floor(xs);
        let j = floor(ys);
        let k = floor(zs);

        let t = float32(i + j + k) * G3;
        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z) space
        let Y0 = float32(j) - t;
        let Z0 = float32(k) - t;
        let x0 = x - X0; // The x,y,z distances from the cell origin
        let y0 = y - Y0;
        let z0 = z - Z0;

        // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
        // Determine which simplex we are in.
        // i1, j1, k1: Offsets for second corner of simplex in (i,j,k) coords */
        // i2, j2, k3: Offsets for third corner of simplex in (i,j,k) coords

        let struct(i1, j1, k1, i2, j2, k2) =
            if x0 >= y0 then
                if y0 >= z0 then struct(1, 0, 0, 1, 1, 0) // X Y Z order 
                else if x0 >= z0 then struct(1, 0, 0, 1, 0, 1) // X Z Y order
                else struct(0, 0, 1, 1, 0, 1) // Z X Y order
            else // x0<y0
                if y0 < z0 then struct(0, 0, 1, 0, 1, 1) // Z Y X order */
                else if x0 < z0 then struct(0, 1, 0, 0, 1, 1) // Y Z X order */
                else struct(0, 1, 0, 1, 1, 0) // Y X Z order */

        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
        // c = 1/6.   */

        let x1 = x0 - float32(i1) + G3; // Offsets for second corner in (x,y,z) coords */
        let y1 = y0 - float32(j1) + G3;
        let z1 = z0 - float32(k1) + G3;
        let x2 = x0 - float32(i2) + 2.0f * G3; // Offsets for third corner in (x,y,z) coords */
        let y2 = y0 - float32(j2) + 2.0f * G3;
        let z2 = z0 - float32(k2) + 2.0f * G3;
        let x3 = x0 - 1.0f + 3.0f * G3; // Offsets for last corner in (x,y,z) coords */
        let y3 = y0 - 1.0f + 3.0f * G3;
        let z3 = z0 - 1.0f + 3.0f * G3;

        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds */
        let ii = i &&& 0xff
        let jj = j &&& 0xff
        let kk = k &&& 0xff

        // Calculate the contribution from the four corners */
        let t0c = 0.6f - x0 * x0 - y0 * y0 - z0 * z0;
        let struct(t0, n0, t20, t40, gx0, gy0, gz0) =
            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let gx0, gy0, gz0 = grad3(perm.[ii + perm.[jj + perm.[kk]]]);
                let t20 = t0c * t0c;
                let t40 = t20 * t20;
                let n0 = t40 * (gx0 * x0 + gy0 * y0 + gz0 * z0)
                struct(t0c, n0, t20, t40, gx0, gy0, gz0);

        let t1c = 0.6f - x1 * x1 - y1 * y1 - z1 * z1;
        let struct(t1, n1, t21, t41, gx1, gy1, gz1) =
            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let gx1, gy1, gz1 = grad3(perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1]]]);
                let t21 = t1c * t1c;
                let t41 = t21 * t21;
                let n1 = t41 * (gx1 * x1 + gy1 * y1 + gz1 * z1);
                struct(t1c, n1, t21, t41, gx1, gy1, gz1);

        let t2c = 0.6f - x2 * x2 - y2 * y2 - z2 * z2;
        let struct(t2, n2, t22, t42, gx2, gy2, gz2) =
            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let gx2, gy2, gz2 = grad3(perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2]]]);
                let t22 = t2c * t2c;
                let t42 = t22 * t22;
                let n2 = t42 * (gx2 * x2 + gy2 * y2 + gz2 * z2);
                struct(t2c, n2, t22, t42, gx2, gy2, gz2);

        let t3c = 0.6f - x3 * x3 - y3 * y3 - z3 * z3;
        let struct(t3, n3, t23, t43, gx3, gy3, gz3) =
            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence
            else
                let gx3, gy3, gz3 = grad3(perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1]]]);
                let t23 = t3c * t3c;
                let t43 = t23 * t23;
                let n3 = t43 * (gx3 * x3 + gy3 * y3 + gz3 * z3);
                struct(t3c, n3, t23, t43, gx3, gy3, gz3);

        // Add contributions from each corner to get the final noise value.
        // The result is scaled to return values in the range [-1,1] */
        let value = 28.0f * (n0 + n1 + n2 + n3);

        //if( ( NULL != dnoise_dx ) && ( NULL != dnoise_dy ) && ( NULL != dnoise_dz ))
        if calcGrad then
            ////  A straight, unoptimised calculation would be like:
            ///     *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gx0;
            ///    *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gy0;
            ///    *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gz0;
            ///    *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gx1;
            ///    *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gy1;
            ///    *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gz1;
            ///    *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gx2;
            ///    *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gy2;
            ///    *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gz2;
            ///    *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gx3;
            ///    *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gy3;
            ///    *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gz3;
            ////
            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0 + gz0 * z0);
            let dnoise_dx = temp0 * x0;
            let dnoise_dy = temp0 * y0;
            let dnoise_dz = temp0 * z0;
            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1 + gz1 * z1);
            let dnoise_dx1 = dnoise_dx + temp1 * x1;
            let dnoise_dy1 = dnoise_dy + temp1 * y1;
            let dnoise_dz1 = dnoise_dz + temp1 * z1;
            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2 + gz2 * z2);
            let dnoise_dx2 = dnoise_dx1 + temp2 * x2;
            let dnoise_dy2 = dnoise_dy1 + temp2 * y2;
            let dnoise_dz2 = dnoise_dz1 + temp2 * z2;
            let temp3 = t23 * t3 * (gx3 * x3 + gy3 * y3 + gz3 * z3);
            let dnoise_dx3 = (dnoise_dx2 + temp3 * x3) * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3;
            let dnoise_dy3 = (dnoise_dy2 + temp3 * y3) * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3;
            let dnoise_dz3 = (dnoise_dz2 + temp3 * z3) * -8.0f + t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3;
            struct(value, dnoise_dx3 * 28.0f, dnoise_dy3 * 28.0f, dnoise_dz3 * 28.0f)
        else
            struct(value, 0.0f, 0.0f, 0.0f)

    // The skewing and unskewing factors are hairy again for the 4D case
    let private F4 = 0.309016994f; // F4 = (Math.sqrt(5.0)-1.0)/4.0
    let private G4 = 0.138196601f; // G4 = (5.0-Math.sqrt(5.0))/20.0
    //let Scaling4 = 27.0f

    //* 4D simplex noise with derivatives.
    //* If the last four arguments are not null, the analytic derivative
    //* (the 4D gradient of the scalar noise field) is also calculated.
    //*/
    let sample4 calcGrad (x: float32) (y: float32) (z: float32) (w: float32) =
        // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
        let s = (x + y + z + w) * F4; // Factor for 4D skewing
        let xs = x + s;
        let ys = y + s;
        let zs = z + s;
        let ws = w + s;
        let i = floor(xs);
        let j = floor(ys);
        let k = floor(zs);
        let l = floor(ws);

        let t = float32(i + j + k + l) * G4; // Factor for 4D unskewing
        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z,w) space
        let Y0 = float32(j) - t;
        let Z0 = float32(k) - t;
        let W0 = float32(l) - t;

        let x0 = x - X0;  // The x,y,z,w distances from the cell origin
        let y0 = y - Y0;
        let z0 = z - Z0;
        let w0 = w - W0;

        // For the 4D case, the simplex is a 4D shape I won't even try to describe.
        // To find out which of the 24 possible simplices we're in, we need to
        // determine the magnitude ordering of x0, y0, z0 and w0.
        // The method below is a reasonable way of finding the ordering of x,y,z,w
        // and then find the correct traversal order for the simplex we’re in.
        // First, six pair-wise comparisons are performed between each possible pair
        // of the four coordinates, and then the results are used to add up binary
        // bits for an integer index into a precomputed lookup table, simplex[].
        let c1 = if (x0 > y0) then 32 else 0;
        let c2 = if (x0 > z0) then 16 else 0;
        let c3 = if (y0 > z0) then 8 else 0;
        let c4 = if (x0 > w0) then 4 else 0;
        let c5 = if (y0 > w0) then 2 else 0;
        let c6 = if (z0 > w0) then 1 else 0;
        let c = c1 ||| c2 ||| c3 ||| c4 ||| c5 ||| c6; // '|' is mostly faster than '+'

        // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
        // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
        // impossible. Only the 24 indices which have non-zero entries make any sense.
        // We use a thresholding to set the coordinates in turn from the largest magnitude.
        // The number 3 in the "simplex" array is at the position of the largest coordinate.
        let i1 = if simplex.[c, 0] >= 3 then 1 else 0;
        let j1 = if simplex.[c, 1] >= 3 then 1 else 0;
        let k1 = if simplex.[c, 2] >= 3 then 1 else 0;
        let l1 = if simplex.[c, 3] >= 3 then 1 else 0;
        // The number 2 in the "simplex" array is at the second largest coordinate.
        let i2 = if simplex.[c, 0] >= 2 then 1 else 0;
        let j2 = if simplex.[c, 1] >= 2 then 1 else 0;
        let k2 = if simplex.[c, 2] >= 2 then 1 else 0;
        let l2 = if simplex.[c, 3] >= 2 then 1 else 0;
        // The number 1 in the "simplex" array is at the second smallest coordinate.
        let i3 = if simplex.[c, 0] >= 1 then 1 else 0;
        let j3 = if simplex.[c, 1] >= 1 then 1 else 0;
        let k3 = if simplex.[c, 2] >= 1 then 1 else 0;
        let l3 = if simplex.[c, 3] >= 1 then 1 else 0;
        // The fifth corner has all coordinate offsets = 1, so no need to look that up.

        let x1 = x0 - float32(i1) + G4; // Offsets for second corner in (x,y,z,w) coords
        let y1 = y0 - float32(j1) + G4;
        let z1 = z0 - float32(k1) + G4;
        let w1 = w0 - float32(l1) + G4;
        let x2 = x0 - float32(i2) + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords
        let y2 = y0 - float32(j2) + 2.0f * G4;
        let z2 = z0 - float32(k2) + 2.0f * G4;
        let w2 = w0 - float32(l2) + 2.0f * G4;
        let x3 = x0 - float32(i3) + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords
        let y3 = y0 - float32(j3) + 3.0f * G4;
        let z3 = z0 - float32(k3) + 3.0f * G4;
        let w3 = w0 - float32(l3) + 3.0f * G4;
        let x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords
        let y4 = y0 - 1.0f + 4.0f * G4;
        let z4 = z0 - 1.0f + 4.0f * G4;
        let w4 = w0 - 1.0f + 4.0f * G4;

        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds
        let ii = i &&& 0xff;
        let jj = j &&& 0xff;
        let kk = k &&& 0xff;
        let ll = l &&& 0xff;

        // Calculate the contribution from the five corners
        let t0c = 0.6f - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
        let struct(n0, t0, t20, t40, gx0, gy0, gz0, gw0) =
            if (t0c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence
            else
                let t20 = t0c * t0c;
                let t40 = t20 * t20;
                let gx0, gy0, gz0, gw0 = grad4(perm.[ii + perm.[jj + perm.[kk + perm.[ll]]]]);
                let n0 = t40 * (gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0);
                struct(n0, t0c, t20, t40, gx0, gy0, gz0, gw0)

        let t1c = 0.6f - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
        let struct(n1, t1, t21, t41, gx1, gy1, gz1, gw1) =
            if (t1c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence
            else
                let t21 = t1c * t1c;
                let t41 = t21 * t21;
                let gx1, gy1, gz1, gw1 = grad4(perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1 + perm.[ll + l1]]]]);
                let n1 = t41 * (gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1);
                struct(n1, t1c, t21, t41, gx1, gy1, gz1, gw1)

        let t2c = 0.6f - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
        let struct(n2, t2, t22, t42, gx2, gy2, gz2, gw2) =
            if (t2c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence
            else
                let t22 = t2c * t2c;
                let t42 = t22 * t22;
                let gx2, gy2, gz2, gw2 = grad4(perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2 + perm.[ll + l2]]]]);
                let n2 = t42 * (gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2);
                struct(n2, t2c, t22, t42, gx2, gy2, gz2, gw2)

        let t3c = 0.6f - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
        let struct(n3, t3, t23, t43, gx3, gy3, gz3, gw3) =
            if (t3c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence
            else
                let t23 = t3c * t3c;
                let t43 = t23 * t23;
                let gx3, gy3, gz3, gw3 = grad4(perm.[ii + i3 + perm.[jj + j3 + perm.[kk + k3 + perm.[ll + l3]]]]);
                let n3 = t43 * (gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3);
                struct(n3, t3c, t23, t43, gx3, gy3, gz3, gw3)

        let t4c = 0.6f - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
        let struct(n4, t4, t24, t44, gx4, gy4, gz4, gw4) =
            if (t4c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence
            else
                let t24 = t4c * t4c;
                let t44 = t24 * t24;
                let gx4, gy4, gz4, gw4 = grad4(perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1 + perm.[ll + 1]]]]);
                let n4 = t44 * (gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4);
                struct(n4, t4c, t24, t44, gx4, gy4, gz4, gw4)

        // Sum up and scale the result to cover the range [-1,1]
        let value = 27.0f * (n0 + n1 + n2 + n3 + n4); // The scale factor is preliminary!

        // Compute derivative, if requested by supplying non-null pointers
        // for the last four arguments */
        //if( ( NULL != dnoise_dx ) && ( NULL != dnoise_dy ) && ( NULL != dnoise_dz ) && ( NULL != dnoise_dw ) )
        if calcGrad then
            //  A straight, unoptimised calculation would be like:
            //*     *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gx0;
            //*    *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gy0;
            //*    *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gz0;
            //*    *dnoise_dw = -8.0f * t20 * t0 * w0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gw0;
            //*    *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gx1;
            //*    *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gy1;
            //*    *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gz1;
            //*    *dnoise_dw = -8.0f * t21 * t1 * w1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gw1;
            //*    *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gx2;
            //*    *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gy2;
            //*    *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gz2;
            //*    *dnoise_dw += -8.0f * t22 * t2 * w2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gw2;
            //*    *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gx3;
            //*    *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gy3;
            //*    *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gz3;
            //*    *dnoise_dw += -8.0f * t23 * t3 * w3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gw3;
            //*    *dnoise_dx += -8.0f * t24 * t4 * x4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gx4;
            //*    *dnoise_dy += -8.0f * t24 * t4 * y4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gy4;
            //*    *dnoise_dz += -8.0f * t24 * t4 * z4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gz4;
            //*    *dnoise_dw += -8.0f * t24 * t4 * w4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gw4;
            //*/
            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0);
            let dnoise_dx = temp0 * x0;
            let dnoise_dy = temp0 * y0;
            let dnoise_dz = temp0 * z0;
            let dnoise_dw = temp0 * w0;
            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1);
            let dnoise_dx1 = dnoise_dx + temp1 * x1;
            let dnoise_dy1 = dnoise_dy + temp1 * y1;
            let dnoise_dz1 = dnoise_dz + temp1 * z1;
            let dnoise_dw1 = dnoise_dw + temp1 * w1;
            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2);
            let dnoise_dx2 = dnoise_dx1 + temp2 * x2;
            let dnoise_dy2 = dnoise_dy1 + temp2 * y2;
            let dnoise_dz2 = dnoise_dz1 + temp2 * z2;
            let dnoise_dw2 = dnoise_dw1 + temp2 * w2;
            let temp3 = t23 * t3 * (gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3);
            let dnoise_dx3 = dnoise_dx2 + temp3 * x3;
            let dnoise_dy3 = dnoise_dy2 + temp3 * y3;
            let dnoise_dz3 = dnoise_dz2 + temp3 * z3;
            let dnoise_dw3 = dnoise_dw2 + temp3 * w3;
            let temp4 = t24 * t4 * (gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4);
            let dnoise_dx4 = dnoise_dx3 + temp4 * x4;
            let dnoise_dy4 = dnoise_dy3 + temp4 * y4;
            let dnoise_dz4 = dnoise_dz3 + temp4 * z4;
            let dnoise_dw4 = dnoise_dw3 + temp4 * w4;
            let dnoise_dx5 = dnoise_dx4 * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3 + t44 * gx4;
            let dnoise_dy5 = dnoise_dy4 * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3 + t44 * gy4;
            let dnoise_dz5 = dnoise_dz4 * -8.0f + t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3 + t44 * gz4;
            let dnoise_dw5 = dnoise_dw4 * -8.0f + t40 * gw0 + t41 * gw1 + t42 * gw2 + t43 * gw3 + t44 * gw4;
            struct(value, dnoise_dx5 * 28.0f, dnoise_dy5 * 28.0f, dnoise_dz5 * 28.0f, dnoise_dw5 * 28.0f)
        else
            struct(value, 0.0f, 0.0f, 0.0f, 0.0f)

type Noise() =
    static member Sample(x) =
        let struct(s, _) = GradientNoise.sample1 false x
        s
        
    static member Sample(p : Vector2) =
        SimplexNoise.sample2 p.X p.Y

    static member Sample(p : Vector3) =
        SimplexNoise.sample3 p.X p.Y p.Z

    static member Sample(p : Vector4) =
        SimplexNoise.sample4 p.X p.Y p.Z p.W
                

================================================
FILE: samples/Garnet.Numerics/Numerics.fs
================================================
namespace Garnet.Numerics

open System
open System.Numerics

[<AutoOpen>]
module MathF =
    type MathF with
        static member inline Lerp(min, max, t : float32) =
            min * (1.0f - t) + max * t

        static member inline Clamp(s0 : float32, s1 : float32, s : float32) =
            s |> max s0 |> min s1
            
        static member inline Clamp01(x) =
            MathF.Clamp(0.0f, 1.0f, x)

        static member inline LinearStep(s0, s1, s) =
            let length = s1 - s0
            if abs length < 1e-7f then 0.0f
            else MathF.Clamp01((s - s0) / length)

        static member inline SmoothStep(s0, s1, s) =
            let x = MathF.LinearStep(s0, s1, s)
            x * x * (3.0f - 2.0f * x)

[<AutoOpen>]
module Matrix4x4 =
    type Matrix4x4 with
        member m.GetInverseOrIdentity() =
            let mutable mInv = Matrix4x4.Identity
            if Matrix4x4.Invert(m, &mInv) then mInv else Matrix4x4.Identity


================================================
FILE: samples/Garnet.Numerics/Random.fs
================================================
namespace Garnet.Numerics

open System
open System.Numerics

type PcgRandom(initState, initSeq) =
    let inc = PcgRandom.GetIncrement(initSeq)
    let mutable state = PcgRandom.GetState(initState, inc)
    new() = PcgRandom(0x853c49e6748fea9bUL, 0xda3e39cb94b95bdbUL)
    
    member c.NextUInt32() =
        let result = PcgRandom.GetUInt32(state)
        state <- PcgRandom.GetNextState(state, inc)
        result
    
    static member GetUInt32(state) =
        let xorShifted = uint32 (((state >>> 18) ^^^ state) >>> 27)
        let rot = int (state >>> 59)
        (xorShifted >>> rot) ||| (xorShifted <<< ((-rot) &&& 31))
    
    static member GetNextState(state, inc) =        
        state * 6364136223846793005UL + inc
    
    static member GetIncrement(initSeq) =
        (initSeq <<< 1) ||| 1UL
    
    static member GetState(seed, inc) =
        let s = PcgRandom.GetNextState(0UL, inc) + seed
        PcgRandom.GetNextState(s, inc)

type PcgRandom with
    member c.NextInt32() =
        c.NextUInt32() |> int

    member c.NextUInt32Unbiased(exclusiveBound : uint) =
        let threshold = uint32 ((0x100000000UL - uint64 exclusiveBound) % uint64 exclusiveBound)
        let mutable r = c.NextUInt32()
        while r < threshold do
            r <- c.NextUInt32()
        r % exclusiveBound

    member c.NextInt32Unbiased(exclusiveBound : int) =
        let threshold = int32 ((0x100000000UL - uint64 exclusiveBound) % uint64 exclusiveBound)
        let mutable r = c.NextInt32()
        while r < threshold do
            r <- c.NextInt32()
        r % exclusiveBound

    member c.NextInt32(max : int) =
        if max = 0 then 0 else c.NextUInt32() % (uint32 max) |> int

    member c.NextUInt32(max : uint) =
        if max = 0u then 0u else c.NextUInt32() % max

    member c.NextUInt32(min, max) =
        c.NextUInt32(max - min) + min

    member c.NextInt32(min, max) =
        c.NextInt32(max - min) + min

    /// Closed [0, 1]
    member c.NextDouble() =
        let x = c.NextUInt32()
        double x * (1.0 / 4294967295.0)

    /// Half open [0, 1)
    member c.NextDouble2() = 
        let x = c.NextUInt32()
        double x * (1.0 / 4294967296.0)

    /// Open (0, 1)
    member c.NextDouble3() = 
        let x = c.NextUInt32()
        (double x + 0.5) * (1.0 / 4294967296.0)

    member c.NextSingle() = float32 (c.NextDouble())
    member c.NextSingle2() = float32 (c.NextDouble2())
    member c.NextSingle3() = float32 (c.NextDouble3())

    member c.NextSingle(min, max) =
        c.NextSingle() * (max - min) + min

    member c.NextRotation(radiansRange) =
        Vector2.FromRadians((c.NextSingle() - 0.5f) * radiansRange) 

    member c.NextRotationDegrees(degreesRange) =
        c.NextRotation(degreesRange * MathF.PI / 180.0f)

    member c.NextVector2() =
        Vector2(c.NextSingle(), c.NextSingle())
        
    member c.NextVector2(range : Range2) =
        Range2.Lerp(range, c.NextVector2())

    member c.NextVector3() =
        Vector3(c.NextSingle(), c.NextSingle(), c.NextSingle())
        
    member c.NextVector3(range : Range3) =
        Range3.Lerp(range, c.NextVector3())

    member c.NextVector2i(r : Range2i) =
        Vector2i(
            c.NextInt32(r.Min.X, r.Max.X),
            c.NextInt32(r.Min.Y, r.Max.Y))

    member c.NextVector2i(min, max) =
        let r = Rangei(min, max)
        c.NextVector2i(Range2i(r, r))

    member c.NextVector3i(r : Range3i) =
        Vector3i(
            c.NextInt32(r.Min.X, r.Max.X),
            c.NextInt32(r.Min.Y, r.Max.Y),
            c.NextInt32(r.Min.Z, r.Max.Z))

    member c.NextVector3i(min, max) =
        let r = Rangei(min, max)
        c.NextVector3i(Range3i(r, r, r))

    member c.Next(dest : Span<int>) =
        for i = 0 to dest.Length - 1 do
            dest.[i] <- c.NextInt32()
        
    member c.Shuffle(items) =
        let r = items |> Seq.toArray
        for i = 0 to r.Length - 2 do
            let j = c.NextInt32(i, r.Length)
            let temp = r.[i]
            r.[i] <- r.[j]
            r.[j] <- temp    
        r
        

================================================
FILE: samples/Garnet.Numerics/Ranges.fs
================================================
namespace Garnet.Numerics

open System.Numerics

// Float range types

[<Struct>]
type Range =
    val Min : float32
    val Max : float32
    new(min, max) = { Min = min; Max = max }
    member inline c.Size = c.Max - c.Min
    member inline c.IsEmpty = c.Min >= c.Max
    member inline c.Contains(x) = x >= c.Min && x < c.Max
    member inline c.Expand(margin) = Range(c.Min - margin, c.Max + margin)
    member inline c.Clamp(x) = x |> max c.Min |> min c.Max
    override v.ToString() = $"%A{v.Min} to %A{v.Max}"
    static member Zero = Range(0.0f, 0.0f)
    static member ZeroToOne = Range(0.0f, 1.0f)
    static member inline Lerp(r : Range, t) = r.Min * (1.0f - t) + r.Max * t
    static member inline (+) (a: Range, c) = Range(a.Min + c, a.Max + c)
    static member inline (-) (a: Range, c) = Range(a.Min - c, a.Max - c)
    static member inline (*) (a: Range, c) = Range(a.Min * c, a.Max * c)
    static member inline (/) (a: Range, c) = Range(a.Min / c, a.Max / c)
    static member inline Point(point) = Range(point, point)
    static member inline Sized(min, size) = 
        Range(min, min + size)
    static member inline Centered(center, size) = 
        Range.Sized(center - size * 0.5f, size)
    static member inline Intersection(a : Range, b : Range) = 
        Range(max a.Min b.Min, min a.Max b.Max)
    static member inline Union(a : Range, b : Range) = 
        Range(min a.Min b.Min, max a.Max b.Max)

[<Struct>]
type Range2 =
    val Min : Vector2
    val Max : Vector2
    new(min, max) = { Min = min; Max = max }
    new(x : Range, y : Range) = { 
        Min = Vector2(x.Min, y.Min)
        Max = Vector2(x.Max, y.Max) 
        }
    member inline c.X = Range(c.Min.X, c.Max.X)
    member inline c.Y = Range(c.Min.Y, c.Max.Y)
    member inline c.Center = (c.Min + c.Max) * 0.5f
    member inline c.Size = c.Max - c.Min
    member inline c.GetArea() =
        let s = c.Size
        s.X * s.Y
    member inline c.Contains(p : Vector2) = 
        c.X.Contains p.X && c.Y.Contains p.Y
    member inline c.Expand(margin : Vector2) = 
        Range2(
            c.X.Expand(margin.X),
            c.Y.Expand(margin.Y))
    override i.ToString() = $"%A{i.Min} to %A{i.Max}"
    static member Zero = Range2(Vector2.Zero, Vector2.Zero)
    static member ZeroToOne = Range2(Vector2.Zero, Vector2.One)
    static member inline Lerp(r : Range2, t : Vector2) =
        Vector2(
            Range.Lerp(r.X, t.X),
            Range.Lerp(r.Y, t.Y))
    static member inline (+) (a: Range2, c : float32) = Range2(a.X + c, a.Y + c)
    static member inline (-) (a: Range2, c : float32) = Range2(a.X - c, a.Y - c)
    static member inline (*) (a: Range2, c : float32) = Range2(a.X * c, a.Y * c)
    static member inline (/) (a: Range2, c : float32) = Range2(a.X / c, a.Y / c)
    static member inline (+) (a: Range2, v : Vector2) = Range2(a.X + v.X, a.Y + v.Y)
    static member inline (-) (a: Range2, v : Vector2) = Range2(a.X - v.X, a.Y - v.Y)
    static member inline (*) (a: Range2, v : Vector2) = Range2(a.X * v.X, a.Y * v.Y)
    static member inline (/) (a: Range2, v : Vector2) = Range2(a.X / v.X, a.Y / v.Y)
    static member inline Point(point : Vector2) = Range2(point, point)
    static member inline Sized(min : Vector2, size : Vector2) = 
        Range2(min, min + size)
    static member inline Centered(center : Vector2, size : Vector2) = 
        Range2.Sized(center - size * 0.5f, size)
    static member inline Intersection(a : Range2, b : Range2) = 
        Range2(
            Range.Intersection(a.X, b.X), 
            Range.Intersection(a.Y, b.Y))
    static member inline Union(a : Range2, b : Range2) =         
        Range2(
            Range.Union(a.X, b.X), 
            Range.Union(a.Y, b.Y))

[<Struct>]
type Range3 =
    val Min : Vector3
    val Max : Vector3
    new(min, max) = { Min = min; Max = max }
    new(x : Range, y : Range, z : Range) = { 
        Min = Vector3(x.Min, y.Min, z.Min)
        Max = Vector3(x.Max, y.Max, z.Max) 
        }
    member inline c.X = Range(c.Min.X, c.Max.X)
    member inline c.Y = Range(c.Min.Y, c.Max.Y)
    member inline c.Z = Range(c.Min.Z, c.Max.Z)
    member inline c.Size = c.Max - c.Min
    member inline c.GetVolume() =
        let s = c.Size
        s.X * s.Y * s.Z
    member inline c.Contains(p : Vector3) = 
        c.X.Contains p.X && 
        c.Y.Contains p.Y && 
        c.Z.Contains p.Z
    member inline c.Expand(margin : Vector3) = 
        Range3(
            c.X.Expand(margin.X),
            c.Y.Expand(margin.Y),
            c.Z.Expand(margin.Z))
    member inline c.Clamp(p : Vector3) = 
        Vector3(
            c.X.Clamp p.X,
            c.Y.Clamp p.Y,
            c.Z.Clamp p.Z)
    override i.ToString() = $"%A{i.Min} to %A{i.Max}"
    static member inline Lerp(r : Range3, t : Vector3) =
        Vector3(
            Range.Lerp(r.X, t.X),
            Range.Lerp(r.Y, t.Y),
            Range.Lerp(r.Z, t.Z))
    static member Zero = Range3(Vector3.Zero, Vector3.Zero)
    static member ZeroToOne = Range3(Vector3.Zero, Vector3.One)
    static member inline (+) (a: Range3, c) = Range3(a.X + c, a.Y + c, a.Z + c)
    static member inline (-) (a: Range3, c) = Range3(a.X - c, a.Y - c, a.Z - c)
    static member inline (*) (a: Range3, c) = Range3(a.X * c, a.Y * c, a.Z * c)
    static member inline (/) (a: Range3, c) = Range3(a.X / c, a.Y / c, a.Z / c)
    static member inline (+) (a: Range3, v : Vector3) = Range3(a.X + v.X, a.Y + v.Y, a.Z + v.Z)
    static member inline (-) (a: Range3, v : Vector3) = Range3(a.X - v.X, a.Y - v.Y, a.Z - v.Z)
    static member inline (*) (a: Range3, v : Vector3) = Range3(a.X * v.X, a.Y * v.Y, a.Z * v.Z)
    static member inline (/) (a: Range3, v : Vector3) = Range3(a.X / v.X, a.Y / v.Y, a.Z / v.Z)
    static member inline Point(point : Vector3) = Range3(point, point)
    static member inline Sized(min : Vector3, size : Vector3) = 
        Range3(min, min + size)
    static member inline Centered(center : Vector3, size : Vector3) = 
        Range3.Sized(center - size * 0.5f, size)
    static member inline Intersection(a : Range3, b : Range3) = 
        Range3(
            Range.Intersection(a.X, b.X), 
            Range.Intersection(a.Y, b.Y),
            Range.Intersection(a.Z, b.Z))
    static member inline Union(a : Range3, b : Range3) =         
        Range3(
            Range.Union(a.X, b.X), 
            Range.Union(a.Y, b.Y),
            Range.Union(a.Z, b.Z))

// Int32 range types

[<Struct>]
type Rangei =
    val Min : int
    val Max : int
    new(min, max) = { Min = min; Max = max }
    member inline c.Size = c.Max - c.Min
    member inline c.IsEmpty = c.Min >= c.Max
    member inline c.Contains(x) = x >= c.Min && x < c.Max
    member inline c.Expand(margin) = Rangei(c.Min - margin, c.Max + margin)
    member inline c.Clamp(x) = x |> max c.Min |> min c.Max
    member inline c.ToRange() = Range(float32 c.Min, float32 c.Max)
    override v.ToString() = $"%A{v.Min} to %A{v.Max}"
    static member Zero = Rangei(0, 0)
    static member ZeroToOne = Rangei(0, 1)
    static member inline (+) (a: Rangei, c) = Rangei(a.Min + c, a.Max + c)
    static member inline (-) (a: Rangei, c) = Rangei(a.Min - c, a.Max - c)
    static member inline (*) (a: Rangei, c) = Rangei(a.Min * c, a.Max * c)
    static member inline (/) (a: Rangei, c) = Rangei(a.Min / c, a.Max / c)
    static member inline Point(point) = Rangei(point, point)
    static member inline Sized(min, size) = 
        Rangei(min, min + size)
    static member inline Centered(center, size) = 
        Rangei.Sized(center - size / 2, size)
    static member inline Intersection(a : Rangei, b : Rangei) = 
        Rangei(max a.Min b.Min, min a.Max b.Max)
    static member inline Union(a : Rangei, b : Rangei) = 
        Rangei(min a.Min b.Min, max a.Max b.Max)

[<Struct>]
type Range2i =
    val Min : Vector2i
    val Max : Vector2i
    new(min, max) = { Min = min; Max = max }
    new(x : Rangei, y : Rangei) = { 
        Min = Vector2i(x.Min, y.Min)
        Max = Vector2i(x.Max, y.Max) 
        }
    member inline c.X = Rangei(c.Min.X, c.Max.X)
    member inline c.Y = Rangei(c.Min.Y, c.Max.Y)
    member inline c.Size = c.Max - c.Min
    member inline c.IsEmpty = c.X.IsEmpty || c.Y.IsEmpty
    member inline c.ToRange2() = Range2(c.Min.ToVector2(), c.Max.ToVector2())
    member inline c.GetArea() =
        let s = c.Size
        s.X * s.Y
    member inline c.Contains (p : Vector2i) = 
        c.X.Contains p.X && 
        c.Y.Contains p.Y
    member inline c.Expand(margin : Vector2i) = 
        Range2i(
            c.X.Expand(margin.X),
            c.Y.Expand(margin.Y))
    member inline c.Clamp(p : Vector2i) = 
        Vector2i(c.X.Clamp p.X, c.Y.Clamp p.Y)
    override i.ToString() = $"%A{i.Min} to %A{i.Max}"
    static member Zero = Range2i(Vector2i.Zero, Vector2i.Zero)
    static member ZeroToOne = Range2i(Vector2i.Zero, Vector2i.One)
    static member inline (+) (a: Range2i, c) = Range2i(a.X + c, a.Y + c)
    static member inline (-) (a: Range2i, c) = Range2i(a.X - c, a.Y - c)
    static member inline (*) (a: Range2i, c) = Range2i(a.X * c, a.Y * c)
    static member inline (/) (a: Range2i, c) = Range2i(a.X / c, a.Y / c)
    static member inline (+) (a: Range2i, v : Vector2i) = Range2i(a.X + v.X, a.Y + v.Y)
    static member inline (-) (a: Range2i, v : Vector2i) = Range2i(a.X - v.X, a.Y - v.Y)
    static member inline (*) (a: Range2i, v : Vector2i) = Range2i(a.X * v.X, a.Y * v.Y)
    static member inline (/) (a: Range2i, v : Vector2i) = Range2i(a.X / v.X, a.Y / v.Y)
    static member inline Point(point : Vector2i) = Range2i(point, point)
    static member inline Sized(min : Vector2i, size : Vector2i) = 
        Range2i(min, min + size)
    static member inline Centered(center : Vector2i, size : Vector2i) = 
        Range2i.Sized(center - size / 2, size)
    static member inline Intersection(a : Range2i, b : Range2i) = 
        Range2i(
            Rangei.Intersection(a.X, b.X), 
            Rangei.Intersection(a.Y, b.Y))
    static member inline Union(a : Range2i, b : Range2i) =         
        Range2i(
            Rangei.Union(a.X, b.X), 
            Rangei.Union(a.Y, b.Y))

[<Struct>]
type Range3i =
    val Min : Vector3i
    val Max : Vector3i
    new(min, max) = { Min = min; Max = max }
    new(x : Rangei, y : Rangei, z : Rangei) = { 
        Min = Vector3i(x.Min, y.Min, z.Min)
        Max = Vector3i(x.Max, y.Max, z.Max) 
        }
    member inline c.X = Rangei(c.Min.X, c.Max.X)
    member inline c.Y = Rangei(c.Min.Y, c.Max.Y)
    member inline c.Z = Rangei(c.Min.Z, c.Max.Z)
    member inline c.Size = c.Max - c.Min
    member inline c.IsEmpty = c.X.IsEmpty || c.Y.IsEmpty || c.Z.IsEmpty
    member inline c.ToRange3() = Range3(c.Min.ToVector3(), c.Max.ToVector3())
    member inline c.GetVolume() =
        let s = c.Size
        s.X * s.Y * s.Z
    member inline c.Contains(p : Vector3i) = 
        c.X.Contains p.X && 
        c.Y.Contains p.Y && 
        c.Z.Contains p.Z
    member inline c.Expand(margin : Vector3i) = 
        Range3i(
            c.X.Expand(margin.X),
            c.Y.Expand(margin.Y),
            c.Z.Expand(margin.Z))
    member inline c.Clamp(p : Vector3i) = 
        Vector3i(
            c.X.Clamp p.X,
            c.Y.Clamp p.Y,
            c.Z.Clamp p.Z)
    override i.ToString() = $"%A{i.Min} to %A{i.Max}"
    static member Zero = Range3i(Vector3i.Zero, Vector3i.Zero)
    static member ZeroToOne = Range3i(Vector3i.Zero, Vector3i.One)
    static member inline (+) (a: Range3i, c) = Range3i(a.X + c, a.Y + c, a.Z + c)
    static member inline (-) (a: Range3i, c) = Range3i(a.X - c, a.Y - c, a.Z - c)
    static member inline (*) (a: Range3i, c) = Range3i(a.X * c, a.Y * c, a.Z * c)
    static member inline (/) (a: Range3i, c) = Range3i(a.X / c, a.Y / c, a.Z / c)
    static member inline (+) (a: Range3i, v : Vector3i) = Range3i(a.X + v.X, a.Y + v.Y, a.Z + v.Z)
    static member inline (-) (a: Range3i, v : Vector3i) = Range3i(a.X - v.X, a.Y - v.Y, a.Z - v.Z)
    static member inline (*) (a: Range3i, v : Vector3i) = Range3i(a.X * v.X, a.Y * v.Y, a.Z * v.Z)
    static member inline (/) (a: Range3i, v : Vector3i) = Range3i(a.X / v.X, a.Y / v.Y, a.Z / v.Z)
    static member inline Point(point : Vector3i) = Range3i(point, point)
    static member inline Sized(min : Vector3i, size : Vector3i) = 
        Range3i(min, min + size)
    static member inline Centered(center : Vector3i, size : Vector3i) = 
        Range3i.Sized(center - size / 2, size)
    static member inline Intersection(a : Range3i, b : Range3i) = 
        Range3i(
            Rangei.Intersection(a.X, b.X), 
            Rangei.Intersection(a.Y, b.Y),
            Rangei.Intersection(a.Z, b.Z))
    static member inline Union(a : Range3i, b : Range3i) =         
        Range3i(
            Rangei.Union(a.X, b.X), 
            Rangei.Union(a.Y, b.Y),
            Rangei.Union(a.Z, b.Z))


================================================
FILE: samples/Garnet.Numerics/Vectors.fs
================================================
namespace Garnet.Numerics

open System
open System.Numerics

// Int32 vector types

[<Struct>]
type Vector2i = 
    val X : int
    val Y : int
    new(x, y) = { X = x; Y = y }
    member c.ToVector2() = Vector2(float32 c.X, float32 c.Y)
    override v.ToString() = $"%A{v.X} %A{v.Y}"
    static member Zero = Vector2i(0, 0)
    static member One = Vector2i(1, 1)
    static member UnitX = Vector2i(1, 0)
    static member UnitY = Vector2i(0, 1)
    static member inline Dot(a: Vector2i, b: Vector2i) = a.X * b.X + a.Y * b.Y
    static member inline (~-) (v: Vector2i) = Vector2i(-v.X, -v.Y)
    static member inline (+) (a: Vector2i, c) = Vector2i(a.X + c, a.Y + c) 
    static member inline (-) (a: Vector2i, c) = Vector2i(a.X - c, a.Y - c)
    static member inline (*) (a: Vector2i, c) = Vector2i(a.X * c, a.Y * c) 
    static member inline (/) (a: Vector2i, c) = Vector2i(a.X / c, a.Y / c)
    static member inline (>>>) (a: Vector2i, c) = Vector2i(a.X >>> c, a.Y >>> c)
    static member inline (<<<) (a: Vector2i, c) = Vector2i(a.X <<< c, a.Y <<< c)
    static member inline (&&&) (a: Vector2i, c) = Vector2i(a.X &&& c, a.Y &&& c)
    static member inline (+) (a: Vector2i, b: Vector2i) = Vector2i(a.X + b.X, a.Y + b.Y)
    static member inline (-) (a: Vector2i, b: Vector2i) = Vector2i(a.X - b.X, a.Y - b.Y)
    static member inline (*) (a: Vector2i, b: Vector2i) = Vector2i(a.X * b.X, a.Y * b.Y) 
    static member inline (/) (a: Vector2i, b: Vector2i) = Vector2i(a.X / b.X, a.Y / b.Y) 
    static member inline Perpendicular(v : Vector2i) = Vector2i(-v.Y, v.X)    
    static member inline FromVector2(v : Vector2) = Vector2i(int v.X, int v.Y)

[<Struct>]
type Vector3i = 
    val X : int
    val Y : int
    val Z : int
    new(x, y, z) = { X = x; Y = y; Z = z }
    member c.ToVector3() = Vector3(float32 c.X, float32 c.Y, float32 c.Z)
    override v.ToString() = $"%A{v.X} %A{v.Y} %A{v.Z}"
    static member Zero = Vector3i(0, 0, 0)
    static member One = Vector3i(1, 1, 1)
    static member UnitX = Vector3i(1, 0, 0)
    static member UnitY = Vector3i(0, 1, 0)
    static member UnitZ = Vector3i(0, 0, 1)
    static member inline Dot(a: Vector3i, b: Vector3i) = a.X * b.X + a.Y * b.Y + a.Z * b.Z
    static member inline (~-) (v: Vector3i) = Vector3i(-v.X, -v.Y, -v.Z)
    static member inline (+) (a: Vector3i, c) = Vector3i(a.X + c, a.Y + c, a.Z + c) 
    static member inline (-) (a: Vector3i, c) = Vector3i(a.X - c, a.Y - c, a.Z - c)
    static member inline (*) (a: Vector3i, c) = Vector3i(a.X * c, a.Y * c, a.Z * c) 
    static member inline (/) (a: Vector3i, c) = Vector3i(a.X / c, a.Y / c, a.Z / c)
    static member inline (>>>) (a: Vector3i, c) = Vector3i(a.X >>> c, a.Y >>> c, a.Z >>> c)
    static member inline (<<<) (a: Vector3i, c) = Vector3i(a.X <<< c, a.Y <<< c, a.Z <<< c)
    static member inline (&&&) (a: Vector3i, c) = Vector3i(a.X &&& c, a.Y &&& c, a.Z &&& c)
    static member inline (+) (a: Vector3i, b: Vector3i) = Vector3i(a.X + b.X, a.Y + b.Y, a.Z + b.Z)
    static member inline (-) (a: Vector3i, b: Vector3i) = Vector3i(a.X - b.X, a.Y - b.Y, a.Z - b.Z)
    static member inline (*) (a: Vector3i, b: Vector3i) = Vector3i(a.X * b.X, a.Y * b.Y, a.Z * b.Z) 
    static member inline (/) (a: Vector3i, b: Vector3i) = Vector3i(a.X / b.X, a.Y / b.Y, a.Z / b.Z) 
    static member inline FromVector3(v : Vector3) = Vector3i(int v.X, int v.Y, int v.Z)

// Float vector types

[<AutoOpen>]
module Vector2 =
    type Vector2 with
        static member FromRadians(a) =
            Vector2(cos a, sin a)

        static member FromDegrees(a) = 
            Vector2.FromRadians(a * MathF.PI / 180.0f)
        
        static member inline Rotate(r : Vector2, a : Vector2) = 
            Vector2(a.X * r.X - a.Y * r.Y, a.X * r.Y + a.Y * r.X)

        static member inline Perpendicular(v : Vector2) =
            Vector2(-v.Y, v.X)

        member v.GetRadians() =
            atan2 v.Y v.X

        member v.DivideOrZero(c) =
            if abs c > 1e-7f then v * (1.0f / c) else Vector2.Zero

        member v.NormalizeOrZero() =
            v.DivideOrZero(v.Length())

        member v.TruncateOrZero(maxLength) =
            let lengthSqr = v.LengthSquared()
            if lengthSqr <= maxLength * maxLength then v
            else v.NormalizeOrZero() * maxLength
        
        member v.GetPerpendicular() = 
            Vector2(-v.Y, v.X)    

        member v.Rotate(r : Vector2) = 
            Vector2(v.X * r.X - v.Y * r.Y, v.X * r.Y + v.Y * r.X)

        member v.InverseRotate(r : Vector2) = 
            Vector2(v.X * r.X + v.Y * r.Y, v.Y * r.X - v.X * r.Y)

        /// Rotates towards a target vector
        /// maxRotation is a unit-length direction vector relative to X axis
        member v.RotateTowards(target : Vector2, maxRotation : Vector2) =
            let dot = Vector2.Dot(v, target)
            let rotDot = Vector2.Dot(maxRotation, Vector2.UnitX)
            if dot >= rotDot then target
            else
                let cross = Vector3.Cross(Vector3(v, 0.0f), Vector3(target, 0.0f))
                if cross.Z > 0.0f then v.Rotate(maxRotation)
                else v.InverseRotate(maxRotation)

        member v.Round() =
            Vector2(floor (v.X + 0.5f), floor (v.Y + 0.5f))

        member v.RoundToInt() =
            let v = v.Round()
            Vector2i(int v.X, int v.Y)

        member v.IsInTriangle(p0 : Vector2, p1 : Vector2, p2 : Vector2) =
            let a = 0.5f * (-p1.Y * p2.X + p0.Y * (-p1.X + p2.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y)
            let sign = if a < 0.0f then -1.0f else 1.0f;
            let s = (p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * v.X + (p0.X - p2.X) * v.Y) * sign
            let t = (p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * v.X + (p1.X - p0.X) * v.Y) * sign
            //s > 0.0f && t > 0.0f && (s + t) < 2.0f * A * sign
            s >= 0.0f && t >= 0.0f && (s + t) <= 2.0f * a * sign

[<AutoOpen>]
module Vector3 =
    type Vector3 with
        member v.ToVector2() =
            Vector2(v.X, v.Y)


================================================
FILE: samples/Garnet.Processor/Args.fs
================================================
namespace Garnet.Processor

open Argu

[<CliPrefix(CliPrefix.Dash)>]
type PackArgs =
    | Input of string
    | Output of string
    | Compression of int
    | Ignore of string list
    | Recurse
    interface IArgParserTemplate with
        member this.Usage =
            match this with
            | Input _ -> "Input directory to pack"
            | Output _ -> "Output packed file"
            | Compression _ -> "Compression level, 0 for none"
            | Recurse -> "Include subdirectories recursively"
            | Ignore _ -> "Ignored file pattern, e.g. *.dat"

and ProcessorArgs =
    | [<CliPrefix(CliPrefix.None)>] Pack of ParseResults<PackArgs>
    interface IArgParserTemplate with
        member this.Usage =
            match this with
            | Pack _ -> "Pack files into a single archive file."


================================================
FILE: samples/Garnet.Processor/Garnet.Processor.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>garnet</ToolCommandName>
  </PropertyGroup>

  <PropertyGroup>
    <Description>Asset processor for games.</Description>
    <PackageTags>asset game</PackageTags>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Args.fs" />
    <Compile Include="PackUtility.fs" />
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Argu" Version="6.1.1" />
  </ItemGroup>

</Project>


================================================
FILE: samples/Garnet.Processor/PackUtility.fs
================================================
namespace Garnet.Processor

open System.Diagnostics
open System.IO
open System.IO.Compression
open System.Security.Cryptography
open System.Text.RegularExpressions
open Argu

module PackUtility =
    let getHash file =
        if File.Exists(file) then
            use fs = File.OpenRead(file)
            use sha = SHA1.Create()
            sha.ComputeHash(fs)
            |> Array.map (fun b -> b.ToString("x2"))
            |> String.concat ""
        else ""
        
    let getGlobRegex (globs : string list) =
        if globs.Length = 0 then Regex("x^")
        else
            let pattern =
                globs
                |> List.map (fun str -> "^" + Regex.Escape(str).Replace(@"\*", ".*").Replace(@"\?", ".") + "$")
                |> String.concat "|"
            Regex(pattern, RegexOptions.IgnoreCase ||| RegexOptions.Singleline)

    let run (args : ParseResults<PackArgs>) =
        let inputPath = Path.GetFullPath(args.GetResult <@ PackArgs.Input @>)
        let outputPath = Path.GetFullPath(args.GetResult <@ PackArgs.Output @>)
        let recurse = args.Contains <@ Recurse @>
        let ignoreRegex = getGlobRegex (args.GetResult(<@ Ignore @>, defaultValue = List.empty))
        let level =
            let compression = args.GetResult (<@ Compression @>, defaultValue = 0)
            if compression = 0 then CompressionLevel.NoCompression else CompressionLevel.Optimal
        printfn $"Input path: {inputPath}"
        // Optionally insert version into output path
        let outputPath =
            let key = "{version}"
            if outputPath.Contains(key) then
                let exeFile = Directory.EnumerateFiles(inputPath, "*.exe") |> Seq.head
                let info = FileVersionInfo.GetVersionInfo(exeFile)
                outputPath.Replace(key, info.ProductVersion)
            else outputPath
        printfn $"Output path: {outputPath}"
        // Create folder
        let outputDir = Path.GetDirectoryName(outputPath)
        Directory.CreateDirectory(outputDir) |> ignore
        // Write to temp path
        let tempPath = outputPath + ".temp"
        let _ =
            use zip = ZipFile.Open(tempPath, ZipArchiveMode.Create)
            let options = EnumerationOptions()
            options.RecurseSubdirectories <- recurse
            for file in Directory.EnumerateFiles(inputPath, "*.*", options) do
                let fullPath = Path.GetFullPath(file)
                let name = Path.GetRelativePath(inputPath, fullPath)
                if ignoreRegex.IsMatch(name) then
                    printfn $"Ignoring {name}"
                else
                    printfn $"Adding {name}"
                    zip.CreateEntryFromFile(fullPath, name, level) |> ignore
        // Calc hash of old and new files
        let oldHash = getHash outputPath
        let newHash = getHash tempPath
        printfn $"Old hash: {oldHash}"
        printfn $"New hash: {newHash}"
        // Overwrite only if hash differs
        if newHash = oldHash then
            printfn $"Hashes match, skipping write"
            File.Delete(tempPath)
        else
            File.Move(tempPath, outputPath, overwrite = true)
            let info = FileInfo(outputPath)
            printfn $"{info.Length} bytes written to {outputPath}"
    

================================================
FILE: samples/Garnet.Processor/Program.fs
================================================
open Argu
open Garnet.Processor

[<EntryPoint>]
let main argv =
    let parser = ArgumentParser.Create<ProcessorArgs>()
    try
        let args = parser.ParseCommandLine(inputs = argv, raiseOnUsage = true)
        match args.GetSubCommand() with
        | Pack args -> PackUtility.run args
    with e -> printfn $"%s{e.Message}"
    0

================================================
FILE: samples/Garnet.Samples.Assorted/Extensions.fs
================================================
namespace Garnet.Samples.Assorted

open System
open System.Numerics
open Veldrid
open Garnet.Composition
open Garnet.Graphics
open Garnet.Input
open Garnet.Numerics

[<AutoOpen>]
module Extensions =
    type Container with
        member c.AddPixelCoordinateCamera(cameraId) =
            c.On<Draw> <| fun e ->
                // Set projection to use pixel coords
                let cameras = c.Get<CameraSet>()
                cameras.[cameraId].ProjectionTransform <- Matrix4x4.CreateOrthographicOffCenter(
                    0.0f, float32 e.ViewSize.X, float32 e.ViewSize.Y, 0.0f, -1.0f, 1.0f)
                
        member c.AddEscapeToClose() =
            c.On<KeyDown> <| fun e ->
                match e.KeyCode with
                | Key.Escape -> c.Get<WindowRenderer>().Close()
                | _ -> ()

        static member Run(register : Container -> IDisposable) =
            let c = Container()
            use sys = register c
            c.RunLoop()



================================================
FILE: samples/Garnet.Samples.Assorted/Garnet.Samples.Assorted.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>Link</TrimMode>
  </PropertyGroup>

  <ItemGroup>
    <None Include="assets\textures\hex.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\textures\triangle.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\textures\multicolor-square.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\textures\pixel-operator-regular-12.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\color.frag">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\color.frag.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\color.vert">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\color.vert.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\texture-color.frag">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\texture-color.frag.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\texture-color.vert">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\shaders\texture-color.vert.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\fonts\pixel-operator-regular-12.font.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <Compile Include="Extensions.fs" />
    <Compile Include="SpriteDrawing.fs" />
    <Compile Include="TextDrawing.fs" />
    <Compile Include="OffscreenDrawing.fs" />
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\Garnet\Garnet.fsproj" />
    <ProjectReference Include="..\Garnet.Toolkit\Garnet.Toolkit.fsproj" />
  </ItemGroup>

</Project>


================================================
FILE: samples/Garnet.Samples.Assorted/OffscreenDrawing.fs
================================================
module Garnet.Samples.Assorted.OffscreenDrawing

open System
open System.Numerics
open Garnet.Composition
open Garnet.Graphics
open Garnet.Numerics
open Veldrid

module Resources =
    let colorTextureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {
        VertexShader = "shaders/texture-color.vert"
        FragmentShader = "shaders/texture-color.frag"
        }

    let spritePipeline = {
        Blend = Blend.Alpha
        Filtering = Filtering.Point
        ShaderSet = colorTextureShaderSet
        Texture = "textures/multicolor-square.png"
        }

    let spriteLayer = {
        LayerId = 0
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = spritePipeline
        }

    let lightPipeline = {
        Blend = Blend.Alpha
        Filtering = Filtering.Point
        ShaderSet = colorTextureShaderSet
        Texture = "textures/hex.png"
        }

    let lightLayer = {
        LayerId = 0
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = lightPipeline
        }

type MainScene(ren : SpriteRenderer) =
    member c.Renderer = ren
    
type LightScene(ren : SpriteRenderer) =
    member c.Renderer = ren
    
type Container with
    member c.AddSpriteLightingDrawing() =
        let device = c.Get<GraphicsDevice>()
        let shaders = c.Get<ShaderSetCache>()
        let cache = c.Get<ResourceCache>()
        let lightTarget =
            let blend = BlendStateDescription(AttachmentStates = [|
                // Multiply destination (main scene) by source (light map)
                BlendAttachmentDescription(
                    BlendEnabled = true,
                    SourceColorFactor = BlendFactor.Zero,
                    DestinationColorFactor = BlendFactor.SourceColor,
                    ColorFunction = BlendFunction.Add,
                    SourceAlphaFactor = BlendFactor.Zero,
                    DestinationAlphaFactor = BlendFactor.SourceAlpha,
                    AlphaFunction = BlendFunction.Add
                    )
                |])
            let shaderSet = shaders.GetOrCreate(device, Resources.colorTextureShaderSet.Untyped, cache)
            let target = new RenderTarget(device, shaderSet, Filtering.Linear, blend)
            target.Background <- RgbaFloat.Clear
            target
        let mainSprites = new SpriteRenderer(device, shaders, cache)
        let lightSprites = new SpriteRenderer(device, shaders, cache)
        c.Set(MainScene(mainSprites))
        c.Set(LightScene(lightSprites))
        Disposable.Create [
            mainSprites :> IDisposable
            lightSprites :> IDisposable
            lightTarget :> IDisposable
            c.On<Draw> <| fun e ->
                lightTarget.Width <- e.ViewSize.X
                lightTarget.Height <- e.ViewSize.Y
            c.On<PushDrawCommands> <| fun _ ->
                let context = c.Get<RenderContext>()
                let cameras = c.Get<CameraSet>()
                // First draw scene normally
                mainSprites.Draw(context, cameras)
                // Next draw lights to offscreen buffer, then draw to main buffer
                // (with multiply blending)
                lightTarget.BeginDraw(context)
                lightSprites.Draw(context, cameras)
                lightTarget.EndDraw(context)
            ]

let run() =
    Container.Run <| fun c ->
        c.Set {
            WindowSettings.Default with
                Background = RgbaFloat.Blue.MultiplyRgb(0.1f)
            }
        Disposable.Create [
            c.AddDefaultSystems()
            c.AddPixelCoordinateCamera(0)
            c.AddEscapeToClose()
            c.AddSpriteLightingDrawing()
            c.On<Draw> <| fun e ->
                let rect = Range2.Sized(Vector2.Zero, e.ViewSize.ToVector2()) 
                // Draw sprites
                let sprites = c.Get<MainScene>().Renderer
                let verts = sprites.GetVertices(Resources.spriteLayer)
                verts.DrawQuad(rect, Range2.ZeroToOne, RgbaFloat.White)
                // Draw lights
                let lights = c.Get<LightScene>().Renderer
                let verts = lights.GetVertices(Resources.lightLayer)
                verts.DrawQuad(rect, Range2.ZeroToOne, RgbaFloat.White)
            ]


================================================
FILE: samples/Garnet.Samples.Assorted/Program.fs
================================================
open Garnet.Composition
open Garnet.Samples.Assorted

[<EntryPoint>]
let main argv =
    //SpriteDrawing.run()
    //TextDrawing.run()
    OffscreenDrawing.run()
    0

================================================
FILE: samples/Garnet.Samples.Assorted/SpriteDrawing.fs
================================================
module Garnet.Samples.Assorted.SpriteDrawing

open System
open System.Numerics
open Garnet.Composition
open Garnet.Graphics
open Garnet.Numerics
open Veldrid

module Resources =
    let colorTextureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {
        VertexShader = "shaders/texture-color.vert"
        FragmentShader = "shaders/texture-color.frag"
        }

    let spritePipeline = {
        Blend = Blend.Alpha
        Filtering = Filtering.Point
        ShaderSet = colorTextureShaderSet
        Texture = "textures/multicolor-square.png"
        }

    let spriteLayer = {
        LayerId = 0
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = spritePipeline
        }

let run() =
    Container.Run <| fun c ->
        c.Set {
            WindowSettings.Default with
                Background = RgbaFloat.Blue.MultiplyRgb(0.1f)
            }
        Disposable.Create [
            c.AddDefaultSystems()
            c.AddPixelCoordinateCamera(0)
            c.AddEscapeToClose()
            c.On<Draw> <| fun _ ->
                let sprites = c.Get<SpriteRenderer>()
                let verts = sprites.GetVertices(Resources.spriteLayer)
                let size = Vector2(80.0f, 40.0f)
                // Identical quads
                verts.DrawQuad(
                    Range2.Centered(Vector2(50.0f, 50.0f), size),
                    Range2.ZeroToOne,
                    RgbaFloat.White)
                verts.DrawQuad {
                    ColorTextureSprite.Default with
                        Center = Vector2(150.0f, 50.0f)
                        Size = size
                        }
                // Rotated - Since we're using pixel coords, positive rotation is clockwise
                verts.DrawQuad {
                    ColorTextureSprite.Default with
                        Center = Vector2(250.0f, 50.0f)
                        Size = size
                        Rotation = Vector2.FromDegrees(90.0f)
                        }
            ]



================================================
FILE: samples/Garnet.Samples.Assorted/TextDrawing.fs
================================================
module Garnet.Samples.Assorted.TextDrawing

open Veldrid
open Garnet.Composition
open Garnet.Graphics
open Garnet.Numerics

module Resources =
    let font = "fonts/pixel-operator-regular-12.font.json"
    let fontTexture = "textures/pixel-operator-regular-12.png"
    
    let textureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {
        VertexShader = "shaders/texture-color.vert"
        FragmentShader = "shaders/texture-color.frag"
        }

    let colorShaderSet : ShaderSetDescriptor<PositionColorVertex> = {
        VertexShader = "shaders/color.vert"
        FragmentShader = "shaders/color.frag"
        }

    let textPipeline = {
        Blend = Blend.Alpha
        // Use point filtering since we plan to scale the font and want a pixelated appearance
        Filtering = Filtering.Point
        ShaderSet = textureShaderSet
        Texture = "textures/pixel-operator-regular-12.png"
        }

    let panelPipeline = {
        Blend = Blend.Alpha
        Filtering = Filtering.Linear
        ShaderSet = colorShaderSet
        Texture = ""
        }

    let textLayer = {
        LayerId = 1
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = textPipeline
        }

    let panelLayer = {
        LayerId = 0
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = panelPipeline
        }

let run() =
    Container.Run <| fun c ->
        c.Set {
            WindowSettings.Default with Width = 640; Height = 480 
        }
        Disposable.Create [
            c.AddDefaultSystems()
            c.AddPixelCoordinateCamera(0)
            c.AddEscapeToClose()
            c.On<Draw> <| fun e ->
                let font = c.LoadJsonFont(Resources.font, Resources.fontTexture)
                let sprites = c.Get<SpriteRenderer>()
                let textVerts = sprites.GetVertices(Resources.textLayer)
                let panelVerts = sprites.GetVertices(Resources.panelLayer)
                let baseBlock = {
                    TextBlock.Default with
                        Scale = 3
                        Bounds = Range2i.Sized(Vector2i.Zero, e.ViewSize)
                        }
                // Draw text in the corners
                textVerts.DrawText(font, {
                    baseBlock with
                        Text = "Upper left"
                        Align = Align.Left
                        Valign = Valign.Top
                        })
                textVerts.DrawText(font, {
                    baseBlock with
                        Text = "Bottom right"
                        Align = Align.Right
                        Valign = Valign.Bottom
                        })
                // Draw text in the center within a panel
                let block = {
                    baseBlock with
                        Text = "Multiple Lines\nCenter"
                        Align = Align.Center
                        Valign = Valign.Center
                        }
                let bounds = font.Measure(block).ToRange2()
                textVerts.DrawText(font, block)
                panelVerts.DrawQuad(bounds, RgbaFloat.Red.MultiplyAlpha(0.5f))
                // Test wrapping
                let scale = 2
                let text = "Title\n\nLine 1\nLine 2 should wrap to next line\nLine 3"
                let size = font.Measure(text) * scale
                let bounds = Range2i.Sized(Vector2i(0, e.ViewSize.Y - size.Y), Vector2i(size.X - 200, size.Y))
                let block = {
                    baseBlock with
                        Scale = scale
                        Bounds = bounds
                        Text = text
                        Align = Align.Left
                        Valign = Valign.Center
                        Wrapping = TextWrapping.WordWrap
                        }
                textVerts.DrawText(font, block)
                panelVerts.DrawQuad(bounds.ToRange2(), RgbaFloat.Red.MultiplyAlpha(0.5f))
            ]



================================================
FILE: samples/Garnet.Samples.Assorted/assets/fonts/pixel-operator-regular-12.font.json
================================================
{
  "family": "Pixel Operator",
  "style": "Regular",
  "size": 12,
  "height": 17,
  "chars": [
    {
      "code": " ",
      "width": 4,
      "offsetX": 0,
      "offsetY": 13,
      "rectX": 1,
      "rectY": 12,
      "rectWidth": 0,
      "rectHeight": 0
    },
    {
      "code": "!",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 2,
      "rectY": 3,
      "rectWidth": 1,
      "rectHeight": 9
    },
    {
      "code": "\"",
      "width": 7,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 4,
      "rectY": 3,
      "rectWidth": 3,
      "rectHeight": 3
    },
    {
      "code": "#",
      "width": 8,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 8,
      "rectY": 3,
      "rectWidth": 6,
      "rectHeight": 9
    },
    {
      "code": "$",
      "width": 7,
      "offsetX": 1,
      "offsetY": 2,
      "rectX": 15,
      "rectY": 1,
      "rectWidth": 5,
      "rectHeight": 13
    },
    {
      "code": "%",
      "width": 9,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 21,
      "rectY": 3,
      "rectWidth": 7,
      "rectHeight": 9
    },
    {
      "code": "&",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 29,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "'",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 35,
      "rectY": 3,
      "rectWidth": 1,
      "rectHeight": 3
    },
    {
      "code": "(",
      "width": 7,
      "offsetX": 3,
      "offsetY": 4,
      "rectX": 37,
      "rectY": 3,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": ")",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 41,
      "rectY": 3,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "*",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 45,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 5
    },
    {
      "code": "+",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 51,
      "rectY": 5,
      "rectWidth": 5,
      "rectHeight": 5
    },
    {
      "code": ",",
      "width": 5,
      "offsetX": 1,
      "offsetY": 12,
      "rectX": 57,
      "rectY": 11,
      "rectWidth": 2,
      "rectHeight": 3
    },
    {
      "code": "-",
      "width": 6,
      "offsetX": 1,
      "offsetY": 8,
      "rectX": 60,
      "rectY": 7,
      "rectWidth": 4,
      "rectHeight": 1
    },
    {
      "code": ".",
      "width": 5,
      "offsetX": 2,
      "offsetY": 12,
      "rectX": 65,
      "rectY": 11,
      "rectWidth": 1,
      "rectHeight": 1
    },
    {
      "code": "/",
      "width": 5,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 67,
      "rectY": 3,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "0",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 71,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "1",
      "width": 7,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 77,
      "rectY": 3,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "2",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 81,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "3",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 87,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "4",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 93,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "5",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 99,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "6",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 105,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "7",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 111,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "8",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 117,
      "rectY": 3,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "9",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 1,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": ":",
      "width": 5,
      "offsetX": 2,
      "offsetY": 6,
      "rectX": 7,
      "rectY": 17,
      "rectWidth": 1,
      "rectHeight": 7
    },
    {
      "code": ";",
      "width": 5,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 9,
      "rectY": 17,
      "rectWidth": 2,
      "rectHeight": 9
    },
    {
      "code": "<",
      "width": 5,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 12,
      "rectY": 17,
      "rectWidth": 3,
      "rectHeight": 5
    },
    {
      "code": "=",
      "width": 6,
      "offsetX": 1,
      "offsetY": 7,
      "rectX": 16,
      "rectY": 18,
      "rectWidth": 4,
      "rectHeight": 3
    },
    {
      "code": ">",
      "width": 5,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 21,
      "rectY": 17,
      "rectWidth": 3,
      "rectHeight": 5
    },
    {
      "code": "?",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 25,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "@",
      "width": 9,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 31,
      "rectY": 15,
      "rectWidth": 7,
      "rectHeight": 9
    },
    {
      "code": "A",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 39,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "B",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 45,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "C",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 51,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "D",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 57,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "E",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 63,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "F",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 69,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "G",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 75,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "H",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 81,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "I",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 87,
      "rectY": 15,
      "rectWidth": 1,
      "rectHeight": 9
    },
    {
      "code": "J",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 89,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "K",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 95,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "L",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 101,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "M",
      "width": 9,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 107,
      "rectY": 15,
      "rectWidth": 7,
      "rectHeight": 9
    },
    {
      "code": "N",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 115,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "O",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 121,
      "rectY": 15,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "P",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 1,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "Q",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 7,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "R",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 13,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "S",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 19,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "T",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 25,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "U",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 31,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "V",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 37,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "W",
      "width": 9,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 43,
      "rectY": 27,
      "rectWidth": 7,
      "rectHeight": 9
    },
    {
      "code": "X",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 51,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "Y",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 57,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "Z",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 63,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "[",
      "width": 7,
      "offsetX": 3,
      "offsetY": 4,
      "rectX": 69,
      "rectY": 27,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "\\",
      "width": 5,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 73,
      "rectY": 27,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "]",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 77,
      "rectY": 27,
      "rectWidth": 3,
      "rectHeight": 9
    },
    {
      "code": "^",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 81,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 3
    },
    {
      "code": "_",
      "width": 5,
      "offsetX": 0,
      "offsetY": 14,
      "rectX": 87,
      "rectY": 37,
      "rectWidth": 5,
      "rectHeight": 1
    },
    {
      "code": "`",
      "width": 5,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 93,
      "rectY": 27,
      "rectWidth": 2,
      "rectHeight": 2
    },
    {
      "code": "a",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 96,
      "rectY": 29,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "b",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 102,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "c",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 108,
      "rectY": 29,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "d",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 114,
      "rectY": 27,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "e",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 120,
      "rectY": 29,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "f",
      "width": 6,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 1,
      "rectY": 39,
      "rectWidth": 4,
      "rectHeight": 9
    },
    {
      "code": "g",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 6,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "h",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 12,
      "rectY": 39,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "i",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 18,
      "rectY": 39,
      "rectWidth": 1,
      "rectHeight": 9
    },
    {
      "code": "j",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 20,
      "rectY": 39,
      "rectWidth": 5,
      "rectHeight": 11
    },
    {
      "code": "k",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 26,
      "rectY": 39,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "l",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 32,
      "rectY": 39,
      "rectWidth": 2,
      "rectHeight": 9
    },
    {
      "code": "m",
      "width": 9,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 35,
      "rectY": 41,
      "rectWidth": 7,
      "rectHeight": 7
    },
    {
      "code": "n",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 43,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "o",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 49,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "p",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 55,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "q",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 61,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "r",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 67,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "s",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 73,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "t",
      "width": 6,
      "offsetX": 1,
      "offsetY": 5,
      "rectX": 79,
      "rectY": 40,
      "rectWidth": 4,
      "rectHeight": 8
    },
    {
      "code": "u",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 84,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "v",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 90,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "w",
      "width": 9,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 96,
      "rectY": 41,
      "rectWidth": 7,
      "rectHeight": 7
    },
    {
      "code": "x",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 104,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "y",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 110,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 9
    },
    {
      "code": "z",
      "width": 7,
      "offsetX": 1,
      "offsetY": 6,
      "rectX": 116,
      "rectY": 41,
      "rectWidth": 5,
      "rectHeight": 7
    },
    {
      "code": "{",
      "width": 7,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 122,
      "rectY": 39,
      "rectWidth": 4,
      "rectHeight": 9
    },
    {
      "code": "|",
      "width": 5,
      "offsetX": 2,
      "offsetY": 4,
      "rectX": 1,
      "rectY": 51,
      "rectWidth": 1,
      "rectHeight": 9
    },
    {
      "code": "}",
      "width": 7,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 3,
      "rectY": 51,
      "rectWidth": 4,
      "rectHeight": 9
    },
    {
      "code": "~",
      "width": 8,
      "offsetX": 1,
      "offsetY": 4,
      "rectX": 8,
      "rectY": 51,
      "rectWidth": 6,
      "rectHeight": 2
    }
  ]
}

================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/color.frag
================================================
#version 450
layout(location = 0) in vec4 fsin_color;
layout(location = 0) out vec4 fsout_color;
void main()
{
    fsout_color = fsin_color;
}

================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/color.frag.hlsl.bytes
================================================
static float4 _9;
static float4 _11;

struct SPIRV_Cross_Input
{
    float4 _11 : TEXCOORD0;
};

struct SPIRV_Cross_Output
{
    float4 _9 : SV_Target0;
};

void frag_main()
{
    _9 = _11;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _11 = stage_input._11;
    frag_main();
    SPIRV_Cross_Output stage_output;
    stage_output._9 = _9;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/color.vert
================================================
#version 450
layout(set = 0, binding = 0) uniform ProjectionBuffer
{
    mat4 Projection;
};
layout(set = 0, binding = 1) uniform ViewBuffer
{
    mat4 View;
};
layout(set = 1, binding = 0) uniform WorldBuffer
{
    mat4 World;
};
layout(location = 0) in vec3 Position;
layout(location = 1) in vec4 Color;
layout(location = 0) out vec4 fsin_Color;
void main()
{
    vec4 worldPosition = World * vec4(Position, 1);
    vec4 viewPosition = View * worldPosition;
    vec4 clipPosition = Projection * viewPosition;
    gl_Position = clipPosition;
    fsin_Color = Color;
}


================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/color.vert.hlsl.bytes
================================================
cbuffer _11_13 : register(b2)
{
    row_major float4x4 _13_m0 : packoffset(c0);
};

cbuffer _30_32 : register(b1)
{
    row_major float4x4 _32_m0 : packoffset(c0);
};

cbuffer _38_40 : register(b0)
{
    row_major float4x4 _40_m0 : packoffset(c0);
};


static float4 gl_Position;
static float3 _21;
static float4 _54;
static float4 _56;

struct SPIRV_Cross_Input
{
    float3 _21 : TEXCOORD0;
    float4 _56 : TEXCOORD1;
};

struct SPIRV_Cross_Output
{
    float4 _54 : TEXCOORD0;
    float4 gl_Position : SV_Position;
};

void vert_main()
{
    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);
    _54 = _56;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _21 = stage_input._21;
    _56 = stage_input._56;
    vert_main();
    SPIRV_Cross_Output stage_output;
    stage_output.gl_Position = gl_Position;
    stage_output._54 = _54;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag
================================================
#version 450
layout(location = 0) in vec2 fsin_texCoords;
layout(location = 1) in vec4 fsin_color;
layout(location = 0) out vec4 fsout_color;
layout(set = 1, binding = 1) uniform texture2D SurfaceTexture;
layout(set = 1, binding = 2) uniform sampler SurfaceSampler;
void main()
{
    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);
    fsout_color =  texColor * fsin_color;
}

================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag.hlsl.bytes
================================================
Texture2D<float4> _12 : register(t0);
SamplerState _16 : register(s0);

static float2 _22;
static float4 _26;
static float4 _29;

struct SPIRV_Cross_Input
{
    float2 _22 : TEXCOORD0;
    float4 _29 : TEXCOORD1;
};

struct SPIRV_Cross_Output
{
    float4 _26 : SV_Target0;
};

void frag_main()
{
    _26 = _12.Sample(_16, _22) * _29;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _22 = stage_input._22;
    _29 = stage_input._29;
    frag_main();
    SPIRV_Cross_Output stage_output;
    stage_output._26 = _26;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert
================================================
#version 450
layout(set = 0, binding = 0) uniform ProjectionBuffer
{
    mat4 Projection;
};
layout(set = 0, binding = 1) uniform ViewBuffer
{
    mat4 View;
};
layout(set = 1, binding = 0) uniform WorldBuffer
{
    mat4 World;
};
layout(location = 0) in vec3 Position;
layout(location = 1) in vec2 TexCoords;
layout(location = 2) in vec4 Color;
layout(location = 0) out vec2 fsin_texCoords;
layout(location = 1) out vec4 fsin_Color;
void main()
{
    vec4 worldPosition = World * vec4(Position, 1);
    vec4 viewPosition = View * worldPosition;
    vec4 clipPosition = Projection * viewPosition;
    gl_Position = clipPosition;
    fsin_texCoords = TexCoords;
    fsin_Color = Color;
}


================================================
FILE: samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert.hlsl.bytes
================================================
cbuffer _11_13 : register(b2)
{
    row_major float4x4 _13_m0 : packoffset(c0);
};

cbuffer _30_32 : register(b1)
{
    row_major float4x4 _32_m0 : packoffset(c0);
};

cbuffer _38_40 : register(b0)
{
    row_major float4x4 _40_m0 : packoffset(c0);
};


static float4 gl_Position;
static float3 _21;
static float2 _56;
static float2 _58;
static float4 _60;
static float4 _62;

struct SPIRV_Cross_Input
{
    float3 _21 : TEXCOORD0;
    float2 _58 : TEXCOORD1;
    float4 _62 : TEXCOORD2;
};

struct SPIRV_Cross_Output
{
    float2 _56 : TEXCOORD0;
    float4 _60 : TEXCOORD1;
    float4 gl_Position : SV_Position;
};

void vert_main()
{
    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);
    _56 = _58;
    _60 = _62;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _21 = stage_input._21;
    _58 = stage_input._58;
    _62 = stage_input._62;
    vert_main();
    SPIRV_Cross_Output stage_output;
    stage_output.gl_Position = gl_Position;
    stage_output._56 = _56;
    stage_output._60 = _60;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.CSharp/Garnet.Samples.CSharp.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>Link</TrimMode>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\Garnet\Garnet.fsproj" />
    <ProjectReference Include="..\Garnet.Toolkit\Garnet.Toolkit.fsproj" />
  </ItemGroup>

</Project>


================================================
FILE: samples/Garnet.Samples.CSharp/Program.cs
================================================
using Garnet.Composition;

var c = new Container();

using (c.AddDefaultSystems())
{
    c.RunLoop();
}


================================================
FILE: samples/Garnet.Samples.Flocking/Debug.fs
================================================
namespace Garnet.Samples.Flocking

open System
open System.Numerics
open System.Diagnostics
open ImGuiNET
open Garnet.Composition

type FpsHud() =
    let fps = FpsGauge(1.0f)
    let fixedFps = FpsGauge(1.0f)
    member _.OnFixedUpdate() =
        let timestamp = Stopwatch.GetTimestamp()
        fixedFps.Update(timestamp)
    member _.OnUpdate() =
        let timestamp = Stopwatch.GetTimestamp()
        fps.Update(timestamp)
    member _.Draw() =
        let flags = 
            ImGuiWindowFlags.NoBackground |||
            ImGuiWindowFlags.NoTitleBar |||
            ImGuiWindowFlags.NoResize |||
            ImGuiWindowFlags.NoMove |||
            ImGuiWindowFlags.NoFocusOnAppearing |||
            ImGuiWindowFlags.NoInputs |||
            ImGuiWindowFlags.NoNavFocus
        ImGui.SetNextWindowSize(Vector2(500.0f, 500.0f))
        ImGui.SetNextWindowPos(Vector2(0.0f, 0.0f))
        if ImGui.Begin("Hud", flags) then
            let info = GC.GetGCMemoryInfo()
            ImGui.SetWindowFontScale(1.0f)
            ImGui.Text $"FPS: %d{int fps.FramesPerSec}, mean: %d{int fps.MeanFrameMs} ms, max: %d{int fps.MaxFrameMs} ms, fixed FPS: %d{int fixedFps.FramesPerSec}"
            ImGui.Text $"GC pause: {info.PauseTimePercentage}%%%%, heap size: {info.HeapSizeBytes / 1024L} Kb"
            ImGui.End()

module DebugSystem =
    let add (c : Container) =
        let hud = c.Get<FpsHud>()
        Disposable.Create [
            c.On<FixedUpdate> <| fun _ ->
                hud.OnFixedUpdate()
            c.On<Update> <| fun _ ->
                hud.OnUpdate()
            c.On<Draw> <| fun _ ->
                hud.Draw()
            ]               


================================================
FILE: samples/Garnet.Samples.Flocking/Drawing.fs
================================================
namespace Garnet.Samples.Flocking

open System
open System.Numerics
open Garnet.Composition
open Garnet.Numerics
open Garnet.Graphics

module DrawingSystems =
    type Container with
        member c.AddCameraUpdates() =
            c.On<Draw> <| fun e ->
                // Update transforms so origin is in the center of the screen and we use pixel coords
                // with +Y as up.
                let displayScale = 1.0f
                let size = e.ViewSize.ToVector2() / displayScale
                let camera = c.Get<CameraSet>().[0]
                camera.ProjectionTransform <- Matrix4x4.CreateOrthographic(size.X, size.Y, -100.0f, 100.0f)

        member c.AddVehicleSprites() =
            c.On<Draw> <| fun _ ->
                let atlas = c.LoadResource<TextureAtlas>(Resources.atlas)
                let layers = c.Get<SpriteRenderer>()
                let texBounds = atlas.[Resources.triangleTexture].NormalizedBounds
                let mesh = layers.GetVertices(Resources.vehicleLayer)
                for r in c.Query<Vehicle, Position, Faction, Heading>() do
                    mesh.DrawQuad {
                        Center = r.Value2.Pos
                        Size = 0.1f * Vector2(1.0f, 1.0f) * 140.0f
                        Rotation = r.Value4.Direction
                        TexBounds = texBounds
                        Color = Faction.toColor r.Value3
                        }

        member c.AddTrailSprites() =
            c.On<Draw> <| fun _ ->
                let atlas = c.LoadResource<TextureAtlas>(Resources.atlas)
                let layers = c.Get<SpriteRenderer>()
                let texBounds = atlas.[Resources.hexTexture].NormalizedBounds
                let mesh = layers.GetVertices(Resources.trailLayer)
                for r in c.Query<Trail, Position, Faction, Lifespan, Rotation>() do
                    mesh.DrawQuad {
                        Center = r.Value2.Pos 
                        Size = r.Value4.Lifespan * 0.3f * Vector2.One * 60.0f
                        Rotation = Vector2.FromRadians(r.Value5.Radians)
                        TexBounds = texBounds
                        Color = (Faction.toColor r.Value3).MultiplyAlpha(r.Value4.Lifespan * 0.3f)
                        }

    let add (c : Container) =
        Disposable.Create [
            c.AddCameraUpdates()
            c.AddVehicleSprites()
            c.AddTrailSprites()
            ]


================================================
FILE: samples/Garnet.Samples.Flocking/Functions.fs
================================================
namespace Garnet.Samples.Flocking

open System.Collections.Generic
open System.Numerics
open Veldrid
open Garnet.Numerics

module WorldSettings =
    let defaults = {
        Seed = 1
        VehicleCount = 100
        SpawnRange = 300.0f
        MaxVehicleSpeed = 50.0f
        TrailLifespan = 0.6f
        Steering = {
            ForwardWeight = 20.0f
            CohesionWeight = 3.0f
            TetherWeight = 1.0f
            SeparationWeight = 3.0f
            AlignmentWeight = 1.0f
            MaxAlignmentDistance = 100.0f
            MaxSeparationDistance = 70.0f
            MaxCohesionDistance = 400.0f
            MaxTetherDistance = 300.0f
        }
    }

module Scalar =
    let tolerance = 1e-9f

    let clamp (s0 : float32) (s1 : float32) (s : float32) =
        s |> max s0 |> min s1

    let linearStep s0 s1 s =
        let length = s1 - s0
        if abs length < tolerance then 0.0f
        else clamp 0.0f 1.0f ((s - s0) / length)

    let smoothStep s0 s1 s =
        let x = linearStep s0 s1 s
        x * x * (3.0f - 2.0f * x)

module Heading =
    let getVelocity vehicle =
        vehicle.Speed * vehicle.Direction

    let fromVelocity (newVelocity : Vector2) =
        let newSpeed = newVelocity.Length()
        { 
            Speed = newSpeed 
            Direction = newVelocity.DivideOrZero(newSpeed)
        }

    let getNextPosition (deltaTime : float32) vehicle pos =
        let velocity = getVelocity vehicle
        let delta = deltaTime * velocity
        pos + delta

module Steering =
    let getForward current =
        current.SteerDir

    let getTether maxDistance current =
        let tetherPoint = Vector2.Zero
        let toTether = tetherPoint - current.SteerPos
        let distance = toTether.Length()
        let scale = Scalar.smoothStep 0.0f maxDistance distance
        toTether.DivideOrZero(distance) * scale

    let getCohesion minDistance maxDistance (neighbors : List<Neighbor>) =
        let mutable sum = Vector2.Zero
        for neighbor in neighbors do
            let weight = Scalar.smoothStep minDistance maxDistance neighbor.Distance
            sum <- sum + (neighbor.TeamWeight * weight) * neighbor.DirectionToNeighbor
        sum.NormalizeOrZero()

    let getSeparation maxDistance (neighbors : List<_>) =
        let mutable sum = Vector2.Zero
        for neighbor in neighbors do
            let weight = Scalar.smoothStep maxDistance 0.0f neighbor.Distance
            sum <- sum + -weight * neighbor.DirectionToNeighbor
        sum

    let getAlignment maxDistance (neighbors : List<_>) current =
        let mutable sum = Vector2.Zero
        for neighbor in neighbors do
            let weight = Scalar.smoothStep maxDistance 0.0f neighbor.Distance
            sum <- sum + -(neighbor.TeamWeight * weight) * current.SteerDir
        sum.NormalizeOrZero()

    let getSteeringDirection s neighbors current =
        let sum = 
            getForward current * s.ForwardWeight +
            getTether s.MaxTetherDistance current * s.TetherWeight +
            getCohesion s.MaxSeparationDistance s.MaxCohesionDistance neighbors * s.CohesionWeight +
            getSeparation s.MaxSeparationDistance neighbors * s.SeparationWeight +
            getAlignment s.MaxAlignmentDistance neighbors current * s.AlignmentWeight
        sum.NormalizeOrZero()
        
module Faction =
    let all = [|
        Red
        Orange
        Yellow
        Green
        Cyan
        Blue
        Purple
    |]

    let toColor = function
        | Red -> RgbaFloat(1.0f, 0.0f, 0.2f, 1.0f)
        | Orange -> RgbaFloat(1.0f, 0.4f, 0.0f, 1.0f)
        | Yellow -> RgbaFloat(0.6f, 1.0f, 0.0f, 1.0f)
        | Green -> RgbaFloat(0.0f, 1.0f, 0.1f, 1.0f)
        | Cyan -> RgbaFloat(0.0f, 0.8f, 0.6f, 1.0f)
        | Blue -> RgbaFloat(0.0f, 0.4f, 1.0f, 1.0f)
        | Purple -> RgbaFloat(0.6f, 0.0f, 1.0f, 1.0f)
        


================================================
FILE: samples/Garnet.Samples.Flocking/Garnet.Samples.Flocking.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>Link</TrimMode>
  </PropertyGroup>

  <ItemGroup>
    <None Include="assets\textures\hex.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\textures\triangle.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\texture-color.frag">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\texture-color.frag.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\texture-color.vert">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="assets\texture-color.vert.hlsl.bytes">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <Compile Include="Types.fs" />
    <Compile Include="Functions.fs" />
    <Compile Include="Resources.fs" />
    <Compile Include="Simulation.fs" />
    <Compile Include="Drawing.fs" />
    <Compile Include="Startup.fs" />
    <Compile Include="Debug.fs" />
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\Garnet\Garnet.fsproj" />
    <ProjectReference Include="..\Garnet.Toolkit\Garnet.Toolkit.fsproj" />
  </ItemGroup>

</Project>


================================================
FILE: samples/Garnet.Samples.Flocking/Program.fs
================================================
open Garnet.Composition
open Garnet.Samples.Flocking

[<EntryPoint>]
let main _ =
    let c = Container()
    use s = c.AddSystems [
        StartupSystem.add
        SimulationSystems.add
        DrawingSystems.add
        DebugSystem.add
        ]
    c.RunLoop()
    0

================================================
FILE: samples/Garnet.Samples.Flocking/Resources.fs
================================================
namespace Garnet.Samples.Flocking

open Garnet.Graphics

module Resources =
    let shaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {
        VertexShader = "texture-color.vert"
        FragmentShader = "texture-color.frag"
        }
    
    let atlas = "textures"
    let hexTexture = "hex.png"
    let triangleTexture = "triangle.png"
    
    let pipeline = {
        Blend = Blend.Alpha
        Filtering = Filtering.Linear
        ShaderSet = shaderSet
        Texture = atlas
        }
    
    let vehicleLayer = {
        LayerId = 2
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = pipeline 
        }

    let trailLayer = {
        LayerId = 1
        CameraId = 0
        Primitive = Quad
        FlushMode = FlushOnDraw
        Pipeline = pipeline 
        }


================================================
FILE: samples/Garnet.Samples.Flocking/Simulation.fs
================================================
namespace Garnet.Samples.Flocking

open System
open System.Collections.Generic
open System.Numerics
open Garnet.Numerics
open Garnet.Composition

// Not we're using Update instead of FixedUpdate here, mainly because this demo
// is intended to measure performance. Normally it would be preferable to use
// FixedUpdate for simulation logic.
module SimulationSystems =
    type Container with
        member c.AddSpawning() =
            c.On<Start> <| fun _ ->
                let settings = c.Get<WorldSettings>()
                let rand = Random(settings.Seed)
                let nextCoord() = float32 (rand.NextDouble() - 0.5) * settings.SpawnRange
                for i = 1 to settings.VehicleCount do
                    c.Create()
                        .With(Faction.all.[rand.Next Faction.all.Length])
                        .With({ MaxSpeed = settings.MaxVehicleSpeed; Radius = 1.0f })
                        .With({ Pos = Vector2(nextCoord(), nextCoord()) })
                        .With({ Direction = Vector2(0.0f, 1.0f); Speed = 0.0f })
                        .Add(TrailEmitter())
                            
        member c.AddSteering() =
            let neighbors = List<Neighbor>()
            c.On<Update> <| fun _ ->
                let settings = c.Get<WorldSettings>().Steering
                for r in c.Query<Eid, Position, Heading, Faction, Vehicle>() do
                    let h = &r.Value3
                    let current = {
                        Eid = r.Value1
                        Pos = r.Value2.Pos
                        Dir = h.Direction
                        Faction = r.Value4
                        }
                    // For simplicity and testing performance, we're iterating over all vehicles
                    // rather than using any spatial partitioning.
                    for r in c.Query<Eid, Heading, Faction, Position>() do
                        if r.Value1 <> current.Eid then
                            let offset = r.Value4.Pos - current.Pos
                            let distance = offset.Length()
                            neighbors.Add { 
                                Direction = r.Value2.Direction
                                TeamWeight = if current.Faction = r.Value3 then 1.0f else 0.0f
                                DirectionToNeighbor = offset.DivideOrZero(distance)
                                Distance = distance
                                }
                    let current = { 
                        SteerPos = current.Pos
                        SteerDir = current.Dir
                        }                    
                    let dir = Steering.getSteeringDirection settings neighbors current
                    let velocity = dir * r.Value5.MaxSpeed
                    neighbors.Clear()
                    h <- Heading.fromVelocity velocity

        member c.AddLifespan() =
            c.On<Update> <| fun e ->
                let dt = float32 e.DeltaTime / 1000.0f
                for r in c.Query<Lifespan, Eid>() do
                    let ls = r.Value1
                    let newLifespan = { Lifespan = ls.Lifespan - dt }
                    if ls.Lifespan <= 0.0f then
                        let eid = r.Value2
                        c.Destroy(eid)
                    r.Value1 <- newLifespan

        member c.AddUpdatePosition() =
            c.On<Update> <| fun e ->
                let dt = float32 e.DeltaTime / 1000.0f
                for r in c.Query<Position, Heading>() do
                    r.Value1 <- { Pos = Heading.getNextPosition dt r.Value2 r.Value1.Pos }

        member c.AddUpdateRotation() =
            c.On<Update> <| fun e ->
                let dt = float32 e.DeltaTime / 1000.0f
                for r in c.Query<Rotation, AngularVelocity>() do
                    r.Value1 <- { Radians = r.Value1.Radians + dt * r.Value2.RotationSpeed }
            
        member c.AddTrailEmission() =
            c.On<Update> <| fun _ ->
                for r in c.Query<TrailEmitter, Position, Faction, Heading>() do
                    c.Create()
                        .With<Faction>(r.Value3)
                        .With<Position>(r.Value2)
                        .With({ Radians = r.Value4.Direction.GetRadians() })
                        .With({ Lifespan = 0.6f })
                        .Add(Trail())

    let add (c : Container) =
        Disposable.Create [
            c.AddSpawning()
            c.AddLifespan()
            c.AddUpdatePosition()
            c.AddUpdateRotation()
            c.AddTrailEmission()
            c.AddSteering()
            ]



================================================
FILE: samples/Garnet.Samples.Flocking/Startup.fs
================================================
namespace Garnet.Samples.Flocking

open System
open Veldrid
open Garnet.Composition
open Garnet.Graphics

module StartupSystem =
    type Container with
        member c.LoadResources() =
            // Manually load textures into atlas. Note other resources like
            // shaders can be loaded on demand and don't need to be explicitly
            // loaded here.
            let device = c.Get<GraphicsDevice>()
            let cache = c.Get<ResourceCache>()
            use fs = new FileFolder("assets")
            fs.LoadTextureAtlasFromFolder(device, Resources.atlas, 512, 512, cache)
            Disposable.Null

    let add (c : Container) =
        // Set global window settings, which will be used by default systems
        c.Set {
            WindowSettings.Default with
                Title = "Flocking"
                Width = 800
                Height = 600
                Background = RgbaFloat(0.0f, 0.1f, 0.2f, 1.0f)
                }
        // Set global settings used by simulation
        c.Set(WorldSettings.defaults)
        Disposable.Create [
            // Default systems for window, sprite drawing, updates, etc
            c.AddDefaultSystems()
            c.LoadResources()
        ]


================================================
FILE: samples/Garnet.Samples.Flocking/Types.fs
================================================
namespace Garnet.Samples.Flocking

open System.Numerics
open Garnet.Composition

[<AutoOpen>]
module Components =
    [<Struct>]
    type Faction =
        | Red
        | Orange
        | Yellow
        | Green
        | Cyan
        | Blue
        | Purple

    [<Struct>]
    type Position = {
        Pos : Vector2
        }

    [<Struct>]
    type Heading = {
        Direction : Vector2
        Speed : float32
        }

    [<Struct>]
    type Vehicle = {
        Radius : float32
        MaxSpeed : float32
        }

    [<Struct>]
    type TrailLifespan = {
        TrailLifespan : float32
        }

    [<Struct>]
    type Lifespan = {
        Lifespan : float32
        }

    [<Struct>]
    type AngularVelocity = {
        RotationSpeed : float32
        }

    [<Struct>]
    type Rotation = {
        Radians : float32
        }

    type TrailEmitter = struct end
    type Trail = struct end

[<AutoOpen>]
module Settings =
    type SteeringSettings = {
        ForwardWeight : float32
        CohesionWeight : float32
        TetherWeight : float32
        SeparationWeight : float32
        AlignmentWeight : float32
        MaxAlignmentDistance : float32
        MaxSeparationDistance : float32
        MaxCohesionDistance : float32
        MaxTetherDistance : float32
        }

    type WorldSettings = {
        Seed : int
        SpawnRange : float32
        VehicleCount : int
        MaxVehicleSpeed : float32
        TrailLifespan : float32
        Steering : SteeringSettings
        }

[<AutoOpen>]
module SteeringTypes =
    [<Struct>]
    type Steerer = {
        SteerPos : Vector2
        SteerDir : Vector2
        }

    [<Struct>]
    type Neighbor = {
        Direction : Vector2
        DirectionToNeighbor : Vector2
        Distance : float32
        TeamWeight : float32
        }

    [<Struct>]
    type CurrentVehicle = {
        Eid : Eid
        Pos : Vector2
        Dir : Vector2
        Faction : Faction
        }

[<AutoOpen>]
module Events =
    type Reset = struct end


================================================
FILE: samples/Garnet.Samples.Flocking/assets/texture-color.frag
================================================
#version 450
layout(location = 0) in vec2 fsin_texCoords;
layout(location = 1) in vec4 fsin_color;
layout(location = 0) out vec4 fsout_color;
layout(set = 1, binding = 1) uniform texture2D SurfaceTexture;
layout(set = 1, binding = 2) uniform sampler SurfaceSampler;
void main()
{
    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);
    fsout_color =  texColor * fsin_color;
}

================================================
FILE: samples/Garnet.Samples.Flocking/assets/texture-color.frag.hlsl.bytes
================================================
Texture2D<float4> _12 : register(t0);
SamplerState _16 : register(s0);

static float2 _22;
static float4 _26;
static float4 _29;

struct SPIRV_Cross_Input
{
    float2 _22 : TEXCOORD0;
    float4 _29 : TEXCOORD1;
};

struct SPIRV_Cross_Output
{
    float4 _26 : SV_Target0;
};

void frag_main()
{
    _26 = _12.Sample(_16, _22) * _29;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _22 = stage_input._22;
    _29 = stage_input._29;
    frag_main();
    SPIRV_Cross_Output stage_output;
    stage_output._26 = _26;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.Flocking/assets/texture-color.vert
================================================
#version 450
layout(set = 0, binding = 0) uniform ProjectionBuffer
{
    mat4 Projection;
};
layout(set = 0, binding = 1) uniform ViewBuffer
{
    mat4 View;
};
layout(set = 1, binding = 0) uniform WorldBuffer
{
    mat4 World;
};
layout(location = 0) in vec3 Position;
layout(location = 1) in vec2 TexCoords;
layout(location = 2) in vec4 Color;
layout(location = 0) out vec2 fsin_texCoords;
layout(location = 1) out vec4 fsin_Color;
void main()
{
    vec4 worldPosition = World * vec4(Position, 1);
    vec4 viewPosition = View * worldPosition;
    vec4 clipPosition = Projection * viewPosition;
    gl_Position = clipPosition;
    fsin_texCoords = TexCoords;
    fsin_Color = Color;
}


================================================
FILE: samples/Garnet.Samples.Flocking/assets/texture-color.vert.hlsl.bytes
================================================
cbuffer _11_13 : register(b2)
{
    row_major float4x4 _13_m0 : packoffset(c0);
};

cbuffer _30_32 : register(b1)
{
    row_major float4x4 _32_m0 : packoffset(c0);
};

cbuffer _38_40 : register(b0)
{
    row_major float4x4 _40_m0 : packoffset(c0);
};


static float4 gl_Position;
static float3 _21;
static float2 _56;
static float2 _58;
static float4 _60;
static float4 _62;

struct SPIRV_Cross_Input
{
    float3 _21 : TEXCOORD0;
    float2 _58 : TEXCOORD1;
    float4 _62 : TEXCOORD2;
};

struct SPIRV_Cross_Output
{
    float2 _56 : TEXCOORD0;
    float4 _60 : TEXCOORD1;
    float4 gl_Position : SV_Position;
};

void vert_main()
{
    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);
    _56 = _58;
    _60 = _62;
}

SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
{
    _21 = stage_input._21;
    _58 = stage_input._58;
    _62 = stage_input._62;
    vert_main();
    SPIRV_Cross_Output stage_output;
    stage_output.gl_Position = gl_Position;
    stage_output._56 = _56;
    stage_output._60 = _60;
    return stage_output;
}


================================================
FILE: samples/Garnet.Samples.Roguelike/ConsoleTest.fsx
================================================
#load "Types.fs"
#load "Functions.fs"

open Garnet.Samples.Roguelike.Types
open Garnet.Samples.Roguelike

let test() =
    let world = World.generate 10 1 
    World.format world |> printfn "%s"
    World.getDistanceMap world [ Vector.zero ] |> DistanceMap.format |> printfn "%s"
    
    [ Move West; Move West ] 
    |> Seq.fold Loop.stepWorld world
    |> World.format 
    |> printfn "%s"

let testInteractive() =
    World.generate 10 1 |> Loop.run


================================================
FILE: samples/Garnet.Samples.Roguelike/Drawing.fs
================================================
namespace Garnet.Samples.Roguelike

open System.Buffers
open System.Runtime.CompilerServices
open Veldrid
open Garnet.Graphics

[<Struct>]
type DisplayTile = {
    ch : char
    fg : RgbaFloat
    bg : RgbaFloat
    }

module DisplayTile =
    let fromTile tile =
        match tile.Entity with
        | Some entity ->
            match entity.EntityType with
            | Rogue -> {
                ch = '@'
                fg = RgbaFloat(0.3f, 1.0f, 1.0f, 1.0f)
                bg = RgbaFloat(0.3f, 1.0f, 1.0f, 0.3f)
                }
            | Minion -> {
                ch = 'm'
                fg = RgbaFloat(1.0f, 0.3f, 0.3f, 1.0f)
                bg = RgbaFloat(1.0f, 0.3f, 0.3f, 0.3f)
                }
        | None ->
            match tile.Terrain with
            | Floor -> {
                ch = '.'
                fg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.5f)
                bg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.1f)
                }
            | Wall -> {
                ch = '#'
                fg = RgbaFloat(0.3f, 0.4f, 0.5f, 1.0f)
                bg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.3f)
                }
            
[<Extension>]
type ViewExtensions =
    [<Extension>]
    static member DrawWorld(w : IBufferWriter<PositionTextureDualColorVertex>, world : World) = 
        let span = w.GetTileSpan(world.Tiles.Count)
        let min = World.getMinLocation world
        let mutable i = 0
        for kvp in world.Tiles do
            let p = Vector.subtract kvp.Key min
            let tile = DisplayTile.fromTile kvp.Value
            span.Slice(i * 4).DrawTile(p.X, p.Y, tile.ch, tile.fg, tile.bg)
            i <- i + 1
        w.Advance(span.Length)


================================================
FILE: samples/Garnet.Samples.Roguelike/Functions.fs
================================================
namespace Garnet.Samples.Roguelike

open System
open System.Collections.Generic

module Vector =
    let init x y = { X = x; Y = y }
    let zero = init 0 0
    let one = init 1 1

    let min a b = { X = min a.X b.X; Y = min a.Y b.Y }
    let max a b = { X = max a.X b.X; Y = max a.Y b.Y }

    let add a b = { 
        X = a.X + b.X
        Y = a.Y + b.Y
        }

    let subtract a b = { 
        X = a.X - b.X
        Y = a.Y - b.Y
        }

module Bounds =
    let init min max = { Min = min; Max = max }
    let sized min size = init min (Vector.add min size)
    let zero = init Vector.zero Vector.zero
    let zeroToOne = init Vector.zero Vector.one

    let maxToMin = {
        Min = { X = Int32.MaxValue; Y = Int32.MaxValue }
        Max = { X = Int32.MinValue; Y = Int32.MinValue }
        }

    let including bounds p = { 
        Min = Vector.min bounds.Min p
        Max = Vector.max bounds.Max p
        }

    let union a b = { 
        Min = Vector.min a.Min b.Min
        Max = Vector.max a.Max b.Max
        }

    let getSize b =
        Vector.subtract b.Max b.Min

    let getCenter b =
        let v = Vector.add b.Max b.Min
        { X = v.X / 2; Y = v.Y / 2 }

    let getCentered contentSize b = 
        let size = getSize b
        {
            X = b.Min.X + (size.X - contentSize.X) / 2
            Y = b.Min.Y + (size.Y - contentSize.Y) / 2
        }
        
    let expand margin b = {
        Min = Vector.subtract b.Min margin.Min
        Max = Vector.add b.Max margin.Max
        }

    let includingAll locs =
        locs 
        |> Seq.fold including maxToMin
        |> expand zeroToOne

module Direction =
    let all = [|
        East
        North
        West
        South
        |]

    let getNext loc dir =
        match dir with
        | East -> { loc with X = loc.X + 1 } 
        | West -> { loc with X = loc.X - 1 }
        | North -> { loc with Y = loc.Y - 1 }
        | South -> { loc with Y = loc.Y + 1 }
    
module DistanceMap =
    let empty = {
        Distances = Map.empty
        }

    let create isPassable (tiles : Map<Vector, _>) seeds =
        let result = Dictionary<Vector, int>()
        let queue = Queue<struct(Vector * int)>()
        let enqueue p dist =
            if not (result.ContainsKey(p)) then
                let canVisit =
                    match tiles.TryGetValue(p) with
                    | false, _ -> false
                    | true, tile -> isPassable tile
                result.Add(p, if canVisit then dist else Int32.MaxValue)
                if canVisit then queue.Enqueue(struct(p, dist))                            
        for seed in seeds do
            enqueue seed 0
        while queue.Count > 0 do
            let struct(p, dist) = queue.Dequeue()
            let nextDist = dist + 1
            for dir in Direction.all do
                let next = Direction.getNext p dir
                enqueue next nextDist
        {
            Distances = 
                result
                |> Seq.map (fun kvp -> kvp.Key, kvp.Value)
                |> Map.ofSeq
        }

    let getDistance p map =
        match map.Distances.TryGetValue(p) with
        | true, dist -> dist
        | false, _ -> Int32.MaxValue

    let distanceToChar x =
        if x = 0 then '.'
        elif x < 10 then '0' + char x
        elif x < 36 then 'a' + char (x - 10)
        elif x < 62 then 'A' + char (x - 36)
        elif x = Int32.MaxValue then '#'
        else '+'
    
    let format map =
        let b = map.Distances |> Seq.map (fun kvp -> kvp.Key) |> Bounds.includingAll
        let size = Bounds.getSize b
        let dw = size.X + 1
        let data = Array.create (dw * size.Y) ' '
        for y = 0 to size.Y - 1 do
            data.[y * dw + dw - 1] <- '\n'
        for kvp in map.Distances do
            let p = Vector.subtract kvp.Key b.Min
            data.[p.Y * dw + p.X] <- distanceToChar kvp.Value
        String(data)

module Tile =
    let getChar tile =
        match tile.Entity with
        | Some e ->
            match e.EntityType with
            | Rogue -> '@'
            | Minion -> 'm'
        | None ->
            match tile.Terrain with
            | Floor -> '.'
            | Wall -> '#' 

    let getMoveEvents loc nextLoc dir tile = seq {
        match tile.Entity with
        | Some entity -> 
            if entity.Hits = 1 then yield Destroyed nextLoc
            else
                yield Attacked {
                    AttackerLoc = loc
                    AttackDir = dir
                    Damage = 1     
                    }
        | None -> yield Moved {
            SourceLoc = loc
            MoveDir = dir
            }
        }
    
    let addEntity entity tile =
        { tile with Entity = Some entity }

    let removeEntity tile =
        { tile with Entity = None }

    let isPassable tile =
        match tile.Terrain with
        | Wall -> false
        | Floor -> true
        
module Entity =
    let rogue = {
        EntityType = Rogue
        Hits = 3
    }

    let minion = {
        EntityType = Minion
        Hits = 1
    }

    let applyDamage damage entity =
        { entity with Hits = entity.Hits - damage }

module Animation =
    let format =
        function
        | Moving e -> $"Moved {e.MoveDir}"
        | Attacking e -> $"{e.AttackerEntityType} attacked {e.TargetEntityType}"
        | Destroying e -> $"{e.DestroyedEntityType} destroyed"

module World =
    let empty = {
        Turn = 0
        RandomSeed = 0UL
        Tiles = Map.empty
        Animations = List.empty
        }

    let generate mapRadius seed =
        let r = mapRadius + 1
        let extent = r * 2 + 1 
        let count = extent * extent
        let rand = Random(seed)
        // draw random walls with border
        let cells1 = Array.zeroCreate count
        for y = -r to r do
            for x = -r to r do
                let i = (y + r) * extent + (x + r)
                let dist = max (abs x) (abs y)
                let cell = dist = r || (dist > 2 && rand.Next(10) = 0)
                cells1.[i] <- cell
        // apply morphological dilate
        le
Download .txt
gitextract_lshbfeyb/

├── .gitignore
├── Directory.Build.props
├── Garnet.sln
├── LICENSE
├── README.md
├── RELEASE_NOTES.md
├── appveyor.yml
├── build.cmd
├── samples/
│   ├── Garnet.Numerics/
│   │   ├── Garnet.Numerics.fsproj
│   │   ├── Hashing.fs
│   │   ├── Noise.fs
│   │   ├── Numerics.fs
│   │   ├── Random.fs
│   │   ├── Ranges.fs
│   │   └── Vectors.fs
│   ├── Garnet.Processor/
│   │   ├── Args.fs
│   │   ├── Garnet.Processor.fsproj
│   │   ├── PackUtility.fs
│   │   └── Program.fs
│   ├── Garnet.Samples.Assorted/
│   │   ├── Extensions.fs
│   │   ├── Garnet.Samples.Assorted.fsproj
│   │   ├── OffscreenDrawing.fs
│   │   ├── Program.fs
│   │   ├── SpriteDrawing.fs
│   │   ├── TextDrawing.fs
│   │   └── assets/
│   │       ├── fonts/
│   │       │   └── pixel-operator-regular-12.font.json
│   │       └── shaders/
│   │           ├── color.frag
│   │           ├── color.frag.hlsl.bytes
│   │           ├── color.vert
│   │           ├── color.vert.hlsl.bytes
│   │           ├── texture-color.frag
│   │           ├── texture-color.frag.hlsl.bytes
│   │           ├── texture-color.vert
│   │           └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Samples.CSharp/
│   │   ├── Garnet.Samples.CSharp.csproj
│   │   └── Program.cs
│   ├── Garnet.Samples.Flocking/
│   │   ├── Debug.fs
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Garnet.Samples.Flocking.fsproj
│   │   ├── Program.fs
│   │   ├── Resources.fs
│   │   ├── Simulation.fs
│   │   ├── Startup.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-color.frag
│   │       ├── texture-color.frag.hlsl.bytes
│   │       ├── texture-color.vert
│   │       └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Samples.Roguelike/
│   │   ├── ConsoleTest.fsx
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Game.fs
│   │   ├── Garnet.Samples.Roguelike.fsproj
│   │   ├── Program.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-dual-color.frag
│   │       └── texture-dual-color.vert
│   ├── Garnet.Samples.Trixel/
│   │   ├── Drawing.fs
│   │   ├── Functions.fs
│   │   ├── Game.fs
│   │   ├── Garnet.Samples.Trixel.fsproj
│   │   ├── Gui.fs
│   │   ├── Imaging.fs
│   │   ├── Program.fs
│   │   ├── Types.fs
│   │   └── assets/
│   │       ├── texture-color.frag
│   │       ├── texture-color.frag.hlsl.bytes
│   │       ├── texture-color.vert
│   │       └── texture-color.vert.hlsl.bytes
│   ├── Garnet.Toolkit/
│   │   ├── Audio.fs
│   │   ├── Collections.fs
│   │   ├── Colors.fs
│   │   ├── Comparisons.fs
│   │   ├── Events.fs
│   │   ├── Fonts.fs
│   │   ├── Garnet.Toolkit.fsproj
│   │   ├── Input.fs
│   │   ├── Logging.fs
│   │   ├── Looping.fs
│   │   ├── Meshes.fs
│   │   ├── Offscreen.fs
│   │   ├── Particles.fs
│   │   ├── Picking.fs
│   │   ├── Pipelines.fs
│   │   ├── Rendering.fs
│   │   ├── Requests.fs
│   │   ├── Serialization.fs
│   │   ├── Shaders.fs
│   │   ├── Sprites.fs
│   │   ├── Systems.fs
│   │   ├── Textures.fs
│   │   ├── Tiling.fs
│   │   ├── Timing.fs
│   │   ├── Vertices.fs
│   │   └── Window.fs
│   └── README.md
├── src/
│   └── Garnet/
│       ├── Actors.fs
│       ├── Channels.fs
│       ├── Collections.fs
│       ├── Comparisons.fs
│       ├── Components.fs
│       ├── Containers.fs
│       ├── Coroutines.fs
│       ├── Entities.fs
│       ├── Formatting.fs
│       ├── Garnet.fsproj
│       ├── Messaging.fs
│       ├── Queries.fs
│       ├── Registry.fs
│       ├── Resources.fs
│       └── Segments.fs
└── tests/
    └── Garnet.Tests/
        ├── ActorTests.fs
        ├── Assertions.fs
        ├── Benchmarks/
        │   ├── ActorBenchmarks.fs
        │   ├── ActorBenchmarks.fsx
        │   ├── ChannelBenchmarks.fs
        │   ├── ChannelBenchmarks.fsx
        │   ├── ContainerBenchmarks.fs
        │   └── ContainerBenchmarks.fsx
        ├── ChannelTests.fs
        ├── CollectionTests.fs
        ├── ComponentTests.fs
        ├── CoroutineTests.fs
        ├── EntityTests.fs
        ├── Garnet.Tests.fsproj
        ├── Iteration.fs
        ├── IterationTests.fs
        ├── Main.fs
        ├── QueryTests.fs
        ├── ReadmeSamples.fs
        ├── Recording.fs
        ├── RegistryTests.fs
        ├── RingBuffer.fs
        ├── Scratch.fsx
        ├── SegmentTests.fs
        ├── Serialization.fs
        ├── SerializationTests.fs
        ├── StateMachineTests.fs
        └── StrategySample.fs
Condensed preview — 140 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (790K chars).
[
  {
    "path": ".gitignore",
    "chars": 253,
    "preview": "*.suo\n*.swp\n*.user\n*.userprefs\n*.pidb\n*.nupkg\n*.sln.ide\n*.orig\n*.vsp\n*.psess\n*.vspx\n*.stackdump\n\n.idea/\n.fake/\n.vs/\n.vsc"
  },
  {
    "path": "Directory.Build.props",
    "chars": 397,
    "preview": "<Project>\n\n  <PropertyGroup>\n    <Version>0.5.3</Version>\n    <Authors>Ben Carruthers</Authors>\n    <Copyright>Copyright"
  },
  {
    "path": "Garnet.sln",
    "chars": 12991,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.2921"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2019 Ben Carruthers\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "README.md",
    "chars": 23330,
    "preview": "# Garnet\n\n[![Build status](https://ci.appveyor.com/api/projects/status/g82kak7btxp48rnd?svg=true)](https://ci.appveyor.c"
  },
  {
    "path": "RELEASE_NOTES.md",
    "chars": 959,
    "preview": "## 0.5.0 – 2021-10-16\n\n- Revised registry implementation\n- Renamed Get() to GetComponents()\n- Renamed GetInstance() regi"
  },
  {
    "path": "appveyor.yml",
    "chars": 282,
    "preview": "version: 1.0.{build}\nimage: Visual Studio 2022\nbuild_script:\n- cmd: build.cmd\nartifacts:\n- path: publish\\*.nupkg\n  name:"
  },
  {
    "path": "build.cmd",
    "chars": 384,
    "preview": "dotnet restore\ndotnet build --no-restore\ndotnet test --no-build --verbosity normal\ndotnet pack -c Release -o publish src"
  },
  {
    "path": "samples/Garnet.Numerics/Garnet.Numerics.fsproj",
    "chars": 622,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net6.0<"
  },
  {
    "path": "samples/Garnet.Numerics/Hashing.fs",
    "chars": 4536,
    "preview": "namespace Garnet.Numerics\n\nopen System\n\nmodule Fnv1a =\n    [<Literal>]\n    let Prime = 0x1000193u\n\n    [<Literal>]\n    "
  },
  {
    "path": "samples/Garnet.Numerics/Noise.fs",
    "chars": 52899,
    "preview": "namespace Garnet.Numerics\n\n// Adapted for F# from Stefan Gustavson code\n// Simplex noise demystified:\n// https://weber."
  },
  {
    "path": "samples/Garnet.Numerics/Numerics.fs",
    "chars": 961,
    "preview": "namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\n[<AutoOpen>]\nmodule MathF =\n    type MathF with\n        st"
  },
  {
    "path": "samples/Garnet.Numerics/Random.fs",
    "chars": 4082,
    "preview": "namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\ntype PcgRandom(initState, initSeq) =\n    let inc = PcgRand"
  },
  {
    "path": "samples/Garnet.Numerics/Ranges.fs",
    "chars": 12897,
    "preview": "namespace Garnet.Numerics\n\nopen System.Numerics\n\n// Float range types\n\n[<Struct>]\ntype Range =\n    val Min : float32\n  "
  },
  {
    "path": "samples/Garnet.Numerics/Vectors.fs",
    "chars": 6028,
    "preview": "namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\n// Int32 vector types\n\n[<Struct>]\ntype Vector2i = \n    val"
  },
  {
    "path": "samples/Garnet.Processor/Args.fs",
    "chars": 823,
    "preview": "namespace Garnet.Processor\n\nopen Argu\n\n[<CliPrefix(CliPrefix.Dash)>]\ntype PackArgs =\n    | Input of string\n    | Output"
  },
  {
    "path": "samples/Garnet.Processor/Garnet.Processor.fsproj",
    "chars": 704,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</Targ"
  },
  {
    "path": "samples/Garnet.Processor/PackUtility.fs",
    "chars": 3284,
    "preview": "namespace Garnet.Processor\n\nopen System.Diagnostics\nopen System.IO\nopen System.IO.Compression\nopen System.Security.Cryp"
  },
  {
    "path": "samples/Garnet.Processor/Program.fs",
    "chars": 335,
    "preview": "open Argu\nopen Garnet.Processor\n\n[<EntryPoint>]\nlet main argv =\n    let parser = ArgumentParser.Create<ProcessorArgs>()\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Extensions.fs",
    "chars": 978,
    "preview": "namespace Garnet.Samples.Assorted\n\nopen System\nopen System.Numerics\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Gr"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Garnet.Samples.Assorted.fsproj",
    "chars": 2494,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</T"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/OffscreenDrawing.fs",
    "chars": 4322,
    "preview": "module Garnet.Samples.Assorted.OffscreenDrawing\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.G"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Program.fs",
    "chars": 167,
    "preview": "open Garnet.Composition\nopen Garnet.Samples.Assorted\n\n[<EntryPoint>]\nlet main argv =\n    //SpriteDrawing.run()\n    //Tex"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/SpriteDrawing.fs",
    "chars": 2045,
    "preview": "module Garnet.Samples.Assorted.SpriteDrawing\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.Grap"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/TextDrawing.fs",
    "chars": 4041,
    "preview": "module Garnet.Samples.Assorted.TextDrawing\n\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Numer"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/fonts/pixel-operator-regular-12.font.json",
    "chars": 16428,
    "preview": "{\n  \"family\": \"Pixel Operator\",\n  \"style\": \"Regular\",\n  \"size\": 12,\n  \"height\": 17,\n  \"chars\": [\n    {\n      \"code\": \" \""
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.frag",
    "chars": 142,
    "preview": "#version 450\nlayout(location = 0) in vec4 fsin_color;\nlayout(location = 0) out vec4 fsout_color;\nvoid main()\n{\n    fsout"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.frag.hlsl.bytes",
    "chars": 384,
    "preview": "static float4 _9;\nstatic float4 _11;\n\nstruct SPIRV_Cross_Input\n{\n    float4 _11 : TEXCOORD0;\n};\n\nstruct SPIRV_Cross_Outp"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.vert",
    "chars": 569,
    "preview": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.vert.hlsl.bytes",
    "chars": 900,
    "preview": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    "
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag",
    "chars": 411,
    "preview": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) "
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag.hlsl.bytes",
    "chars": 558,
    "preview": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic flo"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert",
    "chars": 687,
    "preview": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert.hlsl.bytes",
    "chars": 1064,
    "preview": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    "
  },
  {
    "path": "samples/Garnet.Samples.CSharp/Garnet.Samples.CSharp.csproj",
    "chars": 474,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</T"
  },
  {
    "path": "samples/Garnet.Samples.CSharp/Program.cs",
    "chars": 104,
    "preview": "using Garnet.Composition;\n\nvar c = new Container();\n\nusing (c.AddDefaultSystems())\n{\n    c.RunLoop();\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Debug.fs",
    "chars": 1668,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Numerics\nopen System.Diagnostics\nopen ImGuiNET\nopen Garnet.C"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Drawing.fs",
    "chars": 2427,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.Numerics\nopen G"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Functions.fs",
    "chars": 3889,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numeri"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Garnet.Samples.Flocking.fsproj",
    "chars": 1588,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</T"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Program.fs",
    "chars": 271,
    "preview": "open Garnet.Composition\nopen Garnet.Samples.Flocking\n\n[<EntryPoint>]\nlet main _ =\n    let c = Container()\n    use s = c."
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Resources.fs",
    "chars": 836,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen Garnet.Graphics\n\nmodule Resources =\n    let shaderSet : ShaderSetDescriptor<Pos"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Simulation.fs",
    "chars": 4620,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Numeric"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Startup.fs",
    "chars": 1225,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Graphics\n\nmodule Startu"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Types.fs",
    "chars": 2025,
    "preview": "namespace Garnet.Samples.Flocking\n\nopen System.Numerics\nopen Garnet.Composition\n\n[<AutoOpen>]\nmodule Components =\n    ["
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.frag",
    "chars": 411,
    "preview": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) "
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.frag.hlsl.bytes",
    "chars": 558,
    "preview": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic flo"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.vert",
    "chars": 687,
    "preview": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.vert.hlsl.bytes",
    "chars": 1064,
    "preview": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    "
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/ConsoleTest.fsx",
    "chars": 455,
    "preview": "#load \"Types.fs\"\n#load \"Functions.fs\"\n\nopen Garnet.Samples.Roguelike.Types\nopen Garnet.Samples.Roguelike\n\nlet test() =\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Drawing.fs",
    "chars": 1681,
    "preview": "namespace Garnet.Samples.Roguelike\n\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen Veldrid\nopen Garnet.G"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Functions.fs",
    "chars": 14265,
    "preview": "namespace Garnet.Samples.Roguelike\n\nopen System\nopen System.Collections.Generic\n\nmodule Vector =\n    let init x y = { X"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Game.fs",
    "chars": 4968,
    "preview": "namespace Garnet.Samples.Roguelike\n\nopen System\nopen System.Numerics\nopen System.Threading\nopen Veldrid\nopen Garnet.Num"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Garnet.Samples.Roguelike.fsproj",
    "chars": 1059,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</T"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Program.fs",
    "chars": 146,
    "preview": "open Garnet.Composition\nopen Garnet.Samples.Roguelike\n\n[<EntryPoint>]\nlet main argv =\n    use fs = new FileFolder(\"asset"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Types.fs",
    "chars": 2116,
    "preview": "namespace Garnet.Samples.Roguelike\n\n[<AutoOpen>]\nmodule WorldTypes =\n    type Vector = {\n        X : int\n        Y : in"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/assets/texture-dual-color.frag",
    "chars": 508,
    "preview": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_fg;\nlayout(location = 2) in "
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/assets/texture-dual-color.vert",
    "chars": 901,
    "preview": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Drawing.fs",
    "chars": 4121,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen System.Numer"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Functions.fs",
    "chars": 7296,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen Newtonsoft.Json\nopen Garnet.Numerics\nopen Veldri"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Game.fs",
    "chars": 4710,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen System.Threading\nopen System.IO\nopen Veldrid\nope"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Garnet.Samples.Trixel.fsproj",
    "chars": 1560,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</T"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Gui.fs",
    "chars": 7666,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System\nopen System.IO\nopen System.Numerics\nopen Veldrid\nopen ImGuiNET\nopen Garnet"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Imaging.fs",
    "chars": 1852,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen SixLabors.ImageSharp\nopen Veldrid\nopen ImGuiNET\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Program.fs",
    "chars": 143,
    "preview": "open Garnet.Composition\nopen Garnet.Samples.Trixel\n\n[<EntryPoint>]\nlet main argv =\n    use fs = new FileFolder(\"assets\")"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Types.fs",
    "chars": 1374,
    "preview": "namespace Garnet.Samples.Trixel\n\nopen System.Numerics\nopen Garnet.Numerics\nopen Veldrid\n\n[<AutoOpen>]\nmodule Grids =\n  "
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.frag",
    "chars": 411,
    "preview": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) "
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.frag.hlsl.bytes",
    "chars": 558,
    "preview": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic flo"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.vert",
    "chars": 687,
    "preview": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.vert.hlsl.bytes",
    "chars": 1064,
    "preview": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    "
  },
  {
    "path": "samples/Garnet.Toolkit/Audio.fs",
    "chars": 10208,
    "preview": "namespace Garnet.Audio\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Numerics\nopen Syste"
  },
  {
    "path": "samples/Garnet.Toolkit/Collections.fs",
    "chars": 2933,
    "preview": "namespace Garnet.Collections\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Comparisons\n\n/// Mutable min-heap"
  },
  {
    "path": "samples/Garnet.Toolkit/Colors.fs",
    "chars": 8380,
    "preview": "namespace Garnet.Numerics\n\nopen System\nopen Veldrid\n\nmodule private ColorParsing =\n    let parse defaultValue (parts : "
  },
  {
    "path": "samples/Garnet.Toolkit/Comparisons.fs",
    "chars": 1238,
    "preview": "namespace Garnet\n\n// The purpose of this is to avoid equality operator allocations for value types.\n// Be careful if us"
  },
  {
    "path": "samples/Garnet.Toolkit/Events.fs",
    "chars": 783,
    "preview": "namespace Garnet.Composition\n\nopen Garnet.Numerics\n\ntype Start = struct end    \ntype Closing = struct end\n\n[<Struct>]\nt"
  },
  {
    "path": "samples/Garnet.Toolkit/Fonts.fs",
    "chars": 13767,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen System.Numerics\nop"
  },
  {
    "path": "samples/Garnet.Toolkit/Garnet.Toolkit.fsproj",
    "chars": 2715,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net6.0<"
  },
  {
    "path": "samples/Garnet.Toolkit/Input.fs",
    "chars": 6315,
    "preview": "namespace Garnet.Input\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numer"
  },
  {
    "path": "samples/Garnet.Toolkit/Logging.fs",
    "chars": 3139,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen Microsoft.Extensions.Logging\nopen Cysharp.Text\nopen ZLogg"
  },
  {
    "path": "samples/Garnet.Toolkit/Looping.fs",
    "chars": 2580,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Diagnostics\nopen System.Runtime.C"
  },
  {
    "path": "samples/Garnet.Toolkit/Meshes.fs",
    "chars": 5876,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen Veldrid\nopen Garnet.Collections\n\ntype ResizableDeviceBu"
  },
  {
    "path": "samples/Garnet.Toolkit/Offscreen.fs",
    "chars": 6839,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\n\ntype Position"
  },
  {
    "path": "samples/Garnet.Toolkit/Particles.fs",
    "chars": 11856,
    "preview": "namespace Garnet.Graphics\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numerics\nopen "
  },
  {
    "path": "samples/Garnet.Toolkit/Picking.fs",
    "chars": 6206,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Buffers\nopen System.Numerics\nopen Ga"
  },
  {
    "path": "samples/Garnet.Toolkit/Pipelines.fs",
    "chars": 8031,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Co"
  },
  {
    "path": "samples/Garnet.Toolkit/Rendering.fs",
    "chars": 2519,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen Veldrid\n\n// Allow auto invalidate to avoid "
  },
  {
    "path": "samples/Garnet.Toolkit/Requests.fs",
    "chars": 1623,
    "preview": "namespace Garnet.Composition\n\nopen System.Collections.Generic\nopen Garnet.Composition.Comparisons\nopen Garnet.Compositi"
  },
  {
    "path": "samples/Garnet.Toolkit/Serialization.fs",
    "chars": 3578,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen Microsoft.FSharp.Reflection\nopen Newtonsoft.Json\nopen Vel"
  },
  {
    "path": "samples/Garnet.Toolkit/Shaders.fs",
    "chars": 8427,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.Text\nopen Veldrid\nope"
  },
  {
    "path": "samples/Garnet.Toolkit/Sprites.fs",
    "chars": 5484,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Numerics\n\n[<Str"
  },
  {
    "path": "samples/Garnet.Toolkit/Systems.fs",
    "chars": 8605,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen System.Runtime.CompilerServices\nopen System.Reflection\nop"
  },
  {
    "path": "samples/Garnet.Toolkit/Textures.fs",
    "chars": 15129,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Veldrid"
  },
  {
    "path": "samples/Garnet.Toolkit/Tiling.fs",
    "chars": 3537,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Numerics\nopen System.Runtime.CompilerServices\n\n["
  },
  {
    "path": "samples/Garnet.Toolkit/Timing.fs",
    "chars": 4265,
    "preview": "namespace Garnet.Composition\n\nopen System.Diagnostics\n\ntype FpsGauge(updateInterval) =\n    let mutable count = 0\n    le"
  },
  {
    "path": "samples/Garnet.Toolkit/Vertices.fs",
    "chars": 12919,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Numerics\nopen System.Runtime.CompilerServices\nop"
  },
  {
    "path": "samples/Garnet.Toolkit/Window.fs",
    "chars": 4807,
    "preview": "namespace Garnet.Graphics\n\nopen System\nopen System.IO\nopen Veldrid\nopen Veldrid.StartupUtilities\nopen ImGuiNET\nopen Gar"
  },
  {
    "path": "samples/README.md",
    "chars": 3356,
    "preview": "# Samples\n\n## Flocking\n\n[[Code](Garnet.Samples.Flocking)]\n\nBoids-style flocking and clustering using Garnet ECS and Veld"
  },
  {
    "path": "src/Garnet/Actors.fs",
    "chars": 32830,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Diagnostics\nopen System.Threading\nopen System"
  },
  {
    "path": "src/Garnet/Channels.fs",
    "chars": 13065,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Diagnostics\no"
  },
  {
    "path": "src/Garnet/Collections.fs",
    "chars": 13006,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections\nopen System.Collections.Generic\nopen System.Runtime.I"
  },
  {
    "path": "src/Garnet/Comparisons.fs",
    "chars": 1250,
    "preview": "namespace Garnet.Composition\n\n// The purpose of this is to avoid equality operator allocations for value types.\n// Be c"
  },
  {
    "path": "src/Garnet/Components.fs",
    "chars": 7611,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Runtime.InteropServices\nopen Garnet.Composition.Comparisons\n\ntype"
  },
  {
    "path": "src/Garnet/Containers.fs",
    "chars": 10373,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Runtime.Inter"
  },
  {
    "path": "src/Garnet/Coroutines.fs",
    "chars": 4236,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Composition.Comparisons\n\n[<Struct"
  },
  {
    "path": "src/Garnet/Entities.fs",
    "chars": 8705,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Runtime.CompilerServices\nopen Garnet.Composition.Comparisons\n\nmod"
  },
  {
    "path": "src/Garnet/Formatting.fs",
    "chars": 5826,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections\nopen System.Collections.Generic\nopen System.Text\nopen"
  },
  {
    "path": "src/Garnet/Garnet.fsproj",
    "chars": 1059,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netsta"
  },
  {
    "path": "src/Garnet/Messaging.fs",
    "chars": 14092,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Buffers\nopen System.Threading\nope"
  },
  {
    "path": "src/Garnet/Queries.fs",
    "chars": 62280,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.CompilerServices\nopen Gar"
  },
  {
    "path": "src/Garnet/Registry.fs",
    "chars": 6638,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Syst"
  },
  {
    "path": "src/Garnet/Resources.fs",
    "chars": 12829,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.IO.Compression\nope"
  },
  {
    "path": "src/Garnet/Segments.fs",
    "chars": 23067,
    "preview": "namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Text\nopen System.Runtime.InteropS"
  },
  {
    "path": "tests/Garnet.Tests/ActorTests.fs",
    "chars": 14186,
    "preview": "module Garnet.Tests.Actors\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.Threading\nopen Expect"
  },
  {
    "path": "tests/Garnet.Tests/Assertions.fs",
    "chars": 197,
    "preview": "namespace Garnet.Tests\n\nopen Expecto\n\n[<AutoOpen>]\nmodule Assertions =\n    let shouldEqual b a =\n        Expect.equal a"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ActorBenchmarks.fs",
    "chars": 8396,
    "preview": "module Garnet.Benchmarks.Actors\n\nopen System.Collections.Generic\nopen System.Diagnostics\nopen System.Runtime.InteropSer"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ActorBenchmarks.fsx",
    "chars": 1999,
    "preview": "#r \"../../../src/Garnet/bin/Release/net6.0/Garnet.dll\"\n#load \"ActorBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Actors.PingPo"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ChannelBenchmarks.fs",
    "chars": 1533,
    "preview": "module Garnet.Benchmarks.Channels\n\nopen Garnet.Composition\n\nlet run iterations sendFreq =\n    let mutable sum = 0L\n    "
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ChannelBenchmarks.fsx",
    "chars": 231,
    "preview": "#r \"../bin/Release/net6.0/Garnet.dll\"\n#load \"ChannelBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Channels\n\n#time\n\nrun 30_000_"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ContainerBenchmarks.fs",
    "chars": 7072,
    "preview": "module Garnet.Benchmarks.Containers\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Composition\n\nlet "
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ContainerBenchmarks.fsx",
    "chars": 1208,
    "preview": "#r \"../bin/Release/net6.0/Garnet.dll\"\n#load \"ContainerBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Containers\n\n#time\n\nrunCrea"
  },
  {
    "path": "tests/Garnet.Tests/ChannelTests.fs",
    "chars": 4285,
    "preview": "module Garnet.Tests.Channels\n\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests"
  },
  {
    "path": "tests/Garnet.Tests/CollectionTests.fs",
    "chars": 1809,
    "preview": "module Garnet.Tests.Collections\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Collections\n     "
  },
  {
    "path": "tests/Garnet.Tests/ComponentTests.fs",
    "chars": 4511,
    "preview": "module Garnet.Tests.Components\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tes"
  },
  {
    "path": "tests/Garnet.Tests/CoroutineTests.fs",
    "chars": 5395,
    "preview": "module Garnet.Tests.Coroutines\n\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet test"
  },
  {
    "path": "tests/Garnet.Tests/EntityTests.fs",
    "chars": 6056,
    "preview": "module Garnet.Tests.Entities\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Composition\nopen Expecto\n\ntype Vel"
  },
  {
    "path": "tests/Garnet.Tests/Garnet.Tests.fsproj",
    "chars": 1797,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</Targe"
  },
  {
    "path": "tests/Garnet.Tests/Iteration.fs",
    "chars": 34344,
    "preview": "namespace Garnet.Composition\n\nopen Garnet.Composition.Comparisons\n\nmodule Join =\n    module Array =\n        let iter1 a"
  },
  {
    "path": "tests/Garnet.Tests/IterationTests.fs",
    "chars": 2333,
    "preview": "module Garnet.Tests.Iteration\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Test"
  },
  {
    "path": "tests/Garnet.Tests/Main.fs",
    "chars": 169,
    "preview": "module Garnet.Tests.Program\n\nopen Expecto\n\n[<EntryPoint>]\nlet main argv =\n    Tests.runTestsInAssembly { defaultConfig "
  },
  {
    "path": "tests/Garnet.Tests/QueryTests.fs",
    "chars": 1843,
    "preview": "module Garnet.Tests.Query\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\n"
  },
  {
    "path": "tests/Garnet.Tests/ReadmeSamples.fs",
    "chars": 6324,
    "preview": "module Garnet.Tests.Examples\n\nopen Garnet.Composition\n\n// Common\n\ntype Msg = struct end\ntype UpdatePositions = struct en"
  },
  {
    "path": "tests/Garnet.Tests/Recording.fs",
    "chars": 5937,
    "preview": "namespace Garnet.Streaming\n\nopen System\nopen System.IO\nopen System.Collections.Generic\nopen System.Text\nopen Garnet.Comp"
  },
  {
    "path": "tests/Garnet.Tests/RegistryTests.fs",
    "chars": 1427,
    "preview": "module Garnet.Tests.Registry\n\nopen Expecto\nopen Garnet.Composition\n\n[<AllowNullLiteral>]\ntype TestClass() = class end\n\n"
  },
  {
    "path": "tests/Garnet.Tests/RingBuffer.fs",
    "chars": 3894,
    "preview": "namespace Garnet.Collections\n\nopen System\nopen System.Threading\nopen Garnet.Composition.Comparisons\n\nmodule RingBuffer "
  },
  {
    "path": "tests/Garnet.Tests/Scratch.fsx",
    "chars": 59,
    "preview": "#r \"netstandard\"\n#r \"bin/Release/netcoreapp2.1/Garnet.dll\"\n"
  },
  {
    "path": "tests/Garnet.Tests/SegmentTests.fs",
    "chars": 2425,
    "preview": "module Garnet.Tests.Segments\n\nopen System\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    let copyArrayM"
  },
  {
    "path": "tests/Garnet.Tests/Serialization.fs",
    "chars": 5951,
    "preview": "namespace Garnet.Streaming\n\nopen System\nopen System.IO\nopen System.Collections.Generic\nopen System.Runtime.InteropServi"
  },
  {
    "path": "tests/Garnet.Tests/SerializationTests.fs",
    "chars": 1013,
    "preview": "module Garnet.Tests.Serialization\n\nopen System.IO\nopen Expecto\nopen Garnet.Streaming\n\nlet testRoundtripValue (serializer"
  },
  {
    "path": "tests/Garnet.Tests/StateMachineTests.fs",
    "chars": 1768,
    "preview": "module Garnet.Tests.StateMachine\n\nopen Garnet.Composition\nopen Expecto\n\nmodule Partition =\n    let globals = 0\n    let "
  },
  {
    "path": "tests/Garnet.Tests/StrategySample.fs",
    "chars": 6227,
    "preview": "module Garnet.Tests.StrategySample\n\n// This sample demonstrates using component storage for grid cells\n// for use in a "
  }
]

About this extraction

This page contains the full source code of the bcarruthers/garnet GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 140 files (738.1 KB), approximately 211.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!