[
  {
    "path": ".gitignore",
    "content": "*.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.vscode/\n.ionide\npackages/\nbuild/\npublish/\nartifacts/\nBenchmarkDotNet.Artifacts/\nbin\nobj\n\n#Paket dependency manager\n.paket/\npaket-files/\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n\n  <PropertyGroup>\n    <Version>0.5.3</Version>\n    <Authors>Ben Carruthers</Authors>\n    <Copyright>Copyright © 2021 Ben Carruthers</Copyright>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>\n    <RepositoryUrl>https://github.com/bcarruthers/garnet</RepositoryUrl>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "Garnet.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29215.179\nMinimumVisualStudioVersion = 15.0.26124.0\nProject(\"{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}\") = \"Garnet\", \"src\\Garnet\\Garnet.fsproj\", \"{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}\"\nEndProject\nProject(\"{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}\") = \"Garnet.Tests\", \"tests\\Garnet.Tests\\Garnet.Tests.fsproj\", \"{97D1D0D1-C635-4725-A892-DE1852A0CB4C}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Samples\", \"Samples\", \"{1E82F9DC-427D-46D1-9352-FB1E97724CAF}\"\nEndProject\nProject(\"{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}\") = \"Garnet.Toolkit\", \"samples\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\", \"{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}\"\nEndProject\nProject(\"{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}\") = \"Garnet.Samples.Flocking\", \"samples\\Garnet.Samples.Flocking\\Garnet.Samples.Flocking.fsproj\", \"{16290883-031E-408F-B599-5FEE33355629}\"\nEndProject\nProject(\"{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}\") = \"Garnet.Samples.Roguelike\", \"samples\\Garnet.Samples.Roguelike\\Garnet.Samples.Roguelike.fsproj\", \"{C7826D8F-8343-440B-BD6D-74F73D6814EC}\"\nEndProject\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Garnet.Samples.Trixel\", \"samples\\Garnet.Samples.Trixel\\Garnet.Samples.Trixel.fsproj\", \"{58AB7F65-F130-4624-9458-A4BC5BF867DC}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Garnet.Samples.CSharp\", \"samples\\Garnet.Samples.CSharp\\Garnet.Samples.CSharp.csproj\", \"{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}\"\nEndProject\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Garnet.Numerics\", \"samples\\Garnet.Numerics\\Garnet.Numerics.fsproj\", \"{66F27C8C-B121-426B-BEA2-4379FC217849}\"\nEndProject\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Garnet.Samples.Assorted\", \"samples\\Garnet.Samples.Assorted\\Garnet.Samples.Assorted.fsproj\", \"{957D6CB2-EBFD-42A8-A201-731CBA232FB3}\"\nEndProject\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Garnet.Processor\", \"samples\\Garnet.Processor\\Garnet.Processor.fsproj\", \"{C88FF8AF-962A-4162-8FB8-0D337DA09138}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{4FA4BEC9-C501-40F7-B0F2-B204A932E27B}.Release|x86.Build.0 = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x64.Build.0 = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{97D1D0D1-C635-4725-A892-DE1852A0CB4C}.Release|x86.Build.0 = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x64.Build.0 = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF}.Release|x86.Build.0 = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|x64.Build.0 = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{16290883-031E-408F-B599-5FEE33355629}.Release|x86.Build.0 = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x64.Build.0 = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC}.Release|x86.Build.0 = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x64.Build.0 = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC}.Release|x86.Build.0 = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x64.Build.0 = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41}.Release|x86.Build.0 = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x64.Build.0 = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849}.Release|x86.Build.0 = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x64.Build.0 = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3}.Release|x86.Build.0 = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x64.Build.0 = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{E0F6649F-E652-4B34-B0AA-1E6716AB60FF} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{16290883-031E-408F-B599-5FEE33355629} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{C7826D8F-8343-440B-BD6D-74F73D6814EC} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{58AB7F65-F130-4624-9458-A4BC5BF867DC} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{AF60F947-ED94-4B84-BF8E-D327D2CFCE41} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{66F27C8C-B121-426B-BEA2-4379FC217849} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{957D6CB2-EBFD-42A8-A201-731CBA232FB3} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\t\t{C88FF8AF-962A-4162-8FB8-0D337DA09138} = {1E82F9DC-427D-46D1-9352-FB1E97724CAF}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {D7FA662D-CCF7-4CEF-82D0-82F96A334562}\n\tEndGlobalSection\n\tGlobalSection(Performance) = preSolution\n\t\tHasPerformanceSessions = true\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Ben Carruthers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Garnet\n\n[![Build status](https://ci.appveyor.com/api/projects/status/g82kak7btxp48rnd?svg=true)](https://ci.appveyor.com/project/bcarruthers/garnet)\n\n[NuGet package](https://www.nuget.org/packages/Garnet/)\n\nGarnet is a lightweight game composition library for F# with entity-component-system (ECS) and actor-like messaging features.\n\n```fsharp\nopen Garnet.Composition\n\n// events\n[<Struct>] type Update = { dt : float32 }\n\n// components\n[<Struct>] type Position = { x : float32; y : float32 }\n[<Struct>] type Velocity = { vx : float32; vy : float32 }\n\n// create a world\nlet world = Container()\n\n// register a system that updates position\nlet system =\n    world.On<Update> <| fun e ->\n        for r in world.Query<Position, Velocity>() do\n            let p = &r.Value1\n            let v = r.Value2\n            p <- {\n                x = p.x + v.vx * e.dt\n                y = p.y + v.vy * e.dt\n                }\n\n// add an entity to world\nlet entity = \n    world.Create()\n        .With({ x = 10.0f; y = 5.0f })\n        .With({ vx = 1.0f; vy = 2.0f })\n\n// run updates and print world state\nfor i = 1 to 10 do\n    world.Run <| { dt = 0.1f }\n    printfn \"%O\\n\\n%O\\n\\n\" world entity\n```\n\n## Table of contents\n* Introduction\n    * [Getting started](#gettingstarted)\n    * [Background](#background)\n    * [Goals](#goals)\n* Guide\n    * [Containers](#containers)\n    * [Entities](#entities)\n    * [Components](#components)\n    * [Systems](#systems)\n    * [Actors](#actors)\n    * [Integration](#integration)\n* [FAQ](#faq)\n* [License](#license)\n* [Maintainers](#maintainers)\n\n## Getting started\n\n1. Create either a .NET Framework, Core, or 6.0+ application.\n2. Reference the [Garnet NuGet package](https://www.nuget.org/packages/Garnet/). \n3. For sample code, see unit tests or [sample projects](https://github.com/bcarruthers/garnet/tree/master/samples).\n\n## Background\n\nECS 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.\n\nWhile 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.\n\n## Goals\n\n- **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.\n\n- **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.\n\n- **Minimal**: The core library focuses on events, scheduling, and storage, and anything game-specific like physics, rendering, or update loops should be implemented separately.\n\n## Containers\n\nECS 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.\n\n```fsharp\n// create a container/world\nlet c = Container()\n```\n\n### Registry\n\nContainers 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.\n\n```fsharp\n// option 1: add specific instance\nc.SetValue(defaultWorldSettings)\n// option 2: register a factory\nc.SetFactory(fun () -> defaultWorldSettings)\n// resolve type\nlet settings = c.GetValue<WorldSettings>()\n```\n\nThis works for value types as well:\n\n```fsharp\nc.SetValue { zoomLevel = 0.5f }\nlet zoom = c.GetValue<Zoom>>()\n```\n\n### Object pooling\n\nAvoiding 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.\n\n### Commits\n\nCertain 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.\n\n```fsharp\n// create an entity\nlet e = c.Create().With(\"test\")\n// not yet visible\nc.Commit()\n// now visible\n```\n\n## Entities\n\nAn entity is any identifiable thing in your game which you can attach components to. At minimum, an entity consists only of an entity ID.\n\n### Entity ID\n\nEntity 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). \n\n```fsharp\nlet entity = c.Create()\nprintfn \"%A\" entity.id\n```\n\n### Generations\n\nA 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.\n\n### Partitioning\n\nComponent 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.\n\n### Generic storage\n\nStorage 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.\n\n### Inspecting\n\nYou can print the components of an entity at any time, which is useful in REPL scenarios as an alternative to using a debugger.\n\n```fsharp\nprintfn \"%s\" <| c.Get(Eid 64).ToString()\n```\n```\nEntity 0x40: 20 bytes\nEid 0x40\nLoc {x = 10;\n y = 2;}\nUnitType Archer\nUnitSize {unitSize = 5;}\n```\n\n## Components\n\nComponents 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.\n\n### Data types\n\nComponents 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.\n\n```fsharp\n[<Struct>] type Position = { x : float32; y : float32 }\n[<Struct>] type Velocity = { vx : float32; vy : float32 }\n\n// create an entity and add two components to it\nlet entity = \n    c.Create()\n        .With({ x = 10.0f; y = 5.0f })\n        .With({ vx = 1.0f; vy = 2.0f })\n```\n\n### Storage\n\nComponents 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.\n\nOnly 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).\n\n### Iteration\n\nYou 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.\n\n```fsharp\nlet healthSub =\n    c.On<DestroyZeroHealth> <| fun e ->\n        for r in c.Query<Eid, Position, Health>() do\n            let h = r.Value3\n            if h.hp <= 0 then\n                let eid = r.Value1\n                c.Destroy(eid)\n```\n\nFor batch operations or to improve performance further, you can iterate over segments:\n\n```fsharp\nlet healthSub =\n    c.On<DestroyZeroHealth> <| fun e ->\n        for seg, eids, _, hs in c.QuerySegments<Eid, Position, Health>() do\n            for i in seg do\n                let h = hs.[i]\n                if h.hp <= 0 then\n                    let eid = eids.[i]\n                    c.Destroy(eid)\n```\n\nNote that writes to existing components during iteration occur immediately, unlike adding or removing components.\n\n### Adding\n\nAdditions are deferred until a commit occurs, so any code dependent on those operations completing needs to be implemented as a coroutine.\n\n```fsharp\nlet e = c.Get(Eid 100)\ne.Add<Position> { x = 1.0f; y = 2.0f }\n// change not yet visible\n```\n\n### Removing\n\nLike 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.\n\n```fsharp\ne.Remove<Velocity>()\n// change not yet visible\n```\n\n### Updating\n\nUnlike 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.\n\n```fsharp\nlet e = c.Get(Eid 100)\ne.Set<Position> { x = 1.0f; y = 2.0f }\n// change immediately visible\n```\n\n### Markers\n\nYou 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.\n\n```fsharp\ntype PowerupMarker = struct end\n```\n\n## Systems\n\nSystems 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.\n\n```fsharp\nmodule MovementSystem =     \n    // separate methods as needed\n    let registerUpdate (c : Container) =\n        c.On<UpdatePositions> <| fun e ->\n            printfn \"%A\" e\n\n    // combine all together\n    let register (c : Container) =\n        Disposable.Create [\n            registerUpdate c\n            ]\n```\n\nAlternately, you can define systems as extension methods. This way is more OOP-centric and avoids some redundancy in declarations.\n\n```fsharp\n[<AutoOpen>]\nmodule MovementSystem =\n    type Container with\n        member c.AddMovementUpdate() =\n            c.On<UpdatePositions> <| fun e ->\n                printfn \"%A\" e\n                \n        member c.AddMovementSystems() =  \n            Disposable.Create [\n                c.AddMovementUpdate()\n                ]\n```\n\n### Execution\n\nWhen 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.\n\n```fsharp\n// run the container\nc.Process()\n```\n\n### Events\n\nLike 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. \n\n```fsharp\n[<Struct>] type UpdateTime = { dt : float32 }\n\n// call sub.Dispose() to unsubscribe\nlet sub =\n    c.On<UpdateTime> <| fun e ->\n        // [do update here]\n        printfn \"%A\" e\n\n// send event        \nc.Send { dt = 0.1f }\n```\n\nEvents 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).\n\n### Coroutines\n\nCoroutines 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.\n\nCoroutines 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.\n\n```fsharp\nlet system =\n    c.On<Msg> <| fun e ->\n        printf \"2 \"\n\n// start a coroutine\nc.Start <| seq {\n    printf \"1 \"\n    // send message and defer execution until all messages and\n    // coroutines created as a result of this have completed\n    c.Send <| Msg()\n    yield Wait.All\n    printf \"3 \"\n    }\n\n// run until completion\n// output: 1 2 3\nc.Process()\n```\n\nTime-based coroutines are useful for animations or delayed effects. You can use any unit of time as long as it's consistent.\n\n```fsharp\n// start a coroutine\nc.Start <| seq {\n    for i = 1 to 5 do\n        printf \"[%d] \" i\n        // yield execution until time units pass\n        yield Wait.time 3L\n    }\n\n// run update loop\n// output: [1] 1 2 3 [2] 4 5 6 [3] 7 8 9\nfor i = 1 to 9 do\n    // increment time units and run pending coroutines\n    c.Step 1L\n    c.Process()\n    printf \"%d \" i\n```\n\n### Multithreading\n\nIt'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.\n\n### Event ordering\n\nFor 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.\n\nOne 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).\n\n```fsharp\n// events\ntype Update = struct end\ntype UpdatePhysicsBodies = struct end\ntype UpdateHashSpace = struct end\n\n// systems\nlet updateSystem =\n    c.On<Update> <| fun e -> \n        c.Start <| seq {\n            // sending and suspending execution to \n            // achieve ordering of sub-updates\n            c.Send <| UpdatePhysicsBodies()\n            yield Wait.All\n            c.Send <| UpdateHashSpace()\n            yield Wait.All\n        }\nlet system1 = \n    c.On<UpdatePhysicsBodies> <| fun e ->\n        // [update positions]\n        printfn \"%A\" e\nlet system2 = \n    c.On<UpdateHashSpace> <| fun e ->\n        // [update hash space from positions]\n        printfn \"%A\" e\n```\n\n### Composing systems\n\nSince systems are just named event subscriptions, you can compose them into larger systems. This allows for bundling related functionality.\n\n```fsharp\nmodule CoreSystems =        \n    let register (c : Container) =\n        Disposable.Create [\n            MovementSystem.register c\n            HashSpaceSystem.register c\n        ]\n```\n\n## Actors\n\nWhile 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.\n\n### Definitions\n\nActors 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.\n\n```fsharp\n// message types\ntype Ping = struct end\ntype Pong = struct end\n\n// actor definitions\nlet a = new ActorSystem()\na.Register(ActorId 1, fun (c : Container) ->\n    c.On<Ping> <| fun e -> \n        printf \"ping \"\n        c.Respond(Pong())\n    )\na.Register(ActorId 2, fun (c : Container) ->\n    c.On<Pong> <| fun e -> \n        printf \"pong \"\n    )\n    \n// send a message and run until all complete\n// output: ping pong\na.Send(ActorId 1, Ping(), sourceId = ActorId 2)\na.ProcessAll()\n```\n\n### Actor messages versus container events\n\nContainers 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.\n\n### Wrapping containers\n\nIt'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.\n\n### Replay debugging\n\nIf 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.\n\n### Message ordering\n\nMessages 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.\n\n### Multithreading\n\nYou 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.\n\n## Integration\n\nHow 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.\n\nSee [sample projects](https://github.com/bcarruthers/garnet/tree/master/samples) for integration with Veldrid and OpenAL.\n\n### Abstracting framework calls\n\nWhen 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:\n\n- **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.\n\n- **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.\n\n- **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. \n\n### Sending framework events\n\nFor 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.\n\n```fsharp\ntype Game() =\n    // ...\n    let world = Container()\n    // [configure container here]\n    override c.Update gt = \n        world.Run { deltaTime = gt.ElapsedGameTime }\n    override c.Draw gameTime = \n        world.Run <| Draw()\n```\n\n## FAQ\n\n- **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.\n\n- **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.\n\n- **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. \n\n## License\nThis project is licensed under the [MIT license](https://github.com/bcarruthers/garnet/blob/master/LICENSE).\n\n## Maintainer(s)\n\n- [@bcarruthers](https://github.com/bcarruthers)"
  },
  {
    "path": "RELEASE_NOTES.md",
    "content": "## 0.5.0 – 2021-10-16\n\n- Revised registry implementation\n- Renamed Get() to GetComponents()\n- Renamed GetInstance() registry methods to Get()\n- Renamed Entity.Contains() to Has()\n- Added more toolkit functionality\n\n## 0.4.0 – 2021-08-28\n\n- Added new querying code\n- Renamed various members for consistent PascalCase\n- Added toolkit project and samples\n\n## 0.3.0 – 2021-04-24\n\n- Performance: Implemented static type ID lookup for components\n- Performance: Changed to generic type for segment key mapping\n- Added separate recipient to actor message destinations\n- Refactored resource loading\n- Added more iteration options\n- Fixed iteration bugs\n\n## 0.2.0 – 2019-09-01\n\n- Cleaned up public interfaces\n- Removed experimental serialization code\n- Decoupled entity from container\n- Cleaned up event publishers\n- Rewrote actor system\n- Rewrote entity ID pooling and destruction\n- Switched to using buffers instead of lists\n\n## 0.1.0 – 2019-07-09\n\n- Initial version\n"
  },
  {
    "path": "appveyor.yml",
    "content": "version: 1.0.{build}\nimage: Visual Studio 2022\nbuild_script:\n- cmd: build.cmd\nartifacts:\n- path: publish\\*.nupkg\n  name: packages\ndeploy:\n- provider: GitHub\n  auth_token:\n    secure: qG7eOszX+IfotPE1mnKIg13cJTR1/t4aiI5coav1zcp71CZBXw4BZ6PfAU7jWZ4D\n  artifact: packages\n  draft: true"
  },
  {
    "path": "build.cmd",
    "content": "dotnet restore\ndotnet build --no-restore\ndotnet test --no-build --verbosity normal\ndotnet pack -c Release -o publish src\\Garnet\\Garnet.fsproj\ndotnet pack -c Release -o publish samples\\Garnet.Numerics\\Garnet.Numerics.fsproj\ndotnet pack -c Release -o publish samples\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\ndotnet pack -c Release -o publish samples\\Garnet.Processor\\Garnet.Processor.fsproj"
  },
  {
    "path": "samples/Garnet.Numerics/Garnet.Numerics.fsproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n  </PropertyGroup>\n  <PropertyGroup>\n    <Description>Numeric primitives and operations suitable for games. Supplements System.Numerics.</Description>\n    <PackageTags>math vectors bounds game</PackageTags>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Numerics.fs\" />\n    <Compile Include=\"Vectors.fs\" />\n    <Compile Include=\"Ranges.fs\" />\n    <Compile Include=\"Random.fs\" />\n    <Compile Include=\"Hashing.fs\" />\n    <Compile Include=\"Noise.fs\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "samples/Garnet.Numerics/Hashing.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System\n\nmodule Fnv1a =\n    [<Literal>]\n    let Prime = 0x1000193u\n\n    [<Literal>]\n    let Seed = 0x811c9dc5u\n\ntype Fnv1a() =\n    static member inline Combine(hash, value) =\n        (hash ^^^ value) * Fnv1a.Prime\n\n    static member inline Hash(x1 : uint32) =\n        let h = Fnv1a.Seed\n        let h = Fnv1a.Combine(h, x1)\n        h\n\n    static member inline Hash(x1 : uint32, x2 : uint32) =\n        let h = Fnv1a.Seed\n        let h = Fnv1a.Combine(h, x1)\n        let h = Fnv1a.Combine(h, x2)\n        h\n        \n    static member inline Hash(x1 : uint32, x2 : uint32, x3 : uint32) =\n        let h = Fnv1a.Seed\n        let h = Fnv1a.Combine(h, x1)\n        let h = Fnv1a.Combine(h, x2)\n        let h = Fnv1a.Combine(h, x3)\n        h\n\n    static member inline Hash(key : string) =\n        let mutable hash = Fnv1a.Seed\n        for i = 0 to key.Length - 1 do\n            let value = uint32 key.[i]\n            hash <- Fnv1a.Combine(hash, value)\n        hash\n        \nmodule Fnv1a64 =\n    [<Literal>]\n    let Prime = 0x100000001b3UL\n\n    [<Literal>]\n    let Seed = 0xcbf29ce484222325UL\n\ntype Fnv1a64() =\n    static member inline Combine(hash, value) =\n        (hash ^^^ value) * Fnv1a64.Prime\n\n    static member inline Hash(value : uint64) =\n        let h = Fnv1a64.Seed\n        let h = Fnv1a64.Combine(h, value)\n        h\n\n    static member inline Hash(x1 : uint64, x2 : uint64) =\n        let h = Fnv1a64.Seed\n        let h = Fnv1a64.Combine(h, x1)\n        let h = Fnv1a64.Combine(h, x2)\n        h\n        \n    static member inline Hash(x1 : uint64, x2 : uint64, x3 : uint64) =\n        let h = Fnv1a64.Seed\n        let h = Fnv1a64.Combine(h, x1)\n        let h = Fnv1a64.Combine(h, x2)\n        let h = Fnv1a64.Combine(h, x3)\n        h\n\n    static member inline Hash(key : string) =\n        let mutable hash = Fnv1a64.Seed\n        for i = 0 to key.Length - 1 do\n            let value = uint64 key.[i]\n            hash <- Fnv1a64.Combine(hash, value)\n        hash\n\nmodule XXHash =\n    [<Literal>]\n    let Prime32_1 = 2654435761u\n\n    [<Literal>]\n    let Prime32_2 = 2246822519u\n\n    [<Literal>]\n    let Prime32_3 = 3266489917u\n\n    [<Literal>]\n    let Prime32_4 = 668265263u\n\n    [<Literal>]\n    let Prime32_5 = 374761393u\n\ntype XXHash() =    \n    static member inline RotateLeft(value : uint32, count) =\n        (value <<< count) ||| (value >>> (32 - count))\n\n    static member inline Finalize(hash : uint32) =\n        let h = hash ^^^ (hash >>> 15)\n        let h = h * XXHash.Prime32_2\n        let h = h ^^^ (h >>> 13)\n        let h = h * XXHash.Prime32_3\n        let h = h ^^^ (h >>> 16)\n        h\n\n    static member inline Combine(hash : uint32, value : uint32) = \n        let h = hash + value * XXHash.Prime32_3\n        let h = XXHash.RotateLeft(h, 17) * XXHash.Prime32_4\n        h\n\n    static member inline Initialize(seed : uint32) =\n        seed + XXHash.Prime32_5\n\n    static member inline Initialize(seed : uint32, size : uint32) =\n        let h = seed + XXHash.Prime32_5\n        let h = h + size\n        h\n\n    static member inline Hash(seed : uint32, value : uint32) =        \n        let h = XXHash.Initialize(seed, 4u)\n        let h = XXHash.Combine(h, value)\n        XXHash.Finalize(h)\n\n    static member inline Hash(seed : uint32, x1 : uint32, x2 : uint32) =\n        let h = XXHash.Initialize(seed, 8u)\n        let h = XXHash.Combine(h, x1)\n        let h = XXHash.Combine(h, x2)\n        XXHash.Finalize(h)\n        \n    static member inline Hash(seed : uint32, x1 : uint32, x2 : uint32, x3 : uint32) =\n        let h = XXHash.Initialize(seed, 12u)\n        let h = XXHash.Combine(h, x1)\n        let h = XXHash.Combine(h, x2)\n        let h = XXHash.Combine(h, x3)\n        XXHash.Finalize(h)\n\n    static member inline Hash(seed : uint32, span : ReadOnlySpan<int>) =\n        let mutable h = XXHash.Initialize(seed, uint32 span.Length)\n        for i = 0 to span.Length - 1 do\n            h <- XXHash.Combine(h, uint32 span.[i])\n        XXHash.Finalize(h)\n\n    static member inline Hash(seed : uint32, span : ReadOnlySpan<uint64>) =\n        let mutable h = XXHash.Initialize(seed, uint32 (span.Length * 2))\n        for i = 0 to span.Length - 1 do\n            h <- XXHash.Combine(h, uint32 (span.[i] &&& 0xffffffffUL))\n            h <- XXHash.Combine(h, uint32 (span.[i] >>> 32))\n        XXHash.Finalize(h)\n\n    static member inline FinalizeToRange(hash, index, min, max) =\n        let h = XXHash.Combine(hash, uint32 index)\n        let h = XXHash.Finalize(h)\n        int (h % uint32 (max - min)) + min\n"
  },
  {
    "path": "samples/Garnet.Numerics/Noise.fs",
    "content": "﻿namespace Garnet.Numerics\n\n// Adapted for F# from Stefan Gustavson code\n// Simplex noise demystified:\n// https://weber.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf\n\n// Original license:\n// sdnoise1234, Simplex noise with true analytic\n// derivative in 1D to 4D.\n//\n// Copyright © 2003-2012, Stefan Gustavson\n//\n// Contact: stefan.gustavson@gmail.com\n//\n// This library is public domain software, released by the author\n// into the public domain in February 2011. You may do anything\n// you like with it. You may even remove all attributions,\n// but of course I'd appreciate it if you kept my name somewhere.\n// \n// This library is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n// General Public License for more details.\n//\n// This is an implementation of Perlin \"simplex noise\" over one\n// dimension (x), two dimensions (x,y), three dimensions (x,y,z)\n// and four dimensions (x,y,z,w). The analytic derivative is\n// returned, to make it possible to do lots of fun stuff like\n// flow animations, curl noise, analytic antialiasing and such.\n//\n// Visually, this noise is exactly the same as the plain version of\n// simplex noise provided in the file \"snoise1234.c\". It just returns\n// all partial derivatives in addition to the scalar noise value.\n\nopen System.Numerics\n\nmodule private SimplexNoise =\n    let private grad3 =\n        array2D\n            [[1.0f;1.0f;0.0f];[-1.0f;1.0f;0.0f];[1.0f;-1.0f;0.0f];[-1.0f;-1.0f;0.0f];\n            [1.0f;0.0f;1.0f];[-1.0f;0.0f;1.0f];[1.0f;0.0f;-1.0f];[-1.0f;0.0f;-1.0f];\n            [0.0f;1.0f;1.0f];[0.0f;-1.0f;1.0f];[0.0f;1.0f;-1.0f];[0.0f;-1.0f;-1.0f]];\n\n    let private grad4 =\n        array2D\n            [[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];\n            [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];\n            [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];\n            [-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];\n            [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];\n            [-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];\n            [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];\n            [-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]];\n\n    // A lookup table to traverse the simplex around a given point in 4D.\n    // Details can be found where this table is used; in the 4D noise method.\n    let private simplex =\n        array2D\n            [[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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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]];\n\n    // Permutation table. This is just a random jumble of all numbers 0-255;\n    // repeated twice to avoid wrapping the index at 255 for each lookup.\n    let private perm =\n        [| 151;160;137;91;90;15;\n        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;\n        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;\n        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;\n        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;\n        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;\n        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;\n        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;\n        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;\n        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;\n        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;\n        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;\n        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180;\n        151;160;137;91;90;15;\n        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;\n        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;\n        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;\n        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;\n        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;\n        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;\n        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;\n        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;\n        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;\n        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;\n        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;\n        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180 |]\n\n    let private Sqrt3 = 1.7320508075688772935274463415059f\n    let private F2 = 0.5f * (Sqrt3 - 1.0f);\n    let private G2 = (3.0f - Sqrt3) / 6.0f;\n\n    let inline private dot2 (g : float32[,]) gi x y = g.[gi, 0] * x + g.[gi, 1] * y\n\n    let sample2 x y =\n        //float n0, n1, n2; // Noise contributions from the three corners\n        // Skew the input space to determine which simplex cell we're in\n        let s = (x + y) * F2; // Hairy factor for 2D\n\n        //int i = Floor(x + s);\n        //int j = Floor(y + s);\n        let realI = x + s\n        let realJ = y + s\n        let i = int(if realI > 0.0f then realI else realI - 1.0f)\n        let j = int(if realJ > 0.0f then realJ else realJ - 1.0f)\n    \n        let t = float32(i + j) * G2;\n        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y) space\n        let Y0 = float32(j) - t;\n        let x0 = x - X0; // The x,y distances from the cell origin\n        let y0 = y - Y0;\n    \n        // For the 2D case, the simplex shape is an equilateral triangle.\n        // Determine which simplex we are in.\n        //let i1, j1 =                // Offsets for second (middle) corner of simplex in (i,j) coords\n        //    if (x0 > y0) then 1, 0  // lower triangle, XY order: (0,0)->(1,0)->(1,1)\n        //    else 0, 1               // upper triangle, YX order: (0,0)->(0,1)->(1,1)\n        let i1 = if x0 > y0 then 1 else 0\n        let j1 = 1 - i1\n\n        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and\n        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where\n        // c = (3-sqrt(3))/6\n        let x1 = x0 - float32(i1) + G2; // Offsets for middle corner in (x,y) unskewed coords\n        let y1 = y0 - float32(j1) + G2;\n        let x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords\n        let y2 = y0 - 1.0f + 2.0f * G2;\n        // Work out the hashed gradient indices of the three simplex corners\n        let ii = i &&& 255;\n        let jj = j &&& 255;\n\n        //int gi0 = perm.[ii + perm.[jj]] % 12;\n        //int gi1 = perm.[ii + i1 + perm.[jj + j1]] % 12;\n        //int gi2 = perm.[ii + 1 + perm.[jj + 1]] % 12;\n\n        let gi0 = perm.[ii + perm.[jj]] % 12;\n        let gi1 = perm.[ii + i1 + perm.[jj + j1]] % 12;\n        let gi2 = perm.[ii + 1 + perm.[jj + 1]] % 12;\n\n        //(n * (n * n * 15731 + 789221) + 1376312589)\n\n        // Calculate the contribution from the three corners\n        let t0 = 0.5f - x0 * x0 - y0 * y0\n        let n0 =\n            if (t0 < 0.0f) then 0.0f\n            else\n                let t02 = t0 * t0\n                t02 * t02 * (dot2 grad3 gi0 x0 y0) // (x,y) of grad3 used for 2D gradient\n    \n        let t1 = 0.5f - x1 * x1 - y1 * y1\n        let n1 =\n            if (t1 < 0.0f) then 0.0f\n            else\n                let t12 = t1 * t1\n                t12 * t12 * (dot2 grad3 gi1 x1 y1)\n\n        let t2 = 0.5f - x2 * x2 - y2 * y2\n        let n2 =\n            if (t2 < 0.0f) then 0.0f\n            else\n                let t22 = t2 * t2\n                t22 * t22 * (dot2 grad3 gi2 x2 y2)\n\n        // Add contributions from each corner to get the final noise value.\n        // The result is scaled to return values in the interval [-1,1].\n        70.0f * (n0 + n1 + n2)\n\n    let private F3 = 1.0f / 3.0f\n    let private G3 = 1.0f / 6.0f; // Very nice and simple unskew factor, too\n\n    let inline private dot3 (g : float32[,]) gi x y z = g.[gi, 0] * x + g.[gi, 1] * y + g.[gi, 2] * z\n\n    let inline private floor (x : float32) = if x > 0.0f then int(x) else int(x) - 1\n\n    let sample3 x y z =\n        // Skew the input space to determine which simplex cell we're in\n        let s = (x + y + z) * F3 // Very nice and simple skew factor for 3D\n        let i = floor(x + s)\n        let j = floor(y + s)\n        let k = floor(z + s)\n        let t = float32(i + j + k) * G3\n        let X0 = float32(i) - t // Unskew the cell origin back to (x,y,z) space\n        let Y0 = float32(j) - t\n        let Z0 = float32(k) - t\n        let x0 = x - X0 // The x,y,z distances from the cell origin\n        let y0 = y - Y0\n        let z0 = z - Z0\n        // For the 3D case, the simplex shape is a slightly irregular tetrahedron.\n        // Determine which simplex we are in.\n        // Offsets for second corner of simplex in (i,j,k) coords\n        // Offsets for third corner of simplex in (i,j,k) coords\n        let struct(i1, j1, k1, i2, j2, k2) =\n            if (x0 >= y0) then\n                if (y0 >= z0) then 1, 0, 0, 1, 1, 0 // X Y Z order\n                else if (x0 >= z0) then 1, 0, 0, 1, 0, 1 // X Z Y order\n                else 0, 0, 1, 1, 0, 1 // Z X Y order\n            else \n                if (y0 < z0) then 0, 0, 1, 0, 1, 1 // Z Y X order\n                else if (x0 < z0) then 0, 1, 0, 0, 1, 1 // Y Z X order\n                else 0, 1, 0, 1, 1, 0 // Y X Z order\n\n        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),\n        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and\n        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where\n        // c = 1/6.\n        let x1 = x0 - float32(i1) + G3 // Offsets for second corner in (x,y,z) coords\n        let y1 = y0 - float32(j1) + G3\n        let z1 = z0 - float32(k1) + G3\n        let x2 = x0 - float32(i2) + 2.0f * G3 // Offsets for third corner in (x,y,z) coords\n        let y2 = y0 - float32(j2) + 2.0f * G3\n        let z2 = z0 - float32(k2) + 2.0f * G3\n        let x3 = x0 - 1.0f + 3.0f * G3 // Offsets for last corner in (x,y,z) coords\n        let y3 = y0 - 1.0f + 3.0f * G3\n        let z3 = z0 - 1.0f + 3.0f * G3\n        // Work out the hashed gradient indices of the four simplex corners\n        let ii = i &&& 255\n        let jj = j &&& 255\n        let kk = k &&& 255\n        let gi0 = perm.[ii + perm.[jj + perm.[kk]]] % 12\n        let gi1 = perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1]]] % 12\n        let gi2 = perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2]]] % 12\n        let gi3 = perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1]]] % 12\n        // Calculate the contribution from the four corners\n        let t0 = 0.5f - x0 * x0 - y0 * y0 - z0 * z0\n        let n0 =\n            if (t0 < 0.0f) then 0.0f\n            else\n                let t02 = t0 * t0\n                t02 * t02 * (dot3 grad3 gi0 x0 y0 z0)\n\n        let t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1\n        let n1 =\n            if (t1 < 0.0f) then 0.0f\n            else\n                let t12 = t1 * t1\n                t12 * t12 * (dot3 grad3 gi1 x1 y1 z1)\n\n        let t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2\n        let n2 =\n            if (t2 < 0.0f) then 0.0f\n            else\n                let t22 = t2 * t2\n                t22 * t22 * (dot3 grad3 gi2 x2 y2 z2)\n\n        let t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3\n        let n3 =\n            if (t3 < 0.0f) then 0.0f\n            else\n                let t32 = t3 * t3\n                t32 * t32 * (dot3 grad3 gi3 x3 y3 z3)\n\n        // Add contributions from each corner to get the final noise value.\n        // The result is scaled to stay just inside [-1,1]\n        32.0f * (n0 + n1 + n2 + n3)\n\n    let private F4 = 0.309016994f // F4 = (Math.sqrt(5.0)-1.0)/4.0\n    let private G4 = 0.138196601f // G4 = (5.0-Math.sqrt(5.0))/20.0\n\n    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\n\n    let sample4 x y z w =\n        // The skewing and unskewing factors are hairy again for the 4D case\n        //double n0, n1, n2, n3, n4; // Noise contributions from the five corners\n        // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in\n        let s = (x + y + z + w) * F4; // Factor for 4D skewing\n        let i = floor(x + s);\n        let j = floor(y + s);\n        let k = floor(z + s);\n        let l = floor(w + s);\n        let t = float32(i + j + k + l) * G4; // Factor for 4D unskewing\n        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z,w) space\n        let Y0 = float32(j) - t;\n        let Z0 = float32(k) - t;\n        let W0 = float32(l) - t;\n        let x0 = x - X0; // The x,y,z,w distances from the cell origin\n        let y0 = y - Y0;\n        let z0 = z - Z0;\n        let w0 = w - W0;\n        // For the 4D case, the simplex is a 4D shape I won't even try to describe.\n        // To find out which of the 24 possible simplices we're in, we need to\n        // determine the magnitude ordering of x0, y0, z0 and w0.\n        // The method below is a good way of finding the ordering of x,y,z,w and\n        // then find the correct traversal order for the simplex we’re in.\n        // First, six pair-wise comparisons are performed between each possible pair\n        // of the four coordinates, and the results are used to add up binary bits\n        // for an integer index.\n        let c1 = if (x0 > y0) then 32 else 0\n        let c2 = if (x0 > z0) then 16 else 0\n        let c3 = if (y0 > z0) then 8 else 0\n        let c4 = if (x0 > w0) then 4 else 0\n        let c5 = if (y0 > w0) then 2 else 0\n        let c6 = if (z0 > w0) then 1 else 0\n        let c = c1 + c2 + c3 + c4 + c5 + c6;\n    //    int i1, j1, k1, l1; // The integer offsets for the second simplex corner\n    //    int i2, j2, k2, l2; // The integer offsets for the third simplex corner\n    //    int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner\n        // simplex.[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.\n        // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w\n        // impossible. Only the 24 indices which have non-zero entries make any sense.\n        // We use a thresholding to set the coordinates in turn from the largest magnitude.\n        // The number 3 in the \"simplex\" array is at the position of the largest coordinate.\n        let i1 = if simplex.[c, 0] >= 3 then 1 else 0;\n        let j1 = if simplex.[c, 1] >= 3 then 1 else 0;\n        let k1 = if simplex.[c, 2] >= 3 then 1 else 0;\n        let l1 = if simplex.[c, 3] >= 3 then 1 else 0;\n\n        // The number 2 in the \"simplex\" array is at the second largest coordinate.\n        let i2 = if simplex.[c, 0] >= 2 then 1 else 0;\n        let j2 = if simplex.[c, 1] >= 2 then 1 else 0;\n        let k2 = if simplex.[c, 2] >= 2 then 1 else 0;\n        let l2 = if simplex.[c, 3] >= 2 then 1 else 0;\n\n        // The number 1 in the \"simplex\" array is at the second smallest coordinate.\n        let i3 = if simplex.[c, 0] >= 1 then 1 else 0;\n        let j3 = if simplex.[c, 1] >= 1 then 1 else 0;\n        let k3 = if simplex.[c, 2] >= 1 then 1 else 0;\n        let l3 = if simplex.[c, 3] >= 1 then 1 else 0;\n\n        // The fifth corner has all coordinate offsets = 1, so no need to look that up.\n        let x1 = x0 - float32(i1) + G4; // Offsets for second corner in (x,y,z,w) coords\n        let y1 = y0 - float32(j1) + G4;\n        let z1 = z0 - float32(k1) + G4;\n        let w1 = w0 - float32(l1) + G4;\n        let x2 = x0 - float32(i2) + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords\n        let y2 = y0 - float32(j2) + 2.0f * G4;\n        let z2 = z0 - float32(k2) + 2.0f * G4;\n        let w2 = w0 - float32(l2) + 2.0f * G4;\n        let x3 = x0 - float32(i3) + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords\n        let y3 = y0 - float32(j3) + 3.0f * G4;\n        let z3 = z0 - float32(k3) + 3.0f * G4;\n        let w3 = w0 - float32(l3) + 3.0f * G4;\n        let x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords\n        let y4 = y0 - 1.0f + 4.0f * G4;\n        let z4 = z0 - 1.0f + 4.0f * G4;\n        let w4 = w0 - 1.0f + 4.0f * G4;\n\n        // Work out the hashed gradient indices of the five simplex corners\n        let ii = i &&& 255;\n        let jj = j &&& 255;\n        let kk = k &&& 255;\n        let ll = l &&& 255;\n        let gi0 = perm.[ii + perm.[jj + perm.[kk + perm.[ll]]]] % 32;\n        let gi1 = perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1 + perm.[ll + l1]]]] % 32;\n        let gi2 = perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2 + perm.[ll + l2]]]] % 32;\n        let gi3 = perm.[ii + i3 + perm.[jj + j3 + perm.[kk + k3 + perm.[ll + l3]]]] % 32;\n        let gi4 = perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1 + perm.[ll + 1]]]] % 32;\n\n        // Calculate the contribution from the five corners\n        let t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;\n        let n0 =\n            if (t0 < 0.0f) then 0.0f\n            else\n                let t02 = t0 * t0;\n                t02 * t02 * (dot4 grad4 gi0 x0 y0 z0 w0)\n    \n        let t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;\n        let n1 =\n            if (t1 < 0.0f) then 0.0f\n            else\n                let t12 = t1 * t1;\n                t12 * t12 * (dot4 grad4 gi1 x1 y1 z1 w1)\n\n        let t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;\n        let n2 =\n            if (t2 < 0.0f) then 0.0f\n            else\n                let t22 = t2 * t2;\n                t22 * t22 * (dot4 grad4 gi2 x2 y2 z2 w2);\n\n        let t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;\n        let n3 =\n            if (t3 < 0.0f) then 0.0f\n            else\n                let t32 = t3 * t3;\n                t32 * t32 * (dot4 grad4 gi3 x3 y3 z3 w3);\n\n        let t4 = 0.6f - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;\n        let n4 =\n            if (t4 < 0.0f) then 0.0f\n            else\n                let t42 = t4 * t4;\n                t42 * t42 * (dot4 grad4 gi4 x4 y4 z4 w4);\n\n        // Sum up and scale the result to cover the range [-1,1]\n        27.0f * (n0 + n1 + n2 + n3 + n4);\n\n// This code overlaps a lot with above, but also includes gradient calc\nmodule private GradientNoise =\n    let private floor (x : float32) = if x > 0.0f then int(x) else int(x) - 1\n\n    // Permutation table. This is just a random jumble of all numbers 0-255,\n    // repeated twice to avoid wrapping the index at 255 for each lookup.\n    let private perm = [| 151;160;137;91;90;15;\n        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;\n        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;\n        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;\n        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;\n        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;\n        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;\n        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;\n        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;\n        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;\n        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;\n        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;\n        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180;\n        151;160;137;91;90;15;\n        131;13;201;95;96;53;194;233;7;225;140;36;103;30;69;142;8;99;37;240;21;10;23;\n        190; 6;148;247;120;234;75;0;26;197;62;94;252;219;203;117;35;11;32;57;177;33;\n        88;237;149;56;87;174;20;125;136;171;168; 68;175;74;165;71;134;139;48;27;166;\n        77;146;158;231;83;111;229;122;60;211;133;230;220;105;92;41;55;46;245;40;244;\n        102;143;54; 65;25;63;161; 1;216;80;73;209;76;132;187;208; 89;18;169;200;196;\n        135;130;116;188;159;86;164;100;109;198;173;186; 3;64;52;217;226;250;124;123;\n        5;202;38;147;118;126;255;82;85;212;207;206;59;227;47;16;58;17;182;189;28;42;\n        223;183;170;213;119;248;152; 2;44;154;163; 70;221;153;101;155;167; 43;172;9;\n        129;22;39;253; 19;98;108;110;79;113;224;232;178;185; 112;104;218;246;97;228;\n        251;34;242;193;238;210;144;12;191;179;162;241; 81;51;145;235;249;14;239;107;\n        49;192;214; 31;181;199;106;157;184; 84;204;176;115;121;50;45;127; 4;150;254;\n        138;236;205;93;222;114;67;29;24;72;243;141;128;195;78;66;215;61;156;180 \n    |]\n    \n    // Gradient tables. These could be programmed the Ken Perlin way with\n    // some clever bit-twiddling, but this is more clear, and not really slower.\n    let private grad2lut =\n        [ -1.0f; -1.0f;   1.0f; 0.0f;  -1.0f; 0.0f;  1.0f; 1.0f;\n            -1.0f; 1.0f;   0.0f; -1.0f;   0.0f; 1.0f;  1.0f; -1.0f ]\n\n    // Gradient directions for 3D.\n    // These vectors are based on the midpoints of the 12 edges of a cube.\n    // A larger array of random unit length vectors would also do the job,\n    // but these 12 (including 4 repeats to make the array length a power\n    // of two) work better. They are not random, they are carefully chosen\n    // to represent a small, isotropic set of directions.\n    let private grad3lut =\n        array2D [\n            [ 1.0f; 0.0f; 1.0f ]; [ 0.0f; 1.0f; 1.0f ]; // 12 cube edges\n            [ -1.0f; 0.0f; 1.0f ]; [ 0.0f; -1.0f; 1.0f ];\n            [ 1.0f; 0.0f; -1.0f ]; [ 0.0f; 1.0f; -1.0f ];\n            [ -1.0f; 0.0f; -1.0f ]; [ 0.0f; -1.0f; -1.0f ];\n            [ 1.0f; -1.0f; 0.0f ]; [ 1.0f; 1.0f; 0.0f ];\n            [ -1.0f; 1.0f; 0.0f ]; [ -1.0f; -1.0f; 0.0f ];\n            [ 1.0f; 0.0f; 1.0f ]; [ -1.0f; 0.0f; 1.0f ]; // 4 repeats to make 16\n            [ 0.0f; 1.0f; -1.0f ]; [ 0.0f; -1.0f; -1.0f ] ]\n\n    let private grad4lut =\n        array2D [\n            [ 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\n            [ 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 ];\n            [ 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 ];\n            [ -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 ];\n            [ 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 ];\n            [ -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 ];\n            [ 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 ];\n            [ -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 ] ]\n\n    // A lookup table to traverse the simplex around a given point in 4D.\n    // Details can be found where this table is used; in the 4D noise method.\n    let private simplex =\n        array2D [\n            [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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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];\n            [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]];\n\n    // Helper functions to compute gradients in 1D to 4D and gradients-dot-residualvectors in 2D to 4D.\n\n    let private grad1 (hash: int) =\n        let h = hash &&& 15\n        let gx = 1.0f + float32(h &&& 7)   // Gradient value is one of 1.0, 2.0, ..., 8.0\n        if ((h &&& 8) <> 0) then -gx else gx   // Make half of the gradients negative\n\n    //    let grad2 (hash : int) =\n    //        let h = hash &&& 7\n    //        grad2lut.[h, 0], grad2lut.[h, 1]\n\n    let private grad3 (hash : int) =\n        let h = hash &&& 15\n        grad3lut.[h, 0], grad3lut.[h, 1], grad3lut.[h, 2]\n\n    let private grad4 (hash : int) =\n        let h = hash &&& 31;\n        grad4lut.[h, 0], grad4lut.[h, 1], grad4lut.[h, 2], grad4lut.[h, 3]\n\n    // 1D simplex noise with derivative. If the last argument is not null, the analytic derivative is also calculated.\n    let sample1 calcGrad (x : float32) =\n        let i0 = floor(x)\n        let i1 = i0 + 1\n        let x0 = x - float32(i0)\n        let x1 = x0 - 1.0f\n\n        let x20 = x0 * x0\n        let t0 = 1.0f - x20\n        //  if(t0 < 0.0f) t0 = 0.0f; // Never happens for 1D: x0<=1 always\n        let t20 = t0 * t0\n        let t40 = t20 * t20\n        let gx0 = grad1(perm.[i0 &&& 0xff])\n        let n0 = t40 * gx0 * x0\n\n        let x21 = x1 * x1\n        let t1 = 1.0f - x21\n        //  if(t1 < 0.0f) t1 = 0.0f; // Never happens for 1D: |x1|<=1 always\n        let t21 = t1 * t1\n        let t41 = t21 * t21\n        let gx1 = grad1(perm.[i1 &&& 0xff])\n        let n1 = t41 * gx1 * x1\n\n        // Compute derivative, if requested by supplying non-null pointer\n        // for the last argument\n        // Compute derivative according to:\n        //  *dnoise_dx = -8.0f * t20 * t0 * x0 * (gx0 * x0) + t40 * gx0;\n        //  *dnoise_dx += -8.0f * t21 * t1 * x1 * (gx1 * x1) + t41 * gx1;\n\n        // The maximum value of this noise is 8*(3/4)^4 = 2.53125\n        // A factor of 0.395 would scale to fit exactly within [-1,1], but\n        // to better match classic Perlin noise, we scale it down some more.\n        let value = 0.25f * (n0 + n1)\n        \n        if calcGrad then\n            let dx0 = t20 * t0 * gx0 * x20\n            let dx1 = t21 * t1 * gx1 * x21\n            let dx = (dx0 + dx1) * -8.0f + t40 * gx0 + t41 * gx1\n            struct(value, dx * 0.25f) // Scale derivative to match the noise scaling\n        else\n            struct(value, 0.0f)\n\n    // Skewing factors for 2D simplex grid:\n    // F2 = 0.5*(sqrt(3.0)-1.0)\n    // G2 = (3.0-Math.sqrt(3.0))/6.0\n    let private F2 = 0.366025403f\n    let private G2 = 0.211324865f\n    let private Scaling2 = 40.0f\n\n    let sample2 calcGrad (x: float32) (y: float32) =\n        // Skew the input space to determine which simplex cell we're in\n        let s = (x + y) * F2; // Hairy factor for 2D\n        let xs = x + s;\n        let ys = y + s;\n        let i = floor(xs);\n        let j = floor(ys);\n\n        let t = float32(i + j) * G2;\n        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y) space */\n        let Y0 = float32(j) - t;\n        let x0 = x - X0; // The x,y distances from the cell origin */\n        let y0 = y - Y0;\n\n        // For the 2D case, the simplex shape is an equilateral triangle.\n        // Determine which simplex we are in.\n        let i1, j1 =                    // Offsets for second (middle) corner of simplex in (i,j) coords */\n            if x0 > y0 then 1, 0  // lower triangle, XY order: (0,0)->(1,0)->(1,1) */\n            else 0, 1             // upper triangle, YX order: (0,0)->(0,1)->(1,1) */\n\n        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and\n        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where\n        // c = (3-sqrt(3))/6\n        let x1 = x0 - float32(i1) + G2; // Offsets for middle corner in (x,y) unskewed coords */\n        let y1 = y0 - float32(j1) + G2;\n        let x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords */\n        let y2 = y0 - 1.0f + 2.0f * G2;\n\n        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds */\n        let ii = i &&& 0xff;\n        let jj = j &&& 0xff;\n\n        // Calculate the contribution from the three corners */\n        let t0c = 0.5f - x0 * x0 - y0 * y0\n        let struct(t0, t20, t40, n0, gx0, gy0) =\n            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let h = (perm.[ii + perm.[jj]] &&& 7) <<< 1\n                let gx0, gy0 = grad2lut.[h], grad2lut.[h + 1]\n                //let gx0, gy0 = grad2(perm.[ii + perm.[jj]])\n                let t20 = t0c * t0c\n                let t40 = t20 * t20\n                let n0 = t40 * (gx0 * x0 + gy0 * y0)\n                struct(t0c, t20, t40, n0, gx0, gy0)\n\n        let t1c = 0.5f - x1 * x1 - y1 * y1\n        let struct(t1, t21, t41, n1, gx1, gy1) =\n            if t1c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let h = (perm.[ii + i1 + perm.[jj + j1]] &&& 7) <<< 1\n                let gx1, gy1 = grad2lut.[h], grad2lut.[h + 1]\n                //let gx1, gy1 = grad2(perm.[ii + i1 + perm.[jj + j1]])\n                let t21 = t1c * t1c\n                let t41 = t21 * t21\n                let n1 = t41 * (gx1 * x1 + gy1 * y1)\n                struct(t1c, t21, t41, n1, gx1, gy1)\n\n        let t2c = 0.5f - x2 * x2 - y2 * y2\n        let struct(t2, t22, t42, n2, gx2, gy2) =\n            if (t2c < 0.0f) then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let h = (perm.[ii + 1 + perm.[jj + 1]] &&& 7) <<< 1\n                let gx2, gy2 = grad2lut.[h], grad2lut.[h + 1]\n                //let gx2, gy2 = grad2(perm.[ii + 1 + perm.[jj + 1]])\n                let t22 = t2c * t2c\n                let t42 = t22 * t22\n                let n2 = t42 * (gx2 * x2 + gy2 * y2)\n                struct(t2c, t22, t42, n2, gx2, gy2)\n\n        // Add contributions from each corner to get the final noise value.\n        // The result is scaled to return values in the interval [-1,1].\n        let value = Scaling2 * (n0 + n1 + n2);\n        \n        if calcGrad then\n            //  A straight, unoptimised calculation would be like:\n            //    *dnoise_dx = -8.0f * t20 * t0 * x0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gx0;\n            //    *dnoise_dy = -8.0f * t20 * t0 * y0 * ( gx0 * x0 + gy0 * y0 ) + t40 * gy0;\n            //    *dnoise_dx += -8.0f * t21 * t1 * x1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gx1;\n            //    *dnoise_dy += -8.0f * t21 * t1 * y1 * ( gx1 * x1 + gy1 * y1 ) + t41 * gy1;\n            //    *dnoise_dx += -8.0f * t22 * t2 * x2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gx2;\n            //    *dnoise_dy += -8.0f * t22 * t2 * y2 * ( gx2 * x2 + gy2 * y2 ) + t42 * gy2;\n            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0);\n            let dnoise_dx = temp0 * x0;\n            let dnoise_dy = temp0 * y0;\n            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1);\n            let dnoise_dx1 = dnoise_dx + temp1 * x1;\n            let dnoise_dy1 = dnoise_dy + temp1 * y1;\n            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2);\n            let dnoise_dx2 = (dnoise_dx1 + temp2 * x2) * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2\n            let dnoise_dy2 = (dnoise_dy1 + temp2 * y2) * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2\n            struct(value, dnoise_dx2 * Scaling2, dnoise_dy2 * Scaling2)\n        else\n            struct(value, 0.0f, 0.0f)\n\n    // Skewing factors for 3D simplex grid:\n    // F3 = 1/3\n    // G3 = 1/6 */\n    let private F3 = 0.333333333f\n    let private G3 = 0.166666667f\n    let private Scaling3 = 28.0f\n\n    let sample3 calcGrad (x: float32) (y: float32) (z: float32) =\n        // Skew the input space to determine which simplex cell we're in */\n        let s = (x + y + z) * F3; // Very nice and simple skew factor for 3D\n        let xs = x + s;\n        let ys = y + s;\n        let zs = z + s;\n        let i = floor(xs);\n        let j = floor(ys);\n        let k = floor(zs);\n\n        let t = float32(i + j + k) * G3;\n        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z) space\n        let Y0 = float32(j) - t;\n        let Z0 = float32(k) - t;\n        let x0 = x - X0; // The x,y,z distances from the cell origin\n        let y0 = y - Y0;\n        let z0 = z - Z0;\n\n        // For the 3D case, the simplex shape is a slightly irregular tetrahedron.\n        // Determine which simplex we are in.\n        // i1, j1, k1: Offsets for second corner of simplex in (i,j,k) coords */\n        // i2, j2, k3: Offsets for third corner of simplex in (i,j,k) coords\n\n        let struct(i1, j1, k1, i2, j2, k2) =\n            if x0 >= y0 then\n                if y0 >= z0 then struct(1, 0, 0, 1, 1, 0) // X Y Z order \n                else if x0 >= z0 then struct(1, 0, 0, 1, 0, 1) // X Z Y order\n                else struct(0, 0, 1, 1, 0, 1) // Z X Y order\n            else // x0<y0\n                if y0 < z0 then struct(0, 0, 1, 0, 1, 1) // Z Y X order */\n                else if x0 < z0 then struct(0, 1, 0, 0, 1, 1) // Y Z X order */\n                else struct(0, 1, 0, 1, 1, 0) // Y X Z order */\n\n        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),\n        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and\n        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where\n        // c = 1/6.   */\n\n        let x1 = x0 - float32(i1) + G3; // Offsets for second corner in (x,y,z) coords */\n        let y1 = y0 - float32(j1) + G3;\n        let z1 = z0 - float32(k1) + G3;\n        let x2 = x0 - float32(i2) + 2.0f * G3; // Offsets for third corner in (x,y,z) coords */\n        let y2 = y0 - float32(j2) + 2.0f * G3;\n        let z2 = z0 - float32(k2) + 2.0f * G3;\n        let x3 = x0 - 1.0f + 3.0f * G3; // Offsets for last corner in (x,y,z) coords */\n        let y3 = y0 - 1.0f + 3.0f * G3;\n        let z3 = z0 - 1.0f + 3.0f * G3;\n\n        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds */\n        let ii = i &&& 0xff\n        let jj = j &&& 0xff\n        let kk = k &&& 0xff\n\n        // Calculate the contribution from the four corners */\n        let t0c = 0.6f - x0 * x0 - y0 * y0 - z0 * z0;\n        let struct(t0, n0, t20, t40, gx0, gy0, gz0) =\n            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let gx0, gy0, gz0 = grad3(perm.[ii + perm.[jj + perm.[kk]]]);\n                let t20 = t0c * t0c;\n                let t40 = t20 * t20;\n                let n0 = t40 * (gx0 * x0 + gy0 * y0 + gz0 * z0)\n                struct(t0c, n0, t20, t40, gx0, gy0, gz0);\n\n        let t1c = 0.6f - x1 * x1 - y1 * y1 - z1 * z1;\n        let struct(t1, n1, t21, t41, gx1, gy1, gz1) =\n            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let gx1, gy1, gz1 = grad3(perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1]]]);\n                let t21 = t1c * t1c;\n                let t41 = t21 * t21;\n                let n1 = t41 * (gx1 * x1 + gy1 * y1 + gz1 * z1);\n                struct(t1c, n1, t21, t41, gx1, gy1, gz1);\n\n        let t2c = 0.6f - x2 * x2 - y2 * y2 - z2 * z2;\n        let struct(t2, n2, t22, t42, gx2, gy2, gz2) =\n            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let gx2, gy2, gz2 = grad3(perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2]]]);\n                let t22 = t2c * t2c;\n                let t42 = t22 * t22;\n                let n2 = t42 * (gx2 * x2 + gy2 * y2 + gz2 * z2);\n                struct(t2c, n2, t22, t42, gx2, gy2, gz2);\n\n        let t3c = 0.6f - x3 * x3 - y3 * y3 - z3 * z3;\n        let struct(t3, n3, t23, t43, gx3, gy3, gz3) =\n            if t0c < 0.0f then struct(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) // no influence\n            else\n                let gx3, gy3, gz3 = grad3(perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1]]]);\n                let t23 = t3c * t3c;\n                let t43 = t23 * t23;\n                let n3 = t43 * (gx3 * x3 + gy3 * y3 + gz3 * z3);\n                struct(t3c, n3, t23, t43, gx3, gy3, gz3);\n\n        // Add contributions from each corner to get the final noise value.\n        // The result is scaled to return values in the range [-1,1] */\n        let value = 28.0f * (n0 + n1 + n2 + n3);\n\n        //if( ( NULL != dnoise_dx ) && ( NULL != dnoise_dy ) && ( NULL != dnoise_dz ))\n        if calcGrad then\n            ////  A straight, unoptimised calculation would be like:\n            ///     *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gx0;\n            ///    *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gy0;\n            ///    *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, x0, y0, z0) + t40 * gz0;\n            ///    *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gx1;\n            ///    *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gy1;\n            ///    *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, x1, y1, z1) + t41 * gz1;\n            ///    *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gx2;\n            ///    *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gy2;\n            ///    *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, x2, y2, z2) + t42 * gz2;\n            ///    *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gx3;\n            ///    *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gy3;\n            ///    *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, x3, y3, z3) + t43 * gz3;\n            ////\n            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0 + gz0 * z0);\n            let dnoise_dx = temp0 * x0;\n            let dnoise_dy = temp0 * y0;\n            let dnoise_dz = temp0 * z0;\n            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1 + gz1 * z1);\n            let dnoise_dx1 = dnoise_dx + temp1 * x1;\n            let dnoise_dy1 = dnoise_dy + temp1 * y1;\n            let dnoise_dz1 = dnoise_dz + temp1 * z1;\n            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2 + gz2 * z2);\n            let dnoise_dx2 = dnoise_dx1 + temp2 * x2;\n            let dnoise_dy2 = dnoise_dy1 + temp2 * y2;\n            let dnoise_dz2 = dnoise_dz1 + temp2 * z2;\n            let temp3 = t23 * t3 * (gx3 * x3 + gy3 * y3 + gz3 * z3);\n            let dnoise_dx3 = (dnoise_dx2 + temp3 * x3) * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3;\n            let dnoise_dy3 = (dnoise_dy2 + temp3 * y3) * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3;\n            let dnoise_dz3 = (dnoise_dz2 + temp3 * z3) * -8.0f + t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3;\n            struct(value, dnoise_dx3 * 28.0f, dnoise_dy3 * 28.0f, dnoise_dz3 * 28.0f)\n        else\n            struct(value, 0.0f, 0.0f, 0.0f)\n\n    // The skewing and unskewing factors are hairy again for the 4D case\n    let private F4 = 0.309016994f; // F4 = (Math.sqrt(5.0)-1.0)/4.0\n    let private G4 = 0.138196601f; // G4 = (5.0-Math.sqrt(5.0))/20.0\n    //let Scaling4 = 27.0f\n\n    //* 4D simplex noise with derivatives.\n    //* If the last four arguments are not null, the analytic derivative\n    //* (the 4D gradient of the scalar noise field) is also calculated.\n    //*/\n    let sample4 calcGrad (x: float32) (y: float32) (z: float32) (w: float32) =\n        // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in\n        let s = (x + y + z + w) * F4; // Factor for 4D skewing\n        let xs = x + s;\n        let ys = y + s;\n        let zs = z + s;\n        let ws = w + s;\n        let i = floor(xs);\n        let j = floor(ys);\n        let k = floor(zs);\n        let l = floor(ws);\n\n        let t = float32(i + j + k + l) * G4; // Factor for 4D unskewing\n        let X0 = float32(i) - t; // Unskew the cell origin back to (x,y,z,w) space\n        let Y0 = float32(j) - t;\n        let Z0 = float32(k) - t;\n        let W0 = float32(l) - t;\n\n        let x0 = x - X0;  // The x,y,z,w distances from the cell origin\n        let y0 = y - Y0;\n        let z0 = z - Z0;\n        let w0 = w - W0;\n\n        // For the 4D case, the simplex is a 4D shape I won't even try to describe.\n        // To find out which of the 24 possible simplices we're in, we need to\n        // determine the magnitude ordering of x0, y0, z0 and w0.\n        // The method below is a reasonable way of finding the ordering of x,y,z,w\n        // and then find the correct traversal order for the simplex we’re in.\n        // First, six pair-wise comparisons are performed between each possible pair\n        // of the four coordinates, and then the results are used to add up binary\n        // bits for an integer index into a precomputed lookup table, simplex[].\n        let c1 = if (x0 > y0) then 32 else 0;\n        let c2 = if (x0 > z0) then 16 else 0;\n        let c3 = if (y0 > z0) then 8 else 0;\n        let c4 = if (x0 > w0) then 4 else 0;\n        let c5 = if (y0 > w0) then 2 else 0;\n        let c6 = if (z0 > w0) then 1 else 0;\n        let c = c1 ||| c2 ||| c3 ||| c4 ||| c5 ||| c6; // '|' is mostly faster than '+'\n\n        // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.\n        // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w\n        // impossible. Only the 24 indices which have non-zero entries make any sense.\n        // We use a thresholding to set the coordinates in turn from the largest magnitude.\n        // The number 3 in the \"simplex\" array is at the position of the largest coordinate.\n        let i1 = if simplex.[c, 0] >= 3 then 1 else 0;\n        let j1 = if simplex.[c, 1] >= 3 then 1 else 0;\n        let k1 = if simplex.[c, 2] >= 3 then 1 else 0;\n        let l1 = if simplex.[c, 3] >= 3 then 1 else 0;\n        // The number 2 in the \"simplex\" array is at the second largest coordinate.\n        let i2 = if simplex.[c, 0] >= 2 then 1 else 0;\n        let j2 = if simplex.[c, 1] >= 2 then 1 else 0;\n        let k2 = if simplex.[c, 2] >= 2 then 1 else 0;\n        let l2 = if simplex.[c, 3] >= 2 then 1 else 0;\n        // The number 1 in the \"simplex\" array is at the second smallest coordinate.\n        let i3 = if simplex.[c, 0] >= 1 then 1 else 0;\n        let j3 = if simplex.[c, 1] >= 1 then 1 else 0;\n        let k3 = if simplex.[c, 2] >= 1 then 1 else 0;\n        let l3 = if simplex.[c, 3] >= 1 then 1 else 0;\n        // The fifth corner has all coordinate offsets = 1, so no need to look that up.\n\n        let x1 = x0 - float32(i1) + G4; // Offsets for second corner in (x,y,z,w) coords\n        let y1 = y0 - float32(j1) + G4;\n        let z1 = z0 - float32(k1) + G4;\n        let w1 = w0 - float32(l1) + G4;\n        let x2 = x0 - float32(i2) + 2.0f * G4; // Offsets for third corner in (x,y,z,w) coords\n        let y2 = y0 - float32(j2) + 2.0f * G4;\n        let z2 = z0 - float32(k2) + 2.0f * G4;\n        let w2 = w0 - float32(l2) + 2.0f * G4;\n        let x3 = x0 - float32(i3) + 3.0f * G4; // Offsets for fourth corner in (x,y,z,w) coords\n        let y3 = y0 - float32(j3) + 3.0f * G4;\n        let z3 = z0 - float32(k3) + 3.0f * G4;\n        let w3 = w0 - float32(l3) + 3.0f * G4;\n        let x4 = x0 - 1.0f + 4.0f * G4; // Offsets for last corner in (x,y,z,w) coords\n        let y4 = y0 - 1.0f + 4.0f * G4;\n        let z4 = z0 - 1.0f + 4.0f * G4;\n        let w4 = w0 - 1.0f + 4.0f * G4;\n\n        // Wrap the integer indices at 256, to avoid indexing perm.[] out of bounds\n        let ii = i &&& 0xff;\n        let jj = j &&& 0xff;\n        let kk = k &&& 0xff;\n        let ll = l &&& 0xff;\n\n        // Calculate the contribution from the five corners\n        let t0c = 0.6f - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;\n        let struct(n0, t0, t20, t40, gx0, gy0, gz0, gw0) =\n            if (t0c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence\n            else\n                let t20 = t0c * t0c;\n                let t40 = t20 * t20;\n                let gx0, gy0, gz0, gw0 = grad4(perm.[ii + perm.[jj + perm.[kk + perm.[ll]]]]);\n                let n0 = t40 * (gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0);\n                struct(n0, t0c, t20, t40, gx0, gy0, gz0, gw0)\n\n        let t1c = 0.6f - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;\n        let struct(n1, t1, t21, t41, gx1, gy1, gz1, gw1) =\n            if (t1c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence\n            else\n                let t21 = t1c * t1c;\n                let t41 = t21 * t21;\n                let gx1, gy1, gz1, gw1 = grad4(perm.[ii + i1 + perm.[jj + j1 + perm.[kk + k1 + perm.[ll + l1]]]]);\n                let n1 = t41 * (gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1);\n                struct(n1, t1c, t21, t41, gx1, gy1, gz1, gw1)\n\n        let t2c = 0.6f - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;\n        let struct(n2, t2, t22, t42, gx2, gy2, gz2, gw2) =\n            if (t2c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence\n            else\n                let t22 = t2c * t2c;\n                let t42 = t22 * t22;\n                let gx2, gy2, gz2, gw2 = grad4(perm.[ii + i2 + perm.[jj + j2 + perm.[kk + k2 + perm.[ll + l2]]]]);\n                let n2 = t42 * (gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2);\n                struct(n2, t2c, t22, t42, gx2, gy2, gz2, gw2)\n\n        let t3c = 0.6f - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;\n        let struct(n3, t3, t23, t43, gx3, gy3, gz3, gw3) =\n            if (t3c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence\n            else\n                let t23 = t3c * t3c;\n                let t43 = t23 * t23;\n                let gx3, gy3, gz3, gw3 = grad4(perm.[ii + i3 + perm.[jj + j3 + perm.[kk + k3 + perm.[ll + l3]]]]);\n                let n3 = t43 * (gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3);\n                struct(n3, t3c, t23, t43, gx3, gy3, gz3, gw3)\n\n        let t4c = 0.6f - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;\n        let struct(n4, t4, t24, t44, gx4, gy4, gz4, gw4) =\n            if (t4c < 0.0f) then 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f // no influence\n            else\n                let t24 = t4c * t4c;\n                let t44 = t24 * t24;\n                let gx4, gy4, gz4, gw4 = grad4(perm.[ii + 1 + perm.[jj + 1 + perm.[kk + 1 + perm.[ll + 1]]]]);\n                let n4 = t44 * (gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4);\n                struct(n4, t4c, t24, t44, gx4, gy4, gz4, gw4)\n\n        // Sum up and scale the result to cover the range [-1,1]\n        let value = 27.0f * (n0 + n1 + n2 + n3 + n4); // The scale factor is preliminary!\n\n        // Compute derivative, if requested by supplying non-null pointers\n        // for the last four arguments */\n        //if( ( NULL != dnoise_dx ) && ( NULL != dnoise_dy ) && ( NULL != dnoise_dz ) && ( NULL != dnoise_dw ) )\n        if calcGrad then\n            //  A straight, unoptimised calculation would be like:\n            //*     *dnoise_dx = -8.0f * t20 * t0 * x0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gx0;\n            //*    *dnoise_dy = -8.0f * t20 * t0 * y0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gy0;\n            //*    *dnoise_dz = -8.0f * t20 * t0 * z0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gz0;\n            //*    *dnoise_dw = -8.0f * t20 * t0 * w0 * dot(gx0, gy0, gz0, gw0, x0, y0, z0, w0) + t40 * gw0;\n            //*    *dnoise_dx += -8.0f * t21 * t1 * x1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gx1;\n            //*    *dnoise_dy += -8.0f * t21 * t1 * y1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gy1;\n            //*    *dnoise_dz += -8.0f * t21 * t1 * z1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gz1;\n            //*    *dnoise_dw = -8.0f * t21 * t1 * w1 * dot(gx1, gy1, gz1, gw1, x1, y1, z1, w1) + t41 * gw1;\n            //*    *dnoise_dx += -8.0f * t22 * t2 * x2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gx2;\n            //*    *dnoise_dy += -8.0f * t22 * t2 * y2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gy2;\n            //*    *dnoise_dz += -8.0f * t22 * t2 * z2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gz2;\n            //*    *dnoise_dw += -8.0f * t22 * t2 * w2 * dot(gx2, gy2, gz2, gw2, x2, y2, z2, w2) + t42 * gw2;\n            //*    *dnoise_dx += -8.0f * t23 * t3 * x3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gx3;\n            //*    *dnoise_dy += -8.0f * t23 * t3 * y3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gy3;\n            //*    *dnoise_dz += -8.0f * t23 * t3 * z3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gz3;\n            //*    *dnoise_dw += -8.0f * t23 * t3 * w3 * dot(gx3, gy3, gz3, gw3, x3, y3, z3, w3) + t43 * gw3;\n            //*    *dnoise_dx += -8.0f * t24 * t4 * x4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gx4;\n            //*    *dnoise_dy += -8.0f * t24 * t4 * y4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gy4;\n            //*    *dnoise_dz += -8.0f * t24 * t4 * z4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gz4;\n            //*    *dnoise_dw += -8.0f * t24 * t4 * w4 * dot(gx4, gy4, gz4, gw4, x4, y4, z4, w4) + t44 * gw4;\n            //*/\n            let temp0 = t20 * t0 * (gx0 * x0 + gy0 * y0 + gz0 * z0 + gw0 * w0);\n            let dnoise_dx = temp0 * x0;\n            let dnoise_dy = temp0 * y0;\n            let dnoise_dz = temp0 * z0;\n            let dnoise_dw = temp0 * w0;\n            let temp1 = t21 * t1 * (gx1 * x1 + gy1 * y1 + gz1 * z1 + gw1 * w1);\n            let dnoise_dx1 = dnoise_dx + temp1 * x1;\n            let dnoise_dy1 = dnoise_dy + temp1 * y1;\n            let dnoise_dz1 = dnoise_dz + temp1 * z1;\n            let dnoise_dw1 = dnoise_dw + temp1 * w1;\n            let temp2 = t22 * t2 * (gx2 * x2 + gy2 * y2 + gz2 * z2 + gw2 * w2);\n            let dnoise_dx2 = dnoise_dx1 + temp2 * x2;\n            let dnoise_dy2 = dnoise_dy1 + temp2 * y2;\n            let dnoise_dz2 = dnoise_dz1 + temp2 * z2;\n            let dnoise_dw2 = dnoise_dw1 + temp2 * w2;\n            let temp3 = t23 * t3 * (gx3 * x3 + gy3 * y3 + gz3 * z3 + gw3 * w3);\n            let dnoise_dx3 = dnoise_dx2 + temp3 * x3;\n            let dnoise_dy3 = dnoise_dy2 + temp3 * y3;\n            let dnoise_dz3 = dnoise_dz2 + temp3 * z3;\n            let dnoise_dw3 = dnoise_dw2 + temp3 * w3;\n            let temp4 = t24 * t4 * (gx4 * x4 + gy4 * y4 + gz4 * z4 + gw4 * w4);\n            let dnoise_dx4 = dnoise_dx3 + temp4 * x4;\n            let dnoise_dy4 = dnoise_dy3 + temp4 * y4;\n            let dnoise_dz4 = dnoise_dz3 + temp4 * z4;\n            let dnoise_dw4 = dnoise_dw3 + temp4 * w4;\n            let dnoise_dx5 = dnoise_dx4 * -8.0f + t40 * gx0 + t41 * gx1 + t42 * gx2 + t43 * gx3 + t44 * gx4;\n            let dnoise_dy5 = dnoise_dy4 * -8.0f + t40 * gy0 + t41 * gy1 + t42 * gy2 + t43 * gy3 + t44 * gy4;\n            let dnoise_dz5 = dnoise_dz4 * -8.0f + t40 * gz0 + t41 * gz1 + t42 * gz2 + t43 * gz3 + t44 * gz4;\n            let dnoise_dw5 = dnoise_dw4 * -8.0f + t40 * gw0 + t41 * gw1 + t42 * gw2 + t43 * gw3 + t44 * gw4;\n            struct(value, dnoise_dx5 * 28.0f, dnoise_dy5 * 28.0f, dnoise_dz5 * 28.0f, dnoise_dw5 * 28.0f)\n        else\n            struct(value, 0.0f, 0.0f, 0.0f, 0.0f)\n\ntype Noise() =\n    static member Sample(x) =\n        let struct(s, _) = GradientNoise.sample1 false x\n        s\n        \n    static member Sample(p : Vector2) =\n        SimplexNoise.sample2 p.X p.Y\n\n    static member Sample(p : Vector3) =\n        SimplexNoise.sample3 p.X p.Y p.Z\n\n    static member Sample(p : Vector4) =\n        SimplexNoise.sample4 p.X p.Y p.Z p.W\n                "
  },
  {
    "path": "samples/Garnet.Numerics/Numerics.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\n[<AutoOpen>]\nmodule MathF =\n    type MathF with\n        static member inline Lerp(min, max, t : float32) =\n            min * (1.0f - t) + max * t\n\n        static member inline Clamp(s0 : float32, s1 : float32, s : float32) =\n            s |> max s0 |> min s1\n            \n        static member inline Clamp01(x) =\n            MathF.Clamp(0.0f, 1.0f, x)\n\n        static member inline LinearStep(s0, s1, s) =\n            let length = s1 - s0\n            if abs length < 1e-7f then 0.0f\n            else MathF.Clamp01((s - s0) / length)\n\n        static member inline SmoothStep(s0, s1, s) =\n            let x = MathF.LinearStep(s0, s1, s)\n            x * x * (3.0f - 2.0f * x)\n\n[<AutoOpen>]\nmodule Matrix4x4 =\n    type Matrix4x4 with\n        member m.GetInverseOrIdentity() =\n            let mutable mInv = Matrix4x4.Identity\n            if Matrix4x4.Invert(m, &mInv) then mInv else Matrix4x4.Identity\n"
  },
  {
    "path": "samples/Garnet.Numerics/Random.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\ntype PcgRandom(initState, initSeq) =\n    let inc = PcgRandom.GetIncrement(initSeq)\n    let mutable state = PcgRandom.GetState(initState, inc)\n    new() = PcgRandom(0x853c49e6748fea9bUL, 0xda3e39cb94b95bdbUL)\n    \n    member c.NextUInt32() =\n        let result = PcgRandom.GetUInt32(state)\n        state <- PcgRandom.GetNextState(state, inc)\n        result\n    \n    static member GetUInt32(state) =\n        let xorShifted = uint32 (((state >>> 18) ^^^ state) >>> 27)\n        let rot = int (state >>> 59)\n        (xorShifted >>> rot) ||| (xorShifted <<< ((-rot) &&& 31))\n    \n    static member GetNextState(state, inc) =        \n        state * 6364136223846793005UL + inc\n    \n    static member GetIncrement(initSeq) =\n        (initSeq <<< 1) ||| 1UL\n    \n    static member GetState(seed, inc) =\n        let s = PcgRandom.GetNextState(0UL, inc) + seed\n        PcgRandom.GetNextState(s, inc)\n\ntype PcgRandom with\n    member c.NextInt32() =\n        c.NextUInt32() |> int\n\n    member c.NextUInt32Unbiased(exclusiveBound : uint) =\n        let threshold = uint32 ((0x100000000UL - uint64 exclusiveBound) % uint64 exclusiveBound)\n        let mutable r = c.NextUInt32()\n        while r < threshold do\n            r <- c.NextUInt32()\n        r % exclusiveBound\n\n    member c.NextInt32Unbiased(exclusiveBound : int) =\n        let threshold = int32 ((0x100000000UL - uint64 exclusiveBound) % uint64 exclusiveBound)\n        let mutable r = c.NextInt32()\n        while r < threshold do\n            r <- c.NextInt32()\n        r % exclusiveBound\n\n    member c.NextInt32(max : int) =\n        if max = 0 then 0 else c.NextUInt32() % (uint32 max) |> int\n\n    member c.NextUInt32(max : uint) =\n        if max = 0u then 0u else c.NextUInt32() % max\n\n    member c.NextUInt32(min, max) =\n        c.NextUInt32(max - min) + min\n\n    member c.NextInt32(min, max) =\n        c.NextInt32(max - min) + min\n\n    /// Closed [0, 1]\n    member c.NextDouble() =\n        let x = c.NextUInt32()\n        double x * (1.0 / 4294967295.0)\n\n    /// Half open [0, 1)\n    member c.NextDouble2() = \n        let x = c.NextUInt32()\n        double x * (1.0 / 4294967296.0)\n\n    /// Open (0, 1)\n    member c.NextDouble3() = \n        let x = c.NextUInt32()\n        (double x + 0.5) * (1.0 / 4294967296.0)\n\n    member c.NextSingle() = float32 (c.NextDouble())\n    member c.NextSingle2() = float32 (c.NextDouble2())\n    member c.NextSingle3() = float32 (c.NextDouble3())\n\n    member c.NextSingle(min, max) =\n        c.NextSingle() * (max - min) + min\n\n    member c.NextRotation(radiansRange) =\n        Vector2.FromRadians((c.NextSingle() - 0.5f) * radiansRange) \n\n    member c.NextRotationDegrees(degreesRange) =\n        c.NextRotation(degreesRange * MathF.PI / 180.0f)\n\n    member c.NextVector2() =\n        Vector2(c.NextSingle(), c.NextSingle())\n        \n    member c.NextVector2(range : Range2) =\n        Range2.Lerp(range, c.NextVector2())\n\n    member c.NextVector3() =\n        Vector3(c.NextSingle(), c.NextSingle(), c.NextSingle())\n        \n    member c.NextVector3(range : Range3) =\n        Range3.Lerp(range, c.NextVector3())\n\n    member c.NextVector2i(r : Range2i) =\n        Vector2i(\n            c.NextInt32(r.Min.X, r.Max.X),\n            c.NextInt32(r.Min.Y, r.Max.Y))\n\n    member c.NextVector2i(min, max) =\n        let r = Rangei(min, max)\n        c.NextVector2i(Range2i(r, r))\n\n    member c.NextVector3i(r : Range3i) =\n        Vector3i(\n            c.NextInt32(r.Min.X, r.Max.X),\n            c.NextInt32(r.Min.Y, r.Max.Y),\n            c.NextInt32(r.Min.Z, r.Max.Z))\n\n    member c.NextVector3i(min, max) =\n        let r = Rangei(min, max)\n        c.NextVector3i(Range3i(r, r, r))\n\n    member c.Next(dest : Span<int>) =\n        for i = 0 to dest.Length - 1 do\n            dest.[i] <- c.NextInt32()\n        \n    member c.Shuffle(items) =\n        let r = items |> Seq.toArray\n        for i = 0 to r.Length - 2 do\n            let j = c.NextInt32(i, r.Length)\n            let temp = r.[i]\n            r.[i] <- r.[j]\n            r.[j] <- temp    \n        r\n        "
  },
  {
    "path": "samples/Garnet.Numerics/Ranges.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System.Numerics\n\n// Float range types\n\n[<Struct>]\ntype Range =\n    val Min : float32\n    val Max : float32\n    new(min, max) = { Min = min; Max = max }\n    member inline c.Size = c.Max - c.Min\n    member inline c.IsEmpty = c.Min >= c.Max\n    member inline c.Contains(x) = x >= c.Min && x < c.Max\n    member inline c.Expand(margin) = Range(c.Min - margin, c.Max + margin)\n    member inline c.Clamp(x) = x |> max c.Min |> min c.Max\n    override v.ToString() = $\"%A{v.Min} to %A{v.Max}\"\n    static member Zero = Range(0.0f, 0.0f)\n    static member ZeroToOne = Range(0.0f, 1.0f)\n    static member inline Lerp(r : Range, t) = r.Min * (1.0f - t) + r.Max * t\n    static member inline (+) (a: Range, c) = Range(a.Min + c, a.Max + c)\n    static member inline (-) (a: Range, c) = Range(a.Min - c, a.Max - c)\n    static member inline (*) (a: Range, c) = Range(a.Min * c, a.Max * c)\n    static member inline (/) (a: Range, c) = Range(a.Min / c, a.Max / c)\n    static member inline Point(point) = Range(point, point)\n    static member inline Sized(min, size) = \n        Range(min, min + size)\n    static member inline Centered(center, size) = \n        Range.Sized(center - size * 0.5f, size)\n    static member inline Intersection(a : Range, b : Range) = \n        Range(max a.Min b.Min, min a.Max b.Max)\n    static member inline Union(a : Range, b : Range) = \n        Range(min a.Min b.Min, max a.Max b.Max)\n\n[<Struct>]\ntype Range2 =\n    val Min : Vector2\n    val Max : Vector2\n    new(min, max) = { Min = min; Max = max }\n    new(x : Range, y : Range) = { \n        Min = Vector2(x.Min, y.Min)\n        Max = Vector2(x.Max, y.Max) \n        }\n    member inline c.X = Range(c.Min.X, c.Max.X)\n    member inline c.Y = Range(c.Min.Y, c.Max.Y)\n    member inline c.Center = (c.Min + c.Max) * 0.5f\n    member inline c.Size = c.Max - c.Min\n    member inline c.GetArea() =\n        let s = c.Size\n        s.X * s.Y\n    member inline c.Contains(p : Vector2) = \n        c.X.Contains p.X && c.Y.Contains p.Y\n    member inline c.Expand(margin : Vector2) = \n        Range2(\n            c.X.Expand(margin.X),\n            c.Y.Expand(margin.Y))\n    override i.ToString() = $\"%A{i.Min} to %A{i.Max}\"\n    static member Zero = Range2(Vector2.Zero, Vector2.Zero)\n    static member ZeroToOne = Range2(Vector2.Zero, Vector2.One)\n    static member inline Lerp(r : Range2, t : Vector2) =\n        Vector2(\n            Range.Lerp(r.X, t.X),\n            Range.Lerp(r.Y, t.Y))\n    static member inline (+) (a: Range2, c : float32) = Range2(a.X + c, a.Y + c)\n    static member inline (-) (a: Range2, c : float32) = Range2(a.X - c, a.Y - c)\n    static member inline (*) (a: Range2, c : float32) = Range2(a.X * c, a.Y * c)\n    static member inline (/) (a: Range2, c : float32) = Range2(a.X / c, a.Y / c)\n    static member inline (+) (a: Range2, v : Vector2) = Range2(a.X + v.X, a.Y + v.Y)\n    static member inline (-) (a: Range2, v : Vector2) = Range2(a.X - v.X, a.Y - v.Y)\n    static member inline (*) (a: Range2, v : Vector2) = Range2(a.X * v.X, a.Y * v.Y)\n    static member inline (/) (a: Range2, v : Vector2) = Range2(a.X / v.X, a.Y / v.Y)\n    static member inline Point(point : Vector2) = Range2(point, point)\n    static member inline Sized(min : Vector2, size : Vector2) = \n        Range2(min, min + size)\n    static member inline Centered(center : Vector2, size : Vector2) = \n        Range2.Sized(center - size * 0.5f, size)\n    static member inline Intersection(a : Range2, b : Range2) = \n        Range2(\n            Range.Intersection(a.X, b.X), \n            Range.Intersection(a.Y, b.Y))\n    static member inline Union(a : Range2, b : Range2) =         \n        Range2(\n            Range.Union(a.X, b.X), \n            Range.Union(a.Y, b.Y))\n\n[<Struct>]\ntype Range3 =\n    val Min : Vector3\n    val Max : Vector3\n    new(min, max) = { Min = min; Max = max }\n    new(x : Range, y : Range, z : Range) = { \n        Min = Vector3(x.Min, y.Min, z.Min)\n        Max = Vector3(x.Max, y.Max, z.Max) \n        }\n    member inline c.X = Range(c.Min.X, c.Max.X)\n    member inline c.Y = Range(c.Min.Y, c.Max.Y)\n    member inline c.Z = Range(c.Min.Z, c.Max.Z)\n    member inline c.Size = c.Max - c.Min\n    member inline c.GetVolume() =\n        let s = c.Size\n        s.X * s.Y * s.Z\n    member inline c.Contains(p : Vector3) = \n        c.X.Contains p.X && \n        c.Y.Contains p.Y && \n        c.Z.Contains p.Z\n    member inline c.Expand(margin : Vector3) = \n        Range3(\n            c.X.Expand(margin.X),\n            c.Y.Expand(margin.Y),\n            c.Z.Expand(margin.Z))\n    member inline c.Clamp(p : Vector3) = \n        Vector3(\n            c.X.Clamp p.X,\n            c.Y.Clamp p.Y,\n            c.Z.Clamp p.Z)\n    override i.ToString() = $\"%A{i.Min} to %A{i.Max}\"\n    static member inline Lerp(r : Range3, t : Vector3) =\n        Vector3(\n            Range.Lerp(r.X, t.X),\n            Range.Lerp(r.Y, t.Y),\n            Range.Lerp(r.Z, t.Z))\n    static member Zero = Range3(Vector3.Zero, Vector3.Zero)\n    static member ZeroToOne = Range3(Vector3.Zero, Vector3.One)\n    static member inline (+) (a: Range3, c) = Range3(a.X + c, a.Y + c, a.Z + c)\n    static member inline (-) (a: Range3, c) = Range3(a.X - c, a.Y - c, a.Z - c)\n    static member inline (*) (a: Range3, c) = Range3(a.X * c, a.Y * c, a.Z * c)\n    static member inline (/) (a: Range3, c) = Range3(a.X / c, a.Y / c, a.Z / c)\n    static member inline (+) (a: Range3, v : Vector3) = Range3(a.X + v.X, a.Y + v.Y, a.Z + v.Z)\n    static member inline (-) (a: Range3, v : Vector3) = Range3(a.X - v.X, a.Y - v.Y, a.Z - v.Z)\n    static member inline (*) (a: Range3, v : Vector3) = Range3(a.X * v.X, a.Y * v.Y, a.Z * v.Z)\n    static member inline (/) (a: Range3, v : Vector3) = Range3(a.X / v.X, a.Y / v.Y, a.Z / v.Z)\n    static member inline Point(point : Vector3) = Range3(point, point)\n    static member inline Sized(min : Vector3, size : Vector3) = \n        Range3(min, min + size)\n    static member inline Centered(center : Vector3, size : Vector3) = \n        Range3.Sized(center - size * 0.5f, size)\n    static member inline Intersection(a : Range3, b : Range3) = \n        Range3(\n            Range.Intersection(a.X, b.X), \n            Range.Intersection(a.Y, b.Y),\n            Range.Intersection(a.Z, b.Z))\n    static member inline Union(a : Range3, b : Range3) =         \n        Range3(\n            Range.Union(a.X, b.X), \n            Range.Union(a.Y, b.Y),\n            Range.Union(a.Z, b.Z))\n\n// Int32 range types\n\n[<Struct>]\ntype Rangei =\n    val Min : int\n    val Max : int\n    new(min, max) = { Min = min; Max = max }\n    member inline c.Size = c.Max - c.Min\n    member inline c.IsEmpty = c.Min >= c.Max\n    member inline c.Contains(x) = x >= c.Min && x < c.Max\n    member inline c.Expand(margin) = Rangei(c.Min - margin, c.Max + margin)\n    member inline c.Clamp(x) = x |> max c.Min |> min c.Max\n    member inline c.ToRange() = Range(float32 c.Min, float32 c.Max)\n    override v.ToString() = $\"%A{v.Min} to %A{v.Max}\"\n    static member Zero = Rangei(0, 0)\n    static member ZeroToOne = Rangei(0, 1)\n    static member inline (+) (a: Rangei, c) = Rangei(a.Min + c, a.Max + c)\n    static member inline (-) (a: Rangei, c) = Rangei(a.Min - c, a.Max - c)\n    static member inline (*) (a: Rangei, c) = Rangei(a.Min * c, a.Max * c)\n    static member inline (/) (a: Rangei, c) = Rangei(a.Min / c, a.Max / c)\n    static member inline Point(point) = Rangei(point, point)\n    static member inline Sized(min, size) = \n        Rangei(min, min + size)\n    static member inline Centered(center, size) = \n        Rangei.Sized(center - size / 2, size)\n    static member inline Intersection(a : Rangei, b : Rangei) = \n        Rangei(max a.Min b.Min, min a.Max b.Max)\n    static member inline Union(a : Rangei, b : Rangei) = \n        Rangei(min a.Min b.Min, max a.Max b.Max)\n\n[<Struct>]\ntype Range2i =\n    val Min : Vector2i\n    val Max : Vector2i\n    new(min, max) = { Min = min; Max = max }\n    new(x : Rangei, y : Rangei) = { \n        Min = Vector2i(x.Min, y.Min)\n        Max = Vector2i(x.Max, y.Max) \n        }\n    member inline c.X = Rangei(c.Min.X, c.Max.X)\n    member inline c.Y = Rangei(c.Min.Y, c.Max.Y)\n    member inline c.Size = c.Max - c.Min\n    member inline c.IsEmpty = c.X.IsEmpty || c.Y.IsEmpty\n    member inline c.ToRange2() = Range2(c.Min.ToVector2(), c.Max.ToVector2())\n    member inline c.GetArea() =\n        let s = c.Size\n        s.X * s.Y\n    member inline c.Contains (p : Vector2i) = \n        c.X.Contains p.X && \n        c.Y.Contains p.Y\n    member inline c.Expand(margin : Vector2i) = \n        Range2i(\n            c.X.Expand(margin.X),\n            c.Y.Expand(margin.Y))\n    member inline c.Clamp(p : Vector2i) = \n        Vector2i(c.X.Clamp p.X, c.Y.Clamp p.Y)\n    override i.ToString() = $\"%A{i.Min} to %A{i.Max}\"\n    static member Zero = Range2i(Vector2i.Zero, Vector2i.Zero)\n    static member ZeroToOne = Range2i(Vector2i.Zero, Vector2i.One)\n    static member inline (+) (a: Range2i, c) = Range2i(a.X + c, a.Y + c)\n    static member inline (-) (a: Range2i, c) = Range2i(a.X - c, a.Y - c)\n    static member inline (*) (a: Range2i, c) = Range2i(a.X * c, a.Y * c)\n    static member inline (/) (a: Range2i, c) = Range2i(a.X / c, a.Y / c)\n    static member inline (+) (a: Range2i, v : Vector2i) = Range2i(a.X + v.X, a.Y + v.Y)\n    static member inline (-) (a: Range2i, v : Vector2i) = Range2i(a.X - v.X, a.Y - v.Y)\n    static member inline (*) (a: Range2i, v : Vector2i) = Range2i(a.X * v.X, a.Y * v.Y)\n    static member inline (/) (a: Range2i, v : Vector2i) = Range2i(a.X / v.X, a.Y / v.Y)\n    static member inline Point(point : Vector2i) = Range2i(point, point)\n    static member inline Sized(min : Vector2i, size : Vector2i) = \n        Range2i(min, min + size)\n    static member inline Centered(center : Vector2i, size : Vector2i) = \n        Range2i.Sized(center - size / 2, size)\n    static member inline Intersection(a : Range2i, b : Range2i) = \n        Range2i(\n            Rangei.Intersection(a.X, b.X), \n            Rangei.Intersection(a.Y, b.Y))\n    static member inline Union(a : Range2i, b : Range2i) =         \n        Range2i(\n            Rangei.Union(a.X, b.X), \n            Rangei.Union(a.Y, b.Y))\n\n[<Struct>]\ntype Range3i =\n    val Min : Vector3i\n    val Max : Vector3i\n    new(min, max) = { Min = min; Max = max }\n    new(x : Rangei, y : Rangei, z : Rangei) = { \n        Min = Vector3i(x.Min, y.Min, z.Min)\n        Max = Vector3i(x.Max, y.Max, z.Max) \n        }\n    member inline c.X = Rangei(c.Min.X, c.Max.X)\n    member inline c.Y = Rangei(c.Min.Y, c.Max.Y)\n    member inline c.Z = Rangei(c.Min.Z, c.Max.Z)\n    member inline c.Size = c.Max - c.Min\n    member inline c.IsEmpty = c.X.IsEmpty || c.Y.IsEmpty || c.Z.IsEmpty\n    member inline c.ToRange3() = Range3(c.Min.ToVector3(), c.Max.ToVector3())\n    member inline c.GetVolume() =\n        let s = c.Size\n        s.X * s.Y * s.Z\n    member inline c.Contains(p : Vector3i) = \n        c.X.Contains p.X && \n        c.Y.Contains p.Y && \n        c.Z.Contains p.Z\n    member inline c.Expand(margin : Vector3i) = \n        Range3i(\n            c.X.Expand(margin.X),\n            c.Y.Expand(margin.Y),\n            c.Z.Expand(margin.Z))\n    member inline c.Clamp(p : Vector3i) = \n        Vector3i(\n            c.X.Clamp p.X,\n            c.Y.Clamp p.Y,\n            c.Z.Clamp p.Z)\n    override i.ToString() = $\"%A{i.Min} to %A{i.Max}\"\n    static member Zero = Range3i(Vector3i.Zero, Vector3i.Zero)\n    static member ZeroToOne = Range3i(Vector3i.Zero, Vector3i.One)\n    static member inline (+) (a: Range3i, c) = Range3i(a.X + c, a.Y + c, a.Z + c)\n    static member inline (-) (a: Range3i, c) = Range3i(a.X - c, a.Y - c, a.Z - c)\n    static member inline (*) (a: Range3i, c) = Range3i(a.X * c, a.Y * c, a.Z * c)\n    static member inline (/) (a: Range3i, c) = Range3i(a.X / c, a.Y / c, a.Z / c)\n    static member inline (+) (a: Range3i, v : Vector3i) = Range3i(a.X + v.X, a.Y + v.Y, a.Z + v.Z)\n    static member inline (-) (a: Range3i, v : Vector3i) = Range3i(a.X - v.X, a.Y - v.Y, a.Z - v.Z)\n    static member inline (*) (a: Range3i, v : Vector3i) = Range3i(a.X * v.X, a.Y * v.Y, a.Z * v.Z)\n    static member inline (/) (a: Range3i, v : Vector3i) = Range3i(a.X / v.X, a.Y / v.Y, a.Z / v.Z)\n    static member inline Point(point : Vector3i) = Range3i(point, point)\n    static member inline Sized(min : Vector3i, size : Vector3i) = \n        Range3i(min, min + size)\n    static member inline Centered(center : Vector3i, size : Vector3i) = \n        Range3i.Sized(center - size / 2, size)\n    static member inline Intersection(a : Range3i, b : Range3i) = \n        Range3i(\n            Rangei.Intersection(a.X, b.X), \n            Rangei.Intersection(a.Y, b.Y),\n            Rangei.Intersection(a.Z, b.Z))\n    static member inline Union(a : Range3i, b : Range3i) =         \n        Range3i(\n            Rangei.Union(a.X, b.X), \n            Rangei.Union(a.Y, b.Y),\n            Rangei.Union(a.Z, b.Z))\n"
  },
  {
    "path": "samples/Garnet.Numerics/Vectors.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System\nopen System.Numerics\n\n// Int32 vector types\n\n[<Struct>]\ntype Vector2i = \n    val X : int\n    val Y : int\n    new(x, y) = { X = x; Y = y }\n    member c.ToVector2() = Vector2(float32 c.X, float32 c.Y)\n    override v.ToString() = $\"%A{v.X} %A{v.Y}\"\n    static member Zero = Vector2i(0, 0)\n    static member One = Vector2i(1, 1)\n    static member UnitX = Vector2i(1, 0)\n    static member UnitY = Vector2i(0, 1)\n    static member inline Dot(a: Vector2i, b: Vector2i) = a.X * b.X + a.Y * b.Y\n    static member inline (~-) (v: Vector2i) = Vector2i(-v.X, -v.Y)\n    static member inline (+) (a: Vector2i, c) = Vector2i(a.X + c, a.Y + c) \n    static member inline (-) (a: Vector2i, c) = Vector2i(a.X - c, a.Y - c)\n    static member inline (*) (a: Vector2i, c) = Vector2i(a.X * c, a.Y * c) \n    static member inline (/) (a: Vector2i, c) = Vector2i(a.X / c, a.Y / c)\n    static member inline (>>>) (a: Vector2i, c) = Vector2i(a.X >>> c, a.Y >>> c)\n    static member inline (<<<) (a: Vector2i, c) = Vector2i(a.X <<< c, a.Y <<< c)\n    static member inline (&&&) (a: Vector2i, c) = Vector2i(a.X &&& c, a.Y &&& c)\n    static member inline (+) (a: Vector2i, b: Vector2i) = Vector2i(a.X + b.X, a.Y + b.Y)\n    static member inline (-) (a: Vector2i, b: Vector2i) = Vector2i(a.X - b.X, a.Y - b.Y)\n    static member inline (*) (a: Vector2i, b: Vector2i) = Vector2i(a.X * b.X, a.Y * b.Y) \n    static member inline (/) (a: Vector2i, b: Vector2i) = Vector2i(a.X / b.X, a.Y / b.Y) \n    static member inline Perpendicular(v : Vector2i) = Vector2i(-v.Y, v.X)    \n    static member inline FromVector2(v : Vector2) = Vector2i(int v.X, int v.Y)\n\n[<Struct>]\ntype Vector3i = \n    val X : int\n    val Y : int\n    val Z : int\n    new(x, y, z) = { X = x; Y = y; Z = z }\n    member c.ToVector3() = Vector3(float32 c.X, float32 c.Y, float32 c.Z)\n    override v.ToString() = $\"%A{v.X} %A{v.Y} %A{v.Z}\"\n    static member Zero = Vector3i(0, 0, 0)\n    static member One = Vector3i(1, 1, 1)\n    static member UnitX = Vector3i(1, 0, 0)\n    static member UnitY = Vector3i(0, 1, 0)\n    static member UnitZ = Vector3i(0, 0, 1)\n    static member inline Dot(a: Vector3i, b: Vector3i) = a.X * b.X + a.Y * b.Y + a.Z * b.Z\n    static member inline (~-) (v: Vector3i) = Vector3i(-v.X, -v.Y, -v.Z)\n    static member inline (+) (a: Vector3i, c) = Vector3i(a.X + c, a.Y + c, a.Z + c) \n    static member inline (-) (a: Vector3i, c) = Vector3i(a.X - c, a.Y - c, a.Z - c)\n    static member inline (*) (a: Vector3i, c) = Vector3i(a.X * c, a.Y * c, a.Z * c) \n    static member inline (/) (a: Vector3i, c) = Vector3i(a.X / c, a.Y / c, a.Z / c)\n    static member inline (>>>) (a: Vector3i, c) = Vector3i(a.X >>> c, a.Y >>> c, a.Z >>> c)\n    static member inline (<<<) (a: Vector3i, c) = Vector3i(a.X <<< c, a.Y <<< c, a.Z <<< c)\n    static member inline (&&&) (a: Vector3i, c) = Vector3i(a.X &&& c, a.Y &&& c, a.Z &&& c)\n    static member inline (+) (a: Vector3i, b: Vector3i) = Vector3i(a.X + b.X, a.Y + b.Y, a.Z + b.Z)\n    static member inline (-) (a: Vector3i, b: Vector3i) = Vector3i(a.X - b.X, a.Y - b.Y, a.Z - b.Z)\n    static member inline (*) (a: Vector3i, b: Vector3i) = Vector3i(a.X * b.X, a.Y * b.Y, a.Z * b.Z) \n    static member inline (/) (a: Vector3i, b: Vector3i) = Vector3i(a.X / b.X, a.Y / b.Y, a.Z / b.Z) \n    static member inline FromVector3(v : Vector3) = Vector3i(int v.X, int v.Y, int v.Z)\n\n// Float vector types\n\n[<AutoOpen>]\nmodule Vector2 =\n    type Vector2 with\n        static member FromRadians(a) =\n            Vector2(cos a, sin a)\n\n        static member FromDegrees(a) = \n            Vector2.FromRadians(a * MathF.PI / 180.0f)\n        \n        static member inline Rotate(r : Vector2, a : Vector2) = \n            Vector2(a.X * r.X - a.Y * r.Y, a.X * r.Y + a.Y * r.X)\n\n        static member inline Perpendicular(v : Vector2) =\n            Vector2(-v.Y, v.X)\n\n        member v.GetRadians() =\n            atan2 v.Y v.X\n\n        member v.DivideOrZero(c) =\n            if abs c > 1e-7f then v * (1.0f / c) else Vector2.Zero\n\n        member v.NormalizeOrZero() =\n            v.DivideOrZero(v.Length())\n\n        member v.TruncateOrZero(maxLength) =\n            let lengthSqr = v.LengthSquared()\n            if lengthSqr <= maxLength * maxLength then v\n            else v.NormalizeOrZero() * maxLength\n        \n        member v.GetPerpendicular() = \n            Vector2(-v.Y, v.X)    \n\n        member v.Rotate(r : Vector2) = \n            Vector2(v.X * r.X - v.Y * r.Y, v.X * r.Y + v.Y * r.X)\n\n        member v.InverseRotate(r : Vector2) = \n            Vector2(v.X * r.X + v.Y * r.Y, v.Y * r.X - v.X * r.Y)\n\n        /// Rotates towards a target vector\n        /// maxRotation is a unit-length direction vector relative to X axis\n        member v.RotateTowards(target : Vector2, maxRotation : Vector2) =\n            let dot = Vector2.Dot(v, target)\n            let rotDot = Vector2.Dot(maxRotation, Vector2.UnitX)\n            if dot >= rotDot then target\n            else\n                let cross = Vector3.Cross(Vector3(v, 0.0f), Vector3(target, 0.0f))\n                if cross.Z > 0.0f then v.Rotate(maxRotation)\n                else v.InverseRotate(maxRotation)\n\n        member v.Round() =\n            Vector2(floor (v.X + 0.5f), floor (v.Y + 0.5f))\n\n        member v.RoundToInt() =\n            let v = v.Round()\n            Vector2i(int v.X, int v.Y)\n\n        member v.IsInTriangle(p0 : Vector2, p1 : Vector2, p2 : Vector2) =\n            let a = 0.5f * (-p1.Y * p2.X + p0.Y * (-p1.X + p2.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y)\n            let sign = if a < 0.0f then -1.0f else 1.0f;\n            let s = (p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * v.X + (p0.X - p2.X) * v.Y) * sign\n            let t = (p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * v.X + (p1.X - p0.X) * v.Y) * sign\n            //s > 0.0f && t > 0.0f && (s + t) < 2.0f * A * sign\n            s >= 0.0f && t >= 0.0f && (s + t) <= 2.0f * a * sign\n\n[<AutoOpen>]\nmodule Vector3 =\n    type Vector3 with\n        member v.ToVector2() =\n            Vector2(v.X, v.Y)\n"
  },
  {
    "path": "samples/Garnet.Processor/Args.fs",
    "content": "﻿namespace Garnet.Processor\n\nopen Argu\n\n[<CliPrefix(CliPrefix.Dash)>]\ntype PackArgs =\n    | Input of string\n    | Output of string\n    | Compression of int\n    | Ignore of string list\n    | Recurse\n    interface IArgParserTemplate with\n        member this.Usage =\n            match this with\n            | Input _ -> \"Input directory to pack\"\n            | Output _ -> \"Output packed file\"\n            | Compression _ -> \"Compression level, 0 for none\"\n            | Recurse -> \"Include subdirectories recursively\"\n            | Ignore _ -> \"Ignored file pattern, e.g. *.dat\"\n\nand ProcessorArgs =\n    | [<CliPrefix(CliPrefix.None)>] Pack of ParseResults<PackArgs>\n    interface IArgParserTemplate with\n        member this.Usage =\n            match this with\n            | Pack _ -> \"Pack files into a single archive file.\"\n"
  },
  {
    "path": "samples/Garnet.Processor/Garnet.Processor.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PackAsTool>true</PackAsTool>\n    <ToolCommandName>garnet</ToolCommandName>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <Description>Asset processor for games.</Description>\n    <PackageTags>asset game</PackageTags>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Compile Include=\"Args.fs\" />\n    <Compile Include=\"PackUtility.fs\" />\n    <Compile Include=\"Program.fs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Argu\" Version=\"6.1.1\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Processor/PackUtility.fs",
    "content": "﻿namespace Garnet.Processor\n\nopen System.Diagnostics\nopen System.IO\nopen System.IO.Compression\nopen System.Security.Cryptography\nopen System.Text.RegularExpressions\nopen Argu\n\nmodule PackUtility =\n    let getHash file =\n        if File.Exists(file) then\n            use fs = File.OpenRead(file)\n            use sha = SHA1.Create()\n            sha.ComputeHash(fs)\n            |> Array.map (fun b -> b.ToString(\"x2\"))\n            |> String.concat \"\"\n        else \"\"\n        \n    let getGlobRegex (globs : string list) =\n        if globs.Length = 0 then Regex(\"x^\")\n        else\n            let pattern =\n                globs\n                |> List.map (fun str -> \"^\" + Regex.Escape(str).Replace(@\"\\*\", \".*\").Replace(@\"\\?\", \".\") + \"$\")\n                |> String.concat \"|\"\n            Regex(pattern, RegexOptions.IgnoreCase ||| RegexOptions.Singleline)\n\n    let run (args : ParseResults<PackArgs>) =\n        let inputPath = Path.GetFullPath(args.GetResult <@ PackArgs.Input @>)\n        let outputPath = Path.GetFullPath(args.GetResult <@ PackArgs.Output @>)\n        let recurse = args.Contains <@ Recurse @>\n        let ignoreRegex = getGlobRegex (args.GetResult(<@ Ignore @>, defaultValue = List.empty))\n        let level =\n            let compression = args.GetResult (<@ Compression @>, defaultValue = 0)\n            if compression = 0 then CompressionLevel.NoCompression else CompressionLevel.Optimal\n        printfn $\"Input path: {inputPath}\"\n        // Optionally insert version into output path\n        let outputPath =\n            let key = \"{version}\"\n            if outputPath.Contains(key) then\n                let exeFile = Directory.EnumerateFiles(inputPath, \"*.exe\") |> Seq.head\n                let info = FileVersionInfo.GetVersionInfo(exeFile)\n                outputPath.Replace(key, info.ProductVersion)\n            else outputPath\n        printfn $\"Output path: {outputPath}\"\n        // Create folder\n        let outputDir = Path.GetDirectoryName(outputPath)\n        Directory.CreateDirectory(outputDir) |> ignore\n        // Write to temp path\n        let tempPath = outputPath + \".temp\"\n        let _ =\n            use zip = ZipFile.Open(tempPath, ZipArchiveMode.Create)\n            let options = EnumerationOptions()\n            options.RecurseSubdirectories <- recurse\n            for file in Directory.EnumerateFiles(inputPath, \"*.*\", options) do\n                let fullPath = Path.GetFullPath(file)\n                let name = Path.GetRelativePath(inputPath, fullPath)\n                if ignoreRegex.IsMatch(name) then\n                    printfn $\"Ignoring {name}\"\n                else\n                    printfn $\"Adding {name}\"\n                    zip.CreateEntryFromFile(fullPath, name, level) |> ignore\n        // Calc hash of old and new files\n        let oldHash = getHash outputPath\n        let newHash = getHash tempPath\n        printfn $\"Old hash: {oldHash}\"\n        printfn $\"New hash: {newHash}\"\n        // Overwrite only if hash differs\n        if newHash = oldHash then\n            printfn $\"Hashes match, skipping write\"\n            File.Delete(tempPath)\n        else\n            File.Move(tempPath, outputPath, overwrite = true)\n            let info = FileInfo(outputPath)\n            printfn $\"{info.Length} bytes written to {outputPath}\"\n    "
  },
  {
    "path": "samples/Garnet.Processor/Program.fs",
    "content": "open Argu\nopen Garnet.Processor\n\n[<EntryPoint>]\nlet main argv =\n    let parser = ArgumentParser.Create<ProcessorArgs>()\n    try\n        let args = parser.ParseCommandLine(inputs = argv, raiseOnUsage = true)\n        match args.GetSubCommand() with\n        | Pack args -> PackUtility.run args\n    with e -> printfn $\"%s{e.Message}\"\n    0"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Extensions.fs",
    "content": "﻿namespace Garnet.Samples.Assorted\n\nopen System\nopen System.Numerics\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Input\nopen Garnet.Numerics\n\n[<AutoOpen>]\nmodule Extensions =\n    type Container with\n        member c.AddPixelCoordinateCamera(cameraId) =\n            c.On<Draw> <| fun e ->\n                // Set projection to use pixel coords\n                let cameras = c.Get<CameraSet>()\n                cameras.[cameraId].ProjectionTransform <- Matrix4x4.CreateOrthographicOffCenter(\n                    0.0f, float32 e.ViewSize.X, float32 e.ViewSize.Y, 0.0f, -1.0f, 1.0f)\n                \n        member c.AddEscapeToClose() =\n            c.On<KeyDown> <| fun e ->\n                match e.KeyCode with\n                | Key.Escape -> c.Get<WindowRenderer>().Close()\n                | _ -> ()\n\n        static member Run(register : Container -> IDisposable) =\n            let c = Container()\n            use sys = register c\n            c.RunLoop()\n\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Garnet.Samples.Assorted.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PublishTrimmed>true</PublishTrimmed>\n    <TrimMode>Link</TrimMode>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"assets\\textures\\hex.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\textures\\triangle.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\textures\\multicolor-square.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\textures\\pixel-operator-regular-12.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\color.frag\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\color.frag.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\color.vert\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\color.vert.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\texture-color.frag\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\texture-color.frag.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\texture-color.vert\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\shaders\\texture-color.vert.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\fonts\\pixel-operator-regular-12.font.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <Compile Include=\"Extensions.fs\" />\n    <Compile Include=\"SpriteDrawing.fs\" />\n    <Compile Include=\"TextDrawing.fs\" />\n    <Compile Include=\"OffscreenDrawing.fs\" />\n    <Compile Include=\"Program.fs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Garnet\\Garnet.fsproj\" />\n    <ProjectReference Include=\"..\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/OffscreenDrawing.fs",
    "content": "﻿module Garnet.Samples.Assorted.OffscreenDrawing\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Numerics\nopen Veldrid\n\nmodule Resources =\n    let colorTextureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {\n        VertexShader = \"shaders/texture-color.vert\"\n        FragmentShader = \"shaders/texture-color.frag\"\n        }\n\n    let spritePipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Point\n        ShaderSet = colorTextureShaderSet\n        Texture = \"textures/multicolor-square.png\"\n        }\n\n    let spriteLayer = {\n        LayerId = 0\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = spritePipeline\n        }\n\n    let lightPipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Point\n        ShaderSet = colorTextureShaderSet\n        Texture = \"textures/hex.png\"\n        }\n\n    let lightLayer = {\n        LayerId = 0\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = lightPipeline\n        }\n\ntype MainScene(ren : SpriteRenderer) =\n    member c.Renderer = ren\n    \ntype LightScene(ren : SpriteRenderer) =\n    member c.Renderer = ren\n    \ntype Container with\n    member c.AddSpriteLightingDrawing() =\n        let device = c.Get<GraphicsDevice>()\n        let shaders = c.Get<ShaderSetCache>()\n        let cache = c.Get<ResourceCache>()\n        let lightTarget =\n            let blend = BlendStateDescription(AttachmentStates = [|\n                // Multiply destination (main scene) by source (light map)\n                BlendAttachmentDescription(\n                    BlendEnabled = true,\n                    SourceColorFactor = BlendFactor.Zero,\n                    DestinationColorFactor = BlendFactor.SourceColor,\n                    ColorFunction = BlendFunction.Add,\n                    SourceAlphaFactor = BlendFactor.Zero,\n                    DestinationAlphaFactor = BlendFactor.SourceAlpha,\n                    AlphaFunction = BlendFunction.Add\n                    )\n                |])\n            let shaderSet = shaders.GetOrCreate(device, Resources.colorTextureShaderSet.Untyped, cache)\n            let target = new RenderTarget(device, shaderSet, Filtering.Linear, blend)\n            target.Background <- RgbaFloat.Clear\n            target\n        let mainSprites = new SpriteRenderer(device, shaders, cache)\n        let lightSprites = new SpriteRenderer(device, shaders, cache)\n        c.Set(MainScene(mainSprites))\n        c.Set(LightScene(lightSprites))\n        Disposable.Create [\n            mainSprites :> IDisposable\n            lightSprites :> IDisposable\n            lightTarget :> IDisposable\n            c.On<Draw> <| fun e ->\n                lightTarget.Width <- e.ViewSize.X\n                lightTarget.Height <- e.ViewSize.Y\n            c.On<PushDrawCommands> <| fun _ ->\n                let context = c.Get<RenderContext>()\n                let cameras = c.Get<CameraSet>()\n                // First draw scene normally\n                mainSprites.Draw(context, cameras)\n                // Next draw lights to offscreen buffer, then draw to main buffer\n                // (with multiply blending)\n                lightTarget.BeginDraw(context)\n                lightSprites.Draw(context, cameras)\n                lightTarget.EndDraw(context)\n            ]\n\nlet run() =\n    Container.Run <| fun c ->\n        c.Set {\n            WindowSettings.Default with\n                Background = RgbaFloat.Blue.MultiplyRgb(0.1f)\n            }\n        Disposable.Create [\n            c.AddDefaultSystems()\n            c.AddPixelCoordinateCamera(0)\n            c.AddEscapeToClose()\n            c.AddSpriteLightingDrawing()\n            c.On<Draw> <| fun e ->\n                let rect = Range2.Sized(Vector2.Zero, e.ViewSize.ToVector2()) \n                // Draw sprites\n                let sprites = c.Get<MainScene>().Renderer\n                let verts = sprites.GetVertices(Resources.spriteLayer)\n                verts.DrawQuad(rect, Range2.ZeroToOne, RgbaFloat.White)\n                // Draw lights\n                let lights = c.Get<LightScene>().Renderer\n                let verts = lights.GetVertices(Resources.lightLayer)\n                verts.DrawQuad(rect, Range2.ZeroToOne, RgbaFloat.White)\n            ]\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/Program.fs",
    "content": "open Garnet.Composition\nopen Garnet.Samples.Assorted\n\n[<EntryPoint>]\nlet main argv =\n    //SpriteDrawing.run()\n    //TextDrawing.run()\n    OffscreenDrawing.run()\n    0"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/SpriteDrawing.fs",
    "content": "﻿module Garnet.Samples.Assorted.SpriteDrawing\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Numerics\nopen Veldrid\n\nmodule Resources =\n    let colorTextureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {\n        VertexShader = \"shaders/texture-color.vert\"\n        FragmentShader = \"shaders/texture-color.frag\"\n        }\n\n    let spritePipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Point\n        ShaderSet = colorTextureShaderSet\n        Texture = \"textures/multicolor-square.png\"\n        }\n\n    let spriteLayer = {\n        LayerId = 0\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = spritePipeline\n        }\n\nlet run() =\n    Container.Run <| fun c ->\n        c.Set {\n            WindowSettings.Default with\n                Background = RgbaFloat.Blue.MultiplyRgb(0.1f)\n            }\n        Disposable.Create [\n            c.AddDefaultSystems()\n            c.AddPixelCoordinateCamera(0)\n            c.AddEscapeToClose()\n            c.On<Draw> <| fun _ ->\n                let sprites = c.Get<SpriteRenderer>()\n                let verts = sprites.GetVertices(Resources.spriteLayer)\n                let size = Vector2(80.0f, 40.0f)\n                // Identical quads\n                verts.DrawQuad(\n                    Range2.Centered(Vector2(50.0f, 50.0f), size),\n                    Range2.ZeroToOne,\n                    RgbaFloat.White)\n                verts.DrawQuad {\n                    ColorTextureSprite.Default with\n                        Center = Vector2(150.0f, 50.0f)\n                        Size = size\n                        }\n                // Rotated - Since we're using pixel coords, positive rotation is clockwise\n                verts.DrawQuad {\n                    ColorTextureSprite.Default with\n                        Center = Vector2(250.0f, 50.0f)\n                        Size = size\n                        Rotation = Vector2.FromDegrees(90.0f)\n                        }\n            ]\n\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/TextDrawing.fs",
    "content": "﻿module Garnet.Samples.Assorted.TextDrawing\n\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Numerics\n\nmodule Resources =\n    let font = \"fonts/pixel-operator-regular-12.font.json\"\n    let fontTexture = \"textures/pixel-operator-regular-12.png\"\n    \n    let textureShaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {\n        VertexShader = \"shaders/texture-color.vert\"\n        FragmentShader = \"shaders/texture-color.frag\"\n        }\n\n    let colorShaderSet : ShaderSetDescriptor<PositionColorVertex> = {\n        VertexShader = \"shaders/color.vert\"\n        FragmentShader = \"shaders/color.frag\"\n        }\n\n    let textPipeline = {\n        Blend = Blend.Alpha\n        // Use point filtering since we plan to scale the font and want a pixelated appearance\n        Filtering = Filtering.Point\n        ShaderSet = textureShaderSet\n        Texture = \"textures/pixel-operator-regular-12.png\"\n        }\n\n    let panelPipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Linear\n        ShaderSet = colorShaderSet\n        Texture = \"\"\n        }\n\n    let textLayer = {\n        LayerId = 1\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = textPipeline\n        }\n\n    let panelLayer = {\n        LayerId = 0\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = panelPipeline\n        }\n\nlet run() =\n    Container.Run <| fun c ->\n        c.Set {\n            WindowSettings.Default with Width = 640; Height = 480 \n        }\n        Disposable.Create [\n            c.AddDefaultSystems()\n            c.AddPixelCoordinateCamera(0)\n            c.AddEscapeToClose()\n            c.On<Draw> <| fun e ->\n                let font = c.LoadJsonFont(Resources.font, Resources.fontTexture)\n                let sprites = c.Get<SpriteRenderer>()\n                let textVerts = sprites.GetVertices(Resources.textLayer)\n                let panelVerts = sprites.GetVertices(Resources.panelLayer)\n                let baseBlock = {\n                    TextBlock.Default with\n                        Scale = 3\n                        Bounds = Range2i.Sized(Vector2i.Zero, e.ViewSize)\n                        }\n                // Draw text in the corners\n                textVerts.DrawText(font, {\n                    baseBlock with\n                        Text = \"Upper left\"\n                        Align = Align.Left\n                        Valign = Valign.Top\n                        })\n                textVerts.DrawText(font, {\n                    baseBlock with\n                        Text = \"Bottom right\"\n                        Align = Align.Right\n                        Valign = Valign.Bottom\n                        })\n                // Draw text in the center within a panel\n                let block = {\n                    baseBlock with\n                        Text = \"Multiple Lines\\nCenter\"\n                        Align = Align.Center\n                        Valign = Valign.Center\n                        }\n                let bounds = font.Measure(block).ToRange2()\n                textVerts.DrawText(font, block)\n                panelVerts.DrawQuad(bounds, RgbaFloat.Red.MultiplyAlpha(0.5f))\n                // Test wrapping\n                let scale = 2\n                let text = \"Title\\n\\nLine 1\\nLine 2 should wrap to next line\\nLine 3\"\n                let size = font.Measure(text) * scale\n                let bounds = Range2i.Sized(Vector2i(0, e.ViewSize.Y - size.Y), Vector2i(size.X - 200, size.Y))\n                let block = {\n                    baseBlock with\n                        Scale = scale\n                        Bounds = bounds\n                        Text = text\n                        Align = Align.Left\n                        Valign = Valign.Center\n                        Wrapping = TextWrapping.WordWrap\n                        }\n                textVerts.DrawText(font, block)\n                panelVerts.DrawQuad(bounds.ToRange2(), RgbaFloat.Red.MultiplyAlpha(0.5f))\n            ]\n\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/fonts/pixel-operator-regular-12.font.json",
    "content": "{\n  \"family\": \"Pixel Operator\",\n  \"style\": \"Regular\",\n  \"size\": 12,\n  \"height\": 17,\n  \"chars\": [\n    {\n      \"code\": \" \",\n      \"width\": 4,\n      \"offsetX\": 0,\n      \"offsetY\": 13,\n      \"rectX\": 1,\n      \"rectY\": 12,\n      \"rectWidth\": 0,\n      \"rectHeight\": 0\n    },\n    {\n      \"code\": \"!\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 2,\n      \"rectY\": 3,\n      \"rectWidth\": 1,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"\\\"\",\n      \"width\": 7,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 4,\n      \"rectY\": 3,\n      \"rectWidth\": 3,\n      \"rectHeight\": 3\n    },\n    {\n      \"code\": \"#\",\n      \"width\": 8,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 8,\n      \"rectY\": 3,\n      \"rectWidth\": 6,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"$\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 2,\n      \"rectX\": 15,\n      \"rectY\": 1,\n      \"rectWidth\": 5,\n      \"rectHeight\": 13\n    },\n    {\n      \"code\": \"%\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 21,\n      \"rectY\": 3,\n      \"rectWidth\": 7,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"&\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 29,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"'\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 35,\n      \"rectY\": 3,\n      \"rectWidth\": 1,\n      \"rectHeight\": 3\n    },\n    {\n      \"code\": \"(\",\n      \"width\": 7,\n      \"offsetX\": 3,\n      \"offsetY\": 4,\n      \"rectX\": 37,\n      \"rectY\": 3,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \")\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 41,\n      \"rectY\": 3,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"*\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 45,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 5\n    },\n    {\n      \"code\": \"+\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 51,\n      \"rectY\": 5,\n      \"rectWidth\": 5,\n      \"rectHeight\": 5\n    },\n    {\n      \"code\": \",\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 12,\n      \"rectX\": 57,\n      \"rectY\": 11,\n      \"rectWidth\": 2,\n      \"rectHeight\": 3\n    },\n    {\n      \"code\": \"-\",\n      \"width\": 6,\n      \"offsetX\": 1,\n      \"offsetY\": 8,\n      \"rectX\": 60,\n      \"rectY\": 7,\n      \"rectWidth\": 4,\n      \"rectHeight\": 1\n    },\n    {\n      \"code\": \".\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 12,\n      \"rectX\": 65,\n      \"rectY\": 11,\n      \"rectWidth\": 1,\n      \"rectHeight\": 1\n    },\n    {\n      \"code\": \"/\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 67,\n      \"rectY\": 3,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"0\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 71,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"1\",\n      \"width\": 7,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 77,\n      \"rectY\": 3,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"2\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 81,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"3\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 87,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"4\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 93,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"5\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 99,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"6\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 105,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"7\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 111,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"8\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 117,\n      \"rectY\": 3,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"9\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 1,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \":\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 6,\n      \"rectX\": 7,\n      \"rectY\": 17,\n      \"rectWidth\": 1,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \";\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 9,\n      \"rectY\": 17,\n      \"rectWidth\": 2,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"<\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 12,\n      \"rectY\": 17,\n      \"rectWidth\": 3,\n      \"rectHeight\": 5\n    },\n    {\n      \"code\": \"=\",\n      \"width\": 6,\n      \"offsetX\": 1,\n      \"offsetY\": 7,\n      \"rectX\": 16,\n      \"rectY\": 18,\n      \"rectWidth\": 4,\n      \"rectHeight\": 3\n    },\n    {\n      \"code\": \">\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 21,\n      \"rectY\": 17,\n      \"rectWidth\": 3,\n      \"rectHeight\": 5\n    },\n    {\n      \"code\": \"?\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 25,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"@\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 31,\n      \"rectY\": 15,\n      \"rectWidth\": 7,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"A\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 39,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"B\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 45,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"C\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 51,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"D\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 57,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"E\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 63,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"F\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 69,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"G\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 75,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"H\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 81,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"I\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 87,\n      \"rectY\": 15,\n      \"rectWidth\": 1,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"J\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 89,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"K\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 95,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"L\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 101,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"M\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 107,\n      \"rectY\": 15,\n      \"rectWidth\": 7,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"N\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 115,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"O\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 121,\n      \"rectY\": 15,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"P\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 1,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"Q\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 7,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"R\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 13,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"S\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 19,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"T\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 25,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"U\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 31,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"V\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 37,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"W\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 43,\n      \"rectY\": 27,\n      \"rectWidth\": 7,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"X\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 51,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"Y\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 57,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"Z\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 63,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"[\",\n      \"width\": 7,\n      \"offsetX\": 3,\n      \"offsetY\": 4,\n      \"rectX\": 69,\n      \"rectY\": 27,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"\\\\\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 73,\n      \"rectY\": 27,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"]\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 77,\n      \"rectY\": 27,\n      \"rectWidth\": 3,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"^\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 81,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 3\n    },\n    {\n      \"code\": \"_\",\n      \"width\": 5,\n      \"offsetX\": 0,\n      \"offsetY\": 14,\n      \"rectX\": 87,\n      \"rectY\": 37,\n      \"rectWidth\": 5,\n      \"rectHeight\": 1\n    },\n    {\n      \"code\": \"`\",\n      \"width\": 5,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 93,\n      \"rectY\": 27,\n      \"rectWidth\": 2,\n      \"rectHeight\": 2\n    },\n    {\n      \"code\": \"a\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 96,\n      \"rectY\": 29,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"b\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 102,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"c\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 108,\n      \"rectY\": 29,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"d\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 114,\n      \"rectY\": 27,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"e\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 120,\n      \"rectY\": 29,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"f\",\n      \"width\": 6,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 1,\n      \"rectY\": 39,\n      \"rectWidth\": 4,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"g\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 6,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"h\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 12,\n      \"rectY\": 39,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"i\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 18,\n      \"rectY\": 39,\n      \"rectWidth\": 1,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"j\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 20,\n      \"rectY\": 39,\n      \"rectWidth\": 5,\n      \"rectHeight\": 11\n    },\n    {\n      \"code\": \"k\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 26,\n      \"rectY\": 39,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"l\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 32,\n      \"rectY\": 39,\n      \"rectWidth\": 2,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"m\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 35,\n      \"rectY\": 41,\n      \"rectWidth\": 7,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"n\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 43,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"o\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 49,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"p\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 55,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"q\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 61,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"r\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 67,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"s\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 73,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"t\",\n      \"width\": 6,\n      \"offsetX\": 1,\n      \"offsetY\": 5,\n      \"rectX\": 79,\n      \"rectY\": 40,\n      \"rectWidth\": 4,\n      \"rectHeight\": 8\n    },\n    {\n      \"code\": \"u\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 84,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"v\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 90,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"w\",\n      \"width\": 9,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 96,\n      \"rectY\": 41,\n      \"rectWidth\": 7,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"x\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 104,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"y\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 110,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"z\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 6,\n      \"rectX\": 116,\n      \"rectY\": 41,\n      \"rectWidth\": 5,\n      \"rectHeight\": 7\n    },\n    {\n      \"code\": \"{\",\n      \"width\": 7,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 122,\n      \"rectY\": 39,\n      \"rectWidth\": 4,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"|\",\n      \"width\": 5,\n      \"offsetX\": 2,\n      \"offsetY\": 4,\n      \"rectX\": 1,\n      \"rectY\": 51,\n      \"rectWidth\": 1,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"}\",\n      \"width\": 7,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 3,\n      \"rectY\": 51,\n      \"rectWidth\": 4,\n      \"rectHeight\": 9\n    },\n    {\n      \"code\": \"~\",\n      \"width\": 8,\n      \"offsetX\": 1,\n      \"offsetY\": 4,\n      \"rectX\": 8,\n      \"rectY\": 51,\n      \"rectWidth\": 6,\n      \"rectHeight\": 2\n    }\n  ]\n}"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.frag",
    "content": "#version 450\nlayout(location = 0) in vec4 fsin_color;\nlayout(location = 0) out vec4 fsout_color;\nvoid main()\n{\n    fsout_color = fsin_color;\n}"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.frag.hlsl.bytes",
    "content": "static float4 _9;\nstatic float4 _11;\n\nstruct SPIRV_Cross_Input\n{\n    float4 _11 : TEXCOORD0;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float4 _9 : SV_Target0;\n};\n\nvoid frag_main()\n{\n    _9 = _11;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _11 = stage_input._11;\n    frag_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output._9 = _9;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.vert",
    "content": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1) uniform ViewBuffer\n{\n    mat4 View;\n};\nlayout(set = 1, binding = 0) uniform WorldBuffer\n{\n    mat4 World;\n};\nlayout(location = 0) in vec3 Position;\nlayout(location = 1) in vec4 Color;\nlayout(location = 0) out vec4 fsin_Color;\nvoid main()\n{\n    vec4 worldPosition = World * vec4(Position, 1);\n    vec4 viewPosition = View * worldPosition;\n    vec4 clipPosition = Projection * viewPosition;\n    gl_Position = clipPosition;\n    fsin_Color = Color;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/color.vert.hlsl.bytes",
    "content": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    row_major float4x4 _32_m0 : packoffset(c0);\n};\n\ncbuffer _38_40 : register(b0)\n{\n    row_major float4x4 _40_m0 : packoffset(c0);\n};\n\n\nstatic float4 gl_Position;\nstatic float3 _21;\nstatic float4 _54;\nstatic float4 _56;\n\nstruct SPIRV_Cross_Input\n{\n    float3 _21 : TEXCOORD0;\n    float4 _56 : TEXCOORD1;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float4 _54 : TEXCOORD0;\n    float4 gl_Position : SV_Position;\n};\n\nvoid vert_main()\n{\n    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);\n    _54 = _56;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _21 = stage_input._21;\n    _56 = stage_input._56;\n    vert_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output.gl_Position = gl_Position;\n    stage_output._54 = _54;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag",
    "content": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) out vec4 fsout_color;\nlayout(set = 1, binding = 1) uniform texture2D SurfaceTexture;\nlayout(set = 1, binding = 2) uniform sampler SurfaceSampler;\nvoid main()\n{\n    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);\n    fsout_color =  texColor * fsin_color;\n}"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.frag.hlsl.bytes",
    "content": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic float4 _29;\n\nstruct SPIRV_Cross_Input\n{\n    float2 _22 : TEXCOORD0;\n    float4 _29 : TEXCOORD1;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float4 _26 : SV_Target0;\n};\n\nvoid frag_main()\n{\n    _26 = _12.Sample(_16, _22) * _29;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _22 = stage_input._22;\n    _29 = stage_input._29;\n    frag_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output._26 = _26;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert",
    "content": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1) uniform ViewBuffer\n{\n    mat4 View;\n};\nlayout(set = 1, binding = 0) uniform WorldBuffer\n{\n    mat4 World;\n};\nlayout(location = 0) in vec3 Position;\nlayout(location = 1) in vec2 TexCoords;\nlayout(location = 2) in vec4 Color;\nlayout(location = 0) out vec2 fsin_texCoords;\nlayout(location = 1) out vec4 fsin_Color;\nvoid main()\n{\n    vec4 worldPosition = World * vec4(Position, 1);\n    vec4 viewPosition = View * worldPosition;\n    vec4 clipPosition = Projection * viewPosition;\n    gl_Position = clipPosition;\n    fsin_texCoords = TexCoords;\n    fsin_Color = Color;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Assorted/assets/shaders/texture-color.vert.hlsl.bytes",
    "content": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    row_major float4x4 _32_m0 : packoffset(c0);\n};\n\ncbuffer _38_40 : register(b0)\n{\n    row_major float4x4 _40_m0 : packoffset(c0);\n};\n\n\nstatic float4 gl_Position;\nstatic float3 _21;\nstatic float2 _56;\nstatic float2 _58;\nstatic float4 _60;\nstatic float4 _62;\n\nstruct SPIRV_Cross_Input\n{\n    float3 _21 : TEXCOORD0;\n    float2 _58 : TEXCOORD1;\n    float4 _62 : TEXCOORD2;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float2 _56 : TEXCOORD0;\n    float4 _60 : TEXCOORD1;\n    float4 gl_Position : SV_Position;\n};\n\nvoid vert_main()\n{\n    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);\n    _56 = _58;\n    _60 = _62;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _21 = stage_input._21;\n    _58 = stage_input._58;\n    _62 = stage_input._62;\n    vert_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output.gl_Position = gl_Position;\n    stage_output._56 = _56;\n    stage_output._60 = _60;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.CSharp/Garnet.Samples.CSharp.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PublishTrimmed>true</PublishTrimmed>\n    <TrimMode>Link</TrimMode>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Garnet\\Garnet.fsproj\" />\n    <ProjectReference Include=\"..\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Samples.CSharp/Program.cs",
    "content": "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",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Numerics\nopen System.Diagnostics\nopen ImGuiNET\nopen Garnet.Composition\n\ntype FpsHud() =\n    let fps = FpsGauge(1.0f)\n    let fixedFps = FpsGauge(1.0f)\n    member _.OnFixedUpdate() =\n        let timestamp = Stopwatch.GetTimestamp()\n        fixedFps.Update(timestamp)\n    member _.OnUpdate() =\n        let timestamp = Stopwatch.GetTimestamp()\n        fps.Update(timestamp)\n    member _.Draw() =\n        let flags = \n            ImGuiWindowFlags.NoBackground |||\n            ImGuiWindowFlags.NoTitleBar |||\n            ImGuiWindowFlags.NoResize |||\n            ImGuiWindowFlags.NoMove |||\n            ImGuiWindowFlags.NoFocusOnAppearing |||\n            ImGuiWindowFlags.NoInputs |||\n            ImGuiWindowFlags.NoNavFocus\n        ImGui.SetNextWindowSize(Vector2(500.0f, 500.0f))\n        ImGui.SetNextWindowPos(Vector2(0.0f, 0.0f))\n        if ImGui.Begin(\"Hud\", flags) then\n            let info = GC.GetGCMemoryInfo()\n            ImGui.SetWindowFontScale(1.0f)\n            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}\"\n            ImGui.Text $\"GC pause: {info.PauseTimePercentage}%%%%, heap size: {info.HeapSizeBytes / 1024L} Kb\"\n            ImGui.End()\n\nmodule DebugSystem =\n    let add (c : Container) =\n        let hud = c.Get<FpsHud>()\n        Disposable.Create [\n            c.On<FixedUpdate> <| fun _ ->\n                hud.OnFixedUpdate()\n            c.On<Update> <| fun _ ->\n                hud.OnUpdate()\n            c.On<Draw> <| fun _ ->\n                hud.Draw()\n            ]               \n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Drawing.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Numerics\nopen Garnet.Composition\nopen Garnet.Numerics\nopen Garnet.Graphics\n\nmodule DrawingSystems =\n    type Container with\n        member c.AddCameraUpdates() =\n            c.On<Draw> <| fun e ->\n                // Update transforms so origin is in the center of the screen and we use pixel coords\n                // with +Y as up.\n                let displayScale = 1.0f\n                let size = e.ViewSize.ToVector2() / displayScale\n                let camera = c.Get<CameraSet>().[0]\n                camera.ProjectionTransform <- Matrix4x4.CreateOrthographic(size.X, size.Y, -100.0f, 100.0f)\n\n        member c.AddVehicleSprites() =\n            c.On<Draw> <| fun _ ->\n                let atlas = c.LoadResource<TextureAtlas>(Resources.atlas)\n                let layers = c.Get<SpriteRenderer>()\n                let texBounds = atlas.[Resources.triangleTexture].NormalizedBounds\n                let mesh = layers.GetVertices(Resources.vehicleLayer)\n                for r in c.Query<Vehicle, Position, Faction, Heading>() do\n                    mesh.DrawQuad {\n                        Center = r.Value2.Pos\n                        Size = 0.1f * Vector2(1.0f, 1.0f) * 140.0f\n                        Rotation = r.Value4.Direction\n                        TexBounds = texBounds\n                        Color = Faction.toColor r.Value3\n                        }\n\n        member c.AddTrailSprites() =\n            c.On<Draw> <| fun _ ->\n                let atlas = c.LoadResource<TextureAtlas>(Resources.atlas)\n                let layers = c.Get<SpriteRenderer>()\n                let texBounds = atlas.[Resources.hexTexture].NormalizedBounds\n                let mesh = layers.GetVertices(Resources.trailLayer)\n                for r in c.Query<Trail, Position, Faction, Lifespan, Rotation>() do\n                    mesh.DrawQuad {\n                        Center = r.Value2.Pos \n                        Size = r.Value4.Lifespan * 0.3f * Vector2.One * 60.0f\n                        Rotation = Vector2.FromRadians(r.Value5.Radians)\n                        TexBounds = texBounds\n                        Color = (Faction.toColor r.Value3).MultiplyAlpha(r.Value4.Lifespan * 0.3f)\n                        }\n\n    let add (c : Container) =\n        Disposable.Create [\n            c.AddCameraUpdates()\n            c.AddVehicleSprites()\n            c.AddTrailSprites()\n            ]\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Functions.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numerics\n\nmodule WorldSettings =\n    let defaults = {\n        Seed = 1\n        VehicleCount = 100\n        SpawnRange = 300.0f\n        MaxVehicleSpeed = 50.0f\n        TrailLifespan = 0.6f\n        Steering = {\n            ForwardWeight = 20.0f\n            CohesionWeight = 3.0f\n            TetherWeight = 1.0f\n            SeparationWeight = 3.0f\n            AlignmentWeight = 1.0f\n            MaxAlignmentDistance = 100.0f\n            MaxSeparationDistance = 70.0f\n            MaxCohesionDistance = 400.0f\n            MaxTetherDistance = 300.0f\n        }\n    }\n\nmodule Scalar =\n    let tolerance = 1e-9f\n\n    let clamp (s0 : float32) (s1 : float32) (s : float32) =\n        s |> max s0 |> min s1\n\n    let linearStep s0 s1 s =\n        let length = s1 - s0\n        if abs length < tolerance then 0.0f\n        else clamp 0.0f 1.0f ((s - s0) / length)\n\n    let smoothStep s0 s1 s =\n        let x = linearStep s0 s1 s\n        x * x * (3.0f - 2.0f * x)\n\nmodule Heading =\n    let getVelocity vehicle =\n        vehicle.Speed * vehicle.Direction\n\n    let fromVelocity (newVelocity : Vector2) =\n        let newSpeed = newVelocity.Length()\n        { \n            Speed = newSpeed \n            Direction = newVelocity.DivideOrZero(newSpeed)\n        }\n\n    let getNextPosition (deltaTime : float32) vehicle pos =\n        let velocity = getVelocity vehicle\n        let delta = deltaTime * velocity\n        pos + delta\n\nmodule Steering =\n    let getForward current =\n        current.SteerDir\n\n    let getTether maxDistance current =\n        let tetherPoint = Vector2.Zero\n        let toTether = tetherPoint - current.SteerPos\n        let distance = toTether.Length()\n        let scale = Scalar.smoothStep 0.0f maxDistance distance\n        toTether.DivideOrZero(distance) * scale\n\n    let getCohesion minDistance maxDistance (neighbors : List<Neighbor>) =\n        let mutable sum = Vector2.Zero\n        for neighbor in neighbors do\n            let weight = Scalar.smoothStep minDistance maxDistance neighbor.Distance\n            sum <- sum + (neighbor.TeamWeight * weight) * neighbor.DirectionToNeighbor\n        sum.NormalizeOrZero()\n\n    let getSeparation maxDistance (neighbors : List<_>) =\n        let mutable sum = Vector2.Zero\n        for neighbor in neighbors do\n            let weight = Scalar.smoothStep maxDistance 0.0f neighbor.Distance\n            sum <- sum + -weight * neighbor.DirectionToNeighbor\n        sum\n\n    let getAlignment maxDistance (neighbors : List<_>) current =\n        let mutable sum = Vector2.Zero\n        for neighbor in neighbors do\n            let weight = Scalar.smoothStep maxDistance 0.0f neighbor.Distance\n            sum <- sum + -(neighbor.TeamWeight * weight) * current.SteerDir\n        sum.NormalizeOrZero()\n\n    let getSteeringDirection s neighbors current =\n        let sum = \n            getForward current * s.ForwardWeight +\n            getTether s.MaxTetherDistance current * s.TetherWeight +\n            getCohesion s.MaxSeparationDistance s.MaxCohesionDistance neighbors * s.CohesionWeight +\n            getSeparation s.MaxSeparationDistance neighbors * s.SeparationWeight +\n            getAlignment s.MaxAlignmentDistance neighbors current * s.AlignmentWeight\n        sum.NormalizeOrZero()\n        \nmodule Faction =\n    let all = [|\n        Red\n        Orange\n        Yellow\n        Green\n        Cyan\n        Blue\n        Purple\n    |]\n\n    let toColor = function\n        | Red -> RgbaFloat(1.0f, 0.0f, 0.2f, 1.0f)\n        | Orange -> RgbaFloat(1.0f, 0.4f, 0.0f, 1.0f)\n        | Yellow -> RgbaFloat(0.6f, 1.0f, 0.0f, 1.0f)\n        | Green -> RgbaFloat(0.0f, 1.0f, 0.1f, 1.0f)\n        | Cyan -> RgbaFloat(0.0f, 0.8f, 0.6f, 1.0f)\n        | Blue -> RgbaFloat(0.0f, 0.4f, 1.0f, 1.0f)\n        | Purple -> RgbaFloat(0.6f, 0.0f, 1.0f, 1.0f)\n        \n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Garnet.Samples.Flocking.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PublishTrimmed>true</PublishTrimmed>\n    <TrimMode>Link</TrimMode>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"assets\\textures\\hex.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\textures\\triangle.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.frag\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.frag.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.vert\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.vert.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <Compile Include=\"Types.fs\" />\n    <Compile Include=\"Functions.fs\" />\n    <Compile Include=\"Resources.fs\" />\n    <Compile Include=\"Simulation.fs\" />\n    <Compile Include=\"Drawing.fs\" />\n    <Compile Include=\"Startup.fs\" />\n    <Compile Include=\"Debug.fs\" />\n    <Compile Include=\"Program.fs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Garnet\\Garnet.fsproj\" />\n    <ProjectReference Include=\"..\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Program.fs",
    "content": "open Garnet.Composition\nopen Garnet.Samples.Flocking\n\n[<EntryPoint>]\nlet main _ =\n    let c = Container()\n    use s = c.AddSystems [\n        StartupSystem.add\n        SimulationSystems.add\n        DrawingSystems.add\n        DebugSystem.add\n        ]\n    c.RunLoop()\n    0"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Resources.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen Garnet.Graphics\n\nmodule Resources =\n    let shaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {\n        VertexShader = \"texture-color.vert\"\n        FragmentShader = \"texture-color.frag\"\n        }\n    \n    let atlas = \"textures\"\n    let hexTexture = \"hex.png\"\n    let triangleTexture = \"triangle.png\"\n    \n    let pipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Linear\n        ShaderSet = shaderSet\n        Texture = atlas\n        }\n    \n    let vehicleLayer = {\n        LayerId = 2\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = pipeline \n        }\n\n    let trailLayer = {\n        LayerId = 1\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = FlushOnDraw\n        Pipeline = pipeline \n        }\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Simulation.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Numerics\nopen Garnet.Composition\n\n// Not we're using Update instead of FixedUpdate here, mainly because this demo\n// is intended to measure performance. Normally it would be preferable to use\n// FixedUpdate for simulation logic.\nmodule SimulationSystems =\n    type Container with\n        member c.AddSpawning() =\n            c.On<Start> <| fun _ ->\n                let settings = c.Get<WorldSettings>()\n                let rand = Random(settings.Seed)\n                let nextCoord() = float32 (rand.NextDouble() - 0.5) * settings.SpawnRange\n                for i = 1 to settings.VehicleCount do\n                    c.Create()\n                        .With(Faction.all.[rand.Next Faction.all.Length])\n                        .With({ MaxSpeed = settings.MaxVehicleSpeed; Radius = 1.0f })\n                        .With({ Pos = Vector2(nextCoord(), nextCoord()) })\n                        .With({ Direction = Vector2(0.0f, 1.0f); Speed = 0.0f })\n                        .Add(TrailEmitter())\n                            \n        member c.AddSteering() =\n            let neighbors = List<Neighbor>()\n            c.On<Update> <| fun _ ->\n                let settings = c.Get<WorldSettings>().Steering\n                for r in c.Query<Eid, Position, Heading, Faction, Vehicle>() do\n                    let h = &r.Value3\n                    let current = {\n                        Eid = r.Value1\n                        Pos = r.Value2.Pos\n                        Dir = h.Direction\n                        Faction = r.Value4\n                        }\n                    // For simplicity and testing performance, we're iterating over all vehicles\n                    // rather than using any spatial partitioning.\n                    for r in c.Query<Eid, Heading, Faction, Position>() do\n                        if r.Value1 <> current.Eid then\n                            let offset = r.Value4.Pos - current.Pos\n                            let distance = offset.Length()\n                            neighbors.Add { \n                                Direction = r.Value2.Direction\n                                TeamWeight = if current.Faction = r.Value3 then 1.0f else 0.0f\n                                DirectionToNeighbor = offset.DivideOrZero(distance)\n                                Distance = distance\n                                }\n                    let current = { \n                        SteerPos = current.Pos\n                        SteerDir = current.Dir\n                        }                    \n                    let dir = Steering.getSteeringDirection settings neighbors current\n                    let velocity = dir * r.Value5.MaxSpeed\n                    neighbors.Clear()\n                    h <- Heading.fromVelocity velocity\n\n        member c.AddLifespan() =\n            c.On<Update> <| fun e ->\n                let dt = float32 e.DeltaTime / 1000.0f\n                for r in c.Query<Lifespan, Eid>() do\n                    let ls = r.Value1\n                    let newLifespan = { Lifespan = ls.Lifespan - dt }\n                    if ls.Lifespan <= 0.0f then\n                        let eid = r.Value2\n                        c.Destroy(eid)\n                    r.Value1 <- newLifespan\n\n        member c.AddUpdatePosition() =\n            c.On<Update> <| fun e ->\n                let dt = float32 e.DeltaTime / 1000.0f\n                for r in c.Query<Position, Heading>() do\n                    r.Value1 <- { Pos = Heading.getNextPosition dt r.Value2 r.Value1.Pos }\n\n        member c.AddUpdateRotation() =\n            c.On<Update> <| fun e ->\n                let dt = float32 e.DeltaTime / 1000.0f\n                for r in c.Query<Rotation, AngularVelocity>() do\n                    r.Value1 <- { Radians = r.Value1.Radians + dt * r.Value2.RotationSpeed }\n            \n        member c.AddTrailEmission() =\n            c.On<Update> <| fun _ ->\n                for r in c.Query<TrailEmitter, Position, Faction, Heading>() do\n                    c.Create()\n                        .With<Faction>(r.Value3)\n                        .With<Position>(r.Value2)\n                        .With({ Radians = r.Value4.Direction.GetRadians() })\n                        .With({ Lifespan = 0.6f })\n                        .Add(Trail())\n\n    let add (c : Container) =\n        Disposable.Create [\n            c.AddSpawning()\n            c.AddLifespan()\n            c.AddUpdatePosition()\n            c.AddUpdateRotation()\n            c.AddTrailEmission()\n            c.AddSteering()\n            ]\n\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Startup.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System\nopen Veldrid\nopen Garnet.Composition\nopen Garnet.Graphics\n\nmodule StartupSystem =\n    type Container with\n        member c.LoadResources() =\n            // Manually load textures into atlas. Note other resources like\n            // shaders can be loaded on demand and don't need to be explicitly\n            // loaded here.\n            let device = c.Get<GraphicsDevice>()\n            let cache = c.Get<ResourceCache>()\n            use fs = new FileFolder(\"assets\")\n            fs.LoadTextureAtlasFromFolder(device, Resources.atlas, 512, 512, cache)\n            Disposable.Null\n\n    let add (c : Container) =\n        // Set global window settings, which will be used by default systems\n        c.Set {\n            WindowSettings.Default with\n                Title = \"Flocking\"\n                Width = 800\n                Height = 600\n                Background = RgbaFloat(0.0f, 0.1f, 0.2f, 1.0f)\n                }\n        // Set global settings used by simulation\n        c.Set(WorldSettings.defaults)\n        Disposable.Create [\n            // Default systems for window, sprite drawing, updates, etc\n            c.AddDefaultSystems()\n            c.LoadResources()\n        ]\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/Types.fs",
    "content": "﻿namespace Garnet.Samples.Flocking\n\nopen System.Numerics\nopen Garnet.Composition\n\n[<AutoOpen>]\nmodule Components =\n    [<Struct>]\n    type Faction =\n        | Red\n        | Orange\n        | Yellow\n        | Green\n        | Cyan\n        | Blue\n        | Purple\n\n    [<Struct>]\n    type Position = {\n        Pos : Vector2\n        }\n\n    [<Struct>]\n    type Heading = {\n        Direction : Vector2\n        Speed : float32\n        }\n\n    [<Struct>]\n    type Vehicle = {\n        Radius : float32\n        MaxSpeed : float32\n        }\n\n    [<Struct>]\n    type TrailLifespan = {\n        TrailLifespan : float32\n        }\n\n    [<Struct>]\n    type Lifespan = {\n        Lifespan : float32\n        }\n\n    [<Struct>]\n    type AngularVelocity = {\n        RotationSpeed : float32\n        }\n\n    [<Struct>]\n    type Rotation = {\n        Radians : float32\n        }\n\n    type TrailEmitter = struct end\n    type Trail = struct end\n\n[<AutoOpen>]\nmodule Settings =\n    type SteeringSettings = {\n        ForwardWeight : float32\n        CohesionWeight : float32\n        TetherWeight : float32\n        SeparationWeight : float32\n        AlignmentWeight : float32\n        MaxAlignmentDistance : float32\n        MaxSeparationDistance : float32\n        MaxCohesionDistance : float32\n        MaxTetherDistance : float32\n        }\n\n    type WorldSettings = {\n        Seed : int\n        SpawnRange : float32\n        VehicleCount : int\n        MaxVehicleSpeed : float32\n        TrailLifespan : float32\n        Steering : SteeringSettings\n        }\n\n[<AutoOpen>]\nmodule SteeringTypes =\n    [<Struct>]\n    type Steerer = {\n        SteerPos : Vector2\n        SteerDir : Vector2\n        }\n\n    [<Struct>]\n    type Neighbor = {\n        Direction : Vector2\n        DirectionToNeighbor : Vector2\n        Distance : float32\n        TeamWeight : float32\n        }\n\n    [<Struct>]\n    type CurrentVehicle = {\n        Eid : Eid\n        Pos : Vector2\n        Dir : Vector2\n        Faction : Faction\n        }\n\n[<AutoOpen>]\nmodule Events =\n    type Reset = struct end\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.frag",
    "content": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) out vec4 fsout_color;\nlayout(set = 1, binding = 1) uniform texture2D SurfaceTexture;\nlayout(set = 1, binding = 2) uniform sampler SurfaceSampler;\nvoid main()\n{\n    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);\n    fsout_color =  texColor * fsin_color;\n}"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.frag.hlsl.bytes",
    "content": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic float4 _29;\n\nstruct SPIRV_Cross_Input\n{\n    float2 _22 : TEXCOORD0;\n    float4 _29 : TEXCOORD1;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float4 _26 : SV_Target0;\n};\n\nvoid frag_main()\n{\n    _26 = _12.Sample(_16, _22) * _29;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _22 = stage_input._22;\n    _29 = stage_input._29;\n    frag_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output._26 = _26;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.vert",
    "content": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1) uniform ViewBuffer\n{\n    mat4 View;\n};\nlayout(set = 1, binding = 0) uniform WorldBuffer\n{\n    mat4 World;\n};\nlayout(location = 0) in vec3 Position;\nlayout(location = 1) in vec2 TexCoords;\nlayout(location = 2) in vec4 Color;\nlayout(location = 0) out vec2 fsin_texCoords;\nlayout(location = 1) out vec4 fsin_Color;\nvoid main()\n{\n    vec4 worldPosition = World * vec4(Position, 1);\n    vec4 viewPosition = View * worldPosition;\n    vec4 clipPosition = Projection * viewPosition;\n    gl_Position = clipPosition;\n    fsin_texCoords = TexCoords;\n    fsin_Color = Color;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Flocking/assets/texture-color.vert.hlsl.bytes",
    "content": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    row_major float4x4 _32_m0 : packoffset(c0);\n};\n\ncbuffer _38_40 : register(b0)\n{\n    row_major float4x4 _40_m0 : packoffset(c0);\n};\n\n\nstatic float4 gl_Position;\nstatic float3 _21;\nstatic float2 _56;\nstatic float2 _58;\nstatic float4 _60;\nstatic float4 _62;\n\nstruct SPIRV_Cross_Input\n{\n    float3 _21 : TEXCOORD0;\n    float2 _58 : TEXCOORD1;\n    float4 _62 : TEXCOORD2;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float2 _56 : TEXCOORD0;\n    float4 _60 : TEXCOORD1;\n    float4 gl_Position : SV_Position;\n};\n\nvoid vert_main()\n{\n    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);\n    _56 = _58;\n    _60 = _62;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _21 = stage_input._21;\n    _58 = stage_input._58;\n    _62 = stage_input._62;\n    vert_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output.gl_Position = gl_Position;\n    stage_output._56 = _56;\n    stage_output._60 = _60;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/ConsoleTest.fsx",
    "content": "﻿#load \"Types.fs\"\n#load \"Functions.fs\"\n\nopen Garnet.Samples.Roguelike.Types\nopen Garnet.Samples.Roguelike\n\nlet test() =\n    let world = World.generate 10 1 \n    World.format world |> printfn \"%s\"\n    World.getDistanceMap world [ Vector.zero ] |> DistanceMap.format |> printfn \"%s\"\n    \n    [ Move West; Move West ] \n    |> Seq.fold Loop.stepWorld world\n    |> World.format \n    |> printfn \"%s\"\n\nlet testInteractive() =\n    World.generate 10 1 |> Loop.run\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Drawing.fs",
    "content": "﻿namespace Garnet.Samples.Roguelike\n\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen Veldrid\nopen Garnet.Graphics\n\n[<Struct>]\ntype DisplayTile = {\n    ch : char\n    fg : RgbaFloat\n    bg : RgbaFloat\n    }\n\nmodule DisplayTile =\n    let fromTile tile =\n        match tile.Entity with\n        | Some entity ->\n            match entity.EntityType with\n            | Rogue -> {\n                ch = '@'\n                fg = RgbaFloat(0.3f, 1.0f, 1.0f, 1.0f)\n                bg = RgbaFloat(0.3f, 1.0f, 1.0f, 0.3f)\n                }\n            | Minion -> {\n                ch = 'm'\n                fg = RgbaFloat(1.0f, 0.3f, 0.3f, 1.0f)\n                bg = RgbaFloat(1.0f, 0.3f, 0.3f, 0.3f)\n                }\n        | None ->\n            match tile.Terrain with\n            | Floor -> {\n                ch = '.'\n                fg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.5f)\n                bg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.1f)\n                }\n            | Wall -> {\n                ch = '#'\n                fg = RgbaFloat(0.3f, 0.4f, 0.5f, 1.0f)\n                bg = RgbaFloat(0.3f, 0.4f, 0.5f, 0.3f)\n                }\n            \n[<Extension>]\ntype ViewExtensions =\n    [<Extension>]\n    static member DrawWorld(w : IBufferWriter<PositionTextureDualColorVertex>, world : World) = \n        let span = w.GetTileSpan(world.Tiles.Count)\n        let min = World.getMinLocation world\n        let mutable i = 0\n        for kvp in world.Tiles do\n            let p = Vector.subtract kvp.Key min\n            let tile = DisplayTile.fromTile kvp.Value\n            span.Slice(i * 4).DrawTile(p.X, p.Y, tile.ch, tile.fg, tile.bg)\n            i <- i + 1\n        w.Advance(span.Length)\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Functions.fs",
    "content": "﻿namespace Garnet.Samples.Roguelike\n\nopen System\nopen System.Collections.Generic\n\nmodule Vector =\n    let init x y = { X = x; Y = y }\n    let zero = init 0 0\n    let one = init 1 1\n\n    let min a b = { X = min a.X b.X; Y = min a.Y b.Y }\n    let max a b = { X = max a.X b.X; Y = max a.Y b.Y }\n\n    let add a b = { \n        X = a.X + b.X\n        Y = a.Y + b.Y\n        }\n\n    let subtract a b = { \n        X = a.X - b.X\n        Y = a.Y - b.Y\n        }\n\nmodule Bounds =\n    let init min max = { Min = min; Max = max }\n    let sized min size = init min (Vector.add min size)\n    let zero = init Vector.zero Vector.zero\n    let zeroToOne = init Vector.zero Vector.one\n\n    let maxToMin = {\n        Min = { X = Int32.MaxValue; Y = Int32.MaxValue }\n        Max = { X = Int32.MinValue; Y = Int32.MinValue }\n        }\n\n    let including bounds p = { \n        Min = Vector.min bounds.Min p\n        Max = Vector.max bounds.Max p\n        }\n\n    let union a b = { \n        Min = Vector.min a.Min b.Min\n        Max = Vector.max a.Max b.Max\n        }\n\n    let getSize b =\n        Vector.subtract b.Max b.Min\n\n    let getCenter b =\n        let v = Vector.add b.Max b.Min\n        { X = v.X / 2; Y = v.Y / 2 }\n\n    let getCentered contentSize b = \n        let size = getSize b\n        {\n            X = b.Min.X + (size.X - contentSize.X) / 2\n            Y = b.Min.Y + (size.Y - contentSize.Y) / 2\n        }\n        \n    let expand margin b = {\n        Min = Vector.subtract b.Min margin.Min\n        Max = Vector.add b.Max margin.Max\n        }\n\n    let includingAll locs =\n        locs \n        |> Seq.fold including maxToMin\n        |> expand zeroToOne\n\nmodule Direction =\n    let all = [|\n        East\n        North\n        West\n        South\n        |]\n\n    let getNext loc dir =\n        match dir with\n        | East -> { loc with X = loc.X + 1 } \n        | West -> { loc with X = loc.X - 1 }\n        | North -> { loc with Y = loc.Y - 1 }\n        | South -> { loc with Y = loc.Y + 1 }\n    \nmodule DistanceMap =\n    let empty = {\n        Distances = Map.empty\n        }\n\n    let create isPassable (tiles : Map<Vector, _>) seeds =\n        let result = Dictionary<Vector, int>()\n        let queue = Queue<struct(Vector * int)>()\n        let enqueue p dist =\n            if not (result.ContainsKey(p)) then\n                let canVisit =\n                    match tiles.TryGetValue(p) with\n                    | false, _ -> false\n                    | true, tile -> isPassable tile\n                result.Add(p, if canVisit then dist else Int32.MaxValue)\n                if canVisit then queue.Enqueue(struct(p, dist))                            \n        for seed in seeds do\n            enqueue seed 0\n        while queue.Count > 0 do\n            let struct(p, dist) = queue.Dequeue()\n            let nextDist = dist + 1\n            for dir in Direction.all do\n                let next = Direction.getNext p dir\n                enqueue next nextDist\n        {\n            Distances = \n                result\n                |> Seq.map (fun kvp -> kvp.Key, kvp.Value)\n                |> Map.ofSeq\n        }\n\n    let getDistance p map =\n        match map.Distances.TryGetValue(p) with\n        | true, dist -> dist\n        | false, _ -> Int32.MaxValue\n\n    let distanceToChar x =\n        if x = 0 then '.'\n        elif x < 10 then '0' + char x\n        elif x < 36 then 'a' + char (x - 10)\n        elif x < 62 then 'A' + char (x - 36)\n        elif x = Int32.MaxValue then '#'\n        else '+'\n    \n    let format map =\n        let b = map.Distances |> Seq.map (fun kvp -> kvp.Key) |> Bounds.includingAll\n        let size = Bounds.getSize b\n        let dw = size.X + 1\n        let data = Array.create (dw * size.Y) ' '\n        for y = 0 to size.Y - 1 do\n            data.[y * dw + dw - 1] <- '\\n'\n        for kvp in map.Distances do\n            let p = Vector.subtract kvp.Key b.Min\n            data.[p.Y * dw + p.X] <- distanceToChar kvp.Value\n        String(data)\n\nmodule Tile =\n    let getChar tile =\n        match tile.Entity with\n        | Some e ->\n            match e.EntityType with\n            | Rogue -> '@'\n            | Minion -> 'm'\n        | None ->\n            match tile.Terrain with\n            | Floor -> '.'\n            | Wall -> '#' \n\n    let getMoveEvents loc nextLoc dir tile = seq {\n        match tile.Entity with\n        | Some entity -> \n            if entity.Hits = 1 then yield Destroyed nextLoc\n            else\n                yield Attacked {\n                    AttackerLoc = loc\n                    AttackDir = dir\n                    Damage = 1     \n                    }\n        | None -> yield Moved {\n            SourceLoc = loc\n            MoveDir = dir\n            }\n        }\n    \n    let addEntity entity tile =\n        { tile with Entity = Some entity }\n\n    let removeEntity tile =\n        { tile with Entity = None }\n\n    let isPassable tile =\n        match tile.Terrain with\n        | Wall -> false\n        | Floor -> true\n        \nmodule Entity =\n    let rogue = {\n        EntityType = Rogue\n        Hits = 3\n    }\n\n    let minion = {\n        EntityType = Minion\n        Hits = 1\n    }\n\n    let applyDamage damage entity =\n        { entity with Hits = entity.Hits - damage }\n\nmodule Animation =\n    let format =\n        function\n        | Moving e -> $\"Moved {e.MoveDir}\"\n        | Attacking e -> $\"{e.AttackerEntityType} attacked {e.TargetEntityType}\"\n        | Destroying e -> $\"{e.DestroyedEntityType} destroyed\"\n\nmodule World =\n    let empty = {\n        Turn = 0\n        RandomSeed = 0UL\n        Tiles = Map.empty\n        Animations = List.empty\n        }\n\n    let generate mapRadius seed =\n        let r = mapRadius + 1\n        let extent = r * 2 + 1 \n        let count = extent * extent\n        let rand = Random(seed)\n        // draw random walls with border\n        let cells1 = Array.zeroCreate count\n        for y = -r to r do\n            for x = -r to r do\n                let i = (y + r) * extent + (x + r)\n                let dist = max (abs x) (abs y)\n                let cell = dist = r || (dist > 2 && rand.Next(10) = 0)\n                cells1.[i] <- cell\n        // apply morphological dilate\n        let cells2 = Array.zeroCreate count\n        let rm = r - 1\n        for y = -rm to rm do\n            for x = -rm to rm do\n                let i = (y + r) * extent + (x + r)\n                let cell =\n                    if cells1.[i] then true\n                    else\n                        let ix0 = i - 1\n                        let ix1 = i + 1\n                        let iy0 = i - extent\n                        let iy1 = i + extent\n                        cells1.[ix0] || cells1.[ix1] || cells1.[iy0] || cells1.[iy1]\n                cells2.[i] <- cell\n        // populate tiles\n        let tiles = seq {\n            for y = -rm to rm do\n                for x = -rm to rm do\n                    let i = (y + r) * extent + (x + r)\n                    let terrain = if cells2.[i] then Wall else Floor\n                    let p = Vector.init x y\n                    yield p, {\n                        Terrain = terrain\n                        Entity =\n                            match terrain with\n                            | Wall -> None\n                            | Floor ->\n                                if p = Vector.zero then Some Entity.rogue\n                                elif rand.Next(8) = 0 then Some Entity.minion\n                                else None\n                    }\n            }\n        { empty with \n            RandomSeed = uint64 seed\n            Tiles = Map.ofSeq tiles\n        }\n\n    let getMinLocation world =\n        world.Tiles |> Seq.map (fun kvp -> kvp.Key) |> Seq.reduce Vector.min\n\n    let formatTiles world =\n        let b = world.Tiles |> Seq.map (fun kvp -> kvp.Key) |> Bounds.includingAll\n        let size = Bounds.getSize b\n        let dw = size.X + 1\n        let data = Array.create (dw * size.Y) ' '\n        for y = 0 to size.Y - 1 do\n            data.[y * dw + dw - 1] <- '\\n'\n        for kvp in world.Tiles do\n            let p = Vector.subtract kvp.Key b.Min\n            data.[p.Y * dw + p.X] <- Tile.getChar kvp.Value\n        String(data)\n\n    let formatAnimations world =\n        world.Animations\n        |> List.rev\n        |> Seq.map Animation.format\n        |> String.concat \"\\n\"\n\n    let format world =\n        $\"Turn {world.Turn}:\\n{formatAnimations world}\\n{formatTiles world}\"\n\n    let getEntityLocations entityType world = seq {\n        for kvp in world.Tiles do\n            match kvp.Value.Entity with\n            | Some entity -> if entity.EntityType = entityType then yield kvp.Key\n            | None -> ()\n        }\n        \n    let isOccupied loc world =\n        match Map.tryFind loc world.Tiles with\n        | Some tile -> tile.Terrain = Wall || tile.Entity.IsSome\n        | None -> true\n        \n    let tryGetEntity loc world =\n        Map.tryFind loc world.Tiles\n        |> Option.bind (fun tile -> tile.Entity)\n\n    let mapTile map loc world =\n        match Map.tryFind loc world.Tiles with\n        | Some tile -> { world with Tiles = Map.add loc (map tile) world.Tiles }\n        | None -> world\n\n    let mapEntity map loc world =\n        mapTile (fun tile -> { tile with Entity = Option.map map tile.Entity }) loc world\n\n    let addEntity loc entity world =\n        mapTile (Tile.addEntity entity) loc world\n\n    let removeEntity loc world =\n        mapTile Tile.removeEntity loc world\n\n    let moveEntity loc newLoc world =\n        match tryGetEntity loc world with\n        | Some entity ->\n            world \n            |> removeEntity loc\n            |> addEntity newLoc entity\n        | None -> world\n\n    let appendAnimation anim world = {\n        world with Animations = anim :: world.Animations\n        }\n\n    let find entityType world =\n        world.Tiles\n        |> Map.tryPick (fun loc tile ->\n            tile.Entity \n            |> Option.bind (fun e -> \n                if e.EntityType = entityType then Some (loc, e) else None))\n\n    let getDistanceMap map targets =\n        DistanceMap.create Tile.isPassable map.Tiles targets\n\n    let stepTurn world =\n        { world with Turn = world.Turn + 1 }\n\nmodule Action =\n    let getEvents action loc world =\n        match action with\n        | Move dir ->\n            let nextLoc = Direction.getNext loc dir\n            match Map.tryFind nextLoc world.Tiles with\n            | Some tile -> Tile.getMoveEvents loc nextLoc dir tile\n            | None -> Seq.empty\n\n    let getPlayerEvents action world =\n        match World.find Rogue world with\n        | Some (loc, _) -> getEvents action loc world\n        | None -> Seq.empty\n\nmodule Event =\n    let applyEvent world event =\n        match event with\n        | Attacked e -> \n            match World.tryGetEntity e.AttackerLoc world with\n            | None -> world\n            | Some attacker ->\n                let targetLoc = Direction.getNext e.AttackerLoc e.AttackDir\n                match World.tryGetEntity targetLoc world with\n                | None -> world\n                | Some target ->\n                    world\n                    |> World.mapEntity (Entity.applyDamage e.Damage) targetLoc\n                    |> World.appendAnimation (Attacking {\n                        AttackerLoc = e.AttackerLoc\n                        AttackerEntityType = attacker.EntityType\n                        AttackDir = e.AttackDir\n                        Damage = e.Damage\n                        TargetEntityType = target.EntityType\n                        })\n        | Moved e -> \n            let targetLoc = Direction.getNext e.SourceLoc e.MoveDir\n            world\n            |> World.moveEntity e.SourceLoc targetLoc\n            |> World.appendAnimation (Moving {\n                SourceLoc = e.SourceLoc\n                MoveDir = e.MoveDir\n                })\n        | Destroyed p -> \n            match World.tryGetEntity p world with\n            | None -> world\n            | Some target ->\n                world\n                |> World.removeEntity p\n                |> World.appendAnimation (Destroying {\n                    DestroyedLoc = p\n                    DestroyedEntityType = target.EntityType\n                    })\n\nmodule Loop =\n    let tryGetAction key =\n        match key with\n        | ConsoleKey.RightArrow -> Move East |> Some\n        | ConsoleKey.LeftArrow -> Move West |> Some\n        | ConsoleKey.UpArrow -> Move North |> Some\n        | ConsoleKey.DownArrow -> Move South |> Some\n        | _ -> None\n\n    let readPlayerActions() = seq {\n        let mutable isRunning = true\n        while isRunning do\n            let key = Console.ReadKey().Key\n            match tryGetAction key with\n            | Some action -> yield action\n            | None -> isRunning <- key <> ConsoleKey.Escape\n        }\n\n    let printWorld world =\n        world |> World.format |> printfn \"%s\"\n\n    let applyPlayerEvents action world =\n        Action.getPlayerEvents action world\n        |> Seq.fold Event.applyEvent world\n        \n    let getHostileMoveEvents p dm world =\n        let dirs =\n            Direction.all\n            |> Seq.filter (fun dir ->\n                let next = Direction.getNext p dir\n                not (World.isOccupied next world))\n            |> Seq.toArray\n        if dirs.Length = 0 then world\n        else\n            let nearestDir =\n                dirs\n                |> Seq.minBy (fun dir ->\n                    let next = Direction.getNext p dir\n                    DistanceMap.getDistance next dm)\n            Moved {\n                SourceLoc = p\n                MoveDir = nearestDir\n                }\n            |> Event.applyEvent world\n        \n    let applyHostileEvents world =\n        let targetLocs = World.getEntityLocations Rogue world\n        let dm = World.getDistanceMap world targetLocs\n        World.getEntityLocations Minion world\n        |> Seq.sortBy (fun p -> DistanceMap.getDistance p dm)\n        |> Seq.fold (fun state p -> getHostileMoveEvents p dm state) world           \n            \n    let stepWorld world action =\n        { world with Animations = List.empty }\n        |> applyPlayerEvents action\n        |> applyHostileEvents\n        |> World.stepTurn\n\n    let run world =\n        readPlayerActions()\n        |> Seq.scan stepWorld world\n        |> Seq.iter printWorld\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Game.fs",
    "content": "﻿namespace Garnet.Samples.Roguelike\n\nopen System\nopen System.Numerics\nopen System.Threading\nopen Veldrid\nopen Garnet.Numerics\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Input\n\nmodule Resources =\n    let tileTexture = \"drake-10x10-transparent.png\"\n\n    let shaderSet : ShaderSetDescriptor<PositionTextureDualColorVertex> = {\n        VertexShader = \"texture-dual-color.vert\"\n        FragmentShader = \"texture-dual-color.frag\"\n        }\n\n    // Use point sampling for pixelated appearance\n    let pipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Point\n        ShaderSet = shaderSet\n        Texture = tileTexture\n        }\n\n    // Avoid auto flush since we only update when an action occurs\n    let tileLayer = {\n        LayerId = 0\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = NoFlush\n        Pipeline = pipeline \n        }\n\nmodule Command =\n    let getCommand =\n        function\n        | Key.R -> Command.Reset\n        | Key.Right -> Command.MoveEast\n        | Key.Up -> Command.MoveNorth\n        | Key.Left -> Command.MoveWest\n        | Key.Down -> Command.MoveSouth\n        | Key.F11 -> Command.FullScreen\n        | _ -> Command.None\n\n    let tryGetAction =\n        function\n        | Command.MoveEast -> Move East |> Some\n        | Command.MoveNorth -> Move North |> Some\n        | Command.MoveWest -> Move West |> Some\n        | Command.MoveSouth -> Move South |> Some\n        | _ -> None\n\n[<AutoOpen>]\nmodule DrawingExtensions =\n    type SpriteRenderer with\n        member c.DrawWorld(world) =\n            let tiles = c.GetVertices(Resources.tileLayer)\n            tiles.DrawWorld(world)\n            tiles.Flush()\n\ntype Game(fs : IReadOnlyFolder) =\n    // Image is a tilemap of ASCII chars (16x16=256 tiles)\n    let image = fs.LoadImage(Resources.tileTexture)\n    // Calculate window size to match map and tile size\n    let tileScale = 2\n    let tileWidth = image.Width / 16\n    let tileHeight = image.Height / 16\n    let mapRadius = 15\n    let mapExtent = mapRadius * 2 + 1\n    // Create window and graphics device\n    let ren =\n        new WindowRenderer {\n            WindowSettings.Default with\n                Title = \"Roguelike\"\n                Width = mapExtent * tileWidth * tileScale\n                Height = mapExtent * tileHeight * tileScale\n                Redraw = Redraw.Manual\n                Background = RgbaFloat.Black\n                }\n    // Initialize rendering\n    let shaders = new ShaderSetCache()\n    let cache = new ResourceCache()\n    let sprites = new SpriteRenderer(ren.Device, shaders, cache)\n    do \n        cache.AddShaderLoaders(ren.Device)\n        fs.LoadShadersFromFolder(\".\", ren.Device.BackendType, cache)\n        cache.AddResource(Resources.tileTexture, ren.Device.CreateTexture(image))\n    member c.Run() =\n        // Set transforms so drawing code can use tile coords for source (16x16 tileset) \n        // and destination (80x25 display tiles)\n        let cameras = CameraSet()\n        let texTileSize = 1.0f / 16.0f\n        let camera = cameras.[0]\n        camera.WorldTransform <- Matrix4x4.CreateScale(float32 tileWidth, float32 tileHeight, 1.0f)\n        camera.TextureTransform <- Matrix4x4.CreateScale(texTileSize, texTileSize, 1.0f)\n        // Start loop\n        let inputs = InputCollection()\n        let mutable state = World.generate mapRadius 1\n        sprites.DrawWorld(state)\n        while ren.Update(0.0f, inputs) do\n            for e in inputs.KeyDownEvents do\n                match Command.getCommand e.KeyCode with\n                | Command.FullScreen -> ren.ToggleFullScreen()\n                | Command.None -> ()\n                | command ->\n                    match Command.tryGetAction command with\n                    | None -> ()\n                    | Some action -> \n                        state <- Loop.stepWorld state action\n                        sprites.DrawWorld(state)\n                        // Note we only invalidate when something changes instead of every frame\n                        ren.Invalidate()\n            if ren.BeginDraw() then\n                // Update transforms according to window size so we can draw using pixel coords\n                // with origin in upper left of view\n                let displayScale = float32 tileScale\n                let size = ren.Size.ToVector2() / displayScale\n                camera.ProjectionTransform <- Matrix4x4.CreateOrthographic(size.X, -size.Y, -100.0f, 100.0f)\n                camera.ViewTransform <- Matrix4x4.CreateTranslation(-size.X * 0.5f, -size.Y * 0.5f, 0.0f)\n                sprites.Draw(ren.RenderContext, cameras)\n                ren.EndDraw()\n            // Sleep to avoid spinning CPU\n            Thread.Sleep(1)\n    interface IDisposable with\n        member c.Dispose() =\n            cache.Dispose()\n            shaders.Dispose()\n            sprites.Dispose()\n            ren.Dispose()\n    static member Run(fs) =\n        use game = new Game(fs)\n        game.Run()\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Garnet.Samples.Roguelike.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PublishTrimmed>true</PublishTrimmed>\n    <TrimMode>Link</TrimMode>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"assets\\drake-10x10-transparent.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-dual-color.frag\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-dual-color.vert\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <Compile Include=\"Types.fs\" />\n    <Compile Include=\"Functions.fs\" />\n    <Compile Include=\"Drawing.fs\" />\n    <Compile Include=\"Game.fs\" />\n    <Compile Include=\"Program.fs\" />\n    <None Include=\"ConsoleTest.fsx\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Program.fs",
    "content": "open Garnet.Composition\nopen Garnet.Samples.Roguelike\n\n[<EntryPoint>]\nlet main argv =\n    use fs = new FileFolder(\"assets\")\n    Game.Run(fs)\n    0"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/Types.fs",
    "content": "﻿namespace Garnet.Samples.Roguelike\n\n[<AutoOpen>]\nmodule WorldTypes =\n    type Vector = {\n        X : int\n        Y : int\n        }\n\n    type Bounds = {\n        Min : Vector\n        Max : Vector\n        }\n\n    type Direction =\n        | East\n        | West\n        | North\n        | South\n\n    type DistanceMap = {\n        Distances : Map<Vector, int>\n        }\n        \n    type Terrain =\n        | Floor\n        | Wall\n\n    type EntityType =\n        | Rogue\n        | Minion\n\n    type Entity = {\n        EntityType : EntityType\n        Hits : int\n        }\n\n    type Tile = {\n        Terrain : Terrain\n        Entity : Entity option\n        }\n\n    type Action =\n        | Move of Direction\n\n    type MovedEvent = {\n        SourceLoc : Vector\n        MoveDir : Direction\n        }\n\n    type AttackedEvent = {\n        AttackerLoc : Vector\n        AttackDir : Direction\n        Damage : int\n        }\n\n    /// Events hold the minimal information needed to reconstruct world state\n    type Event =\n        | Moved of MovedEvent\n        | Attacked of AttackedEvent\n        | Destroyed of Vector\n        \n    type MovingEvent = {\n        SourceLoc : Vector\n        MoveDir : Direction\n        }\n\n    type AttackingAnimation = {\n        AttackerLoc : Vector\n        AttackerEntityType : EntityType\n        AttackDir : Direction\n        Damage : int\n        TargetEntityType : EntityType\n        }\n        \n    type DestroyingAnimation = {\n        DestroyedLoc : Vector\n        DestroyedEntityType : EntityType\n        }\n\n    /// Animations can enriched with extra info to help present events that occurred\n    /// during a turn to the player\n    type Animation =\n        | Moving of MovingEvent\n        | Attacking of AttackingAnimation\n        | Destroying of DestroyingAnimation\n\n    type World = {\n        Turn : int\n        RandomSeed : uint64\n        Tiles : Map<Vector, Tile>\n        Animations : Animation list\n        }\n\n    [<Struct>]\n    type Command =\n        | None = 0\n        | MoveEast = 1\n        | MoveNorth = 2\n        | MoveWest = 3\n        | MoveSouth = 4\n        | Reset = 5\n        | FullScreen = 6\n"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/assets/texture-dual-color.frag",
    "content": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_fg;\nlayout(location = 2) in vec4 fsin_bg;\nlayout(location = 0) out vec4 fsout_color;\nlayout(set = 1, binding = 2) uniform texture2D SurfaceTexture;\nlayout(set = 1, binding = 3) uniform sampler SurfaceSampler;\nvoid main()\n{\n    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);    \n    vec4 fg = fsin_fg;\n    fg.rgb *= texColor.rgb;\n    fsout_color =  mix(fsin_bg, fg, texColor.a);\n}"
  },
  {
    "path": "samples/Garnet.Samples.Roguelike/assets/texture-dual-color.vert",
    "content": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1) uniform ViewBuffer\n{\n    mat4 View;\n};\nlayout(set = 1, binding = 0) uniform WorldBuffer\n{\n    mat4 World;\n};\nlayout(set = 1, binding = 1) uniform TexTransformBuffer\n{\n    mat4 TexTransform;\n};\nlayout(location = 0) in vec3 Position;\nlayout(location = 1) in vec2 TexCoords;\nlayout(location = 2) in vec4 FgColor;\nlayout(location = 3) in vec4 BgColor;\nlayout(location = 0) out vec2 fsin_texCoords;\nlayout(location = 1) out vec4 fsin_fg;\nlayout(location = 2) out vec4 fsin_bg;\nvoid main()\n{\n    vec4 worldPosition = World * vec4(Position, 1);\n    vec4 viewPosition = View * worldPosition;\n    vec4 clipPosition = Projection * viewPosition;\n    gl_Position = clipPosition;\n    fsin_texCoords = (TexTransform * vec4(TexCoords, 1, 1)).xy;\n    fsin_fg = FgColor;\n    fsin_bg = BgColor;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Drawing.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numerics\nopen Garnet.Graphics\n\n[<Extension>]\ntype VertexSpanExtensions =\n    [<Extension>]\n    static member DrawLine(span : Span<PositionTextureColorVertex>, \n            p0 : Vector2, \n            p1 : Vector2, \n            thickness : float32, \n            color : RgbaFloat) = \n        let delta = p1 - p0\n        let length = delta.Length()\n        let dir = if length < 1e-5f then Vector2.Zero else delta / length\n        span.DrawQuad {\n            Center = (p0 + p1) * 0.5f\n            Size = Vector2(thickness, length)\n            Rotation = dir.GetPerpendicular()\n            TexBounds = Range2.ZeroToOne\n            Color = color\n            }\n\n    [<Extension>]\n    static member DrawAxialLine(span : Span<PositionTextureColorVertex>, \n            p0 : Vector2i, \n            p1 : Vector2i, \n            thickness, \n            color : RgbaFloat) =\n        let ep0 = TriCoords.vertexToEuc p0\n        let ep1 = TriCoords.vertexToEuc p1\n        span.DrawLine(ep0, ep1, thickness, color)\n\n[<Extension>]\ntype VertexBufferWriterExtensions =\n    [<Extension>]\n    static member DrawGridLines(w : IBufferWriter<PositionTextureColorVertex>, spacing, thickness, color, r) = \n        let extent = r / spacing\n        let count = extent * 2 + 1\n        let span = w.GetQuadSpan(count * 3)\n        for i = -extent to extent do\n            let p = i * spacing\n            let di = (i + extent) * 3 * 4\n            let p0 = Vector2i(-r, p)\n            let p1 = Vector2i(r, p)\n            span.Slice(di + 0).DrawAxialLine(p0, p1, thickness, color)\n            let p0 = Vector2i(p, -r)\n            let p1 = Vector2i(p, r)\n            span.Slice(di + 4).DrawAxialLine(p0, p1, thickness, color)\n            let p0 = Vector2i(-r, p + r)\n            let p1 = Vector2i(r, p - r)\n            span.Slice(di + 8).DrawAxialLine(p0, p1, thickness, color)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawGridLines(w : IBufferWriter<PositionTextureColorVertex>,\n            majorSpacing,\n            majorThickness,\n            majorColor,\n            minorThickness,\n            minorColor,\n            r) =\n        w.DrawGridLines(majorSpacing, majorThickness, majorColor, r)\n        w.DrawGridLines(1, minorThickness, minorColor, r)\n\n    [<Extension>]\n    static member DrawGridLines(w : IBufferWriter<PositionTextureColorVertex>) =\n        let minorColor = RgbaFloat(0.8f, 0.6f, 0.1f, 0.2f)\n        let majorColor = RgbaFloat(0.8f, 0.6f, 0.1f, 0.3f)\n        let majorSpacing = 6\n        let majorThickness = 0.1f\n        let minorThickness = 0.05f\n        w.DrawGridLines(majorSpacing, majorThickness, majorColor, minorThickness, minorColor, 100)\n    \n    [<Extension>]\n    static member DrawGridCells(w : IBufferWriter<PositionTextureColorVertex>, state) =\n        let cellMargin = 0.1f\n        let vertexCount = state.Cells.Count * 3\n        let span = w.GetSpan(vertexCount)\n        let mutable i = 0\n        for kvp in state.Cells do\n            let p = Vector2i(kvp.Key.X, kvp.Key.Y)\n            let tri = TriPositions.fromTriCell p\n            let centroid = (tri.P0 + tri.P1 + tri.P2) / 3.0f\n            let p0 = Vector2.Lerp(tri.P0, centroid, cellMargin)\n            let p1 = Vector2.Lerp(tri.P1, centroid, cellMargin)\n            let p2 = Vector2.Lerp(tri.P2, centroid, cellMargin)\n            let color = kvp.Value.ToRgbaFloat()\n            let verts = span.Slice(i * 3)\n            verts.[0] <- {\n                Position = Vector3(p0.X, p0.Y, 0.0f)\n                TexCoord = Vector2(0.0f, 0.0f)\n                Color = color\n                }\n            verts.[1] <- {\n                Position = Vector3(p1.X, p1.Y, 0.0f)\n                TexCoord = Vector2(1.0f, 0.0f)\n                Color = color\n                }\n            verts.[2] <- {\n                Position = Vector3(p2.X, p2.Y, 0.0f)\n                TexCoord = Vector2(0.0f, 1.0f)\n                Color = color\n                }\n            i <- i + 1\n        w.Advance(vertexCount)\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Functions.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen Newtonsoft.Json\nopen Garnet.Numerics\nopen Veldrid\n\nmodule CellLocation =\n    let origin = { X = 0; Y = 0 }\n    \nmodule TriCoords =\n    /// Height of an equilateral triangle with edge length one. Multiplier to determine simplex\n    /// height given an edge length (sqrt(3/4) = 0.866025403784f).\n    let edgeToHeight = 0.866025403784f\n    \n    /// Edge length of an equilateral triangle with height one. Multiplier to determine simplex\n    /// edge length given a height (sqrt(4/3) = 1.154700538379f).\n    let heightToEdge = 1.154700538379f\n\n    let inline eucToVertexf (v : Vector2) = \n        let y = -heightToEdge * v.Y\n        let x = v.X - 0.5f * y\n        Vector2(x, y)\n        \n    /// Rhombus/vertex to tri cell\n    let inline vertexToTri (p : Vector2i) side =\n        Vector2i(p.X * 2 + side, p.Y)\n\n    /// Gets location of cell in tri coords that contains rhombus point.\n    let vertexToContainingTriCell (p : Vector2) =\n        let bx = floor p.X\n        let by = floor p.Y\n        let fx = p.X - bx\n        let fy = p.Y - by\n        let side = if fy < 1.0f - fx then 0 else 1\n        vertexToTri (Vector2i(int bx, int by)) side\n    \n    let eucToContainingTriCell = \n        eucToVertexf >> vertexToContainingTriCell\n\n    let inline vertexToEucf (v : Vector2) =\n        Vector2(v.X + v.Y * 0.5f, v.Y * -edgeToHeight)\n        \n    let inline vertexToEuc (v : Vector2i) =\n        v.ToVector2() |> vertexToEucf\n\nmodule UndoState =\n    let init value = { \n        Previous = []\n        Next = []\n        Current = value \n        }\n\nmodule Command =\n    let undo state =\n        match state.Previous with\n        | head :: tail -> {\n            Previous = tail\n            Next = state.Current :: state.Next\n            Current = head \n            }\n        | _ -> state\n\n    let redo state =\n        match state.Next with\n        | head :: tail -> {\n            Previous = state.Current :: state.Previous\n            Next = state.Next.Tail\n            Current = state.Next.Head \n            }\n        | _ -> state\n\n    let replace state value = {\n        Previous = state.Current :: state.Previous\n        Next = []\n        Current = value \n        }\n\n    let apply state cmd =\n        match cmd with\n        | Identity -> state\n        | Undo -> undo state\n        | Redo -> redo state\n        | Replace c -> replace state c\n        | Apply f -> replace state (f state.Current)\n\nmodule GridState =\n    let empty = {\n        Cells = Map.empty\n        }\n\n    let draw p color state = {\n        state with Cells = Map.add p color state.Cells\n        }  \n        \n    let erase p state = {\n        state with Cells = Map.remove p state.Cells\n        }  \n\n    type SavedGrid = {\n        cells : string list\n        }\n        \n    let toHexString (c : RgbaByte) =\n       let x = (int c.R <<< 24) ||| (int c.G <<< 16) ||| (int c.B <<< 8) ||| (int c.A <<< 0)\n       $\"%08x{x}\"\n\n    let serialize (state : GridState) =\n        JsonConvert.SerializeObject({\n            cells =\n                state.Cells \n                |> Seq.sortBy (fun kvp -> kvp.Key.Y, kvp.Key.X)\n                |> Seq.map (fun kvp -> $\"%d{kvp.Key.X} %d{kvp.Key.Y} %s{toHexString kvp.Value}\")\n                |> Seq.toList\n            }, Formatting.Indented)\n        \n    /// RGBA from high to low bits\n    let uint32ToRgbaByte (x : uint32) = \n        RgbaByte(\n            byte ((x >>> 24) &&& (uint32 0xff)),\n            byte ((x >>> 16) &&& (uint32 0xff)),\n            byte ((x >>> 8) &&& (uint32 0xff)),\n            byte ((x >>> 0) &&& (uint32 0xff)))\n\n    let parseRgbaHex str =\n        UInt32.Parse(str, Globalization.NumberStyles.HexNumber)\n        |> uint32ToRgbaByte\n\n    let deserialize str =\n        let g = JsonConvert.DeserializeObject<SavedGrid>(str)\n        { \n            GridState.Cells = \n                g.cells \n                |> Seq.map (fun c -> \n                    let parts = c.Split(' ')\n                    { X = Int32.Parse(parts.[0]); Y = Int32.Parse(parts.[1]) }, \n                    parseRgbaHex parts.[2])\n                |> Map.ofSeq\n        }\n\n    let sample param (grid : GridState) =\n        let w = max 0 param.OutputWidth\n        let h = max 0 param.OutputHeight\n        let r = param.Bounds\n        let s = max 1 param.SampleFactor\n        let samplesPerPixel = s * s\n        let data = Array.zeroCreate (w * h * 4)\n        // uniform supersampling of pixels\n        // |.....x.....|.....x.....|\n        // |..x.....x..|..x.....x..|\n        for y = 0 to h - 1 do\n            for x = 0 to w - 1 do\n                let mutable sr = 0\n                let mutable sg = 0\n                let mutable sb = 0\n                let mutable sa = 0\n                for sy = 0 to s - 1 do\n                    for sx = 0 to s - 1 do\n                        let nx = (float32 (x * s + sx) + 0.5f) / float32 (w * s)\n                        let ny = (float32 (y * s + sy) + 0.5f) / float32 (h * s)\n                        let np = Vector2(nx, ny)\n                        let ep = Range2.Lerp(r, np)\n                        let cp = TriCoords.eucToContainingTriCell ep\n                        let color =\n                            let cp = { X = cp.X; Y = cp.Y }\n                            match grid.Cells.TryGetValue(cp) with\n                            | false, _ -> param.Background\n                            | true, x -> x\n                        sr <- sr + int color.R\n                        sg <- sg + int color.G\n                        sb <- sb + int color.B\n                        sa <- sa + int color.A\n                // reverse y for texture\n                let i = (h - 1 - y) * w + x\n                data.[i * 4 + 0] <- sr / samplesPerPixel |> byte\n                data.[i * 4 + 1] <- sg / samplesPerPixel |> byte\n                data.[i * 4 + 2] <- sb / samplesPerPixel |> byte\n                data.[i * 4 + 3] <- sa / samplesPerPixel |> byte\n        data\n\nmodule Viewport =    \n    let getViewSize zoom width height =\n        let tileSize = 24.0f\n        let aspect = float32 width / float32 height\n        let widthInTiles = float32 width / tileSize\n        let heightInTiles = widthInTiles / aspect\n        let scale = MathF.Pow(2.0f, float32 -zoom * 0.5f) |> float32\n        Vector2(widthInTiles, heightInTiles) * scale\n\n    let getInverseOrIdentity (m : Matrix4x4) =\n        let mutable mInv = Matrix4x4.Identity\n        if Matrix4x4.Invert(m, &mInv) then mInv else Matrix4x4.Identity\n\nmodule TriPositions =\n    let inline fromTriCellScaled (tileSize : Vector2) (p : Vector2i) =\n        let y0 = float32 p.Y\n        let y1 = float32 (p.Y + 1)\n        let py0 = Vector2(y0 * 0.5f, y0 * -tileSize.Y)\n        let py1 = Vector2(y1 * 0.5f, y1 * -tileSize.Y)\n        let ax = p.X >>> 1\n        let x0 = float32 ax\n        let x1 = float32 (ax + 1)\n        let p01 = Vector2((py1.X + x0) * tileSize.X, py1.Y)\n        let p10 = Vector2((py0.X + x1) * tileSize.X, py0.Y)\n        let side = p.X &&& 1\n        if side = 0 \n            then { P0 = Vector2((py0.X + x0) * tileSize.X, py0.Y); P1 = p10; P2 = p01 }\n            else { P0 = Vector2((py1.X + x1) * tileSize.X, py1.Y); P1 = p01; P2 = p10 }\n\n    let inline fromTriCell (p : Vector2i) =\n        fromTriCellScaled (Vector2(1.0f, TriCoords.edgeToHeight)) p\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Game.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen System.Threading\nopen System.IO\nopen Veldrid\nopen SixLabors.ImageSharp\nopen Garnet.Numerics\nopen Garnet.Composition\nopen Garnet.Graphics\nopen Garnet.Input\n\nmodule Resources =\n    let squareTex = \"square.png\"\n\n    let shaderSet : ShaderSetDescriptor<PositionTextureColorVertex> = {\n        VertexShader = \"texture-color.vert\"\n        FragmentShader = \"texture-color.frag\"\n        }\n\n    let pipeline = {\n        Blend = Blend.Alpha\n        Filtering = Filtering.Linear\n        ShaderSet = shaderSet\n        Texture = squareTex\n        }\n\n    let cellLayer = {\n        LayerId = 4\n        CameraId = 0\n        Primitive = Triangle\n        FlushMode = NoFlush\n        Pipeline = pipeline \n        }\n\n    let gridLineLayer = {\n        LayerId = 3\n        CameraId = 0\n        Primitive = Quad\n        FlushMode = NoFlush\n        Pipeline = pipeline \n        }\n\n[<AutoOpen>]\nmodule DrawingExtensions =\n    type SpriteRenderer with\n        member c.DrawGrid(state) =\n            let mesh = c.GetVertices(Resources.cellLayer)\n            mesh.DrawGridCells(state.Current)\n            mesh.Flush()        \n\ntype Game(fs : IReadOnlyFolder) =\n    // Create window and graphics device\n    let ren =\n        new WindowRenderer {\n            WindowSettings.Default with\n                Title = \"Trixel\" \n                Width = 800\n                Height = 600\n                Background = RgbaFloat(0.0f, 0.1f, 0.2f, 1.0f)\n            }\n    // Initialize rendering\n    let shaders = new ShaderSetCache()\n    let cache = new ResourceCache()\n    let sprites = new SpriteRenderer(ren.Device, shaders, cache)\n    let gui = new Gui(ren.Device, ren.ImGui)\n    do\n        cache.AddShaderLoaders(ren.Device)\n        fs.LoadShadersFromFolder(\".\", ren.Device.BackendType, cache)\n        fs.LoadTexture(ren.Device, Resources.squareTex, cache)\n    member c.Run() =\n        let mutable state = UndoState.init GridState.empty\n        sprites.DrawGrid(state)\n        // Grid lines\n        let mesh = sprites.GetVertices(Resources.gridLineLayer)\n        mesh.DrawGridLines()\n        mesh.Flush()\n        // Cells\n        let mesh = sprites.GetVertices(Resources.cellLayer)\n        mesh.DrawGridCells(state.Current)\n        mesh.Flush()\n        // Start loop\n        let inputs = InputCollection()\n        let cameras = CameraSet()\n        while ren.Update(0.0f, inputs) do\n            // Calculate transforms\n            let sizeInTiles = Viewport.getViewSize 0 ren.Size.X ren.Size.Y\n            let proj = Matrix4x4.CreateOrthographic(sizeInTiles.X, sizeInTiles.Y, -100.0f, 100.0f)\n            let view = Matrix4x4.Identity\n            let camera = cameras.[0]\n            camera.ProjectionTransform <- proj\n            camera.ViewTransform <-view\n            // Draw GUI and collect any user command\n            let projView = proj * view\n            let invProjView = Viewport.getInverseOrIdentity projView\n            let result = gui.Draw(state, inputs, invProjView)            \n            // Apply command to state or read/write files\n            match result with\n            | None -> ()\n            | Some cmd ->\n                match cmd with\n                | GridCommand cmd ->\n                    // Update state from command\n                    state <- Command.apply state cmd\n                    sprites.DrawGrid(state)\n                | Export cmd ->\n                    let image = Image.createRenderedGridImage cmd.SamplingParams state.Current\n                    use fs = File.OpenWrite(cmd.ExportFile)\n                    image.SaveAsPng(fs)\n                | FileCommand cmd ->\n                    match cmd with\n                    | Load file -> \n                        let cmd =\n                            File.ReadAllText(file) \n                            |> GridState.deserialize\n                            |> Replace\n                        state <- Command.apply state cmd\n                        sprites.DrawGrid(state)\n                    | Save file -> \n                        Directory.CreateDirectory(Path.GetDirectoryName(file)) |> ignore\n                        File.WriteAllText(file, GridState.serialize state.Current)\n            // Draw to window\n            if ren.BeginDraw() then\n                sprites.Draw(ren.RenderContext, cameras)\n                ren.EndDraw()\n            // Sleep to avoid spinning CPU\n            Thread.Sleep(1)\n    interface IDisposable with\n        member c.Dispose() =\n            cache.Dispose()\n            shaders.Dispose()\n            sprites.Dispose()\n            gui.Dispose()\n            ren.Dispose()\n    static member Run(fs) =\n        use game = new Game(fs)\n        game.Run()\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Garnet.Samples.Trixel.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>\n    <PublishTrimmed>true</PublishTrimmed>\n    <TrimMode>Link</TrimMode>\n    <RootNamespace>Garnet.Samples.Trixel</RootNamespace>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"assets\\square.png\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.frag\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.frag.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.vert\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"assets\\texture-color.vert.hlsl.bytes\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <Compile Include=\"Types.fs\" />\n    <Compile Include=\"Functions.fs\" />\n    <Compile Include=\"Imaging.fs\" />\n    <Compile Include=\"Drawing.fs\" />\n    <Compile Include=\"Gui.fs\" />\n    <Compile Include=\"Game.fs\" />\n    <Compile Include=\"Program.fs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Garnet.Toolkit\\Garnet.Toolkit.fsproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.2\" />\n    <PackageReference Include=\"Veldrid.ImGui\" Version=\"5.72.0\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Gui.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System\nopen System.IO\nopen System.Numerics\nopen Veldrid\nopen ImGuiNET\nopen Garnet.Numerics\nopen Garnet.Input\n\ntype CursorGui() =\n    member c.Draw(inputs : InputCollection, invProjView : Matrix4x4) =    \n        let pos = ImGui.GetMousePos()\n        let normPos = inputs.NormalizedMousePosition\n        let viewPos = Vector2.Transform(normPos, invProjView)\n        let cellPos = TriCoords.eucToContainingTriCell viewPos\n        ImGui.Text($\"Position: {pos}\")\n        ImGui.Text($\"Normalized: {normPos}\")\n        ImGui.Text($\"Viewport: {viewPos}\")\n        ImGui.Text($\"Cell: {cellPos}\")\n        ImGui.Text($\"Buttons: {inputs.IsMouseDown(0)} {inputs.IsMouseDown(1)}\")\n            \ntype ViewGui() =\n    let mutable centerX = 35\n    let mutable centerY = -18\n    let mutable cellMargin = 0.1f\n    let mutable zoom = 0\n    member c.Draw(state : UndoState<GridState>) =    \n        let grid = state.Current\n        ImGui.Text($\"Cells: {grid.Cells.Count}\")\n        let inv = false\n        let inv = ImGui.InputInt(\"Zoom\", &zoom) || inv\n        let inv = ImGui.InputInt(\"X\", &centerX) || inv\n        let inv = ImGui.InputInt(\"Y\", &centerY) || inv\n        ()\n        \ntype EditGui() =\n    let mutable primary = Vector4(0.5f, 0.0f, 0.3f, 1.0f)\n    let mutable secondary = Vector4(0.3f, 0.0f, 0.6f, 1.0f)\n    member c.Draw(state : UndoState<GridState>, inputs : InputCollection, invProjView : Matrix4x4) =    \n        if ImGui.Begin(\"Edit\") then\n            ImGui.ColorEdit4(\"Primary\", &primary) |> ignore\n            ImGui.ColorEdit4(\"Secondary\", &secondary) |> ignore\n            let undo = ImGui.Button $\"Undo (%d{state.Previous.Length})\"\n            let redo = ImGui.Button $\"Redo (%d{state.Next.Length})\"\n            let leftButton = inputs.IsMouseDown(0)\n            let rightButton = inputs.IsMouseDown(2)\n            let drawCommand =\n                let canDraw =\n                    not (ImGui.GetIO().WantCaptureMouse) &&\n                    (leftButton || rightButton)\n                if canDraw then\n                    let normPos = inputs.NormalizedMousePosition\n                    let viewPos = Vector2.Transform(normPos, invProjView)\n                    let modifiers = inputs.Modifiers\n                    let p = TriCoords.eucToContainingTriCell viewPos\n                    let cp = { X = p.X; Y = p.Y }\n                    let current = state.Current\n                    let newState =\n                        if leftButton then\n                            let v = if modifiers.HasShift() then secondary else primary\n                            let color = RgbaFloat(v.X, v.Y, v.Z, v.W).ToRgbaByte()\n                            GridState.draw cp color current\n                        else\n                            GridState.erase cp current                        \n                    if newState <> current then Some (Replace newState) else None\n                else None\n            let result =\n                if drawCommand.IsSome then drawCommand\n                elif undo then Some Undo\n                elif redo then Some Redo\n                else None\n            ImGui.End()\n            result\n        else None\n\ntype PreviewGui(device : GraphicsDevice, renderer : ImGuiRenderer) =\n    let previewTex = new PreviewTexture(device, renderer)\n    let mutable zoom = 8\n    let mutable width = 35\n    let mutable height = 30\n    let mutable centerX = 0\n    let mutable centerY = 0\n    let mutable multisample = 4\n    let mutable viewSize = 30\n    let mutable resolution = 1\n    let mutable file = \"preview.png\"\n    member c.Draw(state : GridState) =\n        if ImGui.Begin(\"Preview\") then\n            ImGui.InputInt(\"Zoom\", &zoom) |> ignore\n            ImGui.InputInt(\"View size\", &viewSize) |> ignore\n            ImGui.InputInt(\"Width\", &width) |> ignore\n            ImGui.InputInt(\"Height\", &height) |> ignore\n            ImGui.InputInt(\"Multiplier\", &resolution) |> ignore\n            ImGui.InputInt(\"X\", &centerX) |> ignore\n            ImGui.InputInt(\"Y\", &centerY) |> ignore\n            ImGui.InputInt(\"Multisample\", &multisample) |> ignore\n            ImGui.InputText(\"File\", &file, 128u) |> ignore\n            let export = ImGui.Button(\"Export\")\n            let viewWidth = float32 viewSize\n            let viewHeight = viewWidth * TriCoords.edgeToHeight\n            let center = TriCoords.vertexToEuc (Vector2i(centerX, centerY))\n            let param = {\n                OutputWidth = width * resolution\n                OutputHeight = height * resolution\n                SampleFactor = multisample\n                Bounds = Range2.Sized(center, Vector2(viewWidth, viewHeight))\n                Background = RgbaByte.Black\n                }\n            previewTex.Draw(param, state, zoom, resolution)\n            ImGui.End()\n            if export then Some {\n                ExportFile = file\n                SamplingParams = param\n                }\n            else None\n        else None\n    member c.Dispose() =\n        previewTex.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\ntype FileGui() =\n    let filter = \"*.json\"\n    let mutable dir = @\"trixel-grids\"\n    let mutable file = \"\"\n    let mutable files = [||]\n    let mutable fileIndex = 0\n    let mutable filesValid = false\n    member c.Draw() =\n        if ImGui.Begin(\"File\") then\n            if ImGui.Button(\"Refresh\") then filesValid <- false\n            if ImGui.InputText(\"Folder\", &dir, 128u) || not filesValid then\n                files <- \n                    if Directory.Exists(dir) \n                        then Directory.GetFiles(dir, filter) |> Array.map Path.GetFileName\n                        else [||]\n                fileIndex <- 0\n                filesValid <- true\n            if ImGui.ListBox(\"\", &fileIndex, files, files.Length) then\n                file <- if fileIndex < files.Length then files.[fileIndex] else \"\"\n            let load =\n                if ImGui.Button(\"Load\") && file.Length > 0 then\n                    let path = Path.Combine(dir, file)\n                    Some (Load path)\n                else None\n            let save = \n                ImGui.InputText(\"File\", &file, 128u) |> ignore\n                if ImGui.Button(\"Save\") && file.Length > 0 then\n                    let path = Path.Combine(dir, file)\n                    Some (Save path)\n                else None\n            let result = if load.IsSome then load else save\n            ImGui.End()\n            result\n        else None\n        \ntype StatusGui() =\n    let cursorGui = CursorGui()\n    let viewGui = ViewGui()\n    member c.Draw(state, inputs, invProjView) =\n        if ImGui.Begin(\"Status\") then\n            cursorGui.Draw(inputs, invProjView)\n            viewGui.Draw(state)\n            ImGui.End()\n        \n    \ntype Gui(device : GraphicsDevice, renderer : ImGuiRenderer) =\n    let fileGui = FileGui()\n    let editGui = EditGui()\n    let previewGui = new PreviewGui(device, renderer)\n    let statusGui = StatusGui()\n    member c.Draw(state, inputs, invProjView) =\n        // draw GUI\n        statusGui.Draw(state, inputs, invProjView)\n        let editCommand = editGui.Draw(state, inputs, invProjView)\n        let fileCommand = fileGui.Draw()\n        let previewCommand = previewGui.Draw(state.Current)\n        // resolve command\n        if editCommand.IsSome then Some (GridCommand editCommand.Value)\n        elif previewCommand.IsSome then Some (Export previewCommand.Value)\n        elif fileCommand.IsSome then Some (FileCommand fileCommand.Value)\n        else None\n    member c.Dispose() =\n        previewGui.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Imaging.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System\nopen System.Numerics\nopen SixLabors.ImageSharp\nopen Veldrid\nopen ImGuiNET\nopen Garnet.Graphics\n\nmodule Image =\n    let createRenderedGridImage param state =\n        let data = GridState.sample param state\n        let rgbaData = Array.zeroCreate (data.Length / 4)\n        for i = 0 to rgbaData.Length - 1 do\n            rgbaData.[i] <- \n                PixelFormats.Rgba32(\n                    data.[i * 4],\n                    data.[i * 4 + 1],\n                    data.[i * 4 + 2],\n                    data.[i * 4 + 3])\n        Image.WrapMemory(Memory(rgbaData), param.OutputWidth, param.OutputHeight)\n        \ntype PreviewTexture(device : GraphicsDevice, renderer : ImGuiRenderer) =\n    let mutable texture : Texture = null\n    let mutable lastState = GridState.empty\n    let mutable lastParam = Unchecked.defaultof<SamplingParams>\n    member c.Draw(param, state, zoom, resolution) =\n        let canUpdate =\n            lastParam <> param ||\n            lastState <> state ||\n            texture = null\n        if canUpdate then\n            if texture <> null\n                then texture.Dispose()\n            let data = GridState.sample param state\n            texture <- device.CreateTextureRgba(param.OutputWidth, param.OutputHeight, ReadOnlyMemory(data))        \n        if texture <> null then\n            let texId = renderer.GetOrCreateImGuiBinding(device.ResourceFactory, texture)\n            let width = float32 (int texture.Width * zoom / resolution |> max 1)\n            let height = float32 (int texture.Height * zoom / resolution |> max 1)\n            ImGui.Image(texId, Vector2(width, height))\n    member c.Dispose() =\n        if texture <> null then\n            texture.Dispose()\n            texture <- null\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n        "
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Program.fs",
    "content": "open Garnet.Composition\nopen Garnet.Samples.Trixel\n\n[<EntryPoint>]\nlet main argv =\n    use fs = new FileFolder(\"assets\")\n    Game.Run(fs)\n    0"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/Types.fs",
    "content": "﻿namespace Garnet.Samples.Trixel\n\nopen System.Numerics\nopen Garnet.Numerics\nopen Veldrid\n\n[<AutoOpen>]\nmodule Grids =\n    [<Struct>]\n    type CellLocation = {\n        X : int\n        Y : int\n        }\n\n    type GridState = {\n        Cells : Map<CellLocation, RgbaByte>\n        }\n\n    type GridStateChanged = {\n        NewGridState : GridState\n        }\n\n    [<Struct>]\n    type SamplingParams = {\n        OutputWidth : int\n        OutputHeight : int\n        SampleFactor : int\n        Bounds : Range2\n        Background : RgbaByte\n        }\n\n    type GridLineState = {\n        LineSpacing : int\n        }\n\n    type ViewportSize = {\n        ViewportSize : int\n        }\n\n    [<Struct>]\n    type TriPositions = {\n        P0 : Vector2\n        P1 : Vector2\n        P2 : Vector2\n        }\n\n[<AutoOpen>]\nmodule Commands =\n    type Command<'a> =\n        | Identity\n        | Undo\n        | Redo\n        | Replace of 'a\n        | Apply of ('a -> 'a)\n\n    type UndoState<'a> = {\n        Previous : 'a list\n        Next : 'a list\n        Current : 'a \n        }\n\n    type FileCommand =\n        | Load of string\n        | Save of string\n        \n    type ExportCommand = {\n        ExportFile : string\n        SamplingParams : SamplingParams\n        }\n\n    type Command =\n        | Export of ExportCommand\n        | FileCommand of FileCommand\n        | GridCommand of Command<GridState>"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.frag",
    "content": "#version 450\nlayout(location = 0) in vec2 fsin_texCoords;\nlayout(location = 1) in vec4 fsin_color;\nlayout(location = 0) out vec4 fsout_color;\nlayout(set = 1, binding = 1) uniform texture2D SurfaceTexture;\nlayout(set = 1, binding = 2) uniform sampler SurfaceSampler;\nvoid main()\n{\n    vec4 texColor = texture(sampler2D(SurfaceTexture, SurfaceSampler), fsin_texCoords);\n    fsout_color =  texColor * fsin_color;\n}"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.frag.hlsl.bytes",
    "content": "Texture2D<float4> _12 : register(t0);\nSamplerState _16 : register(s0);\n\nstatic float2 _22;\nstatic float4 _26;\nstatic float4 _29;\n\nstruct SPIRV_Cross_Input\n{\n    float2 _22 : TEXCOORD0;\n    float4 _29 : TEXCOORD1;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float4 _26 : SV_Target0;\n};\n\nvoid frag_main()\n{\n    _26 = _12.Sample(_16, _22) * _29;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _22 = stage_input._22;\n    _29 = stage_input._29;\n    frag_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output._26 = _26;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.vert",
    "content": "#version 450\nlayout(set = 0, binding = 0) uniform ProjectionBuffer\n{\n    mat4 Projection;\n};\nlayout(set = 0, binding = 1) uniform ViewBuffer\n{\n    mat4 View;\n};\nlayout(set = 1, binding = 0) uniform WorldBuffer\n{\n    mat4 World;\n};\nlayout(location = 0) in vec3 Position;\nlayout(location = 1) in vec2 TexCoords;\nlayout(location = 2) in vec4 Color;\nlayout(location = 0) out vec2 fsin_texCoords;\nlayout(location = 1) out vec4 fsin_Color;\nvoid main()\n{\n    vec4 worldPosition = World * vec4(Position, 1);\n    vec4 viewPosition = View * worldPosition;\n    vec4 clipPosition = Projection * viewPosition;\n    gl_Position = clipPosition;\n    fsin_texCoords = TexCoords;\n    fsin_Color = Color;\n}\n"
  },
  {
    "path": "samples/Garnet.Samples.Trixel/assets/texture-color.vert.hlsl.bytes",
    "content": "cbuffer _11_13 : register(b2)\n{\n    row_major float4x4 _13_m0 : packoffset(c0);\n};\n\ncbuffer _30_32 : register(b1)\n{\n    row_major float4x4 _32_m0 : packoffset(c0);\n};\n\ncbuffer _38_40 : register(b0)\n{\n    row_major float4x4 _40_m0 : packoffset(c0);\n};\n\n\nstatic float4 gl_Position;\nstatic float3 _21;\nstatic float2 _56;\nstatic float2 _58;\nstatic float4 _60;\nstatic float4 _62;\n\nstruct SPIRV_Cross_Input\n{\n    float3 _21 : TEXCOORD0;\n    float2 _58 : TEXCOORD1;\n    float4 _62 : TEXCOORD2;\n};\n\nstruct SPIRV_Cross_Output\n{\n    float2 _56 : TEXCOORD0;\n    float4 _60 : TEXCOORD1;\n    float4 gl_Position : SV_Position;\n};\n\nvoid vert_main()\n{\n    gl_Position = mul(mul(mul(float4(_21, 1.0f), _13_m0), _32_m0), _40_m0);\n    _56 = _58;\n    _60 = _62;\n}\n\nSPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)\n{\n    _21 = stage_input._21;\n    _58 = stage_input._58;\n    _62 = stage_input._62;\n    vert_main();\n    SPIRV_Cross_Output stage_output;\n    stage_output.gl_Position = gl_Position;\n    stage_output._56 = _56;\n    stage_output._60 = _60;\n    return stage_output;\n}\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Audio.fs",
    "content": "﻿namespace Garnet.Audio\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Numerics\nopen System.IO\nopen System.Runtime.InteropServices\nopen OpenTK.Audio.OpenAL\nopen OpenTK.Audio.OpenAL.Extensions\nopen Garnet.Composition\nopen Garnet.Collections\n\n[<Struct>]\ntype SoundId = SoundId of int\n\n[<Struct>]\ntype SoundDescriptor = {\n    Channels : int\n    BitsPerSample : int\n    SampleRate : int\n    SampleCount : int\n    }\n\n[<Struct>]\ntype SoundPlayback = {\n    LoopCount : int\n    Gain : float32\n    Pitch : float32\n    Position : Vector3\n    Relative : bool\n    } with\n    static member Default = {\n        LoopCount = 1\n        Gain = 1.0f\n        Pitch = 1.0f\n        Position = Vector3.Zero\n        Relative = false\n        } \n\nmodule SoundDescriptor =\n    let getALFormat desc =\n        if desc.Channels = 1 && desc.BitsPerSample = 8 then ALFormat.Mono8\n        elif desc.Channels = 1 && desc.BitsPerSample = 16 then ALFormat.Mono16\n        elif desc.Channels = 2 && desc.BitsPerSample = 8 then ALFormat.Stereo8\n        elif desc.Channels = 2 && desc.BitsPerSample = 16 then ALFormat.Stereo16\n        else failwith $\"Unsupported audio format, {desc.Channels} channels, {desc.BitsPerSample} bits\"\n\n    let getDuration desc =\n        (desc.SampleCount * 1000 + 999) / desc.Channels / desc.SampleRate\n\n[<AutoOpen>]\nmodule internal OpenALInternal =\n    module SoundId =\n        let toInt id = match id with SoundId x -> x\n\n    type AL =\n        static member GetAvailableDeviceNames() = [|\n            yield! ALC.GetStringList(GetEnumerationStringList.DeviceSpecifier)\n            yield! Creative.EnumerateAll.EnumerateAll.GetStringList(Creative.EnumerateAll.GetEnumerateAllContextStringList.AllDevicesSpecifier)\n            |]\n\n        static member ThrowIfError(str) =\n            let error = AL.GetError()\n            if int error <> int ALError.NoError then\n                failwith $\"OpenAL error on {str}: {AL.GetErrorString(error)}\"\n\n    type internal SourcePool() =\n        let maxSources = 32\n        let sources = AL.GenSources(maxSources)\n        let pool = \n            let stack = Stack<int>()\n            for id in sources do \n                stack.Push(id)\n            stack\n        member c.TryGetSource() =\n            if pool.Count > 0 then ValueSome (pool.Pop())\n            else ValueNone\n        member c.RecycleSource(id) =\n            pool.Push(id)\n        member c.Dispose() =\n            AL.DeleteSources(sources)\n        interface IDisposable with\n            member c.Dispose() =\n                c.Dispose()\n\n    [<Struct>]\n    type Sound = {\n        descriptor : SoundDescriptor\n        buffer : int\n        }\n\ntype AudioDevice() =\n    let devices = AL.GetAvailableDeviceNames()\n    let device = ALC.OpenDevice(null)\n    let context = \n        let c = ALC.CreateContext(device, Array.empty)\n        let _ = ALC.MakeContextCurrent(c)\n        c\n    let sources = new SourcePool()\n    let sounds = List<Sound>()\n    let activeSources = PriorityQueue<int64, int>()\n    let mutable scale = 1.0f\n    let mutable time = 0L\n    member c.CreateSound(desc, data : ReadOnlyMemory<byte>) =\n        let buffers = AL.GenBuffers(1)\n        use handle = data.Pin()\n        let format = SoundDescriptor.getALFormat desc\n        AL.BufferData(buffers.[0], format, IntPtr handle.Pointer, desc.SampleCount, desc.SampleRate)\n        AL.ThrowIfError(\"loading audio data\")\n        let sound = {\n            descriptor = desc\n            buffer = buffers.[0]\n            }\n        let soundId = sounds.Count\n        sounds.Add(sound)\n        SoundId soundId\n    member c.StopSounds() =\n        while activeSources.Count > 0 do\n            let sourceId = activeSources.Dequeue()\n            AL.SourceStop(sourceId)\n            sources.RecycleSource(sourceId)            \n    member c.Update(currentTime) =\n        time <- currentTime\n        while activeSources.Count > 0 && currentTime >= activeSources.Top.Key do\n            let sourceId = activeSources.Dequeue()\n            AL.SourceStop(sourceId)\n            sources.RecycleSource(sourceId)\n    member c.PlaySound(soundId, playback : SoundPlayback) =\n        match sources.TryGetSource() with\n        | ValueNone -> ()\n        | ValueSome sourceId ->\n            let soundId = SoundId.toInt soundId\n            let sound = sounds.[soundId]\n            let p = playback.Position * scale\n            AL.Source(sourceId, ALSourcei.Buffer, sound.buffer)\n            AL.Source(sourceId, ALSourceb.Looping, playback.LoopCount > 1)\n            AL.Source(sourceId, ALSourcef.Pitch, playback.Pitch)\n            AL.Source(sourceId, ALSourcef.Gain, playback.Gain)\n            AL.Source(sourceId, ALSourceb.SourceRelative, playback.Relative)\n            AL.Source(sourceId, ALSource3f.Position, p.X, p.Y, p.Z)\n            AL.SourcePlay(sourceId)\n            let duration = SoundDescriptor.getDuration sound.descriptor * playback.LoopCount\n            activeSources.Enqueue(time + int64 duration, sourceId)\n    member c.SetPosition(pos : Vector3) =\n        let mutable v = OpenTK.Mathematics.Vector3(pos.X, pos.Y, pos.Z) * scale\n        AL.Listener(ALListener3f.Position, &v)\n    member c.SetGain(gain) =\n        AL.Listener(ALListenerf.Gain, gain)\n    member c.SetScaling(newScale) =\n        scale <- newScale\n    member c.Dispose() =\n        c.StopSounds()\n        sources.Dispose()\n        for sound in sounds do\n            AL.DeleteBuffers([| sound.buffer |])            \n        ALC.MakeContextCurrent(ALContext.Null) |> ignore\n        ALC.DestroyContext(context)\n        ALC.CloseDevice(device) |> ignore\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n    override c.ToString() =\n        let version = AL.Get(ALGetString.Version)\n        let vendor = AL.Get(ALGetString.Vendor)\n        let renderer = AL.Get(ALGetString.Renderer)\n        sprintf \"Audio devices (%d):\\n  %s\\nSelected device:\\n  %s\\n  %s\\n  %s\" \n            devices.Length (String.Join(\"\\n  \", devices)) \n            renderer version vendor\n\ntype AudioDevice with\n    member c.CreateSoundFromSineWave() =\n        let sampleRate = 44100\n        let dt = 2.0 * Math.PI / float sampleRate\n        let amp = 0.5\n        let freq = 440.0\n        let sampleCount = float sampleRate / freq\n        let data = Array.zeroCreate<byte> (int sampleCount * 2)\n        let dest = MemoryMarshal.Cast<byte, int16>(data.AsSpan())\n        for i = 0 to dest.Length - 1 do\n            dest.[i] <- int16 (amp * float Int16.MaxValue * sin (float i * dt * freq))\n        let desc = {\n            Channels = 1\n            BitsPerSample = 16\n            SampleRate = sampleRate\n            SampleCount = data.Length * 8 / 16\n            }\n        c.CreateSound(desc, ReadOnlyMemory(data))\n\n[<AutoOpen>]\nmodule AudioLoadingExtensions =\n    type IStreamSource with\n        member c.LoadWave(device : AudioDevice, key) =\n            use stream = c.Open(key)\n            // https://stackoverflow.com/questions/8754111/how-to-read-the-data-in-a-wav-file-to-an-array\n            use reader = new BinaryReader(stream)\n            // chunk 0\n            let _           = reader.ReadInt32() // chunkId\n            let _           = reader.ReadInt32() // fileSize\n            let _           = reader.ReadInt32() // riffType\n            // chunk 1\n            let _           = reader.ReadInt32() // fmtID\n            let fmtSize     = reader.ReadInt32() // bytes for this chunk (expect 16 or 18)\n            // 16 bytes coming\n            let _           = int (reader.ReadInt16()) // fmtCode\n            let channels    = int (reader.ReadInt16())\n            let sampleRate  = reader.ReadInt32()\n            let _           = reader.ReadInt32() // byteRate\n            let _           = int (reader.ReadInt16()) // fmtBlockAlign\n            let bitDepth    = int (reader.ReadInt16())\n            if fmtSize = 18 then\n                // Read any extra values\n                let fmtExtraSize = int (reader.ReadInt16())\n                stream.Seek(int64 fmtExtraSize, SeekOrigin.Current) |> ignore\n            // Skip to data chunk\n            let dataTag = 0x61_74_61_64 // 'data'\n            let mutable tag = reader.ReadInt32()\n            let mutable length = reader.ReadInt32()\n            while tag <> dataTag do\n                // Read instead of seeking since zip stream doesn't support seek\n                let buffer = ArrayPool.Shared.Rent(length)\n                reader.BaseStream.Read(buffer, 0, length) |> ignore\n                ArrayPool.Shared.Return(buffer)\n                // Read next header\n                tag <- reader.ReadInt32()\n                length <- reader.ReadInt32()\n            // Read data\n            // https://stackoverflow.com/questions/10996917/openal-albufferdata-returns-al-invalid-value-even-though-input-variables-look\n            let adjustedLength = (length + 3) / 4 * 4\n            let data = ArrayPool.Shared.Rent(adjustedLength)\n            stream.Read(data, 0, length) |> ignore\n            // Create descriptor\n            let bytesPerSample = bitDepth / 8\n            let sampleCount = adjustedLength / bytesPerSample\n            let desc = {\n                Channels = channels\n                BitsPerSample = bitDepth\n                SampleRate = sampleRate\n                SampleCount = sampleCount\n                }\n            try\n                let sound = device.CreateSound(desc, ReadOnlyMemory(data, 0, adjustedLength))\n                ArrayPool.Shared.Return(data)\n                sound\n            with ex ->\n                raise (Exception($\"Could not load WAV file '{key}'\", ex))\n\n    type IReadOnlyFolder with\n        member c.LoadAudioFromFolder(path, device, cache : IResourceCache) =\n            for file in c.GetFiles(path, \"*.wav\") do\n                let soundId = c.LoadWave(device, file)\n                cache.AddResource(file, soundId)\n\ntype WaveFileLoader(device : AudioDevice) =\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            cache.AddResource<SoundId>(key, folder.LoadWave(device, key))\n\n[<AutoOpen>]\nmodule AudioLoaderExtensions =\n    type ResourceCache with\n        member c.AddAudioLoaders(device) =\n            c.AddLoader(\".wav\", WaveFileLoader(device))\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Collections.fs",
    "content": "﻿namespace Garnet.Collections\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Comparisons\n\n/// Mutable min-heap\ntype Heap<'k, 'a when 'k :> IComparable<'k>>() =\n    // create a dummy value for easier indexing\n    let items = List<KeyValuePair<'k, 'a>>()\n    do items.Add(Unchecked.defaultof<_>)\n    let compare a b = \n        items.[a].Key.CompareTo(items.[b].Key)\n    let swap a b =\n        let temp = items.[b]\n        items.[b] <- items.[a]\n        items.[a] <- temp\n    let getMinChildIndex parentIndex =\n        let ci = parentIndex * 2\n        if ci >= items.Count then -1\n        else\n            // if we have a second child that's smaller, pick it\n            // we know that if second exists, first exists due to shape\n            let offset =\n                if ci + 1 < items.Count && \n                    compare (ci + 1) ci < 0\n                    then 1 else 0\n            ci + offset\n    let rec siftDown index =\n        // start at top and swap down through min child\n        let ci = getMinChildIndex index\n        if ci >= 0 && compare index ci > 0 then\n            swap index ci\n            siftDown ci\n    let rec siftUp index =\n        // start at end and swap up through parent\n        // maintain parent/child invariant at each iteration\n        if index > 1 && compare index (index / 2) < 0 then\n            swap index (index / 2)\n            siftUp (index / 2)\n    member h.Count = items.Count - 1\n    member h.Top = items.[1]\n    member h.Insert(key, value) =\n        items.Add(KeyValuePair(key, value))\n        siftUp (items.Count - 1)\n    member h.RemoveMin() =\n        if h.Count = 0 then failwith \"Heap is empty\"\n        let top = h.Top\n        items.[1] <- items.[items.Count - 1]\n        items.RemoveAt(items.Count - 1)\n        siftDown 1\n        top\n    member h.Clear() =\n        while items.Count > 1 do items.RemoveAt(items.Count - 1)\n\n/// Mutable, min queue (min priority value dequeued first)\ntype PriorityQueue<'k, 'a when 'k :> IComparable<'k>>() =\n    let heap = Heap<'k, 'a>()\n    member _.Count = heap.Count\n    member _.Top = heap.Top\n    member _.Enqueue(priority, value) =\n        heap.Insert(priority, value)\n    member _.Dequeue() =\n        heap.RemoveMin().Value\n    member _.Clear() = \n        heap.Clear()\n        \nmodule internal Buffer =\n    let private log2 x =\n        let mutable log = 0\n        let mutable y = x\n        while y > 1 do\n            y <- y >>> 1\n            log <- log + 1;\n        log\n\n    let private nextLog2 x =\n        let log = log2 x\n        if x - (1 <<< log) > 0 then 1 + log else log\n\n    let getRequiredCount count =\n        1 <<< nextLog2 count\n\n    let resizeArray count (arr : byref<_[]>) =\n        if isNull arr || count > arr.Length then\n            let required = 1 <<< nextLog2 count\n            let newArr = Array.zeroCreate required\n            if isNotNull arr then\n                arr.CopyTo(newArr, 0)\n            arr <- newArr\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Colors.fs",
    "content": "﻿namespace Garnet.Numerics\n\nopen System\nopen Veldrid\n\nmodule private ColorParsing =\n    let parse defaultValue (parts : string[]) index =\n        if index >= parts.Length then defaultValue\n        else \n            match Single.TryParse(parts.[index]) with\n            | true, x -> x\n            | false, _ -> defaultValue\n\n[<Struct>]\ntype HsvaFloat =\n    val H : float32\n    val S : float32\n    val V : float32\n    val A : float32\n    new(h, s, v, a) = { H = h; S = s; V = v; A = a; }\n    new(c : RgbaFloat) =\n        let m = min (min c.R c.G) c.B\n        let v = max (max c.R c.G) c.B\n        let s = if v > Single.Epsilon then 1.0f - m / v else 0.0f\n        let l = (m + v) / 2.0f\n        if l < Single.Epsilon || v < m \n        then HsvaFloat(0.0f, s, v, c.A)\n        else\n            let vm = v - m\n            let r2 = (v - c.R) / vm\n            let g2 = (v - c.G) / vm\n            let b2 = (v - c.B) / vm\n            let hx = \n                if c.R = v then if c.G = m then 5.0f + b2 else 1.0f - g2\n                else if c.G = v then if c.B = m then 1.0f + r2 else 3.0f - b2\n                else if c.R = m then 3.0f + g2 else 5.0f - r2\n                / 6.0f\n            let h = if hx >= 1.0f then hx - 1.0f else hx\n            HsvaFloat(h, s, v, c.A)\n    member c.ShiftHue(shift) =\n        HsvaFloat(c.H + shift, c.S, c.V, c.A)\n    member c.ToRgbaFloat() =\n        // from: http://alvyray.com/Papers/CG/hsv2rgb.htm\n        // H is given on [0, 6] or UNDEFINED. S and V are given on [0, 1].  \n        // RGB are each returned on [0, 1].\n        let f = c.H - floor c.H\n        let h = f * 6.0f\n        let v = c.V\n        let i = int32(floor h)\n        // if i is even\n        let g = if (i &&& 1) = 0 then 1.0f - f else f\n        let m = v * (1.0f - c.S)\n        let n = v * (1.0f - c.S * g)\n        match i with\n        | 6 -> RgbaFloat(v, n, m, c.A)\n        | 0 -> RgbaFloat(v, n, m, c.A)\n        | 1 -> RgbaFloat(n, v, m, c.A)\n        | 2 -> RgbaFloat(m, v, n, c.A)\n        | 3 -> RgbaFloat(m, n, v, c.A)\n        | 4 -> RgbaFloat(n, m, v, c.A)\n        | _ -> RgbaFloat(v, m, n, c.A)\n    static member Lerp(min : HsvaFloat, max : HsvaFloat, t) =\n        HsvaFloat(\n            MathF.Lerp(min.H, max.H, t),\n            MathF.Lerp(min.S, max.S, t),\n            MathF.Lerp(min.V, max.V, t),\n            MathF.Lerp(min.A, max.A, t))\n    static member Parse(str : string) =\n        // hsva(120,100%,50%,0.3)\n        let parts = str.Replace(\" \", \"\").Replace(\"%\", \"\").Split([|','; '('; ')'|], StringSplitOptions.RemoveEmptyEntries)\n        let h = ColorParsing.parse 0.0f parts 1 / 360.0f\n        let s = ColorParsing.parse 0.0f parts 2 / 100.0f\n        let v = ColorParsing.parse 0.0f parts 3 / 100.0f\n        let a = ColorParsing.parse 1.0f parts 4\n        HsvaFloat(h, s, v, a)\n\n[<Struct>]\ntype HslaFloat =\n    val H : float32\n    val S : float32\n    val L : float32\n    val A : float32\n    new(h, s, l, a) = { H = h; S = s; L = l; A = a; }\n    new(c : RgbaFloat) =\n        let c0 = min (min c.R c.G) c.B\n        let c1 = max (max c.R c.G) c.B\n        let l = (c0 + c1) / 2.0f\n        if c0 = c1 then HslaFloat(0.0f, 0.0f, l, c.A)\n        else\n            let delta = c1 - c0\n            let s =\n                if l <= 0.5f\n                    then (delta / (c1 + c0)) \n                    else (delta / (2.0f - (c1 + c0)))\n            let h =\n                if c.R = c1 then (c.G - c.B) / delta\n                else if c.G = c1 then 2.0f + (c.B - c.R) / delta\n                else if c.B = c1 then 4.0f + (c.R - c.G) / delta\n                else 0.0f\n            let x = h / 6.0f\n            HslaFloat(x - floor x, s, l, c.A)\n    member c.ShiftHue(shift) =\n        HsvaFloat(c.H + shift, c.S, c.L, c.A)\n    member c.ToRgbaFloat() =\n        // Note: there is a typo in the 2nd International Edition of Foley and\n        // van Dam's \"Computer Graphics: Principles and Practice\", section 13.3.5\n        // (The HLS Color Model). This incorrectly replaces the 1f in the following\n        // line with \"l\", giving confusing results.\n        if c.S = 0.0f then RgbaFloat(c.L, c.L, c.L, c.A)\n        else\n            let m2 = \n                if c.L <= 0.5f \n                then c.L * (1.0f + c.S)\n                else c.L + c.S - c.L * c.S\n            let m1 = 2.0f * c.L - m2\n            RgbaFloat(\n                HslaFloat.GetChannelValue(m1, m2, (c.H + 1.0f / 3.0f)),\n                HslaFloat.GetChannelValue(m1, m2, c.H),\n                HslaFloat.GetChannelValue(m1, m2, (c.H - 1.0f / 3.0f)),\n                c.A)\n    static member Lerp(min : HslaFloat, max : HslaFloat, t) =\n        HslaFloat(\n            MathF.Lerp(min.H, max.H, t),\n            MathF.Lerp(min.S, max.S, t),\n            MathF.Lerp(min.L, max.L, t),\n            MathF.Lerp(min.A, max.A, t))\n    static member private GetChannelValue(n1, n2, t) =\n        let hue =\n            if t < 0.0f then t + 1.0f\n            else if t > 1.0f then t - 1.0f\n            else t\n        if hue < 1.0f / 6.0f then n1 + (n2 - n1) * hue * 6.0f\n        else if hue < 0.5f then n2\n        else if (hue < 2.0f / 3.0f) then n1 + (n2 - n1) * (2.0f / 3.0f - hue) * 6.0f\n        else n1\n    static member Parse(str : string) =\n        // hsla(120,100%,50%,0.3)\n        let parts = str.Replace(\" \", \"\").Replace(\"%\", \"\").Split([|','; '('; ')'|], StringSplitOptions.RemoveEmptyEntries)\n        let h = ColorParsing.parse 0.0f parts 1 / 360.0f\n        let s = ColorParsing.parse 0.0f parts 2 / 100.0f\n        let l = ColorParsing.parse 0.0f parts 3 / 100.0f\n        let a = ColorParsing.parse 1.0f parts 4\n        HslaFloat(h, s, l, a)\n\n[<AutoOpen>]\nmodule RgbaByte =\n    type RgbaByte with\n        member c.ToRgbaFloat() =\n            RgbaFloat(\n                float32 c.R / 255.0f,\n                float32 c.G / 255.0f,\n                float32 c.B / 255.0f,\n                float32 c.A / 255.0f)\n            \n        member c.ToUInt32() =\n            (int(c.R) <<< 24) |||\n            (int(c.G) <<< 16) |||\n            (int(c.B) <<< 8) |||\n            (int(c.A) <<< 0)\n\n        static member FromUInt32(x : uint) =\n            RgbaByte(\n                byte ((x >>> 24) &&& (uint32 0xff)),\n                byte ((x >>> 16) &&& (uint32 0xff)),\n                byte ((x >>> 8) &&& (uint32 0xff)),\n                byte ((x >>> 0) &&& (uint32 0xff)))\n\n[<AutoOpen>]\nmodule RgbaFloat =\n    type RgbaFloat with\n        member c.Add(b : RgbaFloat) =\n            RgbaFloat(c.R + b.R, c.G + b.G, c.B + b.B, c.A + b.A)\n\n        member c.Multiply(b : RgbaFloat) =\n            RgbaFloat(c.R * b.R, c.G * b.G, c.B * b.B, c.A * b.A)\n\n        member c.Multiply(x) =\n            RgbaFloat(c.R * x, c.G * x, c.B * x, c.A * x)\n\n        member c.MultiplyRgb(x) =\n            RgbaFloat(c.R * x, c.G * x, c.B * x, c.A)\n\n        member c.MultiplyAlpha(a) =\n            RgbaFloat(c.R, c.G, c.B, c.A * a)\n            \n        member c.WithAlpha(a) =\n            RgbaFloat(c.R, c.G, c.B, a)\n            \n        member c.Clamp() =\n            RgbaFloat(\n                MathF.Clamp01(c.R),\n                MathF.Clamp01(c.G),\n                MathF.Clamp01(c.B),\n                MathF.Clamp01(c.A))\n\n        member c.ToRgbaByte() =\n            RgbaByte(\n                byte (c.R * 255.0f |> max 0.0f |> min 255.0f),\n                byte (c.G * 255.0f |> max 0.0f |> min 255.0f),\n                byte (c.B * 255.0f |> max 0.0f |> min 255.0f),\n                byte (c.A * 255.0f |> max 0.0f |> min 255.0f))\n            \n        static member Lerp(min : RgbaFloat, max : RgbaFloat, t) =\n            RgbaFloat(\n                MathF.Lerp(min.R, max.R, t),\n                MathF.Lerp(min.G, max.G, t),\n                MathF.Lerp(min.B, max.B, t),\n                MathF.Lerp(min.A, max.A, t))\n        \n        static member Luminance(x, a) =\n            RgbaFloat(x, x, x, a)\n            \n        static member FromUInt32(x : uint) =\n            RgbaByte.FromUInt32(x).ToRgbaFloat()\n            \n        static member Parse(str : string) =\n            if str.StartsWith(\"hsla\") then HslaFloat.Parse(str).ToRgbaFloat()\n            elif str.StartsWith(\"hsva\") then HsvaFloat.Parse(str).ToRgbaFloat()\n            else\n                // #rrggbbaa\n                match UInt32.TryParse(str.TrimStart([|'#'|]), Globalization.NumberStyles.HexNumber, null) with\n                | true, x -> RgbaFloat.FromUInt32(x)\n                | false, _ -> RgbaFloat.Clear\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Comparisons.fs",
    "content": "﻿namespace Garnet\n\n// The purpose of this is to avoid equality operator allocations for value types.\n// Be careful if using this with floating point values.\n// https://github.com/dotnet/fsharp/issues/526\n// https://zeckul.wordpress.com/2015/07/09/how-to-avoid-boxing-value-types-in-f-equality-comparisons/\n#nowarn \"86\"\nmodule Comparisons =\n    let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y    \n    let inline (=) x y = eq x y\n    let inline (<>) x y = not (eq x y)\n     \n    let inline (=@) x y = Microsoft.FSharp.Core.Operators.(=) x y\n    let inline (<>@) x y = Microsoft.FSharp.Core.Operators.(<>) x y\n\n    let inline lt<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) < 0\n    let inline gt<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) > 0\n    let inline lte<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) <= 0\n    let inline gte<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) >= 0\n    let inline (<) x y = lt x y\n    let inline (>) x y = gt x y\n    let inline (<=) x y = lte x y\n    let inline (>=) x y = gte x y\n\n    let inline isNull x = obj.ReferenceEquals(x, null)\n    let inline isNotNull x = not (isNull x)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Events.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen Garnet.Numerics\n\ntype Start = struct end    \ntype Closing = struct end\n\n[<Struct>]\ntype HandleInput = {\n    Time : int64\n    }\n\n[<Struct>]\ntype Schedule = {\n    DueTime : int64\n    }\n\n[<Struct>]\ntype Tick = {\n    Time : int64\n    }\n\n[<Struct>]\ntype Update = {\n    FrameNumber : int64\n    FixedTime : int64\n    FixedDeltaTime : int64\n    Time : int64\n    DeltaTime : int64\n    }\n\n[<Struct>]\ntype PreUpdate = {\n    Update : Update\n    }\n    \n[<Struct>]\ntype PostUpdate = {\n    Update : Update\n    }\n\n[<Struct>]\ntype FixedUpdate = {\n    FixedFrameNumber : int64\n    FixedTime : int64\n    FixedDeltaTime : int64\n    Time : int64\n    }\n\n[<Struct>]\ntype Draw = {\n    ViewSize : Vector2i\n    Update : Update\n    }\n    \ntype PushDrawCommands = struct end\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Fonts.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Runtime.CompilerServices\nopen System.Numerics\nopen System.Runtime.InteropServices\nopen Veldrid\nopen Garnet.Numerics\nopen Garnet.Composition\n\n/// JSON-serializable\ntype FontCharDescriptor = {\n    Code : char\n    Width : int\n    OffsetX : int\n    OffsetY : int\n    RectX : int\n    RectY : int\n    RectWidth : int\n    RectHeight : int\n    }\n\n/// JSON-serializable\ntype FontDescriptor = {\n    Size : int\n    Family : string\n    Style : string\n    Height : int\n    Chars : FontCharDescriptor[]\n    }\n\ntype Align =\n    | Left = 0\n    | Center = 1\n    | Right = 2\n\ntype Valign =\n    | Top = 0\n    | Center = 1\n    | Bottom = 2\n\ntype TextWrapping =\n    | NoWrap = 0\n    | WordWrap = 1\n\n[<Struct>]\ntype TextBlock = {\n    Text : string\n    Color : RgbaFloat\n    Bounds : Range2i\n    Scale : int\n    Align : Align\n    Valign : Valign\n    Wrapping : TextWrapping\n    Spacing : Vector2i\n    } with\n    static member Default = {\n        Text = \"\"\n        Color = RgbaFloat.White\n        Bounds = Range2i.Zero\n        Scale = 1\n        Align = Align.Left\n        Valign = Valign.Top\n        Wrapping = TextWrapping.NoWrap\n        Spacing = Vector2i.Zero\n        }\n\n[<Struct>]\ntype FontCharInfo = {\n    Width : int\n    Offset : Vector2i\n    Size : Vector2i\n    Rect : Range2\n    }\n\nmodule private FontRun =\n    let getCharWidth ch (charWidths : int[]) =\n        let index = int ch\n        if index < charWidths.Length then charWidths.[index] else 0\n\n    let getTrimmedRange (str : string) (run : Rangei) =\n        let mutable start = run.Min\n        while start < run.Max && Char.IsWhiteSpace str.[start] do\n            start <- start + 1\n        let mutable stop = run.Max\n        while stop > run.Min && Char.IsWhiteSpace str.[stop - 1] do\n            stop <- stop - 1\n        Rangei(start, stop)\n        \n    let isWhitespace ch =\n        Char.IsWhiteSpace ch ||\n        ch = '\\n'\n\n    let getWordStart (str : string) start =\n        let mutable i = start\n        while i < str.Length && isWhitespace str.[i] do\n            i <- i + 1\n        i\n\n    let getWordEnd (str : string) start =\n        let mutable i = start\n        while i < str.Length && not (isWhitespace str.[i]) do\n            i <- i + 1\n        i\n\n    let getRunWidth (str : string) (charWidths : int[]) (run : Rangei) =\n        let mutable width = 0\n        for i = run.Min to run.Max - 1 do\n            width <- width + getCharWidth str.[i] charWidths\n        width\n        \n    let tryGetNextRun (str : string) charWidths start maxAllowedWidth =\n        let mutable runWidth = 0\n        let mutable i = start\n        let mutable result = ValueNone\n        while result.IsNone && i < str.Length do\n            let ch = str.[i]\n            if ch = '\\n' then\n                // Newline\n                result <- ValueSome (Rangei(start, i + 1))\n            else\n                runWidth <- runWidth + getCharWidth str.[i] charWidths\n                if runWidth > maxAllowedWidth then\n                    // Backtrack to start of word\n                    let mutable stop = i\n                    while stop > start + 1 && not (Char.IsWhiteSpace(str.[stop])) do\n                        stop <- stop - 1\n                    result <- ValueSome (Rangei(start, stop + 1))\n            i <- i + 1\n        // If no newline or limit reached, return remainder\n        if result.IsNone then\n            let remaining = str.Length - start\n            if remaining > 0 then\n                result <- ValueSome (Rangei(start, str.Length))\n        result\n\n    let measure (str : string) (charWidths : int[]) charHeight maxAllowedWidth =\n        let mutable maxWidth = 0\n        let mutable count = 0\n        let mutable runOpt = tryGetNextRun str charWidths 0 maxAllowedWidth\n        while runOpt.IsSome do\n            let run = runOpt.Value\n            let width = getRunWidth str charWidths run\n            maxWidth <- max maxWidth width\n            count <- count + 1\n            runOpt <- tryGetNextRun str charWidths run.Max maxAllowedWidth\n        Vector2i(maxWidth, count * charHeight)    \n\n    let getBounds (size : Vector2i) (bounds : Range2i) align valign =\n        let p0 = bounds.Min\n        let p1 = bounds.Max\n        let boxSize = p1 - p0\n        let x0 =\n            match align with\n            | Align.Left -> p0.X\n            | Align.Right -> p1.X - size.X\n            | Align.Center -> p0.X + (boxSize.X - size.X) / 2\n            | x -> failwith $\"Invalid align {x}\"\n        let y0 =\n            match valign with\n            | Valign.Top -> p0.Y\n            | Valign.Bottom -> p1.Y - size.Y\n            | Valign.Center -> p0.Y + (boxSize.Y - size.Y) / 2\n            | x -> failwith $\"Invalid valign {x}\"\n        Range2i.Sized(Vector2i(x0, y0), size)\n\n    let getMaxAllowedWidth wrapping size =\n        match wrapping with\n        | TextWrapping.NoWrap -> Int32.MaxValue\n        | TextWrapping.WordWrap -> size\n        | x -> failwith $\"Invalid wrapping {x}\"\n\nmodule private FontCharInfo =\n    let getTexBounds (charRect : Range2i) (mapSize : Vector2i) texBounds =\n        let scale = Vector2.One / (mapSize.ToVector2())\n        let t0 = charRect.Min.ToVector2() * scale\n        let t1 = charRect.Max.ToVector2() * scale       \n        let p0 = Range2.Lerp(texBounds, t0) \n        let p1 = Range2.Lerp(texBounds, t1)\n        Range2(p0, p1)\n        \n    let fromCharDescriptor (mapSize : Vector2i) (p : FontCharDescriptor) (texBounds : Range2) = \n        let r = Range2i.Sized(Vector2i(p.RectX, p.RectY), Vector2i(p.RectWidth, p.RectHeight))\n        {\n            Width = p.Width\n            Offset = Vector2i(p.OffsetX, p.OffsetY)\n            Size = Vector2i(p.RectWidth, p.RectHeight)\n            Rect = getTexBounds r mapSize texBounds\n        }\n\ntype Font(height, charLookup : FontCharInfo[]) =\n    let widths = charLookup |> Array.map (fun c -> c.Width)\n    member c.Height = height\n    member c.GetCharInfo(ch : char) =\n        let code = int ch\n        if code < charLookup.Length then charLookup.[code] else charLookup.[0]\n    member c.TryGetNextRun(str, start, maxAllowedWidth) =\n        FontRun.tryGetNextRun str widths start maxAllowedWidth\n    member c.Measure(text) =\n        FontRun.measure text widths height Int32.MaxValue\n    member c.Measure(block) =\n        let bounds = block.Bounds\n        let maxAllowedWidth = FontRun.getMaxAllowedWidth block.Wrapping (bounds.Size.X * block.Scale)\n        let size = FontRun.measure block.Text widths height maxAllowedWidth * block.Scale\n        FontRun.getBounds size bounds block.Align block.Valign\n    static member CreateMonospaced(charSheetSize : Vector2i, texBounds, xSpacing) =\n        // Assume this is a 16x16 char tile sheet\n        let charSize = charSheetSize / 16\n        let lookup = [|\n            for y = 0 to 15 do\n                for x = 0 to 15 do\n                    let p = Vector2i(x, y) * charSize\n                    let charRect = Range2i.Sized(p, charSize)\n                    {\n                        Width = charSize.X + xSpacing\n                        Offset = Vector2i.Zero \n                        Size = charSize\n                        Rect = FontCharInfo.getTexBounds charRect charSheetSize texBounds\n                    }    \n            |]\n        Font(charSize.Y, lookup)\n    static member FromDescriptor(desc, mapSize, texBounds) =\n        let table = Array.zeroCreate 256\n        for ch in desc.Chars do\n            let code = int ch.Code\n            if code < table.Length then\n                let coords = FontCharInfo.fromCharDescriptor mapSize ch texBounds\n                table.[code] <- coords\n        Font(desc.Height, table)\n        \n[<AutoOpen>]\nmodule FontLoadingExtensions =\n    type IReadOnlyFolder with\n        member c.LoadJsonFontDescriptor(fontName : string) =\n            c.LoadJson<FontDescriptor>(fontName)\n\n    type IResourceCache with\n        /// Loads a monospace font stored in a texture atlas\n        member c.LoadMonospacedFont(atlasName, fontName, xSpacing) =\n            match c.TryGetResource<Font>(fontName) with\n            | true, font -> font\n            | false, _ ->\n                let atlas = c.LoadResource<TextureAtlas>(atlasName)\n                let tex = atlas.[fontName]\n                let font = Font.CreateMonospaced(tex.Bounds.Size, tex.NormalizedBounds, xSpacing)\n                c.AddResource(fontName, font)\n                font\n\n        /// Loads a JSON font paired with a PNG with matching name\n        member c.LoadJsonFont(fontName,\n                fontTextureName,\n                [<Optional; DefaultParameterValue(\"\")>] atlasName : string) =\n            match c.TryGetResource<Font>(fontName) with\n            | true, font -> font\n            | false, _ ->\n                let desc = c.LoadResource<FontDescriptor>(fontName)\n                let font =\n                    if String.IsNullOrEmpty(atlasName) then\n                        let tex = c.LoadResource<Texture>(fontTextureName)\n                        Font.FromDescriptor(desc, Vector2i(int tex.Width, int tex.Height), Range2.ZeroToOne)\n                    else\n                        let atlas = c.LoadResource<TextureAtlas>(atlasName)\n                        let tex = atlas.[fontTextureName]\n                        let size = tex.Bounds.Size //- Vector2i.One * tex.Padding * 2\n                        let texBounds = tex.NormalizedBounds\n                        Font.FromDescriptor(desc, Vector2i(abs size.X, abs size.Y), texBounds)\n                c.AddResource(fontName, font)\n                font\n        \ntype FontLoader() =\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            let font = folder.LoadJsonFontDescriptor(key)\n            cache.AddResource<FontDescriptor>(key, font)\n\n[<AutoOpen>]\nmodule FontLoaderExtensions =\n    type ResourceCache with\n        member c.AddFontLoaders() =\n            c.AddLoader(\".font.json\", FontLoader())\n        \n[<Extension>]\ntype FontVertexSpanExtensions =\n    /// Draws monospace text\n    [<Extension>]\n    static member DrawText(w : IBufferWriter<PositionTextureColorVertex>,\n            text : string,\n            pos : Vector2i,\n            atlasSize : Vector2i,\n            fontTexBounds : Range2i,\n            charMargin : Vector2i,\n            color : RgbaFloat) =\n        let charSetSize = fontTexBounds.Size\n        let charSize = charSetSize / Vector2i(16, 16)\n        let displayCharSize = charSize + charMargin\n        let atlasScale = Vector2.One / atlasSize.ToVector2()\n        let span = w.GetQuadSpan(text.Length)\n        for i = 0 to text.Length - 1 do\n            let ch = text.[i]\n            let tileId = int ch\n            let tx = tileId &&& 0xf\n            let ty = tileId >>> 4\n            let t0 = Vector2i(tx, ty) * charSize + fontTexBounds.Min\n            let t1 = t0 + charSize\n            let tb = Range2(t0.ToVector2() * atlasScale, t1.ToVector2() * atlasScale)\n            let b = Range2i.Sized(pos + Vector2i(i * displayCharSize.X, 0), charSize)\n            let span = span.Slice(i * 4)\n            span.DrawQuad(b.ToRange2(), tb, color)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawText(w : IBufferWriter<PositionTextureColorVertex>, font : Font, block : TextBlock) =\n        let textBounds = font.Measure(block)\n        let span = w.GetQuadSpan(block.Text.Length)\n        let maxUnscaledWidth =\n            match block.Wrapping with\n            | TextWrapping.NoWrap -> Int32.MaxValue\n            | TextWrapping.WordWrap -> block.Bounds.Size.X / block.Scale\n            | x -> failwith $\"Invalid wrapping: {x}\"\n        let mutable runOpt = font.TryGetNextRun(block.Text, 0, maxUnscaledWidth)\n        let mutable row = 0\n        let mutable vi = 0\n        while runOpt.IsSome do\n            let run = runOpt.Value\n            let y = textBounds.Y.Min + row * font.Height * block.Scale\n            let mutable x = textBounds.X.Min\n            for i = run.Min to run.Max - 1 do\n                let ch = block.Text.[i]\n                let desc = font.GetCharInfo(ch)\n                if desc.Size.X > 0 then\n                    let b =\n                        let offset = desc.Offset * block.Scale\n                        let p0 = Vector2i(x + offset.X, y + offset.Y)\n                        let p1 = p0 + desc.Size * block.Scale\n                        Range2i(p0, p1)\n                    let span = span.Slice(vi)\n                    span.DrawQuad(b.ToRange2(), desc.Rect, block.Color)\n                    vi <- vi + 4\n                x <- x + desc.Width * block.Scale\n            runOpt <- font.TryGetNextRun(block.Text, run.Max, maxUnscaledWidth)\n            row <- row + 1\n        w.Advance(vi)\n\n    [<Extension>]\n    static member DrawText(w : IBufferWriter<PositionTextureColorVertex>, font,\n            text,\n            pos : Vector2i,\n            color : RgbaFloat,\n            [<Optional; DefaultParameterValue(0)>] width : int,\n            [<Optional; DefaultParameterValue(0)>] height : int,\n            [<Optional; DefaultParameterValue(Align.Left)>] align : Align,\n            [<Optional; DefaultParameterValue(Valign.Top)>] valign : Valign,\n            [<Optional; DefaultParameterValue(TextWrapping.NoWrap)>] wrapping : TextWrapping,\n            [<Optional; DefaultParameterValue(1)>] scale : int,\n            [<Optional; DefaultParameterValue(0)>] xSpacing : int,\n            [<Optional; DefaultParameterValue(0)>] ySpacing : int\n            ) =\n        w.DrawText(font, {\n            Text = text\n            Color = color\n            Bounds = Range2i.Sized(pos, Vector2i(width, height))\n            Scale = scale\n            Align = align\n            Valign = valign\n            Wrapping = wrapping\n            Spacing = Vector2i(xSpacing, ySpacing)\n        })\n    "
  },
  {
    "path": "samples/Garnet.Toolkit/Garnet.Toolkit.fsproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n    <GenerateDocumentationFile>false</GenerateDocumentationFile>\n    <RootNamespace>Garnet.Toolkit</RootNamespace>\n  </PropertyGroup>\n  <PropertyGroup>\n    <Description>Utility code for games, including graphics, audio, and integration with Garnet.</Description>\n    <PackageTags>graphics audio game</PackageTags>\n  </PropertyGroup>\n  <ItemGroup>\n    <Content Include=\"runtimes\\win-x64\\native\\openal32.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n      <PackagePath>runtimes/win-x64/native</PackagePath>\n      <Pack>true</Pack>\n    </Content>\n    <Content Include=\"runtimes\\win-x86\\native\\openal32.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n      <PackagePath>runtimes/win-x86/native</PackagePath>\n      <Pack>true</Pack>\n    </Content>\n    <Compile Include=\"Comparisons.fs\" />\n    <Compile Include=\"Collections.fs\" />\n    <Compile Include=\"Colors.fs\" />\n    <Compile Include=\"Serialization.fs\" />\n    <Compile Include=\"Input.fs\" />\n    <Compile Include=\"Rendering.fs\" />\n    <Compile Include=\"Shaders.fs\" />\n    <Compile Include=\"Meshes.fs\" />\n    <Compile Include=\"Vertices.fs\" />\n    <Compile Include=\"Textures.fs\" />\n    <Compile Include=\"Pipelines.fs\" />\n    <Compile Include=\"Offscreen.fs\" />\n    <Compile Include=\"Window.fs\" />\n    <Compile Include=\"Tiling.fs\" />\n    <Compile Include=\"Sprites.fs\" />\n    <Compile Include=\"Picking.fs\" />\n    <Compile Include=\"Fonts.fs\" />\n    <Compile Include=\"Particles.fs\" />\n    <Compile Include=\"Audio.fs\" />\n    <Compile Include=\"Events.fs\" />\n    <Compile Include=\"Timing.fs\" />\n    <Compile Include=\"Requests.fs\" />\n    <Compile Include=\"Systems.fs\" />\n    <Compile Include=\"Looping.fs\" />\n    <Compile Include=\"Logging.fs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.DotNet.PlatformAbstractions\" Version=\"3.1.6\" />\n    <PackageReference Include=\"Veldrid\" Version=\"4.8.0\" />\n    <PackageReference Include=\"Veldrid.ImGui\" Version=\"5.72.0\" />\n    <PackageReference Include=\"Veldrid.SDL2\" Version=\"4.8.0\" />\n    <PackageReference Include=\"Veldrid.SPIRV\" Version=\"1.0.14\" />\n    <PackageReference Include=\"Veldrid.StartupUtilities\" Version=\"4.8.0\" />\n    <PackageReference Include=\"OpenTK.OpenAL\" Version=\"4.6.4\" />\n    <PackageReference Include=\"SixLabors.ImageSharp\" Version=\"1.0.3\" />\n    <PackageReference Include=\"ZLogger\" Version=\"1.6.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Garnet\\Garnet.fsproj\" />\n    <ProjectReference Include=\"..\\Garnet.Numerics\\Garnet.Numerics.fsproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "samples/Garnet.Toolkit/Input.fs",
    "content": "﻿namespace Garnet.Input\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numerics\n\n[<Struct>]\ntype KeyDown = {\n    KeyCode : Key\n    Modifiers : ModifierKeys\n    } with\n    static member None = {\n        KeyCode = Key.Unknown\n        Modifiers = ModifierKeys.None\n        }\n\n[<Struct>]\ntype KeyUp = {\n    KeyCode : Key\n    Modifiers : ModifierKeys\n    }\n\n[<Struct>]\ntype MouseWheel = {\n    modifiers : int\n    wheel : int\n    }\n\n[<Struct>]\ntype MouseUpdate = {\n    pos : Vector2\n    devicePos : Vector2i\n    button1 : bool\n    button2 : bool\n    }\n\n[<Struct>]\ntype MouseMoved = {\n    pos : Vector2\n    delta : Vector2\n    devicePos : Vector2i\n    deviceDelta : Vector2i\n    modifiers : ModifierKeys\n    }\n\n[<Struct>]\ntype MouseDown = {\n    pos : Vector2\n    devicePos : Vector2i\n    button : int\n    modifiers : ModifierKeys\n    }\n\n[<Struct>]\ntype MouseUp = {\n    pos : Vector2\n    devicePos : Vector2i\n    button : int\n    }\n\n[<AutoOpen>]\nmodule InputExtensions =\n    type ModifierKeys with\n        member c.IsShift() = int c = int ModifierKeys.Shift\n        member c.IsCtrl() = int c = int ModifierKeys.Control\n        member c.IsAlt() = int c = int ModifierKeys.Alt\n        member c.HasShift() = int (c &&& ModifierKeys.Shift) <> 0\n        member c.HasCtrl() = int (c &&& ModifierKeys.Control) <> 0\n        member c.HasAlt() = int (c &&& ModifierKeys.Alt) <> 0\n\ntype InputCollection() =\n    let keys = Array.zeroCreate (int Key.LastKey)\n    let keyUpEvents = List<KeyUp>()\n    let keyDownEvents = List<KeyDown>()\n    let mouseDownEvents = List<int>()\n    let mouseUpEvents = List<int>()\n    let mouseButtons = Array.zeroCreate 10\n    let mutable mousePos = Vector2i.Zero\n    let mutable lastMousePos = Vector2i.Zero\n    let mutable normMousePos = Vector2.Zero\n    let mutable lastNormMousePos = Vector2.Zero\n    let mutable wheelDelta = 0.0f\n    member c.MousePosition = mousePos\n    member c.LastMousePosition = lastMousePos\n    member c.MouseDelta = mousePos - lastMousePos\n    member c.NormalizedMousePosition = normMousePos\n    member c.LastNormalizedMousePosition = lastNormMousePos\n    member c.NormalizedMouseDelta = normMousePos - lastNormMousePos\n    member c.WheelDelta = wheelDelta\n    member c.KeyUpEvents = keyUpEvents\n    member c.KeyDownEvents = keyDownEvents\n    member c.MouseUpEvents = mouseUpEvents\n    member c.MouseDownEvents = mouseDownEvents\n    member c.Modifiers =\n        let hasAlt =\n            c.IsKeyDown(Key.AltLeft) || \n            c.IsKeyDown(Key.AltRight) ||\n            c.IsKeyDown(Key.LAlt) ||\n            c.IsKeyDown(Key.RAlt)\n        let hasCtrl =\n            c.IsKeyDown(Key.ControlLeft) || \n            c.IsKeyDown(Key.ControlRight) ||\n            c.IsKeyDown(Key.LControl) ||\n            c.IsKeyDown(Key.RControl)\n        let hasShift =\n            c.IsKeyDown(Key.ShiftLeft) || \n            c.IsKeyDown(Key.ShiftRight) ||\n            c.IsKeyDown(Key.LShift) ||\n            c.IsKeyDown(Key.RShift)\n        (if hasAlt then ModifierKeys.Alt else ModifierKeys.None) |||\n        (if hasCtrl then ModifierKeys.Control else ModifierKeys.None) |||\n        (if hasShift then ModifierKeys.Shift else ModifierKeys.None)\n    member c.IsMouseDown(button) =\n        mouseButtons.[button]\n    member c.IsMousePressed(button) =\n        mouseDownEvents.Contains(button)\n    member c.IsKeyDown(code : Key) =\n        keys.[int code]\n    member c.IsKeyPressed(code, modifiers) =\n        keyDownEvents.Contains { \n            KeyCode = code\n            Modifiers = modifiers\n            }\n    member c.UpdateMouse(newPos, newNormPos, newWheelDelta) =\n        wheelDelta <- newWheelDelta\n        lastMousePos <- mousePos\n        mousePos <- newPos\n        lastNormMousePos <- normMousePos\n        normMousePos <- newNormPos\n    member c.SetMouseButton(button : MouseButton, state) =\n        if mouseButtons.[int button] <> state then\n            if state then mouseDownEvents.Add(int button)\n            else mouseUpEvents.Add(int button)\n        mouseButtons.[int button] <- state\n    member c.Add(code, modifier) =\n        keys.[int code] <- true\n        keyDownEvents.Add { \n            KeyCode = code\n            Modifiers = modifier\n            }\n    member c.Remove(code, modifier) =\n        keys.[int code] <- false\n        keyUpEvents.Add { \n            KeyCode = code\n            Modifiers = modifier\n            }\n    member c.Clear() =\n        Array.Clear(keys, 0, keys.Length)\n    member c.ClearEvents() =\n        keyUpEvents.Clear()\n        keyDownEvents.Clear()\n        mouseDownEvents.Clear()\n        mouseUpEvents.Clear()\n    override c.ToString() =\n        String.Join(\", \", keys)\n\ntype InputCollection with\n    member c.IsKeyDown(code, modifier : ModifierKeys) =\n        c.IsKeyDown(code) && int modifier = int c.Modifiers\n\n    member c.IsKeyPressed(code) =\n        c.IsKeyPressed(code, ModifierKeys.None)\n\n    member c.IsAnyKeyDown(codes : IReadOnlyList<Key>) =\n        let mutable down = false\n        for i = 0 to codes.Count - 1 do\n            let code = codes.[i]\n            down <- down || c.IsKeyDown(code)\n        down\n        \n    member c.IsAnyKeyPressed(codes : IReadOnlyList<Key>, modifiers) =\n        let mutable down = false\n        for i = 0 to codes.Count - 1 do\n            let code = codes.[i]\n            down <- down || c.IsKeyPressed(code, modifiers)\n        down\n\n    member c.IsAnyKeyPressed(codes) =\n        c.IsAnyKeyPressed(codes, ModifierKeys.None)\n\n    member c.UpdateKeysFromSnapshot(snapshot : InputSnapshot) =        \n        for i = 0 to snapshot.KeyEvents.Count - 1 do\n            let e = snapshot.KeyEvents.[i]\n            let m = e.Modifiers\n            let key = e.Key\n            if e.Down then c.Add(key, m)\n            else c.Remove(key, e.Modifiers)\n\n    member c.UpdateMouseFromSnapshot(snapshot : InputSnapshot, viewSize : Vector2i) =        \n        let mousePos = Vector2i(int snapshot.MousePosition.X, int snapshot.MousePosition.Y)\n        let normMousePosition =\n            let v = snapshot.MousePosition / viewSize.ToVector2() * 2.0f - Vector2.One\n            Vector2(v.X, -v.Y)       \n        c.UpdateMouse(mousePos, normMousePosition, snapshot.WheelDelta)\n        for e in snapshot.MouseEvents do\n            let button = e.MouseButton\n            c.SetMouseButton(button, e.Down)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Logging.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen Microsoft.Extensions.Logging\nopen Cysharp.Text\nopen ZLogger\n    \ntype LogSettings = {\n    WriteToFile : bool\n    WriteToConsole : bool\n    RollFile : bool\n    LogPath : string\n    LogName : string\n    TimestampFormat : string\n    PrefixFormat : string\n    MinLogLevel : LogLevel\n    FileRollSizeKb : int\n    }\n\nmodule private Logging =\n    let configure settings (options : ZLoggerOptions) =\n        let prefixFormat = ZString.PrepareUtf8<string, LogLevel, string>(settings.PrefixFormat)\n        options.PrefixFormatter <- fun writer info -> \n            let mutable w = writer\n            let timestamp = info.Timestamp.DateTime.ToLocalTime().ToString(settings.TimestampFormat)\n            prefixFormat.FormatTo(&w, timestamp, info.LogLevel, info.CategoryName)\n\ntype LogSettings with\n    static member Default = {\n        WriteToFile = true\n        WriteToConsole = false\n        RollFile = false\n        LogPath = \".\"\n        LogName = \"run\"\n        TimestampFormat = \"yyyy-MM-dd HH:mm:ss.fff\"\n        PrefixFormat = \"{0} {1} [{2}] \"\n        MinLogLevel = LogLevel.Information\n        FileRollSizeKb = 1024\n        }\n    \n    member settings.CreateFactory() =\n        LoggerFactory.Create(fun builder ->\n            let builder = builder.SetMinimumLevel(settings.MinLogLevel)\n            // Add console\n            let builder =\n                if settings.WriteToConsole\n                then builder.AddZLoggerConsole(Logging.configure settings)\n                else builder\n            // Add rolling file\n            let _ =\n                if settings.WriteToFile then\n                    if settings.RollFile then\n                        let logPrefix = Path.Combine(settings.LogPath, settings.LogName + \"-\")\n                        builder.AddZLoggerRollingFile(\n                            (fun dt index ->\n                                let timestamp = dt.ToLocalTime().ToString(\"yyyy-MM-dd\")\n                                let num = index.ToString().PadLeft(3, '0')\n                                $\"{logPrefix}{timestamp}_{num}.log\"),\n                            (fun dt -> DateTimeOffset(dt.ToLocalTime().Date)),\n                            settings.FileRollSizeKb,\n                            Action<ZLoggerOptions>(Logging.configure settings))\n                    else\n                        let file = Path.Combine(settings.LogPath, settings.LogName + \".log\")\n                        builder.AddZLoggerFile(file, Action<_>(Logging.configure settings))\n                else builder\n            ())\n\n[<AutoOpen>]\nmodule LoggingExtensions =\n    type Container with\n        static member RunLoop(settings : LogSettings, register) =\n            use factory = settings.CreateFactory()\n            let logger = factory.CreateLogger(\"Default\")\n            try\n                let c = Container()\n                c.Set<ILogger>(logger)\n                use s = register c\n                c.RunLoop()\n            with ex ->\n                logger.LogError(ex, \"\")\n\n        static member RunLoop(register) =\n            Container.RunLoop(LogSettings.Default, register)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Looping.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Diagnostics\nopen System.Runtime.CompilerServices\nopen System.Threading\nopen System.Runtime.InteropServices\nopen Garnet.Composition\n\n[<Extension>]\ntype Looping =\n    [<Extension>]\n    static member RunLoop(c : Container, [<Optional; DefaultParameterValue(1)>] sleepDuration) =\n        let mutable running = true\n        use sub = c.On<Closing> <| fun _ -> running <- false\n        c.Run(Start())\n        let sw = Stopwatch.StartNew()\n        while running do\n            c.Run<Tick> { Time = sw.ElapsedMilliseconds }\n            // Sleep to avoid spinning CPU\n            if sleepDuration >= 0 then\n                Thread.Sleep(sleepDuration)\n        \n    [<Extension>]\n    static member RunStartup(c : ActorSystem, loopActorId : ActorId, destIds : ActorId seq) =\n        // Create error handler to collect exceptions\n        let exceptions = List<Exception>()\n        use sub = c.RegisterExceptionHandler(fun ex -> exceptions.Add(ex))\n        // Send start message to all root actors\n        let destIds = ReadOnlySpan(destIds |> Seq.toArray)\n        for destId in destIds do\n            c.Send(destId, loopActorId, Start())\n        // Wait for all threads to complete\n        c.ProcessAll()\n        if exceptions.Count > 0 then\n            raise (AggregateException(\"Startup failed\", exceptions))\n\n    [<Extension>]\n    static member RunLoop(c : ActorSystem, loopActorId : ActorId, destActorId : ActorId, [<Optional; DefaultParameterValue(1)>] sleepDuration) =\n        let exceptions = List<Exception>()\n        let mutable running = true\n        // Stop on exception\n        use sub = c.RegisterExceptionHandler(fun ex ->\n            exceptions.Add(ex)\n            running <- false)\n        // Stop if closing message received\n        c.Register(loopActorId, fun (c : Container) ->\n            c.On<Closing> <| fun _ -> running <- false)\n        // Run loop\n        let sw = Stopwatch.StartNew()\n        while running do\n            // Send tick to destination actor with source as loop actor to indicate\n            // where closing messages should be sent.\n            let e = { Time = sw.ElapsedMilliseconds }\n            c.Send<Tick>(destActorId, loopActorId, e)\n            c.Process()\n            // Sleep to avoid spinning CPU\n            if sleepDuration >= 0 then\n                Thread.Sleep(sleepDuration)\n        // Wait for all threads to complete\n        c.ProcessAll()\n        if exceptions.Count > 0 then\n            raise (AggregateException(\"Updating failed\", exceptions))\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Meshes.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen Veldrid\nopen Garnet.Collections\n\ntype ResizableDeviceBuffer(device : GraphicsDevice, elementSize, usage) =\n    let mutable buffer = device.ResourceFactory.CreateBuffer(BufferDescription(uint32 (elementSize * 8), usage))\n    member c.Buffer = buffer\n    member c.Write<'v\n            when 'v : struct \n            and 'v : (new : unit -> 'v) \n            and 'v :> ValueType>(src : ReadOnlyMemory<'v>) =\n        // ensure device buffer is large enough\n        let size = src.Length * elementSize\n        if buffer.SizeInBytes < uint32 size then\n            // destroy old buffer\n            buffer.Dispose()\n            // round up to pow2 number of elements (not bytes)\n            let requiredSize = Buffer.getRequiredCount src.Length * elementSize\n            let desc = BufferDescription(uint32 requiredSize, usage)    \n            buffer <- device.ResourceFactory.CreateBuffer(desc)\n        // write data\n        use handle = src.Pin()\n        device.UpdateBuffer(buffer, 0u, IntPtr handle.Pointer, uint32 size)\n    member c.Dispose() =\n        buffer.Dispose()\n    interface IDisposable with \n        member c.Dispose() = c.Dispose()\n\ntype DeviceMesh(device, vertexSize) =\n    let vb = new ResizableDeviceBuffer(device, vertexSize, BufferUsage.Dynamic ||| BufferUsage.VertexBuffer)\n    let ib = new ResizableDeviceBuffer(device, sizeof<uint32>, BufferUsage.Dynamic ||| BufferUsage.IndexBuffer)\n    member c.WriteVertices(src) = vb.Write(src)\n    member c.WriteIndexes(src) = ib.Write(src)\n    member c.Draw(cmds : CommandList, indexCount) =\n        if indexCount > 0 then\n            cmds.SetVertexBuffer(0u, vb.Buffer)\n            cmds.SetIndexBuffer(ib.Buffer, IndexFormat.UInt32)\n            cmds.DrawIndexed(\n                indexCount = uint32 indexCount,\n                instanceCount = 1u,\n                indexStart = 0u,\n                vertexOffset = 0,\n                instanceStart = 0u)\n    member c.Dispose() =\n        vb.Dispose()\n        ib.Dispose()\n\ntype QuadIndexBuffer(device : GraphicsDevice) =\n    let indexesPer = 6\n    let verticesPer = 4\n    let elementSize = sizeof<uint32>\n    let usage = BufferUsage.Dynamic ||| BufferUsage.IndexBuffer\n    // Initially allocate just 1 to force creation on first request\n    let mutable buffer = device.ResourceFactory.CreateBuffer(BufferDescription(1u, usage))\n    member private c.Update(requestedCount) =\n        let bytesPer = elementSize * indexesPer\n        let bufferedCount = int buffer.SizeInBytes / bytesPer\n        if bufferedCount < requestedCount then\n            // Round up to pow2 number of elements (not bytes)\n            let requiredCount = Buffer.getRequiredCount requestedCount\n            let requiredIndexes = requiredCount * indexesPer\n            let requiredBytes = requiredCount * bytesPer\n            // Generate indexes for primitive\n            let arr = ArrayPool<int>.Shared.Rent(requiredIndexes)\n            for i = 0 to requiredCount - 1 do\n                let vi = i * verticesPer\n                let ii = i * indexesPer\n                arr.[ii + 0] <- vi + 0\n                arr.[ii + 1] <- vi + 1\n                arr.[ii + 2] <- vi + 2\n                arr.[ii + 3] <- vi + 0\n                arr.[ii + 4] <- vi + 2\n                arr.[ii + 5] <- vi + 3\n            // Destroy old device buffer and create new one\n            buffer.Dispose()\n            let desc = BufferDescription(uint32 requiredBytes, usage)    \n            buffer <- device.ResourceFactory.CreateBuffer(desc)\n            // Write data to device buffer\n            let src = ReadOnlyMemory(arr)\n            use handle = src.Pin()            \n            device.UpdateBuffer(buffer, 0u, IntPtr handle.Pointer, uint32 requiredBytes)\n            ArrayPool<int>.Shared.Return(arr)\n    member c.Draw(cmds : CommandList, primitiveCount) =\n        if primitiveCount > 0 then\n            c.Update(primitiveCount)\n            cmds.SetIndexBuffer(buffer, IndexFormat.UInt32)\n            cmds.DrawIndexed(\n                indexCount = uint32 (primitiveCount * indexesPer),\n                instanceCount = 1u,\n                indexStart = 0u,\n                vertexOffset = 0,\n                instanceStart = 0u)\n    member c.Dispose() = buffer.Dispose()\n    interface IDisposable with \n        member c.Dispose() = c.Dispose()\n\ntype IVertexBuffer =\n    inherit IDisposable\n    abstract VertexCount : int\n    abstract SetVertexBuffer : CommandList -> unit\n    abstract Flush : unit -> unit\n\ntype VertexBuffer<'v\n                when 'v : struct \n                and 'v : (new : unit -> 'v) \n                and 'v :> ValueType>(device) =\n    let vertices = ArrayBufferWriter<'v>()\n    let vb = new ResizableDeviceBuffer(device, sizeof<'v>, BufferUsage.Dynamic ||| BufferUsage.VertexBuffer)\n    let mutable vertexCount = 0\n    member c.VertexCount = vertexCount\n    member c.WrittenSpan = vertices.WrittenSpan\n    member c.GetMemory(count) =\n        vertices.GetMemory(count)\n    member c.GetSpan(count) =\n        vertices.GetSpan(count)\n    member c.Advance(count) =\n        vertices.Advance(count)\n    member c.Flush() =\n        vertexCount <- vertices.WrittenCount\n        vb.Write(vertices.WrittenMemory)\n        vertices.Clear()\n    interface IBufferWriter<'v> with\n        member c.GetSpan(count) = c.GetSpan(count)\n        member c.GetMemory(count) = c.GetMemory(count)\n        member c.Advance(count) = c.Advance(count)        \n    member c.SetVertexBuffer(commands : CommandList) =\n        commands.SetVertexBuffer(0u, vb.Buffer)\n    member c.Dispose() =\n        vb.Dispose()\n    interface IVertexBuffer with\n        member c.VertexCount = vertexCount\n        member c.SetVertexBuffer(commands) = c.SetVertexBuffer(commands)\n        member c.Flush() = c.Flush()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()        \n"
  },
  {
    "path": "samples/Garnet.Toolkit/Offscreen.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\n\ntype PositionTextureColorQuadMesh(device : GraphicsDevice) =\n    let vb =\n        let size = uint32 (sizeof<PositionTextureColorVertex> * 4)\n        let b = device.ResourceFactory.CreateBuffer(BufferDescription(size, BufferUsage.VertexBuffer))\n        device.UpdateBuffer(b, 0u, [| \n            // flip Y tex coords\n            { Position = Vector3(-1.0f, -1.0f, 0.0f); TexCoord = Vector2(0.0f, 1.0f); Color = RgbaFloat.White }\n            { Position = Vector3(+1.0f, -1.0f, 0.0f); TexCoord = Vector2(1.0f, 1.0f); Color = RgbaFloat.White }\n            { Position = Vector3(+1.0f, +1.0f, 0.0f); TexCoord = Vector2(1.0f, 0.0f); Color = RgbaFloat.White }\n            { Position = Vector3(-1.0f, +1.0f, 0.0f); TexCoord = Vector2(0.0f, 0.0f); Color = RgbaFloat.White }\n            |])\n        b\n    let ib = \n        let b = device.ResourceFactory.CreateBuffer(BufferDescription(uint32 (sizeof<uint32> * 6), BufferUsage.IndexBuffer))\n        device.UpdateBuffer(b, 0u, [| 0u; 1u; 2u; 0u; 2u; 3u |])\n        b\n    member c.Draw(cmds : CommandList) =\n        cmds.SetVertexBuffer(0u, vb)\n        cmds.SetIndexBuffer(ib, IndexFormat.UInt32)\n        cmds.DrawIndexed(\n            indexCount = 6u,\n            instanceCount = 1u,\n            indexStart = 0u,\n            vertexOffset = 0,\n            instanceStart = 0u)\n    member c.Dispose() =\n        vb.Dispose()\n        ib.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\ntype ColorDepthRenderTarget(device : GraphicsDevice, width, height) =\n    let renderTarget =\n        device.ResourceFactory.CreateTexture(\n            TextureDescription(\n                uint32 width,\n                uint32 height,\n                1u,\n                1u,\n                1u,\n                PixelFormat.R16_G16_B16_A16_Float,\n                TextureUsage.RenderTarget ||| TextureUsage.Sampled,\n                TextureType.Texture2D))\n    let depthTexture = \n        device.ResourceFactory.CreateTexture(\n            TextureDescription.Texture2D(\n                uint32 width,\n                uint32 height,\n                1u, \n                1u, \n                PixelFormat.R32_Float, \n                TextureUsage.DepthStencil))\n    let frameBuffer =\n        device.ResourceFactory.CreateFramebuffer(\n            FramebufferDescription(depthTexture, renderTarget))\n    member c.Texture = renderTarget\n    member c.OutputDescription =\n        frameBuffer.OutputDescription\n    member c.SetFramebuffer(cmds : CommandList) =\n        cmds.SetFramebuffer(frameBuffer)\n    member c.Dispose() =\n        renderTarget.Dispose()\n        depthTexture.Dispose()\n        frameBuffer.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\ntype ColorRenderTargetBuffer(device : GraphicsDevice, width, height) =\n    let renderTarget =\n        device.ResourceFactory.CreateTexture(\n            TextureDescription(\n                uint32 width,\n                uint32 height,\n                1u,\n                1u,\n                1u,\n                PixelFormat.R16_G16_B16_A16_Float,\n                TextureUsage.RenderTarget ||| TextureUsage.Sampled,\n                TextureType.Texture2D))\n    let frameBuffer =\n        device.ResourceFactory.CreateFramebuffer(\n            FramebufferDescription(null, renderTarget))\n    member c.Texture = renderTarget\n    member c.OutputDescription =\n        frameBuffer.OutputDescription\n    member c.PushFramebuffer(context : RenderContext) =\n        context.PushFramebuffer(frameBuffer)\n    member c.Dispose() =\n        renderTarget.Dispose()\n        frameBuffer.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\ntype ColorRenderTargetPipeline(device : GraphicsDevice, width, height, shaders, sampler, blend) =\n    let target = new ColorRenderTargetBuffer(device, width, height)\n    let pipelines = Dictionary<_, TextureTrianglePipeline>()\n    member c.Width = width\n    member c.Height = height\n    member c.PushFramebuffer(context) =\n        target.PushFramebuffer(context)\n    member c.SetPipeline(cmds : CommandList, worldTransform, outputDesc) =\n        let pipeline =\n            match pipelines.TryGetValue(outputDesc) with\n            | true, x -> x\n            | false, _ ->\n                let pipeline = new TextureTrianglePipeline(device, shaders, target.Texture, sampler, blend, outputDesc)\n                pipelines.Add(outputDesc, pipeline)\n                pipeline\n        pipeline.SetPipeline(cmds)\n        pipeline.SetProjectionView(Matrix4x4.Identity, Matrix4x4.Identity, cmds)\n        pipeline.SetWorldTexture(worldTransform, Matrix4x4.Identity, cmds)        \n    member c.Dispose() =\n        target.Dispose()\n        for pipeline in pipelines.Values do\n            pipeline.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\n/// Offscreen render target along with a quad mesh so render target can be drawn in\n/// another viewport. Does not take ownership of shaders or drawables.\ntype RenderTarget(device : GraphicsDevice, shaders, filtering, blend) =\n    let sampler = device.GetSampler(filtering)\n    let mesh = new PositionTextureColorQuadMesh(device)\n    let mutable pipelineOpt = ValueNone\n    member val Background = RgbaFloat.Black with get, set\n    member val WorldTransform = Matrix4x4.Identity with get, set\n    member val Width = 0 with get, set\n    member val Height = 0 with get, set\n    member private c.CreatePipeline() =\n        let pipeline = new ColorRenderTargetPipeline(device, c.Width, c.Height, shaders, sampler, blend)\n        pipelineOpt <- ValueSome pipeline\n        pipeline\n    member c.BeginDraw(context) =\n        let pipeline =\n            match pipelineOpt with\n            | ValueNone -> c.CreatePipeline()\n            | ValueSome pipeline ->\n                if pipeline.Width = c.Width && pipeline.Height = c.Height then pipeline\n                else \n                    pipeline.Dispose()\n                    c.CreatePipeline()\n        // Set target before making draw calls for render target\n        pipeline.PushFramebuffer(context)\n        context.Commands.ClearColorTarget(0u, c.Background)\n    member c.EndDraw(context : RenderContext) =\n        match pipelineOpt with\n        | ValueNone -> () \n        | ValueSome pipeline ->\n            // Draw render target itself as a quad\n            context.PopFramebuffer()\n            pipeline.SetPipeline(context.Commands, c.WorldTransform, context.OutputDescription)\n            mesh.Draw(context.Commands)\n    member c.Dispose() =\n        match pipelineOpt with\n        | ValueNone -> ()\n        | ValueSome pipeline -> pipeline.Dispose()\n        mesh.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Particles.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Numerics\nopen Garnet.Collections\n\ntype ParticleAnimation = {\n    RandomSeed : uint64\n    AnimationId : int\n    Layer : SpriteLayerDescriptor<PositionTextureColorVertex>\n    Duration : float32\n    Width : float32\n    Height : float32\n    SizeByEnergy : float32\n    OpacityByEnergy : float32\n    Textures : string[]\n    MinTint : RgbaFloat\n    MaxTint : RgbaFloat\n    TintWeight : float32\n    MinHue : float32\n    MaxHue : float32\n    MinSaturation : float32\n    MaxSaturation : float32\n    MinValue : float32\n    MaxValue : float32\n    Opacity : float32\n    UseColor : bool\n    StartColor : RgbaFloat\n    EndColor : RgbaFloat\n    MinSpeed : float32\n    MaxSpeed : float32\n    VelocityAngleRange : float32\n    RotationAngleRange : float32\n    MinCount : int\n    MaxCount : int\n    InitialDistance : float32\n    InitialSize : float32\n    InitialSizeRange : float32\n    }\n\n[<Struct>]\ntype ParticleEmission = {\n    GroupId : int\n    EmitDelay : float32\n    EmitInterval : float32\n    EmitCount : int\n    Position : Vector2\n    Velocity : Vector2\n    Rotation : Vector2\n    Color : RgbaFloat\n    Energy : float32\n    }\n\ntype ParticleGroup = {\n    GroupId : int\n    Animations : ParticleAnimation[]\n    }\n\ntype ParticleAnimation with\n    static member Defaults = {\n        RandomSeed = 1UL\n        AnimationId = 0\n        Layer = Unchecked.defaultof<_>\n        Duration = 1.0f\n        Width = 1.0f\n        Height = 1.0f\n        SizeByEnergy = 0.0f\n        OpacityByEnergy = -1.0f\n        Textures = Array.empty\n        MinHue = 0.0f\n        MaxHue = 0.0f\n        MinSaturation = 0.0f\n        MaxSaturation = 0.0f\n        MinValue = 1.0f\n        MaxValue = 1.0f\n        MinTint = RgbaFloat.White\n        MaxTint = RgbaFloat.White\n        UseColor = false\n        StartColor = RgbaFloat.White\n        EndColor = RgbaFloat.White\n        MinSpeed = 0.0f\n        MaxSpeed = 0.0f\n        VelocityAngleRange = 360.0f\n        RotationAngleRange = 360.0f\n        MinCount = 1\n        MaxCount = 1\n        InitialDistance = 0.0f\n        InitialSize = 1.0f\n        InitialSizeRange = 0.0f\n        TintWeight = 1.0f\n        Opacity = 1.0f \n        }\n\ntype private ParticleBuffers() =\n    [<DefaultValue>] val mutable timestamps : int64[]\n    [<DefaultValue>] val mutable positions : Vector2[]\n    [<DefaultValue>] val mutable velocities : Vector2[]\n    [<DefaultValue>] val mutable sizes : float32[]\n    [<DefaultValue>] val mutable rotations : Vector2[]\n    [<DefaultValue>] val mutable energies : float32[]\n    [<DefaultValue>] val mutable colors : RgbaFloat[]\n    [<DefaultValue>] val mutable opacities : float32[]\n    [<DefaultValue>] val mutable texIndices : int[]\n    member c.Allocate(count) =\n        Buffer.resizeArray count &c.timestamps \n        Buffer.resizeArray count &c.positions\n        Buffer.resizeArray count &c.velocities\n        Buffer.resizeArray count &c.sizes\n        Buffer.resizeArray count &c.rotations\n        Buffer.resizeArray count &c.energies\n        Buffer.resizeArray count &c.colors\n        Buffer.resizeArray count &c.opacities\n        Buffer.resizeArray count &c.texIndices\n    static member inline Copy(src : ParticleBuffers, srcIndex, dest : ParticleBuffers, destIndex) =\n        dest.timestamps.[destIndex] <- src.timestamps.[srcIndex]\n        dest.positions.[destIndex] <- src.positions.[srcIndex]\n        dest.velocities.[destIndex] <- src.velocities.[srcIndex]\n        dest.sizes.[destIndex] <- src.sizes.[srcIndex]\n        dest.rotations.[destIndex] <- src.rotations.[srcIndex]\n        dest.energies.[destIndex] <- src.energies.[srcIndex]\n        dest.colors.[destIndex] <- src.colors.[srcIndex]\n        dest.opacities.[destIndex] <- src.opacities.[srcIndex]\n        dest.texIndices.[destIndex] <- src.texIndices.[srcIndex]\n\n/// Cached for every kind of animation, not per emission\ntype ParticleSet(anim : ParticleAnimation) =\n    let rand = PcgRandom(anim.RandomSeed, 1UL)\n    let texBounds = Array.zeroCreate anim.Textures.Length\n    let b = ParticleBuffers()\n    let mutable time = 0L\n    let mutable pc = 0\n    let swapRemove (index : int byref) =\n        pc <- pc - 1\n        ParticleBuffers.Copy(b, pc, b, index)\n        index <- index - 1        \n    let pruneByTimestamp() =\n        let mutable i = 0\n        while i < pc do\n            if time < b.timestamps.[i] then swapRemove &i\n            i <- i + 1\n    let updateEnergy energyUnitToTick =\n        let energyRate = -energyUnitToTick\n        let mutable i = 0\n        while i < pc do\n            let e = b.energies.[i] + energyRate\n            if e > 0.0f\n                then b.energies.[i] <- e\n                else swapRemove &i\n            i <- i + 1\n    let addToScalars (arr : _[]) pc delta =\n        if abs delta > 0.0f then\n            for i = 0 to pc - 1 do\n                arr.[i] <- arr.[i] + delta                \n    let updatePositions deltaTime =\n        for i = 0 to pc - 1 do\n            b.positions.[i] <- b.positions.[i] + b.velocities.[i] * deltaTime\n    let updateColors () =\n        if anim.UseColor then\n            let c0 = anim.StartColor\n            let c1 = anim.EndColor\n            for i = 0 to pc - 1 do\n                b.colors.[i] <- RgbaFloat.Lerp(c0, c1, b.energies.[i])\n    member c.ParticleCount = pc\n    member c.Clear() =\n        pc <- 0\n        time <- 0L\n    member s.Update(deltaTime : int64) =\n        time <- time + deltaTime\n        if pc > 0 then\n            pruneByTimestamp()\n            // Energy goes from 1.0 to 0.0 over duration\n            let deltaSec = float32 deltaTime / 1000.0f\n            let relativeDelta = deltaSec / anim.Duration \n            let opacityDelta = anim.OpacityByEnergy * relativeDelta\n            let sizeDelta = anim.SizeByEnergy * relativeDelta\n            updateEnergy relativeDelta\n            addToScalars b.opacities pc opacityDelta\n            addToScalars b.sizes pc sizeDelta\n            updatePositions deltaSec\n            updateColors ()\n    member s.Emit(pe : ParticleEmission) =\n        let emitCount = \n            let x0 = anim.MinCount * pe.EmitCount\n            let x1 = anim.MaxCount * pe.EmitCount\n            rand.NextInt32(x0, x1)\n        let size = \n            let x0 = anim.InitialSize - anim.InitialSizeRange * 0.5f\n            let x1 = anim.InitialSize + anim.InitialSizeRange * 0.5f\n            rand.NextSingle(x0, x1)\n        let minTint = anim.MinTint\n        let maxTint = anim.MaxTint\n        b.Allocate(pc + emitCount)\n        for i = 0 to emitCount - 1 do\n            let dir = rand.NextRotationDegrees(anim.VelocityAngleRange).Rotate(pe.Rotation)\n            let index = pc + i\n            b.timestamps.[index] <- time\n            b.positions.[index] <- pe.Position + dir * anim.InitialDistance\n            b.velocities.[index] <-\n                let v = \n                    let speed = rand.NextSingle(anim.MinSpeed, anim.MaxSpeed)\n                    dir * speed + pe.Velocity\n                v\n            b.sizes.[index] <- size\n            b.rotations.[index] <- rand.NextRotationDegrees(anim.RotationAngleRange).Rotate(dir)\n            b.energies.[index] <- pe.Energy\n            b.colors.[index] <-\n                let color =\n                    let h = rand.NextSingle(anim.MinHue, anim.MaxHue)\n                    let s = rand.NextSingle(anim.MinSaturation, anim.MaxSaturation)\n                    let v = rand.NextSingle(anim.MinValue, anim.MaxValue)\n                    let c = HsvaFloat(h, s, v, 1.0f).ToRgbaFloat()\n                    let tint =\n                        let c = RgbaFloat.Lerp(minTint, maxTint, rand.NextSingle())\n                        let tint = RgbaFloat.Lerp(RgbaFloat.White, pe.Color, anim.TintWeight)\n                        c.Multiply(tint)\n                    c.Multiply(tint)\n                color\n            b.opacities.[index] <- anim.Opacity\n            b.texIndices.[index] <- 0\n        // Textures\n        if anim.Textures.Length > 1 then\n            for i = 0 to emitCount - 1 do\n                b.texIndices.[pc + i] <- rand.NextInt32(anim.Textures.Length)\n        pc <- pc + emitCount    \n    member c.Draw(layers : SpriteRenderer, atlas : TextureAtlas) =\n        // Update tex bounds lookup\n        for i = 0 to texBounds.Length - 1 do\n            texBounds.[i] <- atlas.GetNormalizedBounds(anim.Textures.[i])\n        // Draw sprites\n        let w = layers.GetVertices(anim.Layer)\n        let baseSize = Vector2(anim.Width, anim.Height) * 0.5f\n        let span = w.GetQuadSpan(pc)\n        for i = 0 to pc - 1 do\n            let p = b.positions.[i]\n            let dir = b.rotations.[i]\n            let scaling = b.sizes.[i]\n            let fg =\n                let opacity = b.opacities.[i]\n                let color = b.colors.[i] \n                color.MultiplyAlpha(opacity)\n            let side = -dir.GetPerpendicular()\n            let s = baseSize * scaling\n            let dy = dir * s.Y\n            let dx = side * s.X\n            let p0 = p - dx + dy\n            let p1 = p - dx - dy\n            let p2 = p + dx - dy\n            let p3 = p + dx + dy\n            let texIndex = b.texIndices.[i]\n            let tb = texBounds.[texIndex]\n            let t0 = tb.Min\n            let t2 = tb.Max\n            let span = span.Slice(i * 4)\n            span.[0] <- {\n                Position = Vector3(p0.X, p0.Y, 0.0f)\n                TexCoord = Vector2(t0.X, t0.Y)\n                Color = fg\n                }\n            span.[1] <- {\n                Position = Vector3(p1.X, p1.Y, 0.0f)\n                TexCoord = Vector2(t2.X, t0.Y)\n                Color = fg\n                }\n            span.[2] <- {\n                Position = Vector3(p2.X, p2.Y, 0.0f)\n                TexCoord = Vector2(t2.X, t2.Y)\n                Color = fg\n                }\n            span.[3] <- {\n                Position = Vector3(p3.X, p3.Y, 0.0f)\n                TexCoord = Vector2(t0.X, t2.Y)\n                Color = fg\n                }\n        w.Advance(span.Length)        \n\ntype ParticleSystem() =\n    let sets = List<ParticleSet>()\n    let groups = List<ParticleSet[]>()\n    let layerDescriptors = HashSet<SpriteLayerDescriptor>()\n    member c.ParticleCount =\n        let mutable count = 0\n        for set in sets do\n            count <- count + set.ParticleCount\n        count\n    member c.Clear() =\n        for set in sets do\n            set.Clear()\n    member c.AddGroup(groupId, anims) =\n        let group =\n            anims\n            |> Seq.map (fun anim ->\n                let set = ParticleSet(anim)\n                sets.Add(set)\n                set)\n            |> Seq.toArray\n        while groups.Count <= groupId do\n            groups.Add(Array.empty)\n        groups.[groupId] <- group\n        // Track the set of layers we're using\n        for anim in anims do\n            layerDescriptors.Add(anim.Layer.Untyped) |> ignore\n    member c.Emit(emission : ParticleEmission) =\n        if emission.GroupId < groups.Count then\n            let group = groups.[emission.GroupId]\n            for set in group do\n                set.Emit(emission)\n    member c.Update(deltaTime) =\n        for set in sets do\n            set.Update(deltaTime)\n    member c.Draw(canvas : SpriteRenderer, atlas) =\n        for set in sets do\n            set.Draw(canvas, atlas)\n        // If not automatically flushing on draw, we need to manually do so here. This\n        // assume layers are used exclusively for particles, otherwise they would overwrite\n        // anything previously flushed to the same buffer\n        for desc in layerDescriptors do\n            match desc.FlushMode with\n            | NoFlush -> canvas.Flush(desc.LayerId)\n            | FlushOnDraw -> ()\n\ntype ParticleSystem with\n    member c.AddGroups(groups) =\n        for group  in groups do\n            c.AddGroup(group.GroupId, group.Animations)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Picking.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Buffers\nopen System.Numerics\nopen Garnet.Numerics\n\n[<Struct>]\ntype PickLayerDescriptor = {\n    LayerId : int\n    CameraId : int\n    Primitive : Primitive\n    FlushMode : SpriteFlushMode\n    }\n\n[<Struct>]\ntype PickResult = {\n    LayerId : int\n    PrimitiveIndex : int\n    WorldPosition : Vector2\n    }\n\n[<Struct>]\ntype PickResult<'a> = {\n    Param : 'a\n    LayerId : int\n    PrimitiveIndex : int\n    }\n\ntype VertexPicking() =\n    /// Returns index of first vertex contained in rect\n    static member TryPickPoint(span : ReadOnlySpan<Vector2>, rect : Range2) = \n        let mutable result = ValueNone\n        let mutable i = 0\n        while result.IsNone && i < span.Length do\n            let v = span.[i]\n            if rect.Contains(v) then\n                result <- ValueSome i\n            i <- i + 1\n        result\n\n    /// Returns index of first triangle containing point\n    static member TryPickTriangle(span : ReadOnlySpan<Vector2>, p : Vector2) = \n        let mutable result = ValueNone\n        let mutable i = 0\n        while result.IsNone && i < span.Length do\n            let v0 = span.[i + 0]\n            let v1 = span.[i + 1]\n            let v2 = span.[i + 2]\n            if p.IsInTriangle(v0, v1, v2) then\n                result <- ValueSome (i / 3)\n            i <- i + 3\n        result\n        \n    /// Returns index of first quad containing point\n    static member TryPickQuad(span : ReadOnlySpan<Vector2>, p : Vector2) = \n        let mutable result = ValueNone\n        let mutable i = 0\n        while result.IsNone && i < span.Length do\n            let v0 = span.[i + 0]\n            let v1 = span.[i + 1]\n            let v2 = span.[i + 2]\n            let v3 = span.[i + 3]\n            if p.IsInTriangle(v0, v1, v2) || p.IsInTriangle(v0, v2, v3) then\n                result <- ValueSome (i / 4)\n            i <- i + 4\n        result\n\ntype IPickLayer =\n    abstract Descriptor : PickLayerDescriptor\n    abstract WrittenVertexSpan : ReadOnlySpan<Vector2>\n    abstract Clear : unit -> unit\n \ntype PickLayer<'a>(desc : PickLayerDescriptor) =\n    let values = ArrayBufferWriter<'a>()\n    let vertices = ArrayBufferWriter<Vector2>()\n    member c.Descriptor = desc\n    member c.Values = values :> IBufferWriter<'a>\n    member c.VertexWriter = vertices :> IBufferWriter<Vector2>\n    member c.WrittenValueSpan = values.WrittenSpan\n    member c.WrittenVertexSpan = vertices.WrittenSpan\n    member c.Clear() =\n        values.Clear()\n        vertices.Clear()\n    interface IPickLayer with\n        member c.Descriptor = desc\n        member c.WrittenVertexSpan = c.WrittenVertexSpan\n        member c.Clear() = c.Clear()\n\ntype PickLayerSet() =\n    let layers = List<IPickLayer voption>()\n    member c.GetLayer<'a>(desc : PickLayerDescriptor) =\n        while layers.Count <= desc.LayerId do\n            layers.Add(ValueNone)\n        match layers.[desc.LayerId] with\n        | ValueNone ->\n            let layer = PickLayer<'a>(desc)\n            layers.[desc.LayerId] <- ValueSome (layer :> IPickLayer)\n            layer\n        | ValueSome layer -> layer :?> PickLayer<'a>\n    member c.GetValue<'a>(layerId, primitiveIndex) =\n        let layer = layers.[layerId].Value :?> PickLayer<'a>\n        layer.WrittenValueSpan.[primitiveIndex]\n    member c.TryPick(cameras : CameraSet, layerId, normPos : Vector2) =\n        if layerId >= layers.Count then ValueNone\n        else\n            match layers.[layerId] with\n            | ValueNone -> ValueNone\n            | ValueSome layer ->\n                let span = layer.WrittenVertexSpan\n                let viewport = cameras.[layer.Descriptor.CameraId]\n                let worldPos = viewport.NormalizedToWorld(normPos)\n                let primitiveResult =\n                    match layer.Descriptor.Primitive with\n                    | Triangle -> VertexPicking.TryPickTriangle(span, worldPos)\n                    | Quad -> VertexPicking.TryPickQuad(span, worldPos)\n                match primitiveResult with\n                | ValueNone -> ValueNone\n                | ValueSome index ->\n                    ValueSome {\n                        LayerId = layerId\n                        PrimitiveIndex = index\n                        WorldPosition = worldPos\n                        }\n    /// Returns index of primitive containing point, if any\n    member c.TryPick(cameras : CameraSet, normPos : Vector2) =\n        let mutable result = ValueNone\n        let mutable i = layers.Count - 1\n        while result.IsNone && i >= 0 do\n            result <- c.TryPick(cameras, i, normPos)\n            i <- i - 1\n        result\n    member c.PickAll(param, cameras : CameraSet, layerId, normRect : Range2, action) =\n        if layerId < layers.Count then\n            match layers.[layerId] with\n            | ValueNone -> ()\n            | ValueSome layer ->\n                let span = layer.WrittenVertexSpan\n                let vertsPerPrimitive = Primitive.GetVertexCount(layer.Descriptor.Primitive)\n                let viewport = cameras.[layer.Descriptor.CameraId]\n                let worldRect = viewport.NormalizedToWorld(normRect)\n                // Scan vertices\n                let mutable vi = 0\n                while vi < span.Length do\n                    let v = span.[vi]\n                    if worldRect.Contains(v) then\n                        action {\n                            Param = param\n                            LayerId = layerId\n                            PrimitiveIndex = vi / vertsPerPrimitive\n                            }\n                    vi <- vi + 1\n    /// Returns index of primitive with a vertex contained within rect, if any\n    member c.PickAll(param, cameras : CameraSet, normRect : Range2, action) =\n        let mutable i = layers.Count - 1\n        while i >= 0 do\n            c.PickAll(param, cameras, i, normRect, action)\n            i <- i - 1\n    member c.Flush() =\n        for i = 0 to layers.Count - 1 do\n            match layers.[i] with\n            | ValueNone -> ()\n            | ValueSome layer ->\n                match layer.Descriptor.FlushMode with\n                | NoFlush -> ()\n                | FlushOnDraw -> layer.Clear()\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Pipelines.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Veldrid\nopen Garnet.Composition\n\n[<Struct>]\ntype Blend =\n    | Additive = 0\n    | Alpha = 1\n    | Override = 2\n\n[<Struct>]\ntype Filtering =\n    | Point = 0\n    | Linear = 1\n\n[<Struct>]\ntype TexturePipelineDescriptor = {\n    Blend : Blend\n    Filtering : Filtering\n    ShaderSet : ShaderSetDescriptor\n    Texture : string\n    }\n\n[<Struct>]\ntype TexturePipelineDescriptor<'v\n                        when 'v : struct \n                        and 'v : (new : unit -> 'v) \n                        and 'v :> ValueType\n                        and 'v :> IVertex> = {\n    Blend : Blend\n    Filtering : Filtering\n    ShaderSet : ShaderSetDescriptor<'v>\n    Texture : string\n    } with\n    member c.Untyped : TexturePipelineDescriptor = {\n        Blend = c.Blend\n        Filtering = c.Filtering\n        ShaderSet = c.ShaderSet.Untyped\n        Texture = c.Texture\n        }\n\n[<AutoOpen>]\nmodule internal GraphicsDeviceExtensions =\n    type GraphicsDevice with\n        member c.GetSampler(filtering) =\n            match filtering with\n            | Filtering.Point -> c.PointSampler\n            | Filtering.Linear -> c.LinearSampler\n            | x -> failwith $\"Invalid filtering {x}\"\n\ntype ProjectionViewSet(device : GraphicsDevice, slot) =\n    let factory = device.ResourceFactory\n    let projBuffer =\n        device.ResourceFactory.CreateBuffer(\n            BufferDescription(uint32 sizeof<Matrix4x4>, BufferUsage.UniformBuffer))\n    let viewBuffer =\n        device.ResourceFactory.CreateBuffer(\n            BufferDescription(uint32 sizeof<Matrix4x4>, BufferUsage.UniformBuffer))\n    let projViewLayout = \n        factory.CreateResourceLayout(\n            ResourceLayoutDescription(\n                ResourceLayoutElementDescription(\n                    \"ProjectionBuffer\", \n                    ResourceKind.UniformBuffer, \n                    ShaderStages.Vertex),\n                ResourceLayoutElementDescription(\n                    \"ViewBuffer\", \n                    ResourceKind.UniformBuffer, \n                    ShaderStages.Vertex)))\n    let projViewSet = \n        factory.CreateResourceSet(\n            ResourceSetDescription(projViewLayout, projBuffer, viewBuffer))\n    member c.Layout = projViewLayout\n    member c.Apply(proj : Matrix4x4, view : Matrix4x4, cmds : CommandList) =\n        cmds.UpdateBuffer(projBuffer, 0u, proj)\n        cmds.UpdateBuffer(viewBuffer, 0u, view)\n        cmds.SetGraphicsResourceSet(uint32 slot, projViewSet)\n    member c.Dispose() =\n        projViewSet.Dispose()\n        projViewLayout.Dispose()\n        projBuffer.Dispose()\n        viewBuffer.Dispose()\n    interface IDisposable with \n        member c.Dispose() = c.Dispose()\n   \ntype WorldTextureSet(device : GraphicsDevice, surfaceTexture : Texture, slot, sampler) =\n    let factory = device.ResourceFactory\n    let worldBuffer = factory.CreateBuffer(BufferDescription(64u, BufferUsage.UniformBuffer))\n    let texTransformBuffer = factory.CreateBuffer(BufferDescription(64u, BufferUsage.UniformBuffer))\n    let worldTextureLayout = \n        factory.CreateResourceLayout(\n            ResourceLayoutDescription(\n                ResourceLayoutElementDescription(\n                    \"WorldBuffer\", \n                    ResourceKind.UniformBuffer, \n                    ShaderStages.Vertex),\n                ResourceLayoutElementDescription(\n                    \"TexTransformBuffer\", \n                    ResourceKind.UniformBuffer, \n                    ShaderStages.Vertex),\n                ResourceLayoutElementDescription(\n                    \"SurfaceTexture\", \n                    ResourceKind.TextureReadOnly, \n                    ShaderStages.Fragment),\n                ResourceLayoutElementDescription(\n                    \"SurfaceSampler\", \n                    ResourceKind.Sampler, \n                    ShaderStages.Fragment)))\n    let surfaceTextureView = factory.CreateTextureView(surfaceTexture)\n    let worldTextureSet =\n        factory.CreateResourceSet(\n            ResourceSetDescription(\n                worldTextureLayout,\n                worldBuffer,\n                texTransformBuffer,\n                surfaceTextureView,\n                sampler))\n    member c.Layout = worldTextureLayout\n    member c.Apply(world : Matrix4x4, texTransform : Matrix4x4, cmds : CommandList) =\n        cmds.UpdateBuffer(worldBuffer, 0u, world)\n        cmds.UpdateBuffer(texTransformBuffer, 0u, texTransform)\n        cmds.SetGraphicsResourceSet(uint32 slot, worldTextureSet)\n    member c.Dispose() =\n        worldTextureSet.Dispose()\n        worldTextureLayout.Dispose()\n        texTransformBuffer.Dispose()\n        worldBuffer.Dispose()\n        surfaceTextureView.Dispose()\n    interface IDisposable with \n        member c.Dispose() = c.Dispose()\n\ntype TextureTrianglePipeline(device, shaders : ShaderSet, texture : Texture, sampler, blendState, outputDesc) =\n    let projView = new ProjectionViewSet(device, 0)\n    let worldTexture = new WorldTextureSet(device, texture, 1, sampler)\n    let layouts = [| \n        projView.Layout\n        worldTexture.Layout \n        |]\n    let pipeline =\n        let desc =\n            GraphicsPipelineDescription(\n                BlendState = blendState,\n                DepthStencilState = DepthStencilStateDescription.Disabled,\n                RasterizerState =\n                    RasterizerStateDescription(\n                        cullMode = FaceCullMode.None,\n                        fillMode = PolygonFillMode.Solid,\n                        frontFace = FrontFace.Clockwise,\n                        depthClipEnabled = false,\n                        scissorTestEnabled = false),\n                PrimitiveTopology = PrimitiveTopology.TriangleList,\n                ResourceLayouts = layouts,\n                ShaderSet = shaders.Description,\n                Outputs = outputDesc)\n        device.ResourceFactory.CreateGraphicsPipeline(desc)\n    member c.Dispose() =\n        pipeline.Dispose()\n        projView.Dispose()\n        worldTexture.Dispose()\n    member c.SetPipeline(cmds : CommandList) =\n        cmds.SetPipeline(pipeline)\n    member c.SetProjectionView(projection, view, cmds) =\n        projView.Apply(projection, view, cmds)\n    member c.SetWorldTexture(world, texTransform, cmds) =\n        worldTexture.Apply(world, texTransform, cmds)\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\ntype TexturePipelineCache(device : GraphicsDevice,\n        shaderCache : ShaderSetCache,\n        cache : IResourceCache) =\n    let solidTexture = device.CreateTextureRgba(1, 1, ReadOnlyMemory(Array.create 4 255uy))\n    let pipelines = Dictionary<_, TextureTrianglePipeline>()\n    member c.GetPipeline(desc : TexturePipelineDescriptor, outputDesc) =\n        let key = struct(desc, outputDesc)\n        match pipelines.TryGetValue(key) with\n        | true, pipeline -> pipeline\n        | false, _ ->\n            let sampler = device.GetSampler(desc.Filtering)\n            let blend =\n                match desc.Blend with\n                | Blend.Additive -> BlendStateDescription.SingleAdditiveBlend\n                | Blend.Alpha -> BlendStateDescription.SingleAlphaBlend\n                | Blend.Override -> BlendStateDescription.SingleOverrideBlend\n                | x -> failwith $\"Invalid blend {x}\"\n            let shaders = shaderCache.GetOrCreate(device, desc.ShaderSet, cache)\n            let texture =\n                // Use a solid white texture as a fallback when none specified\n                if String.IsNullOrEmpty(desc.Texture) then solidTexture\n                else cache.LoadResource<Texture>(desc.Texture)\n            let pipeline = new TextureTrianglePipeline(device, shaders, texture, sampler, blend, outputDesc)\n            pipelines.Add(key, pipeline)\n            pipeline\n    member c.Dispose() =\n        solidTexture.Dispose()\n        for pipeline in pipelines.Values do\n            pipeline.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Rendering.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen Veldrid\n\n// Allow auto invalidate to avoid requiring extra call when every frame\n// is considered invalidated (for uses of manual mode, consider simple\n// turn-based games without animation or tools that don't need to be\n// constantly drawing.\ntype Redraw =\n    | Manual = 0\n    | Auto = 1\n\ntype RenderContext(commands : CommandList) =\n    let frameBuffers = Stack<Framebuffer>()\n    member c.Commands = commands\n    member c.OutputDescription =\n        frameBuffers.Peek().OutputDescription\n    member c.PushFramebuffer(framebuffer) =\n        frameBuffers.Push(framebuffer)\n        commands.SetFramebuffer(framebuffer)\n    member c.PopFramebuffer() =\n        frameBuffers.Pop() |> ignore\n        if frameBuffers.Count > 0 then\n            commands.SetFramebuffer(frameBuffers.Peek())\n        \ntype Renderer(device : GraphicsDevice, redraw) =\n    let commands = device.ResourceFactory.CreateCommandList()\n    let context = RenderContext(commands)\n    let mutable width = 0\n    let mutable height = 0\n    let mutable valid = false\n    member c.Context = context\n    member c.Invalidate() =\n        valid <- false\n    member c.BeginDraw(newWidth : int, newHeight : int, bgColor) =\n        let resized = newWidth <> width || newHeight <> height \n        if resized then\n            device.ResizeMainWindow(uint32 newWidth, uint32 newHeight)\n            width <- newWidth\n            height <- newHeight\n        // Only proceed if something to redraw\n        if valid && not resized then false\n        else\n            commands.Begin()        \n            // We want to render directly to the output window\n            context.PushFramebuffer(device.SwapchainFramebuffer)\n            // Clear viewports\n            commands.ClearColorTarget(0u, bgColor)\n            // Mark valid if using manual redraw\n            match redraw with\n            | Redraw.Auto -> ()\n            | Redraw.Manual | _ -> valid <- true           \n            true\n    member c.EndDraw() =\n        // End() must be called before commands can be submitted for execution.\n        commands.End()\n        device.SubmitCommands(commands)\n        // Once commands have been submitted, the rendered image can be presented to \n        // the application window.\n        device.SwapBuffers()\n        context.PopFramebuffer()\n    member c.Dispose() =\n        device.WaitForIdle()\n        commands.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Requests.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System.Collections.Generic\nopen Garnet.Composition.Comparisons\nopen Garnet.Composition\n\n/// Useful for linking response messages back to their corresponding\n/// request message.\n[<Struct>]\ntype RequestId = RequestId of uint64\n    with static member Undefined = RequestId 0UL\n\n[<AutoOpen>]\nmodule RequestTrackingExtensions =\n    type private RequestTracker() =\n        let requests = Dictionary<RequestId, int>()\n        let mutable count = 0UL\n        member c.Clear() =\n            requests.Clear()\n        member c.Create(waitingCount) =\n            // start at one\n            count <- count + 1UL\n            let requestId = RequestId count\n            requests.Add(requestId, waitingCount)\n            requestId\n        member c.Complete(requestId) =\n            match requests.TryGetValue(requestId) with\n            | false, _ -> false\n            | true, waitingCount ->\n                if waitingCount = 1 then                     \n                    // if this is the last response, request is complete\n                    requests.Remove(requestId) |> ignore\n                    true\n                else\n                    // otherwise decrement counter\n                    requests.[requestId] <- waitingCount - 1\n                    false            \n        \n    type Container with\n        member c.CreateRequest(waitingCount) =\n            c.Get<RequestTracker>().Create(waitingCount)\n\n        member c.ClearRequests() =\n            c.Get<RequestTracker>().Clear()\n\n        member c.CompleteRequest(requestId) =\n            c.Get<RequestTracker>().Complete(requestId)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Serialization.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen Microsoft.FSharp.Reflection\nopen Newtonsoft.Json\nopen Veldrid\nopen Garnet.Numerics\n\nmodule private JsonSerialization =\n//    type RgbaFloatConverter() =\n//        inherit JsonConverter<RgbaFloat>()\n//        override _.WriteJson(writer : JsonWriter, value : RgbaFloat, _ : JsonSerializer) =\n//            writer.WriteValue(value.ToRgbaByte().ToString())\n//        override _.ReadJson(reader, objectType, existingValue, hasExistingValue, serializer) =\n//            let s = string reader.Value\n//            RgbaFloat.Parse s\n            \n    // http://gorodinski.com/blog/2013/01/05/json-dot-net-type-converters-for-f-option-list-tuple/\n    type OptionConverter() =\n        inherit JsonConverter() \n        override x.CanConvert(t) = \n            t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>\n        override x.WriteJson(writer, value, serializer) =\n            let value = \n                if value = null then null\n                else \n                    let _,fields = FSharpValue.GetUnionFields(value, value.GetType())\n                    fields.[0]  \n            serializer.Serialize(writer, value)\n        override x.ReadJson(reader, t, existingValue, serializer) =        \n            let innerType = t.GetGenericArguments().[0]\n            let innerType = \n                if innerType.IsValueType then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])\n                else innerType        \n            let value = serializer.Deserialize(reader, innerType)\n            let cases = FSharpType.GetUnionCases(t)\n            if value = null then FSharpValue.MakeUnion(cases.[0], [||])\n            else FSharpValue.MakeUnion(cases.[1], [|value|])\n            \n    let defaultSettings =\n        let settings =\n            JsonSerializerSettings(\n                Formatting = Formatting.Indented,\n                NullValueHandling = NullValueHandling.Ignore)\n        settings.Converters.Add(OptionConverter())\n        //settings.Converters.Add(RgbaFloatConverter())\n        settings\n        \n[<AutoOpen>]\nmodule LoadingExtensions =\n    type IStreamSource with\n        member c.LoadText(key) =\n            use stream = c.Open(key)\n            use reader = new StreamReader(stream)\n            reader.ReadToEnd()\n                \n        member c.LoadJson<'a>(key, settings : JsonSerializerSettings) =\n            let json = c.LoadText(key)\n            try JsonConvert.DeserializeObject<'a>(json, settings)\n            with ex -> raise (exn($\"Could not load JSON from {key} as {typeof<'a>.Name}\", ex))\n\n        member c.LoadJson<'a> key =\n            c.LoadJson<'a>(key, JsonSerialization.defaultSettings)\n\n        member c.LoadJson<'a>(key, cache : IResourceCache, settings) =\n            let resource = c.LoadJson<'a>(key, settings)\n            cache.AddResource(key, resource)\n\n        member c.LoadJson<'a>(key, cache) =\n            c.LoadJson<'a>(key, cache, JsonSerialization.defaultSettings)\n\ntype TextLoader() =\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            cache.AddResource(key, folder.LoadText(key))\n\ntype JsonLoader<'a>(settings) =\n    new() = JsonLoader<'a>(JsonSerialization.defaultSettings)\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            folder.LoadJson<'a>(key, cache, settings)\n\n[<AutoOpen>]\nmodule LoaderExtensions =\n    type ResourceCache with\n        member c.AddTextLoaders() =\n            c.AddLoader(\".json\", TextLoader())\n            c.AddLoader(\".txt\", TextLoader())\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Shaders.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.Text\nopen Veldrid\nopen Garnet.Composition\nopen Veldrid.SPIRV\n\ntype IVertex = \n    abstract Layout : VertexLayoutDescription\n\ntype Vertex<'v when 'v : struct and 'v :> IVertex> =\n    static member Layout = Unchecked.defaultof<'v>.Layout\n\n[<Struct>]\ntype ShaderSetDescriptor = {\n    VertexShader : string\n    FragmentShader : string\n    Layout : VertexLayoutDescription\n    }\n\n[<Struct>]\ntype ShaderSetDescriptor<'v\n                when 'v : struct \n                and 'v : (new : unit -> 'v) \n                and 'v :> ValueType\n                and 'v :> IVertex> = {\n    VertexShader : string\n    FragmentShader : string\n    } with\n    member c.Untyped = {\n        VertexShader = c.VertexShader\n        FragmentShader = c.FragmentShader\n        Layout = Vertex<'v>.Layout\n        } \n\ntype ShaderSet(device : GraphicsDevice, \n                vert : ShaderDescription, \n                frag : ShaderDescription,\n                layout : VertexLayoutDescription,\n                isCompiled) =\n    let shaders =\n        try\n            if not isCompiled then device.ResourceFactory.CreateFromSpirv(vert, frag)\n            else\n                let vsCode = vert.ShaderBytes\n                let fsCode = frag.ShaderBytes\n                let vertexShader = device.ResourceFactory.CreateShader(ShaderDescription(vert.Stage, vsCode, vert.EntryPoint))\n                let fragmentShader = device.ResourceFactory.CreateShader(ShaderDescription(frag.Stage, fsCode, frag.EntryPoint))\n                [| vertexShader; fragmentShader |]\n        with ex ->\n            let msg =\n                let vertStr = Encoding.UTF8.GetString(vert.ShaderBytes)\n                let fragStr = Encoding.UTF8.GetString(frag.ShaderBytes) \n                \"Could not create shaders:\\n\" +\n                $\"Vertex ({vert.ShaderBytes.Length} bytes):\\n{vertStr}\\n\" +\n                $\"Fragment ({frag.ShaderBytes.Length} bytes):\\n{fragStr}\"\n            raise (Exception(msg, ex))\n    member c.Description =\n        ShaderSetDescription(\n            vertexLayouts = [| layout |],\n            shaders = shaders)\n    member c.Dispose() =\n        for shader in shaders do\n            shader.Dispose()\n    interface IDisposable with\n        member c.Dispose() =\n            c.Dispose()\n\nmodule private ShaderFileExtension =\n    let stages = [|\n        \".vert\", ShaderStages.Vertex\n        \".tesc\", ShaderStages.TessellationControl\n        \".tese\", ShaderStages.TessellationEvaluation\n        \".geom\", ShaderStages.Geometry\n        \".frag\", ShaderStages.Fragment\n        \".comp\", ShaderStages.Compute\n        |]\n    \n    let extensionFilter =\n        stages\n        |> Array.map (fun (extension, _) -> \"*\" + extension)\n        |> String.concat \",\"\n\n    let extensionToStage =\n        let dict = Dictionary<string, ShaderStages>()\n        for extension, stage in stages do\n            dict.[extension] <- stage\n        dict :> IReadOnlyDictionary<_,_>\n\n[<AutoOpen>]\nmodule ShaderExtensions =\n    type Shader with\n        static member GetCompilationTarget(backend) =\n            match backend with\n            | GraphicsBackend.Direct3D11 -> CrossCompileTarget.HLSL\n            | GraphicsBackend.OpenGL -> CrossCompileTarget.GLSL\n            | GraphicsBackend.Metal -> CrossCompileTarget.MSL\n            | GraphicsBackend.OpenGLES -> CrossCompileTarget.ESSL\n            | _ -> raise (SpirvCompilationException($\"Invalid GraphicsBackend: {backend}\"))\n\n        static member GetBytecodeExtension(backend) =\n            match backend with\n            | GraphicsBackend.Direct3D11 -> \".hlsl.bytes\"\n            | GraphicsBackend.Vulkan -> \".spv\"\n            | GraphicsBackend.OpenGL\n            | GraphicsBackend.OpenGLES -> raise (InvalidOperationException(\"OpenGL and OpenGLES do not support shader bytecode.\"))\n            | _ -> raise (Exception($\"Invalid GraphicsBackend: {backend}\"))    \n\n        static member GetShaderBytes(backend, code : string) =\n            match backend with\n            | GraphicsBackend.Direct3D11\n            | GraphicsBackend.OpenGL\n            | GraphicsBackend.OpenGLES -> Encoding.ASCII.GetBytes(code)\n            | GraphicsBackend.Metal -> Encoding.UTF8.GetBytes(code)\n            | _ -> raise (Exception($\"Invalid GraphicsBackend: {backend}\"))\n\n        static member TryGetStage(extension) =\n            match ShaderFileExtension.extensionToStage.TryGetValue(extension) with\n            | true, stage -> ValueSome stage\n            | false, _ -> ValueNone//raise (Exception($\"Invalid extension: {extension}\"))\n\n        static member Compile(vert, frag, backend) =\n            let target = Shader.GetCompilationTarget(backend)\n            let result = SpirvCompilation.CompileVertexFragment(vert, frag, target)\n            let vsCode = Shader.GetShaderBytes(backend, result.VertexShader)\n            let fsCode = Shader.GetShaderBytes(backend, result.FragmentShader)\n            (vsCode, fsCode)\n\ntype ShaderSetCache() =\n    let cache = Dictionary<ShaderSetDescriptor, ShaderSet>()\n    member c.TryGet(desc) =\n        match cache.TryGetValue(desc) with\n        | true, x -> ValueSome x\n        | false, _ -> ValueNone\n    member c.Add(name, shaderSet) =\n        cache.Add(name, shaderSet)\n    member c.Dispose() =\n        for set in cache.Values do\n            set.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n    \ntype ShaderResource = {\n    Description : ShaderDescription\n    IsCompiled : bool\n    } with\n    static member FromStream(stage, isCompiled, stream : Stream) =\n        let ms = new MemoryStream()\n        stream.CopyTo(ms)\n        {\n            Description = ShaderDescription(stage, ms.ToArray(), \"main\")\n            IsCompiled = isCompiled\n        }\n\ntype ShaderLoader(backend : GraphicsBackend, stage) =\n    interface IResourceLoader with\n        /// Key should be the base shader without backend-specific extension, e.g. shader.vert\n        member c.Load(folder, cache, key) =\n            // First look for a compiled shader with the backend-specific extension\n            let extension = Shader.GetBytecodeExtension(backend)\n            let bytecodePath = key + extension\n            let result = folder.TryOpen(bytecodePath)\n            use stream = \n                match result with\n                | ValueSome x -> x\n                | ValueNone ->\n                    // If backend-specific file was not found, fallback to original file\n                    folder.Open(key)\n            let resource = ShaderResource.FromStream(stage, result.IsSome, stream)\n            cache.AddResource<ShaderResource>(key, resource)\n    \n[<AutoOpen>]\nmodule ShaderLoadingExtensions =\n    type IReadOnlyFolder with\n        /// Key should be the base shader without backend-specific extension, e.g. shader.vert\n        member c.LoadShader(key : string, backend : GraphicsBackend, cache : IResourceCache) =\n            let extension = Path.GetExtension(key)\n            match Shader.TryGetStage(extension) with\n            | ValueNone -> ()\n            | ValueSome stage ->\n                let loader = ShaderLoader(backend, stage) :> IResourceLoader\n                loader.Load(c, cache, key)\n            \n        member c.LoadShadersFromFolder(path, backend, cache : IResourceCache) =\n            for key in c.GetFiles(path) do\n                c.LoadShader(key, backend, cache)\n\n[<AutoOpen>]\nmodule ShaderLoaderExtensions =\n    type ResourceCache with\n        member c.AddShaderLoaders(device : GraphicsDevice) =\n            for extension, stage in ShaderFileExtension.stages do\n                c.AddLoader(extension, ShaderLoader(device.BackendType, stage))\n            \n    type ShaderSetCache with\n        member c.GetOrCreate(device, desc, cache : IResourceCache) =\n            match c.TryGet(desc) with\n            | ValueSome x -> x\n            | ValueNone ->\n                let vert = cache.LoadResource<ShaderResource>(desc.VertexShader)\n                let frag = cache.LoadResource<ShaderResource>(desc.FragmentShader)\n                if vert.IsCompiled <> frag.IsCompiled then\n                    failwith $\"Shaders must both be GLSL or compiled for the same backend: {desc.VertexShader}, {desc.FragmentShader}\"\n                let set = new ShaderSet(device, vert.Description, frag.Description, desc.Layout, vert.IsCompiled)\n                c.Add(desc, set)\n                set\n\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Sprites.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Numerics\n\n[<Struct>]    \ntype Primitive =\n    | Triangle\n    | Quad\n    \ntype Primitive with\n    static member GetVertexCount(primitive) =\n        match primitive with\n        | Triangle -> 3\n        | Quad -> 4\n\n[<Struct>]    \ntype SpriteFlushMode =\n    | NoFlush\n    | FlushOnDraw\n\n[<Struct>]\ntype SpriteLayerDescriptor = {\n    LayerId : int\n    CameraId : int\n    Primitive : Primitive\n    Pipeline : TexturePipelineDescriptor\n    FlushMode : SpriteFlushMode\n    } \n\n[<Struct>]\ntype SpriteLayerDescriptor<'v\n                    when 'v : struct \n                    and 'v : (new : unit -> 'v) \n                    and 'v :> ValueType\n                    and 'v :> IVertex> = {\n    LayerId : int\n    CameraId : int\n    Primitive : Primitive\n    Pipeline : TexturePipelineDescriptor<'v>\n    FlushMode : SpriteFlushMode\n    } with\n    member c.Untyped : SpriteLayerDescriptor = {\n        LayerId = c.LayerId\n        CameraId = c.CameraId\n        Primitive = c.Primitive\n        Pipeline = c.Pipeline.Untyped\n        FlushMode = c.FlushMode\n        }\n\ntype Camera() =\n    member val WorldTransform = Matrix4x4.Identity with get, set\n    member val TextureTransform = Matrix4x4.Identity with get, set\n    member val ViewTransform = Matrix4x4.Identity with get, set\n    member val ProjectionTransform = Matrix4x4.Identity with get, set\n    member c.GetNormalizedToWorld() =\n        let projView = c.ViewTransform * c.ProjectionTransform \n        projView.GetInverseOrIdentity()\n    member c.NormalizedToWorld(normPos : Vector2) =\n        let xf = c.GetNormalizedToWorld()\n        Vector2.Transform(normPos, xf)\n    member c.NormalizedToWorld(rect : Range2) =\n        let xf = c.GetNormalizedToWorld()\n        let p0 = Vector2.Transform(rect.Min, xf)\n        let p1 = Vector2.Transform(rect.Max, xf)\n        Range2.Union(Range2.Point(p0), Range2.Point(p1))\n    \ntype CameraSet() =\n    let cameras = List<Camera>()\n    member c.Item with get i =\n        while cameras.Count <= i do\n            cameras.Add(Camera())\n        cameras.[i]\n        \n[<Struct>]\ntype private SpriteLayer = {\n    Descriptor : SpriteLayerDescriptor\n    Vertices : IVertexBuffer\n    }\n\ntype SpriteRenderer(device, shaders, cache) =\n    let indexes = new QuadIndexBuffer(device)\n    let pipelines = new TexturePipelineCache(device, shaders, cache)\n    let layers = List<SpriteLayer voption>()\n    member c.VertexCount =\n        let mutable count = 0\n        for layer in layers do\n            match layer with\n            | ValueNone -> ()\n            | ValueSome layer ->\n                count <- count + layer.Vertices.VertexCount\n        count\n    member c.GetVertices<'v\n                when 'v : struct \n                and 'v : (new : unit -> 'v) \n                and 'v :> ValueType\n                and 'v :> IVertex>(desc : SpriteLayerDescriptor<'v>) =\n        while layers.Count <= desc.LayerId do\n            layers.Add(ValueNone)\n        match layers.[desc.LayerId] with\n        | ValueNone ->\n            let vertices = new VertexBuffer<'v>(device)\n            let layer = {\n                Descriptor = desc.Untyped\n                Vertices = vertices\n                }\n            layers.[desc.LayerId] <- ValueSome layer\n            vertices\n        | ValueSome layer -> layer.Vertices :?> VertexBuffer<'v>\n    member c.Flush(layerId) =\n        if layerId < layers.Count then\n            match layers.[layerId] with\n            | ValueSome layer -> layer.Vertices.Flush()\n            | ValueNone -> ()\n    member c.Draw(context : RenderContext, cameras : CameraSet) =\n        for layer in layers do\n            match layer with\n            | ValueNone -> ()\n            | ValueSome layer ->\n                let desc = layer.Descriptor\n                let vertices = layer.Vertices\n                // Flush if needed\n                match desc.FlushMode with\n                | NoFlush -> ()\n                | FlushOnDraw -> vertices.Flush()\n                // Proceed if not empty\n                let vertexCount = vertices.VertexCount\n                if vertexCount > 0 then\n                    // Set shader params\n                    let camera = cameras.[desc.CameraId]\n                    let pipeline = pipelines.GetPipeline(desc.Pipeline, context.OutputDescription)\n                    pipeline.SetPipeline(context.Commands)\n                    pipeline.SetProjectionView(camera.ProjectionTransform, camera.ViewTransform, context.Commands)\n                    pipeline.SetWorldTexture(camera.WorldTransform, camera.TextureTransform, context.Commands)\n                    // Draw primitives\n                    vertices.SetVertexBuffer(context.Commands)\n                    match desc.Primitive with\n                    | Quad -> indexes.Draw(context.Commands, vertexCount / 4)\n                    | Triangle ->\n                        context.Commands.Draw(\n                            vertexCount = uint32 vertexCount,\n                            instanceCount = 1u,\n                            vertexStart = 0u,\n                            instanceStart = 0u)\n    member c.Dispose() =\n        for layer in layers do\n            match layer with\n            | ValueNone -> ()\n            | ValueSome layer ->            \n                layer.Vertices.Dispose()\n        pipelines.Dispose()\n        indexes.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Systems.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.IO\nopen System.Runtime.CompilerServices\nopen System.Reflection\nopen Veldrid\nopen Garnet.Input\nopen Garnet.Graphics\nopen Garnet.Audio\nopen Garnet.Composition\n\ntype AssetSettings = {\n    AssetPath : string\n    ArchiveExtension : string\n    } with\n    static member Default = {\n        AssetPath = \"assets\"\n        ArchiveExtension = \".dat\"\n        }\n\n[<Extension>]\ntype Systems =\n    [<Extension>]\n    static member AddAssetsFolder(c : Container) =\n        let folder =\n            let settings = c.GetOrDefault(AssetSettings.Default)\n            let path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)\n            let path = if String.IsNullOrEmpty(path) then \"\" else path\n            // First look for asset folder, then fallback to an archive file\n            let folderPath = Path.Combine(path, settings.AssetPath)\n            let archivePath = Path.Combine(path, settings.AssetPath + settings.ArchiveExtension)\n            if Directory.Exists(folderPath) then new FileFolder(folderPath) :> IReadOnlyFolder\n            elif File.Exists(archivePath) then new ZipArchiveFolder(archivePath) :> IReadOnlyFolder\n            else new FileFolder(path) :> IReadOnlyFolder\n        let cache = c.Get<ResourceCache>()\n        cache.SetFolder(folder)\n        c.Set<IReadOnlyFolder>(folder)\n        Disposable.Create [\n            folder :> IDisposable\n            ]\n        \n    [<Extension>]\n    static member AddTextLoaders(c : Container) =\n        let cache = c.Get<ResourceCache>()\n        cache.AddTextLoaders()\n        Disposable.Null\n\n    [<Extension>]\n    static member AddGraphicsDevice(c : Container) =\n        let settings = c.GetOrSetDefault(WindowSettings.Default)\n        let ren = new WindowRenderer(settings)\n        let shaders = new ShaderSetCache()\n        c.Set<WindowRenderer>(ren)\n        c.Set<GraphicsDevice>(ren.Device)\n        c.Set<RenderContext>(ren.RenderContext)\n        c.Set<ShaderSetCache>(shaders)\n        Disposable.Create [\n            ren :> IDisposable\n            shaders :> IDisposable\n            ]\n        \n    [<Extension>]\n    static member AddGraphicsLoaders(c : Container) =\n        let device = c.Get<GraphicsDevice>()\n        let cache = c.Get<ResourceCache>()\n        cache.AddShaderLoaders(device)\n        cache.AddTextureLoaders(device)\n        cache.AddFontLoaders()\n        Disposable.Null\n\n    [<Extension>]\n    static member AddWindowRendering(c : Container) =\n        Disposable.Create [\n            c.On<PreUpdate> <| fun e ->\n                let ren = c.Get<WindowRenderer>()\n                let inputs = c.Get<InputCollection>()\n                let deltaTime = float32 e.Update.DeltaTime / 1000.0f\n                let isRunning = ren.Update(deltaTime, inputs)\n                if not isRunning then\n                    c.Send(Closing())\n            c.On<PostUpdate> <| fun e ->\n                let ren = c.Get<WindowRenderer>()\n                c.Start <| seq {\n                    let draw = {\n                        Update = e.Update\n                        ViewSize = ren.Size\n                        }\n                    yield c.Wait<Draw>(draw)\n                    // This check must come after since the drawing stage may\n                    // have invalidated renderer, causing check to pass.\n                    if ren.BeginDraw() then\n                        yield c.Wait(PushDrawCommands())\n                        ren.EndDraw()\n                    }\n            ]\n        \n    [<Extension>]\n    static member AddInputPublishing(c : Container) =\n        Disposable.Create [\n            c.On<HandleInput> <| fun _ ->\n                let inputs = c.Get<InputCollection>()\n                for e in inputs.KeyUpEvents do\n                    c.Send<KeyUp> e\n                for e in inputs.KeyDownEvents do\n                    c.Send<KeyDown> e\n                c.Send<MouseMoved> { \n                    devicePos = inputs.MousePosition\n                    deviceDelta = inputs.MouseDelta\n                    pos = inputs.NormalizedMousePosition\n                    delta = inputs.NormalizedMouseDelta\n                    modifiers = inputs.Modifiers\n                    }\n                if abs inputs.WheelDelta > 0.0f then\n                    c.Send<MouseWheel> { \n                        modifiers = int inputs.Modifiers\n                        wheel = int inputs.WheelDelta\n                    }\n                for e in inputs.MouseDownEvents do\n                    c.Send<MouseDown> { \n                        devicePos = inputs.MousePosition\n                        pos = inputs.NormalizedMousePosition\n                        button = e\n                        modifiers = inputs.Modifiers\n                        }\n                for e in inputs.MouseUpEvents do\n                    c.Send<MouseUp> { \n                        devicePos = inputs.MousePosition\n                        pos = inputs.NormalizedMousePosition\n                        button = e\n                        }\n            ]\n        \n    [<Extension>]\n    static member AddSpriteDrawing(c : Container) =\n        let sprites =\n            let device = c.Get<GraphicsDevice>()\n            let shaders = c.Get<ShaderSetCache>()\n            let cache = c.Get<ResourceCache>()\n            new SpriteRenderer(device, shaders, cache)\n        c.Set<SpriteRenderer>(sprites)\n        Disposable.Create [\n            sprites :> IDisposable\n            c.On<PushDrawCommands> <| fun _ ->\n                let context = c.Get<RenderContext>()\n                let cameras = c.Get<CameraSet>()\n                sprites.Draw(context, cameras)\n            ]\n\n    [<Extension>]\n    static member AddAudioDevice(c : Container) =\n        let device = new AudioDevice()\n        c.Set<AudioDevice>(device)\n        Disposable.Create [\n            device :> IDisposable\n            ]\n        \n    [<Extension>]\n    static member AddAudioLoaders(c : Container) =\n        let device = c.Get<AudioDevice>()\n        let cache = c.Get<ResourceCache>()\n        cache.AddAudioLoaders(device)\n        Disposable.Null\n\n    [<Extension>]\n    static member AddTickUpdate(c : Container) =\n        let settings = c.GetOrSetDefault(TimingSettings.Default)\n        let timer = FixedUpdateTimer(settings)\n        Disposable.Create [\n            c.On<Start> <| fun _ ->\n                // Send initial request for tick\n                let settings = c.Get<TimingSettings>()\n                if settings.ClockActorId.IsDefined then\n                    c.Send<Schedule>(settings.ClockActorId, {\n                        DueTime = settings.MinDeltaTime\n                        })\n            c.On<Tick> <| fun e ->\n                c.Start <| seq {\n                    let settings = c.Get<TimingSettings>()\n                    timer.SetSettings(settings)\n                    timer.SetTime(e.Time)\n                    let update = timer.TryTakeUpdate()\n                    match update with\n                    | ValueNone -> ()\n                    | ValueSome e ->\n                        // Handle input\n                        yield c.Wait<PreUpdate> { Update = e }\n                        yield c.Wait<HandleInput> { Time = e.Time } \n                    // Run fixed updates\n                    while timer.HasFixedUpdate do\n                        let e = timer.TakeFixedUpdate()\n                        yield c.Wait<FixedUpdate>(e)\n                    match update with\n                    | ValueNone -> ()\n                    | ValueSome e ->\n                        // Run variable update\n                        c.Step(e.DeltaTime)\n                        yield c.Wait<Update>(e)\n                        yield c.Wait<PostUpdate> { Update = e }\n                    // Request next update\n                    if settings.ClockActorId.IsDefined then\n                        c.Send<Schedule>(settings.ClockActorId, {\n                            DueTime = e.Time + settings.MinDeltaTime\n                            })\n                    }\n            ]\n\n    [<Extension>]\n    static member AddGraphics(c : Container) =\n        Disposable.Create [\n            c.AddGraphicsDevice()\n            c.AddGraphicsLoaders()\n            c.AddWindowRendering()\n            c.AddSpriteDrawing()\n            c.AddInputPublishing()\n            ]\n        \n    [<Extension>]\n    static member AddDefaultSystems(c : Container) =\n        Disposable.Create [\n            c.AddAssetsFolder()\n            c.AddTextLoaders()\n            c.AddGraphics()\n            c.AddAudioDevice()\n            c.AddAudioLoaders()\n            c.AddTickUpdate()\n            ]\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Textures.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Veldrid\nopen SixLabors.ImageSharp\nopen SixLabors.ImageSharp.PixelFormats\nopen Garnet.Numerics\nopen Garnet.Composition\n\ntype TextureAtlasEntry = {\n    Bounds : Range2i\n    NormalizedBounds : Range2\n    }\n\nmodule internal TextureLoading =\n    let getMipSize original mipLevel =\n        original >>> mipLevel |> max 1\n    \n    let getFormatSize format =\n        match format with\n        | PixelFormat.R8_G8_B8_A8_UNorm -> 4\n        | PixelFormat.BC3_UNorm -> 1\n        | _ -> failwithf $\"Unsupported format %A{format}\"\n\n[<AutoOpen>]\nmodule private Packing =\n    let isContainedIn (a : Range2i) (b : Range2i) =\n        a.X.Min >= b.X.Min && \n        a.Y.Min >= b.Y.Min &&\n        a.X.Max <= b.X.Max &&\n        a.Y.Max <= b.Y.Max\n    \n    let isOverlapping (a : Range2i) (b : Range2i) =\n        a.X.Min >= b.X.Max ||\n        a.X.Max <= b.X.Min ||\n        a.Y.Min >= b.Y.Max ||\n        a.Y.Max <= b.Y.Min\n\n// http://wiki.unity3d.com/index.php?title=MaxRectsBinPack\ntype MaxRectsBinPack(size : Vector2i) =\n    let w = max 0 size.X\n    let h = max 0 size.Y\n    let n = Range2i(Vector2i.Zero, Vector2i(w, h))\n    let usedRects = List<string * Range2i>()\n    let freeRects = List<Range2i>([ n ]) \n    member c.Entries = \n        usedRects :> seq<_>\n    member c.Insert(key, size : Vector2i) =\n        if size.X <= 0 || size.Y <= 0 then None\n        else\n            match c.TryFindPositionForNewNodeBestAreaFit(size) with\n            | Some rect -> c.PlaceRect(key, rect) |> Some\n            | None -> None\n    member private c.PlaceRect(key, newNode) =\n        let mutable n = freeRects.Count\n        let mutable i = 0\n        while i < n do\n            if c.SplitFreeNode(freeRects.[i], newNode) then\n                freeRects.RemoveAt(i)\n                i <- i - 1\n                n <- n - 1\n            i <- i + 1\n        c.PruneFreeList()\n        usedRects.Add(key, newNode)\n        newNode\n    member private c.TryFindPositionForNewNodeBestAreaFit(size : Vector2i) = //(int width, int height, ref int bestAreaFit, ref int bestShortSideFit) \n        let mutable bestNode = None \n        let mutable bestAreaFit = Int64.MaxValue \n        let mutable bestShortSideFit = Int32.MaxValue \n        for rect in freeRects do\n            let areaFit = int64 rect.X.Size * int64 rect.Y.Size - int64 size.X * int64 size.Y \n            // Try to place the rectangle in upright (non-flipped) orientation.\n            if (rect.X.Size >= size.X && rect.Y.Size >= size.Y) then\n                let leftoverHoriz = abs (rect.X.Size - size.X)\n                let leftoverVert = abs (rect.Y.Size - size.Y)\n                let shortSideFit = min leftoverHoriz leftoverVert \n                if areaFit < bestAreaFit || (areaFit = bestAreaFit && shortSideFit < bestShortSideFit) then\n                    bestNode <- Some (Range2i.Sized(rect.Min, size))\n                    bestShortSideFit <- shortSideFit\n                    bestAreaFit <- areaFit                                      \n        bestNode \n    member private c.SplitFreeNode(free : Range2i, used : Range2i) =\n        // Test with SAT if the rectangles even intersect.\n        if isOverlapping used free then false\n        else \n            if (used.X.Min < free.X.Max && used.X.Max > free.X.Min) then\n                // New node at the top side of the used node.\n                if (used.Y.Min > free.Y.Min && used.Y.Min < free.Y.Max) then\n                    freeRects.Add(Range2i(free.X, (Rangei.Sized(free.Y.Min, used.Y.Min - free.Y.Min))))\n                // New node at the bottom side of the used node.\n                if (used.Y.Max < free.Y.Max) then\n                    freeRects.Add(Range2i(free.X, (Rangei.Sized(used.Y.Max, free.Y.Max - used.Y.Max))))\n            if (used.Y.Min < free.Y.Max && used.Y.Max > free.Y.Min) then\n                // New node at the left side of the used node.\n                if (used.X.Min > free.X.Min && used.X.Min < free.X.Max) then\n                    freeRects.Add(Range2i(Rangei.Sized(free.X.Min, used.X.Min - free.X.Min), free.Y))\n                // New node at the right side of the used node.\n                if (used.X.Max < free.X.Max) then\n                    freeRects.Add(Range2i(Rangei.Sized(used.X.Max, free.X.Max - used.X.Max), free.Y))\n            true \n    member private c.PruneFreeList() =\n        let mutable isDone = false\n        let mutable i = 0\n        while not isDone && i < freeRects.Count do\n            let mutable j = i + 1\n            while not isDone && j < freeRects.Count do\n                if isContainedIn freeRects.[i] freeRects.[j] then\n                    freeRects.RemoveAt(i)\n                    i <- i - 1\n                    isDone <- true                \n                elif isContainedIn freeRects.[j] freeRects.[i] then\n                    freeRects.RemoveAt(j)\n                    j <- j - 1\n                j <- j + 1\n            i <- i + 1\n\ntype TextureAtlas(size : Vector2i, entries : (string * Range2i) seq) =\n    let dict = \n        let size = size.ToVector2() \n        let dict = Dictionary<string, TextureAtlasEntry>()\n        for key, rect in entries do\n            let p0 = rect.Min.ToVector2() / size\n            let p1 = rect.Max.ToVector2() / size\n            let tex = {\n                Bounds = rect\n                NormalizedBounds = Range2(p0, p1)\n                }\n            dict.[key] <- tex\n            dict.[key.ToLowerInvariant()] <- tex\n        dict\n    member c.Size = size\n    member c.Item with get key =\n        match dict.TryGetValue(key) with\n        | false, _ -> failwith $\"Could not find {key} in atlas\"\n        | true, x -> x\n    member c.GetNormalizedBounds(key) =\n        match dict.TryGetValue(key) with\n        | false, _ -> Range2.Zero\n        | true, x -> x.NormalizedBounds\n\n[<AutoOpen>]\nmodule TextureExtensions =\n    type Texture with\n        member texture.Load(device : GraphicsDevice, desc : TextureDescription, data : ReadOnlyMemory<byte>) =\n            let factory = device.ResourceFactory\n            // create staging texture\n            use staging = \n                factory.CreateTexture(\n                    TextureDescription(\n                        desc.Width, desc.Height, desc.Depth, desc.MipLevels, \n                        desc.ArrayLayers, desc.Format, TextureUsage.Staging, \n                        desc.Type))\n            // copy from buffer to staging\n            use handle = data.Pin()\n            let formatSize = TextureLoading.getFormatSize desc.Format\n            let mutable offset = 0\n            for level = 0 to int desc.MipLevels - 1 do\n                let mipWidth = TextureLoading.getMipSize (int desc.Width) level\n                let mipHeight = TextureLoading.getMipSize (int desc.Height) level\n                let mipDepth = TextureLoading.getMipSize (int desc.Depth) level\n                let subresourceSize = mipWidth * mipHeight * mipDepth * formatSize\n                for layer = 0 to int desc.ArrayLayers - 1 do\n                    device.UpdateTexture(\n                        staging, IntPtr handle.Pointer + nativeint offset, uint32 subresourceSize,\n                        0u, 0u, 0u, uint32 mipWidth, uint32 mipHeight, uint32 mipDepth,\n                        uint32 level, \n                        uint32 layer)\n                    offset <- offset + subresourceSize\n            // copy from staging to final\n            use cl = factory.CreateCommandList()\n            cl.Begin()\n            cl.CopyTexture(staging, texture)\n            cl.End()\n            device.SubmitCommands(cl)\n            texture\n\n    type GraphicsDevice with\n        member device.CreateTexture(desc : TextureDescription, data) =\n            let texture = device.ResourceFactory.CreateTexture(desc)\n            texture.Load(device, desc, data)\n\n        member device.CreateTextureRgba(width, height, data) =\n            let desc = \n                TextureDescription(\n                    Width = uint32 width, \n                    Height = uint32 height, \n                    Depth = 1u, \n                    MipLevels = 1u, \n                    ArrayLayers = 1u, \n                    Format = PixelFormat.R8_G8_B8_A8_UNorm,\n                    Usage = TextureUsage.Sampled,\n                    Type = TextureType.Texture2D)\n            device.CreateTexture(desc, data)\n\n        member device.CreateTexture(image : Image<Rgba32>) =\n            let w = image.Width\n            let h = image.Height\n            let bytes = Array.zeroCreate<byte>(w * h * 4)\n            for y = 0 to h - 1 do\n                let row = image.GetPixelRowSpan(y)\n                let src = MemoryMarshal.Cast<Rgba32, byte>(row)\n                let dest = bytes.AsSpan().Slice(w * 4 * y, w * 4)\n                src.CopyTo(dest)\n            device.CreateTextureRgba(image.Width, image.Height, ReadOnlyMemory(bytes))\n\n        member device.CreateTextureAtlas(atlasWidth, atlasHeight, images : (string * Image<Rgba32>) seq) =\n            let padding = 1\n            let bpp = 4\n            // Pack textures into atlas buffer\n            let bytes = Array.zeroCreate<byte>(atlasWidth * atlasHeight * 4)\n            let packer = MaxRectsBinPack(Vector2i(atlasWidth, atlasHeight))\n            let getIndex x y = (y * atlasWidth + x) * bpp\n            let span = bytes.AsSpan()\n            for key, image in images do\n                let size = Vector2i(image.Width, image.Height) + padding * 2\n                match packer.Insert(key, size) with\n                | None -> failwithf $\"Could not pack texture %s{key}\"\n                | Some rect ->\n                    let w = image.Width\n                    let h = image.Height\n                    let rowSize = w * bpp\n                    for y = 0 to h - 1 do\n                        let row = image.GetPixelRowSpan(y)\n                        let src = MemoryMarshal.Cast<Rgba32, byte>(row)\n                        let xDest = rect.Min.X + padding\n                        let yDest = rect.Min.Y + y + padding\n                        let start = getIndex xDest yDest\n                        let dest = span.Slice(start, rowSize)\n                        src.CopyTo(dest)\n                    let x0 = rect.Min.X\n                    let x3 = rect.Max.X - 1\n                    let x1 = x0 + padding\n                    let x2 = x3 - padding\n                    let y0 = rect.Min.Y\n                    let y3 = rect.Max.Y - 1\n                    let y1 = y0 + padding\n                    let y2 = y3 - padding\n                    // Copy first and last rows to padding\n                    span.Slice(getIndex x1 y1, rowSize).CopyTo(span.Slice(getIndex x1 y0, rowSize))\n                    span.Slice(getIndex x1 y2, rowSize).CopyTo(span.Slice(getIndex x1 y3, rowSize))\n                    for y = rect.Min.Y to rect.Max.Y - 1 do\n                        // Copy first and last columns to padding\n                        span.Slice(getIndex x1 y, bpp).CopyTo(span.Slice(getIndex x0 y, bpp))\n                        span.Slice(getIndex x2 y, bpp).CopyTo(span.Slice(getIndex x3 y, bpp))\n            // Create device texture\n            let desc = \n                TextureDescription(\n                    Width = uint32 atlasWidth, \n                    Height = uint32 atlasHeight, \n                    Depth = 1u, \n                    MipLevels = 1u, \n                    ArrayLayers = 1u, \n                    Format = PixelFormat.R8_G8_B8_A8_UNorm,\n                    Usage = TextureUsage.Sampled,\n                    Type = TextureType.Texture2D)\n            let texture = device.CreateTexture(desc, ReadOnlyMemory(bytes))\n            // Remove padding from entries\n            let entries =\n                packer.Entries\n                |> Seq.map (fun (key, rect) -> key, rect.Expand(Vector2i.One * -padding))\n            let size = Vector2i(int texture.Width, int texture.Height)\n            TextureAtlas(size, entries), texture\n\ntype JsonTextureEntry = {\n    Name : string\n    X : int\n    Y : int\n    Width : int\n    Height : int\n    Padding : int\n    }\n\ntype JsonTextureAtlas = {\n    Width : int\n    Height : int\n    UndefinedName : string\n    Textures : JsonTextureEntry[]\n    }\n\n[<AutoOpen>]\nmodule TextureLoadingExtensions =\n    let private getTexBounds (t : JsonTextureEntry) =\n//        let tc0 = Vector2i(tex.X, tex.Y)\n//        let tc1 = tc0 + Vector2i(tex.Width, tex.Height)\n//        let padding = Vector2i.One * tex.Padding\n//        let p0 = tc0 + padding\n//        let p1 = tc1 - padding\n//        Range2i(Vector2i(p0.X, p1.Y), Vector2i(p1.X, p0.Y))\n                    Range2i.Sized(Vector2i(t.X, t.Y), Vector2i(t.Width, t.Height))\n                        .Expand(Vector2i.One * -t.Padding)\n\n    type IStreamSource with\n        member c.LoadImage(key) =\n            use stream = c.Open(key)\n            Image.Load<Rgba32>(stream)\n\n        member c.LoadTexture(device : GraphicsDevice, key) =\n            let image = c.LoadImage(key)\n            device.CreateTexture(image)\n\n        member c.LoadTexture(device : GraphicsDevice, key, cache : IResourceCache) =\n            let texture = c.LoadTexture(device, key)\n            cache.AddResource(key, texture)\n\n        member c.LoadTextureAtlas(key) =\n            let atlas = c.LoadJson<JsonTextureAtlas>(key)\n            let entries = atlas.Textures |> Seq.map (fun t -> t.Name, getTexBounds t)\n            TextureAtlas(Vector2i(atlas.Width, atlas.Height), entries)\n\n    type IReadOnlyFolder with\n        member c.LoadTextureAtlasFromFolder(device : GraphicsDevice, path, atlasWidth, atlasHeight) =\n            // If this is a folder, create atlas on the fly from images within the folder\n            let images =\n                c.GetFiles(path)\n                |> Seq.map (fun file ->\n                    // Make the keys relative within the atlas\n                    let key = file.Replace(path, \"\").TrimStart('/')\n                    key, c.LoadImage(file))\n            device.CreateTextureAtlas(atlasWidth, atlasHeight, images)\n\n        member c.LoadTextureAtlasFromFolder(device, key, atlasWidth, atlasHeight, cache : IResourceCache) =\n            let atlas, texture = c.LoadTextureAtlasFromFolder(device, key, atlasWidth, atlasHeight)\n            cache.AddResource<TextureAtlas>(key, atlas)\n            cache.AddResource<Texture>(key, texture)\n\ntype TextureAtlasLoader() =\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            let atlas = folder.LoadTextureAtlas(key)\n            cache.AddResource<TextureAtlas>(key, atlas)\n\ntype TextureLoader(device) =\n    interface IResourceLoader with\n        member c.Load(folder, cache, key) =\n            let texture = folder.LoadTexture(device, key)\n            cache.AddResource<Texture>(key, texture)\n\n[<AutoOpen>]\nmodule TextureLoaderExtensions =\n    type ResourceCache with\n        member c.AddTextureAtlasLoaders() =\n            c.AddLoader(\".atlas.json\", TextureAtlasLoader())\n            \n        member c.AddTextureLoaders(device) =\n            c.AddLoader(\".jpg\", TextureLoader(device))\n            c.AddLoader(\".png\", TextureLoader(device))\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Tiling.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Numerics\nopen System.Runtime.CompilerServices\n\n[<Extension>]\ntype TileVertexSpanExtensions =\n    [<Extension>]\n    static member DrawTile(span : Span<PositionTextureDualColorVertex>, x0, y0, x1, y1, tx, ty, fg, bg) = \n        let tx0 = float32 tx\n        let ty0 = float32 ty\n        let tx1 = tx0 + 1.0f\n        let ty1 = ty0 + 1.0f\n        let px0 = float32 x0\n        let py0 = float32 y0\n        let px1 = float32 x1\n        let py1 = float32 y1\n        span.[0] <- {\n            Position = Vector3(px0, py0, 0.0f)\n            TexCoord = Vector2(tx0, ty0)\n            Foreground = fg\n            Background = bg\n            }\n        span.[1] <- {\n            Position = Vector3(px1, py0, 0.0f)\n            TexCoord = Vector2(tx1, ty0)\n            Foreground = fg\n            Background = bg\n            }\n        span.[2] <- {\n            Position = Vector3(px1, py1, 0.0f)\n            TexCoord = Vector2(tx1, ty1)\n            Foreground = fg\n            Background = bg\n            }\n        span.[3] <- {\n            Position = Vector3(px0, py1, 0.0f)\n            TexCoord = Vector2(tx0, ty1)\n            Foreground = fg\n            Background = bg\n            }\n\n    [<Extension>]\n    static member DrawTile(span : Span<PositionTextureDualColorVertex>, x, y, tx, ty, fg, bg) = \n        span.DrawTile(x, y, x + 1, y + 1, tx, ty, fg, bg)\n\n    [<Extension>]\n    static member DrawTile(span : Span<PositionTextureDualColorVertex>, x0, y0, x1, y1, ch : char, fg, bg) = \n        let tileId = int ch\n        let tx = tileId &&& 0xf\n        let ty = tileId >>> 4\n        span.DrawTile(x0, y0, x1, y1, tx, ty, fg, bg)\n\n    [<Extension>]\n    static member DrawTile(span : Span<PositionTextureDualColorVertex>, x, y, ch : char, fg, bg) = \n        span.DrawTile(x, y, x + 1, y + 1, ch, fg, bg)\n\n[<Extension>]\ntype TileVertexBufferWriterExtensions =\n    [<Extension>]\n    static member GetTileSpan(w : IBufferWriter<PositionTextureDualColorVertex>, tileCount) = \n        let vertexCount = tileCount * 4\n        w.GetSpan(vertexCount).Slice(0, vertexCount)\n\n    [<Extension>]\n    static member DrawTile(w : IBufferWriter<PositionTextureDualColorVertex>, x, y, ch, fg, bg) = \n        let span = w.GetTileSpan(1)\n        span.DrawTile(x, y, ch, fg, bg)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawTileText(w : IBufferWriter<PositionTextureDualColorVertex>, x0, y0, str : string, fg, bg) = \n        let span = w.GetTileSpan(str.Length)\n        for i = 0 to str.Length - 1 do\n            span.Slice(i * 4).DrawTile(x0 + i, y0, str.[i], fg, bg)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawTileRect(w : IBufferWriter<PositionTextureDualColorVertex>, x0, y0, x1, y1, fg) =\n        let span = w.GetTileSpan(1)\n        span.DrawTile(x0, y0, x1, y1, '\\u00db', fg, fg)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawTileTextBorder(w : IBufferWriter<PositionTextureDualColorVertex>, x0, y0, x1, y1, fg, bg) = \n        let x1 = x1 - 1\n        let y1 = y1 - 1\n        w.DrawTile(x0, y0, '\\u00da', fg, bg)\n        w.DrawTile(x1, y0, '\\u00bf', fg, bg)\n        w.DrawTile(x0, y1, '\\u00c0', fg, bg)\n        w.DrawTile(x1, y1, '\\u00d9', fg, bg)\n        for x = x0 + 1 to x1 - 1 do\n            w.DrawTile(x, y0, '\\u00c4', fg, bg)\n            w.DrawTile(x, y1, '\\u00c4', fg, bg)\n        for y = y0 + 1 to y1 - 1 do\n            w.DrawTile(x0, y, '\\u00b3', fg, bg)\n            w.DrawTile(x1, y, '\\u00b3', fg, bg)\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Timing.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System.Diagnostics\n\ntype FpsGauge(updateInterval) =\n    let mutable count = 0\n    let mutable maxValue = 0.0f\n    let mutable lastUpdateTimestamp = 0L\n    let mutable lastTimestamp = 0L\n    let mutable resultMax = 0.0f\n    let mutable resultFps = 0.0f\n    member c.FramesPerSec = resultFps\n    member c.MeanFrameMs = if resultFps > 0.0f then 1000.0f / resultFps else 0.0f\n    member c.MaxFrameMs = resultMax * 1000.0f\n    member c.Reset() =\n        count <- 0\n        maxValue <- 0.0f\n        lastTimestamp <- 0L\n        lastUpdateTimestamp <- 0L\n        resultMax <- 0.0f\n        resultFps <- 0.0f\n    member c.Update(timestamp) =\n        let deltaSec = \n            let delta = timestamp - lastTimestamp\n            float32 (float delta / float Stopwatch.Frequency)\n        let deltaUpdateSec = \n            let delta = timestamp - lastUpdateTimestamp\n            float32 (float delta / float Stopwatch.Frequency)\n        lastUpdateTimestamp <- timestamp\n        maxValue <- max maxValue deltaUpdateSec\n        count <- count + 1\n        if deltaSec >= updateInterval then\n            resultFps <- float32 count / float32 deltaSec\n            resultMax <- maxValue\n            lastTimestamp <- timestamp\n            maxValue <- 0.0f\n            count <- 0\n\ntype ScalarGauge(updateInterval) =\n    let mutable count = 0\n    let mutable lastTimestamp = 0L\n    let mutable total = 0.0f\n    let mutable current = 0.0f\n    member c.Current = current\n    member c.Reset() =\n        count <- 0\n        lastTimestamp <- 0L\n        total <- 0.0f\n        current <- 0.0f\n    member c.Update(timestamp, value) =\n        let delta = timestamp - lastTimestamp\n        let deltaSec = float32 (float delta / float Stopwatch.Frequency)\n        count <- count + 1\n        total <- total + value\n        if deltaSec >= updateInterval then\n            lastTimestamp <- timestamp\n            current <- total / float32 count\n            count <- 0\n            total <- 0.0f\n\n[<Struct>]\ntype TimingSettings = {\n    MinDeltaTime : int64\n    MaxDeltaTime : int64\n    FixedDeltaTime : int64\n    IsRunning : bool\n    ClockActorId : ActorId\n    } with\n    static member Default = {\n        MinDeltaTime = 0L\n        MaxDeltaTime = 250L\n        FixedDeltaTime = 16L\n        IsRunning = true\n        ClockActorId = ActorId.Undefined\n        }\n\ntype FixedUpdateTimer(settings) =\n    let mutable settings = settings\n    let mutable lastTime = 0L\n    let mutable accumulatedTime = 0L\n    let mutable accumulatedFixedTime = 0L\n    let mutable variableTime = 0L\n    let mutable variableDeltaTime = 0L\n    let mutable fixedTime = 0L\n    let mutable frameCount = 0L\n    member c.HasUpdate =\n        accumulatedTime >= settings.MinDeltaTime \n    member c.HasFixedUpdate =\n        accumulatedFixedTime >= settings.FixedDeltaTime && settings.IsRunning\n    member c.SetSettings(newSettings) =\n        settings <- newSettings\n    member c.SetTime(time) =\n        let frameTime = min (time - lastTime) settings.MaxDeltaTime\n        lastTime <- time\n        if settings.IsRunning then\n            accumulatedTime <- accumulatedTime + frameTime\n            accumulatedFixedTime <- accumulatedFixedTime + frameTime\n            variableTime <- variableTime + frameTime\n            variableDeltaTime <- frameTime\n    member c.TakeFixedUpdate() =\n        let e = {\n            FixedFrameNumber = fixedTime / settings.FixedDeltaTime\n            FixedTime = fixedTime\n            FixedDeltaTime = settings.FixedDeltaTime\n            Time = lastTime\n            }\n        fixedTime <- fixedTime + settings.FixedDeltaTime\n        accumulatedFixedTime <- accumulatedFixedTime - settings.FixedDeltaTime\n        e\n    /// Should be called after fixed update\n    member c.TakeUpdate() =\n        let e = {\n            FrameNumber = frameCount\n            FixedTime = fixedTime - settings.FixedDeltaTime\n            FixedDeltaTime = settings.FixedDeltaTime\n            Time = variableTime\n            DeltaTime = variableDeltaTime\n            }\n        // Reset variable time rather than reducing by delta\n        accumulatedTime <- 0L\n        frameCount <- frameCount + 1L\n        e\n    member c.TryTakeUpdate() =\n        if c.HasUpdate then ValueSome (c.TakeUpdate()) else ValueNone\n"
  },
  {
    "path": "samples/Garnet.Toolkit/Vertices.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.Buffers\nopen System.Numerics\nopen System.Runtime.CompilerServices\nopen Veldrid\nopen Garnet.Numerics\n\n[<Struct>]\ntype PositionColorVertex = {\n    Position : Vector3\n    Color : RgbaFloat\n} with\n    interface IVertex with\n        member _.Layout =\n            VertexLayoutDescription([|\n                VertexElementDescription(\"Position\",\n                    VertexElementFormat.Float3,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"Color\",\n                    VertexElementFormat.Float4,\n                    VertexElementSemantic.TextureCoordinate)\n                |])\n\n[<Struct>]\ntype PositionTextureVertex = {\n    Position : Vector3\n    TexCoord : Vector2\n} with\n    interface IVertex with\n        member _.Layout =\n            VertexLayoutDescription([|\n                VertexElementDescription(\"Position\",\n                    VertexElementFormat.Float3,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"TexCoord\",\n                    VertexElementFormat.Float2,\n                    VertexElementSemantic.TextureCoordinate)\n                |])\n\n[<Struct>]\ntype PositionTextureColorVertex = {\n    Position : Vector3\n    TexCoord : Vector2\n    Color : RgbaFloat\n} with\n    interface IVertex with\n        member _.Layout =\n            VertexLayoutDescription([|\n                VertexElementDescription(\"Position\",\n                    VertexElementFormat.Float3,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"TexCoord\",\n                    VertexElementFormat.Float2,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"Color\",\n                    VertexElementFormat.Float4,\n                    VertexElementSemantic.TextureCoordinate)\n                |])\n\n[<Struct>]\ntype PositionTextureDualColorVertex = {\n    Position : Vector3\n    TexCoord : Vector2\n    Foreground : RgbaFloat\n    Background : RgbaFloat\n    } with\n    interface IVertex with\n        member _.Layout =\n            VertexLayoutDescription([|\n                VertexElementDescription(\"Position\",\n                    VertexElementFormat.Float3,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"TexCoord\",\n                    VertexElementFormat.Float2,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"Foreground\",\n                    VertexElementFormat.Float4,\n                    VertexElementSemantic.TextureCoordinate)\n                VertexElementDescription(\"Background\",\n                    VertexElementFormat.Float4,\n                    VertexElementSemantic.TextureCoordinate)\n                |])\n\n[<Struct>]\ntype internal ReadOnlyArray4<'a> = {\n    Value0 : 'a\n    Value1 : 'a\n    Value2 : 'a\n    Value3 : 'a\n    } with\n    member c.Item with get i =\n        match i with\n        | 0 -> c.Value0\n        | 1 -> c.Value1\n        | 2 -> c.Value2\n        | 3 -> c.Value3\n        | _ -> failwith $\"Index out of range ({i})\"\n    static member Create(v0, v1, v2, v3) = {\n        Value0 = v0\n        Value1 = v1\n        Value2 = v2\n        Value3 = v3\n        }\n\n[<Struct>]\ntype ColorTextureSprite = {\n    Center : Vector2 \n    Size : Vector2\n    Rotation : Vector2 \n    TexBounds : Range2 \n    Color : RgbaFloat\n    } with\n    static member Default = {\n        Center = Vector2.Zero \n        Size = Vector2.One\n        Rotation = Vector2.UnitX \n        TexBounds = Range2.ZeroToOne \n        Color = RgbaFloat.White\n        }\n\n[<Extension>]\ntype VertexSpanExtensions =        \n    [<Extension>]\n    static member GetTriangleSpan<'a>(w : IBufferWriter<'a>, spriteCount) = \n        let vertexCount = spriteCount * 3\n        w.GetSpan(vertexCount).Slice(0, vertexCount)\n\n    [<Extension>]\n    static member GetQuadSpan<'a>(w : IBufferWriter<'a>, spriteCount) = \n        let vertexCount = spriteCount * 4\n        w.GetSpan(vertexCount).Slice(0, vertexCount)\n\n    // Vector2 triangles\n    \n    [<Extension>]\n    static member DrawTriangle(span : Span<Vector2>, p0 : Vector2, p1 : Vector2, p2 : Vector2) = \n        span.[0] <- p0\n        span.[1] <- p1\n        span.[2] <- p2\n    \n    [<Extension>]\n    static member DrawTriangle(w : IBufferWriter<Vector2>, p0 : Vector2, p1 : Vector2, p2 : Vector2) = \n        let span = w.GetTriangleSpan(1)\n        span.DrawTriangle(p0, p1, p2)\n        w.Advance(span.Length)\n\n    // Vector2 quads\n\n    [<Extension>]\n    static member DrawQuad(span : Span<Vector2>, rect : Range2) = \n        span.[0] <- Vector2(rect.Min.X, rect.Min.Y)\n        span.[1] <- Vector2(rect.Max.X, rect.Min.Y)\n        span.[2] <- Vector2(rect.Max.X, rect.Max.Y)\n        span.[3] <- Vector2(rect.Min.X, rect.Max.Y)\n\n    [<Extension>]\n    static member DrawQuad(w : IBufferWriter<Vector2>, rect : Range2) = \n        let span = w.GetQuadSpan(1)\n        span.DrawQuad(rect)\n        w.Advance(span.Length)\n\n    // PositionColorVertex triangles\n\n    [<Extension>]\n    static member DrawTriangle(span : Span<PositionColorVertex>, p0 : Vector2, p1 : Vector2, p2 : Vector2, color) =\n        span.[0] <- { Position = Vector3(p0.X, p0.Y, 0.0f); Color = color }\n        span.[1] <- { Position = Vector3(p1.X, p1.Y, 0.0f); Color = color }\n        span.[2] <- { Position = Vector3(p2.X, p2.Y, 0.0f); Color = color }\n\n    [<Extension>]\n    static member DrawTriangle(w : IBufferWriter<PositionColorVertex>, p0, p1, p2, color) = \n        let span = w.GetTriangleSpan(1)\n        span.DrawTriangle(p0, p1, p2, color)\n        w.Advance(span.Length)\n\n    // PositionColorVertex quads\n\n    [<Extension>]\n    static member DrawQuad(span : Span<PositionColorVertex>, rect : Range2, color) =\n        span.[0] <- { Position = Vector3(rect.Min.X, rect.Min.Y, 0.0f); Color = color }\n        span.[1] <- { Position = Vector3(rect.Max.X, rect.Min.Y, 0.0f); Color = color }\n        span.[2] <- { Position = Vector3(rect.Max.X, rect.Max.Y, 0.0f); Color = color }\n        span.[3] <- { Position = Vector3(rect.Min.X, rect.Max.Y, 0.0f); Color = color }\n\n    [<Extension>]\n    static member DrawQuad(w : IBufferWriter<PositionColorVertex>, rect : Range2, color) = \n        let span = w.GetQuadSpan(1)\n        span.DrawQuad(rect, color)\n        w.Advance(span.Length)\n    \n    // PositionTextureColorVertex triangles\n\n    [<Extension>]\n    static member DrawTriangle(span : Span<PositionTextureColorVertex>, p0 : Vector2, p1 : Vector2, p2 : Vector2, tc0, tc1, tc2, color) =\n        span.[0] <- { Position = Vector3(p0.X, p0.Y, 0.0f); TexCoord = tc0; Color = color }\n        span.[1] <- { Position = Vector3(p1.X, p1.Y, 0.0f); TexCoord = tc1; Color = color }\n        span.[2] <- { Position = Vector3(p2.X, p2.Y, 0.0f); TexCoord = tc2; Color = color }\n\n    [<Extension>]\n    static member DrawTriangle(w : IBufferWriter<PositionTextureColorVertex>, p0, p1, p2, tc0, tc1, tc2, color) = \n        let span = w.GetTriangleSpan(1)\n        span.DrawTriangle(p0, p1, p2, tc0, tc1, tc2, color)\n        w.Advance(span.Length)\n\n    // PositionTextureColorVertex quads\n\n    [<Extension>]\n    static member DrawQuad(span : Span<PositionTextureColorVertex>, rect : Range2, texBounds : Range2, color) =\n        span.[0] <- { Position = Vector3(rect.Min.X, rect.Min.Y, 0.0f); TexCoord = Vector2(texBounds.X.Min, texBounds.Y.Min); Color = color }\n        span.[1] <- { Position = Vector3(rect.Max.X, rect.Min.Y, 0.0f); TexCoord = Vector2(texBounds.X.Max, texBounds.Y.Min); Color = color }\n        span.[2] <- { Position = Vector3(rect.Max.X, rect.Max.Y, 0.0f); TexCoord = Vector2(texBounds.X.Max, texBounds.Y.Max); Color = color }\n        span.[3] <- { Position = Vector3(rect.Min.X, rect.Max.Y, 0.0f); TexCoord = Vector2(texBounds.X.Min, texBounds.Y.Max); Color = color }\n    \n    [<Extension>]\n    static member DrawQuad(span : Span<PositionTextureColorVertex>, sprite : ColorTextureSprite) = \n        let dxDir = sprite.Rotation\n        let dyDir = dxDir.GetPerpendicular()\n        let dx = dxDir * sprite.Size.X\n        let dy = dyDir * sprite.Size.Y\n        let p00 = sprite.Center - (dx + dy) * 0.5f\n        let p10 = p00 + dx\n        let p11 = p10 + dy\n        let p01 = p11 - dx\n        let t00 = sprite.TexBounds.Min\n        let t11 = sprite.TexBounds.Max\n        span.[0] <- {\n            Position = Vector3(p00.X, p00.Y, 0.0f)\n            TexCoord = Vector2(t00.X, t00.Y)\n            Color = sprite.Color\n            }\n        span.[1] <- {\n            Position = Vector3(p10.X, p10.Y, 0.0f)\n            TexCoord = Vector2(t11.X, t00.Y)\n            Color = sprite.Color\n            }\n        span.[2] <- {\n            Position = Vector3(p11.X, p11.Y, 0.0f)\n            TexCoord = Vector2(t11.X, t11.Y)\n            Color = sprite.Color\n            }\n        span.[3] <- {\n            Position = Vector3(p01.X, p01.Y, 0.0f)\n            TexCoord = Vector2(t00.X, t11.Y)\n            Color = sprite.Color\n            }\n\n    [<Extension>]\n    static member DrawQuad(w : IBufferWriter<PositionTextureColorVertex>, rect : Range2, texBounds : Range2, color) = \n        let span = w.GetQuadSpan(1)\n        span.DrawQuad(rect, texBounds, color)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawQuad(w : IBufferWriter<PositionTextureColorVertex>, sprite : ColorTextureSprite) = \n        let span = w.GetQuadSpan(1)\n        span.DrawQuad(sprite)\n        w.Advance(span.Length)\n\n    [<Extension>]\n    static member DrawNinePatchRect(w : IBufferWriter<PositionTextureColorVertex>, \n            rect : Range2i,\n            atlasSize : Vector2i,\n            texBounds : Range2i,\n            margin0 : Vector2i,\n            margin1 : Vector2i,\n            color : RgbaFloat) = \n        let m0 = margin0.ToVector2()\n        let m1 = margin1.ToVector2()\n        let p0 = rect.Min.ToVector2()\n        let p3 = rect.Max.ToVector2()\n        let tc0 = texBounds.Min.ToVector2()\n        let tc3 = texBounds.Max.ToVector2()\n        let px = ReadOnlyArray4<_>.Create(p0.X, p0.X + m0.X, p3.X - m1.X, p3.X)        \n        let py = ReadOnlyArray4<_>.Create(p0.Y, p0.Y + m0.Y, p3.Y - m1.Y, p3.Y)\n        let tx = ReadOnlyArray4<_>.Create(tc0.X, tc0.X + m0.X, tc3.X - m1.X, tc3.X)        \n        let ty = ReadOnlyArray4<_>.Create(tc0.Y, tc0.Y + m0.Y, tc3.Y - m1.Y, tc3.Y)\n        let atlasScale = Vector2.One / atlasSize.ToVector2()\n        let span = w.GetQuadSpan(9)\n        for y = 0 to 2 do\n            for x = 0 to 2 do\n                let t0 = Vector2(tx.[x], ty.[y]) * atlasScale\n                let t1 = Vector2(tx.[x + 1], ty.[y + 1]) * atlasScale\n                let p0 = Vector2(px.[x], py.[y])\n                let p1 = Vector2(px.[x + 1], py.[y + 1])\n                let i = y * 3 + x\n                let span = span.Slice(i * 4)\n                span.DrawQuad(\n                    Range2(p0, p1),\n                    Range2(t0, t1),\n                    color)\n        w.Advance(span.Length)\n\n    // PositionTextureDualColorVertex triangles\n\n    [<Extension>]\n    static member DrawTriangle(span : Span<PositionTextureDualColorVertex>, p0 : Vector2, p1 : Vector2, p2 : Vector2, tc0, tc1, tc2, fg, bg) =\n        span.[0] <- { Position = Vector3(p0.X, p0.Y, 0.0f); TexCoord = tc0; Foreground = fg; Background = bg }\n        span.[1] <- { Position = Vector3(p1.X, p1.Y, 0.0f); TexCoord = tc1; Foreground = fg; Background = bg }\n        span.[2] <- { Position = Vector3(p2.X, p2.Y, 0.0f); TexCoord = tc2; Foreground = fg; Background = bg }\n\n    [<Extension>]\n    static member DrawTriangle(w : IBufferWriter<PositionTextureDualColorVertex>, p0, p1, p2, tc0, tc1, tc2, fg, bg) = \n        let span = w.GetTriangleSpan(1)\n        span.DrawTriangle(p0, p1, p2, tc0, tc1, tc2, fg, bg)\n        w.Advance(span.Length)\n\n    // PositionTextureDualColorVertex quads\n    \n    [<Extension>]\n    static member DrawQuad(span : Span<PositionTextureDualColorVertex>, rect : Range2, texBounds : Range2, fg, bg) =\n        span.[0] <- { Position = Vector3(rect.Min.X, rect.Min.Y, 0.0f); TexCoord = Vector2(texBounds.X.Min, texBounds.Y.Min); Foreground = fg; Background = bg }\n        span.[1] <- { Position = Vector3(rect.Max.X, rect.Min.Y, 0.0f); TexCoord = Vector2(texBounds.X.Max, texBounds.Y.Min); Foreground = fg; Background = bg }\n        span.[2] <- { Position = Vector3(rect.Max.X, rect.Max.Y, 0.0f); TexCoord = Vector2(texBounds.X.Max, texBounds.Y.Max); Foreground = fg; Background = bg }\n        span.[3] <- { Position = Vector3(rect.Min.X, rect.Max.Y, 0.0f); TexCoord = Vector2(texBounds.X.Min, texBounds.Y.Max); Foreground = fg; Background = bg }\n\n    [<Extension>]\n    static member DrawQuad(w : IBufferWriter<PositionTextureDualColorVertex>, rect : Range2, texBounds : Range2, fg, bg) = \n        let span = w.GetQuadSpan(1)\n        span.DrawQuad(rect, texBounds, fg, bg)\n        w.Advance(span.Length)\n    "
  },
  {
    "path": "samples/Garnet.Toolkit/Window.fs",
    "content": "﻿namespace Garnet.Graphics\n\nopen System\nopen System.IO\nopen Veldrid\nopen Veldrid.StartupUtilities\nopen ImGuiNET\nopen Garnet.Numerics\nopen Garnet.Input\n\ntype WindowSettings = {\n    X : int\n    Y : int\n    Width : int\n    Height : int\n    Title : string\n    Background : RgbaFloat\n    Redraw : Redraw\n    FullScreen : bool\n    } with\n    static member Default = {\n        X = 100\n        Y = 100\n        Width = 640\n        Height = 360\n        Title = \"Garnet\"\n        Background = RgbaFloat.Clear\n        Redraw = Redraw.Auto\n        FullScreen = false\n        }\n\nmodule private Environment =\n    let addPathVariables() =\n        // Set path variable so native DLLs can be found when running in FSI\n        let basePath = Path.GetDirectoryName(Directory.GetCurrentDirectory())\n        let nativePath = Path.Combine(basePath, @\"runtimes\\win-x64\\native\")\n        let ev = Environment.GetEnvironmentVariable(\"Path\")\n        if not (ev.Contains(nativePath)) then\n            Environment.SetEnvironmentVariable(\"Path\", $\"{ev};{nativePath}\")\n\ntype WindowRenderer(settings) =\n    let window = \n        Environment.addPathVariables()\n        // Create window initially hidden until background can be drawn\n        let windowCI = \n            WindowCreateInfo(\n                X = settings.X,\n                Y = settings.Y,\n                WindowWidth = settings.Width,\n                WindowHeight = settings.Height,\n                WindowInitialState = WindowState.Hidden,\n                WindowTitle = settings.Title\n            )\n        VeldridStartup.CreateWindow(windowCI)\n    let device = \n        let options = GraphicsDeviceOptions(false, Nullable<PixelFormat>(), false)\n        VeldridStartup.CreateGraphicsDevice(window, options)\n    let imGui =\n        new ImGuiRenderer(device,\n            device.MainSwapchain.Framebuffer.OutputDescription,\n            window.Width, window.Height)\n    let renderer = new Renderer(device, settings.Redraw)\n    let mutable isDrawn = false\n    member c.Title \n        with get() = window.Title\n        and set value = window.Title <- value\n    member c.Position \n        with get() = Vector2i(window.X, window.Y) \n        and set(value : Vector2i) =\n            window.X <- value.X\n            window.Y <- value.Y\n    member c.Size \n        with get() = Vector2i(window.Width, window.Height) \n        and set(value : Vector2i) =\n            window.Width <- value.X\n            window.Height <- value.Y\n    member val Background = settings.Background with get, set\n    member c.ImGui = imGui\n    member c.Device = device\n    member c.RenderContext = renderer.Context\n    member c.IsShown =\n        match window.WindowState with\n        | WindowState.Hidden\n        | WindowState.Minimized -> false\n        | _ -> window.Visible\n    member c.Invalidate() =\n        renderer.Invalidate()\n    member c.Close() =\n        window.Close()\n    member c.ToggleFullScreen() =\n        window.WindowState <- \n            match window.WindowState with\n            | WindowState.BorderlessFullScreen -> WindowState.Normal\n            | _ -> WindowState.BorderlessFullScreen\n    member c.BeginDraw() =\n        // Avoid drawing when we've drawn at least once but window isn't visible\n        if isDrawn && not c.IsShown then false\n        else\n            // Proceed with drawing\n            imGui.WindowResized(window.Width, window.Height)\n            renderer.BeginDraw(window.Width, window.Height, c.Background)\n    member c.EndDraw() =\n        // Need to call this after beginning drawing but before ending. If we call\n        // before any drawing, then there will be a brief white flicker as the window\n        // is shown. If we call after all drawing, the initial render will not be\n        // presented to the window, which is visible if doing manual redraw.\n        if not window.Visible then\n            if settings.FullScreen then\n                window.WindowState <- WindowState.BorderlessFullScreen\n            window.Visible <- true\n        isDrawn <- true\n        // Complete rendering\n        imGui.Render(device, renderer.Context.Commands)\n        renderer.EndDraw()\n    member c.Update(deltaSeconds, inputs : InputCollection) =\n        let snapshot = window.PumpEvents()\n        if not window.Exists then false\n        else\n            imGui.Update(deltaSeconds, snapshot)\n            inputs.ClearEvents()\n            if not (ImGui.GetIO().WantCaptureKeyboard) then\n                inputs.UpdateKeysFromSnapshot(snapshot)\n            if not (ImGui.GetIO().WantCaptureMouse) then\n                inputs.UpdateMouseFromSnapshot(snapshot, c.Size)                                \n            true\n    member c.Dispose() =\n        renderer.Dispose()\n        imGui.Dispose()\n        device.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "samples/README.md",
    "content": "# Samples\n\n## Flocking\n\n[[Code](Garnet.Samples.Flocking)]\n\nBoids-style flocking and clustering using Garnet ECS and Veldrid.\n\n![Flocking](Garnet.Samples.Flocking/flocking-screenshot.png \"Flocking\")\n\n## Trixel\n\n[[Code](Garnet.Samples.Trixel)]\n\nTrixel editor using Veldrid and Dear ImGUI.\n\n![Trixel](Garnet.Samples.Trixel/trixel-screenshot.png \"Trixel\")\n\n## Roguelike\n\n[[Code](Garnet.Samples.Roguelike)]\n\nClassic roguelike using Veldrid. Core logic and types are idiomatic F# with no ECS. FSI script allows for replay and testing.\n\n![Roguelike](Garnet.Samples.Roguelike/roguelike-screenshot.png \"Roguelike\")\n\n## Processor\n\n[[Code](Garnet.Processor)]\n\nThis experimental utility is intended for asset processing. Currently it's just a packing utility for either assets or game files, using zip as the archive format.\n\nGarnet.Processor is available as a dotnet tool Nuget package [here](https://www.nuget.org/packages/Garnet.Processor/) or via the command: `dotnet tool install Garnet.Processor`.\n\n**Background:** Asset files (textures, sounds, etc) are often converted and packed into an archive when released with the game. Converting files to an optimal format for the target platform or hardware can make loading faster. Having a small number of packed asset files reduces the overhead of opening individual files and reduces wasted space on the file system.\n\n## Numerics\n\n[[Code](Garnet.Numerics)]\n\nThis experimental library has a variety of game-centric numerics or related code that isn't already covered by System.Numerics. It has no dependency on other Garnet libraries.\n\nGarnet.Numerics is available as a Nuget package [here](https://www.nuget.org/packages/Garnet.Numerics).\n\n## Toolkit\n\n[[Code](Garnet.Toolkit)]\n\nExperimental shared code used by samples, including sprite drawing, particles, noise, audio playback, and more. Depends on [Veldrid](https://github.com/mellinoe/veldrid) for graphics, [OpenTK.OpenAL](https://opentk.net/) for audio, [ZLogger](https://github.com/Cysharp/ZLogger) for logging, and Garnet.Numerics.\n\nGarnet.Toolkit is available as a Nuget package [here](https://www.nuget.org/packages/Garnet.Toolkit).\n\nThis library has two levels of integration:\n- Graphics/audio/etc: Wrapper or convenience methods over other libraries with no dependence on Garnet ECS\n- Composition: For use with Garnet ECS, the library includes predefined plugin-like systems for the various functionality\n\n```fsharp\nstatic member AddDefaultSystems(c : Container) =\n    Disposable.Create [\n        c.AddAssetsFolder()\n        c.AddTextLoaders()\n        c.AddGraphics()\n        c.AddAudioDevice()\n        c.AddAudioLoaders()\n        c.AddTickUpdate()\n        ]\n```\n\nThe asset system in Garnet.Toolkit can load assets either from a folder (first choice) or from an asset archive file (if no folder is present). The folder option can be convenient for editing assets during development or for allowing modding.\n\n**Known issue:** Microsoft.CodeAnalysis.* assemblies may be included in build output, but they are not needed and add around 10 MB. They are included because Garnet.Toolkit depends on Veldrid, which depends on SharpDX, which depends on NETCore.App, which depends on the CodeAnalysis assemblies. Workaround: Use [Paket](https://github.com/fsprojects/Paket), which uninstalls transitive dependencies if no direct dependencies still depend on them."
  },
  {
    "path": "src/Garnet/Actors.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Diagnostics\nopen System.Threading\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\n\n/// Defines which kind of thread an actor can be executed on\ntype DispatcherType =\n    /// Actor which can be run on a background thread\n    | Background = 0\n    /// Actor which must be run on the main thread\n    | Foreground = 1\n\ntype DispatcherDescriptor = {\n    DispatcherType : DispatcherType\n    /// Optional, useful to see thread name when printing, debugging, or profiling.\n    Name : string\n    /// For background dispatchers, the number of worker threads\n    ThreadCount : int\n    /// Max number of batches to process for an actor before releasing lock. When\n    /// the number of actors exceeds the number of workers, lower values increase \n    /// fairness while higher values reduce overhead from locking and other worker\n    /// queue operations.\n    Throughput : int\n    } with\n\n    // Allow at least one background thread\n    static member DefaultThreadCount =\n        Environment.ProcessorCount - 1 |> max 1\n\n    /// Pool of background threads\n    static member Background = {\n        DispatcherType = DispatcherType.Background\n        Name = \"Background\"\n        ThreadCount = DispatcherDescriptor.DefaultThreadCount\n        Throughput = 100\n        }\n\n    /// Single foreground thread\n    static member Foreground = {\n        DispatcherType = DispatcherType.Foreground\n        Name = \"Foreground\"\n        ThreadCount = 0\n        Throughput = 100\n        }\n\n    /// Single dedicated background thread\n    static member Dedicated = {\n        DispatcherType = DispatcherType.Background\n        Name = \"Dedicated\"\n        ThreadCount = 1\n        Throughput = 1000\n        }\n\ntype ActorSystemConfiguration = {\n    /// The last dispatcher in the list is used if an actor defines \n    /// a dispatcher ID that does not exist\n    Dispatchers : DispatcherDescriptor[]\n    } with\n    \n    static member SingleThread = {\n        Dispatchers = [| \n            DispatcherDescriptor.Foreground \n            |]\n        }\n    \n    static member Create(workerCount) = \n        if workerCount = 0 then ActorSystemConfiguration.SingleThread\n        else {\n            Dispatchers = [| \n                { DispatcherDescriptor.Background with ThreadCount = workerCount }\n                DispatcherDescriptor.Foreground\n                |]\n            }\n        \n    static member Default =\n        ActorSystemConfiguration.Create(DispatcherDescriptor.DefaultThreadCount)\n\ntype ActorException(sourceId : ActorId, destId : ActorId, msg : string, innerEx : Exception) =\n    inherit Exception(msg, innerEx)\n    member _.SourceId = sourceId\n    member _.DestinationId = destId\n           \n[<AutoOpen>]\nmodule internal Pooling =                            \n    module TypeInfo =\n        let mutable private count = 0\n        let next() = Interlocked.Increment(&count) - 1\n\n    type TypeInfo<'a>() =\n        static let mutable id = TypeInfo.next()\n        static member Id = id\n\n[<AutoOpen>]\nmodule internal Processing =\n    [<Struct>]\n    type QueuedMessage = {\n        Buffer : obj\n        Count : int\n        SourceId : ActorId\n        DestinationId : ActorId\n        Pool : obj\n        }\n    \n    // Stateless\n    type IDeliverer =\n        abstract Deliver : IInbox * IOutbox * QueuedMessage -> unit        \n\n    // Stateless\n    type Deliverer<'a>() =\n        static let instance = Deliverer<'a>() :> IDeliverer\n        static member Instance = instance\n        interface IDeliverer with\n            member c.Deliver(inbox, outbox, message) =\n                let buffer = message.Buffer :?> 'a[]\n                let pool = message.Pool :?> ArrayPool<'a>\n                try\n                    try\n                        inbox.Receive(outbox, {\n                            Buffer = buffer\n                            Count = message.Count\n                            SourceId = message.SourceId\n                            DestinationId = message.DestinationId\n                            Pool = pool\n                            })\n                    with\n                    | ex -> \n                        let msg = $\"Processing failed:\\n%A{buffer.[0]}\"\n                        raise (exn(msg, ex))\n                finally\n                    pool.Return(buffer)\n                        \n    // Thread-safe\n    type IDispatcher =\n        inherit IDisposable\n        abstract Process : bool -> bool\n        abstract Enqueue : ActorProcessor -> unit\n        abstract OnException : ActorException -> unit\n        abstract ToString : IStringBlockWriter -> unit\n\n    // Thread-safe\n    and ActorProcessor(actorId, inbox : IInbox, dispose, dispatcher : IDispatcher) =\n        let processSync = obj()\n        let queueSync = obj()\n        let queue = Queue<struct(QueuedMessage * IDeliverer)>()\n        let mutable inbox = inbox\n        let mutable maxQueued = 0\n        let mutable total = 0\n        let mutable waitDuration = 0L\n        let mutable waitCount = 0\n        // returns number of messages before processing\n        let run outbox =\n            // lock for duration of dequeue, which ends\n            // in handler process method\n            Monitor.Enter(queueSync)\n            let remaining = queue.Count\n            if remaining = 0 then Monitor.Exit queueSync\n            else\n                let struct(message, deliverer) = queue.Dequeue()\n                Monitor.Exit(queueSync)\n                try\n                    deliverer.Deliver(inbox, outbox, message)\n                with\n                | ex ->\n                    // Halt any further processing of actor\n                    inbox <- NullInbox.Instance\n                    // Report exception\n                    let msg = $\"Actor %d{actorId} failed\"\n                    let actorEx = ActorException(message.SourceId, message.DestinationId, msg, ex)\n                    dispatcher.OnException(actorEx)\n            remaining\n        member c.ActorId = actorId\n        member c.WaitDuration = waitDuration\n        member c.Enqueue<'a>(message : Message<'a>) =\n            let queued : QueuedMessage = {\n                SourceId = message.SourceId\n                DestinationId = message.DestinationId\n                Count = message.Count\n                Buffer = message.Buffer\n                Pool = message.Pool\n            }\n            let entry = struct(queued, Deliverer<'a>.Instance)\n            Monitor.Enter(queueSync)\n            queue.Enqueue(entry)\n            // This check is an optimization to avoid queuing actors repeatedly in dispatchers,\n            // but it relies on dispatchers taking full responsibility to ensure all messages\n            // are handled. Otherwise, the actor will be orphaned and continue to accumulate \n            // messages without a dispatcher to run it.\n            let r = if queue.Count = 1 then ValueSome dispatcher else ValueNone\n            maxQueued <- max maxQueued queue.Count\n            Monitor.Exit queueSync\n            r            \n        member private c.Process(outbox, throughput) =\n            if not (Monitor.TryEnter processSync) then\n                // This is a case where actor is in more than one dispatcher queue and\n                // contention has occurred. This should only occur when a worker has\n                // completed all messages and has already decided to release its lock\n                // when another message is enqueued, causing a second worker to pick it\n                // up and attempt to process it (before the first has released lock).\n                let start = Stopwatch.GetTimestamp()\n                Monitor.Enter processSync\n                let stop = Stopwatch.GetTimestamp()\n                waitDuration <- waitDuration + stop - start\n                waitCount <- waitCount + 1\n            // using remaining count to avoid locking again\n            // when throughput >1\n            let mutable remaining = 1\n            let original = total\n            let target = total + throughput\n            while total < target && remaining > 0 do\n                // get count in queue prior to dequeuing\n                remaining <- run outbox\n                if remaining > 0 then\n                    // count the batch that was just processed\n                    total <- total + 1\n                    remaining <- remaining - 1\n            let count = total - original\n            Monitor.Exit processSync\n            count\n        /// Takes total byref to allow frequent updating in case this takes a long\n        /// time to return.\n        member c.ProcessAll(outbox, throughput, total : byref<int64>) =\n            let mutable count = 0\n            let mutable pending = true\n            while pending do\n                let delta = c.Process(outbox, throughput)\n                count <- count + delta\n                total <- total + int64 delta\n                pending <- delta > 0\n            count\n        member c.Dispose() =\n            lock processSync (fun () ->\n                lock queueSync (fun () -> queue.Clear())\n                dispose()\n                )     \n        override c.ToString() =\n            $\"Actor %d{actorId}: %d{total} batches processed (%d{waitCount} waits, %d{waitDuration} ticks), %d{maxQueued} max queued\"\n        interface IDisposable with\n            member c.Dispose() = c.Dispose()\n                            \n    // Thread-safe\n    type IDispatcherLookup =\n        abstract GetDispatcher : int -> IDispatcher\n\n    // Thread-safe\n    type SharedActorMap(lookup : IDispatcherLookup) =\n        let sync = obj()\n        let dict = Dictionary<int, ActorProcessor>()\n        let actors = List<ActorProcessor>()\n        let factories = ActorFactoryCollection()\n        member c.Count =\n            actors.Count\n        member c.Register(factory : IActorFactory) =\n            Monitor.Enter sync\n            factories.Add(factory)\n            Monitor.Exit sync\n        member c.GetProcessor(destId) =           \n            Monitor.Enter sync\n            let r =\n                match dict.TryGetValue(destId) with\n                | true, x -> x\n                | false, _ ->\n                    let actor = factories.Create(ActorId destId)\n                    let proc = \n                        new ActorProcessor(destId, \n                            actor.Inbox, \n                            actor.Dispose, \n                            lookup.GetDispatcher(actor.DispatcherId))\n                    actors.Add(proc)\n                    dict.Add(destId, proc)\n                    proc\n            Monitor.Exit sync\n            r\n        member c.Dispose() =\n            lock sync <| fun () ->\n                for proc in actors do\n                    proc.Dispose()\n        interface IDisposable with\n            member c.Dispose() = c.Dispose()\n        member c.ToString(writer : IStringBlockWriter) =\n            lock sync <| fun () ->\n                let count = min dict.Count 20\n                let ordered = \n                    dict.Values \n                    |> Seq.sortBy (fun a -> -a.WaitDuration, a.ActorId) \n                    |> Seq.take count\n                if writer.BeginList(\"Actors\", actors.Count) then\n                    for actor in ordered do\n                        writer.Write(actor.ToString())\n                    writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n                \n[<AutoOpen>]\nmodule internal Dispatchers =\n    // Thread-safe\n    type DispatchQueue(owner : IDispatcher) =\n        let mutable maxCount = 0\n        let queue = Queue<ActorProcessor>()\n        let signal = new ManualResetEventSlim(false)\n        let sync = obj()\n        member c.Wait() =\n            signal.Wait()\n        member c.GetCount() =\n            Monitor.Enter(sync)\n            let r = queue.Count\n            Monitor.Exit(sync)\n            r\n        member c.Enqueue(x) =\n            Monitor.Enter(sync)\n            queue.Enqueue(x)\n            maxCount <- max queue.Count maxCount\n            if queue.Count = 1 then signal.Set()\n            Monitor.Exit(sync)\n        member c.TryDequeue([<Out>] item : byref<_>) =\n            Monitor.Enter(sync)\n            let r = queue.Count > 0\n            // note we don't reset when queue is empty, which can be when disposing\n            let canReset = queue.Count = 1\n            if r then item <- queue.Dequeue()\n            if canReset then signal.Reset()\n            Monitor.Exit(sync)\n            r        \n        member c.Enqueue(actor : ActorProcessor, message : Message<'a>) =\n            match actor.Enqueue<'a>(message) with\n            | ValueNone -> ()\n            | ValueSome dispatcher ->\n                if obj.ReferenceEquals(dispatcher, owner) \n                    then c.Enqueue(actor)\n                    else dispatcher.Enqueue(actor)\n        member c.Dispose() =\n            Monitor.Enter(sync)            \n            queue.Clear()\n            signal.Set()\n            signal.Dispose()\n            Monitor.Exit(sync)\n        interface IDisposable with\n            member c.Dispose() = c.Dispose()\n        override c.ToString() =\n            lock sync <| fun () -> $\"%d{maxCount} actors max queued\"\n\n    // Not thread-safe\n    type LocalOutbox(name, queue : DispatchQueue, actorMap : SharedActorMap) =\n        let actors = Dictionary<int, ActorProcessor>()\n        member private c.Get(destId) =\n            match actors.TryGetValue(destId) with\n            | true, actor -> actor\n            | false, _ -> \n                let actor = actorMap.GetProcessor(destId)\n                actors.Add(destId, actor)\n                actor\n        member val ActiveId = 0 with get, set\n        member c.SendAll<'a>(message : Message<'a>, deliveryId : ActorId) =\n            let processor = c.Get(deliveryId.Value)\n            let message = if message.SourceId.Value = 0 then { message with SourceId = ActorId c.ActiveId } else message\n            queue.Enqueue(processor, message)\n        interface IOutbox with\n            member c.SendAll<'a>(message, deliveryId) =\n                c.SendAll<'a>(message, deliveryId)\n        override c.ToString() =\n            $\"{name}: %d{actors.Count} outbox actors\"\n\n    // Thread-safe\n    type SharedOutbox(actors : SharedActorMap) =\n        member c.SendAll<'a>(message : Message<'a>, deliveryId : ActorId) =\n            let actor = actors.GetProcessor(deliveryId.Value)\n            match actor.Enqueue<'a>(message) with\n            | ValueSome dispatcher -> dispatcher.Enqueue(actor)\n            | ValueNone -> ()\n        interface IOutbox with\n            member c.SendAll<'a>(message, deliveryId) =\n                c.SendAll<'a>(message, deliveryId)\n\n    [<Struct>]\n    type WorkerStatus = {\n        QueuedCount : int\n        ProcessedCount : int64\n        }\n\n    module WorkerStatus =\n        let empty = {\n            QueuedCount = 0\n            ProcessedCount = 0L\n            }\n\n        let add a b = {\n            QueuedCount = a.QueuedCount + b.QueuedCount\n            ProcessedCount = a.ProcessedCount + b.ProcessedCount\n            }                \n\n        let isRunning s1 s2 =\n            s1.ProcessedCount <> s2.ProcessedCount ||\n            s2.QueuedCount > 0\n\n    // Thread-safe\n    type private Worker(owner : IDispatcher, actorMap, name, workerId, throughput, workers : Worker[]) =\n        let queue = new DispatchQueue(owner)\n        let outbox = LocalOutbox(workerId.ToString(), queue, actorMap)\n        let sync = obj()\n        let mutable active = Unchecked.defaultof<ActorProcessor>\n        let mutable isRunning = true\n        let mutable waits = 0\n        let mutable total = 0L\n        let runStealing() =\n            // Note we are stealing actors, not individual batches of messages. This way\n            // we avoid situations where a actor that takes a long time to process spreads \n            // across and blocks all workers.\n            let mutable found = false\n            if workers.Length > 1 then\n                let mutable i = 0\n                while i < workers.Length && not found do\n                    if i <> workerId then\n                        found <- workers.[i].TryDequeue(&active)\n                    i <- i + 1\n            found\n        let runDequeue() =\n            queue.TryDequeue(&active)\n        let runActive() =\n            let mutable count = 0\n            if not (obj.ReferenceEquals(active, null)) then\n                // run single actor until no messages remain, respecting\n                // throughput param\n                outbox.ActiveId <- active.ActorId\n                count <- count + active.ProcessAll(outbox, throughput, &total)\n                outbox.ActiveId <- 0\n                active <- Unchecked.defaultof<_>\n            count > 0\n        let runAll() =\n            runActive() || \n            runDequeue() ||\n            runStealing()\n        let run() =\n            try\n                while isRunning do\n                    Monitor.Enter(sync)\n                    while isRunning && runAll() do ()\n                    Monitor.Exit(sync)\n                    queue.Wait()\n                    waits <- waits + 1\n            with ex ->\n                printfn \"Failed %s\" <| ex.ToString()\n        let thread = \n            let t = Thread(run)\n            t.Name <- $\"{name} %d{workerId}\"\n            t\n        member c.TryDequeue([<Out>] item : byref<_>) =\n            queue.TryDequeue(&item)\n        member c.Start() =\n            thread.Start()\n        member c.Dispose() =\n            isRunning <- false\n            queue.Dispose()\n            thread.Join()\n        member c.GetStatus() =\n            Monitor.Enter sync\n            let r = {\n                QueuedCount = queue.GetCount()\n                ProcessedCount = total\n                }\n            Monitor.Exit sync\n            r\n        member c.Enqueue(x : ActorProcessor) =\n            queue.Enqueue(x)\n        member c.ToString(writer : IStringBlockWriter) =\n            let name = $\"{thread.Name} (%d{total} batches processed, %d{waits} waits)\"\n            let id = thread.Name\n            if writer.Begin(name, id) then\n                writer.Write(outbox.ToString())\n                writer.Write(queue.ToString())\n                writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n        interface IDisposable with\n            member c.Dispose() = c.Dispose()\n\n    // Thread-safe\n    type WorkerDispatcher(actorMap, name, workerCount, throughput, onException) as c =\n        let workers = \n            // round up to pow2 size so we can avoid modulus\n            let size = Bits.getNextPow2 workerCount\n            let arr = Array.zeroCreate size\n            // create workers\n            for i = 0 to workerCount - 1 do\n                arr.[i] <- new Worker(c, actorMap, name, i, throughput, arr)\n            // fill in remaining by distributing original workers\n            for i = workerCount to arr.Length - 1 do\n                arr.[i] <- arr.[i % workerCount]\n            // start separately since all workers must be created first\n            for i = 0 to workerCount - 1 do\n                arr.[i].Start()            \n            arr\n        let getStatus() =\n            // get aggregate status of all workers\n            let mutable status = WorkerStatus.empty\n            for i = 0 to workerCount - 1 do\n                status <- WorkerStatus.add status (workers.[i].GetStatus())\n            status\n        interface IDispatcher with\n            member c.Process(waitThreads) = \n                // pow2 size to avoid modulus\n                let spinPeriod = 128\n                let mutable count = 0\n                if waitThreads then\n                    // keep polling status to see if anything changed or\n                    // there are queued items outstanding\n                    let mutable s1 = getStatus()\n                    let mutable s2 = getStatus()\n                    while WorkerStatus.isRunning s1 s2 do\n                        s1 <- s2\n                        s2 <- getStatus()\n                        count <- count + 1\n                        // if we've polled a bunch of times in a row with\n                        // continued activity, sleep and then resume\n                        if count &&& (spinPeriod - 1) = 0 then\n                            Thread.Sleep(1)\n                count > 0\n            member c.Enqueue(actor) =\n                // Distribute according to actor ID. If ID is uniformly\n                // distributed, this is round-robin. Even if distribution is\n                // not uniform, expect other workers to steal as needed. Note \n                // worker array is a pow2 size.\n                // For waiting/polling, we could just always add to the first\n                // worker queue and rely on stealing, so this is really more \n                // for use of signals instead of wait polling.\n                let index = actor.ActorId &&& (workers.Length - 1)\n                workers.[index].Enqueue(actor)\n            member c.OnException(ex) =\n                onException ex\n            member c.Dispose() =\n                for i = 0 to workerCount - 1 do\n                    workers.[i].Dispose()\n            member c.ToString(writer) =\n                c.ToString(writer)\n        member c.ToString(writer : IStringBlockWriter) =\n            if writer.BeginList(\"Workers\", workerCount) then\n                for i = 0 to workerCount - 1 do\n                    workers.[i].ToString(writer)\n                writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n\n    // Thread-safe\n    type Dispatcher(actorMap, name, throughput) as c =\n        let queue = new DispatchQueue(c)\n        let outbox = LocalOutbox(name, queue, actorMap)\n        let sync = obj()\n        let mutable total = 0L\n        interface IDispatcher with\n            member c.Process _ =\n                // Process on current thread until queue is empty\n                Monitor.Enter sync\n                let mutable actor = Unchecked.defaultof<_>\n                let mutable count = 0\n                while queue.TryDequeue &actor do\n                    outbox.ActiveId <- actor.ActorId\n                    count <- count + actor.ProcessAll(outbox, throughput, &total)\n                    outbox.ActiveId <- 0\n                Monitor.Exit sync\n                count > 0\n            member c.Enqueue(actor) =\n                queue.Enqueue actor\n            member c.OnException(ex) =\n                raise ex\n            member c.Dispose() =\n                queue.Dispose()\n            member c.ToString(writer) =\n                c.ToString(writer)\n        member c.ToString(writer : IStringBlockWriter) =\n            let name = $\"{name} (%d{total} processed)\"\n            let id = name\n            if writer.Begin(name, id) then\n                writer.Write(outbox.ToString())\n                writer.Write(queue.ToString())\n                writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n\n    // Thread-safe\n    type CurrentDispatcher(actorMap, name, throughput) =\n        let outbox = SharedOutbox(actorMap)\n        let sync = obj()\n        let mutable total = 0L\n        interface IDispatcher with\n            member c.Process _ = false\n            member c.Enqueue(actor) =\n                Monitor.Enter sync\n                actor.ProcessAll(outbox, throughput, &total) |> ignore\n                Monitor.Exit sync\n            member c.OnException(ex) =\n                raise ex\n            member c.Dispose() = ()\n            member c.ToString(writer) =\n                c.ToString(writer)\n        member c.ToString(writer : IStringBlockWriter) =\n            let name = $\"{name} (%d{total} processed)\"\n            let id = name\n            if writer.Begin(name, id) then\n                writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n\n    /// Thread-safe if dispatchers do not change\n    type DispatcherLookup(dispatcherCount) =\n        let lookup = Array.zeroCreate<IDispatcher> dispatcherCount\n        let runOnce waitThreads =\n            // go once through dispatchers, waiting until\n            // they individually have no work remaining, then\n            // return whether work was done\n            let mutable pending = false\n            for d in lookup do \n                pending <- d.Process(waitThreads) || pending\n            pending\n        member c.SetDispatcher(dispatcherId, dispatcher) =\n            lookup.[dispatcherId] <- dispatcher\n        member c.GetDispatcher(dispatcherId) =\n            // assume last entry is the fallback\n            let index = min dispatcherId (lookup.Length - 1)                \n            lookup.[index]\n        interface IDispatcherLookup with\n            member c.GetDispatcher execution =\n                c.GetDispatcher execution\n        member c.Process(waitThreads) =\n            // keep running until no additional work is done\n            // any blocking should be done at lower level like worker\n            let mutable s1 = runOnce(waitThreads)\n            let mutable s2 = runOnce(waitThreads)\n            while s1 || s2 do\n                s1 <- s2\n                s2 <- runOnce(waitThreads)\n        /// Runs until all foreground work is done\n        member c.Process() = \n            c.Process(false)\n        /// Sleep/poll while background threads complete\n        member c.ProcessAll() = \n            c.Process(true)\n        member c.Dispose() =\n            for d in lookup do \n                d.Dispose()                    \n        interface IDisposable with\n            member c.Dispose() = c.Dispose()\n        member c.ToString(writer : IStringBlockWriter) =\n            if writer.BeginList(\"Dispatchers\", lookup.Length) then\n                for dispatcher in lookup do\n                    dispatcher.ToString(writer)\n                writer.End()\n        override c.ToString() =\n            StringBlockWriter.Format(c.ToString)\n\n    type DispatcherLookup with\n        member c.SetDispatchers(actors, onWorkerError, dispatchers : _[]) =\n            for i = 0 to dispatchers.Length - 1 do\n                let desc = dispatchers.[i]\n                let dispatcher : IDispatcher = \n                    match desc.DispatcherType with\n                    | DispatcherType.Foreground -> \n                        new Dispatcher(actors, desc.Name, desc.Throughput) :> IDispatcher\n                    | DispatcherType.Background | _ -> \n                        if desc.ThreadCount <= 0 \n                        then new Dispatcher(actors, desc.Name, desc.Throughput) :> IDispatcher\n                        else \n                            new WorkerDispatcher(actors, desc.Name, desc.ThreadCount, \n                                desc.Throughput, onWorkerError) :> IDispatcher                            \n                c.SetDispatcher(i, dispatcher)\n\ntype IMessagePump =\n    inherit IOutbox\n    inherit IDisposable\n    abstract member Process : unit -> unit\n    abstract member ProcessAll : unit -> unit\n\ntype internal ExceptionHandlers() =\n    let handlers = List<Action<ActorException>>()\n    let sync = obj()\n    member private c.Remove(handler) =\n        lock sync (fun () ->\n            handlers.Remove(handler) |> ignore)        \n    member c.Handle(ex) =\n        lock sync (fun () ->\n            for handler in handlers do\n                handler.Invoke(ex))\n    member c.Add(handler) =\n        lock sync (fun () ->\n            handlers.Add(handler))        \n        new Disposable(fun () -> c.Remove(handler)) :> IDisposable\n\n// Thread-safe\ntype ActorSystem(config) =\n    let dispatchers = new DispatcherLookup(config.Dispatchers.Length)\n    let actors = new SharedActorMap(dispatchers)\n    let outbox = SharedOutbox(actors)\n    let handlers = ExceptionHandlers()\n    do dispatchers.SetDispatchers(actors, handlers.Handle, config.Dispatchers)\n    new() = new ActorSystem(ActorSystemConfiguration.Default)\n    new(workerCount) = new ActorSystem(ActorSystemConfiguration.Create(workerCount))\n    member c.ActorCount =\n        actors.Count\n    member c.Register(factory : IActorFactory) =\n        actors.Register(factory)\n    member c.RegisterExceptionHandler(onException) =\n        handlers.Add(onException)\n    member c.Dispose() =\n        dispatchers.Dispose()\n        actors.Dispose()\n    /// Runs until all foreground work is done\n    member c.Process() = \n        dispatchers.Process()\n    /// Sleep/poll while background threads complete\n    member c.ProcessAll() =\n        dispatchers.ProcessAll()\n    member c.SendAll<'a>(message, deliveryId) =\n        outbox.SendAll<'a>(message, deliveryId)\n    interface IMessagePump with\n        member c.SendAll<'a>(message, deliveryId) = c.SendAll<'a>(message, deliveryId)\n        member c.Process() = c.Process()\n        member c.ProcessAll() = c.ProcessAll()\n        member c.Dispose() = c.Dispose()\n    member c.ToString(writer : IStringBlockWriter) =\n        actors.ToString(writer)\n        dispatchers.ToString(writer)\n    override c.ToString() =\n        $\"Actor system\\n  %s{Format.addIndent (actors.ToString())}%s{Format.addIndent (dispatchers.ToString())}\"\n    static member CreateSingleThread() =\n        new ActorSystem(ActorSystemConfiguration.SingleThread)\n\ntype NullMessagePump() =\n    static let mutable instance = new NullMessagePump() :> IMessagePump\n    static member Instance = instance\n    interface IMessagePump with\n        member c.SendAll<'a>(message, deliveryId) = NullOutbox.Instance.SendAll<'a>(message, deliveryId)\n        member c.Process() = ()\n        member c.ProcessAll() = ()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ActorReference = {\n    ActorId : ActorId\n    MessagePump : IMessagePump\n    } with\n    override c.ToString() = c.ActorId.ToString()\n    static member Null = {\n        ActorId = ActorId.Undefined\n        MessagePump = NullMessagePump.Instance\n        }\n\n[<AutoOpen>]\nmodule ActorSystem =\n    type IMessagePump with\n        member c.Get id = {\n            ActorId = id\n            MessagePump = c\n            }\n\n        member c.Process<'a>(destId, msg : 'a) =\n            c.Send(destId, msg)\n            c.Process()\n\n        member c.Process<'a>(destId, sourceId, msg : 'a) =\n            c.Send(destId, sourceId, msg)\n            c.Process()\n\n    type ActorSystem with\n        member c.Register(factories : IActorFactory seq) =\n            for f in factories do\n                c.Register(f)\n\n        member c.Register(tryCreate : ActorId -> Actor voption) =\n            c.Register(ActorFactory.Create(tryCreate))\n\n        /// Creates for any actor ID\n        member c.Register(create : ActorId -> Actor) =\n            c.Register(ActorFactory.Create(create))\n\n        /// Creates conditionally for actor ID\n        member c.Register(predicate : ActorId -> bool, create : ActorId -> Actor) =\n            c.Register(ActorFactory.Create(predicate, create))\n\n        /// Creates for a specific actor ID\n        member c.Register(actorId : ActorId, create : ActorId -> Actor) =\n            c.Register(ActorFactory.Create(actorId, create))\n\n    /// Extensions for Container\n    type ActorSystem with\n        /// Creates conditionally for actor ID\n        member c.Register(predicate, dispatcherId, register : Container -> IDisposable) =\n            let create createId =\n                let inbox = new LazyContainerInbox(createId, register)\n                Actor(inbox, dispatcherId, inbox.Dispose)\n            c.Register(predicate, create)\n\n        member c.Register(predicate, register : Container -> IDisposable) =\n            c.Register(predicate, 0, register)\n            \n        /// Creates for any actor ID\n        member c.Register(dispatcherId, register : Container -> IDisposable) =\n            let predicate (_ : ActorId) = true\n            c.Register(predicate, dispatcherId, register)\n\n        member c.Register(register : Container -> IDisposable) =\n            c.Register(0, register)\n\n        /// Creates for a specific actor ID\n        member c.Register(actorId : ActorId, dispatcherId, register : Container -> IDisposable) =\n            c.Register((=)actorId, dispatcherId, register)\n\n        member c.Register(actorId : ActorId, register : Container -> IDisposable) =\n            c.Register(actorId, 0, register)\n    \n    type ActorReference with\n        member c.Send(msg) =\n            c.MessagePump.Send(c.ActorId, msg)\n\n        member c.Send(msg, sourceId) =\n            c.MessagePump.Send(c.ActorId, sourceId, msg)\n\n        member c.SendAll(span) =\n            c.MessagePump.SendAll(c.ActorId, span)\n\n        member c.SendAll<'a>(sourceId, span : ReadOnlySpan<'a>) =\n            c.MessagePump.SendAll(c.ActorId, sourceId, span)\n\n        member c.Process msg =\n            c.MessagePump.Process(c.ActorId, msg)\n    "
  },
  {
    "path": "src/Garnet/Channels.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Diagnostics\nopen System.Text\nopen Garnet.Composition.Comparisons\n\ntype internal EventHandler<'a> = ReadOnlyMemory<'a> -> unit\n\ntype IPublisher =\n    abstract member PublishAll<'a> : ReadOnlyMemory<'a> * ReadOnlyMemory<EventHandler<'a>> -> unit\n\ntype internal IChannel =\n    abstract member Clear : unit -> unit\n    abstract member Commit : unit -> unit\n    abstract member Publish : unit -> bool\n    abstract member SetPublisher : IPublisher voption -> unit\n\nmodule internal Publisher =\n    let formatBatch (messages : ReadOnlySpan<_>) =\n        let sb = StringBuilder()\n        let count = min 20 messages.Length\n        for i = 0 to count - 1 do \n            let msg = messages.[i]\n            sb.AppendLine().Append(sprintf \"%A\" msg) |> ignore\n        let remaining = messages.Length - count\n        if remaining > 0 then\n            sb.AppendLine().Append(sprintf \"(+%d)\" remaining) |> ignore\n        sb.ToString()\n\n    let publishAll<'a>(batch : ReadOnlyMemory<'a>) (handlers : ReadOnlySpan<_>) =\n        for i = 0 to handlers.Length - 1 do\n            let handler = handlers.[i]\n            try\n                handler batch\n            with\n            | ex -> \n                let str = \n                    sprintf \"Error in handler %d on %s batch (%d):%s\" \n                        i (typeof<'a> |> Format.typeToString) batch.Length (formatBatch batch.Span)\n                exn(str, ex) |> raise                \n\ntype Channel<'a>() =\n    let unsubscribed = List<EventHandler<'a>>()\n    let handlers = ResizableBuffer<EventHandler<'a>>(8)\n    let stack = ResizableBuffer<'a>(8)\n    let mutable publisher : IPublisher voption = ValueNone\n    let mutable events = ResizableBuffer<'a>(8)\n    let mutable pending = ResizableBuffer<'a>(8)\n    let mutable total = 0\n    member c.Clear() =\n        stack.Clear()\n        pending.Clear()\n    member c.SetPublisher(newPublisher) =\n        publisher <- newPublisher\n    member c.PublishAll(batch : ReadOnlyMemory<'a>) =\n        match publisher with \n        | ValueNone -> Publisher.publishAll batch handlers.WrittenSpan\n        | ValueSome publisher -> publisher.PublishAll(batch, handlers.WrittenMemory)\n    /// Dispatches event immediately/synchronously\n    member c.Publish(event) =\n        stack.WriteValue(event)\n        try\n            let mem = stack.WrittenMemory.Slice(stack.WrittenCount - 1, 1)\n            c.PublishAll(mem)\n        finally\n            stack.RemoveLast()\n    member c.Send(event) =\n        pending.WriteValue(event)\n        total <- total + 1\n    member c.SendAll(events : ReadOnlyMemory<_>) =\n        pending.Write(events.Span)\n        total <- total + events.Length\n    member c.Advance(count) =\n        pending.Advance(count)\n        total <- total + count\n    member c.GetMemory(sizeHint) =\n        pending.GetMemory(sizeHint)\n    member c.GetSpan(sizeHint) =\n        pending.GetSpan(sizeHint)\n    interface IBufferWriter<'a> with\n        member c.Advance(count) = c.Advance(count)\n        member c.GetMemory(sizeHint) = c.GetMemory(sizeHint)\n        member c.GetSpan(sizeHint) = c.GetSpan(sizeHint)\n    member c.OnAll(handler : EventHandler<_>) =\n        handlers.WriteValue(handler)\n        Disposable.Create(fun () -> unsubscribed.Add(handler))\n    /// Calls handler behaviors and prunes subscriptions after\n    member c.Publish() =\n        if events.WrittenCount = 0 then false\n        else\n            c.PublishAll(events.WrittenMemory)\n            true\n    /// Commit pending events to publish list and resets\n    member c.Commit() =\n        if unsubscribed.Count > 0 then\n            let mutable i = 0\n            while i < handlers.WrittenCount do\n                // only remove one occurrence\n                if unsubscribed.Remove handlers.[i] then\n                    // note ordering changes, but subscribers of the same\n                    // event type should not have any ordering dependencies\n                    handlers.[i] <- handlers.[handlers.WrittenCount - 1]\n                    handlers.RemoveLast()\n                else i <- i + 1\n            unsubscribed.Clear()\n        // clear prior events and swap in pending to current\n        events.Clear()\n        if pending.WrittenCount > 0 then\n            let temp = pending\n            pending <- events\n            events <- temp\n    interface IChannel with\n        member c.Clear() = c.Clear()\n        member c.Publish() = c.Publish()\n        member c.Commit() = c.Commit()\n        member c.SetPublisher(p) = c.SetPublisher(p)\n    override c.ToString() =            \n        sprintf \"%s: %dH %dP %dE %dT %dSE\" (typeof<'a> |> Format.typeToString) \n            handlers.WrittenCount pending.WrittenCount events.WrittenCount total stack.WrittenCount\n\ntype IChannels =\n    abstract member GetChannel<'a> : unit -> Channel<'a>\n\n/// Supports reentrancy\ntype Channels() =\n    let channels = List<IChannel>()\n    let mutable lookup = Array.zeroCreate<IChannel>(8)\n    let mutable publisher : IPublisher voption = ValueNone\n    member c.Count = channels.Count\n    member c.Clear() =\n        for channel in channels do\n            channel.Clear()\n    member c.Commit() =\n        for channel in channels do\n            channel.Commit()\n    member c.SetPublisher(newPublisher) =\n        publisher <- newPublisher\n        for channel in channels do\n            channel.SetPublisher newPublisher\n    /// Returns true if any events were handled\n    member c.Publish() =\n        // to handle reentrancy, avoid foreach and iterate up to current count\n        let mutable published = false\n        let count = channels.Count\n        for i = 0 to count - 1 do\n            published <- channels.[i].Publish() || published\n        published\n    member c.GetChannel<'a>() =\n        let id = MessageTypeId<'a>.Id\n        if id >= lookup.Length then\n            Buffer.resizeArray (id + 1) &lookup\n        let channel = lookup.[id]\n        if isNotNull channel then channel :?> Channel<'a>\n        else            \n            let channel = Channel<'a>()\n            channel.SetPublisher publisher\n            lookup.[id] <- channel :> IChannel\n            channels.Add(channel)\n            channel\n    interface IChannels with\n        member c.GetChannel<'a>() = c.GetChannel<'a>()\n    override c.ToString() =\n        let sb = StringBuilder()\n        sb.Append(\"Channels\") |> ignore\n        let groups = \n            channels\n            |> Seq.map (fun ch -> ch.GetType().GetGenericArguments().[0], ch)\n            |> Seq.groupBy (fun (t, _) -> t.Namespace)\n            |> Seq.sortBy (fun (key, _) -> key)\n        for ns, group in groups do\n            let name = if String.IsNullOrEmpty(ns) then \"[None]\" else ns\n            sb.AppendLine().Append(\"  \" + name) |> ignore\n            let channels = \n                group \n                |> Seq.sortBy (fun (t, _) -> t.Name)\n                |> Seq.map snd\n            for channel in channels do\n                sb.AppendLine().Append(\"    \" + channel.ToString()) |> ignore\n        sb.ToString()\n                \n[<AutoOpen>]\nmodule Channels =\n    type IChannels with    \n        member c.GetSender<'a>() =\n            c.GetChannel<'a>().Send\n\n        member c.Send(msg) =\n            c.GetChannel<'a>().Send(msg)\n\n        member c.OnAll<'a> handler =\n            c.GetChannel<'a>().OnAll(handler)\n\n        member c.Publish<'a>(event : 'a) =\n            c.GetChannel<'a>().Publish(event)\n\n        member c.OnAll<'a>() =\n            c.GetChannel<'a>().OnAll\n\n        member c.On<'a> handle =\n            c.GetChannel<'a>().OnAll(\n                fun batch -> \n                    let span = batch.Span\n                    for i = 0 to span.Length - 1 do\n                        handle span.[i])\n\n        /// Buffers incoming events in a second set of channels and subscribes\n        /// handler to the buffered channel. THis is useful for holding events\n        /// until a commit event is received or an appropriate update event occurs.\n        member c.BufferOnAll<'a>(buffer : IChannels, handler) =\n            let channel = buffer.GetChannel<'a>()\n            Disposable.Create [\n                // when first channels receive events, write to buffer\n                // events will need to be published by a separate mechanism\n                c.OnAll<'a> channel.SendAll                    \n                // subscribe handler to buffer rather than original channels\n                channel.OnAll(handler)\n                ]\n    \n        /// Buffers incoming events in a second set of channels and subscribes\n        /// handler to the buffered channel. THis is useful for holding events\n        /// until a commit event is received or an appropriate update event occurs.\n        member c.BufferOn<'a>(buffer : IChannels, handle) =\n            c.BufferOnAll<'a>(buffer, fun mem ->\n                for e in mem.Span do\n                    handle e)\n\n    type Channel<'a> with    \n        member c.Wait(msg) =\n            c.Send(msg)\n            Wait.All\n\n    type IChannels with    \n        member c.Wait(msg) =\n            c.GetChannel<'a>().Wait(msg)\n\ntype internal NullPublisher() =\n    static let mutable instance = NullPublisher() :> IPublisher\n    static member Instance = instance\n    interface IPublisher with\n        member c.PublishAll(_, _) = ()\n\n/// Single recorded timing consisting of N operations in a window of time.\n[<Struct>]\ntype Timing = {\n    Name : string\n    Start : int64\n    Stop : int64\n    Count : int\n    } with\n    member t.Duration = t.Stop - t.Start\n    override t.ToString() =\n        sprintf \"%A, %A to %A (%A), %A ops\" t.Name t.Start t.Stop t.Duration t.Count\n\ntype PrintPublisherOptions = {\n    EnableLog : bool\n    LogLabel : string\n    MessageSizeLimit : int\n    MinDurationMicroseconds : int\n    SendLog : string -> unit\n    SendTiming : Timing -> unit\n    CanSendLog : Type -> bool\n    CanSendTiming : Type -> bool\n    BasePublisher : IPublisher\n    Formatter : IFormatter\n    }\n\n/// Prints published events\ntype internal PrintPublisher(options) =\n    let sb = StringBuilder()\n    let mutable count = 0\n    interface IPublisher with        \n        member c.PublishAll<'a>(batch : ReadOnlyMemory<'a>, handlers) =\n            let start = Stopwatch.GetTimestamp()\n            let mutable completed = false\n            try\n                options.BasePublisher.PublishAll(batch, handlers)\n                completed <- true\n            finally\n                let typeInfo = Format.CachedTypeInfo<'a>.Info\n                let canLog =\n                    options.EnableLog &&\n                    options.CanSendLog typeof<'a> && \n                    options.Formatter.CanFormat<'a>()\n                let canTime =\n                    options.CanSendTiming typeof<'a>\n                // stop timer\n                let stop = Stopwatch.GetTimestamp()\n                // send timing\n                if canTime then\n                    options.SendTiming {\n                        Name = typeInfo.typeName\n                        Start = start\n                        Stop = stop\n                        Count = batch.Length \n                        }\n                // send log message\n                if canLog then\n                    let duration = stop - start\n                    let usec = duration * 1000L * 1000L / Stopwatch.Frequency |> int\n                    if not completed || usec >= options.MinDurationMicroseconds then\n                        sb.Append(\n                            sprintf \"[%s] %d: %dx %s to %d handlers in %dus%s\"\n                                options.LogLabel count batch.Length \n                                (typeof<'a> |> Format.typeToString)\n                                handlers.Length usec\n                                (if completed then \"\" else \" failed\")\n                            ) |> ignore\n                        // print messages\n                        if not typeInfo.isEmpty then\n                            Format.formatMessagesTo sb options.Formatter.Format batch.Span options.MessageSizeLimit\n                        sb.AppendLine() |> ignore\n                        options.SendLog (sb.ToString())\n                        sb.Clear() |> ignore\n                count <- count + 1\n    \ntype Publisher() =\n    static let mutable instance = Publisher() :> IPublisher\n    static member Default = instance\n    static member Null = NullPublisher.Instance\n    static member Print options = PrintPublisher(options) :> IPublisher\n    interface IPublisher with\n        member c.PublishAll<'a>(batch : ReadOnlyMemory<'a>, handlers : ReadOnlyMemory<_>) =\n            Publisher.publishAll batch handlers.Span\n\nmodule PrintPublisherOptions =\n    let enabled = {\n        EnableLog = true\n        LogLabel = \"\"\n        MessageSizeLimit = 10\n        MinDurationMicroseconds = 0\n        SendLog = printfn \"%s\"\n        SendTiming = ignore\n        CanSendLog = fun _ -> true\n        CanSendTiming = fun _ -> true\n        BasePublisher = Publisher.Default\n        Formatter = Formatter()\n        }\n"
  },
  {
    "path": "src/Garnet/Collections.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Garnet.Composition.Comparisons\nopen System.Buffers\n\nmodule internal Bits =\n    let inline bitCount x =\n        let x = x - ((x >>> 1) &&& 0x55555555)\n        let x = (x &&& 0x33333333) + ((x >>> 2) &&& 0x33333333)\n        (((x + (x >>> 4)) &&& 0x0F0F0F0F) * 0x01010101) >>> 24\n\n    let inline bitCount64 (x : uint64) =\n        bitCount (int (x >>> 32)) + bitCount (int (x &&& 0xffffffffUL))\n\n    let bitCount64Array (arr : uint64[]) =\n        let mutable total = 0\n        for x in arr do\n            total <- total + bitCount64 x\n        total\n\n    let inline getNextPow2 x =\n        let mutable y = x - 1\n        y <- y ||| (y >>> 1)\n        y <- y ||| (y >>> 2)\n        y <- y ||| (y >>> 4)\n        y <- y ||| (y >>> 8)\n        y <- y ||| (y >>> 16)\n        y <- y + 1\n        if y > 0 then y else 1\n\nmodule internal Buffer =\n    let expandArray required (arr : _[]) =\n        let newArr = Array.zeroCreate (Bits.getNextPow2 required)\n        arr.CopyTo(newArr, 0)\n        newArr\n\n    let resizeArray count (arr : byref<_[]>) =\n        if count > arr.Length then\n            arr <- expandArray count arr\n\n    let addToArray (count : byref<int>) (arr : byref<_[]>) x =\n        count <- count + 1\n        resizeArray count &arr\n        arr.[count - 1] <- x\n\n    let addAllToArray (count : byref<int>) (arr : byref<_[]>) (src : ReadOnlySpan<_>) =\n        let destOffset = count\n        count <- count + src.Length\n        resizeArray count &arr\n        let dest = Span(arr, destOffset, src.Length)\n        src.CopyTo(dest)\n        \n    let copyArrayMask mask (src : _[]) (dest : _[]) =\n        let mutable m = mask\n        let mutable i = 0\n        while m <> 0UL do\n            if m &&& 1UL <> 0UL then dest.[i] <- src.[i]\n            m <- m >>> 1\n            i <- i + 1\n\n    let clearArray (arr : _[]) =\n        Array.Clear(arr, 0, arr.Length)\n        \n    let clearArrayMask mask (arr : _[]) =\n        let mutable m = mask\n        let mutable i = 0\n        while m <> 0UL do\n            if m &&& 1UL <> 0UL then arr.[i] <- Unchecked.defaultof<_>\n            m <- m >>> 1\n            i <- i + 1\n\n    let getArrayBitCount64 (masks : uint64[]) count =\n        let mutable total = 0\n        for i = 0 to count - 1 do\n            total <- total + Bits.bitCount64 masks.[i]\n        total\n\n/// Similar to ArrayBufferWriter, but provides additional read/write access to \n/// the underlying array.\ntype internal ResizableBuffer<'a>(capacity) =\n    let mutable buffer = Array.zeroCreate<'a> capacity\n    let mutable pos = 0\n    member c.Item \n        with get i = buffer.[i]\n        and set i x = buffer.[i] <- x\n    member c.WrittenCount = pos\n    member c.WrittenSpan =\n        ReadOnlySpan(buffer, 0, pos)\n    member c.WrittenMemory =\n        ReadOnlyMemory(buffer, 0, pos)\n    member c.GetMemory(count) =\n        // note min allocation in case count is zero\n        let required = pos + max count 8\n        Buffer.resizeArray required &buffer\n        buffer.AsMemory().Slice(pos)\n    member c.GetSpan(count) =\n        c.GetMemory(count).Span\n    member c.Advance(count) = \n        if count < 0 then failwithf \"Cannot advance a negative value: %d\" count\n        pos <- pos + count\n    member c.WriteValue(value) =\n        if pos >= buffer.Length then\n            Buffer.resizeArray (pos + 1) &buffer\n        buffer.[pos] <- value\n        pos <- pos + 1\n    member c.RemoveLast() =\n        pos <- pos - 1\n    member c.Clear() =\n        Array.Clear(buffer, 0, pos)\n        pos <- 0\n    interface IBufferWriter<'a> with\n        member c.GetSpan(count) =\n            c.GetSpan(count)\n        member c.GetMemory(count) =\n            c.GetMemory(count)\n        member c.Advance(count) = \n            c.Advance(count)        \n    \n/// Mutable min-heap\ntype internal Heap<'k, 'a when 'k :> IComparable<'k>>() =\n    // create a dummy value for easier indexing\n    let items = List<KeyValuePair<'k, 'a>>()\n    do items.Add(Unchecked.defaultof<_>)\n    let compare a b = \n        items.[a].Key.CompareTo(items.[b].Key)\n    let swap a b =\n        let temp = items.[b]\n        items.[b] <- items.[a]\n        items.[a] <- temp\n    let getMinChildIndex parentIndex =\n        let ci = parentIndex * 2\n        if ci >= items.Count then -1\n        else\n            // if we have a second child that's smaller, pick it\n            // we know that if second exists, first exists due to shape\n            let offset =\n                if ci + 1 < items.Count && \n                    compare (ci + 1) ci < 0\n                    then 1 else 0\n            ci + offset\n    let rec siftDown index =\n        // start at top and swap down through min child\n        let ci = getMinChildIndex index\n        if ci >= 0 && compare index ci > 0 then\n            swap index ci\n            siftDown ci\n    let rec siftUp index =\n        // start at end and swap up through parent\n        // maintain parent/child invariant at each iteration\n        if index > 1 && compare index (index / 2) < 0 then\n            swap index (index / 2)\n            siftUp (index / 2)\n    member h.Items = items\n    member h.Count = items.Count - 1\n    member h.Top = items.[1]\n    member h.Insert(key, value) =\n        items.Add(KeyValuePair(key, value))\n        siftUp (items.Count - 1)\n    member h.RemoveMin() =\n        if h.Count = 0 then failwith \"Heap is empty\"\n        let top = h.Top\n        items.[1] <- items.[items.Count - 1]\n        items.RemoveAt(items.Count - 1)\n        siftDown 1\n        top\n    member h.Clear() =\n        while items.Count > 1 do items.RemoveAt(items.Count - 1)\n\n/// Mutable, min queue (min priority value dequeued first)\ntype PriorityQueue<'k, 'a when 'k :> IComparable<'k>>() =\n    let heap = Heap<'k, 'a>()\n    member q.Items = heap.Items\n    member q.Count = heap.Count\n    member q.Top = heap.Top\n    member q.Enqueue(priority, value) =\n        heap.Insert(priority, value)\n    member q.Dequeue() =\n        heap.RemoveMin().Value\n    member q.Clear() = \n        heap.Clear()\n\n[<AutoOpen>]\nmodule internal DictionarySlim =    \n    [<Struct>]\n    type Entry<'TKey, 'TValue \n        when 'TKey :> IEquatable<'TKey>\n        and 'TKey : equality> = {\n        mutable key : 'TKey\n        mutable value : 'TValue\n        mutable next : int\n    }\n\n    let sizeOneIntArray = Array.zeroCreate<int> 1\n    let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y    \n    \n    let inline hash x mask =\n        let h = x.GetHashCode()\n        h &&& mask\n\n// Adapted from:\n// https://github.com/dotnet/corefxlab/blob/master/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/DictionarySlim.cs\ntype internal DictionarySlim<'TKey, 'TValue \n    when 'TKey :> IEquatable<'TKey>\n    and 'TKey : equality>(capacity) =\n    let mutable _count = 0\n    let mutable _freeList = -1\n    let mutable _buckets = Array.zeroCreate<int> (max 2 capacity)\n    let mutable _entries = Array.zeroCreate<Entry<'TKey, 'TValue>> (max 2 capacity)\n    new() = DictionarySlim(2)\n    member internal c.Entries = _entries\n    member c.Count = _count\n    member c.Clear() =\n        _count <- 0\n        _freeList <- -1\n        // changed from original: keeping buffers to avoid GC\n        Array.Clear(_buckets, 0, _buckets.Length)\n        Array.Clear(_entries, 0, _entries.Length)\n    member c.TryGetValue(key : 'TKey, [<Out>] value : byref<'TValue>) =\n        let entries = _entries\n        let mutable result = false\n        let mutable i = _buckets.[hash key (_buckets.Length - 1)] - 1\n        value <- Unchecked.defaultof<'TValue>\n        while uint32 i < uint32 entries.Length && not result do\n            if eq key entries.[i].key then\n                value <- entries.[i].value\n                result <- true\n            i <- entries.[i].next\n        result\n    member c.Contains(key) =\n        let entries = _entries\n        let mutable result = false\n        let mutable i = _buckets.[hash key (_buckets.Length - 1)] - 1\n        while uint32 i < uint32 entries.Length && not result do\n            if eq key entries.[i].key then\n                result <- true\n            i <- entries.[i].next\n        result\n    member c.Remove(key : 'TKey) =\n        let entries = _entries\n        let bucketIndex = hash key (_buckets.Length - 1)\n        let mutable entryIndex = _buckets.[bucketIndex] - 1\n        let mutable lastIndex = -1\n        let mutable result = false\n        while entryIndex <> -1 && not result do\n            let candidate = entries.[entryIndex]\n            if eq candidate.key key then\n                if lastIndex <> -1 then\n                    // Fixup preceding element in chain to point to next (if any)\n                    entries.[lastIndex].next <- candidate.next\n                else\n                    // Fixup bucket to new head (if any)\n                    _buckets.[bucketIndex] <- candidate.next + 1\n                entries.[entryIndex] <- Unchecked.defaultof<_>\n                entries.[entryIndex].next <- -3 - _freeList // New head of free list\n                _freeList <- entryIndex\n                _count <- _count - 1\n                result <- true\n            lastIndex <- entryIndex\n            entryIndex <- candidate.next\n        result\n    member c.GetOrAddValueRef(key : inref<'TKey>) : byref<'TValue> =\n        let entries = _entries\n        let mutable bucketIndex = hash key (_buckets.Length - 1)\n        let mutable i = _buckets.[bucketIndex] - 1\n        let mutable resultIndex = -1\n        while uint32 i < uint32 entries.Length && resultIndex < 0 do\n            if eq key entries.[i].key then\n                resultIndex <- i                \n            i <- entries.[i].next\n        if resultIndex >= 0 then &entries.[resultIndex].value\n        else \n            // AddKey()\n            let mutable entries = _entries\n            let entryIndex =\n                if _freeList <> -1 then\n                    let entryIndex = _freeList\n                    _freeList <- -3 - entries.[_freeList].next\n                    entryIndex\n                else\n                    if _count = entries.Length || entries.Length = 1 then\n                        entries <- c.Resize()\n                        bucketIndex <- hash key (_buckets.Length - 1)\n                        // entry indexes were not changed by Resize\n                    _count\n            entries.[entryIndex].key <- key\n            entries.[entryIndex].next <- _buckets.[bucketIndex] - 1\n            _buckets.[bucketIndex] <- entryIndex + 1\n            _count <- _count + 1\n            &entries.[entryIndex].value\n    member private c.Resize() =\n        let mutable count = _count\n        let newSize = _entries.Length * 2\n        let entries = Array.zeroCreate<Entry<'TKey, 'TValue>> newSize\n        Array.Copy(_entries, 0, entries, 0, count)\n        let newBuckets = Array.zeroCreate entries.Length\n        while count > 0 do\n            count <- count - 1\n            let bucketIndex = hash entries.[count].key (newBuckets.Length - 1)\n            entries.[count].next <- newBuckets.[bucketIndex] - 1\n            newBuckets.[bucketIndex] <- count + 1\n        _buckets <- newBuckets\n        _entries <- entries\n        entries\n    member c.GetEnumerator() = \n        new Enumerator<'TKey,'TValue>(c)\n    interface IEnumerable<KeyValuePair<'TKey, 'TValue>> with\n        member c.GetEnumerator() =\n            new Enumerator<'TKey,'TValue>(c) :> IEnumerator<_>\n    interface IEnumerable with\n        member c.GetEnumerator() =\n            new Enumerator<'TKey,'TValue>(c) :> IEnumerator\n\nand internal Enumerator<'TKey, 'TValue\n    when 'TKey :> IEquatable<'TKey>\n    and 'TKey : equality> =\n    val _dictionary : DictionarySlim<'TKey, 'TValue>\n    val mutable _index : int\n    val mutable _count : int\n    val mutable _current : KeyValuePair<'TKey, 'TValue>\n    new(dict) = {\n        _dictionary = dict\n        _index = 0\n        _count = dict.Count\n        _current = Unchecked.defaultof<_>\n        }\n    member c.MoveNext() =\n        if c._count = 0 then\n            c._current <- Unchecked.defaultof<_>\n            false\n        else\n            c._count <- c._count - 1\n            while c._dictionary.Entries.[c._index].next < -1 do\n                c._index <- c._index + 1\n            c._current <-\n                new KeyValuePair<'TKey, 'TValue>(\n                    c._dictionary.Entries.[c._index].key,\n                    c._dictionary.Entries.[c._index].value)\n            c._index <- c._index + 1\n            true\n    member c.Current = c._current\n    member c.Reset() =\n        c._index <- 0\n        c._count <- c._dictionary.Count\n        c._current <- Unchecked.defaultof<_>\n    interface IEnumerator with\n        member c.Current = c._current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n    interface IEnumerator<KeyValuePair<'TKey, 'TValue>> with\n        member c.Current = c._current\n        member c.Dispose() = ()\n"
  },
  {
    "path": "src/Garnet/Comparisons.fs",
    "content": "﻿namespace Garnet.Composition\n\n// The purpose of this is to avoid equality operator allocations for value types.\n// Be careful if using this with floating point values.\n// https://github.com/dotnet/fsharp/issues/526\n// https://zeckul.wordpress.com/2015/07/09/how-to-avoid-boxing-value-types-in-f-equality-comparisons/\n#nowarn \"86\"\nmodule Comparisons =\n    let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y    \n    let inline (=) x y = eq x y\n    let inline (<>) x y = not (eq x y)\n     \n    let inline (=@) x y = Microsoft.FSharp.Core.Operators.(=) x y\n    let inline (<>@) x y = Microsoft.FSharp.Core.Operators.(<>) x y\n\n    let inline lt<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) < 0\n    let inline gt<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) > 0\n    let inline lte<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) <= 0\n    let inline gte<'a when 'a :> System.IComparable<'a>> (x:'a) (y:'a) = x.CompareTo(y) >= 0\n    let inline (<) x y = lt x y\n    let inline (>) x y = gt x y\n    let inline (<=) x y = lte x y\n    let inline (>=) x y = gte x y\n\n    let inline isNull x = obj.ReferenceEquals(x, null)\n    let inline isNotNull x = not (isNull x)\n"
  },
  {
    "path": "src/Garnet/Components.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Runtime.InteropServices\nopen Garnet.Composition.Comparisons\n\ntype ISegmentKeyMapper<'k, 'c> = \n    abstract GetSegmentKey : 'c -> 'k\n    abstract GetComponentIndex : 'c -> int\n\nmodule IdSegmentMapper =\n    let inline getSegmentKey<'k, 'c, 'm when 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>> (id : 'c) =\n        Unchecked.defaultof<'m>.GetSegmentKey(id)\n\n    let inline getComponentIndex<'k, 'c, 'm when 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>> (id : 'c) =\n        Unchecked.defaultof<'m>.GetComponentIndex(id)\n\n/// Adds convenience methods to access individual components\n[<Struct>]\ntype Components<'k, 'c, 'm, 'a \n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality \n        and 'c :> IComparable<'c>\n        and 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>>(segments : Segments<'k, 'a>) =\n    member c.Segments = segments\n    member c.Count = segments.GetComponentCount()\n    member internal c.Components = segments.Components\n    member c.Clear() =\n        segments.Clear()\n    member c.Commit() =\n        segments.Commit()\n    member c.Contains(id) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        let mask = segments.GetMask sid\n        (mask &&& (1UL <<< ci)) <> 0UL\n    member c.Get(id : 'c) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        let seg = segments.Get(sid)\n        if seg.Mask &&& (1UL <<< ci) = 0UL then \n            failwithf \"Cannot get %s %s\" (Format.typeToString typeof<'a>) (id.ToString())\n        &seg.Data.[ci]\n    member c.Set(id, value) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        let seg = segments.Get(sid)\n        if seg.Mask &&& (1UL <<< ci) = 0UL then \n            failwithf \"Cannot set %s %s\" (Format.typeToString typeof<'a>) (id.ToString())\n        seg.Data.[ci] <- value\n    member c.TryGet(id, [<Out>] value : byref<_>)=        \n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        match segments.TryFind(sid) with\n        | false, _ ->\n            value <- Unchecked.defaultof<_>\n            false\n        | true, si ->\n            let s = segments.[si]\n            if s.Mask &&& (1UL <<< ci) = 0UL then \n                value <- Unchecked.defaultof<_>\n                false\n            else \n                value <- s.Data.[ci]                \n                true\n    member c.GetOrDefault(id, fallback) =        \n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        match segments.TryFind(sid) with\n        | false, _ -> fallback\n        | true, si ->\n            let s = segments.[si]\n            if s.Mask &&& (1UL <<< ci) = 0UL then fallback\n            else s.Data.[ci]                \n    member c.Copy(srcId, destId) =\n        let mutable value = Unchecked.defaultof<_>\n        if c.TryGet(srcId, &value) \n            then c.Add(destId, value)\n            else c.Remove(destId)               \n    member c.Add(id, value) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        let data = segments.Add(sid, 1UL <<< ci)\n        data.[ci] <- value\n    /// Removes single component\n    member c.Remove(id) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        segments.Remove(sid, 1UL <<< ci)\n    override c.ToString() =\n        segments.ToString()\n    static member Create() = \n        Components<'k, 'c, 'm, 'a>(Segments<'k, 'a>())        \n\ntype IComponentStore<'k, 'c, 'm \n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality\n    and 'c :> IComparable<'c>\n    and 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>> =\n    abstract member GetComponents<'b> : unit -> Components<'k, 'c, 'm, 'b>\n\ntype ComponentStore<'k, 'c, 'm \n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality\n        and 'c :> IComparable<'c>\n        and 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>>(segments : SegmentStore<'k>) =\n    new() =\n        ComponentStore(SegmentStore())\n    member c.Segments = segments\n    member c.GetSegments<'a>() = \n        segments.GetSegments<'a>()\n    member c.GetComponents<'a>() =\n        Components(segments.GetSegments<'a>())\n    member c.Clear() =\n        segments.Clear()\n    member c.Handle(param, id, handler) =        \n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        let mask = 1UL <<< ci\n        segments.Handle(param, sid, mask, handler)\n    member c.Handle(param, sid, mask, handler) =        \n        segments.Handle(param, sid, mask, handler)\n    member c.Handle(param, handler) =      \n        segments.Handle(param, handler)\n    /// Removes ID component and assumes other components\n    /// will be cleaned up in commit\n    member c.Destroy(id) =\n        let sid = IdSegmentMapper.getSegmentKey<'k, 'c, 'm> id\n        let ci = IdSegmentMapper.getComponentIndex<'k, 'c, 'm> id\n        segments.GetSegments<'c>().Remove(sid, 1UL <<< ci)\n    member c.Commit() =\n        segments.Commit()\n    interface IComponentStore<'k, 'c, 'm> with\n        member c.GetComponents<'a>() = \n            c.GetComponents<'a>()\n    interface ISegmentStore<'k> with\n        member c.Handle(param, handler) =      \n            c.Handle(param, handler)\n        member c.Handle(param, sid, mask, handler) =        \n            c.Handle(param, sid, mask, handler)\n        member c.GetSegments<'a>() = \n            segments.GetSegments<'a>()\n    override c.ToString() =\n        segments.ToString()\n\n[<Struct>]\ntype Entity<'k, 'c, 'm\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality\n        and 'c :> IComparable<'c>\n        and 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>> =\n    val Id : 'c\n    val Components : ComponentStore<'k, 'c, 'm>\n    new(id, components) = { Id = id; Components = components }\n    member c.Add(x) = c.Components.GetComponents<_>().Add(c.Id, x)\n    member c.Set(x) = c.Components.GetComponents<_>().Set(c.Id, x)\n    member c.Remove<'a>() = c.Components.GetComponents<'a>().Remove(c.Id)\n    member c.Get<'a>() = &c.Components.GetComponents<'a>().Get(c.Id)    \n    member c.CopyTo<'a> destId = c.Components.GetComponents<'a>().Copy(c.Id, destId)    \n    member c.TryGet<'a>([<Out>] value : byref<_>) = c.Components.GetComponents<'a>().TryGet(c.Id, &value)\n    member c.GetOrDefault<'a> fallback = c.Components.GetComponents<'a>().GetOrDefault(c.Id, fallback)\n    member c.Has<'a>() = c.Components.GetComponents<'a>().Contains(c.Id)\n    member c.Destroy() = c.Components.Destroy(c.Id)\n    member c.With(x) = c.Add(x); c\n    member c.Without<'a>() = c.Remove<'a>(); c\n    override c.ToString() = \n        let printer = PrintHandler<'k>(UInt64.MaxValue)\n        c.Components.Handle((), c.Id, printer)\n        \"Entity \" + c.Id.ToString() + \": \" + printer.ToString()\n\ntype ComponentStore<'k, 'c, 'm\n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality\n    and 'c :> IComparable<'c>\n    and 'm : struct and 'm :> ISegmentKeyMapper<'k, 'c>> with\n    member c.Get(id) = Entity(id, c)"
  },
  {
    "path": "src/Garnet/Containers.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Buffers\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Garnet.Composition.Comparisons\n\ntype DisposableReference<'a when 'a :> IDisposable>(init : 'a) =\n    let mutable current = init\n    member c.Value = current\n    member c.Set(create) =\n        current.Dispose()\n        current <- create()\n    member c.Dispose() =\n        current.Dispose()\n    interface IDisposable with\n        member c.Dispose() =\n            c.Dispose()\n\n/// Wrapper over resource lookup with default types for ECS\ntype Container() =\n    let reg = Registry()\n    let channels = reg.Get<Channels>()\n    let scheduler = reg.Get<CoroutineScheduler>()\n    let segments = reg.Get<SegmentStore<int>>()\n    let outbox = reg.Get<Outbox>()\n    let eidPools = reg.Get<EidPools>()\n    let resources = reg.Get<ResourceCache>()\n    let components = ComponentStore(segments)\n    let eids = segments.GetSegments<Eid>()\n    member c.GetComponents<'a>() = components.GetComponents<'a>()\n    member c.GetSegments<'a>() = segments.GetSegments<'a>()\n    member c.GetChannel<'a>() = channels.GetChannel<'a>()\n    member c.SetFactory(x) = reg.SetFactory(x)\n    member c.Set(x) = reg.Set(x)\n    member c.Get() = &reg.Get()\n    member c.TryGet<'a>([<Out>] value : byref<'a>) =\n        reg.TryGet<'a>(&value)\n    member c.AddResource<'a>(key, resource) =\n        resources.AddResource<'a>(key, resource)\n    member c.TryGetResource<'a>(key, [<Out>] value : byref<'a>) =\n        resources.TryGetResource(key, &value)\n    member c.LoadResource<'a> key =\n        resources.LoadResource<'a>(key)\n    member c.Iter(param, handler) =\n        reg.Iter(param, handler)\n    member c.GetAddresses() =\n        outbox.Current.Addresses\n    member internal c.Clear() =\n        channels.Clear()\n        components.Clear()\n        eidPools.Clear()\n        scheduler.Clear()\n    member c.Commit() =\n        // Order of commits doesn't matter since we're just moving data\n        // into committed state and not calling any handlers.\n        channels.Commit()\n        // Copy removals from eids to other component types first,\n        // then apply eid changes to partition cache, then after all\n        // this commit all resulting component changes.\n        segments.ApplyRemovalsFrom(eids)   \n        eidPools.Apply(eids)\n        components.Commit()\n    /// Returns true if events were handled\n    member private c.DispatchOnce() = \n        c.Commit()\n        channels.Publish()\n    member private c.DispatchAll() = \n        while c.DispatchOnce() do ()\n    member private c.RunOnce() = \n        c.Commit()\n        scheduler.RunOnce()\n    member c.Run() = \n        c.DispatchAll()\n        while c.RunOnce() do\n            c.DispatchAll()\n    member c.Contains(eid : Eid) =\n        let sid = eid.SegmentIndex\n        let ci = eid.ComponentIndex\n        let mask = eids.GetMask sid\n        (mask &&& (1UL <<< ci)) <> 0UL\n    member c.Get(eid) = Entity(eid, components)\n    member internal c.CreateEid(partition) =\n        let eid = eidPools.Next(partition)\n        let sid = eid.SegmentIndex\n        let ci = eid.ComponentIndex\n        let data = eids.Add(sid, 1UL <<< ci)\n        data.[ci] <- eid\n        eid\n    member c.Handle(param, handler : ISegmentListHandler<_, int>) =\n        segments.Handle(param, handler)\n    member c.Handle(param, sid, mask, handler) =        \n        segments.Handle(param, sid, mask, handler)\n    member c.Handle(param, id, handler) =\n        components.Handle(param, id, handler)\n    member c.Destroy(eid : Eid) =\n        // Only removing from eids and relying on commit to remove\n        // other components.\n        let sid = eid.SegmentIndex\n        let ci = eid.ComponentIndex\n        eids.Remove(sid, 1UL <<< ci)\n    member c.Step(deltaTime) =\n        scheduler.Step(deltaTime)\n    member c.Start(coroutine) = \n        scheduler.Schedule(coroutine)\n    member c.SetPublisher(pub) =\n        channels.SetPublisher(pub)\n    member c.SetPublisher(pub) =\n        c.SetPublisher(ValueSome pub)\n    member c.UnsubscribeAll() =\n        channels.Clear()\n    interface IRegistry with\n        member c.SetFactory(x) = c.SetFactory(x)\n        member c.Set(x) = c.Set(x)\n        member c.Get() = &c.Get()\n        member c.TryGet<'a>([<Out>] value) = c.TryGet<'a>(&value)\n        member c.Iter(param, handler) =\n            c.Iter(param, handler)\n    interface IResourceCache with\n        member c.TryGetResource<'a>(key, [<Out>] value : byref<'a>) =\n            c.TryGetResource<'a>(key, &value)\n        member c.LoadResource<'a> key = c.LoadResource<'a>(key)\n        member c.AddResource(key, resource) = c.AddResource(key, resource)\n    interface IChannels with\n        member c.GetChannel<'a>() = channels.GetChannel<'a>()\n    interface IComponentStore<int, Eid, EidSegmentKeyMapper> with\n        member c.GetComponents<'a>() = \n            components.GetComponents<'a>()\n    interface ISegmentStore<int> with\n        member c.Handle(param, handler) =      \n            c.Handle(param, handler)\n        member c.Handle(param, sid, mask, handler) =        \n            c.Handle(param, sid, mask, handler)\n        member c.GetSegments<'a>() = \n            segments.GetSegments<'a>()\n    member c.SendAll(message, deliveryId) =\n        outbox.SendAll(message, deliveryId)\n    interface IOutbox with\n        member c.SendAll(message, deliveryId) =\n            outbox.SendAll(message, deliveryId)\n    member c.Receive(currentOutbox, message) =\n        // assign outbox for duration of call\n        use s = outbox.Push(currentOutbox, message)\n        let channel = c.GetChannel<'a>()\n        channel.PublishAll(ReadOnlyMemory(message.Buffer, 0, message.Count))\n        c.Run()\n    interface IInbox with\n        member c.Receive(outbox, message) =\n            c.Receive(outbox, message)\n    override c.ToString() = \n        reg.ToString()\n\ntype SystemRegistry() =\n    let dict = Dictionary<string, DisposableReference<IDisposable>>()\n    member private c.GetSubscription(name) =\n        Disposable.Create(fun () -> \n            dict.[name].Dispose()\n            dict.Remove(name) |> ignore)\n    member c.Contains(name) =\n        dict.ContainsKey(name)\n    member c.Add(name, create) =\n        match dict.TryGetValue(name) with\n        | true, entry -> entry.Set(create)\n        | false, _ -> \n            let entry = new DisposableReference<_>(create())\n            dict.Add(name, entry)\n        c.GetSubscription(name)\n    member c.Dispose() =\n        for entry in dict.Values do\n            entry.Dispose()\n        dict.Clear()\n    member c.ToString(writer : IStringBlockWriter) =\n        if writer.BeginList(\"Systems\", dict.Count) then\n            for name in Seq.sort dict.Keys do\n                writer.Write(name)\n            writer.End()\n    override c.ToString() =\n        StringBlockWriter.Format(c.ToString)\n\ntype Container with\n    member c.GetSourceId() =\n        c.GetAddresses().SourceId\n\n    member c.GetDestinationId() =\n        c.GetAddresses().DestinationId\n\n    member c.BeginSend<'a>() =\n        let writer = c.Get<MessageWriter<'a>>()\n        writer.Outbox <- c\n        writer\n        \n    member c.BeginSend<'a>(destId : ActorId) =\n        let writer = c.BeginSend<'a>()\n        writer.DestinationId <- destId\n        writer\n\n    member c.BeginSend<'a>(destId : ActorId, sourceId : ActorId) =\n        let writer = c.BeginSend<'a>()\n        writer.DestinationId <- destId\n        writer.SourceId <- sourceId\n        writer\n\n    member c.BeginSend<'a>(destId : ActorId, sourceId : ActorId, deliveryId) =\n        let writer = c.BeginSend<'a>()\n        writer.DestinationId <- destId\n        writer.SourceId <- sourceId\n        writer.DeliveryId <- deliveryId\n        writer\n\n    member c.Create(partition) =\n        let eid = c.CreateEid(partition)\n        c.Get eid\n\n    member c.Create() = c.Create(0)\n\n    member c.Create(eid : Eid) =\n        c.Get(eid).With(eid)\n\n    member c.DestroyAll() =\n        c.GetSegments<Eid>().RemoveAll()\n\n    member c.Run(msg) = \n        c.Send(msg)\n        c.Run()\n\n    member c.BeginRespond() =\n        c.BeginSend(c.GetSourceId())\n\n    member c.Respond(message) =\n        c.Send(c.GetSourceId(), message)\n\n    member c.AddSystems(systems) =\n        systems\n        |> Seq.map (fun sys -> sys c)\n        |> Disposable.Create \n\n    member c.AddSystem(name : string, register : Container -> IDisposable) =\n        let reg = c.Get<SystemRegistry>()\n        reg.Add(name, fun () -> register c)\n\n    member c.AddSystem(actorId, actorOutbox, register : Container -> IDisposable) =\n        let outbox = c.Get<Outbox>()\n        use s = outbox.Push(actorOutbox, {\n            SourceId = ActorId.Undefined\n            DestinationId = actorId\n            Pool = ArrayPool.Shared\n            Buffer = ArrayPool.Shared.Rent(1)\n            Count = 1\n            })\n        let sub = register c\n        c.Commit()\n        sub\n\n    /// Stores a state value in registry and calls registration when state events occur.\n    /// This is useful for allowing a container to have multiple states with their own\n    /// subscriptions and transition logic.\n    member c.AddStateMachine<'a>(initState, registerState) =\n        let state = new DisposableReference<IDisposable>(Disposable.Null)\n        let setState e =\n            c.Set<'a>(e)\n            // Register subscriptions specific to new state, replacing prior\n            state.Set(fun () -> registerState e c)\n        setState initState\n        Disposable.Create [\n            // when state message arrives\n            c.On<'a> setState\n            state :> IDisposable\n            ]        \n    \n    static member Create(register : Container -> IDisposable) =\n        let c = Container()\n        register c |> ignore\n        c.Commit()\n        c\n\n/// Allows container creation in actor thread instead of main thread\ntype LazyContainerInbox(actorId, register) =\n    let container = Container()\n    let mutable sub = Disposable.Null\n    let mutable isCreated = false\n    interface IInbox with\n        member c.Receive(outbox, message) =\n            if not isCreated then                \n                sub <- container.AddSystem(actorId, outbox, register)\n                isCreated <- true\n            container.Receive(outbox, message)\n    member c.Dispose() = sub.Dispose()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "src/Garnet/Coroutines.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Composition.Comparisons\n\n[<Struct>]\ntype Wait =\n    val Duration : int64\n    new(duration) = { Duration = duration }\n    static member All = Wait(-1L)\n    \ntype internal StackScheduler() =\n    let temp = List<_>()\n    let pending = List<_>()\n    let active = List<_>()\n    let frames = List<_>()\n    member c.Clear() =\n        pending.Clear()\n        active.Clear()\n        frames.Clear()\n    member c.Schedule(coroutine : Wait seq) =\n        pending.Add coroutine\n    member c.Enqueue e =\n        active.Add e\n    // Returns true if work was done\n    member c.RunOnce(iterate : Action<_>) =\n        let hasPending = pending.Count > 0\n        if hasPending then\n            frames.Add(pending.Count)\n            for coroutine in pending do\n                let e = coroutine.GetEnumerator()\n                c.Enqueue e\n            pending.Clear()\n        let hasFrames = frames.Count > 0\n        if hasFrames then\n            let frameSize = frames.[frames.Count - 1]\n            let priorActiveCount = active.Count\n            let frameStart = active.Count - frameSize\n            // transfer into temp list and clear frame\n            for i = frameStart to active.Count - 1 do\n                temp.Add(active.[i])\n            active.RemoveRange(frameStart, frameSize)\n            // run coroutines, possibly re-enqueuing\n            try\n                for e in temp do\n                    iterate.Invoke e\n            finally\n                temp.Clear()\n                // update frame\n                let removedCount = priorActiveCount - active.Count\n                let newFrameSize = frameSize - removedCount\n                if newFrameSize = 0 \n                    then frames.RemoveAt(frames.Count - 1)\n                    else frames.[frames.Count - 1] <- newFrameSize\n        hasPending || hasFrames      \n    override c.ToString() =\n        sprintf \"Stack: %d pending, %d active, frames: %s\" pending.Count active.Count\n            (String.Join(\", \", frames))\n            \ntype internal TimeScheduler() =\n    let mutable time = 0L\n    let active = Garnet.Composition.PriorityQueue<int64, _>()\n    member c.Enqueue(e : IEnumerator<Wait>) =\n        let delay = e.Current.Duration\n        let nextTime = time + delay\n        active.Enqueue(nextTime, e)\n    member c.RunOnce(iterate : Action<_>) =\n        let mutable iterCount = 0\n        while active.Count > 0 && active.Top.Key <= time do\n            iterCount <- iterCount + 1\n            let e = active.Dequeue()\n            iterate.Invoke e\n        iterCount > 0\n    member c.Step(deltaTime) =\n        time <- time + deltaTime\n    member c.Clear() =\n        active.Clear()\n        time <- 0L\n    override c.ToString() =\n        sprintf \"Timed: %d total, due: %s\" active.Count\n            (String.Join(\", \", active.Items |> Seq.map (fun p -> p.Key)))\n            \ntype CoroutineScheduler() =\n    let timed = TimeScheduler()\n    let stacked = StackScheduler()\n    let iterate = \n        Action<_>(fun (e : IEnumerator<Wait>) ->\n            let isContinued = \n                try \n                    e.MoveNext()\n                with\n                | ex -> \n                    let str = sprintf \"Error in coroutine %s\" (e.ToString())\n                    exn(str, ex) |> raise                \n            if isContinued then \n                // Add to queue based on scheduling type:\n                // - Non-negative indicates we want to wait for some duration\n                // - Negative indicates we want to wait on nested coroutines\n                let isTimed = e.Current.Duration >= 0L\n                if isTimed then timed.Enqueue e else stacked.Enqueue e)\n    member c.Schedule(coroutine) =\n        stacked.Schedule(coroutine)\n    member c.RunOnce() =\n        let hasStacked = stacked.RunOnce(iterate)\n        let hasTimed = timed.RunOnce(iterate)\n        hasStacked || hasTimed\n    member c.Run() =\n        while c.RunOnce() do ()      \n    member c.Step(deltaTime) =\n        timed.Step(deltaTime)\n    member c.Clear() =\n        timed.Clear()\n        stacked.Clear()\n    override c.ToString() =\n        sprintf \"Coroutines\\n  %s\\n  %s\" (stacked.ToString()) (timed.ToString())\n"
  },
  {
    "path": "src/Garnet/Entities.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Runtime.CompilerServices\nopen Garnet.Composition.Comparisons\n\nmodule Eid =\n    // Entity ID bits:\n    // gggg gggg [ pppp xxxx xxxx xxxx xxxx xxxx ]\n    // (8)  g: generation, max 256\n    // (24) Slot:\n    //      (4)  p: partition, max 16\n    //      (20) x: index, max ~1,000,000\n    \n    [<Literal>]\n    let TotalBits = 32\n\n    [<Literal>]\n    let GenBits = 8\n\n    [<Literal>]\n    let GenCount = 256\n\n    [<Literal>]\n    let GenMask = 255\n\n    [<Literal>]\n    let MaxGen = GenMask\n\n    [<Literal>]\n    let SlotBits = 24\n\n    [<Literal>]\n    let SlotCount = 0x1000000\n    \n    [<Literal>]\n    let SlotMask = 0xffffff\n\n    [<Literal>]\n    let PartitionBits = 4\n\n    [<Literal>]\n    let PartitionCount = 0x10\n\n    [<Literal>]\n    let PartitionMask = 0xf\n\n    [<Literal>]\n    let IndexBits = 20\n    \n    [<Literal>]\n    let IndexCount = 0x100000\n\n    [<Literal>]\n    let IndexMask = 0xfffff\n\n    [<Literal>]\n    let SegmentToPartitionBits = 14\n\n    [<Literal>]\n    let SegmentInPartitionMask = 0x3fff\n\n/// 32-bit entity ID\n[<Struct>]\ntype Eid =\n    val Value : int\n    new(value) = { Value = value }\n    new(gen, partition, index) = {\n        Value =\n            (gen <<< Eid.SlotBits) |||\n            (partition <<< Eid.IndexBits) |||\n            index\n        }\n    member inline eid.IsDefined = eid.Value <> 0\n    member inline eid.IsUndefined = eid.Value = 0\n    member inline eid.Index =\n        eid.Value &&& Eid.IndexMask\n    member inline eid.Slot =\n        eid.Value &&& Eid.SlotMask\n    member inline eid.Gen =\n        uint32 eid.Value >>> Eid.SlotBits |> int\n    member inline eid.Partition =\n        (eid.Value >>> Eid.IndexBits) &&& Eid.PartitionMask\n    member inline eid.SegmentIndex = \n        eid.Slot >>> Segment.SegmentBits\n    member inline eid.ComponentIndex = \n        eid.Value &&& Segment.SegmentMask\n    member inline eid.WithGen(gen) =\n        Eid(eid.Slot ||| (gen <<< Eid.SlotBits))\n    member inline eid.IncrementGen() =\n        let next = (eid.Gen + 1) &&& Eid.GenMask\n        eid.WithGen(next)\n    override e.ToString() =\n        \"0x\" + e.Value.ToString(\"x\")\n    member eid.ToPartString() =\n        sprintf \"%d %d %d\" eid.Gen eid.Partition eid.Index\n    static member inline Undefined = Eid 0\n    static member inline SegmentToPartition(sid) =\n        sid >>> Eid.SegmentToPartitionBits\n\n[<Struct>]\ntype EidSegmentKeyMapper =\n    interface ISegmentKeyMapper<int, Eid> with\n        [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n        member c.GetSegmentKey(id) = id.SegmentIndex\n        [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n        member c.GetComponentIndex(id) = id.ComponentIndex\n\ntype Entity = Entity<int, Eid, EidSegmentKeyMapper>\n\ntype EidPool(partition) =\n    let mutable known = Array.zeroCreate 1\n    let mutable used = Array.zeroCreate 1\n    let mutable eids = Array.zeroCreate 64\n    let mutable current = Segment.SegmentBits - 1\n    let mutable mask = 0UL\n    member c.Used = Bits.bitCount64Array used\n    member c.Allocated = Bits.bitCount64Array known\n    member c.Total = c.Used + c.Allocated\n    member c.SegmentCount = known.Length\n    member private c.EnsureSize si =\n        let required = si + 1\n        if used.Length < required then\n            Buffer.resizeArray required &known\n            Buffer.resizeArray required &used\n            Buffer.resizeArray (required * 64) &eids\n    member c.Clear() =\n        Array.Clear(known, 0, known.Length)\n        Array.Clear(used, 0, used.Length)\n        Array.Clear(eids, 0, eids.Length)\n        c.Reset()\n    member c.Reset() =\n        current <- Segment.SegmentBits - 1\n        mask <- 0UL\n    member c.Next() =\n        if mask = 0UL then\n            // Seek until unused segment found. Note we always increment,\n            // ensuring we start at segment 1 to avoid eid zero.\n            let mutable si = (current >>> 6) + 1\n            c.EnsureSize si\n            while used.[si] = UInt64.MaxValue do\n                si <- si + 1\n                current <- si * 64\n                c.EnsureSize si\n            // allocate new eids as needed\n            let knownMask = known.[si]\n            if knownMask <> UInt64.MaxValue then\n                let offset = si * 64\n                let mutable m = ~~~knownMask\n                let mutable i = offset\n                while m <> 0UL do\n                    if m &&& 1UL <> 0UL then \n                        eids.[i] <- Eid(0, partition, i)\n                    m <- m >>> 1\n                    i <- i + 1\n                known.[si] <- UInt64.MaxValue\n            mask <- ~~~used.[si]\n            current <- (si <<< 6) - 1\n        // increment first with assumption that we start at -1\n        current <- current + 1\n        // advance to next unused slot with segment\n        while mask &&& 1UL = 0UL do\n            mask <- mask >>> 1\n            current <- current + 1\n        // claim eid and advance to next\n        let eid = eids.[current]        \n        mask <- mask >>> 1\n        eid\n    member c.Recycle(eid : Eid) =\n        c.Apply {\n            Data = null\n            Id = eid.SegmentIndex\n            Mask = 0UL\n            RemovalMask = 1UL <<< eid.ComponentIndex\n        }\n    member internal c.Apply(seg : PendingSegment<int, Eid>) =\n        let sid = seg.Id &&& Eid.SegmentInPartitionMask\n        c.EnsureSize sid\n        let offset = sid * 64\n        if seg.Mask &&& seg.RemovalMask <> 0UL then\n            failwithf \"Segment contains overlapping add/remove\"\n        // When eid added and not previously known, write to pool with \n        // incremented gen.\n        if seg.Mask <> 0UL then\n            let addMask = seg.Mask &&& ~~~known.[sid]\n            let mutable m = addMask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then \n                    eids.[offset + i] <- seg.Data.[i].IncrementGen()\n                m <- m >>> 1\n                i <- i + 1\n            known.[sid] <- known.[sid] ||| addMask\n            used.[sid] <- used.[sid] ||| seg.Mask\n        // When eid removed, mark as unused and increment.\n        if seg.RemovalMask <> 0UL then\n            // Note we're not checking if used or not -- if not marked as used, \n            // eid was created/destroyed before commit, but we still need to \n            // return it to increment gen.\n            let removalMask = seg.RemovalMask\n            // Since removal seg doesn't have populated eids, we must take them\n            // from stored value in pool, which means they must be known. In the\n            // case of restore, expect that add would always be called first to\n            // populate eids.\n            if removalMask &&& known.[sid] <> removalMask then\n                failwithf \"Cannot return unknown IDs to pool\"\n            let mutable m = removalMask\n            let mutable i = offset\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then \n                    eids.[i] <- eids.[i].IncrementGen()\n                m <- m >>> 1\n                i <- i + 1\n            used.[sid] <- used.[sid] &&& ~~~removalMask\n        c.Reset()\n    override p.ToString() =\n        let formatBits known used =\n            Array.init 64 (fun i ->\n                let k = (known >>> i) &&& 1UL\n                let u = (used >>> i) &&& 1UL\n                match k, u with\n                | 0UL, 0UL -> ' '\n                | 0UL, 1UL -> 'u'\n                | 1UL, 0UL -> '.'\n                | 1UL, 1UL | _ -> 'x')\n                |> String\n        sprintf \"%d alloc, %d used, %d total, %d segs%s\" \n            p.Allocated p.Used p.Total used.Length\n            (seq {\n                for i = 0 to used.Length - 1 do\n                    let k = known.[i] \n                    let u = used.[i]\n                    if k ||| u <> 0UL then\n                        yield sprintf \"%d %s\" i (formatBits k u)\n                } |> Format.listToString \"    \" \"\")\n\ntype EidPools() =\n    let pools = Array.init Eid.PartitionCount EidPool\n    member c.Count = pools.Length\n    member c.Next p = \n        pools.[p].Next()\n    member c.Apply(active : Segments<int, Eid>) =\n        for i = 0 to active.PendingCount - 1 do\n            let seg = active.GetPending i\n            let p = Eid.SegmentToPartition(seg.Id)\n            pools.[p].Apply seg        \n    member c.Clear() =\n        for pool in pools do\n            pool.Clear()\n    override c.ToString() =\n        let prefix = \"\"\n        pools\n        |> Seq.mapi (fun i p -> \n            if p.Total > 0 \n            then sprintf \"%d: %s\" i (p.ToString()) \n            else \"\")\n        |> Seq.filter (fun str -> str.Length > 0)\n        |> Format.listToString (prefix + \"  \") (prefix + \"Pools\")\n"
  },
  {
    "path": "src/Garnet/Formatting.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections\nopen System.Collections.Generic\nopen System.Text\nopen Garnet.Composition.Comparisons\n\n/// Provides debug formatting for specific types\ntype IFormatter =\n    abstract member Format<'a> : 'a -> string\n    abstract member CanFormat<'a> : unit -> bool\n        \n/// Provides debug formatting for specific types\ntype Formatter() =\n    let skippedTypes = HashSet<Type>()\n    let dict = Dictionary<Type, obj>()\n    member c.Skip(t : Type) =\n        skippedTypes.Add(t) |> ignore\n    member c.Skip<'a>() =\n        c.Skip(typeof<'a>)\n    member c.Register(t : Type, format) =\n        dict.[t] <- format\n    member c.Register<'a>(action : 'a -> string) =\n        dict.[typeof<'a>] <- action\n    member c.Format<'a> e =\n        match dict.TryGetValue(typeof<'a>) with\n        | true, x -> \n            let handle = x :?> 'a -> string\n            handle e\n        | false, _ -> sprintf \"%A\" e\n    interface IFormatter with\n        member c.Format<'a> e =\n            c.Format<'a> e\n        member c.CanFormat<'a>() =\n            not (skippedTypes.Contains(typeof<'a>))\n\ntype IStringBlockWriter =\n    abstract Begin : string * string -> bool\n    abstract Write : string -> unit\n    abstract End : unit -> unit\n\n[<AutoOpen>]\nmodule StringBlockWriter =\n    type IStringBlockWriter with\n        member c.Begin(name) =\n            c.Begin(name, name)\n\n        member c.BeginList(name, count) =\n            let str = sprintf \"%s (%d)\" name count\n            if count = 0 \n                then c.Write(str); false\n                else c.Begin(str, name)\n\ntype StringBlockWriter() =\n    let sb = StringBuilder()\n    let mutable indent = 0\n    member private c.AppendLine(str : string) =\n        for i = 0 to indent - 1 do\n            sb.Append(\"  \") |> ignore\n        sb.AppendLine(str) |> ignore\n    interface IStringBlockWriter with\n        member c.Begin(name, _) =\n            c.AppendLine(name)\n            indent <- indent + 1\n            true\n        member c.Write(text) =\n            c.AppendLine(text)\n        member c.End() =\n            indent <- indent - 1\n    member c.Clear() =\n        sb.Clear()\n    override c.ToString() =\n        sb.ToString()\n    static member Format(toString) =\n        let w = StringBlockWriter()\n        toString w\n        w.ToString()\n\nmodule internal Format =\n    let private getNonGenericTypeName (name : string) =\n        let last = name.LastIndexOf('`')\n        if last < 0 then name else name.Substring(0, last)\n\n    let rec typeToString (t : Type) =\n        let name = getNonGenericTypeName t.Name\n        let args = t.GetGenericArguments()\n        if args.Length = 0 then name\n        else sprintf \"%s<%s>\" name (String.Join(\",\", args\n            |> Seq.map typeToString |> Seq.toArray))\n    \n    let isEmptyType (t : Type) =\n        not (t.IsPrimitive || t.IsEnum || t.GetProperties().Length > 0)\n\n    let formatRecord indent value =        \n        let str = sprintf \"%A\" value\n        str.Replace(\"\\n\", \"\\n\" + indent)\n        \n    let listInfoToString (list : IList) =\n        let args = list.GetType().GetGenericArguments()\n        sprintf \"%s (%d)\" (args.[0] |> typeToString) list.Count\n    \n    let addIndent (str : string) =\n        str.Replace(\"\\n\", \"\\n  \")\n\n    let formatList name count (str : string) =\n        sprintf \"%s (%d)%s\" name count\n            (if count > 0 then \":\\n  \" + addIndent str else \"\")\n\n    let listToString prefix title items =\n        let str =\n            String.Join(\"\", \n                items \n                |> Seq.map (fun x -> \"\\n\" + prefix + x.ToString()) \n                |> Seq.toArray)\n        sprintf \"%s%s\" title str \n\n    let reverseString (str : string) = \n        let ch = str.ToCharArray()\n        Array.Reverse(ch)\n        String(ch)\n\n    let maskToString maxBits (mask : uint64) =\n        Convert.ToString(int64 mask, 2)\n            .PadLeft(maxBits, '0')\n            .Replace('0', '.')\n            .Replace('1', 'x')\n            |> reverseString\n                    \n    [<Struct>]\n    type CachedTypeInfo = {\n        isEmpty : bool\n        typeName : string\n        }\n\n    let isEnumerable<'a> =\n        typeof<'a>.GetInterfaces() \n        |> Seq.exists (fun i -> \n            let isEnumerable = \n                obj.ReferenceEquals(i, typeof<IEnumerable>) || \n                obj.ReferenceEquals(i, typeof<IEnumerator>)\n            let isGenericEnumerable = \n                i.IsGenericType && \n                (obj.ReferenceEquals(i.GetGenericTypeDefinition(), typeof<IEnumerable<_>>) || \n                    obj.ReferenceEquals(i.GetGenericTypeDefinition(), typeof<IEnumerator<_>>))\n            isEnumerable || isGenericEnumerable)\n\n    type CachedTypeInfo<'a>() =        \n        static member val Info = {\n            // special cases\n            // - strings already have logging\n            // - don't want enumerators to execute\n            typeName = \"Handle \" + typeToString typeof<'a>\n            isEmpty = isEmptyType typeof<'a>\n            }\n\n    let formatMessagesTo (sb : StringBuilder) (formatMsg : _ -> string) (batch : ReadOnlySpan<_>) maxCount =\n        let printCount = min batch.Length maxCount\n        for i = 0 to printCount - 1 do\n            let msg = batch.[i]\n            sb.AppendLine() |> ignore\n            sb.Append(\"  \") |> ignore\n            sb.Append(formatMsg msg) |> ignore\n            //sb.Append(sprintf \"%A\" msg) |> ignore\n        // count of messages not printed\n        let remaining = batch.Length - printCount\n        if remaining > 0 then\n            sb.AppendLine() |> ignore\n            sb.Append(sprintf \"  +%d\" remaining) |> ignore\n    \n    let formatIndexedList prefix (segments : ReadOnlyMemory<_>) =\n        segments.ToArray()\n        |> Seq.mapi (fun i x -> sprintf \"%d %s\" i (x.ToString()))\n        |> listToString prefix \"\" \n"
  },
  {
    "path": "src/Garnet/Garnet.fsproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"Comparisons.fs\" />\n    <Compile Include=\"Collections.fs\" />\n    <Compile Include=\"Formatting.fs\" />\n    <Compile Include=\"Messaging.fs\" />\n    <Compile Include=\"Registry.fs\" />\n    <Compile Include=\"Resources.fs\" />\n    <Compile Include=\"Segments.fs\" />\n    <Compile Include=\"Queries.fs\" />\n    <Compile Include=\"Components.fs\" />\n    <Compile Include=\"Coroutines.fs\" />\n    <Compile Include=\"Channels.fs\" />\n    <Compile Include=\"Entities.fs\" />\n    <Compile Include=\"Containers.fs\" />\n    <Compile Include=\"Actors.fs\" />\n  </ItemGroup>\n  <PropertyGroup>\n    <Description>F# game composition library, including ECS and actor-like messaging.</Description>\n    <PackageTags>fsharp ecs actor entity component game</PackageTags>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"System.Memory\" Version=\"4.5.4\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/Garnet/Messaging.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Buffers\nopen System.Threading\nopen Garnet.Composition.Comparisons\n\n/// Identifies an actor\n[<Struct>]\ntype ActorId =\n    val Value : int\n    new(value) = { Value = value }\n    override e.ToString() = \"0x\" + e.Value.ToString(\"x\")\n    member e.IsDefined = e.Value <> 0\n    member e.IsUndefined = e.Value = 0\n    static member inline Undefined = ActorId 0\n\n[<Struct>]\ntype Message<'a> = {\n    Buffer : 'a[]\n    Count : int\n    SourceId : ActorId\n    DestinationId : ActorId\n    Pool : ArrayPool<'a>\n    }\n\ntype IOutbox =\n    abstract member SendAll<'a> : Message<'a> * ActorId -> unit\n     \n[<AutoOpen>]\nmodule Mailbox =\n    type IBufferWriter<'a> with\n        member c.WriteValue(x) =\n            c.GetSpan(1).[0] <- x\n            c.Advance(1)\n\n    type IOutbox with\n        member c.Send<'a>(destId : ActorId, payload : 'a) =\n            c.Send(destId, ActorId.Undefined, destId, payload)\n\n        member c.Send<'a>(destId : ActorId, sourceId : ActorId, payload : 'a) =\n            c.Send(destId, sourceId, destId, payload)\n\n        member c.Send<'a>(destId : ActorId, sourceId : ActorId, deliveryId : ActorId, payload : 'a) =\n            let arr = ArrayPool.Shared.Rent(1)\n            arr.[0] <- payload\n            c.SendAll({\n                Buffer = arr\n                Count = 1\n                SourceId = sourceId\n                DestinationId = destId\n                Pool = ArrayPool.Shared\n                }, deliveryId)\n\n        member c.SendAll<'a>(destId : ActorId, payload : ReadOnlySpan<'a>) =\n            c.SendAll(destId, ActorId.Undefined, destId, payload)\n\n        member c.SendAll<'a>(destId : ActorId, sourceId : ActorId, payload : ReadOnlySpan<'a>) =\n            c.SendAll(destId, sourceId, destId, payload)\n\n        member c.SendAll<'a>(destId : ActorId, sourceId : ActorId, deliveryId : ActorId, payload : ReadOnlySpan<'a>) =\n            let arr = ArrayPool.Shared.Rent(payload.Length)\n            payload.CopyTo(arr.AsSpan())\n            c.SendAll({\n                Buffer = arr\n                Count = payload.Length\n                SourceId = sourceId\n                DestinationId = destId\n                Pool = ArrayPool.Shared\n                }, deliveryId)\n\ntype NullOutbox() =\n    static let mutable instance = NullOutbox() :> IOutbox\n    static member Instance = instance\n    interface IOutbox with\n        member c.SendAll<'a>(message : Message<'a>, _) =\n            if message.Buffer.Length > 0 then\n                message.Pool.Return(message.Buffer, true)\n\n/// Similar to ArrayBufferWriter, but uses ArrayPool for allocations\ntype MessageWriter<'a>() =\n    let mutable arr = Array.Empty<'a>()\n    let mutable pos = 0\n    member val Outbox = NullOutbox.Instance with get, set\n    member val SourceId = ActorId.Undefined with get, set\n    member val DestinationId = ActorId.Undefined with get, set\n    member val DeliveryId = ActorId.Undefined with get, set\n    member c.WrittenCount = pos\n    member c.WrittenSpan = ReadOnlySpan(arr, 0, pos)\n    member c.WrittenMemory = ReadOnlyMemory(arr, 0, pos)\n    member c.Allocate(minSize) =\n        // Note min allocation in case count is zero\n        let required = pos + max minSize 16\n        if required > arr.Length then\n            let newMem = ArrayPool.Shared.Rent(required)\n            arr.CopyTo(newMem, 0)\n            ArrayPool.Shared.Return(arr, true)\n            arr <- newMem        \n    member c.WriteValue(x) =\n        if pos >= arr.Length then\n            c.Allocate(1)\n        arr.[pos] <- x\n        pos <- pos + 1\n    member c.Advance(count) =\n        pos <- pos + count\n    member c.GetMemory(minSize) =\n        c.Allocate(minSize)\n        Memory(arr, pos, arr.Length - pos)\n    member c.GetSpan(minSize) =\n        c.GetMemory(minSize).Span\n    member c.Clear() =\n        if arr.Length > 0 then\n            ArrayPool.Shared.Return(arr, true)\n            arr <- Array.Empty<'a>()\n        pos <- 0\n        c.Outbox <- NullOutbox.Instance\n        c.SourceId <- ActorId.Undefined\n        c.DestinationId <- ActorId.Undefined\n        c.DeliveryId <- ActorId.Undefined\n    member c.Send() =\n        if pos > 0 then\n            c.Outbox.SendAll({\n                Buffer = arr\n                Count = pos\n                SourceId = c.SourceId\n                DestinationId = c.DestinationId\n                Pool = ArrayPool.Shared\n                }, if c.DeliveryId.IsDefined then c.DeliveryId else c.DestinationId)\n            arr <- Array.Empty<'a>()\n        c.Clear()\n    member c.Dispose() = c.Send()\n    interface IDisposable with\n        member c.Dispose() = c.Send()\n    interface IBufferWriter<'a> with\n        member c.Advance(count) = c.Advance(count)\n        member c.GetMemory(minSize) = c.GetMemory(minSize)\n        member c.GetSpan(minSize) = c.GetSpan(minSize)\n\ntype IInbox =\n    abstract member Receive<'a> : IOutbox * Message<'a> -> unit\n    \ntype internal MessageTypeId() =\n    static let mutable id  = 0\n    static member GetNext() = Interlocked.Increment(&id)\n\ntype internal MessageTypeId<'a>() =\n    static let mutable id = MessageTypeId.GetNext()\n    static member Id = id\n\ntype Mailbox() =\n    let mutable lookup = Array.zeroCreate<obj>(8)\n    let mutable outbox = NullOutbox() :> IOutbox\n    let mutable sourceId = ActorId.Undefined\n    let mutable destId = ActorId.Undefined\n    member c.SourceId = sourceId\n    member c.DestinationId = destId\n    member c.OnAll<'a>(action : ReadOnlyMemory<'a> -> unit) =\n        let id = MessageTypeId<'a>.Id\n        Buffer.resizeArray (id + 1) &lookup\n        let combined =\n            let h = lookup.[id]\n            if isNotNull h then\n                let existing = h :?> ReadOnlyMemory<'a> -> unit\n                fun e ->\n                    existing e\n                    action e\n            else action\n        lookup.[id] <- combined :> obj\n    member c.On<'a>(handle : 'a -> unit) =\n        c.OnAll<'a>(fun buffer ->\n            let span = buffer.Span\n            for i = 0 to span.Length - 1 do\n                handle span.[i])\n    member c.TryReceive<'a>(currentOutbox : IOutbox, e : Message<'a>) =\n        let id = MessageTypeId<'a>.Id\n        if id < lookup.Length then\n            let h = lookup.[id]\n            if isNotNull h then\n                outbox <- currentOutbox\n                sourceId <- e.SourceId\n                destId <- e.DestinationId\n                try\n                    let handle = h :?> ReadOnlyMemory<'a> -> unit\n                    handle (ReadOnlyMemory(e.Buffer, 0, e.Count))\n                    true\n                finally\n                    outbox <- NullOutbox.Instance\n                    sourceId <- ActorId.Undefined\n                    destId <- ActorId.Undefined\n            else false\n        else false\n    member c.Send<'a>(message, deliveryId) =\n        outbox.Send<'a>(message, deliveryId)\n    interface IInbox with\n        member c.Receive(outbox, e) =\n            c.TryReceive(outbox, e) |> ignore\n    interface IOutbox with\n        member c.SendAll<'a>(message, deliveryId) =\n            outbox.SendAll<'a>(message, deliveryId)\n    override c.ToString() =\n        outbox.ToString()\n    static member Create(register) =\n        let inbox = Mailbox()\n        register inbox\n        inbox\n\ntype Mailbox with\n    member c.Respond(msg) =\n        c.Send(c.SourceId, msg)\n\ntype NullInbox() =\n    static let mutable instance = NullInbox() :> IInbox\n    static member Instance = instance\n    interface IInbox with\n        member c.Receive(_, _) = ()\n    \ntype InboxCollection(handlers : IInbox[]) =\n    new(handlers : IInbox seq) =\n        InboxCollection(handlers |> Seq.toArray)\n    interface IInbox with\n        member c.Receive<'a>(outbox, e) =\n            for handler in handlers do\n                handler.Receive<'a>(outbox, e)\n    override c.ToString() =\n         Format.formatList \"Inboxes\" handlers.Length (String.Join(\"\\n\", handlers))\n\n[<Struct>]\ntype Actor(inbox : IInbox, dispatcherId : int, dispose : unit -> unit) =\n    static let mutable instance = Actor(NullInbox.Instance)\n    static member Null = instance\n    new(inbox, dispose) = Actor(inbox, 0, dispose)\n    new(inbox, dispatcherId) = Actor(inbox, dispatcherId, ignore)\n    new(inbox) = Actor(inbox, 0)\n    member _.Inbox = inbox\n    member _.DispatcherId = dispatcherId\n    member _.Dispose() = dispose()\n    member _.WithDispatcher(newId) =\n        Actor(inbox, newId, dispose)\n\ntype IActorFactory =\n    abstract member TryCreate : ActorId -> ValueOption<Actor>\n\ntype internal ActorFactoryCollection() =\n    let factories = List<IActorFactory>()\n    member c.Add(factory) =\n        factories.Add(factory)\n    member c.TryCreate(createId : ActorId) =\n        let mutable result = ValueNone\n        let mutable i = factories.Count - 1\n        while result.IsNone && i >= 0 do\n            result <- factories.[i].TryCreate(createId)\n            i <- i - 1\n        result\n    interface IActorFactory with\n        member c.TryCreate(createId) =\n            c.TryCreate(createId)\n\ntype ActorFactory(tryCreate) =\n    static let mutable instance = \n        ActorFactory(fun _ -> ValueSome Actor.Null) \n        :> IActorFactory\n    static member Null = instance\n    member c.TryCreate(createId : ActorId) =\n        tryCreate createId\n    interface IActorFactory with\n        member c.TryCreate(createId) =\n            c.TryCreate(createId)\n    static member Create(tryCreate) =\n        ActorFactory(tryCreate) :> IActorFactory\n    static member Create(factories) =\n        let c = ActorFactoryCollection()\n        for factory in factories do\n            c.Add(factory)\n        c :> IActorFactory\n    /// Creates for any actor ID\n    static member Create(create : ActorId -> Actor) =\n        ActorFactory(fun (createId : ActorId) ->\n            create createId |> ValueSome)\n    /// Creates conditionally for actor ID\n    static member Create(predicate : ActorId -> bool, create : ActorId -> Actor) =\n        ActorFactory(fun (createId : ActorId) ->\n            if predicate createId then create createId |> ValueSome\n            else ValueNone) :> IActorFactory\n    /// Creates for a specific actor ID\n    static member Create(actorId : ActorId, create : ActorId -> Actor) =\n        ActorFactory.Create((=)actorId, create)\n    /// Creates conditionally for actor ID\n    static member Create(predicate : ActorId -> bool, register : ActorId -> Mailbox -> unit) =\n        let create (createId : ActorId) =\n            Actor(Mailbox.Create(register createId))\n        ActorFactory.Create(predicate, create)\n    /// Creates for any actor ID\n    static member Create(register : ActorId -> Mailbox -> unit) =\n        let predicate (_ : ActorId) = true\n        ActorFactory.Create(predicate, register)\n    /// Creates for a specific actor ID\n    static member Create(actorId : ActorId, register : ActorId -> Mailbox -> unit) =\n        ActorFactory.Create((=)actorId, register)\n\n[<AutoOpen>]\nmodule ActorFactory =\n    type IActorFactory with\n        member c.Wrap(map) =\n            let tryCreate (createId : ActorId) =\n                match c.TryCreate(createId) with\n                | ValueSome actor -> ValueSome (map createId actor)\n                | ValueNone -> ValueNone\n            ActorFactory.Create(tryCreate)\n\n        member c.WithDispatcher(dispatcherId) =\n            c.Wrap(fun _ actor -> actor.WithDispatcher(dispatcherId))\n\n        member c.Create(actorId) =\n            match c.TryCreate(actorId) with\n            | ValueSome actor -> actor\n            | ValueNone -> Actor.Null\n\ntype internal DisposableCollection<'a when 'a :> IDisposable>(items : 'a[]) =\n    interface IDisposable with\n        member c.Dispose() =\n            for item in items do \n                item.Dispose()\n\ntype Disposable(dispose) =\n    static let mutable instance = new Disposable(ignore) :> IDisposable\n    static member Null = instance\n    interface IDisposable with\n        member c.Dispose() = dispose()\n    static member Create(dispose) =\n        new Disposable(dispose) :> IDisposable\n    static member Create(items : IDisposable[]) =\n        new DisposableCollection<_>(items) :> IDisposable\n    static member Create(items : IDisposable seq) =\n        Disposable.Create(items |> Seq.toArray)\n\n[<Struct>]\ntype Addresses = {\n    SourceId : ActorId\n    DestinationId : ActorId\n    } with\n    static member inline Undefined = {        \n        SourceId = ActorId.Undefined\n        DestinationId = ActorId.Undefined\n        }\n\n[<Struct>]\ntype internal MessageContext = {\n    Addresses : Addresses\n    Outbox : IOutbox\n    }\n             \nmodule internal MessageContext = \n    let empty = {\n        Addresses = Addresses.Undefined\n        Outbox = NullOutbox.Instance\n        }\n    \n/// Need sender member indirection because container registrations\n/// need permanent reference while incoming messages have varying sender\ntype internal Outbox() =\n    let mutable sendCount = 0\n    let mutable pushCount = 0\n    let mutable popCount = 0\n    let mutable current = MessageContext.empty\n    let outboxStack = Stack<_>()\n    let popOutbox() = \n        popCount <- popCount + 1\n        current <- outboxStack.Pop()\n    let scope = new Disposable(popOutbox)\n    member c.Current = current\n    /// Set temporary outbox for a scope such as handling incoming message\n    member c.Push(outbox, message : Message<_>) = \n        let context = {\n            Outbox = outbox\n            Addresses = {\n                SourceId = message.SourceId\n                DestinationId = message.DestinationId\n                }\n            }\n        outboxStack.Push(current)\n        current <- context\n        pushCount <- pushCount + 1\n        scope\n    member c.SendAll(message, deliveryId) =             \n        current.Outbox.SendAll(message, deliveryId)\n        sendCount <- sendCount + 1\n    interface IOutbox with\n        member c.SendAll(message, deliveryId) =\n            c.SendAll(message, deliveryId)            \n    override c.ToString() =\n        sprintf \"Outbox: %d outboxes, %d sent, %d/%d push/pop\" outboxStack.Count sendCount pushCount popCount\n        "
  },
  {
    "path": "src/Garnet/Queries.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.CompilerServices\nopen Garnet.Composition.Comparisons\n\n[<Struct>]\ntype MaskEnumerator = \n    val mutable private mask : uint64\n    val mutable private i : int\n    new(mask) = {\n        mask = mask\n        i = -1\n    }\n    member c.Current = c.i\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        // Alternative for .NET 3.0 onward\n        // let skip = System.Numerics.BitOperations.TrailingZeroCount(c.mask) + 1\n        // c.mask <- c.mask >>> skip\n        // c.i <- c.i + skip\n        // c.i < 64\n        if c.mask = 0UL then false\n        else\n            while c.mask &&& 1UL = 0UL do\n                c.mask <- c.mask >>> 1\n                c.i <- c.i + 1\n            c.mask <- c.mask >>> 1\n            c.i <- c.i + 1\n            true\n    member c.Reset() =\n        raise (NotSupportedException())\n    member c.Dispose() = ()\n    interface IEnumerator<int> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype MaskEnumerable =\n    val Mask : uint64\n    new(mask) = { Mask = mask }\n    member inline c.GetEnumerator() = new MaskEnumerator(c.Mask)\n    interface IEnumerable<int> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<int> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n[<Struct>]\ntype SegmentDescriptor<'k> =\n    val Id : 'k\n    val Mask : uint64\n    new(id, mask) = { Id = id; Mask = mask }\n    member inline c.GetEnumerator() = new MaskEnumerator(c.Mask)\n    interface IEnumerable<int> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<int> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n[<Struct>]\ntype SegmentData<'a> =\n    val Array : 'a[]\n    val Offset : int\n    new(array, offset) = { Array = array; Offset = offset }\n    member inline c.Item with get i : byref<'a> =\n        &c.Array.[c.Offset + i]\n    member c.AsSpan() =\n        Span(c.Array, c.Offset, Segment.SegmentSize)\n    member c.AsReadOnlySpan() =\n        ReadOnlySpan(c.Array, c.Offset, Segment.SegmentSize)\n\ntype internal SD<'a> = SegmentData<'a>\n\n// ComponentBatchEnumerator\n\n// These are for iterating individual Items (read-only) in a batch.\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1\n    }\n    member c.Current =\n        let i = c.m.Current\n        c.s1.[i]\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<'s1> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2, 's3 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val private s3 : SD<'s3>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2, s3)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2; s3 = s3\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i], c.s3.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2 * 's3)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2, 's3, 's4 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val private s3 : SD<'s3>\n    val private s4 : SD<'s4>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2, s3, s4)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2; s3 = s3; s4 = s4\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i], c.s3.[i], c.s4.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2 * 's3 * 's4)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2, 's3, 's4, 's5 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val private s3 : SD<'s3>\n    val private s4 : SD<'s4>\n    val private s5 : SD<'s5>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2, s3, s4, s5)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i], c.s3.[i], c.s4.[i], c.s5.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2 * 's3 * 's4 * 's5)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val private s3 : SD<'s3>\n    val private s4 : SD<'s4>\n    val private s5 : SD<'s5>\n    val private s6 : SD<'s6>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2, s3, s4, s5, s6)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i], c.s3.[i], c.s4.[i], c.s5.[i], c.s6.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2 * 's3 * 's4 * 's5 * 's6)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n[<Struct>]\ntype ComponentBatchEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : SD<'s1>\n    val private s2 : SD<'s2>\n    val private s3 : SD<'s3>\n    val private s4 : SD<'s4>\n    val private s5 : SD<'s5>\n    val private s6 : SD<'s6>\n    val private s7 : SD<'s7>\n    val mutable private m : MaskEnumerator\n    new(struct(desc : SegmentDescriptor<'k>, s1, s2, s3, s4, s5, s6, s7)) = {\n        m = new MaskEnumerator(desc.Mask); s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6; s7 = s7\n    }\n    member c.Current =\n        let i = c.m.Current\n        struct(c.s1.[i], c.s2.[i], c.s3.[i], c.s4.[i], c.s5.[i], c.s6.[i], c.s7.[i])\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() = c.m.MoveNext()\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<struct('s1 * 's2 * 's3 * 's4 * 's5 * 's6 * 's7)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n// SegmentQueryResult\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment : SD<'s1>\n    new(id, mask, s1) = { Id = id; Mask = mask; Segment = s1 }\n    member c.Indices = MaskEnumerable(c.Mask)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    new(id, mask, s1, s2) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2, 's3 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    val Segment3 : SD<'s3> \n    new(id, mask, s1, s2, s3) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2; Segment3 = s3\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2, c.Segment3)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2, 's3, 's4 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    val Segment3 : SD<'s3> \n    val Segment4 : SD<'s4> \n    new(id, mask, s1, s2, s3, s4) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2; Segment3 = s3; Segment4 = s4\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2, c.Segment3, c.Segment4)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2, 's3, 's4, 's5 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    val Segment3 : SD<'s3> \n    val Segment4 : SD<'s4> \n    val Segment5 : SD<'s5> \n    new(id, mask, s1, s2, s3, s4, s5) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2; Segment3 = s3; Segment4 = s4; Segment5 = s5\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2, c.Segment3, c.Segment4, c.Segment5)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2, 's3, 's4, 's5, 's6 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    val Segment3 : SD<'s3> \n    val Segment4 : SD<'s4> \n    val Segment5 : SD<'s5> \n    val Segment6 : SD<'s6> \n    new(id, mask, s1, s2, s3, s4, s5, s6) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2; Segment3 = s3; Segment4 = s4; Segment5 = s5; Segment6 = s6\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2, c.Segment3, c.Segment4, c.Segment5, c.Segment6)\n\n[<Struct>]\ntype SegmentQueryResult<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val Id : 'k\n    val Mask : uint64\n    val Segment1 : SD<'s1> \n    val Segment2 : SD<'s2> \n    val Segment3 : SD<'s3> \n    val Segment4 : SD<'s4> \n    val Segment5 : SD<'s5> \n    val Segment6 : SD<'s6> \n    val Segment7 : SD<'s7> \n    new(id, mask, s1, s2, s3, s4, s5, s6, s7) = {\n        Id = id; Mask = mask\n        Segment1 = s1; Segment2 = s2; Segment3 = s3; Segment4 = s4; Segment5 = s5; Segment6 = s6; Segment7 = s7\n    }\n    member c.Indices = MaskEnumerable(c.Mask)\n    member c.Segments = struct(c.Segment1, c.Segment2, c.Segment3, c.Segment4, c.Segment5, c.Segment6, c.Segment7)\n\n// SegmentQueryEnumerator\n\n// These implement segment intersections (inner joins) for iterating over segments.\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val mutable private i1 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    new(s1) = {\n        s1 = s1\n        i1 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        }\n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        &c.data1.[i]\n    member c.Current =\n        struct(c.descriptor, c.data1)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count do\n            let seg1 = c.s1.[c.i1]\n            let n1 = seg1.Id\n            let mask = seg1.Mask\n            if mask <> 0UL then\n                c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                c.data1 <- SD(seg1.Data, 0)\n                found <- true\n            c.i1 <- c.i1 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    new(s1, s2) = {\n        s1 = s1; s2 = s2\n        i1 = 0;  i2 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            if   n1 < n2 then c.i1 <- c.i1 + 1\n            elif n2 < n1 then c.i2 <- c.i2 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask\n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2, 's3\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private i3 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    val mutable private data3 : SD<'s3>\n    new(s1, s2, s3) = {\n        s1 = s1; s2 = s2; s3 = s3\n        i1 = 0;  i2 = 0;  i3 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        data3 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.GetValue3(i) = &c.data3.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i], c.data3.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2, c.data3)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count &&\n              c.i3 < c.s3.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let seg3 = c.s3.[c.i3]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            let n3 = seg3.Id\n            if   n1 < n2 || n1 < n3 then c.i1 <- c.i1 + 1\n            elif n2 < n1 || n2 < n3 then c.i2 <- c.i2 + 1\n            elif n3 < n1 || n3 < n2 then c.i3 <- c.i3 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask &&& seg3.Mask\n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    c.data3 <- SD(seg3.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n                c.i3 <- c.i3 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0; c.i3 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private i3 : int\n    val mutable private i4 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    val mutable private data3 : SD<'s3>\n    val mutable private data4 : SD<'s4>\n    new(s1, s2, s3, s4) = {\n        s1 = s1; s2 = s2; s3 = s3; s4 = s4\n        i1 = 0;  i2 = 0;  i3 = 0;  i4 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        data3 = Unchecked.defaultof<_>\n        data4 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.GetValue3(i) = &c.data3.[i] \n    member c.GetValue4(i) = &c.data4.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i], c.data3.[i], c.data4.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2, c.data3, c.data4)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count &&\n              c.i3 < c.s3.Count &&\n              c.i4 < c.s4.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let seg3 = c.s3.[c.i3]\n            let seg4 = c.s4.[c.i4]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            let n3 = seg3.Id\n            let n4 = seg4.Id\n            if   n1 < n2 || n1 < n3 || n1 < n4 then c.i1 <- c.i1 + 1\n            elif n2 < n1 || n2 < n3 || n2 < n4 then c.i2 <- c.i2 + 1\n            elif n3 < n1 || n3 < n2 || n3 < n4 then c.i3 <- c.i3 + 1\n            elif n4 < n1 || n4 < n2 || n4 < n3 then c.i4 <- c.i4 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask &&& seg3.Mask &&& seg4.Mask\n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    c.data3 <- SD(seg3.Data, 0)\n                    c.data4 <- SD(seg4.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n                c.i3 <- c.i3 + 1\n                c.i4 <- c.i4 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0; c.i3 <- 0; c.i4 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private i3 : int\n    val mutable private i4 : int\n    val mutable private i5 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    val mutable private data3 : SD<'s3>\n    val mutable private data4 : SD<'s4>\n    val mutable private data5 : SD<'s5>\n    new(s1, s2, s3, s4, s5) = {\n        s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5\n        i1 = 0;  i2 = 0;  i3 = 0;  i4 = 0;  i5 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        data3 = Unchecked.defaultof<_>\n        data4 = Unchecked.defaultof<_>\n        data5 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.GetValue3(i) = &c.data3.[i] \n    member c.GetValue4(i) = &c.data4.[i] \n    member c.GetValue5(i) = &c.data5.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i], c.data3.[i], c.data4.[i], c.data5.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2, c.data3, c.data4, c.data5)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count &&\n              c.i3 < c.s3.Count &&\n              c.i4 < c.s4.Count &&\n              c.i5 < c.s5.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let seg3 = c.s3.[c.i3]\n            let seg4 = c.s4.[c.i4]\n            let seg5 = c.s5.[c.i5]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            let n3 = seg3.Id\n            let n4 = seg4.Id\n            let n5 = seg5.Id\n            if   n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 then c.i1 <- c.i1 + 1\n            elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 then c.i2 <- c.i2 + 1\n            elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 then c.i3 <- c.i3 + 1\n            elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 then c.i4 <- c.i4 + 1\n            elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 then c.i5 <- c.i5 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask &&& seg3.Mask &&& seg4.Mask &&& seg5.Mask\n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    c.data3 <- SD(seg3.Data, 0)\n                    c.data4 <- SD(seg4.Data, 0)\n                    c.data5 <- SD(seg5.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n                c.i3 <- c.i3 + 1\n                c.i4 <- c.i4 + 1\n                c.i5 <- c.i5 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0; c.i3 <- 0; c.i4 <- 0; c.i5 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private i3 : int\n    val mutable private i4 : int\n    val mutable private i5 : int\n    val mutable private i6 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    val mutable private data3 : SD<'s3>\n    val mutable private data4 : SD<'s4>\n    val mutable private data5 : SD<'s5>\n    val mutable private data6 : SD<'s6>\n    new(s1, s2, s3, s4, s5, s6) = {\n        s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6\n        i1 = 0;  i2 = 0;  i3 = 0;  i4 = 0;  i5 = 0;  i6 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        data3 = Unchecked.defaultof<_>\n        data4 = Unchecked.defaultof<_>\n        data5 = Unchecked.defaultof<_>\n        data6 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.GetValue3(i) = &c.data3.[i] \n    member c.GetValue4(i) = &c.data4.[i] \n    member c.GetValue5(i) = &c.data5.[i] \n    member c.GetValue6(i) = &c.data6.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i], c.data3.[i], c.data4.[i], c.data5.[i], c.data6.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2, c.data3, c.data4, c.data5, c.data6)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count &&\n              c.i3 < c.s3.Count &&\n              c.i4 < c.s4.Count &&\n              c.i5 < c.s5.Count &&\n              c.i6 < c.s6.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let seg3 = c.s3.[c.i3]\n            let seg4 = c.s4.[c.i4]\n            let seg5 = c.s5.[c.i5]\n            let seg6 = c.s6.[c.i6]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            let n3 = seg3.Id\n            let n4 = seg4.Id\n            let n5 = seg5.Id\n            let n6 = seg6.Id\n            if   n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 || n1 < n6 then c.i1 <- c.i1 + 1\n            elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 || n2 < n6 then c.i2 <- c.i2 + 1\n            elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 || n3 < n6 then c.i3 <- c.i3 + 1\n            elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 || n4 < n6 then c.i4 <- c.i4 + 1\n            elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 || n5 < n6 then c.i5 <- c.i5 + 1\n            elif n6 < n1 || n6 < n2 || n6 < n3 || n6 < n4 || n6 < n5 then c.i6 <- c.i6 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask &&& seg3.Mask &&& seg4.Mask &&& seg5.Mask &&& seg6.Mask\n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    c.data3 <- SD(seg3.Data, 0)\n                    c.data4 <- SD(seg4.Data, 0)\n                    c.data5 <- SD(seg5.Data, 0)\n                    c.data6 <- SD(seg6.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n                c.i3 <- c.i3 + 1\n                c.i4 <- c.i4 + 1\n                c.i5 <- c.i5 + 1\n                c.i6 <- c.i6 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0; c.i3 <- 0; c.i4 <- 0; c.i5 <- 0; c.i6 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5> * SD<'s6>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n[<Struct>]\ntype SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> = \n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    val private s7 : Segments<'k, 's7> \n    val mutable private i1 : int\n    val mutable private i2 : int\n    val mutable private i3 : int\n    val mutable private i4 : int\n    val mutable private i5 : int\n    val mutable private i6 : int\n    val mutable private i7 : int\n    val mutable private descriptor : SegmentDescriptor<'k>\n    val mutable private data1 : SD<'s1>\n    val mutable private data2 : SD<'s2>\n    val mutable private data3 : SD<'s3>\n    val mutable private data4 : SD<'s4>\n    val mutable private data5 : SD<'s5>\n    val mutable private data6 : SD<'s6>\n    val mutable private data7 : SD<'s7>\n    new(s1, s2, s3, s4, s5, s6, s7) = {\n        s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6; s7 = s7\n        i1 = 0;  i2 = 0;  i3 = 0;  i4 = 0;  i5 = 0;  i6 = 0;  i7 = 0\n        descriptor = SegmentDescriptor<'k>(Unchecked.defaultof<'k>, 0UL)\n        data1 = Unchecked.defaultof<_>\n        data2 = Unchecked.defaultof<_>\n        data3 = Unchecked.defaultof<_>\n        data4 = Unchecked.defaultof<_>\n        data5 = Unchecked.defaultof<_>\n        data6 = Unchecked.defaultof<_>\n        data7 = Unchecked.defaultof<_>\n        }\n    member c.GetValue1(i) = &c.data1.[i] \n    member c.GetValue2(i) = &c.data2.[i] \n    member c.GetValue3(i) = &c.data3.[i] \n    member c.GetValue4(i) = &c.data4.[i] \n    member c.GetValue5(i) = &c.data5.[i] \n    member c.GetValue6(i) = &c.data6.[i] \n    member c.GetValue7(i) = &c.data7.[i] \n    member c.Mask = c.descriptor.Mask\n    member c.Item with get i =\n        struct(c.data1.[i], c.data2.[i], c.data3.[i], c.data4.[i], c.data5.[i], c.data6.[i], c.data7.[i])\n    member c.Current =\n        struct(c.descriptor, c.data1, c.data2, c.data3, c.data4, c.data5, c.data6, c.data7)\n    member c.MoveNext() =\n        let mutable found = false\n        while not found &&\n              c.i1 < c.s1.Count &&\n              c.i2 < c.s2.Count &&\n              c.i3 < c.s3.Count &&\n              c.i4 < c.s4.Count &&\n              c.i5 < c.s5.Count &&\n              c.i6 < c.s6.Count &&\n              c.i7 < c.s7.Count do\n            let seg1 = c.s1.[c.i1]\n            let seg2 = c.s2.[c.i2]\n            let seg3 = c.s3.[c.i3]\n            let seg4 = c.s4.[c.i4]\n            let seg5 = c.s5.[c.i5]\n            let seg6 = c.s6.[c.i6]\n            let seg7 = c.s7.[c.i7]\n            let n1 = seg1.Id\n            let n2 = seg2.Id\n            let n3 = seg3.Id\n            let n4 = seg4.Id\n            let n5 = seg5.Id\n            let n6 = seg6.Id\n            let n7 = seg7.Id\n            if   n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 || n1 < n6 || n1 < n7 then c.i1 <- c.i1 + 1\n            elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 || n2 < n6 || n2 < n7 then c.i2 <- c.i2 + 1\n            elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 || n3 < n6 || n3 < n7 then c.i3 <- c.i3 + 1\n            elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 || n4 < n6 || n4 < n7 then c.i4 <- c.i4 + 1\n            elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 || n5 < n6 || n5 < n7 then c.i5 <- c.i5 + 1\n            elif n6 < n1 || n6 < n2 || n6 < n3 || n6 < n4 || n6 < n5 || n6 < n7 then c.i6 <- c.i6 + 1\n            elif n7 < n1 || n7 < n2 || n7 < n3 || n7 < n4 || n7 < n5 || n7 < n6 then c.i7 <- c.i7 + 1\n            else\n                let mask = seg1.Mask &&& seg2.Mask &&& seg3.Mask &&& seg4.Mask &&& seg5.Mask &&& seg6.Mask &&& seg7.Mask \n                if mask <> 0UL then\n                    c.descriptor <- SegmentDescriptor<'k>(n1, mask)\n                    c.data1 <- SD(seg1.Data, 0)\n                    c.data2 <- SD(seg2.Data, 0)\n                    c.data3 <- SD(seg3.Data, 0)\n                    c.data4 <- SD(seg4.Data, 0)\n                    c.data5 <- SD(seg5.Data, 0)\n                    c.data6 <- SD(seg6.Data, 0)\n                    c.data7 <- SD(seg7.Data, 0)\n                    found <- true\n                c.i1 <- c.i1 + 1\n                c.i2 <- c.i2 + 1\n                c.i3 <- c.i3 + 1\n                c.i4 <- c.i4 + 1\n                c.i5 <- c.i5 + 1\n                c.i6 <- c.i6 + 1\n                c.i7 <- c.i7 + 1\n        found\n    member c.Reset() =\n        c.i1 <- 0; c.i2 <- 0; c.i3 <- 0; c.i4 <- 0; c.i5 <- 0; c.i6 <- 0; c.i7 <- 0\n    member c.Dispose() = ()\n    interface IEnumerator<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5> * SD<'s6> * SD<'s7>)> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.Dispose() = ()\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n\n// ComponentQueryEnumerator\n\n// These enumerate over all Items over all segments, so they combine\n// two enumerators (or for loops) into one.\n\n// Note they are reference types and actually return themself during\n// enumeration. This is so we can access byref members directly and\n// avoid overhead from returning a large struct type on each iteration.\n// Although GC impact appears low, we can consider pooling these objects\n// to avoid GC overhead if needed.\n\n// Unfortunately, this approach requires access components by numbered\n// fields like Value1, Value2, etc. There's an option to read values as\n// tuples using 'Values', but this is significantly slower. \n\ntype ComponentQueryEnumerator<'k, 's1 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1>\n    val mutable private m : MaskEnumerator\n    new(s1) = {\n        si = new SegmentQueryEnumerator<_,_>(s1)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value = &c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2>\n    val mutable private m : MaskEnumerator\n    new(s1, s2) = {\n        si = new SegmentQueryEnumerator<_,_,_>(s1, s2)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current)\n    member c.Values = c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2, 's3 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2, 's3>\n    val mutable private m : MaskEnumerator\n    new(s1, s2, s3) = {\n        si = new SegmentQueryEnumerator<_,_,_,_>(s1, s2, s3)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current) \n    member c.Value3 = &c.si.GetValue3(c.m.Current) \n    member c.Values = struct(c.Value1, c.Value2, c.Value3)\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2, 's3>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4>\n    val mutable private m : MaskEnumerator\n    new(s1, s2, s3, s4) = {\n        si = new SegmentQueryEnumerator<_,_,_,_,_>(s1, s2, s3, s4)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current) \n    member c.Value3 = &c.si.GetValue3(c.m.Current) \n    member c.Value4 = &c.si.GetValue4(c.m.Current) \n    member c.Values = c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5>\n    val mutable private m : MaskEnumerator\n    new(s1, s2, s3, s4, s5) = {\n        si = new SegmentQueryEnumerator<_,_,_,_,_,_>(s1, s2, s3, s4, s5)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current) \n    member c.Value3 = &c.si.GetValue3(c.m.Current) \n    member c.Value4 = &c.si.GetValue4(c.m.Current) \n    member c.Value5 = &c.si.GetValue5(c.m.Current) \n    member c.Values = c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6>\n    val mutable private m : MaskEnumerator\n    new(s1, s2, s3, s4, s5, s6) = {\n        si = new SegmentQueryEnumerator<_,_,_,_,_,_,_>(s1, s2, s3, s4, s5, s6)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current) \n    member c.Value3 = &c.si.GetValue3(c.m.Current) \n    member c.Value4 = &c.si.GetValue4(c.m.Current) \n    member c.Value5 = &c.si.GetValue5(c.m.Current) \n    member c.Value6 = &c.si.GetValue6(c.m.Current)\n    member c.Values = c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\ntype ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val mutable private si : SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>\n    val mutable private m : MaskEnumerator\n    new(s1, s2, s3, s4, s5, s6, s7) = {\n        si = new SegmentQueryEnumerator<_,_,_,_,_,_,_,_>(s1, s2, s3, s4, s5, s6, s7)\n        m = new MaskEnumerator(0UL)\n        }\n    member c.Value1 = &c.si.GetValue1(c.m.Current) \n    member c.Value2 = &c.si.GetValue2(c.m.Current) \n    member c.Value3 = &c.si.GetValue3(c.m.Current) \n    member c.Value4 = &c.si.GetValue4(c.m.Current) \n    member c.Value5 = &c.si.GetValue5(c.m.Current) \n    member c.Value6 = &c.si.GetValue6(c.m.Current) \n    member c.Value7 = &c.si.GetValue7(c.m.Current) \n    member c.Values = c.si.[c.m.Current]\n    member c.Current = c\n    [<MethodImpl(MethodImplOptions.AggressiveInlining)>]\n    member c.MoveNext() =\n        if c.m.MoveNext() then true\n        else\n            let mutable found = c.si.MoveNext() \n            while found && c.si.Mask = 0UL do\n                found <- c.si.MoveNext() \n            if found then\n                c.m <- new MaskEnumerator(c.si.Mask)\n                c.m.MoveNext() |> ignore\n            found\n    member c.Reset() = c.m.Reset()\n    member c.Dispose() = ()\n    interface IEnumerator<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>> with\n        member c.Current = c.Current\n        member c.Current = c.Current :> obj\n        member c.MoveNext() = c.MoveNext()\n        member c.Reset() = c.Reset()\n        member c.Dispose() = ()\n\n// SegmentQuery\n\n// These each store a set of segment lists for iterating/joining over. Note they\n// yield tuples of segments instead of component enumerator which yields a result\n// object. This way appears to be faster for segment-level iteration.\n\n[<Struct>]\ntype SegmentQuery<'k, 's1 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    new(s1) = { s1 = s1 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1>(c.s1)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    new(s1, s2) = { s1 = s1; s2 = s2 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2>(c.s1, c.s2)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2, 's3 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    new(s1, s2, s3) = { s1 = s1; s2 = s2; s3 = s3 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2, 's3>(c.s1, c.s2, c.s3)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2, 's3, 's4 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    new(s1, s2, s3, s4) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4>(c.s1, c.s2, c.s3, c.s4)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    new(s1, s2, s3, s4, s5) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5>(c.s1, c.s2, c.s3, c.s4, c.s5)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    new(s1, s2, s3, s4, s5, s6) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5> * SD<'s6>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    val private s7 : Segments<'k, 's7> \n    new(s1, s2, s3, s4, s5, s6, s7) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6; s7 = s7 }\n    member c.GetEnumerator() = new SegmentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6, c.s7)\n    member c.GetComponentCount() =\n        let mutable count = 0\n        let mutable e = c.GetEnumerator()\n        while e.MoveNext() do count <- count + Bits.bitCount64 e.Mask\n        count\n    interface IEnumerable<struct(SegmentDescriptor<'k> * SD<'s1> * SD<'s2> * SD<'s3> * SD<'s4> * SD<'s5> * SD<'s6> * SD<'s7>)> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n// ComponentQuery\n\n// These are nearly identical to SegmentQuery, but they provide enumeration over Items\n// instead of segments.\n\n[<Struct>]\ntype ComponentQuery<'k, 's1 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    new(s1) = { s1 = s1 }\n    member c.Segments = SegmentQuery<_,_>(c.s1)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1>(c.s1)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    new(s1, s2) = { s1 = s1; s2 = s2 }\n    member c.Segments = SegmentQuery<_,_,_>(c.s1, c.s2)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2>(c.s1, c.s2)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2, 's3 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    new(s1, s2, s3) = { s1 = s1; s2 = s2; s3 = s3 }\n    member c.Segments = SegmentQuery<_,_,_,_>(c.s1, c.s2, c.s3)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2, 's3>(c.s1, c.s2, c.s3)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2, 's3>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2, 's3, 's4 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    new(s1, s2, s3, s4) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4 }\n    member c.Segments = SegmentQuery<_,_,_,_,_>(c.s1, c.s2, c.s3, c.s4)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4>(c.s1, c.s2, c.s3, c.s4)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    new(s1, s2, s3, s4, s5) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5 }\n    member c.Segments = SegmentQuery<_,_,_,_,_,_>(c.s1, c.s2, c.s3, c.s4, c.s5)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5>(c.s1, c.s2, c.s3, c.s4, c.s5)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    new(s1, s2, s3, s4, s5, s6) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6 }\n    member c.Segments = SegmentQuery<_,_,_,_,_,_,_>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n    \n[<Struct>]\ntype ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7 when 'k :> IComparable<'k> and 'k :> IEquatable<'k> and 'k : equality> =\n    val private s1 : Segments<'k, 's1> \n    val private s2 : Segments<'k, 's2> \n    val private s3 : Segments<'k, 's3> \n    val private s4 : Segments<'k, 's4> \n    val private s5 : Segments<'k, 's5> \n    val private s6 : Segments<'k, 's6> \n    val private s7 : Segments<'k, 's7> \n    new(s1, s2, s3, s4, s5, s6, s7) = { s1 = s1; s2 = s2; s3 = s3; s4 = s4; s5 = s5; s6 = s6; s7 = s7 }\n    member c.Segments = SegmentQuery<_,_,_,_,_,_,_,_>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6, c.s7)\n    member c.GetEnumerator() = new ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>(c.s1, c.s2, c.s3, c.s4, c.s5, c.s6, c.s7)\n    member c.GetCount() = c.Segments.GetComponentCount()\n    interface IEnumerable<ComponentQueryEnumerator<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>> with\n        member c.GetEnumerator() = c.GetEnumerator() :> IEnumerator<_> \n        member c.GetEnumerator() = c.GetEnumerator() :> Collections.IEnumerator\n\n// Extensions\n\n[<AutoOpen>]\nmodule QueryExtensions =\n    type ISegmentStore<'k\n            when 'k :> IComparable<'k> \n            and 'k :> IEquatable<'k> \n            and 'k : equality> with\n\n        // Segment queries\n        \n        member c.QuerySegments<'s1>() =\n            SegmentQuery<'k, 's1>(\n                c.GetSegments<'s1>())\n            \n        member c.QuerySegments<'s1, 's2>() =\n            SegmentQuery<'k, 's1, 's2>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>())\n            \n        member c.QuerySegments<'s1, 's2, 's3>() =\n            SegmentQuery<'k, 's1, 's2, 's3>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>())\n            \n        member c.QuerySegments<'s1, 's2, 's3, 's4>() =\n            SegmentQuery<'k, 's1, 's2, 's3, 's4>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>())\n                \n        member c.QuerySegments<'s1, 's2, 's3, 's4, 's5>() =\n            SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>())\n                \n        member c.QuerySegments<'s1, 's2, 's3, 's4, 's5, 's6>() =\n            SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>(),\n                c.GetSegments<'s6>())\n                \n        member c.QuerySegments<'s1, 's2, 's3, 's4, 's5, 's6, 's7>() =\n            SegmentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>(),\n                c.GetSegments<'s6>(),\n                c.GetSegments<'s7>())\n\n        // Component queries\n        \n        member c.Query<'s1>() =\n            ComponentQuery<'k, 's1>(\n                c.GetSegments<'s1>())\n            \n        member c.Query<'s1, 's2>() =\n            ComponentQuery<'k, 's1, 's2>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>())\n            \n        member c.Query<'s1, 's2, 's3>() =\n            ComponentQuery<'k, 's1, 's2, 's3>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>())\n            \n        member c.Query<'s1, 's2, 's3, 's4>() =\n            ComponentQuery<'k, 's1, 's2, 's3, 's4>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>())\n                \n        member c.Query<'s1, 's2, 's3, 's4, 's5>() =\n            ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>())\n                \n        member c.Query<'s1, 's2, 's3, 's4, 's5, 's6>() =\n            ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>(),\n                c.GetSegments<'s6>())\n                \n        member c.Query<'s1, 's2, 's3, 's4, 's5, 's6, 's7>() =\n            ComponentQuery<'k, 's1, 's2, 's3, 's4, 's5, 's6, 's7>(\n                c.GetSegments<'s1>(),\n                c.GetSegments<'s2>(),\n                c.GetSegments<'s3>(),\n                c.GetSegments<'s4>(),\n                c.GetSegments<'s5>(),\n                c.GetSegments<'s6>(),\n                c.GetSegments<'s7>())\n"
  },
  {
    "path": "src/Garnet/Registry.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen System.Threading\nopen Garnet.Composition.Comparisons\n\ntype IRegistryHandler<'p> =\n    /// Serves as a callback when iterating over typed instances in registry.\n    /// Takes a custom param, index, and instance\n    abstract member Handle<'a> : 'p * int * 'a byref-> unit\n\n/// Provides methods to register and resolve single-instance objects by type\ntype IRegistry =\n    /// Registers a factory for creating values of a specific type\n    abstract member SetFactory<'a> : (unit -> 'a) -> unit\n    /// Adds or replaces a specific instance of a type\n    abstract member Set<'a> : 'a -> unit\n    /// Gets or creates a reference to a typed value\n    abstract member Get<'a> : unit -> 'a byref\n    /// Attempts to resolve a type, returning true if successful\n    abstract member TryGet<'a> : [<Out>] value : byref<'a> -> bool\n    /// Iterates over all instances, calling handler for each\n    abstract member Iter<'p> : 'p * IRegistryHandler<'p> -> unit\n    \ntype private IRegistryEntryHandler =\n    abstract member Handle<'p> : 'p * int * IRegistryHandler<'p> -> unit\n\ntype private RegistryEntryHandler<'a>(instance : 'a ref) =\n    interface IRegistryEntryHandler with \n        member c.Handle<'p>(param : 'p, index, handler) =\n            handler.Handle(param, index, &instance.contents)\n\n[<Struct>]\ntype private RegistryEntry = {\n    Handler : IRegistryEntryHandler\n    Reference : obj\n    } with\n    static member Create<'a>(instance : 'a ref) = {\n        Handler = RegistryEntryHandler<'a>(instance)\n        Reference = instance\n        }        \n\ntype internal RegistryTypeId() =\n    static let mutable id  = 0\n    static member GetNext() = Interlocked.Increment(&id)\n\ntype internal RegistryTypeId<'a>() =\n    static let mutable id = MessageTypeId.GetNext()\n    static member Id = id\n\n/// Provides methods to register and resolve single-instance objects by type\ntype Registry() =\n    let mutable factories = Array.zeroCreate<Func<RegistryEntry>>(8)\n    let mutable lookup = Array.zeroCreate<obj>(8)\n    let instances = List<RegistryEntry>()\n    member private c.TryGetReference<'a>([<Out>] reference : byref<'a ref>) =\n        let id = RegistryTypeId<'a>.Id\n        if id >= lookup.Length then\n            Buffer.resizeArray (id + 1) &lookup    \n        let value = lookup.[id]\n        if isNotNull value then\n            // Value is present already\n            reference <- value :?> 'a ref\n            true\n        elif id < factories.Length && isNotNull factories.[id] then\n            // Use factory to create\n            let factory = factories.[id]\n            let entry = factory.Invoke()\n            lookup.[id] <- entry.Reference        \n            instances.Add(entry)\n            reference <- entry.Reference :?> 'a ref\n            true\n        else false\n    member c.SetFactory<'a>(create : unit -> 'a) =\n        let id = RegistryTypeId<'a>.Id\n        if id >= factories.Length then\n            Buffer.resizeArray (id + 1) &factories\n        factories.[id] <- Func<_>(fun () ->\n            // Entry is temporarily marked null to detect cycles\n            if obj.ReferenceEquals(factories.[id], null) \n                then failwithf \"Cycle detected for %s\" (Format.typeToString typeof<'a>)\n                else\n                    // Mark null to detect cycles\n                    let factory = factories.[id]\n                    factories.[id] <- null\n                    // Instantiate type\n                    let value = create()\n                    let cell = ref value\n                    let entry = RegistryEntry.Create(cell)\n                    // Restore factory\n                    factories.[id] <- factory\n                    entry)    \n    member c.Set<'a>(newValue : 'a) =\n        let id = RegistryTypeId<'a>.Id\n        if id >= lookup.Length then\n            Buffer.resizeArray (id + 1) &lookup\n        if isNull lookup.[id] then\n            let cell = ref newValue\n            lookup.[id] <- cell :> obj\n            instances.Add(RegistryEntry.Create(cell))\n        let cell = lookup.[id] :?> 'a ref\n        cell.Value <- newValue        \n    member c.Get<'a>() =\n        let mutable cell = Unchecked.defaultof<_>\n        if c.TryGetReference(&cell) then &cell.contents\n        else\n            // No factory, create default value\n            let value = Activator.CreateInstance<'a>()\n            let cell = ref value\n            let id = RegistryTypeId<'a>.Id\n            lookup.[id] <- cell :> obj\n            instances.Add(RegistryEntry.Create(cell))\n            &cell.contents\n    member c.TryGet<'a>([<Out>] value : byref<'a>) =\n        let mutable cell = Unchecked.defaultof<_>\n        let result = c.TryGetReference(&cell)\n        if result then value <- cell.contents\n        result\n    member c.Iter(param, handler) =\n        c.Iter(param, handler, 0)\n    member c.Iter(param, handler : IRegistryHandler<'p>, offset) =\n        for i = 0 to instances.Count - 1 do\n            instances.[i].Handler.Handle(param, offset + i, handler)\n    interface IRegistry with\n        member c.SetFactory(x) = c.SetFactory(x)\n        member c.Set(x) = c.Set(x)\n        member c.Get<'a>() = &c.Get<'a>()\n        member c.TryGet<'a>([<Out>] value) = c.TryGet<'a>(&value)\n        member c.Iter(param, handler) =\n            c.Iter(param, handler)\n    member c.ToString(writer : IStringBlockWriter) =\n        if writer.BeginList(\"Types\", instances.Count) then\n            let sorted = \n                instances \n                |> Seq.sortBy (fun x -> x.Reference.GetType().Name)\n            for item in sorted do\n                writer.Write(item.Reference.ToString().Replace(\"\\n\", \"\\n  \"))\n            writer.End()\n    override c.ToString() =\n        StringBlockWriter.Format(c.ToString)\n        \ntype private CopyRegistryHandler() =\n    interface IRegistryHandler<IRegistry> with\n        member c.Handle<'a>(registry, _, instance : 'a byref) =\n            registry.Set<'a>(instance)\n\n[<AutoOpen>]\nmodule Registry =\n    type IRegistry with\n        member c.CopyTo(dest : IRegistry) =\n            let handler = CopyRegistryHandler()\n            c.Iter(dest, handler)\n            \n        member c.GetOrDefault<'a>(fallback : 'a) =\n            match c.TryGet<'a>() with\n            | true, x -> x\n            | false, _ -> fallback\n\n        member c.GetOrDefault<'a>() =\n            c.GetOrDefault(Unchecked.defaultof<'a>)\n\n        member c.GetOrSetDefault<'a>(fallback : 'a) =\n            match c.TryGet<'a>() with\n            | true, value -> value \n            | false, _ -> c.Set(fallback); fallback\n"
  },
  {
    "path": "src/Garnet/Resources.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.IO.Compression\nopen System.Runtime.InteropServices\nopen System.Threading\n\ntype IStreamSource =\n    abstract TryOpen : string -> ValueOption<Stream>\n\ntype IReadOnlyFolder =\n    inherit IStreamSource\n    inherit IDisposable\n    abstract GetFiles : string * string -> string seq\n    abstract Contains : string -> bool\n    abstract FlushChanged : Action<string> -> unit\n\ntype IFolder =\n    inherit IReadOnlyFolder\n    abstract OpenWrite : string -> Stream\n\nmodule private ResourcePath =\n    let getCanonical (path : string) =\n        path.Replace('\\\\', '/').ToLowerInvariant()\n\n    /// Result has front slashes\n    let getRelativePath (dir : string) (file : string) =\n        try\n            let pathUri = Uri(getCanonical file)\n            let dir = getCanonical dir\n            let dirWithSlash = if dir.EndsWith(\"/\") then dir else dir + \"/\"\n            let dirUri = Uri(dirWithSlash);\n            Uri.UnescapeDataString(dirUri.MakeRelativeUri(pathUri).ToString())\n        with ex ->\n            raise(Exception($\"Could not get relative path in directory '%s{dir}' for file '%s{file}'\", ex))\n\n    let getExtensions (path : string) = seq {\n        let file = Path.GetFileName(path)\n        let mutable start = file.IndexOf('.')\n        while start >= 0 do\n            yield file.Substring(start)\n            start <- file.IndexOf('.', start + 1)\n        }\n\n/// Thread-safe\ntype private ResourceInvalidationSet() =\n    let changedSet = Dictionary<string, DateTime>()\n    let flushed = List<_>()\n    let sync = obj()\n    member c.Invalidate(key, timestamp) =\n        Monitor.Enter sync\n        changedSet.[key] <- timestamp\n        Monitor.Exit sync\n    member c.FlushChanged(maxTimestamp, action : Action<string>) =\n        Monitor.Enter sync\n        for kvp in changedSet do\n            if kvp.Value < maxTimestamp then\n                flushed.Add kvp.Key\n        for key in flushed do\n            action.Invoke key\n            changedSet.Remove key |> ignore\n        flushed.Clear()\n        Monitor.Exit sync\n\ntype ZipArchiveFolder(archive : ZipArchive) =\n    let lookup =\n        let d = Dictionary<string, ZipArchiveEntry>()\n        for entry in archive.Entries do\n            d.Add(ResourcePath.getCanonical entry.FullName, entry)\n        d\n    new(file : string) =\n        new ZipArchiveFolder(ZipFile.OpenRead(file))\n    member c.GetFiles(dir : string, _) =\n        lookup.Keys\n        |> Seq.filter (fun name -> name.StartsWith(dir))\n    member c.Contains(file) = \n        lookup.ContainsKey(file)\n    member c.TryOpen(file) =\n        match lookup.TryGetValue(file) with\n        | true, entry -> ValueSome (entry.Open())\n        | false, _ -> ValueNone\n    member c.Dispose() =\n        archive.Dispose()\n    interface IReadOnlyFolder with\n        member c.GetFiles(dir, searchPattern) = c.GetFiles(dir, searchPattern)\n        member c.Contains(file) = c.Contains(file)\n        member c.TryOpen(file) = c.TryOpen(file)\n        member c.FlushChanged _ = ()\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n\n/// Thread-safe\ntype FileFolder(rootDir, delay) =\n    let rootDir = \n        let path = if String.IsNullOrEmpty(rootDir) then Directory.GetCurrentDirectory() else rootDir\n        Path.GetFullPath(path)\n    let changedSet = ResourceInvalidationSet()\n    let handler =\n        FileSystemEventHandler(fun s e ->              \n            let path = ResourcePath.getRelativePath rootDir e.FullPath\n            //log <| sprintf \"'%s' %A\" path e.ChangeType\n            changedSet.Invalidate(path, DateTime.UtcNow))\n    let disposeWatcher = \n        if delay = Int32.MaxValue then ignore\n        else\n            Directory.CreateDirectory(rootDir) |> ignore\n            let watcher = \n                new FileSystemWatcher(\n                    Path = rootDir,\n                    NotifyFilter = NotifyFilters.LastWrite,\n                    Filter = \"*.*\",\n                    IncludeSubdirectories = true,\n                    EnableRaisingEvents = true)\n            watcher.add_Changed handler\n            watcher.add_Created handler\n            watcher.Dispose\n    let rec openFile path (delay : int) retryCount =\n        try\n            File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)\n        with ex ->\n            if retryCount = 0 then raise ex\n            else \n                Thread.Sleep delay\n                openFile path (delay * 2) (retryCount - 1)\n    let getFullPath (file : string) =\n        if Path.IsPathRooted(file) then file\n        else Path.Combine(rootDir, file)\n    new(rootDir) = new FileFolder(rootDir, Int32.MaxValue)\n    new() = new FileFolder(\"\")\n    member c.GetFiles(dir, searchPattern) =\n        let subDir = Path.Combine(rootDir, dir)\n        if Directory.Exists subDir \n        then \n            Directory.EnumerateFiles(subDir, searchPattern)\n            |> Seq.map (fun file -> \n                let fullPath = Path.GetFullPath file\n                ResourcePath.getRelativePath rootDir fullPath)\n        else Seq.empty\n    member c.Contains(file) = \n        let path = getFullPath file\n        File.Exists(path)\n    member c.TryOpen(file) =\n        let path = getFullPath file\n        if File.Exists(path) then ValueSome (openFile path 1 5 :> Stream)\n        else ValueNone\n    member c.OpenWrite(file) =\n        let path = getFullPath file\n        let dir = Path.GetDirectoryName path\n        Directory.CreateDirectory dir |> ignore\n        File.Open(path, FileMode.Create) :> Stream\n    member c.FlushChanged(action : Action<string>) =\n        let maxTimestamp = DateTime.UtcNow - TimeSpan.FromMilliseconds(float delay)\n        changedSet.FlushChanged(maxTimestamp, action)\n    member c.Dispose() =\n        disposeWatcher()\n    interface IFolder with\n        member c.GetFiles(dir, searchPattern) = c.GetFiles(dir, searchPattern)\n        member c.Contains(file) = c.Contains(file)\n        member c.TryOpen(file) = c.TryOpen(file)\n        member c.OpenWrite(file) = c.OpenWrite(file)\n        member c.FlushChanged(action) = c.FlushChanged(action)\n        member c.Dispose() = c.Dispose()\n    override c.ToString() = rootDir\n\n/// Note Dispose() is absent\ntype NonDisposingStream(stream : Stream, onClose) =\n    inherit Stream()\n    override c.Position\n        with get() = stream.Position\n        and set value = stream.Position <- value\n    override c.CanRead = stream.CanRead\n    override c.CanWrite = stream.CanWrite\n    override c.CanSeek = stream.CanSeek\n    override c.Length = stream.Length\n    override c.Write(input, offset, count) =\n        stream.Write(input, offset, count)\n    override c.Read(output, offset, count) =\n        stream.Read(output, offset, count)\n    override c.Flush() = stream.Flush()\n    override c.Seek(offset, origin) =\n        stream.Seek(offset, origin)\n    override c.SetLength(length) =\n        stream.SetLength(length)\n    override c.Close() =\n        onClose()\n\n/// Stream lookup is thread-safe, but reading/writing is not\ntype MemoryStreamLookup<'k when 'k : equality>() =\n    let updated = List<'k>()\n    let streams = Dictionary<'k, MemoryStream>()\n    let sync = obj()\n    member c.OpenWrite (id : 'k) =\n        let ms = \n            lock sync <| fun () ->\n                match streams.TryGetValue id with\n                | true, x -> x\n                | false, _ -> \n                    let ms = new MemoryStream()\n                    streams.Add(id, ms)\n                    ms\n        new NonDisposingStream(ms, fun () -> updated.Add id) :> Stream\n    member c.GetKeys() =\n        lock sync <| fun () ->\n            streams.Keys |> Seq.toArray :> seq<_>\n    member c.Contains(key) =\n        lock sync <| fun () ->\n            streams.ContainsKey(key)\n    member c.TryOpen(id) =\n        lock sync <| fun () ->\n            match streams.TryGetValue id with\n            | true, x -> ValueSome x\n            | false, _ -> ValueNone\n        |> ValueOption.map (fun ms ->\n            let length = int ms.Length\n            let buffer = ms.GetBuffer()\n            new MemoryStream(buffer, 0, length, false) :> Stream)\n    member c.FlushChanged (action : Action<'k>) =\n        let count = updated.Count\n        for i = 0 to count - 1 do\n            let key = updated.[i]\n            action.Invoke(key)\n        updated.RemoveRange(0, count)\n    override c.ToString() =\n        $\"Streams (%d{streams.Count}):\\n\" + String.Join(\"\\n\",\n            streams |> Seq.map (fun kvp ->\n                $\"%A{kvp.Key}: %d{kvp.Value.Length}\"))\n\ntype MemoryFolder() =\n    let lookup = MemoryStreamLookup<string>()\n    interface IFolder with\n        member c.GetFiles(_, _) =\n            lookup.GetKeys()\n        member c.Contains(file) =\n            lookup.Contains(file)\n        member c.TryOpen(file) =\n            lookup.TryOpen(file)\n        member c.OpenWrite(file) =\n            lookup.OpenWrite(file)\n        member c.FlushChanged _ = ()\n        member c.Dispose() = ()\n    override c.ToString() =\n        lookup.ToString()\n\n[<AutoOpen>]\nmodule FileFolder =\n    type IStreamSource with\n        member c.Open(key) =\n            match c.TryOpen(key) with\n            | ValueNone -> failwithf $\"Could not open %s{key}\"\n            | ValueSome x -> x\n\n    type IReadOnlyFolder with\n        member c.GetFiles(dir) =\n            c.GetFiles(dir, \"*.*\")\n\n        member c.GetFiles() =\n            c.GetFiles(\".\", \"*.*\")\n\ntype IResourceCache =\n    abstract TryGetResource<'a> : string * [<Out>] value : byref<'a> -> bool\n    abstract LoadResource<'a> : string -> 'a\n    abstract AddResource<'a> : string * 'a -> unit\n\ntype IResourceLoader =\n    abstract Load : IReadOnlyFolder * IResourceCache * string -> unit\n\ntype private ResourceTypeId() =\n    static let mutable id  = 0\n    static member GetNext() = Interlocked.Increment(&id)\n\ntype private ResourceTypeId<'a>() =\n    static let mutable id = ResourceTypeId.GetNext()\n    static member Id = id\n\ntype ResourceCache(folder : IReadOnlyFolder) =\n    let mutable caches = Array.zeroCreate<obj>(8)\n    let mutable folder = folder\n    let disposables = List<IDisposable>()\n    let loaders = Dictionary<string, IResourceLoader>()\n    new() = new ResourceCache(new MemoryFolder())\n    member private c.GetResources<'a>() =\n        let id = ResourceTypeId<'a>.Id\n        if id >= caches.Length then\n            Buffer.resizeArray (id + 1) &caches\n        let cache = caches.[id]\n        if Comparisons.isNotNull cache then cache :?> Dictionary<string, 'a>\n        else            \n            let cache = Dictionary<string, 'a>()\n            caches.[id] <- cache :> obj\n            cache\n    member private c.TryLoad(key) =\n        let extensions = ResourcePath.getExtensions key\n        let mutable loaded = false\n        let mutable e = extensions.GetEnumerator()\n        while not loaded && e.MoveNext() do\n            match loaders.TryGetValue(e.Current) with\n            | true, loader ->\n                loader.Load(folder, c, key)\n                loaded <- true\n            | false, _ -> ()\n        loaded\n    member c.SetFolder(newFolder) =\n        folder <- newFolder\n    member c.AddLoader(extension, typeLoader) =\n        loaders.[extension] <- typeLoader\n    member c.AddResource(key, resource) =\n        let key = ResourcePath.getCanonical key\n        c.GetResources().[key] <- resource\n        match resource :> obj with\n        | :? IDisposable as disposable -> disposables.Add(disposable)\n        | _ -> ()\n    member c.TryGetResource<'a>(key, [<Out>] value : byref<'a>) =\n        let canonicalKey = ResourcePath.getCanonical key\n        let cache = c.GetResources<'a>() \n        cache.TryGetValue(canonicalKey, &value)\n    member c.LoadResource<'a> key =\n        let canonicalKey = ResourcePath.getCanonical key\n        let cache = c.GetResources<'a>() \n        match cache.TryGetValue(canonicalKey) with\n        | true, resource -> resource\n        | false, _ ->\n            if not (c.TryLoad(canonicalKey)) then\n                let str = String.Join(\"\\n\", loaders.Keys)\n                failwith $\"No loader for {key}, available:\\n{str}\" \n            match cache.TryGetValue(canonicalKey) with\n            | true, resource -> resource\n            | false, _ -> failwith $\"{key} was not loaded\"\n    member c.LoadAll(path) =\n        for file in folder.GetFiles(path) do\n            c.TryLoad(file) |> ignore\n    member c.Dispose() =\n        for disposable in disposables do\n           disposable.Dispose() \n    interface IResourceCache with\n        member c.TryGetResource<'a>(key, [<Out>] value : byref<'a>) =\n            c.TryGetResource<'a>(key, &value)\n        member c.LoadResource<'a> key = c.LoadResource<'a>(key)\n        member c.AddResource(key, resource) = c.AddResource(key, resource)\n    interface IDisposable with\n        member c.Dispose() = c.Dispose()\n"
  },
  {
    "path": "src/Garnet/Segments.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen System\nopen System.Collections.Generic\nopen System.Text\nopen System.Runtime.InteropServices\nopen System.Threading\nopen Garnet.Composition.Comparisons\n\nmodule Segment =\n    [<Literal>]\n    let SegmentBits = 6\n\n    [<Literal>]\n    let SegmentSize = 64\n\n    [<Literal>]\n    let SegmentMask = 0b111111\n    \n/// Contiguous 64-element segment with a mask indicating which elements\n/// are defined and ID to identify the segment in a sparse collection\n[<Struct>]\ntype Segment<'k, 'a when 'k :> IComparable<'k>> =\n    val Id : 'k\n    val Mask : uint64\n    val Data : 'a[]\n    new(id, mask, data) = { Id = id; Mask = mask; Data = data }\n    override s.ToString() =\n        sprintf \"%s %s\" (s.Id.ToString()) (Format.maskToString 64 s.Mask)\n    member c.GetOrDefault(i, fallback) =\n        if c.Mask &&& (1UL <<< i) <> 0UL then c.Data.[i]\n        else fallback    \n\n/// 64-bit mask and ID to identify the segment in a sparse collection\n[<Struct>]\ntype internal BitSegment<'k when 'k :> IComparable<'k>> =\n    val Id : 'k\n    val Mask : uint64\n    new(id, mask) = { Id = id; Mask = mask }\n    override s.ToString() =\n        sprintf \"%s %s\" (s.Id.ToString()) (Format.maskToString 64 s.Mask)\n\n/// Provides a method for accepting a generically-typed segment\ntype ISegmentHandler<'p, 'k\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> =\n    abstract member Handle<'a> : 'p * Segment<'k, 'a> -> unit\n\ntype ISegmentListHandler<'p, 'k\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> =\n    abstract member Handle<'a> : 'p * ReadOnlyMemory<Segment<'k, 'a>> -> unit\n    \ntype PrintHandler<'k\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality>(mask) =\n    let sb = StringBuilder()\n    let mutable bytes = 0\n    let iter action param mask (sa : _[]) =\n        let mutable m = mask\n        let mutable i = 0\n        while m <> 0UL do\n            if m &&& 1UL <> 0UL then action param sa.[i]\n            m <- m >>> 1\n            i <- i + 1\n    member private c.Print<'a, 'b> (_ : 'a) (x : 'b) = \n        let t = typeof<'b>\n        sb.AppendLine() |> ignore\n        sb.Append(t.Name) |> ignore\n        if not (Format.isEmptyType t) then \n            bytes <- bytes + sizeof<'b>\n            sb.Append(sprintf \" %A\" x) |> ignore\n    interface ISegmentHandler<unit, 'k> with               \n        member c.Handle((), segment) =\n            iter c.Print () (segment.Mask &&& mask) segment.Data            \n    interface ISegmentListHandler<unit, 'k> with               \n        member c.Handle((), segments) =\n            for segment in segments.Span do\n                iter c.Print () (segment.Mask &&& mask) segment.Data            \n    override c.ToString() =\n        sprintf \"%d bytes\" bytes\n        + sb.ToString()    \n\n[<AutoOpen>]\nmodule internal Internal =\n    type ISegments<'k \n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality> =\n        abstract member Clear : unit -> unit\n        abstract member TryFind : 'k * byref<int> -> bool\n        abstract member Remove : 'k * uint64 -> unit\n        abstract member Commit : unit -> unit\n        abstract member Handle<'p> : 'p * ISegmentListHandler<'p, 'k> ->unit\n        abstract member Handle<'p> : 'p * ISegmentHandler<'p, 'k> * 'k * uint64 ->unit\n\n    let failComponentOperation op mask conflict (s : Segment<_, 'a>) =\n        failwithf \"Could not %s %s, sid: %A\\n  Requested: %s\\n  Existing:  %s\\n  Error:     %s\"\n            op (typeof<'a> |> Format.typeToString) s.Id (Format.maskToString 64 mask) \n            (Format.maskToString 64 s.Mask) (Format.maskToString 64 conflict)\n            \n    [<Struct>]\n    type PendingSegment<'k, 'a when 'k :> IComparable<'k>> = {\n        Data : 'a[]\n        Id : 'k\n        mutable Mask : uint64\n        mutable RemovalMask : uint64\n        }\n\n    /// Ordered list of segments and lookup    \n    type CurrentSegments<'k, 'a \n            when 'k :> IComparable<'k> \n            and 'k :> IEquatable<'k> \n            and 'k : equality>(pool : Stack<'a[]>) =    \n        let mutable segments = Array.zeroCreate<Segment<'k, 'a>> 8\n        let mutable count = 0\n        let idToIndex = DictionarySlim<'k, int>()\n        member internal c.ComponentCount = \n            let mutable total = 0\n            for i = 0 to count - 1 do\n                let seg = segments.[i]\n                total <- total + Bits.bitCount64 seg.Mask\n            total\n        member c.Components = seq {\n            for i = 0 to count - 1 do\n                let seg = segments.[i]\n                let mutable m = seg.Mask\n                let mutable i = 0\n                while m <> 0UL do\n                    if m &&& 1UL <> 0UL then \n                        yield seg.Data.[i]\n                    m <- m >>> 1\n                    i <- i + 1\n            }            \n        member c.Segments = \n            ReadOnlyMemory(segments).Slice(0, count)\n        member c.Count = count\n        /// Takes segment index, not ID\n        member c.Item with get i = segments.[i]\n        member c.Clear() =\n            for i = 0 to count - 1 do\n                let seg = segments.[i]\n                Buffer.clearArrayMask seg.Mask seg.Data\n                pool.Push(seg.Data)\n            Array.Clear(segments, 0, count)\n            count <- 0\n            idToIndex.Clear()\n        member c.Handle<'p>(param, handler : ISegmentListHandler<'p, 'k>) =\n            handler.Handle(param, c.Segments)\n        /// Given a segment ID, returns segment index if found or -1 if not found\n        member c.TryFind(id, [<Out>] i : byref<_>) = \n            idToIndex.TryGetValue(id, &i)\n        /// Change value of components which are already present\n        member c.Set(i, mask) =\n            let s = segments.[i]\n            let newMask = s.Mask ||| mask\n            let diff = s.Mask ^^^ newMask\n            if diff <> 0UL then failComponentOperation \"set\" mask (mask &&& ~~~s.Mask) s\n            s.Data        \n        member c.Add(i, mask) =\n            let s = segments.[i]\n            let newMask = s.Mask ||| mask\n            segments.[i] <- Segment(s.Id, newMask, s.Data)\n            s.Data        \n        member c.Remove(i, mask) =\n            let s = segments.[i]\n            let newMask = s.Mask &&& ~~~mask\n            segments.[i] <- Segment(s.Id, newMask, s.Data)\n            s.Data\n        /// Input must be new sorted segments\n        member c.MergeFrom(src : PendingSegment<'k, 'a>[], srcCount) =\n            // add new segments\n            let hasAdded = srcCount > 0\n            if hasAdded then\n                // allocate space first\n                let origCount = count\n                count <- count + srcCount\n                Buffer.resizeArray count &segments\n                let a = segments\n                let b = src\n                let mutable k = count - 1\n                let mutable j = srcCount - 1\n                let mutable i = origCount - 1\n                while k >= 0 do\n                    a.[k] <-\n                        if j < 0 || (i >= 0 && (a.[i].Id.CompareTo(b.[j].Id) >= 0)) then\n                            let x = a.[i]\n                            i <- i - 1\n                            x\n                        else\n                            let x = b.[j]\n                            j <- j - 1\n                            Segment(x.Id, x.Mask, x.Data)\n                    k <- k - 1\n            // remove any empty segments\n            let mutable iDest = 0\n            for iSrc = 0 to count - 1 do\n                let seg = segments.[iSrc]\n                if seg.Mask = 0UL then\n                    pool.Push(seg.Data)\n                else\n                    segments.[iDest] <- seg\n                    iDest <- iDest + 1\n            let hasRemoved = iDest < count\n            if hasRemoved then\n                Array.Clear(segments, iDest, count - iDest)\n                count <- iDest\n            // rebuild lookup\n            if hasAdded || hasRemoved then\n                idToIndex.Clear()\n                for i = 0 to count - 1 do\n                    let seg = segments.[i]\n                    let index = &idToIndex.GetOrAddValueRef(&seg.Id)\n                    index <- i        \n\n    type PendingSegments<'k, 'a \n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k>  \n        and 'k : equality>(pool : Stack<_>) =\n        let comparison = Comparison<PendingSegment<'k, 'a>>(fun a b -> \n            a.Id.CompareTo(b.Id))\n        let comparer = Comparer.Create comparison\n        let allocateData =\n            // if no members, assume type has a single state and use single array\n            // in this case, only bits will be stored\n            let t = typeof<'a>\n            if Format.isEmptyType t then\n                let data = Array.zeroCreate(Segment.SegmentSize)\n                fun () -> data\n            else\n                fun () ->\n                    if pool.Count > 0 then pool.Pop() \n                    else Array.zeroCreate(Segment.SegmentSize)\n        let mutable segments = Array.zeroCreate<PendingSegment<'k, 'a>> 8\n        let mutable count = 0\n        let idToIndex = DictionarySlim<'k, int>()\n        member c.Clear() =\n            for i = 0 to count - 1 do\n                let seg = segments.[i]\n                Buffer.clearArray seg.Data\n                pool.Push(seg.Data)\n            count <- 0\n            idToIndex.Clear()\n        member c.Item with get i = segments.[i]\n        member internal c.Count = count        \n        member internal c.Segments = seq {\n            for i = 0 to count - 1 do\n                yield c.[i]\n            }\n        member c.GetMask(id) =\n            match idToIndex.TryGetValue(id) with\n            | true, i -> segments.[i].Mask\n            | false, _ -> 0UL\n        member c.Add(id, mask) =\n            match idToIndex.TryGetValue(id) with\n            | true, i ->\n                let s = &segments.[i]\n                s.Mask <- s.Mask ||| mask\n                s.RemovalMask <- s.RemovalMask &&& ~~~mask\n                s.Data        \n            | false, _ ->\n                let i = count\n                if count = segments.Length then\n                    segments <- Buffer.expandArray (count + 1) segments \n                let data = allocateData()\n                segments.[i] <- { \n                    Id = id\n                    Mask = mask\n                    Data = data\n                    RemovalMask = 0UL\n                    }\n                count <- count + 1\n                let index = &idToIndex.GetOrAddValueRef(&id)\n                index <- i\n                data\n        /// Removes bits given ID, but does not remove segment if empty\n        member c.Remove(id, mask) =\n            match idToIndex.TryGetValue(id) with\n            | true, i ->\n                let s = &segments.[i]\n                s.Mask <- s.Mask &&& ~~~mask\n                s.RemovalMask <- s.RemovalMask ||| mask \n            | false, _ ->\n                let i = count\n                if count = segments.Length then\n                    segments <- Buffer.expandArray (count + 1) segments \n                let data = allocateData()\n                segments.[i] <- { \n                    Id = id\n                    Mask = 0UL\n                    Data = data\n                    RemovalMask = mask\n                    }\n                count <- count + 1\n                let index = &idToIndex.GetOrAddValueRef(&id)\n                index <- i\n        member c.ApplyRemovalsTo(target : ISegments<_>) =\n            // copy in case this is called on self\n            let count = count\n            for i = 0 to count - 1 do\n                let delta = segments.[i]\n                if delta.RemovalMask <> 0UL then\n                    target.Remove(delta.Id, delta.RemovalMask)           \n        member c.FlushTo(target : CurrentSegments<'k, 'a>) =\n            if count > 0 then\n                // first copy into existing, removing deltas\n                let mutable di = 0\n                while di < count do\n                    let delta = segments.[di]\n                    // if not found, skip past for now\n                    match target.TryFind(delta.Id) with\n                    | false, _ -> di <- di + 1\n                    | true, i -> \n                        // apply removal\n                        if delta.RemovalMask <> 0UL then\n                            let data = target.Remove(i, delta.RemovalMask)\n                            Buffer.clearArrayMask delta.RemovalMask data\n                        // apply addition\n                        if delta.Mask <> 0UL then\n                            // copy into existing\n                            let data = target.Add(i, delta.Mask)\n                            Buffer.copyArrayMask delta.Mask delta.Data data\n                            Buffer.clearArrayMask delta.Mask delta.Data\n                        // remove from deltas\n                        Array.Clear(delta.Data, 0, delta.Data.Length)\n                        pool.Push(delta.Data)\n                        count <- count - 1\n                        segments.[di] <- segments.[count]\n                        segments.[count] <- Unchecked.defaultof<_>\n                // remaining are all new segments\n                Array.Sort(segments, 0, count, comparer)\n                target.MergeFrom(segments, count)\n                // clear without recycling to pool since we passed ownership\n                Array.Clear(segments, 0, count)\n                count <- 0\n                idToIndex.Clear()\n        member c.ToString(formatSegments, formatBitSegments) =\n            let additions = \n                segments \n                |> Seq.filter (fun s -> s.Mask <> 0UL)\n                |> Seq.map (fun s -> Segment(s.Id, s.Mask, s.Data))\n                |> Seq.toArray\n            let removals = \n                segments \n                |> Seq.filter (fun s -> s.RemovalMask <> 0UL)\n                |> Seq.map (fun s -> BitSegment(s.Id, s.RemovalMask))\n                |> Seq.toArray\n            sprintf \"%d/%dA %d/%dR%s%s\"\n                (additions |> Seq.sumBy (fun s -> Bits.bitCount64 s.Mask)) additions.Length\n                (removals |> Seq.sumBy (fun s -> Bits.bitCount64 s.Mask)) removals.Length\n                (formatSegments \"  A\" (ReadOnlyMemory(additions)))\n                (formatBitSegments \"  R\" (ReadOnlyMemory(removals)))\n        override c.ToString() =\n            c.ToString(Format.formatIndexedList, Format.formatIndexedList)\n\n/// Sparse list of segments\ntype Segments<'k, 'a \n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality>() =    \n    let pool = Stack<_>()\n    let pending = PendingSegments<'k, 'a>(pool)\n    let current = CurrentSegments<'k, 'a>(pool)\n    member internal c.PendingCount = pending.Count\n    member internal c.GetPending i = pending.[i]\n    /// Returns a sequence of the components present\n    member internal c.Components = current.Components\n    member c.GetSpan() = \n        current.Segments.Span\n    member c.GetMemory() = \n        current.Segments\n    /// Number of current segments\n    member c.Count = current.Count        \n    /// Takes segment index, not ID\n    member c.Item with get i = current.[i]\n    /// Returns the number of current components stored\n    member c.GetComponentCount() = \n        let mutable total = 0\n        for i = 0 to c.Count - 1 do\n            total <- total + Bits.bitCount64 c.[i].Mask\n        total\n    /// Given a segment ID, returns true if the segment is present and assigns its index\n    member c.TryFind(sid, [<Out>] i : byref<_>) = \n        current.TryFind(sid, &i)\n    /// Immediately clears all current and pending data\n    member c.Clear() =\n        pending.Clear()\n        current.Clear()\n    /// Sets mask for a segment ID\n    member c.Set(sid, mask) =\n        match c.TryFind(sid) with\n        | true, i -> current.Set(i, mask)\n        | false, _ -> failwithf \"Segment %A not present\" sid\n    /// Returns segment so data can be filled\n    /// Assume either not present or in removal list\n    member c.Add(sid, mask) =\n        pending.Add(sid, mask)\n    /// Assumes present, not considering addition list\n    member c.Remove(sid, mask) =\n        pending.Remove(sid, mask)\n    /// Commits any pending changes and removes empty segments\n    member c.Commit() = \n        pending.FlushTo(current)\n    member internal c.ApplyRemovalsTo segments =\n        if not (obj.ReferenceEquals(c, segments)) then\n            pending.ApplyRemovalsTo segments\n    interface ISegments<'k> with\n        member c.Clear() =\n            c.Clear()\n        member c.TryFind(sid, [<Out>] i : byref<_>) = \n            current.TryFind(sid, &i)\n        member c.Remove(sid, mask) =\n            c.Remove(sid, mask)\n        member c.Commit() = \n            c.Commit()\n        member c.Handle<'p>(param, handler : ISegmentListHandler<'p, 'k>) =\n            current.Handle(param, handler)\n        member c.Handle(param, handler, sid, mask) =\n            match c.TryFind(sid) with\n            | false, _ -> ()\n            | true, si ->\n                let seg = current.[si]\n                let masked = Segment(seg.Id, seg.Mask &&& mask, seg.Data)\n                handler.Handle(param, masked)\n    member internal c.ToString(formatSegments, formatBitSegments) =\n        let prefix = \"\"\n        let pendingStr = pending.ToString(formatSegments, formatBitSegments)\n        sprintf \"%s: %d/%dC %s%s\"\n            (typeof<'a> |> Format.typeToString)\n            current.ComponentCount current.Count\n            (formatSegments (prefix + \"  C\") current.Segments)\n            (if pendingStr.Length > 0 then \"\\n\" + pendingStr else \"\")\n    override c.ToString() =\n        c.ToString(Format.formatIndexedList, Format.formatIndexedList)\n\ntype Segments<'k, 'a\n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality> with\n    /// Returns mask if present, zero otherwise\n    member c.GetMask sid =\n        match c.TryFind(sid) with\n        | true, i -> c.[i].Mask\n        | false, _ -> 0UL\n    member c.Contains(sid) =\n        let mutable i = 0\n        c.TryFind(sid, &i)          \n    member c.Get(sid) =\n        match c.TryFind(sid) with\n        | true, si -> c.[si]\n        | false, _ -> failwithf \"Cannot get %s segment %A\" (Format.typeToString typeof<'a>) sid\n    /// Removes entire segment\n    member c.Remove(sid) =\n        match c.TryFind(sid) with\n        | false, _ -> ()\n        | true, si ->\n            let mask = c.[si].Mask\n            c.Remove(sid, mask)\n    /// Marks all segments for removal, which is different than immediate Clear()\n    member c.RemoveAll() =\n        for i = 0 to c.Count - 1 do\n            let seg = c.[i]\n            c.Remove(seg.Id, seg.Mask)\n    member c.GetSegmentOrEmpty(sid) =\n        match c.TryFind(sid) with\n        | true, i -> c.[i]\n        | false, _ -> Segment(sid, 0UL, null)\n    /// Given a segment ID, returns segment index if found or -1 if not found\n    member c.Find(sid) = \n        match c.TryFind(sid) with\n        | true, i -> i\n        | false, _ -> -1\n\ntype ISegmentStore<'k\n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality> =\n    abstract member GetSegments<'b> : unit -> Segments<'k, 'b>\n    abstract member Handle<'p> : 'p * ISegmentListHandler<'p, 'k> -> unit\n    abstract member Handle<'p> : 'p * 'k * uint64 * ISegmentHandler<'p, 'k> -> unit\n\ntype CopyHandler<'k\n        when 'k :> IComparable<'k> \n        and 'k :> IEquatable<'k> \n        and 'k : equality>() =\n    static let mutable instance = CopyHandler<'k>()\n    static member Instance = instance\n    interface ISegmentHandler<ISegmentStore<'k>, 'k> with               \n        member c.Handle<'a>(store, segment : Segment<'k, 'a>) =\n            let dest = store.GetSegments<'a>()\n            let data = dest.Add(segment.Id, segment.Mask)\n            segment.Data.CopyTo(data, 0)\n    interface ISegmentListHandler<ISegmentStore<'k>, 'k> with               \n        member c.Handle<'a>(store, src : ReadOnlyMemory<Segment<'k, 'a>>) =\n            let dest = store.GetSegments<'a>()\n            for seg in src.Span do\n                let data = dest.Add(seg.Id, seg.Mask)\n                seg.Data.CopyTo(data, 0)\n\ntype internal ComponentTypeId() =\n    static let mutable id  = 0\n    static member GetNext() = Interlocked.Increment(&id)\n\ntype internal ComponentTypeId<'a>() =\n    static let mutable id = ComponentTypeId.GetNext()\n    static member Id = id\n        \ntype SegmentStore<'k \n    when 'k :> IComparable<'k> \n    and 'k :> IEquatable<'k> \n    and 'k : equality>() =\n    let segmentLists = List<ISegments<'k>>()\n    let mutable lookup = Array.zeroCreate<ISegments<'k>>(8)\n    member c.GetSegments<'a>() =\n        let id = ComponentTypeId<'a>.Id\n        if id >= lookup.Length then\n            Buffer.resizeArray (id + 1) &lookup\n        let segs = lookup.[id]\n        if isNotNull segs then segs :?> Segments<'k, 'a>\n        else            \n            let segs = Segments<'k, 'a>()\n            lookup.[id] <- segs :> ISegments<'k>\n            segmentLists.Add(segs)\n            segs\n    member c.Clear() =\n        for segs in segmentLists do\n            segs.Clear()\n    member c.Remove(sid, mask) =\n        for segs in segmentLists do\n            segs.Remove(sid, mask)\n    member c.Handle(param, handler : ISegmentListHandler<_,_>) =      \n        for s in segmentLists do\n            s.Handle(param, handler)\n    member c.Handle(param, sid, mask, handler : ISegmentHandler<_,_>) =      \n        for s in segmentLists do\n            s.Handle(param, handler, sid, mask)\n    member c.Commit() =\n        for segs in segmentLists do\n            segs.Commit()\n    member c.ApplyRemovalsFrom (segments : Segments<_,_>) =\n        if segments.PendingCount > 0 then\n            for segs in segmentLists do\n                segments.ApplyRemovalsTo(segs)\n    interface ISegmentStore<'k> with\n        member c.Handle(param, handler : ISegmentListHandler<_,_>) =      \n            c.Handle(param, handler)\n        member c.Handle(param, sid, mask, handler : ISegmentHandler<_,_>) =      \n            c.Handle(param, sid, mask, handler)\n        member c.GetSegments<'a>() = \n            c.GetSegments<'a>()\n    override c.ToString() =\n        let prefix = \"\"\n        segmentLists\n        |> Seq.map (fun item -> item.ToString().Replace(\"\\n\", \"\\n  \"))\n        |> Format.listToString (prefix + \"  \") (c.GetType() |> Format.typeToString)\n\n[<AutoOpen>]\nmodule SegmentStore =\n    type ISegmentStore<'k\n           when 'k :> IComparable<'k> \n           and 'k :> IEquatable<'k> \n           and 'k : equality> with\n           \n        member c.CopyTo(dest : ISegmentStore<'k>) =\n            let handler = CopyHandler.Instance :> ISegmentListHandler<_,_>\n            c.Handle(dest, handler)\n            \n        member c.CopyTo(dest : ISegmentStore<'k>, sid, mask) =\n            let handler = CopyHandler.Instance :> ISegmentHandler<_,_>\n            c.Handle(dest, sid, mask, handler)            "
  },
  {
    "path": "tests/Garnet.Tests/ActorTests.fs",
    "content": "module Garnet.Tests.Actors\n\nopen System\nopen System.Collections.Generic\nopen System.IO\nopen System.Threading\nopen Expecto\nopen Garnet.Composition\nopen Garnet.Streaming\n\ntype Run = struct end\ntype Ping = struct end\ntype Pong = struct end\n\ntype Inbox() =\n    let dict = Dictionary<Type, obj>()\n    member c.OnAll<'a>(action : Message<'a> -> unit) =\n        let t = typeof<'a>\n        let combined =\n            match dict.TryGetValue t with\n            | false, _ -> action\n            | true, existing -> \n                let existing = existing :?> (Message<'a> -> unit)\n                fun e -> \n                    existing e\n                    action e        \n        dict.[t] <- combined\n    member c.TryReceive<'a> e =\n        match dict.TryGetValue(typeof<'a>) with\n        | true, x -> \n            let handle = x :?> (Message<'a> -> unit)\n            handle e\n            true\n        | false, _ -> false\n    interface IInbox with\n        member c.Receive(outbox, message) =\n            c.TryReceive(message) |> ignore\n\nlet bgDispatcherId = 0\nlet mainDispatcherId = 1\n\nlet runPingPong onPing onPong iterations =\n    let mutable count = 0\n    use a = new ActorSystem(0)\n    a.Register(ActorId 1, fun _ ->\n        let h = Mailbox()\n        h.On<Run> <| fun e ->\n            h.Send(ActorId 2, Ping())\n        h.On<Pong> <| fun e ->\n            count <- count + 1\n            if count < iterations then\n                onPing(e)\n                h.Respond(Ping())\n        Actor(h))\n    a.Register(ActorId 2, fun _ -> \n        let h = Mailbox()\n        h.On<Ping> <| fun e -> \n            onPong e\n            h.Respond(Pong())\n        Actor(h))\n    a.Process(ActorId 1, Run())\n    a.ProcessAll()\n    count\n\nlet sendReceiveMessages send =\n    let results = List<_>()\n    use a = new ActorSystem(0)\n    let h = Inbox()\n    h.OnAll<int> <| fun msg ->\n        results.Add {\n            Buffer = ReadOnlySpan(msg.Buffer, 0, msg.Count).ToArray()\n            Pool = null\n            Count = msg.Count\n            SourceId = msg.SourceId\n            DestinationId = msg.DestinationId\n            }\n    a.Register(ActorId 1, fun _ -> Actor(h))\n    send (a.Get(ActorId 1))\n    a.ProcessAll()\n    results |> List.ofSeq\n    \n[<Tests>]\nlet tests =\n    testList \"actors\" [            \n        testCase \"send to undefined actor\" <| fun () ->\n            use a = new ActorSystem()\n            a.Send(ActorId 1, 10)\n            a.ProcessAll()\n\n        testCase \"send batch\" <| fun () ->\n            let results = sendReceiveMessages <| fun a ->\n                a.SendAll(ReadOnlyMemory([| 1; 2; 3 |]).Span)\n            let r = List.head results\n            r.SourceId |> shouldEqual (ActorId 0)\n            r.DestinationId |> shouldEqual (ActorId 1)\n            r.Buffer |> shouldEqual [| 1; 2; 3 |]\n\n        testCase \"send single\" <| fun () ->\n            let results = sendReceiveMessages <| fun a ->\n                a.Send(1)                \n            let r = List.head results\n            r.SourceId |> shouldEqual (ActorId 0)\n            r.DestinationId |> shouldEqual (ActorId 1)\n            r.Buffer |> shouldEqual [| 1 |]\n\n        testCase \"send single with source\" <| fun () ->\n            let results = sendReceiveMessages <| fun a ->\n                a.Send(1, sourceId = ActorId 2)                \n            let r = List.head results\n            r.SourceId |> shouldEqual (ActorId 2)\n            r.DestinationId |> shouldEqual (ActorId 1)\n            r.Buffer |> shouldEqual [| 1 |]\n\n        testCase \"send to any actor\" <| fun () ->\n            let msgs = List<_>()\n            let inbox = Mailbox()\n            inbox.On<int> msgs.Add\n            use a = new ActorSystem()\n            a.Register(fun id -> Actor(inbox))\n            a.Send(ActorId 1, 10)\n            a.ProcessAll()\n            msgs.Count |> shouldEqual 1\n\n        testCase \"create ping pong actors\" <| fun () ->\n            let iterations = 10\n            runPingPong ignore ignore iterations |> shouldEqual iterations\n\n        testCase \"send message to self\" <| fun () ->\n            let mutable count = 0\n            use a = new ActorSystem(0)\n            a.Register(ActorId 1, fun _ ->\n                let c = Mailbox()\n                c.On<int> <| fun e ->\n                    if e < 10 then\n                        count <- count + 1\n                        c.Send(ActorId 1, e + 1)\n                Actor(c, mainDispatcherId))\n            a.Process(ActorId 1, 0)\n            count |> shouldEqual 10\n\n        testCase \"send message to other\" <| fun () ->\n            let mutable count1 = 0\n            let mutable count2 = 0\n            use a = new ActorSystem(0)\n            a.Register(ActorId 1, fun _ ->\n                let c = Mailbox()\n                c.On<int> <| fun e ->\n                    if e < 10 then\n                        count1 <- count1 + 1\n                        c.Send(ActorId 2, e + 1)\n                Actor(c))\n            a.Register(ActorId 2, fun _ ->\n                let c = Mailbox()\n                c.On<int> <| fun e ->\n                    if e < 10 then\n                        count2 <- count2 + 1\n                        c.Send(ActorId 1, e + 1)\n                Actor(c))\n            a.Process(ActorId 1, 0)\n            count1 |> shouldEqual 5\n            count2 |> shouldEqual 5\n\n        testCase \"send message to background actor\" <| fun () ->\n            let mutable count1 = 0\n            let mutable count2 = 0\n            use a = new ActorSystem(1)\n            a.Register(ActorId 1, fun _ ->\n                let c = Mailbox()\n                c.On<Thread> <| fun e ->\n                    // bg thread should be different\n                    Expect.notEqual Thread.CurrentThread.ManagedThreadId e.ManagedThreadId \"\"\n                c.On<int> <| fun e ->\n                    if e < 10 then\n                        count1 <- count1 + 1\n                        //printfn \"FG: %d\" Thread.CurrentThread.ManagedThreadId\n                        c.Send(ActorId 2, e + 1)\n                Actor(c, mainDispatcherId))\n            a.Register(ActorId 2, fun _ ->\n                let c = Mailbox()\n                c.On<int> <| fun e ->\n                    if e < 10 then\n                        count2 <- count2 + 1\n                        //printfn \"BG: %d\" Thread.CurrentThread.ManagedThreadId\n                        c.Send(ActorId 1, Thread.CurrentThread)\n                        c.Send(ActorId 1, e + 1)\n                Actor(c))    \n            a.Process(ActorId 1, 0)\n            a.ProcessAll()\n            count1 |> shouldEqual 5\n            count2 |> shouldEqual 5\n\n        testCase \"respond to messages\" <| fun () ->\n            let config = { \n                Dispatchers = \n                    [|\n                        {\n                            Name = \"\"\n                            DispatcherType = DispatcherType.Background\n                            ThreadCount = 2\n                            Throughput = 100\n                        }                \n                    |]\n                }\n            use a = new ActorSystem(config)\n            a.Register(ActorFactory.Create(fun _ m ->\n                m.On<Ping> <| fun e -> \n                    //printfn \"Responding from %A to %A\" m.DestinationId m.SourceId\n                    m.Respond(Pong())))\n            for i = 1 to 10 do\n                a.Send(ActorId i, ActorId (i * 2), Ping())\n            a.ProcessAll()\n\n        testCase \"send random messages to background actors\" <| fun () ->\n            use a = new ActorSystem(2)\n            a.Register(fun (createId : ActorId) -> \n                let rand = Random(createId.Value)\n                let c = Mailbox()\n                c.On<int> <| fun e -> \n                    //printfn \"%d: %d\" id.id e\n                    if e < 1000 then\n                        let nextId = rand.Next(1, 256)\n                        c.Send(ActorId nextId, e + 1)\n                Actor(c))\n            a.Process(ActorId 1, 123)\n\n        testCase \"actor thread stress test\" <| fun () ->\n            let mutable count = 0L\n            let log = MemoryActorStreamSource()\n            let createRegistry() = \n                let r = MessageRegistry()\n                r.Register 1 <| RawSerializer<MessageHeader>()\n                r.Register 2 <| RawSerializer<int>()\n                r.Register 3 <| RawSerializer<uint32>()                \n                r.Register 4 <| RawSerializer<uint8>()                \n                r.Register 5 <| RawSerializer<uint16>()\n                r.Register 6 <| RawSerializer<int8>()\n                r\n            let start = DateTime.Now\n            let config = { \n                Dispatchers = \n                    [|\n                        {\n                            Name = \"\"\n                            DispatcherType = DispatcherType.Background\n                            ThreadCount = 4\n                            Throughput = 100\n                        }                \n                    |]\n                }\n            use a = new ActorSystem(4)\n            let rules = \n                [\n                ActorFactory.Create(ActorId 1, fun _ (c : Mailbox) ->\n                    c.On<int> <| fun e ->\n                        for i = 1 to 1000 do\n                            c.Send(ActorId 2, uint32 i)\n                            c.Send(ActorId 2, uint16 i))\n                ActorFactory.Create(ActorId 2, fun _ (c : Mailbox) ->\n                    c.On<int> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 3, uint32 i)\n                            c.Send(ActorId 4, uint16 i)\n                    c.On<uint32> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 3, uint8 i))\n                ActorFactory.Create(ActorId 3, fun _ (c : Mailbox) ->\n                    c.On<uint8> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 4, int8 i)\n                    c.On<uint16> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 4, int8 i)\n                    c.On<uint32> <| fun e ->\n                        Interlocked.Increment(&count) |> ignore)\n                ActorFactory.Create(ActorId 4, fun _ (c : Mailbox) ->\n                    c.On<int8> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 2, int i)\n                    c.On<uint16> <| fun e ->\n                        Interlocked.Increment(&count) |> ignore)\n                ]     \n                //|> List.map (ActorFactory.withLogging createRegistry log.OpenWrite)\n            a.Register(rules)\n            for i = 1 to 100 do\n                a.Process(ActorId 1, 0)\n                Thread.Sleep(0)\n            a.ProcessAll()\n            //let duration = DateTime.Now - start\n            // printfn \"%s\" <| a.ToString()\n            // printfn \"%A\" <| duration\n            //log.Print {\n            //    createMessageRegistry = createRegistry\n            //    createFormatter = Formatter\n            //    print = ignore\n            //    range = MessageRange.count 1000\n            //    filter = ActorLogFilter.all\n            //    }\n\n        testCase \"threaded actor logging\" <| fun () ->\n            let log = MemoryActorStreamSource()\n            let createRegistry() = \n                let r = MessageRegistry()\n                r.Register 1 <| RawSerializer<MessageHeader>()\n                r.Register 2 <| RawSerializer<int>()\n                r.Register 3 <| RawSerializer<uint32>()\n                r.Register 4 <| RawSerializer<uint8>()\n                r\n            use a = new ActorSystem(4)\n            let rules = \n                [\n                ActorFactory.Create(ActorId 1, fun _ c ->\n                    c.On<int> <| fun e ->\n                        for i = 1 to 100 do\n                            c.Send(ActorId 2, uint32 i))\n                ActorFactory.Create(ActorId 2, fun _ c ->\n                    c.On<uint32> <| fun e ->\n                        for i = 1 to 1 do\n                            c.Send(ActorId 3, uint8 i))\n                ]     \n                //|> List.map (ActorFactory.withLogging createRegistry log.OpenWrite)\n            a.Register(rules)\n            for i = 1 to 100 do\n                a.Process(ActorId 1, 0)\n                Thread.Sleep(0)\n            a.ProcessAll()\n            //log.Print {\n            //    createMessageRegistry = createRegistry\n            //    createFormatter = Formatter\n            //    print = ignore\n            //    range = MessageRange.count 1000\n            //    filter = ActorLogFilter.all\n            //    }\n\n        testCase \"serialize string\" <| fun () ->\n            let s = StringSerializer() :> ISerializer<_>\n            let ms = new MemoryStream()\n            let value = \"test\"\n            s.Write ms value\n            ms.Position <- 0L\n            let str2 = s.Read ms \n            str2 |> shouldEqual value\n\n//        testCase \"log messages\" <| fun () ->\n//            let reg = MessageRegistry()\n//            reg.Register 1 <| RawSerializer<MessageHeader>()\n//            reg.Register 10 <| RawSerializer<int>()            \n//            reg.Register 11 <| StringSerializer()\n//            let ms = new MemoryStream()\n//            use a = new ActorSystem(0)\n//            let id1 = ActorId 15\n//            let id2 = ActorId 25\n//            a.Register [\n//                ActorFactory.Create(id1, fun _ ->\n//                    let h = Mailbox()\n//                    h.On<int> <| fun e -> \n//                        h.Send(id2, \"msg\" + e.ToString())\n//                    Actor(LogInbox(id1, h, StreamInbox(reg, ms))))\n//                ]\n//            a.Process(id1, 100)\n//            ms.Position <- 0L\n//            use a2 = new ActorSystem(0)\n//            a2.Register <| ActorFactory.Create(fun id -> \n//                Actor(PrintInbox(id, Formatter(), ref 0, ignore)))\n//            a2.Process(id1, { enableActorLog = true })\n//            a2.Process(id2, { enableActorLog = true })\n//            let sender = StreamMessageSender(reg, fun h -> true)\n//            sender.SendAll(ms, a2)\n//            a2.ProcessAll()\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/Assertions.fs",
    "content": "﻿namespace Garnet.Tests\n\nopen Expecto\n\n[<AutoOpen>]\nmodule Assertions =\n    let shouldEqual b a =\n        Expect.equal a b \"\"\n\n    let shouldNotEqual b a =\n        Expect.notEqual a b \"\"\n          "
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ActorBenchmarks.fs",
    "content": "﻿module Garnet.Benchmarks.Actors\n\nopen System.Collections.Generic\nopen System.Diagnostics\nopen System.Runtime.InteropServices\nopen System.Threading\nopen Garnet.Composition\n\nmodule PingPong =\n    type RecordedMessage = {\n        sourceId : ActorId \n        destId : ActorId \n        sequence : int\n        payload : int64\n        dispatcher : string\n        timestamp : int64\n        }\n\n    module RecordedMessage =\n        let formatPair (s, r) =\n            $\"%d{r.payload} from [%d{s.sequence}] a%d{s.sourceId.Value} (d%s{s.dispatcher}) to [%d{r.sequence}] a%d{r.destId.Value} (d%s{r.dispatcher}) in %d{r.timestamp - s.timestamp}\"\n        \n    type SynchronizedQueue<'a>() =\n        let incoming = Queue<'a>()\n        let sync = obj()\n        member c.Enqueue x =\n            Monitor.Enter sync\n            incoming.Enqueue x\n            Monitor.Exit sync\n        member c.TryDequeue([<Out>] item : byref<_>) =\n            Monitor.Enter sync\n            let r = incoming.Count > 0\n            if r then item <- incoming.Dequeue()\n            Monitor.Exit sync\n            r        \n        member c.Flush() = seq {\n            let mutable item = Unchecked.defaultof<'a>\n            while c.TryDequeue(&item) do\n                yield item\n            }\n\n    module Tests =\n        let runLogging log onSend onReceive poolCount actorsPerPool workerCount duration initCount maxCount batchSize =\n            let actorCount = actorsPerPool * poolCount\n            let receivedCount = ref 0\n            let sentCount = ref 0\n            let config = {\n                Dispatchers = [|\n                    for i = 1 to poolCount do\n                        yield {\n                            // workers\n                            Name = $\"Worker{i}\"\n                            ThreadCount = workerCount\n                            Throughput = 100\n                            DispatcherType = DispatcherType.Background\n                        }\n                    yield {\n                        // main\n                        Name = \"Main\"\n                        DispatcherType = DispatcherType.Foreground\n                        ThreadCount = 0\n                        Throughput = 100\n                    }\n                    |]\n                }\n            use a = new ActorSystem(config)\n            a.Register(fun (actorId : ActorId) ->\n                let writer = new MessageWriter<_>()\n                let mailbox = Mailbox()\n                mailbox.OnAll<int64> <| fun e ->\n                    let span = e.Span\n                    let c = Interlocked.Increment receivedCount\n                    if log then\n                        onReceive { \n                            sourceId = ActorId.Undefined\n                            destId = actorId\n                            sequence = c\n                            payload = span.[0]\n                            timestamp = Stopwatch.GetTimestamp()\n                            dispatcher = mailbox.ToString()\n                            }\n                    if c <= maxCount then\n                        let _ = Interlocked.Increment sentCount\n                        let rand = uint64 c * 2862933555777941757UL + 3037000493UL\n                        let destId = (abs (int rand) % actorCount) + 1 |> ActorId\n                        if duration > 0 then\n                            Thread.Sleep duration\n                        if log  then\n                            let nextItem = span.[0] + 1L\n                            onSend { \n                                sourceId = actorId\n                                destId = destId\n                                sequence = c\n                                payload = nextItem\n                                timestamp = Stopwatch.GetTimestamp()\n                                dispatcher = mailbox.ToString()\n                                }\n                        for i = 0 to span.Length - 1 do\n                            writer.WriteValue(span.[i] + 1L)\n                        writer.DestinationId <- destId\n                        writer.Outbox <- mailbox\n                        writer.Send()\n                let dispatcherId = (actorId.Value - 1) / actorsPerPool\n                Actor(mailbox, dispatcherId))\n            let writer = new MessageWriter<_>()\n            for i = 0 to initCount - 1 do\n                let destId = (i % actorCount) + 1 |> ActorId\n                let payload = (i + 1) * 10000000\n                for i = 0 to batchSize - 1 do\n                    writer.WriteValue (payload + i * 10000 |> int64)\n                if log then\n                    onSend { \n                        sourceId = ActorId.Undefined\n                        destId = destId\n                        sequence = 0\n                        payload = payload |> int64\n                        timestamp = Stopwatch.GetTimestamp()\n                        dispatcher = \"\"\n                        }\n                writer.Outbox <- a\n                writer.DestinationId <- destId\n                writer.Send()\n            a.ProcessAll()\n            let expected = maxCount + initCount\n            let actual = receivedCount.Value\n            if actual <> expected then\n                printfn $\"Expected received count: %d{expected}, actual: %d{actual}\"\n                printfn \"%s\" <| a.ToString()\n            let expected = maxCount\n            let actual = sentCount.Value\n            if actual <> expected then\n                printfn $\"Expected sent count: %d{expected}, actual: %d{actual}\"\n            if log then\n                printfn $\"%s{a.ToString()}\"\n\n        let run = runLogging false ignore ignore\n\n        let runHistory log poolCount actorsPerPool workerCount duration initCount maxCount batchSize =\n            let sent = SynchronizedQueue<_>()\n            let received = SynchronizedQueue<_>()\n            runLogging log sent.Enqueue received.Enqueue poolCount actorsPerPool workerCount duration initCount maxCount batchSize\n            let sent = sent.Flush() |> Seq.toArray\n            let received = received.Flush() |> Seq.toArray\n            let sentSet = sent |> Seq.map (fun x -> (x.destId, x.payload), x) |> Map.ofSeq\n            received |> Seq.map (fun x -> sentSet.[x.destId, x.payload], x) |> Seq.toArray\n\n        let runMain log useMain (workerCount : int) initCount maxCount =\n            let maxActorCount = maxCount * 2 - initCount\n            let count = ref 0\n            let createInbox _ =\n                let writer = new MessageWriter<_>()\n                let mailbox = Mailbox()\n                mailbox.OnAll<int64> <| fun e ->\n                    if Interlocked.Increment count <= maxActorCount then\n                        let span = e.Span\n                        for i = 0 to span.Length - 1 do\n                            writer.WriteValue span.[i]\n                        writer.Outbox <- mailbox\n                        writer.DestinationId <- mailbox.SourceId \n                        writer.Send()\n                mailbox\n            use a = new ActorSystem(workerCount)\n            a.Register(ActorId 1, fun _ -> Actor(createInbox()))\n            a.Register(ActorId 2, fun _ -> Actor(createInbox(), if useMain then 1 else 0))\n            for i = 1 to initCount do\n                a.Send(ActorId 1, ActorId 2, int64 i)\n            a.ProcessAll()\n            if log then\n                printfn $\"%s{a.ToString()}\\n%d{count.Value}\"\n\ntype Run = struct end\ntype Ping = struct end\ntype Pong = struct end\n\n//[<SimpleJob(RuntimeMoniker.CoreRt50)>]\n//type SimplePingPongBenchmark() =\n//    let a = new ActorSystem(0)\n//    let mutable count = 0\n//    [<Params(1000)>]\n//    member val N = 1 with get, set\n//    [<GlobalSetup>]\n//    member this.Setup() =\n//        a.Register(ActorId 1, fun _ ->\n//            let h = Mailbox()\n//            h.On<Run> <| fun e ->\n//                h.Send(ActorId 2, Ping())\n//            h.On<Pong> <| fun e ->\n//                count <- count + 1\n//                if count < this.N then\n//                    h.Respond(Ping())\n//            Actor(h))\n//        a.Register(ActorId 2, fun _ -> \n//            let h = Mailbox()\n//            h.On<Ping> <| fun e -> \n//                h.Respond(Pong())\n//            Actor(h)\n//            )\n//    [<Benchmark>]\n//    member this.SingleThread() = \n//        count <- 0\n//        a.Process(ActorId 1, Run())\n//        a.ProcessAll()\n//        count\n"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ActorBenchmarks.fsx",
    "content": "﻿#r \"../../../src/Garnet/bin/Release/net6.0/Garnet.dll\"\n#load \"ActorBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Actors.PingPong\nopen Garnet.Benchmarks.Actors.PingPong.Tests\n\n#time\n\nrunHistory true 1 2 2 0 10 20 1\n|> Seq.map RecordedMessage.formatPair\n|> Seq.iter (printfn \"%s\")\n\nrunHistory false 200 16 0 1000 200000 1 |> ignore\n\n//runMain true true 1 10 6000\n//runMain true true 0 10 4000000\n//runMain true false 2 10 4000000\n\n// poolCount actorsPerPool workerCount duration initCount maxCount batchSize\n\n// 0ms tasks\nrun 1 1000 1 0 5000 4000000 1\nrun 1 1000 2 0 5000 4000000 1 // 0.845\nrun 1 1000 4 0 5000 4000000 1\nrun 1 1000 8 0 5000 4000000 1\n\n// 1ms tasks\nrun 1 1000 1 1 50 100 1\nrun 1 1000 2 1 50 100 1 // 0.774\nrun 1 1000 4 1 50 100 1 // 0.388\nrun 1 1000 8 1 50 100 1\n// 1ms multiple pools\nrun 2  500 2 1 50 100 1 // 0.525\nrun 2  500 4 1 50 100 1\n\n// 0ms tasks\nrun 1 1000 8 0 5000 1000000 100\nrun 1 1000 1 0 5000 10000000 1 // 2.726\nrun 1 1000 2 0 5000 10000000 1 // 2.088\nrun 1 1000 3 0 5000 10000000 1 //\nrun 1 1000 4 0 5000 10000000 1 // 1.461\nrun 1 10000 8 0 50000 10000000 1\nrun 1 1000 8 1 5000 400 1\n\n// multiple pools\nrun 2 500 2 0 5000 10000000 1 // 1.573\nrun 1 1000 4 0 5000 10000000 1 // 1.512\nrun 1 1000 8 0 50000 10000000 1\n\nrun 1 5 1 0 10 20 1\nrun 1 2000 4 0 500 2000 1\nrun 1 1000 1 0 5000 5000000 20\nrun 1 1000 4 0 5000 4000000 1\n\n// batch size 1000\nrun 1 1000 8 0 500 1000000 1000\nrun 1 1000 12 0 500 1000000 1000\n\n// More threads than cores with large number of messages:\n// This reproduces issue where not all messages would be\n// completed. Using throughput=1 can also help to repro.\nrunLogging true ignore ignore 1 5000 32 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 16 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 8 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 6 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 4 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 2 0 500 10000000 1\nrunLogging true ignore ignore 1 5000 1 0 500 10000000 1\n"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ChannelBenchmarks.fs",
    "content": "﻿module Garnet.Benchmarks.Channels\n\nopen Garnet.Composition\n\nlet run iterations sendFreq =\n    let mutable sum = 0L\n    let c = Channels()\n    let channel = c.GetChannel<int>()\n    let _ =\n        c.On<int> <| fun e ->\n            sum <- sum + int64 e\n            if e < iterations then\n                if e % sendFreq = 0 then channel.Send (e + 1)\n                else channel.Publish (e + 1)\n    c.Publish 1\n    c.Commit()\n    let mutable count = 0\n    while c.Publish() do\n        c.Commit()\n        count <- count + 1\n    sum, count\n\nlet runWithoutCachingChannel iterations sendFreq =\n    let mutable sum = 0L\n    let c = Channels()\n    let _ =\n        c.On<int> <| fun e ->\n            sum <- sum + int64 e\n            if e < iterations then\n                if e % sendFreq = 0 then c.Send<int> (e + 1)\n                else c.Publish<int> (e + 1)\n    c.Publish 1\n    c.Commit()\n    let mutable count = 0\n    while c.Publish() do\n        c.Commit()\n        count <- count + 1\n    sum, count\n\nlet runBatches iterations batchSize =\n    let mutable sum = 0L\n    let mutable count = 0\n    let c = Channels()\n    let channel = c.GetChannel<int>()\n    let _ =\n        c.OnAll<int> <| fun list ->\n            for e in list.Span do\n                sum <- sum + int64 e\n            if count < iterations then\n                count <- count + 1\n                for e in list.Span do\n                    channel.Send e\n    for i = 1 to batchSize do\n        c.Send i\n    c.Commit()\n    while c.Publish() do\n        c.Commit()\n    sum, count\n"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ChannelBenchmarks.fsx",
    "content": "﻿#r \"../bin/Release/net6.0/Garnet.dll\"\n#load \"ChannelBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Channels\n\n#time\n\nrun 30_000_000 1\nrun 30_000_000 10\nrunWithoutCachingChannel 30_000_000 1\nrunBatches 10_000_000 1\nrunBatches 1_000_000 100\n"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ContainerBenchmarks.fs",
    "content": "﻿module Garnet.Benchmarks.Containers\n\nopen System.Collections.Generic\nopen System.Numerics\nopen Garnet.Composition\n\nlet create1 count =\n    let c = Container()\n    for i = 1 to count do c.Create().Add(1)\n    c.Commit()\n    c\n    \nlet create2 count =\n    let c = Container()\n    for i = 1 to count do c.Create().With(5.0).Add(1)\n    c.Commit()\n    c\n    \nlet create3 count =\n    let c = Container()\n    for i = 1 to count do c.Create().With(Vector2(2.0f, 3.0f)).With(0.1).Add(1)\n    c.Commit()\n    c\n    \nlet create4 count =\n    let c = Container()\n    for i = 1 to count do c.Create().With(\"a\").With(Vector2(2.0f, 3.0f)).With(0.1).Add(1)\n    c.Commit()\n    c\n\nlet runCreateDestroyEntities count iterations =\n    let c = Container()\n    let eids = List<Eid>()\n    for i = 1 to iterations do\n        for i = 1 to count do\n            let e = c.Create()\n            eids.Add(e.Id)\n        c.Commit()\n        for eid in eids do\n            c.Destroy eid\n        c.Commit()\n        eids.Clear()\n\nlet runCreateDestroyMultipleComponents count iterations =\n    let c = Container()\n    let eids = List<Eid>()\n    for i = 1 to iterations do\n        for i = 1 to count do\n            let e = c.Create().With(1).With(\"a\").With(1.2).With('b')\n            eids.Add(e.Id)\n        c.Commit()\n        for eid in eids do\n            c.Destroy eid\n        c.Commit()\n        eids.Clear()\n\nlet runAddRemoveComponent count iterations =\n    let c = Container()\n    let eids = List<Eid>()\n    for i = 1 to count do\n        let e = c.Create()\n        eids.Add(e.Id)\n    for i = 1 to iterations do\n        for eid in eids do\n            c.Get(eid).Add(1)\n        c.Commit()\n        for eid in eids do\n            c.Get(eid).Remove<int>()\n        c.Commit()\n\nlet runAddRemoveComponentDirect count iterations =\n    let c = Container()\n    let eids = List<Eid>()\n    for i = 1 to count do\n        let e = c.Create()\n        eids.Add(e.Id)\n    let cmp = c.GetComponents<int>()\n    for i = 1 to iterations do\n        for eid in eids do\n            cmp.Add(eid, 1)\n        c.Commit()\n        for eid in eids do\n            cmp.Remove(eid)\n        c.Commit()\n\nlet runAddRemoveMultipleComponents count iterations =\n    let c = Container()\n    let eids = List<Eid>()\n    for i = 1 to count do\n        let e = c.Create()\n        eids.Add(e.Id)\n    for i = 1 to iterations do\n        for eid in eids do\n            c.Get(eid).With(1).With(\"a\").With(1.2).Add('b')\n        c.Commit()\n        for eid in eids do\n            c.Get(eid).Without<int>().Without<string>().Without<double>().Remove<char>()\n        c.Commit()\n\nlet runIterateComponents1 count iterations =\n    let c = create1 count\n    let mutable sum = 0L\n    let iter =\n        fun _ (x1 : int) ->\n            sum <- sum + int64 x1\n        |> Join.iter1\n        |> Join.over c\n    for i = 1 to iterations do\n        iter()\n    sum\n\nlet runIterateComponents2 count iterations =\n    let c = create2 count\n    let mutable sum = 0L\n    let iter =\n        fun _ struct(x1 : int, x2 : double) ->\n            sum <- sum + int64 x1 + int64 x2\n        |> Join.iter2\n        |> Join.over c\n    for i = 1 to iterations do\n        iter()\n    sum\n\nlet runIterateComponents3 count iterations =\n    let c = create3 count\n    let mutable sum = 0L\n    let iter =\n        fun _ struct(x1 : int, x2 : double, x3 : Vector2) ->\n            sum <- sum + int64 x1 + int64 x2 + int64 x3.X + int64 x3.Y\n        |> Join.iter3\n        |> Join.over c\n    for i = 1 to iterations do\n        iter()\n    sum\n    \nlet runIterateComponents4 count iterations =\n    let c = create4 count\n    let mutable sum = 0L\n    let iter =\n        fun _ struct(x1 : int, x2 : double, x3 : Vector2, x4 : string) ->\n            sum <- sum + int64 x1 + int64 x2 + int64 x3.X + int64 x3.Y + int64 x4.Length\n        |> Join.iter4\n        |> Join.over c\n    for i = 1 to iterations do\n        iter()\n    sum\n    \nlet runQuerySegments1 count iterations =\n    let c = create1 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for seg, s1 in c.QuerySegments<int>() do\n            for i in seg do\n                let x = s1.[i]\n                sum <- sum + int64 x\n    sum\n\nlet runQuerySegments2 count iterations =\n    let c = create2 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for seg, s1, s2 in c.QuerySegments<int, double>() do\n            for i in seg do\n                sum <- sum + int64 s1.[i] + int64 s2.[i]\n    sum\n\nlet runQuerySegments3 count iterations =\n    let c = create3 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for seg, s1, s2, s3 in c.QuerySegments<int, double, Vector2>() do\n            for i in seg do\n                sum <- sum + int64 s1.[i] + int64 s2.[i] + int64 s3.[i].X + int64 s3.[i].Y\n    sum\n\nlet runQuerySegments4 count iterations =\n    let c = create4 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for seg, s1, s2, s3, s4 in c.QuerySegments<int, double, Vector2, string>() do\n            for i in seg do\n                sum <- sum + int64 s1.[i] + int64 s2.[i] + int64 s3.[i].X + int64 s3.[i].Y + int64 s4.[i].Length\n    sum\n\nlet runQueryComponents1 count iterations =\n    let c = create1 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int>() do\n            sum <- sum + int64 r.Value\n    sum\n\nlet runQueryComponentsTuple2 count iterations =\n    let c = create2 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double>() do\n            let struct(x1, x2) = r.Values\n            sum <- sum + int64 x1 + int64 x2\n    sum\n\nlet runQueryComponentsTuple3 count iterations =\n    let c = create3 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double, Vector2>() do\n            let struct(x1, x2, x3) = r.Values\n            sum <- sum + int64 x1 + int64 x2 + int64 x3.X + int64 x3.Y\n    sum\n\nlet runQueryComponentsTuple4 count iterations =\n    let c = create4 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double, Vector2, string>() do\n            let struct(x1, x2, x3, x4) = r.Values\n            sum <- sum + int64 x1 + int64 x2 + int64 x3.X + int64 x3.Y + int64 x4.Length\n    sum\n\nlet runQueryComponents2 count iterations =\n    let c = create2 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double>() do\n            sum <- sum + int64 r.Value1 + int64 r.Value2\n    sum\n\nlet runQueryComponents3 count iterations =\n    let c = create3 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double, Vector2>() do\n            sum <- sum + int64 r.Value1 + int64 r.Value2 + int64 r.Value3.X + int64 r.Value3.Y\n    sum\n\nlet runQueryComponents4 count iterations =\n    let c = create4 count\n    let mutable sum = 0L\n    for i = 1 to iterations do\n        for r in c.Query<int, double, Vector2, string>() do\n            sum <- sum + int64 r.Value1 + int64 r.Value2 + int64 r.Value3.X + int64 r.Value3.Y + int64 r.Value4.Length\n    sum\n"
  },
  {
    "path": "tests/Garnet.Tests/Benchmarks/ContainerBenchmarks.fsx",
    "content": "﻿#r \"../bin/Release/net6.0/Garnet.dll\"\n#load \"ContainerBenchmarks.fs\"\n\nopen Garnet.Benchmarks.Containers\n\n#time\n\nrunCreateDestroyEntities 10000 1000\nrunCreateDestroyMultipleComponents 10000 100\nrunAddRemoveComponent 10000 1000\nrunAddRemoveComponentDirect 10000 1000\nrunAddRemoveMultipleComponents 10000 100\n\n// These are different ways of iterating over entities\n\n// Original function-based approach:\nrunIterateComponents1 100000 1000 // 00:00:00.628\nrunIterateComponents2 100000 1000 // 00:00:00.970\nrunIterateComponents3 100000 1000 // 00:00:01.215\nrunIterateComponents4 100000 1000 // 00:00:01.201\n\n// New enumerable approach:\nrunQueryComponents1 100000 1000 // 00:00:00.265\nrunQueryComponents2 100000 1000 // 00:00:00.368\nrunQueryComponents3 100000 1000 // 00:00:00.518\nrunQueryComponents4 100000 1000 // 00:00:00.728\n\n// New approach reading values to tuple:\nrunQueryComponentsTuple2 100000 1000 // 00:00:01.122\nrunQueryComponentsTuple3 100000 1000\nrunQueryComponentsTuple4 100000 1000\n\n// New approach using batches:\nrunQuerySegments1 100000 1000 // 00:00:00.176\nrunQuerySegments2 100000 1000 // 00:00:00.254\nrunQuerySegments3 100000 1000 // 00:00:00.328\nrunQuerySegments4 100000 1000 // 00:00:00.578\n\n"
  },
  {
    "path": "tests/Garnet.Tests/ChannelTests.fs",
    "content": "﻿module Garnet.Tests.Channels\n\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    testList \"channels\" [\n        testCase \"send before subscribe\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            c.Send 1\n            let sub = c.On<int> r.Add\n            c.Commit()\n            c.Publish() |> shouldEqual true\n            r |> Seq.toList |> shouldEqual [ 1 ]\n            \n        testCase \"publish before subscribe\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            c.Publish 1\n            let sub = c.On<int> r.Add\n            c.Commit()\n            c.Publish() |> shouldEqual false\n            r |> Seq.toList |> shouldEqual []\n\n        testCase \"publish\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub = c.On<int> r.Add\n            for i = 1 to 10 do\n                c.Publish i\n            r |> Seq.toList |> shouldEqual [ 1..10 ]\n        \n        testCase \"reentrant publish\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub =\n                c.On<int> <| fun e ->\n                    r.Add e\n                    if e < 10 then\n                        c.Publish (e + 1)\n            c.Publish 1\n            r |> Seq.toList |> shouldEqual [ 1..10 ]\n        \n        testCase \"send\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub =\n                c.On<int> <| fun e ->\n                    r.Add e\n                    if e < 10 then\n                        c.Send (e + 1)\n            c.Publish 1\n            c.Commit()\n            while c.Publish() do\n                c.Commit()\n            r |> Seq.toList |> shouldEqual [ 1..10 ]\n\n        testCase \"publish then send\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub =\n                c.On<int> <| fun e ->\n                    r.Add e\n                    if e < 10 then\n                        if e % 2 = 0 then c.Publish (e + 1)\n                        else c.Send (e + 1)\n            c.Send 1\n            c.Commit()\n            while c.Publish() do\n                c.Commit()\n            r |> Seq.toList |> shouldEqual [ 1..10 ]\n\n        testCase \"mixed send and publish\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub =\n                c.On<int> <| fun e ->\n                    r.Add e\n                    if e < 10 then\n                        if e % 2 = 0 then c.Publish (e + 1)\n                        else c.Send (e + 1)\n            c.Publish 1\n            c.Commit()\n            while c.Publish() do\n                c.Commit()\n            r |> Seq.toList |> shouldEqual [ 1..10 ]\n            \n        testCase \"set publisher\" <| fun () ->\n            let r = List<_>()\n            let c = Channels()\n            let sub = c.On<int> r.Add\n            c.Send 1\n            c.Commit()\n            c.Publish() |> shouldEqual true\n            r |> Seq.toList |> shouldEqual [ 1 ]\n            c.SetPublisher (ValueSome Publisher.Null)\n            c.Send 2\n            c.Commit()\n            c.Publish() |> shouldEqual true\n            r |> Seq.toList |> shouldEqual [ 1 ]\n            let r = List<_>()\n            let sub = c.On<string> r.Add\n            c.Send \"a\"\n            c.Commit()\n            c.Publish() |> shouldEqual true\n            r |> Seq.toList |> shouldEqual []\n            c.SetPublisher (ValueSome Publisher.Default)\n            c.Send \"a\"\n            c.Commit()\n            c.Publish() |> shouldEqual true\n            r |> Seq.toList |> shouldEqual [ \"a\" ]\n\n        testCase \"log publisher\" <| fun () ->\n            let r = List<_>()\n            let c = Container()\n            let sub =\n                c.On<int> <| fun e -> \n                    if e = 2 then failwith \"error\"\n                    c.Send<string> \"a\"\n                    c.Publish 'b'\n            c.SetPublisher(Publisher.Print {\n                PrintPublisherOptions.enabled with\n                    SendLog = r.Add\n                    })\n            c.Run 1\n            try c.Run 2 with ex -> r.Add(sprintf \"%s\" <| ex.ToString())\n            c.Run \"c\"\n            r.Count |> shouldEqual 6\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/CollectionTests.fs",
    "content": "﻿module Garnet.Tests.Collections\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Collections\n          \n[<Tests>]\nlet tests =\n    testList \"collections\" [\n        testCase \"enqueue to ring buffer\" <| fun () ->\n            let r = RingBuffer(2)\n            r.TryEnqueue 1 |> shouldEqual true\n            r.TryEnqueue 2 |> shouldEqual true\n            r.TryEnqueue 3 |> shouldEqual false\n            // dequeue\n            let x = ref 0\n            r.TryDequeue x |> shouldEqual true\n            x.Value |> shouldEqual 1\n            r.TryDequeue x |> shouldEqual true\n            x.Value |> shouldEqual 2\n            r.TryDequeue x |> shouldEqual false\n            // enqueue\n            r.TryEnqueue 3 |> shouldEqual true\n            r.TryDequeue x |> shouldEqual true\n            x.Value |> shouldEqual 3\n\n        testCase \"enqueue to ring buffer node\" <| fun () ->\n            let n = RingBufferNode(2)\n            n.Enqueue 1 |> shouldEqual n\n            n.Enqueue 2 |> shouldEqual n\n            let n2 = n.Enqueue 3 \n            n2 |> shouldNotEqual n\n            n2.Enqueue 4 |> shouldEqual n2\n            let x = ref 0\n            n.TryDequeue x |> shouldEqual n\n            x.Value |> shouldEqual 1\n            n.TryDequeue x |> shouldEqual n\n            x.Value |> shouldEqual 2\n            n2.TryDequeue x |> shouldEqual n2\n            x.Value |> shouldEqual 3\n            n2.TryDequeue x |> shouldEqual n2\n            x.Value |> shouldEqual 4\n            n2.TryDequeue x |> shouldEqual null\n\n        testCase \"enqueue to ring buffer queue\" <| fun () ->\n            let items = List<int>()\n            let q = RingBufferQueue(2)\n            [ 1..40 ] |> List.iter q.Enqueue\n            q.DequeueAll (Action<_>(items.Add))\n            Expect.sequenceEqual items [ 1..40 ] \"\"\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/ComponentTests.fs",
    "content": "﻿module Garnet.Tests.Components\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    let create() = Components<_,_,EidSegmentKeyMapper,_>.Create()\n    testList \"components\" [\n        testCase \"get component\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Commit()\n            s.GetOrDefault(Eid 1, 10) |> shouldEqual 10\n\n        testCase \"get component without commit\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            Expect.throws (fun () -> s.Get(Eid 1) |> ignore) \"exception expected\"\n\n        testCase \"get component or default\" <| fun () ->\n            let s = create()\n            s.GetOrDefault(Eid 1, 10) |> shouldEqual 10\n\n        testCase \"add component multiple times\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Commit()\n            s.Get(Eid 1) |> shouldEqual 10\n            s.Add(Eid 1, 11)\n            s.Commit()\n            s.Get(Eid 1) |> shouldEqual 11\n\n        testCase \"add component multiple times before commit\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Add(Eid 1, 11)\n            s.Commit()\n            s.Get(Eid 1) |> shouldEqual 11\n\n        testCase \"add and remove component before commit\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Remove(Eid 1)\n            s.Commit()\n            s.Contains(Eid 1) |> shouldEqual false\n\n        testCase \"remove and add component before commit\" <| fun () ->\n            let s = create()\n            s.Remove(Eid 1)\n            s.Add(Eid 1, 10)\n            s.Commit()\n            s.Get(Eid 1) |> shouldEqual 10\n\n        testCase \"remove component not present\" <| fun () ->\n            let s = create()\n            s.Remove(Eid 1)\n            s.Commit()\n            \n        testCase \"adding is deferred\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Count |> shouldEqual 0\n            s.Contains(Eid 1) |> shouldEqual false\n            s.GetOrDefault(Eid 1, 0) |> shouldEqual 0\n            s.Commit()\n            s.Count |> shouldEqual 1\n            s.Contains(Eid 1) |> shouldEqual true\n            s.GetOrDefault(Eid 1, 0) |> shouldEqual 10\n                \n        testCase \"removal is deferred\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Commit()\n            s.Remove(Eid 1)\n            s.Count |> shouldEqual 1\n            s.Contains(Eid 1) |> shouldEqual true\n            s.GetOrDefault(Eid 1, 0) |> shouldEqual 10\n            s.Commit()\n            s.Count |> shouldEqual 0\n            s.Contains(Eid 1) |> shouldEqual false\n                \n        testCase \"setting is immediate\" <| fun () ->\n            let s = create()\n            s.Add(Eid 1, 10)\n            s.Commit()\n            s.Set(Eid 1, 11)\n            s.Get(Eid 1) |> shouldEqual 11\n                \n        testCase \"random adding and removing\" <| fun () ->\n            let remove (list : List<_>) index =\n                let eid = list.[index]\n                list.[index] <- list.[list.Count - 1]\n                list.RemoveAt(list.Count - 1)\n                eid                    \n            let s = create()\n            let rand = Random(1)\n            let added = List<Eid>()\n            let removed = List<Eid>()\n            let meanCount = 1000\n            for i = 1 to 60 do\n                for i = 1 to 1000 do\n                    // half probability of add/removing\n                    let index = rand.Next(meanCount * 2)\n                    if index < added.Count then\n                        // remove component\n                        let eid = remove added index\n                        removed.Add(eid)\n                        s.Remove(eid)\n                    else\n                        // add component\n                        let index = index - added.Count\n                        let eid =\n                            if index < removed.Count \n                                then remove removed index \n                                else Eid (added.Count + removed.Count)\n                        added.Add(eid)\n                        s.Add(eid, eid.Value)\n                    s.Commit()\n            for eid in added do\n                s.Contains(eid) |> shouldEqual true\n                s.Get(eid) |> shouldEqual eid.Value\n            for eid in removed do\n                s.Contains(eid) |> shouldEqual false\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/CoroutineTests.fs",
    "content": "module Garnet.Tests.Coroutines\n\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    testList \"coroutines\" [\n        testCase \"nested\" <| fun _ ->\n            let s = CoroutineScheduler()\n            let r = List<_>()\n            let append = r.Add\n            s.Schedule <| seq {\n                append 1\n                yield Wait.All\n                append 2\n                // these happen in parallel\n                s.Schedule <| seq {\n                    append 21\n                    yield Wait.All\n                    append 22\n                    }\n                s.Schedule <| seq {\n                    append 31\n                    yield Wait.All\n                    append 32\n                    yield Wait.All\n                    append 33\n                    }\n                append 3\n                // here we block until two children complete\n                yield Wait.All\n                append 4\n                }\n            s.Run()\n            Expect.sequenceEqual [|1; 2; 3; 21; 31; 22; 32; 33; 4|] r \"\"\n\n        testCase \"waiting on messages\" <| fun _ ->\n            let c = Container()\n            let r = List<_>()\n            let append = r.Add\n            c.On<string> <| fun e ->\n                if e = \"1\" then \n                    append 21\n                    c.Send \"2\"\n                elif e = \"2\" then\n                    append 22\n                    c.Send \"3\"\n                else\n                    append 23\n            |> ignore\n            c.Start <| seq {\n                append 1\n                c.Send \"1\"\n                yield Wait.All\n                append 2\n                }\n            c.Run()\n            // r.ToArray()\n            Expect.sequenceEqual [|1; 21; 22; 23; 2|] r \"\"\n\n        testCase \"using container\" <| fun _ ->\n            let c = Container()\n            let r = List<_>()\n            let append = r.Add\n            c.On<string> <| fun e ->\n                c.Start <| seq {\n                    append 21\n                    yield Wait.All\n                    append 22\n                    }\n            |> ignore\n            c.Start <| seq {\n                append 1\n                yield Wait.All\n                append 2\n                c.Send \"\"\n                yield Wait.All\n                append 3\n                yield Wait.All\n                append 4\n                }\n            c.Run()\n            // coroutine waits for nested message handling\n            Expect.sequenceEqual [|1; 2; 21; 22; 3; 4|] r \"\"\n\n        testCase \"timed\" <| fun _ ->\n            let s = CoroutineScheduler()\n            let r = List<_>()\n            let append = r.Add\n            s.Schedule <| seq {\n                append 1\n                yield Wait 2L\n                append 2\n                append 3\n                s.Schedule <| seq {\n                    append 21\n                    yield Wait 1L\n                    append 22\n                    yield Wait 5L\n                    append 23\n                    }\n                yield Wait 3L\n                append 4\n                append 5\n                }\n            s.Run()\n            for i = 1 to 8 do\n                append (i * 100)\n                s.Step 1L\n                s.Run()\n            //r.ToArray()\n            Expect.sequenceEqual [|1; 100; 200; 2; 3; 21; 300; 22; 400; 500; 4; 5; 600; 700; 800; 23|] r \"\"\n\n        testCase \"timed nested using container\" <| fun _ ->\n            let c = Container()\n            let r = List<_>()\n            let append = r.Add\n            c.On<string> <| fun e ->\n                c.Start <| seq {\n                    append 21\n                    yield Wait.All\n                    append 22\n                    yield Wait.All\n                    append 23\n                    }\n            |> ignore\n            c.Start <| seq {\n                append 1\n                c.Send \"\"\n                yield Wait 0L\n                append 2\n                }\n            c.Run()\n            // coroutine does not wait for nested message handling\n            Expect.sequenceEqual [|1; 2; 21; 22; 23 |] r \"\"\n\n        testCase \"mixed nested using container\" <| fun _ ->\n            let c = Container()\n            let r = List<_>()\n            let append = r.Add\n            c.On<string> <| fun e ->\n                if e = \"update\" then\n                    c.Start <| seq {\n                        append 21\n                        yield Wait.All\n                        append 22\n                        yield Wait.All\n                        append 23\n                        }\n                elif e = \"anim\" then\n                    c.Start <| seq {\n                        append 31\n                        yield Wait 1L\n                        append 32\n                        yield Wait 2L\n                        append 33\n                        }\n            |> ignore\n            c.Start <| seq {\n                append 1\n                c.Send \"update\"\n                yield Wait.All\n                append 2\n                c.Send \"anim\"\n                yield Wait 5L\n                append 3\n                }\n            for i = 1 to 8 do\n                append (i * 100)\n                c.Step 1L\n                c.Run()\n            //r.ToArray()\n            Expect.sequenceEqual [|100; 1; 21; 22; 23; 2; 31; 200; 32; 300; 400; 33; 500; 600; 3; 700; 800|] r \"\"\n    ]\n"
  },
  {
    "path": "tests/Garnet.Tests/EntityTests.fs",
    "content": "module Garnet.Tests.Entities\n\nopen System\nopen System.Collections.Generic\nopen Garnet.Composition\nopen Expecto\n\ntype Velocity = {\n    velocity : int }\n\ntype Position = {\n    position : int }\n\n[<Tests>]\nlet tests =\n    let getComponents (c : Segments<_,_>) = seq {\n        for i = 0 to c.Count - 1 do\n            let seg = c.[i]\n            let mutable m = seg.Mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then \n                    yield seg.Data.[i]\n                m <- m >>> 1\n                i <- i + 1\n        }            \n\n    testList \"entities\" [\n        testCase \"increment ID version\" <| fun () ->\n            Eid(1, 0, 0).Gen |> shouldEqual 1\n            Eid(100).IncrementGen() |> shouldEqual (Eid(1, 0, 100))\n\n        testCase \"incrementing ID version wraps\" <| fun () ->\n            Eid(Eid.MaxGen, 0, 100).IncrementGen()\n            |> shouldEqual (Eid(0, 0, 100))\n\n        testCase \"create IDs from pool\" <| fun () ->\n            let p = EidPools()\n            [ for i = 1 to 100 do yield p.Next(0).Index ]\n            |> shouldEqual [ 64..163 ]\n\n        testCase \"create IDs from partition\" <| fun () ->\n            let p = EidPool(5)\n            p.Next().Value |> shouldEqual 0x500040\n            p.SegmentCount |> shouldEqual 2\n\n        testCase \"create and destroy before commit\" <| fun () ->\n            let c = Container()\n            let e = c.Create()\n            e.Destroy()\n            c.Commit()\n            c.GetComponents<Eid>().Count |> shouldEqual 0\n\n        testCase \"create and destroy before commit with pooled\" <| fun () ->\n            let c = Container()\n            let e1 = c.Create()\n            c.Commit()\n            let e2 = c.Create()\n            e2.Destroy()\n            c.Commit()\n            c.GetComponents<Eid>().Count |> shouldEqual 1\n\n        testCase \"recycle ID to pool\" <| fun () ->\n            let segmentSize = 64\n            let c = Container()\n            let usedIds = HashSet<Eid>()\n            for j = 1 to 100 do\n                for i = 1 to segmentSize do\n                    let eid = c.Create().Id\n                    eid.Gen |> shouldEqual (j - 1)\n                    usedIds.Add(eid) |> shouldEqual true\n                    c.Destroy eid\n                c.Commit()\n\n        testCase \"create entity\" <| fun () ->\n            let c = Container()\n            let e = c.Create()\n            e.Add { velocity = 3 }\n            e.Add { position = 5 }\n            c.GetSegments<Eid>().Count |> shouldEqual 0\n            c.Commit()\n            c.GetSegments<Eid>().Count |> shouldEqual 1            \n            c.GetSegments<Velocity>().Count |> shouldEqual 1\n            \n        testCase \"remove entity\" <| fun () ->\n            let c = Container()\n            let e = c.Create()\n            e.Add { velocity = 3 }\n            c.Commit()\n            e.Destroy()\n            c.GetSegments<Eid>().Count |> shouldEqual 1\n            c.GetSegments<Velocity>().Count |> shouldEqual 1\n            c.Commit()\n            c.GetSegments<Eid>().Count |> shouldEqual 0\n            c.GetSegments<Velocity>().Count |> shouldEqual 0\n\n        testCase \"add and remove simultaneously\" <| fun () ->\n            let c = Container()\n            // Eid and component go into additions\n            let e = c.Create()\n            e.Add 123\n            // Eid goes into removal\n            e.Destroy()\n            c.Commit()\n            // so components are present\n            c.GetSegments<Eid>().Count |> shouldEqual 0\n\n        testCase \"add and remove sequentially\" <| fun () ->\n            let c = Container()\n            // Eid and component go into additions\n            let e = c.Create()\n            e.Add 123\n            // additions applied\n            c.Commit()\n            c.GetSegments<Eid>().Count |> shouldEqual 1\n            // Eid goes into removal\n            e.Destroy()\n            // removals applied\n            c.Commit()\n            // so components are not present\n            c.GetSegments<Eid>().Count |> shouldEqual 0\n\n        testCase \"add and remove entities randomly\" <| fun () ->\n            let iterations = 10000\n            let maxBatchSize = 1000\n            let print = false\n            let rand = Random(1)\n            let eids = HashSet<Eid>()\n            let active = List<Eid>()\n            let c = Container()\n            for i = 1 to iterations do\n                if print then printfn \"Iteration %d\" i\n                let batchSize = min i maxBatchSize\n                match rand.Next() % 3 with\n                | 0 ->\n                    // create batch\n                    let count = rand.Next(batchSize)\n                    for i = 1 to count do\n                        let e = c.Create(rand.Next 16)\n                        if print then printfn \"Create %A\" e.Id\n                        if e.Id.Index < 64 then\n                            failwithf \"Invalid value: %A\" e.Id\n                        if not (eids.Add e.Id) then\n                            failwithf \"Duplicate: %A\" e.Id\n                        active.Add e.Id\n                | 1 ->\n                    // destroy batch\n                    let count = rand.Next(min batchSize active.Count)\n                    for i = 1 to count do\n                        let ri = rand.Next(active.Count)\n                        let eid = active.[ri]\n                        active.[ri] <- active.[active.Count - 1]\n                        active.RemoveAt(active.Count - 1)\n                        c.Destroy(eid)\n                        if print then printfn \"Destroy %A\" eid\n                | _ -> \n                    // commit and validate\n                    c.Commit()\n                    let eidStore = c.GetComponents<Eid>()\n                    if print then \n                        printfn \"Commit %s\" <| c.ToString()\n                        getComponents eidStore.Segments\n                        |> Seq.toArray\n                        |> printfn \"Active: %A\"\n                    eidStore.Count |> shouldEqual active.Count\n            c.Commit()\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/Garnet.Tests.fsproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <GenerateProgramFile>false</GenerateProgramFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Garnet\\Garnet.fsproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Iteration.fs\" />\n    <Compile Include=\"Benchmarks\\ActorBenchmarks.fs\" />\n    <Compile Include=\"Benchmarks\\ChannelBenchmarks.fs\" />\n    <Compile Include=\"Benchmarks\\ContainerBenchmarks.fs\" />\n    <None Include=\"Benchmarks\\ActorBenchmarks.fsx\" />\n    <None Include=\"Benchmarks\\ChannelBenchmarks.fsx\" />\n    <None Include=\"Benchmarks\\ContainerBenchmarks.fsx\" />\n    <Compile Include=\"RingBuffer.fs\" />\n    <Compile Include=\"Assertions.fs\" />\n    <Compile Include=\"Serialization.fs\" />\n    <Compile Include=\"Recording.fs\" />\n    <Compile Include=\"RegistryTests.fs\" />\n    <Compile Include=\"CoroutineTests.fs\" />\n    <Compile Include=\"SegmentTests.fs\" />\n    <Compile Include=\"ComponentTests.fs\" />\n    <Compile Include=\"ChannelTests.fs\" />\n    <Compile Include=\"IterationTests.fs\" />\n    <Compile Include=\"QueryTests.fs\" />\n    <Compile Include=\"EntityTests.fs\" />\n    <Compile Include=\"CollectionTests.fs\" />\n    <Compile Include=\"ActorTests.fs\" />\n    <Compile Include=\"SerializationTests.fs\" />\n    <Compile Include=\"StateMachineTests.fs\" />\n    <Compile Include=\"StrategySample.fs\" />\n    <Compile Include=\"ReadmeSamples.fs\" />\n    <Compile Include=\"Main.fs\" />\n    <None Include=\"Scratch.fsx\" />\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Expecto\" Version=\"9.0.2\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"16.10.0\" />\n    <PackageReference Include=\"YoloDev.Expecto.TestSdk\" Version=\"0.11.1\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "tests/Garnet.Tests/Iteration.fs",
    "content": "﻿namespace Garnet.Composition\n\nopen Garnet.Composition.Comparisons\n\nmodule Join =\n    module Array =\n        let iter1 action param mask (s1 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param s1.[i]\n                m <- m >>> 1\n                i <- i + 1\n    \n        let iter2 action param mask struct(s1 : _[], s2 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i])\n                m <- m >>> 1\n                i <- i + 1\n    \n        let iter3 action param mask struct(s1 : _[], s2 : _[], s3 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i], s3.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let iter4 action param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i], s3.[i], s4.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let iter5 action param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let iter6 action param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[], sf : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i], sf.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let iter7 action param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[], s6 : _[], s7 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then action param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i], s6.[i], s7.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let update1 map param mask (s1 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param s1.[i]\n                m <- m >>> 1\n                i <- i + 1\n    \n        let update2 map param mask struct(s1 : _[], s2 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i])\n                m <- m >>> 1\n                i <- i + 1\n    \n        let update3 map param mask struct(s1 : _[], s2 : _[], s3 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i], s3.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let update4 map param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let update5 map param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let update6 map param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[], s6 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i], s6.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let update7 map param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[], s6 : _[], s7 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then s1.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i], s6.[i], s7.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let add1 map param mask struct(sr : _[], s1 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then sr.[i] <- map param s1.[i]\n                m <- m >>> 1\n                i <- i + 1\n    \n        let add2 map param mask struct(sr : _[], s1 : _[], s2 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then sr.[i] <- map param struct(s1.[i], s2.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let add3 map param mask struct(sr : _[], s1 : _[], s2 : _[], s3 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then sr.[i] <- map param struct(s1.[i], s2.[i], s3.[i])\n                m <- m >>> 1\n                i <- i + 1\n            \n        let add4 map param mask struct(sr : _[], s1 : _[], s2 : _[], s3 : _[], s4 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then sr.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i])\n                m <- m >>> 1\n                i <- i + 1\n            \n        let add5 map param mask struct(sr : _[], s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then sr.[i] <- map param struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i])\n                m <- m >>> 1\n                i <- i + 1\n\n        let fold1 action initState param mask (s1 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            let mutable state = initState\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then state <- action param state s1.[i]\n                m <- m >>> 1\n                i <- i + 1\n            state\n\n        let fold2 action initState param mask struct(s1 : _[], s2 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            let mutable state = initState\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then state <- action param state struct(s1.[i], s2.[i])\n                m <- m >>> 1\n                i <- i + 1\n            state\n    \n        let fold3 action initState param mask struct(s1 : _[], s2 : _[], s3 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            let mutable state = initState\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then state <- action param state struct(s1.[i], s2.[i], s3.[i])\n                m <- m >>> 1\n                i <- i + 1\n            state        \n    \n        let fold4 action initState param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            let mutable state = initState\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then state <- action param state struct(s1.[i], s2.[i], s3.[i], s4.[i])\n                m <- m >>> 1\n                i <- i + 1\n            state        \n    \n        let fold5 action initState param mask struct(s1 : _[], s2 : _[], s3 : _[], s4 : _[], s5 : _[]) =\n            let mutable m = mask\n            let mutable i = 0\n            let mutable state = initState\n            while m <> 0UL do\n                if m &&& 1UL <> 0UL then state <- action param state struct(s1.[i], s2.[i], s3.[i], s4.[i], s5.[i])\n                m <- m >>> 1\n                i <- i + 1\n            state        \n\n    module Joins =\n        let count (a : Segments<_,_>) = a.Count\n        let get (a : Segments<_,_>) i = a.[i]\n        let find (a : Segments<_,_>) sid =\n            match a.TryFind(sid) with\n            | true, i -> i\n            | false, _ -> -1\n        \n        let iter1 action a param =\n            let c1 = count a\n            for i1 = 0 to c1 - 1 do\n                let s1 = get a i1\n                action param s1.Mask s1.Data\n    \n        let iter2 action struct(a, b) param =\n            let c1 = count a\n            let c2 = count b\n            let mutable i1 = 0\n            let mutable i2 = 0\n            while i1 < c1 && i2 < c2 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let n1 = s1.Id\n                let n2 = s2.Id\n                if n1 < n2 then i1 <- i1 + 1\n                elif n2 < n1 then i2 <- i2 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n    \n        let iter3 action struct(a, b, c) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                if n1 < n2 || n1 < n3 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 then i3 <- i3 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data, s3.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n\n        let iter4 action struct(a, b, c, d) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                if n1 < n2 || n1 < n3 || n1 < n4 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 then i4 <- i4 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data, s3.Data, s4.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n\n        let iter5 action struct(a, b, c, d, e) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let c5 = count e\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            let mutable i5 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 && i5 < c5 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let s5 = get e i5\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                let n5 = s5.Id\n                if n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 then i4 <- i4 + 1\n                elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 then i5 <- i5 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask &&& s5.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data, s3.Data, s4.Data, s5.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n                    i5 <- i5 + 1\n  \n        let iter6 action struct(a, b, c, d, e, f) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let c5 = count e\n            let c6 = count f\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            let mutable i5 = 0\n            let mutable i6 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 && i5 < c5 && i6 < c6 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let s5 = get e i5\n                let s6 = get f i6\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                let n5 = s5.Id\n                let n6 = s6.Id\n                if   n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 || n1 < n6 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 || n2 < n6 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 || n3 < n6 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 || n4 < n6 then i4 <- i4 + 1\n                elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 || n5 < n6 then i5 <- i5 + 1\n                elif n6 < n1 || n6 < n2 || n6 < n3 || n6 < n4 || n6 < n5 then i6 <- i6 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask &&& s5.Mask &&& s6.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data, s3.Data, s4.Data, s5.Data, s6.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n                    i5 <- i5 + 1\n                    i6 <- i6 + 1\n\n        let iter7 action struct(a, b, c, d, e, f, g) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let c5 = count e\n            let c6 = count f\n            let c7 = count g\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            let mutable i5 = 0\n            let mutable i6 = 0\n            let mutable i7 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 && i5 < c5 && i6 < c6 && i7 < c7 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let s5 = get e i5\n                let s6 = get f i6\n                let s7 = get g i7\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                let n5 = s5.Id\n                let n6 = s6.Id\n                let n7 = s7.Id\n                if   n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 || n1 < n6 || n1 < n7 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 || n2 < n6 || n2 < n7 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 || n3 < n6 || n3 < n7 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 || n4 < n6 || n4 < n7 then i4 <- i4 + 1\n                elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 || n5 < n6 || n5 < n7 then i5 <- i5 + 1\n                elif n6 < n1 || n6 < n2 || n6 < n3 || n6 < n4 || n6 < n5 || n6 < n7 then i6 <- i6 + 1\n                elif n7 < n1 || n7 < n2 || n7 < n3 || n7 < n4 || n7 < n5 || n7 < n6 then i7 <- i7 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask &&& s5.Mask &&& s6.Mask &&& s7.Mask\n                    if mask <> 0UL then action param mask struct(s1.Data, s2.Data, s3.Data, s4.Data, s5.Data, s6.Data, s7.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n                    i5 <- i5 + 1\n                    i6 <- i6 + 1\n                    i7 <- i7 + 1\n        \n        let iterKey1 action a param =\n            let ca = count a\n            for ia = 0 to ca - 1 do\n                let sa = a.[ia]\n                action param sa.Mask sa.Id sa.Data\n    \n        let iterKey2 action struct(a, b) param =\n            let ca = count a\n            let cb = count b\n            let mutable ia = 0\n            let mutable ib = 0\n            while ia < ca && ib < cb do\n                let sa = a.[ia]\n                let sb = b.[ib]\n                let na = sa.Id\n                let nb = sb.Id\n                if na < nb then ia <- ia + 1\n                elif nb < na then ib <- ib + 1\n                else\n                    let mask = sa.Mask &&& sb.Mask\n                    if mask <> 0UL then action param mask na struct(sa.Data, sb.Data)\n                    ia <- ia + 1\n                    ib <- ib + 1\n    \n        let iterKey3 action struct(a, b, c) param =\n            let ca = count a\n            let cb = count b\n            let cc = count c\n            let mutable ia = 0\n            let mutable ib = 0\n            let mutable ic = 0\n            while ia < ca && ib < cb && ic < cc do\n                let sa = a.[ia]\n                let sb = b.[ib]\n                let sc = c.[ic]\n                let na = sa.Id\n                let nb = sb.Id\n                let nc = sc.Id\n                if na < nb || na < nc then ia <- ia + 1\n                elif nb < na || nb < nc then ib <- ib + 1\n                elif nc < na || nc < nb then ic <- ic + 1\n                else\n                    let mask = sa.Mask &&& sb.Mask &&& sc.Mask\n                    if mask <> 0UL then action param mask na struct(sa.Data, sb.Data, sc.Data)\n                    ia <- ia + 1\n                    ib <- ib + 1\n                    ic <- ic + 1\n\n        let iterKey4 action struct(a, b, c, d) param =\n            let ca = count a\n            let cb = count b\n            let cc = count c\n            let cd = count d\n            let mutable ia = 0\n            let mutable ib = 0\n            let mutable ic = 0\n            let mutable id = 0\n            while ia < ca && ib < cb && ic < cc && id < cd do\n                let sa = a.[ia]\n                let sb = b.[ib]\n                let sc = c.[ic]\n                let sd = d.[id]\n                let na = sa.Id\n                let nb = sb.Id\n                let nc = sc.Id\n                let nd = sd.Id\n                if   na < nb || na < nc || na < nd then ia <- ia + 1\n                elif nb < na || nb < nc || nb < nd then ib <- ib + 1\n                elif nc < na || nc < nb || nc < nd then ic <- ic + 1\n                elif nd < na || nd < nb || nd < nc then id <- id + 1\n                else\n                    let mask = sa.Mask &&& sb.Mask &&& sc.Mask &&& sd.Mask\n                    if mask <> 0UL then action param mask na struct(sa.Data, sb.Data, sc.Data, sd.Data)\n                    ia <- ia + 1\n                    ib <- ib + 1\n                    ic <- ic + 1\n                    id <- id + 1\n\n        /// Creates components when present in A and missing from R\n        let add1 action struct(r, a) param =\n            let c1 = count a\n            for i1 = 0 to c1 - 1 do\n                let s1 = get a i1\n                let ir = find r s1.Id\n                let mask = s1.Mask\n                let sid = s1.Id\n                if ir < 0 then\n                    let data = r.Add(sid, mask)\n                    action param mask struct(data, s1.Data)\n                else\n                    let sr = r.[ir]\n                    let addMask = mask &&& ~~~sr.Mask\n                    if addMask <> 0UL then\n                        let data = r.Add(sid, addMask)\n                        action param addMask struct(data, s1.Data)\n\n        /// Creates components when present in A and B and missing from R\n        let add2 action struct(r, a, b) param =\n            let c1 = count a\n            let c2 = count b\n            let mutable i1 = 0\n            let mutable i2 = 0\n            while i1 < c1 && i2 < c2 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let n1 = s1.Id\n                let n2 = s2.Id\n                if n1 < n2 then i1 <- i1 + 1\n                elif n2 < n1 then i2 <- i2 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask\n                    if mask <> 0UL then \n                        let sid = s1.Id\n                        let ir = find r sid\n                        if ir < 0 then\n                            let data = r.Add(sid, mask)\n                            action param mask struct(data, s1.Data, s2.Data)\n                        else\n                            let sr = r.[ir]\n                            let addMask = mask &&& ~~~sr.Mask\n                            if addMask <> 0UL then\n                                let data = r.Add(sid, addMask)\n                                action param addMask struct(data, s1.Data, s2.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1            \n    \n        /// Creates components when present in A and B and C and missing from R\n        let add3 action struct(r, a, b, c) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                if n1 < n2 || n1 < n3 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 then i3 <- i3 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask\n                    if mask <> 0UL then \n                        let sid = s1.Id\n                        let ir = find r sid\n                        if ir < 0 then\n                            let data = r.Add(sid, mask)\n                            action param mask struct(data, s1.Data, s2.Data, s3.Data)\n                        else\n                            let sr = r.[ir]\n                            let addMask = mask &&& ~~~sr.Mask\n                            if addMask <> 0UL then\n                                let data = r.Add(sid, addMask)\n                                action param addMask struct(data, s1.Data, s2.Data, s3.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n\n        /// Creates components when present in A and B and C and missing from R\n        let add4 action struct(r, a, b, c, d) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                if n1 < n2 || n1 < n3 || n1 < n4 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 then i4 <- i4 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask\n                    if mask <> 0UL then \n                        let sid = s1.Id\n                        let ir = find r sid\n                        if ir < 0 then\n                            let data = r.Add(sid, mask)\n                            action param mask struct(data, s1.Data, s2.Data, s3.Data, s4.Data)\n                        else\n                            let sr = r.[ir]\n                            let addMask = mask &&& ~~~sr.Mask\n                            if addMask <> 0UL then\n                                let data = r.Add(sid, addMask)\n                                action param addMask struct(data, s1.Data, s2.Data, s3.Data, s4.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n\n        let add5 action struct(r, a, b, c, d, e) param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let c5 = count e\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable i4 = 0\n            let mutable i5 = 0\n            while i1 < c1 && i2 < c2 && i3 < c3 && i4 < c4 && i5 < c5 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d i4\n                let s5 = get e i5\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                let n5 = s5.Id\n                if n1 < n2 || n1 < n3 || n1 < n4 || n1 < n5 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 || n2 < n5 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 || n3 < n5 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 || n4 < n5 then i4 <- i4 + 1\n                elif n5 < n1 || n5 < n2 || n5 < n3 || n5 < n4 then i5 <- i5 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask &&& s5.Mask\n                    if mask <> 0UL then \n                        let sid = s1.Id\n                        let ir = find r sid\n                        if ir < 0 then\n                            let data = r.Add(sid, mask)\n                            action param mask struct(data, s1.Data, s2.Data, s3.Data, s4.Data, s5.Data)\n                        else\n                            let sr = r.[ir]\n                            let addMask = mask &&& ~~~sr.Mask\n                            if addMask <> 0UL then\n                                let data = r.Add(sid, addMask)\n                                action param addMask struct(data, s1.Data, s2.Data, s3.Data, s4.Data, s5.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    i4 <- i4 + 1\n                    i5 <- i5 + 1\n\n        let fold1 action a initState param =\n            let mutable state = initState\n            let c1 = count a\n            for i1 = 0 to c1 - 1 do\n                let s1 = a.[i1]\n                state <- action state param s1.Mask s1.Data\n            state\n            \n        let fold2 action struct(a, b) initState param =\n            let c1 = count a\n            let c2 = count b\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable state = initState\n            while i1 < c1 && i2 < c2 do\n                let s1 = a.[i1]\n                let s2 = b.[i2]\n                let n1 = s1.Id\n                let n2 = s2.Id\n                if n1 < n2 then i1 <- i1 + 1\n                elif n2 < n1 then i2 <- i2 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask\n                    if mask <> 0UL then state <- action state param mask struct(s1.Data, s2.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n            state    \n\n        let fold3 action struct(a, b, c) initState param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable state = initState\n            while i1 < c1 && i2 < c2 && i3 < c3 do\n                let s1 = a.[i1]\n                let s2 = b.[i2]\n                let s3 = c.[i3]\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                if n1 < n2 || n1 < n3 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 then i3 <- i3 + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask\n                    if mask <> 0UL then state <- action state param mask struct(s1.Data, s2.Data, s3.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n            state\n\n        let fold4 action struct(a, b, c, d) initState param =\n            let c1 = count a\n            let c2 = count b\n            let c3 = count c\n            let c4 = count d\n            let mutable i1 = 0\n            let mutable i2 = 0\n            let mutable i3 = 0\n            let mutable id = 0\n            let mutable state = initState\n            while i1 < c1 && i2 < c2 && i3 < c3 && id < c4 do\n                let s1 = get a i1\n                let s2 = get b i2\n                let s3 = get c i3\n                let s4 = get d id\n                let n1 = s1.Id\n                let n2 = s2.Id\n                let n3 = s3.Id\n                let n4 = s4.Id\n                if n1 < n2 || n1 < n3 || n1 < n4 then i1 <- i1 + 1\n                elif n2 < n1 || n2 < n3 || n2 < n4 then i2 <- i2 + 1\n                elif n3 < n1 || n3 < n2 || n3 < n4 then i3 <- i3 + 1\n                elif n4 < n1 || n4 < n2 || n4 < n3 then id <- id + 1\n                else\n                    let mask = s1.Mask &&& s2.Mask &&& s3.Mask &&& s4.Mask\n                    if mask <> 0UL then state <- action state param mask struct(s1.Data, s2.Data, s3.Data, s4.Data)\n                    i1 <- i1 + 1\n                    i2 <- i2 + 1\n                    i3 <- i3 + 1\n                    id <- id + 1\n            state\n\n    module Segments =\n        let get (c : ISegmentStore<_>) = c.GetSegments()\n        let get2 c = struct(get c, get c)\n        let get3 c = struct(get c, get c, get c)\n        let get4 c = struct(get c, get c, get c, get c)\n        let get5 c = struct(get c, get c, get c, get c, get c)\n        let get6 c = struct(get c, get c, get c, get c, get c, get c)\n        let get7 c = struct(get c, get c, get c, get c, get c, get c, get c)\n        \n        let iter1 a c = get c |> Joins.iter1 a    \n        let iter2 a c = get2 c |> Joins.iter2 a\n        let iter3 a c = get3 c |> Joins.iter3 a    \n        let iter4 a c = get4 c |> Joins.iter4 a\n        let iter5 a c = get5 c |> Joins.iter5 a\n        let iter6 a c = get6 c |> Joins.iter6 a\n        let iter7 a c = get7 c |> Joins.iter7 a\n\n        let iterKey1 a c = get c |> Joins.iterKey1 a    \n        let iterKey2 a c = get2 c |> Joins.iterKey2 a\n        let iterKey3 a c = get3 c |> Joins.iterKey3 a    \n        let iterKey4 a c = get4 c |> Joins.iterKey4 a    \n\n        let add1 a c = get2 c |> Joins.add1 a\n        let add2 a c = get3 c |> Joins.add2 a\n        let add3 a c = get4 c |> Joins.add3 a\n        let add4 a c = get5 c |> Joins.add4 a\n        let add5 a c = get6 c |> Joins.add5 a\n\n        let fold1 a c = get c |> Joins.fold1 a\n        let fold2 a c = get2 c |> Joins.fold2 a\n        let fold3 a c = get3 c |> Joins.fold3 a\n        let fold4 a c = get4 c |> Joins.fold4 a\n\n    let iter1 a = a |> Array.iter1 |> Segments.iter1\n    let iter2 a = a |> Array.iter2 |> Segments.iter2\n    let iter3 a = a |> Array.iter3 |> Segments.iter3\n    let iter4 a = a |> Array.iter4 |> Segments.iter4\n    let iter5 a = a |> Array.iter5 |> Segments.iter5\n    let iter6 a = a |> Array.iter6 |> Segments.iter6\n    let iter7 a = a |> Array.iter7 |> Segments.iter7\n\n    let add1 a = a |> Array.add1 |> Segments.add1\n    let add2 a = a |> Array.add2 |> Segments.add2        \n    let add3 a = a |> Array.add3 |> Segments.add3\n    let add4 a = a |> Array.add4 |> Segments.add4\n    let add5 a = a |> Array.add5 |> Segments.add5\n\n    let update1 a = a |> Array.update1 |> Segments.iter1\n    let update2 a = a |> Array.update2 |> Segments.iter2\n    let update3 a = a |> Array.update3 |> Segments.iter3\n    let update4 a = a |> Array.update4 |> Segments.iter4\n    let update5 a = a |> Array.update5 |> Segments.iter5\n    let update6 a = a |> Array.update6 |> Segments.iter6\n    let update7 a = a |> Array.update7 |> Segments.iter7\n\n    let fold1 a = a |> Array.fold1 |> Segments.fold1\n    let fold2 a = a |> Array.fold2 |> Segments.fold2\n    let fold3 a = a |> Array.fold3 |> Segments.fold3\n    let fold4 a = a |> Array.fold4 |> Segments.fold4\n\n    let list procs = fun c -> procs |> List.iter (fun proc -> proc c)\n\n    /// Takes a single event processor and returns a processor which operates on a batch of events\n    let batch proc =\n        fun c -> \n            let run = proc c\n            fun (events : List<_>) ->\n                for e in events do\n                    run e\n\n    let where pred action =\n        fun param values ->\n            if pred param values then\n                action param values\n\n    let over c proc =\n        proc c\n\n    let run param iter = iter param\n    "
  },
  {
    "path": "tests/Garnet.Tests/IterationTests.fs",
    "content": "﻿module Garnet.Tests.Iteration\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    testList \"iteration\" [\n        testCase \"iter2\" <| fun () ->\n            let c = ComponentStore<_,_,EidSegmentKeyMapper>()\n            for i = 1 to 100 do\n                let e = c.Get(Eid i)\n                if i % 2 = 0 then e.Add(i)\n                if i % 3 = 0 then e.Add('a' + char i)\n                if i % 5 = 0 then e.Add(i.ToString())\n            c.Commit()\n            let r = List<_>()\n            let iter =\n                fun param struct(a : int, b : string) ->\n                    r.Add((param, a, b))\n                |> Join.iter2\n                |> Join.over c\n            iter 0\n            r.Count |> shouldEqual 10\n            let r = List<_>()\n            let iter =\n                fun param struct(a : int, b : char) ->\n                    r.Add((param, a, b))\n                |> Join.iter2\n                |> Join.over c\n            iter 0\n            r.Count |> shouldEqual 16\n            \n        testCase \"where\" <| fun () ->\n            let c = ComponentStore<_,_,EidSegmentKeyMapper>()\n            for i = 1 to 100 do\n                let e = c.Get(Eid i)\n                e.Add(i)\n                e.Add(Eid i)\n            c.Commit()\n            let r = List<_>()\n            let iterWhere =\n                fun param struct(a : int, b : Eid) ->\n                    r.Add((param, a, b))\n                |> Join.where (fun param struct(a : int, _ : Eid) -> a % 2 = 0)\n                |> Join.iter2\n                |> Join.over c\n            iterWhere 0\n            r.Count |> shouldEqual 50            \n\n        testCase \"add4\" <| fun () ->\n            let c = Container()\n            let mutable count = 0\n            let add4 =\n                fun () struct(a : string, b : int, c : char, d : byte) -> \n                    count <- count + 1\n                    1us\n                |> Join.add4\n                |> Join.over c\n            for i = 1 to 10 do\n                c.Create().With(\"\").With(1).With('a').Add(1uy)\n            for i = 1 to 10 do\n                c.Create().With(\"\").With(1).Add('a')\n            c.Commit()\n            add4()\n            count |> shouldEqual 10\n            c.Commit()\n            add4()\n            count |> shouldEqual 10\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/Main.fs",
    "content": "﻿module Garnet.Tests.Program\n\nopen Expecto\n\n[<EntryPoint>]\nlet main argv =\n    Tests.runTestsInAssembly { defaultConfig with verbosity = Logging.LogLevel.Verbose } argv\n"
  },
  {
    "path": "tests/Garnet.Tests/QueryTests.fs",
    "content": "﻿module Garnet.Tests.Query\n\nopen System\nopen System.Collections.Generic\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    let testMaskIteration mask =\n        let mutable r = 0UL\n        let mutable last = -1\n        for i in MaskEnumerable(mask) do\n            if i <= last || i < 0 || i >= 64 then failwith $\"Invalid index {i}\"\n            r <- r ||| (1UL <<< i)\n            last <- i\n        r |> shouldEqual mask\n    \n    testList \"query\" [\n        testCase \"iterate over mask\" <| fun () ->\n            testMaskIteration 0UL\n            testMaskIteration UInt64.MaxValue\n            testMaskIteration 0x10UL\n            let rand = Random(1)\n            for i = 1 to 1000 do\n                let x = \n                    (uint64 (rand.Next()) <<< 32) |||\n                    (uint64 (rand.Next()))\n                testMaskIteration x\n        \n        testCase \"iter2\" <| fun () ->\n            let c = ComponentStore<_,_,EidSegmentKeyMapper>()\n            for i = 1 to 100 do\n                let e = c.Get(Eid i)\n                if i % 2 = 0 then e.Add(i)\n                if i % 3 = 0 then e.Add('a' + char i)\n                if i % 5 = 0 then e.Add(i.ToString())\n            c.Commit()\n            let r = List<_>()\n            for row in c.Query<int, string>() do\n                r.Add(row.Values)\n            r.Count |> shouldEqual 10\n            let r = List<_>()\n            for row in c.Query<int, char>() do\n                r.Add(row.Values)\n            r.Count |> shouldEqual 16\n\n        testCase \"update2\" <| fun () ->\n            let c = Container()\n            let e = c.Create().With(1L).With(5)\n            c.Commit()\n            for r in c.Query<int64, int>() do\n                let p = &r.Value1\n                let v = r.Value2\n                p <- p + int64 v\n            e.Get<int64>() |> shouldEqual 6L \n    ]"
  },
  {
    "path": "tests/Garnet.Tests/ReadmeSamples.fs",
    "content": "module Garnet.Tests.Examples\n\nopen Garnet.Composition\n\n// Common\n\ntype Msg = struct end\ntype UpdatePositions = struct end\ntype DestroyZeroHealth = struct end\ntype EnemyMarker = struct end\n\n[<Struct>] type Position = { x : float32; y : float32 }\n[<Struct>] type Velocity = { vx : float32; vy : float32 }\n\n// create a world\nlet world = Container()\n\nmodule Example1a =\n    // events\n    [<Struct>] type Update = { dt : float32 }\n\n    // register a system that updates position\n    let system =\n        world.On<Update> (\n            fun e struct(p : Position, v : Velocity) -> {\n                x = p.x + v.vx * e.dt\n                y = p.y + v.vy * e.dt\n                }\n            |> Join.update2\n            |> Join.over world)\n\nmodule Example1b =\n    // events\n    [<Struct>] type Update = { dt : float32 }\n\n    // register a system that updates position\n    let system =\n        world.On<Update> <| fun e ->\n            for r in world.Query<Position, Velocity>() do\n                let p = &r.Value1\n                let v = r.Value2\n                p <- {\n                    x = p.x + v.vx * e.dt\n                    y = p.y + v.vy * e.dt\n                    }\n\n[<Struct>]\ntype Health = {\n    hp : int\n}\n\nmodule HashSpaceSystem =     \n    let register (c : Container) =\n        Disposable.Create [\n            ]\n\nlet c = Container()\n\n// Subscribing to events\n\n[<Struct>] type UpdateTime = { dt : float32 }\n\n// sub is IDisposable, which can be used to unsubscribe\nlet sub =\n    c.On<UpdateTime> <| fun e ->\n        // [do update here]\n        printfn \"%A\" e\n\n// Defining systems\n\n// a system is just a group of related subscriptions,\n// optionally with a name\nmodule MovementSystem =     \n    // separate methods as needed\n    let registerUpdate (c : Container) =\n        c.On<UpdatePositions> <| fun e ->\n            printfn \"%A\" e\n\n    // combine all together\n    let register (c : Container) =\n        Disposable.Create [\n            registerUpdate c\n            ]\n\n[<AutoOpen>]\nmodule MovementSystemExtensions =\n    type Container with\n        member c.AddMovementUpdate() =\n            c.On<UpdatePositions> <| fun e ->\n                printfn \"%A\" e\n                \n        member c.AddMovementSystems() =  \n            Disposable.Create [\n                c.AddMovementUpdate()\n                ]\n            \n// Iterating over entities\n\nlet runIter =\n    // first define an iteration callback:\n    // (1) param can be any type or just ignored\n    // (2) use struct record for component types\n    fun param struct(eid : Eid, p : Position, h : Health) ->\n        if h.hp <= 0 then \n            // [start animation at position]\n            // destroy entity\n            c.Destroy(eid)\n    // iterate over all entities with all components\n    // present (inner join)\n    |> Join.iter3\n    // iterate over container\n    |> Join.over c\nlet healthSub =\n    c.On<DestroyZeroHealth> <| fun e ->\n        runIter()\n\nlet healthSubQuery =\n    c.On<DestroyZeroHealth> <| fun e ->\n        for r in c.Query<Eid, Position, Health>() do\n            let h = r.Value3\n            if h.hp <= 0 then\n                let eid = r.Value1\n                c.Destroy(eid)\n\nlet healthSubBatchQuery =\n    c.On<DestroyZeroHealth> <| fun e ->\n        for seg, eids, _, hs in c.QuerySegments<Eid, Position, Health>() do\n            for i in seg do\n                let h = hs.[i]\n                if h.hp <= 0 then\n                    let eid = eids.[i]\n                    c.Destroy(eid)\n\n// Composing systems\n\nmodule CoreSystems =        \n    let register (c : Container) =\n        Disposable.Create [\n            MovementSystem.register c\n            HashSpaceSystem.register c\n        ]\n\n// Running stack-like coroutines\n\nlet system =\n    c.On<Msg> <| fun e ->\n        printf \"2 \"\n\n// start a coroutine\nc.Start <| seq {\n    printf \"1 \"\n    // send message and defer execution until all messages and\n    // coroutines created as a result of this have completed\n    c.Send <| Msg()\n    yield Wait.All\n    printf \"3 \"\n    }\n\n// run until completion\n// output: 1 2 3\nc.Run()\n\n// Updating in stages\n\n// events\ntype Update = struct end\ntype UpdatePhysicsBodies = struct end\ntype UpdateHashSpace = struct end\n\n// systems\nlet updateSystem =\n    c.On<Update> <| fun e -> \n        c.Start <| seq {\n            // using shorthand 'send and defer' to suspend\n            // execution here to achieve ordering of \n            // sub-updates\n            yield c.Wait <| UpdatePhysicsBodies()\n            yield c.Wait <| UpdateHashSpace()\n        }\nlet system1 = \n    c.On<UpdatePhysicsBodies> <| fun e ->\n        // [update positions]\n        printfn \"%A\" e\nlet system2 = \n    c.On<UpdateHashSpace> <| fun e ->\n        // [update hash space from positions]\n        printfn \"%A\" e\n\n// Running time-based coroutines\n\n// start a coroutine\nc.Start <| seq {\n    for i = 1 to 5 do\n        printf \"[%d] \" i\n        // yield execution until time units pass\n        yield Wait 3L\n    }\n\n// simulate update loop\n// output: [1] 1 2 3 [2] 4 5 6 [3] 7 8 9\nfor i = 1 to 9 do\n    // increment time units and run pending coroutines\n    c.Step 1L\n    c.Run()\n    printf \"%d \" i\n\n// Creating actors\n\n// message types\ntype Ping = struct end\ntype Pong = struct end\n\n// actor definitions\nlet a = new ActorSystem()\na.Register(ActorId 1, fun (c : Container) ->\n    c.On<Ping> <| fun e -> \n        printf \"ping \"\n        c.Respond(Pong())\n    )\na.Register(ActorId 2, fun (c : Container) ->\n    c.On<Pong> <| fun e -> \n        printf \"pong \"\n    )\n    \n// send a message and run until all complete\n// output: ping pong\na.Send(ActorId 1, ActorId 2, Ping())\na.ProcessAll()\n\n// Subscribing to event batch\n\n[<Struct>] type AddShip = { x : float32; y : float32 }\n\nlet batchSub =\n    c.OnAll<AddShip> <| fun list ->\n        for e in list.Span do\n            // [do update here]\n            printfn \"%A\" e\n\n// Creating an entity\n\nlet entity = \n    c.Create()\n        .With({ x = 10.0f; y = 5.0f })\n        .With({ vx = 1.0f; vy = 2.0f })\n        \n// Defining a container actor\nlet test() =\n    use a = new ActorSystem()\n    a.Register(ActorId 1, fun (c : Container) ->\n        c.On<Ping> <| fun e ->\n            c.Respond(Pong()))\n    a.Register(ActorId 2, fun (c : Container) ->\n        c.On<Ping> <| fun e ->\n            c.Respond(Pong()))\n    a.Send(ActorId 1, Ping())\n    a.ProcessAll()"
  },
  {
    "path": "tests/Garnet.Tests/Recording.fs",
    "content": "namespace Garnet.Streaming\n\nopen System\nopen System.IO\nopen System.Collections.Generic\nopen System.Text\nopen Garnet.Composition.Comparisons\nopen Garnet.Composition\n\ntype ActorLogFilter = {\n    actorFilter : ActorId -> bool\n    sourceFilter : ActorId -> bool\n    destinationFilter : ActorId -> bool\n    }\n\nmodule ActorLogFilter =\n    let isAny (_ : ActorId) = true\n\n    let all = {\n        actorFilter = isAny\n        sourceFilter = isAny\n        destinationFilter = isAny\n        }\n\n    let singleLog actorId = {\n        actorFilter = (=)actorId\n        sourceFilter = isAny\n        destinationFilter = isAny\n        }\n\n    let toActor actorId = {\n        actorFilter = (=)actorId\n        sourceFilter = isAny\n        destinationFilter = isAny\n        }\n\n    let receivedBy actorId = {\n        actorFilter = (=)actorId\n        sourceFilter = isAny\n        destinationFilter = (=)actorId\n        }\n\n    let sent actorId = {\n        actorFilter = (=)actorId\n        sourceFilter = isAny\n        destinationFilter = (<>)actorId\n        }\n\n    let sentTo actorId destinationId = {\n        actorFilter = (=)actorId\n        sourceFilter = isAny\n        destinationFilter = (=)destinationId\n        }\n\n[<Struct>]\ntype MessageRange = {\n    messageTypeId : int\n    start : int\n    count : int \n    }\n        \nmodule MessageRange =\n    let init id start count = { \n        messageTypeId = id\n        start = start\n        count = count }\n\n    let typeCount id count = init id 0 count\n    let count count = typeCount 0 count\n    let all = count Int32.MaxValue\n\ntype PrintOptions = {\n    createMessageRegistry : unit -> MessageRegistry\n    createFormatter : unit -> Formatter\n    print : string -> unit\n    filter : ActorLogFilter\n    range : MessageRange\n    }\n\nmodule PrintOptions =\n    let range r opt = { opt with range = r }\n    let filtered filter opt = { opt with filter = filter }\n    let count c opt = range (MessageRange.count c) opt\n\ntype ReplayOptions = {\n    createMessageRegistry : unit -> MessageRegistry\n    filter : ActorLogFilter\n    range : MessageRange\n    }\n\ntype IActorStreamReader =\n    abstract member GetActorIds : unit -> ActorId seq\n    abstract member OpenRead : ActorId -> Stream\n\ntype IActorStreamWriter =\n    abstract member OpenWrite : ActorId -> Stream\n\ntype IActorStreamSource =\n    inherit IActorStreamReader\n    inherit IActorStreamWriter\n\n/// Multiple files in a directory\n/// Stream lookup is thread-safe, but reading/writing is not\ntype DirectoryActorStreamSource(path) =\n    let prefix = \"actor-\"\n    let extension = \".log\"\n    let sync = obj()\n    let getFullPath (actorId : ActorId) =\n        let file = sprintf \"%s%d%s\" prefix actorId.Value extension\n        Path.Combine(path, file)\n    interface IActorStreamSource with\n        member c.GetActorIds() =\n            lock sync <| fun () ->\n                Directory.EnumerateFiles(path, prefix + \"*\" + extension)\n                |> Seq.map (fun file -> \n                    // remove prefix and extension, then extract number \n                    let name = Path.GetFileNameWithoutExtension(file)\n                    Int32.Parse(name.Replace(prefix, \"\")) |> ActorId)\n                |> Seq.cache\n        member c.OpenRead (id : ActorId) =\n            lock sync <| fun () ->\n                let fullPath = getFullPath id\n                File.OpenRead(fullPath) :> Stream\n        member c.OpenWrite (id : ActorId) =\n            lock sync <| fun () ->\n                let fullPath = getFullPath id\n                Directory.CreateDirectory path |> ignore\n                File.OpenWrite(fullPath) :> Stream\n        \n/// Single file/stream\ntype FileActorStreamSource(path) =\n    let actorIds = [| ActorId.Undefined |] :> seq<_>\n    interface IActorStreamSource with\n        member c.GetActorIds() = actorIds\n        member c.OpenRead (id : ActorId) =\n            File.OpenRead(path) :> Stream\n        member c.OpenWrite (id : ActorId) =\n            File.OpenWrite(path) :> Stream\n\n/// Note Dispose() is absent\ntype private NonDisposingStream(stream : Stream) =\n    inherit Stream()\n    override c.Position\n        with get() = stream.Position\n        and set value = stream.Position <- value\n    override c.CanRead = stream.CanRead\n    override c.CanWrite = stream.CanWrite\n    override c.CanSeek = stream.CanSeek\n    override c.Length = stream.Length\n    override c.Write(input, offset, count) =\n        stream.Write(input, offset, count)\n    override c.Read(output, offset, count) =\n        stream.Read(output, offset, count)\n    override c.Flush() = stream.Flush()\n    override c.Seek(offset, origin) =\n        stream.Seek(offset, origin)\n    override c.SetLength(length) =\n        stream.SetLength(length)\n\n/// Stream lookup is thread-safe, but reading/writing is not\ntype MemoryActorStreamSource() =\n    let logs = Dictionary<_,_>()\n    let sync = obj()\n    member private c.Open (id : ActorId) =\n        lock sync <| fun () ->\n            match logs.TryGetValue id with\n            | true, x -> x\n            | false, _ ->\n                let ms = new MemoryStream()\n                logs.Add(id, ms)\n                ms\n    member c.OpenWrite (id : ActorId) =\n        new NonDisposingStream(c.Open id) :> Stream\n    member c.GetActorIds() =\n        lock sync <| fun () ->\n            logs.Keys |> Seq.cache\n    member c.OpenRead (id : ActorId) =\n        let ms = c.Open id\n        let length = int ms.Length\n        let buffer = ms.GetBuffer()\n        new MemoryStream(buffer, 0, length, false) :> Stream \n    interface IActorStreamSource with\n        member c.OpenWrite id =\n            c.OpenWrite id\n        member c.GetActorIds() = \n            c.GetActorIds()\n        member c.OpenRead id =\n            c.OpenRead id\n    override c.ToString() =\n        String.Join(\"\\n\",\n            logs |> Seq.map (fun kvp ->\n                sprintf \"%d: %d\" kvp.Key.Value kvp.Value.Length))\n\n[<Struct>]\ntype ActorLogCommand = {\n    enableActorLog : bool \n    }\n    "
  },
  {
    "path": "tests/Garnet.Tests/RegistryTests.fs",
    "content": "﻿module Garnet.Tests.Registry\n\nopen Expecto\nopen Garnet.Composition\n\n[<AllowNullLiteral>]\ntype TestClass() = class end\n\n[<Tests>]\nlet tests =\n    testList \"registry\" [\n        testCase \"resolve default\" <| fun () ->\n            let r = Registry()\n            r.Get<TestClass>() |> isNull |> shouldEqual false\n\n        testCase \"resolve instance\" <| fun () ->\n            let r = Registry()\n            r.Set(\"a\")\n            r.Get<string>() |> shouldEqual \"a\"\n\n        testCase \"resolve ref\" <| fun () ->\n            let r = Registry()\n            r.SetFactory<int ref>(fun () -> ref 10)\n            r.Get<int ref>().Value |> shouldEqual 10\n\n        testCase \"resolve circular\" <| fun () ->\n            let r = Registry()\n            r.SetFactory<int ref>(fun () -> ref (r.Get<string>().Length))\n            r.SetFactory<string>(fun () -> r.Get<int ref>().ToString())\n            Expect.throws(fun () -> r.Get<int ref>() |> ignore) \"\"\n\n        testCase \"instance overrides factory\" <| fun () ->\n            let r = Registry()\n            r.Set(\"a\")\n            r.SetFactory(fun () -> \"b\")\n            r.Get<string>() |> shouldEqual \"a\"\n\n        testCase \"copy registry\" <| fun () ->\n            let r = Registry()\n            r.Set(ref 10)\n            r.Set(\"a\")\n            let r2 = Registry()\n            r.CopyTo(r2)\n            r2.Get<int ref>().Value |> shouldEqual 10\n            r2.Get<string>() |> shouldEqual \"a\"\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/RingBuffer.fs",
    "content": "﻿namespace Garnet.Collections\n\nopen System\nopen System.Threading\nopen Garnet.Composition.Comparisons\n\nmodule RingBuffer =\n    let defaultBufferSize = 32\n\n/// Single producer, single consumer\ntype RingBuffer<'a>(size) =\n    let buffer = Array.zeroCreate<'a> (size)\n    [<VolatileField>]\n    let mutable readPos = 0\n    [<VolatileField>]\n    let mutable writePos = 0\n    new() = new RingBuffer<'a>(RingBuffer.defaultBufferSize)\n    /// Assumes single thread access\n    member c.TryEnqueue(item) =\n        if writePos - readPos = buffer.Length then false\n        else\n            let index = writePos &&& (buffer.Length - 1)\n            buffer.[index] <- item\n            writePos <- writePos + 1\n            true\n    /// Assumes single thread access\n    member c.TryDequeue(item : byref<'a>) =\n        if readPos = writePos then false\n        else\n            let index = readPos &&& (buffer.Length - 1)\n            item <- buffer.[index]\n            readPos <- readPos + 1\n            true\n\n/// Single producer, single consumer\n[<AllowNullLiteral>]\ntype RingBufferNode<'a>(size) =\n    let buffer = RingBuffer(size)\n    [<VolatileField>]\n    let mutable next = null\n    new() = new RingBufferNode<'a>(RingBuffer.defaultBufferSize)\n    member c.Enqueue(item) =\n        // first try to add to current\n        if buffer.TryEnqueue(item) then c\n        else\n            // if full, create a new node\n            // next will only ever be set to non-null\n            // need to guarantee this value will be written AFTER ring buffer\n            // increment, otherwise consumer could miss an item\n            next <- RingBufferNode(size * 2)\n            next.Enqueue(item)\n    /// Returns the node item was obtained from, or null if no item available\n    member c.TryDequeue(item : byref<'a>) =\n        // first look in current\n        if buffer.TryDequeue(&item) then c\n        // if empty, then either no items or writer moved onto another buffer\n        // if another buffer, we can safely discard current buffer\n        elif isNotNull next then next.TryDequeue(&item)\n        else null\n    member c.NodeCount = \n        if isNull next then 1 else 1 + next.NodeCount\n            \n/// Single producer, single consumer\ntype RingBufferQueue<'a>(initialSize) =\n    let mutable count = 0\n    let mutable enqueueCount = 0\n    let mutable dequeueCount = 0\n    let mutable allocatedCount = 1\n    [<VolatileField>]\n    let mutable readNode = RingBufferNode<'a>(initialSize)\n    [<VolatileField>]\n    let mutable writeNode = readNode\n    new() = new RingBufferQueue<'a>(RingBuffer.defaultBufferSize)\n    member c.Count = count\n    member c.Enqueue(item) =\n        writeNode <- writeNode.Enqueue(item)\n        Interlocked.Increment(&count) |> ignore\n        enqueueCount <- enqueueCount + 1\n    member c.TryDequeue(item : byref<'a>) =\n        let newReadNode = readNode.TryDequeue(&item)\n        let isDequeued = isNotNull newReadNode\n        if isDequeued then \n            if not (obj.ReferenceEquals(readNode, newReadNode)) then\n                readNode <- newReadNode\n                allocatedCount <- allocatedCount + 1\n            Interlocked.Decrement(&count) |> ignore\n            dequeueCount <- dequeueCount + 1\n        isDequeued\n    member c.DequeueAll(action : Action<_>) =\n        let mutable item = Unchecked.defaultof<'a>\n        while c.TryDequeue(&item) do\n            action.Invoke item\n    override c.ToString() =\n        sprintf \"%d items, %d/%d nodes, %d enqueued, %d dequeued\" \n            count readNode.NodeCount allocatedCount enqueueCount dequeueCount\n                \ntype RingBufferPool<'a>(create) =\n    let pool = RingBufferQueue<'a>()\n    let onDispose = Action<_>(pool.Enqueue)\n    member c.Get() =\n        let mutable item = Unchecked.defaultof<'a>\n        if pool.TryDequeue(&item) then item\n        else create onDispose\n    override c.ToString() =\n        pool.ToString()\n"
  },
  {
    "path": "tests/Garnet.Tests/Scratch.fsx",
    "content": "#r \"netstandard\"\n#r \"bin/Release/netcoreapp2.1/Garnet.dll\"\n"
  },
  {
    "path": "tests/Garnet.Tests/SegmentTests.fs",
    "content": "module Garnet.Tests.Segments\n\nopen System\nopen Expecto\nopen Garnet.Composition\n\n[<Tests>]\nlet tests =\n    let copyArrayMask mask (src : _[]) (dest : _[]) =\n        let mutable m = mask\n        let mutable i = 0\n        while m <> 0UL do\n            if m &&& 1UL <> 0UL then dest.[i] <- src.[i]\n            m <- m >>> 1\n            i <- i + 1\n\n    testList \"segments\" [\n        testCase \"add\" <| fun () ->\n            let s = Segments()\n            s.Add(1, 0b111000UL).[5] <- 10\n            s.Count |> shouldEqual 0\n            s.Commit()\n            s.Count |> shouldEqual 1\n            s.[0].Id |> shouldEqual 1\n            s.[0].Mask |> shouldEqual 0b111000UL\n            s.[0].Data.[5] |> shouldEqual 10\n\n        testCase \"remove\" <| fun () ->\n            let s = Segments()\n            s.Add(1, 0b111000UL).[4] <- 10\n            s.Commit()\n            s.Remove(1, 0b101000UL)\n            s.[0].Mask |> shouldEqual 0b111000UL\n            s.Commit()\n            s.Count |> shouldEqual 1\n            s.[0].Mask |> shouldEqual 0b010000UL\n            s.[0].Data.[4] |> shouldEqual 10\n\n        testCase \"clear immediately\" <| fun () ->\n            let s = Segments<int, int>()\n            s.Add(1, UInt64.MaxValue) |> ignore\n            s.Commit()\n            s.Count |> shouldEqual 1\n            s.Clear()\n            s.Count |> shouldEqual 0\n\n        testCase \"data cleared before added back to pool\" <| fun () ->\n            let s = Segments<int, int>()\n            let data = s.Add(1, UInt64.MaxValue)\n            Array.fill data 0 data.Length 1\n            s.Commit()\n            let data = s.Add(2, 0UL)\n            data.[0] |> shouldEqual 0\n\n        testCase \"copy masked data\" <| fun () ->\n            let a = Array.init 64 id\n            let b = Array.zeroCreate<int> 64\n            copyArrayMask 0x00ff00ff00ffUL a b\n            b.[1] |> shouldEqual 1\n            b.[8] |> shouldEqual 0\n            b.[16] |> shouldEqual 16\n\n        testCase \"copy segments\" <| fun () ->\n            let s = SegmentStore<int>()\n            s.GetSegments<int>().Add(1, UInt64.MaxValue) |> ignore\n            s.Commit()\n            s.GetSegments<int>().Add(2, UInt64.MaxValue) |> ignore\n            let s2 = SegmentStore<int>()\n            s.CopyTo(s2)\n            s2.GetSegments<int>().Count |> shouldEqual 0\n            s2.Commit()\n            // note pending not copied\n            s2.GetSegments<int>().Count |> shouldEqual 1\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/Serialization.fs",
    "content": "﻿namespace Garnet.Streaming\n\nopen System\nopen System.IO\nopen System.Collections.Generic\nopen System.Runtime.InteropServices\nopen Garnet.Composition\n\ntype ISerializer<'a> =\n    abstract member Write : Stream -> 'a -> unit\n    abstract member Read : Stream -> 'a\n\ntype Serializer<'a>(read, write) =\n    interface ISerializer<'a> with\n        member c.Write output value = write value output\n        member c.Read input = read input\n\n[<AutoOpen>]\nmodule internal Serialization =\n    let getNextPow2 x =\n        let mutable y = x - 1\n        y <- y ||| (y >>> 1)\n        y <- y ||| (y >>> 2)\n        y <- y ||| (y >>> 4)\n        y <- y ||| (y >>> 8)\n        y <- y ||| (y >>> 16)\n        y <- y + 1\n        if y > 0 then y else 1\n\n    let checkBlittable<'a>() =\n        try\n            let x = Array.zeroCreate<'a> 1\n            let handle = GCHandle.Alloc(x, GCHandleType.Pinned)\n            handle.Free()\n            true\n        with _ ->\n            false\n\n    let copyFromBytes<'a> (src : byte[]) (dest : 'a[]) =\n        let handle = GCHandle.Alloc(dest, GCHandleType.Pinned)\n        let ptr = handle.AddrOfPinnedObject()\n        Marshal.Copy(src, 0, ptr, src.Length)\n        handle.Free()\n\n    let copyToBytes<'a> (src : 'a[]) (dest : byte[]) =\n        let handle = GCHandle.Alloc(src, GCHandleType.Pinned)\n        let ptr = handle.AddrOfPinnedObject()\n        Marshal.Copy(ptr, dest, 0, dest.Length)\n        handle.Free()\n\n    let serializer read write = Serializer(read, write)\n    \n    let isEmptyType (t : Type) =\n        not (t.IsPrimitive || t.IsEnum || t.GetProperties().Length > 0)\n\nmodule internal LittleEndian =\n    let writeInt32 (bytes : byte[]) offset x =\n        bytes.[offset] <- x &&& 0xff |> byte\n        bytes.[offset + 1] <- (x >>> 8) &&& 0xff |> byte\n        bytes.[offset + 2] <- (x >>> 16) &&& 0xff |> byte\n        bytes.[offset + 3] <- (x >>> 24) &&& 0xff |> byte\n        \ntype StringSerializer() =\n    let mutable buffer = Array.zeroCreate<byte> 4\n    let resize length =\n        if buffer.Length < length then\n            Array.Resize(&buffer, getNextPow2 length)\n    interface ISerializer<string> with\n        member c.Read stream =\n            stream.Read(buffer, 0, 4) |> ignore\n            let length = BitConverter.ToInt32(buffer, 0)\n            resize length\n            stream.Read(buffer, 0, length) |> ignore\n            System.Text.Encoding.UTF8.GetString(buffer, 0, length)\n        member c.Write stream value =\n            LittleEndian.writeInt32 buffer 0 value.Length\n            stream.Write(buffer, 0, 4)\n            let length = System.Text.Encoding.UTF8.GetByteCount(value)\n            resize length\n            System.Text.Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, 0) |> ignore\n            stream.Write(buffer, 0, length)\n\ntype RawSerializer<'a>() =\n    let bytes = Array.zeroCreate<byte> sizeof<'a>\n    let data = Array.zeroCreate<'a> 1\n    // for types without members, avoid writing any bytes\n    let storedLength = if isEmptyType typeof<'a> then 0 else bytes.Length\n    do\n        if not (checkBlittable<'a>()) then\n            failwithf \"Type %s is not blittable\" (typeof<'a>.Name)\n    interface ISerializer<'a> with\n        member c.Write (output : Stream) (value : 'a) =\n            data.[0] <- value\n            copyToBytes data bytes\n            output.Write(bytes, 0, storedLength)\n        member c.Read(input : Stream) =\n            let count = input.Read(bytes, 0, storedLength) \n            if count < bytes.Length then Unchecked.defaultof<'a>\n            else\n                copyFromBytes bytes data\n                data.[0]\n\ntype Accessor<'a, 'b> = {\n    wrap : 'a -> 'b\n    unwrap : 'b -> 'a\n    }\n\ntype WrappedStringSerializer<'a>(accessor) =\n    let serializer = StringSerializer() :> ISerializer<string>\n    interface ISerializer<'a> with\n        member c.Write (output : Stream) (value : 'a) =\n            serializer.Write output (accessor.unwrap value)\n        member c.Read(input : Stream) =\n            serializer.Read input |> accessor.wrap\n                    \ntype IFactory =\n    abstract member Create<'a> : unit -> obj\n\ntype MessageTypeInfo = {\n    messageType : Type\n    typeId : int\n    serializer : obj\n    create : Func<IFactory, obj>\n    }\n\n[<Struct>]\ntype MessageTypeInfo<'a> = {\n    typeId : int\n    serializer : ISerializer<'a>\n    }\n\ntype MessageRegistry() =\n    let typeLookup = Dictionary<Type, MessageTypeInfo>()\n    let idLookup = Dictionary<int, MessageTypeInfo>()\n    member c.Get<'a>() =\n        let t = typeof<'a>\n        match typeLookup.TryGetValue(t) with\n        | false, _ -> failwithf \"Message type %s not registered\" t.Name\n        | true, info ->\n            {   typeId = info.typeId\n                serializer = info.serializer :?> ISerializer<'a>\n                }\n    member c.Get id =\n        match idLookup.TryGetValue(id) with\n        | false, _ -> failwithf \"Message ID %d not registered\" id\n        | true, info -> info\n    member c.Ignore<'a>() =\n        let read s = Unchecked.defaultof<'a>\n        let write value s = ()\n        c.Register<'a> 0 (serializer read write)\n    member c.Register<'a> id (serializer : ISerializer<'a>) =\n        let t = typeof<'a>\n        let info = {\n            messageType = t\n            typeId = id \n            serializer = serializer\n            create = Func<_,_>(fun factory -> factory.Create<'a>())\n            }\n        typeLookup.Add(t, info)\n        if id <> 0 then\n            idLookup.Add(id, info)\n\n[<Struct>]\ntype MessageHeader = {\n    sourceId : ActorId\n    destinationId : ActorId\n    messageTypeId : int\n    messageCount : int\n    }\n\nmodule MessageHeader =\n    let empty = {\n        sourceId = ActorId 0\n        destinationId = ActorId 0\n        messageTypeId = 0\n        messageCount = 0\n        }\n    \ntype IMessageStreamReader =\n    abstract member Start : MessageHeader -> IOutbox -> unit\n    abstract member Read : Stream -> unit\n    abstract member Flush : unit -> unit\n    "
  },
  {
    "path": "tests/Garnet.Tests/SerializationTests.fs",
    "content": "module Garnet.Tests.Serialization\n\nopen System.IO\nopen Expecto\nopen Garnet.Streaming\n\nlet testRoundtripValue (serializer : ISerializer<_>) x =\n    let ms = new MemoryStream()\n    serializer.Write ms x\n    ms.Position <- 0L\n    let x2 = serializer.Read ms\n    x2 |> shouldEqual x\n    //printfn \"%s: %d\" (typeToString (serializer.GetType())) ms.Length\n\nlet testRoundtrip (serializer : ISerializer<_>) =\n    let x = Unchecked.defaultof<_>\n    testRoundtripValue serializer x\n\n[<Struct>]\ntype Test1 = {\n    field1 : int\n    field2 : uint64\n    field3 : uint64\n    }\n\ntype Enum1 =\n    | Case1 = 0uy\n\n[<Struct>]\ntype Test2 = {\n    field1 : int\n    field2 : Enum1 \n    field3 : Test1\n    }\n\n[<Tests>]\nlet tests =\n    testList \"serialization\" [\n        testCase \"raw serialization roundtrips\" <| fun () ->\n            testRoundtrip (RawSerializer<MessageHeader>())\n            testRoundtrip (RawSerializer<int>())\n            testRoundtrip (RawSerializer<Test1>())\n            testRoundtrip (RawSerializer<Test2>())\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/StateMachineTests.fs",
    "content": "﻿module Garnet.Tests.StateMachine\n\nopen Garnet.Composition\nopen Expecto\n\nmodule Partition =\n    let globals = 0\n    let objects = 1\n\n[<Struct>]\ntype WorldStatus =\n    | WorldInactive\n    | WorldActive\n\ntype CreateWorld = struct end\ntype Update = struct end\n\nmodule WorldSystems =\n    let inactive (c : Container) =\n        c.On<CreateWorld> <| fun e ->\n            // populate world\n            c.Create(Partition.objects).Add<int>(1)\n            // switch to active state\n            c.Send<WorldStatus> WorldActive\n        \n    let active (c : Container) =\n        let update =\n            fun param (x : int) -> x + 1\n            |> Join.update1\n            |> Join.over c               \n        Disposable.Create [\n            c.On<Update> <| fun e ->\n                update()\n        ]\n\n    /// Maps a state value to a registration for the state\n    let getState (status : WorldStatus) =\n        match status with\n        | WorldInactive -> inactive\n        | WorldActive -> active\n    \n    let registerCommon (c : Container) =\n        // register systems common across all states here\n        Disposable.Null\n\n    let register (c : Container) =\n        Disposable.Create [\n            registerCommon c\n            // register so that state is stored in a globals entity\n            // and we start in inactive state\n            c.AddStateMachine(WorldInactive, getState)\n            ]\n\n[<Tests>]\nlet tests =\n    testList \"state machine\" [\n        testCase \"create state machine container\" <| fun () ->\n            let c = Container()\n            let sub = WorldSystems.register c\n            c.Run(Update())\n            c.GetComponents<int>().Count |> shouldEqual 0\n            c.Run(CreateWorld())\n            c.GetComponents<int>().Count |> shouldEqual 1\n    ]"
  },
  {
    "path": "tests/Garnet.Tests/StrategySample.fs",
    "content": "﻿module Garnet.Tests.StrategySample\n\n// This sample demonstrates using component storage for grid cells\n// for use in a strategy game.\n\nopen System\nopen System.Text\nopen Garnet.Composition\n\n// primitives\n[<Struct>]\ntype Loc = { x : int; y : int }\n    with \n        override c.ToString() = \n            sprintf \"(%d, %d)\" c.x c.y\n\n[<Struct>]\ntype Size = { w : int; h : int }\n\n// Components are stored in size 64 segments, so we need to define a mapping \n// from component keys to segment key and index within segment.\n[<Struct>]\ntype LocSegmentKeyMapper =\n    interface ISegmentKeyMapper<Loc, Loc> with\n        member c.GetSegmentKey(p) = { x = p.x >>> 3; y = p.y >>> 3 }\n        member c.GetComponentIndex(p) = ((p.y &&& 7) <<< 3) ||| (p.x &&& 7)\n\n// events\ntype ResetMap = {\n    worldSeed : int\n    size : Size\n    }\n\ntype PrintMap = struct end\ntype StepSim = struct end\ntype CommitMap = struct end\n\nmodule ResetMap =\n    let defaultParams = {\n        worldSeed = 1\n        size = { w = 32; h = 32 }\n        }\n\n// map cell components\n[<Struct>]\ntype Terrain =\n    | Grassland\n    | Desert\n    | Mountains\n\n[<Struct>]\ntype Ore = \n    | Iron\n    | Gold\n\n[<Struct>]\ntype Climate = {\n    temperature : int\n    humidity : int\n    }\n\n[<Struct>]\ntype City = {\n    population : int\n    }\n    \n[<Struct>]\ntype Occupant = {\n    unitEid : Eid\n    }\n\nmodule Occupant =\n    let none = { unitEid = Eid.Undefined }\n\n// entity components    \n[<Struct>]\ntype UnitType = \n    | Swordsmen\n    | Archers\n    \n[<Struct>]\ntype UnitSize = {\n    unitSize : int\n    }\n\n// storage            \ntype WorldGrid() = \n    let store = ComponentStore<Loc, Loc, LocSegmentKeyMapper>()\n    let mutable size = { w = 0; h = 0 }\n    member c.Size = size\n    member c.Reset newSize =\n        size <- newSize\n        store.Clear()\n    member c.Get(p) = Entity<_,_,_>(p, store)\n    member c.Commit() =\n        let locs = store.GetSegments<Loc>()\n        store.Segments.ApplyRemovalsFrom(locs)   \n        store.Commit()\n    interface ISegmentStore<Loc> with\n        member c.GetSegments() = \n            store.GetSegments()\n        member c.Handle(param, handler) =      \n            store.Handle(param, handler)\n        member c.Handle(param, sid, mask, handler) =      \n            store.Handle(param, sid, mask, handler)\n    override c.ToString() =\n        store.ToString()\n\n// systems\nmodule MapSystem =\n    let register (c : Container) =\n        let map = c.Get<WorldGrid>()\n        Disposable.Create [\n            c.On<CommitMap> <| fun _ ->\n                map.Commit()\n            c.On<ResetMap> <| fun e ->\n                let rand = Random(e.worldSeed)\n                // create map cells\n                map.Reset e.size\n                for y = 0 to e.size.h - 1 do\n                    for x = 0 to e.size.w - 1 do\n                        let cell = map.Get { x = x; y = y}\n                        cell.Add {\n                            temperature = rand.Next(0, 100)\n                            humidity = rand.Next(0, 100)\n                            }\n                        cell.Add Grassland\n                        if rand.Next(10) = 0 then\n                            cell.Add Iron\n                        if rand.Next(20) = 0 then\n                            cell.Add<City> {\n                                population = rand.Next(1, 10)\n                                }\n                // add units/entities into map\n                c.DestroyAll()\n                for i = 1 to 50 do\n                    // Pick a random location in map.\n                    // Note we aren't checking for collisions. Changes are \n                    // not immediately applied, so we can't rely on map\n                    // itself and would need another way to track.\n                    let loc = {\n                        x = rand.Next(e.size.w) \n                        y = rand.Next(e.size.w)\n                        }\n                    let entity =\n                        c.Create()\n                            .With(loc)\n                            .With(Swordsmen)\n                            .With({ unitSize = rand.Next(1, 10)})                \n                    // add (cached) reference to entity in map the entity \n                    // loc is the source of truth, so we'll need to keep \n                    // map synchronized with it.\n                    map.Get(loc).Add { unitEid = entity.Id }\n            c.On<StepSim> (\n                // Each sim step, increase the temperature of all\n                // cells with iron. Cells can be iterated over the\n                // same way as non-grid entities.\n                fun _ struct(cl : Climate, _ : Ore) ->\n                    { cl with temperature = cl.temperature + 1 }\n                |> Join.update2\n                |> Join.over map)\n            c.On<PrintMap> <| fun _ ->\n                let sb = StringBuilder()\n                for y = 0 to map.Size.h - 1 do\n                    for x = 0 to map.Size.w - 1 do\n                        let cell = map.Get { x = x; y = y}\n                        let occupant = cell.GetOrDefault<Occupant>(Occupant.none)\n                        let ch =\n                            if occupant.unitEid.IsDefined then '!'\n                            elif cell.Has<Ore>() then '$'\n                            else \n                                match cell.Get<Terrain>() with\n                                | Grassland -> '.'\n                                | Desert -> 'd'\n                                | Mountains -> 'm'\n                        sb.Append ch |> ignore\n                    sb.AppendLine() |> ignore\n                printfn \"%s\" (sb.ToString())\n            ]\n\n// startup\nlet run() =\n    let c = Container.Create(MapSystem.register)\n    c.Run {\n        worldSeed = 1\n        size = { w = 20; h = 20 }\n        }\n    c.Run <| StepSim()\n    c.Run <| PrintMap()\n    // print all components\n    printfn \"%s\" <| c.ToString()\n    // print a single unit\n    printfn \"%s\" <| c.Get(Eid 64).ToString()\n    // print a single grid cell\n    let cell = c.Get<WorldGrid>().Get({ x = 10; y = 15 })\n    printfn \"%s\" <| cell.ToString()\n    // destroy cell\n    cell.Destroy()\n    c.Commit()\n    c.Run <| CommitMap()\n    printfn \"%s\" <| cell.ToString()\n"
  }
]