├── .github └── workflows │ ├── metrics.yml │ ├── nuget.yml │ └── test.yml ├── .gitignore ├── CODE_METRICS.md ├── Directory.Build.props ├── LICENSE ├── README.md ├── Resources └── Images │ ├── Banner.afdesign │ ├── Banner.jpg │ ├── Banner.png │ ├── Demos │ └── networkcreationtool.jpg │ ├── Logo.afdesign │ ├── Logo.png │ └── StateNetLogo.ico ├── StateNet.Tests ├── Engine │ ├── Engine_Tests.cs │ └── TransitionHistory_Tests.cs ├── Network │ ├── Data │ │ └── StateNetwork_Constructor_TestData.cs │ ├── Helpers │ │ ├── StateNetworkBuilder_Helpers.cs │ │ ├── StateNetworkDictionary_Helpers.cs │ │ └── StateNetwork_Helpers.cs │ ├── StateNetworkBuilder_Tests.cs │ ├── StateNetwork_Tests.cs │ └── Validator │ │ ├── StateNetworkValidator_TestData.cs │ │ └── StateNetworkValidator_Tests.cs ├── StateNet.Tests.csproj └── packages.lock.json ├── StateNet.sln ├── StateNet ├── Documentation │ ├── core_classes.md │ ├── game_playing_bot_machine.png │ ├── planned_features.md │ └── sample_project.md ├── Engine │ ├── StateNetEngine.cs │ └── Transitions │ │ ├── Transition.cs │ │ ├── TransitionHistory.cs │ │ ├── TransitionHistoryExtensions.cs │ │ └── TransitionResult.cs ├── Logo.ico ├── Network │ ├── Connection.cs │ ├── NetworkBuilder.cs │ ├── StateNetwork.cs │ ├── StateNetworkResult.cs │ └── Validator │ │ ├── MatchesVisitor.cs │ │ ├── StateNetworkValidationResult.cs │ │ └── StateNetworkValidator.cs ├── PatternMatching │ ├── Expressions │ │ ├── Matches.cs │ │ ├── StateCount.cs │ │ ├── StateCountFromEnd.cs │ │ ├── StateCountFromStart.cs │ │ ├── TransitionCount.cs │ │ ├── TransitionCountFromEnd.cs │ │ └── TransitionCountFromStart.cs │ ├── Pattern.cs │ ├── PatternMatcher.cs │ └── StringExtensions.cs ├── Random │ ├── IRandomNumberGenerator.cs │ └── SystemRandomNumberGenerator.cs ├── Resources.cs ├── StateNet.csproj └── packages.lock.json └── azure-pipelines.yml /.github/workflows/metrics.yml: -------------------------------------------------------------------------------- 1 | name: 'code metrics' 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: 7 | - '!./CODE_METRICS.md' # ignore this file 8 | 9 | workflow_dispatch: 10 | inputs: 11 | reason: 12 | description: 'The reason for running the workflow' 13 | required: true 14 | default: 'Manual run' 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: 'Print manual run reason' 25 | if: ${{ github.event_name == 'workflow_dispatch' }} 26 | run: | 27 | echo 'Reason: ${{ github.event.inputs.reason }}' 28 | - name: .NET code metrics 29 | id: dotnet-code-metrics 30 | uses: dotnet/samples/github-actions/DotNet.GitHubAction@main 31 | env: 32 | GREETINGS: 'Hello, .NET developers!' # ${{ secrets.GITHUB_TOKEN }} 33 | with: 34 | owner: ${{ github.repository_owner }} 35 | name: ${{ github.repository }} 36 | branch: ${{ github.ref }} 37 | dir: ${{ './' }} 38 | 39 | - name: Create pull request 40 | uses: peter-evans/create-pull-request@v3.4.1 41 | if: ${{ steps.dotnet-code-metrics.outputs.updated-metrics }} == 'true' 42 | with: 43 | title: '${{ steps.dotnet-code-metrics.outputs.summary-title }}' 44 | body: '${{ steps.dotnet-code-metrics.outputs.summary-details }}' 45 | commit-message: '.NET code metrics, automated pull request.' 46 | -------------------------------------------------------------------------------- /.github/workflows/nuget.yml: -------------------------------------------------------------------------------- 1 | name: nuget 2 | env: 3 | dotnet_version: 6.0.x 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | types: [closed] 10 | branches: 11 | - main 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-18.04 16 | name: Update NuGet package 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v1 21 | with: 22 | dotnet-version: ${{ env.dotnet_version }} 23 | - name: Restore 24 | run: dotnet restore 25 | - name: Build 26 | run: dotnet build -c Release --no-restore 27 | - name: Pack 28 | run: dotnet pack -c Release -o out 29 | - name: Push 30 | run: dotnet nuget push ./out/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{secrets.NUGET_TOKEN}} --skip-duplicate --no-symbols true 31 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # The name of the workflow. 2 | # This is the name that's displayed for status 3 | # badges (commonly embedded in README.md files). 4 | name: tests 5 | 6 | # Trigger this workflow on a push, or pull request to 7 | # the production branch, when either C# or project files changed 8 | on: 9 | push: 10 | pull_request: 11 | branches: [ main ] 12 | paths-ignore: 13 | - 'README.md' 14 | 15 | # Create an environment variable named DOTNET_VERSION 16 | # and set it as "6.0.x" 17 | env: 18 | DOTNET_VERSION: '6.0.x' # The .NET SDK version to use 19 | 20 | # Defines a single job named "build-and-test" 21 | jobs: 22 | build-and-test: 23 | 24 | # When the workflow runs, this is the name that is logged 25 | # This job will run three times, once for each "os" defined 26 | name: build-and-test-${{matrix.os}} 27 | runs-on: ${{ matrix.os }} 28 | strategy: 29 | matrix: 30 | os: [ubuntu-latest, windows-latest, macOS-latest] 31 | 32 | # Each job run contains these five steps 33 | steps: 34 | 35 | # 1) Check out the source code so that the workflow can access it. 36 | - uses: actions/checkout@v2 37 | 38 | # 2) Set up the .NET CLI environment for the workflow to use. 39 | # The .NET version is specified by the environment variable. 40 | - name: Setup .NET 41 | uses: actions/setup-dotnet@v1 42 | with: 43 | dotnet-version: ${{ env.DOTNET_VERSION }} 44 | 45 | # 3) Restore the dependencies and tools of a project or solution. 46 | - name: Install dependencies 47 | run: dotnet restore 48 | 49 | # 4) Build a project or solution and all of its dependencies. 50 | - name: Build 51 | run: dotnet build --configuration Release --no-restore 52 | 53 | # 5) Test a project or solution. 54 | - name: Test 55 | run: dotnet test --no-restore --verbosity normal 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | 4 |
5 |

6 | 7 | ## A .Net Standard library used to model complicated State Machines 8 | 9 | [![Discord Server](https://img.shields.io/discord/533275703882547200?logo=discord)](https://discord.gg/D8MSXJB) 10 | [![NuGet](https://img.shields.io/nuget/v/Aptacode.StateNet.svg?style=flat)](https://www.nuget.org/packages/Aptacode.StateNet/) 11 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/1d08da0b20b0407682ed6cf71f7bd3a1)](https://www.codacy.com/gh/Aptacode/StateNet/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Aptacode/StateNet&utm_campaign=Badge_Grade) 12 | ![last commit](https://img.shields.io/github/last-commit/Aptacode/StateNet?style=flat-square&cacheSeconds=86000) 13 | [![Build Status](https://dev.azure.com/Aptacode/StateNet/_apis/build/status/Aptacode.StateNet?branchName=Development)](https://dev.azure.com/Aptacode/StateNet/_build/latest?definitionId=21&branchName=Development) 14 | 15 | ### Overview 16 | 17 | StateNet's primary purpose is to create a simple way to define and control the flow through pages of an application. However, since its inception the library has grown versatile with usecases ranging from X to Y. 18 | 19 | ### Usage 20 | 21 | At its core, StateNet works by defining a network of states, and how those states are connected. Inter-state connections are defined by a list of 'Connections' for a given 'Input' each of which have dynamically-computed probabilities. 22 | 23 | For example, consider a network that defines the traffic lights at a pedestrian crossing. The network will have these states: 24 | -Red 25 | -Yellow 26 | -Green 27 | -Pending Pedestrians 28 | 29 | The network's state will be `Green` until a pedestrian Triggers `Crossing`. An equation will then check if the `Green` state has been active for long enough. If it has, then the odds of moving to `Yellow` are 100%. If it hasn't been long enough, then the probability of transitioning to `Pending Pedestrians` is 100%. Once in either the `Yellow` or `Red` state, a Trigger such as 'timer-check' might fire every second. Every time `timer-check` fires, the state will only change back to `Green` if enough time has passed for pedestrians to have crossed. 30 | 31 | #### How to Configure the Network 32 | List all of the states your application needs. Then consider the relationships between those states in order to determine your system's Inputs (state transition trigger events). Create a connection by defining a source state, input, destination state and an expression which determines the weight of the connection at runtime. 33 | 34 | Weights can be as simple or dynamic as you need. For example, a dice will have 6 states, 1 Trigger (`roll`), and each state-connection (all 36 of them [6X6]) has a hard coded weight of 16.66%. A more complex system might use boolean logic, comparisons or arithmatic expressions to determine the connection weight based on the transition history. 35 | 36 | 37 | ```csharp 38 | //Defining the network 39 | var network = NetworkBuilder.New.SetStartState("A") 40 | .AddConnection("A", "Next", "B", new ConstantInteger(1)) 41 | .AddConnection("B", "Next", "A", new ConstantInteger(1)) 42 | .Build().Network; 43 | 44 | //Running the engine 45 | var engine = new StateNetEngine(network, new SystemRandomNumberGenerator()); 46 | engine.OnTransition += (transition) => Console.WriteLine(transition); 47 | var state1 = engine.CurrentState; //A 48 | var state2 = engine.Apply("Next"); //B 49 | var state2 = engine.Apply("Next"); //A 50 | 51 | ``` 52 | 53 | ## License 54 | 55 | MIT License 56 | -------------------------------------------------------------------------------- /Resources/Images/Banner.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Banner.afdesign -------------------------------------------------------------------------------- /Resources/Images/Banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Banner.jpg -------------------------------------------------------------------------------- /Resources/Images/Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Banner.png -------------------------------------------------------------------------------- /Resources/Images/Demos/networkcreationtool.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Demos/networkcreationtool.jpg -------------------------------------------------------------------------------- /Resources/Images/Logo.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Logo.afdesign -------------------------------------------------------------------------------- /Resources/Images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/Logo.png -------------------------------------------------------------------------------- /Resources/Images/StateNetLogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/Resources/Images/StateNetLogo.ico -------------------------------------------------------------------------------- /StateNet.Tests/Engine/Engine_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Aptacode.Expressions; 3 | using Aptacode.StateNet.Engine; 4 | using Aptacode.StateNet.Engine.Transitions; 5 | using Aptacode.StateNet.Network; 6 | using Aptacode.StateNet.PatternMatching; 7 | using Aptacode.StateNet.PatternMatching.Expressions; 8 | using Aptacode.StateNet.Random; 9 | using Moq; 10 | using StateNet.Tests.Network.Helpers; 11 | using Xunit; 12 | 13 | namespace StateNet.Tests.Engine; 14 | 15 | public class Engine_Tests 16 | { 17 | private readonly ExpressionFactory _expressions = new(); 18 | 19 | [Fact] 20 | public void CurrentStateChanges_After_SuccessfulTransition() 21 | { 22 | //Arrange 23 | var networkResponse = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 24 | .Build().Network; 25 | 26 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 27 | 28 | //Act 29 | sut.Apply("1"); 30 | 31 | //Assert 32 | Assert.Equal("b", sut.CurrentState); 33 | } 34 | 35 | [Fact] 36 | public void CurrentStateDoesNotChange_After_FailedTransition() 37 | { 38 | //Arrange 39 | var networkResponse = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 40 | .Build().Network; 41 | 42 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 43 | 44 | //Act 45 | sut.Apply("2"); 46 | 47 | //Assert 48 | Assert.Equal("a", sut.CurrentState); 49 | } 50 | 51 | 52 | [Fact] 53 | public void Engine_Chooses_CorrectConnection_GivenWeights() 54 | { 55 | //Arrange 56 | var network = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 57 | .AddConnection("a", "1", "c", _expressions.Int(1)) 58 | .Build().Network; 59 | 60 | var mockRandomNumberGenerator = new Mock(); 61 | mockRandomNumberGenerator 62 | .Setup(r => r.Generate(It.IsAny(), It.IsAny())) 63 | .Returns(1); 64 | //Act 65 | var sut = new StateNetEngine(network, mockRandomNumberGenerator.Object); 66 | 67 | var startState = sut.CurrentState; 68 | var secondState = sut.Apply("1"); 69 | 70 | //Assert 71 | Assert.Equal("a", startState); 72 | Assert.Equal("c", secondState.Transition.Destination); 73 | } 74 | 75 | [Fact] 76 | public void Engine_Chooses_CorrectConnection_GivenWeights_NetworkWithMultipleBranches() 77 | { 78 | //Arrange 79 | var network = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 80 | .AddConnection("a", "1", "c", _expressions.Int(1)) 81 | .AddConnection("c", "1", "a", _expressions.Int(1)) 82 | .AddConnection("c", "1", "b", _expressions.Int(1)) 83 | .Build().Network; 84 | 85 | var mockRandomNumberGenerator = new Mock(); 86 | mockRandomNumberGenerator 87 | .Setup(r => r.Generate(It.IsAny(), It.IsAny())) 88 | .Returns(1); 89 | //Act 90 | var sut = new StateNetEngine(network, mockRandomNumberGenerator.Object); 91 | 92 | var startState = sut.CurrentState; 93 | var secondState = sut.Apply("1"); 94 | var thirdState = sut.Apply("1"); 95 | 96 | //Assert 97 | Assert.Equal("a", startState); 98 | Assert.Equal("c", secondState.Transition.Destination); 99 | Assert.Equal("b", thirdState.Transition.Destination); 100 | } 101 | 102 | 103 | [Fact] 104 | public void EngineReverseTransition() 105 | { 106 | //Arrange 107 | var network = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 108 | .AddConnection("b", "1", "a", _expressions.Int(1)) 109 | .Build().Network; 110 | 111 | //Act 112 | var sut = new StateNetEngine(network, new SystemRandomNumberGenerator()); 113 | 114 | var startState = sut.CurrentState; 115 | var secondState = sut.Apply("1"); 116 | var thirdState = sut.Apply("1"); 117 | 118 | //Assert 119 | Assert.Equal("a", startState); 120 | Assert.Equal("b", secondState.Transition.Destination); 121 | Assert.Equal("a", thirdState.Transition.Destination); 122 | } 123 | 124 | [Fact] 125 | public void EngineSingleTransition() 126 | { 127 | var network = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 128 | .Build() 129 | .Network; 130 | 131 | var sut = new StateNetEngine(network, new SystemRandomNumberGenerator()); 132 | 133 | var startState = sut.CurrentState; 134 | var secondState = sut.Apply("1"); 135 | 136 | Assert.Equal("a", startState); 137 | Assert.Equal("b", secondState.Transition.Destination); 138 | } 139 | 140 | [Fact] 141 | public void EngineTransitionHistory() 142 | { 143 | var network = NetworkBuilder.New.SetStartState("A") 144 | .AddConnection("A", "Next", "B", 145 | _expressions.Conditional( 146 | _expressions.LessThan(_expressions.Count(new Matches(new Pattern("B"))) 147 | , 148 | _expressions.Int(1)), 149 | _expressions.Int(1), 150 | _expressions.Int(0))) 151 | .AddConnection("A", "Next", "C", 152 | _expressions.Conditional( 153 | _expressions.GreaterThanOrEqualTo( 154 | _expressions.Count(new Matches(new Pattern("B"))), 155 | _expressions.Int(1)), 156 | _expressions.Int(1), 157 | _expressions.Int(0))) 158 | .AddConnection("B", "Next", "A", _expressions.Int(1)) 159 | .AddConnection("C", "Next", "D", _expressions.Int(1)) 160 | .Build().Network; 161 | 162 | var sut = new StateNetEngine(network, new SystemRandomNumberGenerator()); 163 | 164 | var state1 = sut.CurrentState; 165 | var state2 = sut.Apply("Next"); 166 | var state3 = sut.Apply("Next"); 167 | var state4 = sut.Apply("Next"); 168 | var state5 = sut.Apply("Next"); 169 | 170 | Assert.Equal("A", state1); 171 | Assert.Equal("B", state2.Transition.Destination); 172 | Assert.Equal("A", state3.Transition.Destination); 173 | Assert.Equal("C", state4.Transition.Destination); 174 | Assert.Equal("D", state5.Transition.Destination); 175 | } 176 | 177 | [Fact] 178 | public void GetAvailableConnections_Returns_CorrectList_WhenConnectionsExistForCurrentStateAndInput() 179 | { 180 | //Arrange 181 | var networkResponse = NetworkBuilder.New 182 | .SetStartState("Start").AddConnection("Start", "Next", "A", _expressions.Int(1)) 183 | .Build().Network; 184 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 185 | //Act 186 | var connections = sut.GetAvailableConnections("Next"); 187 | //Assert 188 | Assert.Equal("A", connections.FirstOrDefault().Target); 189 | } 190 | 191 | [Fact] 192 | public void GetAvailableConnections_Returns_EmptyList_WhenNoConnectionsExistsForCurrentStateAndInput() 193 | { 194 | //Arrange 195 | var networkResponse = NetworkBuilder.New 196 | .SetStartState("Start") 197 | .Build().Network; 198 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 199 | //Act 200 | var connections = sut.GetAvailableConnections("Next"); 201 | //Assert 202 | Assert.Empty(connections); 203 | } 204 | 205 | [Fact] 206 | public void GetAvailableInputs_Returns_CorrectList_WhenInputExistsForCurrentState() 207 | { 208 | //Arrange 209 | var networkResponse = NetworkBuilder.New 210 | .SetStartState("Start").AddConnection("Start", "Next", "A", _expressions.Int(1)) 211 | .Build().Network; 212 | 213 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 214 | //Act 215 | var inputs = sut.GetAvailableInputs(); 216 | //Assert 217 | Assert.Equal("Next", inputs.FirstOrDefault()); 218 | } 219 | 220 | [Fact] 221 | public void GetAvailableInputs_Returns_EmptyList_WhenNoInputExistsForCurrentState() 222 | { 223 | //Arrange 224 | var networkResponse = NetworkBuilder.New 225 | .SetStartState("Start") 226 | .Build().Network; 227 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 228 | //Act 229 | var inputs = sut.GetAvailableInputs(); 230 | //Assert 231 | Assert.Empty(inputs); 232 | } 233 | 234 | [Fact] 235 | public void InputNotDefined_ReturnsFailTransition() 236 | { 237 | //Arrange 238 | var networkResponse = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 239 | .Build().Network; 240 | 241 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 242 | 243 | //Act 244 | var transitionResult = sut.Apply("2"); 245 | 246 | //Assert 247 | Assert.False(transitionResult.Success); 248 | } 249 | 250 | 251 | [Fact] 252 | public void OnTransition_Invoked_After_SuccessfulTransition() 253 | { 254 | //Arrange 255 | var networkResponse = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder 256 | .Build().Network; 257 | 258 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 259 | var onTransitionWasCalled = false; 260 | sut.OnTransition += (_, __) => { onTransitionWasCalled = true; }; 261 | //Act 262 | sut.Apply("1"); 263 | 264 | //Assert 265 | Assert.True(onTransitionWasCalled); 266 | } 267 | 268 | [Fact] 269 | public void OnTransition_NotInvoked_After_FailedTransition() 270 | { 271 | //Arrange 272 | var networkResponse = NetworkBuilder.New 273 | .SetStartState("Start") 274 | .Build().Network; 275 | 276 | var sut = new StateNetEngine(networkResponse, new SystemRandomNumberGenerator()); 277 | var onTransitionWasCalled = false; 278 | sut.OnTransition += (_, __) => { onTransitionWasCalled = true; }; 279 | //Act 280 | sut.Apply("Next"); 281 | 282 | //Assert 283 | Assert.False(onTransitionWasCalled); 284 | } 285 | } -------------------------------------------------------------------------------- /StateNet.Tests/Engine/TransitionHistory_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Aptacode.StateNet.Engine.Transitions; 4 | using Aptacode.StateNet.PatternMatching; 5 | using StateNet.Tests.Network.Helpers; 6 | using Xunit; 7 | 8 | namespace StateNet.Tests.Engine; 9 | 10 | public class TransitionHistory_Tests 11 | { 12 | [Fact] 13 | public void Constructor_Throws_ArgumentNullException_WhenStartStateIsNull() 14 | { 15 | //Assert 16 | Assert.Throws(() => 17 | { 18 | //Arrange 19 | //Act 20 | new TransitionHistory(null); 21 | }); 22 | } 23 | 24 | [Fact] 25 | public void GetMatches_Returns_CorrectMatches_SingleMatch() 26 | { 27 | //Arrange 28 | var sut = new TransitionHistory(StateNetwork_Helpers 29 | .Minimal_Valid_Connected_StaticWeight_Network_WithPattern); 30 | sut.Add("1", "b"); 31 | //Act 32 | var pattern = new Pattern(StateNetwork_Helpers.StateB); 33 | var matches = sut.GetMatches(pattern); 34 | //Assert 35 | Assert.Equal("1", matches.First().ToString()); 36 | } 37 | 38 | //GetMatchesTest 39 | //AddTest 40 | [Fact] 41 | public void ToString_Returns_CorrectHistory_WithMultipleTransitions() 42 | { 43 | //Arrange 44 | var sut = new TransitionHistory(StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network); 45 | sut.Add("1", "b"); 46 | sut.Add("2", "c"); 47 | //Act 48 | var actualResult = sut.ToString(); 49 | 50 | //Assert 51 | Assert.Equal("a,1,b,2,c", actualResult); 52 | } 53 | 54 | [Fact] 55 | public void ToString_Returns_CorrectHistory_WithOneTransition() 56 | { 57 | //Arrange 58 | var sut = new TransitionHistory(StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network); 59 | sut.Add("next", "b"); 60 | //Act 61 | var actualResult = sut.ToString(); 62 | 63 | //Assert 64 | Assert.Equal("a,next,b", actualResult); 65 | } 66 | 67 | [Fact] 68 | public void ToString_Returns_StartState_WhenNoTransition() 69 | { 70 | //Arrange 71 | var sut = new TransitionHistory(StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network); 72 | //Act 73 | var actualResult = sut.ToString(); 74 | 75 | //Assert 76 | Assert.Equal("a", actualResult); 77 | } 78 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Data/StateNetwork_Constructor_TestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using StateNet.Tests.Network.Helpers; 5 | 6 | namespace StateNet.Tests.Network.Data; 7 | 8 | public class StateNetwork_Constructor_TestData : IEnumerable 9 | { 10 | private readonly List _data = new() 11 | { 12 | new object[] 13 | { 14 | typeof(ArgumentNullException), null, "A" 15 | }, //Constructor throws ArgumentNullException when StateDictionary is null. 16 | new object[] 17 | { 18 | typeof(ArgumentException), StateNetworkDictionary_Helpers.Empty_NetworkDictionary, "A" 19 | }, //Constructor throws ArgumentException when StateDictionary is empty. 20 | new object[] 21 | { 22 | typeof(ArgumentNullException), StateNetworkDictionary_Helpers.SingleState_NetworkDictionary, "" 23 | }, //Constructor throws ArgumentNullException when the StartState is empty 24 | new object[] 25 | { 26 | typeof(ArgumentNullException), StateNetworkDictionary_Helpers.SingleState_NetworkDictionary, null 27 | } //Constructor throws ArgumentNullException when the StartState is null 28 | }; 29 | 30 | public IEnumerator GetEnumerator() 31 | { 32 | return _data.GetEnumerator(); 33 | } 34 | 35 | IEnumerator IEnumerable.GetEnumerator() 36 | { 37 | return GetEnumerator(); 38 | } 39 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Helpers/StateNetworkBuilder_Helpers.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions; 2 | using Aptacode.StateNet.Engine.Transitions; 3 | using Aptacode.StateNet.Network; 4 | 5 | namespace StateNet.Tests.Network.Helpers; 6 | 7 | public static class StateNetworkBuilder_Helpers 8 | { 9 | private static readonly ExpressionFactory Expressions = 10 | new(); 11 | 12 | public static NetworkBuilder Minimal_Valid_Connected_StaticWeight_NetworkBuilder => 13 | NetworkBuilder.New.SetStartState("a") 14 | .AddConnection("a", "1", "b", Expressions.Int(1)); 15 | 16 | public static NetworkBuilder Empty_NetworkBuilder => 17 | NetworkBuilder.New; 18 | 19 | public static NetworkBuilder SingleState_NetworkBuilder => 20 | NetworkBuilder.New.SetStartState("a"); 21 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Helpers/StateNetworkDictionary_Helpers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Aptacode.Expressions; 3 | using Aptacode.Expressions.List.IntegerListOperators; 4 | using Aptacode.StateNet.Engine.Transitions; 5 | using Aptacode.StateNet.Network; 6 | using Aptacode.StateNet.PatternMatching; 7 | using Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | namespace StateNet.Tests.Network.Helpers; 10 | 11 | public static class StateNetworkDictionary_Helpers 12 | { 13 | private static readonly ExpressionFactory Expressions = 14 | new(); 15 | 16 | public static Dictionary>> 17 | Minimal_Valid_Connected_StaticWeight_NetworkDictionary => 18 | new() 19 | { 20 | { 21 | "a", new Dictionary> 22 | { 23 | { 24 | "1", new List 25 | { 26 | new("b", Expressions.Int(1)) 27 | } 28 | } 29 | } 30 | }, 31 | { 32 | "b", new Dictionary> 33 | { 34 | { 35 | "1", new List 36 | { 37 | new("a", Expressions.Int(1)) 38 | } 39 | } 40 | } 41 | } 42 | }; 43 | 44 | public static Dictionary>> 45 | Invalid_UnusableInput_NetworkDictionary => 46 | new() 47 | { 48 | { 49 | "a", new Dictionary> 50 | { 51 | { 52 | "1", new List 53 | { 54 | new("b", Expressions.Int(1)) 55 | } 56 | } 57 | } 58 | }, 59 | { 60 | "b", new Dictionary> 61 | { 62 | { 63 | "1", new List 64 | { 65 | new("a", Expressions.Int(1)) 66 | } 67 | }, 68 | { 69 | "2", new List() 70 | } 71 | } 72 | } 73 | }; 74 | 75 | public static Dictionary>> 76 | Invalid_Unreachable_State_NetworkDictionary => 77 | new() 78 | { 79 | { 80 | "a", new Dictionary> 81 | { 82 | { 83 | "1", new List 84 | { 85 | new("b", Expressions.Int(1)) 86 | } 87 | } 88 | } 89 | }, 90 | { 91 | "b", new Dictionary> 92 | { 93 | { 94 | "1", new List 95 | { 96 | new("a", Expressions.Int(1)) 97 | } 98 | } 99 | } 100 | }, 101 | { 102 | "c", new Dictionary> 103 | { 104 | { 105 | "1", new List() 106 | } 107 | } 108 | } 109 | }; 110 | 111 | public static Dictionary>> 112 | Invalid_ConnectionTargetState_NetworkDictionary => 113 | new() 114 | { 115 | { 116 | "a", new Dictionary> 117 | { 118 | { 119 | "1", new List 120 | { 121 | new("c", Expressions.Int(1)) 122 | } 123 | } 124 | } 125 | }, 126 | { 127 | "b", new Dictionary> 128 | { 129 | { 130 | "1", new List 131 | { 132 | new("a", Expressions.Int(1)) 133 | } 134 | } 135 | } 136 | } 137 | }; 138 | 139 | public static Dictionary>> 140 | Invalid_ConnectionPatternState_NetworkDictionary => 141 | new() 142 | { 143 | { 144 | "a", new Dictionary> 145 | { 146 | { 147 | "1", new List 148 | { 149 | new("b", new Count(new Matches(new Pattern("c")))) 150 | } 151 | } 152 | } 153 | }, 154 | { 155 | "b", new Dictionary> 156 | { 157 | { 158 | "1", new List 159 | { 160 | new("a", Expressions.Int(1)) 161 | } 162 | } 163 | } 164 | } 165 | }; 166 | 167 | public static Dictionary>> 168 | Invalid_ConnectionPatternInput_NetworkDictionary => 169 | new() 170 | { 171 | { 172 | "a", new Dictionary> 173 | { 174 | { 175 | "1", new List 176 | { 177 | new("b", new Count(new Matches(new Pattern("bi")))) 178 | } 179 | } 180 | } 181 | }, 182 | { 183 | "b", new Dictionary> 184 | { 185 | { 186 | "1", new List 187 | { 188 | new("a", Expressions.Int(1)) 189 | } 190 | } 191 | } 192 | } 193 | }; 194 | 195 | public static Dictionary>> 196 | Empty_NetworkDictionary => 197 | new(); 198 | 199 | public static Dictionary>> 200 | SingleState_NetworkDictionary => 201 | new() 202 | { 203 | { 204 | "a", new Dictionary>() 205 | } 206 | }; 207 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Helpers/StateNetwork_Helpers.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions; 2 | using Aptacode.StateNet.Engine.Transitions; 3 | using Aptacode.StateNet.Network; 4 | using Aptacode.StateNet.PatternMatching; 5 | 6 | namespace StateNet.Tests.Network.Helpers; 7 | 8 | public static class StateNetwork_Helpers 9 | { 10 | public static readonly string StateB = "b"; 11 | 12 | private static readonly ExpressionFactory Expressions = 13 | new(); 14 | 15 | public static StateNetwork 16 | Minimal_Valid_Connected_StaticWeight_Network => //Make valid networks with the builder. 17 | NetworkBuilder.New.SetStartState("a").AddConnection("a", "1", "b", Expressions.Int(1)) 18 | .AddConnection("b", "1", "a", Expressions.Int(1)) 19 | .Build().Network; 20 | 21 | public static StateNetwork State_WithMultiple_Inputs_Network => 22 | NetworkBuilder.New.SetStartState("a").AddConnection("a", "1", "b", Expressions.Int(1)) 23 | .AddConnection("b", "2", "a", Expressions.Int(1)) 24 | .Build().Network; 25 | 26 | public static StateNetwork State_WithMultiple_Inputs_WithPatterns_Network => 27 | NetworkBuilder.New.SetStartState("a").AddConnection("a", "1", "b", Expressions.Int(1)) 28 | .AddConnection("b", "2", "a", Expressions.Int(1)).AddPattern(new Pattern("a", "1")) 29 | .Build().Network; 30 | 31 | public static StateNetwork Minimal_Valid_Connected_StaticWeight_Network_WithPattern => 32 | new("a", StateNetworkDictionary_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkDictionary, 33 | new[] { new Pattern(StateB) }); 34 | 35 | public static StateNetwork Invalid_StartState_Network => 36 | new("c", StateNetworkDictionary_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkDictionary, 37 | new Pattern[0]); //Takes a state dictionary with states connected 'a' <-> 'b', sets start state to 'c' 38 | 39 | public static StateNetwork Invalid_ConnectionTargetState_Network => 40 | new("a", StateNetworkDictionary_Helpers.Invalid_ConnectionTargetState_NetworkDictionary, 41 | new Pattern[0]); 42 | 43 | public static StateNetwork Invalid_ConnectionPatternState_Network => 44 | new("a", StateNetworkDictionary_Helpers.Invalid_ConnectionPatternState_NetworkDictionary, 45 | new Pattern[0]); 46 | 47 | public static StateNetwork Invalid_ConnectionPatternInput_Network => 48 | new("a", StateNetworkDictionary_Helpers.Invalid_ConnectionPatternInput_NetworkDictionary, 49 | new Pattern[0]); 50 | 51 | //To make this test even a thing I made StateNetwork.StartState settable, might not be the correct idea if the exception is thrown beforehand in the constructor anyway. 52 | public static StateNetwork Empty_Network => 53 | new("", StateNetworkDictionary_Helpers.Empty_NetworkDictionary, new Pattern[0]); 54 | 55 | public static StateNetwork Invalid_Unreachable_State_Network => 56 | new("a", StateNetworkDictionary_Helpers.Invalid_Unreachable_State_NetworkDictionary, 57 | new Pattern[0]); 58 | 59 | public static StateNetwork Invalid_UnusableInput_Network => 60 | new("a", StateNetworkDictionary_Helpers.Invalid_UnusableInput_NetworkDictionary, 61 | new Pattern[0]); 62 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/StateNetworkBuilder_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Aptacode.Expressions; 3 | using Aptacode.StateNet.Engine.Transitions; 4 | using Aptacode.StateNet.Network; 5 | using StateNet.Tests.Network.Helpers; 6 | using Xunit; 7 | 8 | namespace StateNet.Tests.Network; 9 | 10 | public class StateNetworkBuilder_Tests 11 | { 12 | private static readonly ExpressionFactory Expressions = 13 | new(); 14 | 15 | private readonly TransitionHistory _context; 16 | 17 | [Fact] 18 | public void 19 | AddConnection_SuccessfullyAddsConnection_AfterBuild() //This is dependent on some strange business with the context stuff to assert equality, not sure if this is perfect. 20 | { 21 | //Arrange 22 | var networkBuilder = StateNetworkBuilder_Helpers.SingleState_NetworkBuilder; 23 | //Act 24 | var sut = networkBuilder.AddConnection("a", "1", "b", Expressions.Int(1)).Build(); 25 | //Assert 26 | Assert.True(sut.Network.GetAllConnections().ElementAt(0).Target == 27 | new Connection("b", Expressions.Int(1)).Target); 28 | Assert.True(Expressions 29 | .EqualTo(sut.Network.GetAllConnections().ElementAt(0).Expression, Expressions.Int(1)) 30 | .Interpret(_context)); 31 | // Assert.Contains(new Connection("b", _expressions.Int(1)), sut.Network.GetAllConnections(),); 32 | } 33 | 34 | [Fact] 35 | public void Builder_Returns_FailMessage_WhenStartStateIsNotSet() 36 | { 37 | //Arrange 38 | //Act 39 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder.Build(); 40 | 41 | //Assert 42 | Assert.False(sut.Success); 43 | } 44 | 45 | [Fact] 46 | public void ClearConnectionsFromState_OfGivenInput_SuccessfullyClearsConnection_AfterBuild() 47 | { 48 | //Arrange 49 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 50 | //Act 51 | sut.SetStartState("a"); 52 | sut.AddConnection("a", "1", "b", Expressions.Int(1)); 53 | sut.AddConnection("a", "2", "b", Expressions.Int(1)); 54 | sut.ClearConnectionsFromState("a", "1"); 55 | var result = sut.Build(); 56 | 57 | Assert.Empty(result.Network.GetConnections("a", "1")); 58 | Assert.Equal(1, result.Network.GetConnections("a", "2").Count()); 59 | } 60 | 61 | [Fact] 62 | public void ClearConnectionsFromState_SuccessfullyClearsConnection_AfterBuild() 63 | { 64 | //Arrange 65 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 66 | //Act 67 | sut.SetStartState("a"); 68 | sut.AddConnection("a", "1", "b", Expressions.Int(1)); 69 | sut.AddConnection("a", "2", "b", Expressions.Int(1)); 70 | sut.ClearConnectionsFromState("a"); 71 | sut.AddConnection("a", "3", "b", Expressions.Int(1)); 72 | var result = sut.Build(); 73 | 74 | //Asset 75 | Assert.Empty(result.Network.GetConnections("a", "1")); 76 | Assert.Empty(result.Network.GetConnections("a", "2")); 77 | Assert.Equal(1, result.Network.GetConnections("a").Count()); 78 | Assert.Equal(1, result.Network.GetConnections("a", "3").Count()); 79 | } 80 | 81 | [Fact] 82 | public void ClearConnectionsToState_OfGivenInput_SuccessfullyClearsConnection_AfterBuild() 83 | { 84 | //Arrange 85 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 86 | //Act 87 | sut.SetStartState("a"); 88 | sut.AddConnection("a", "1", "b", Expressions.Int(1)); 89 | sut.AddConnection("a", "2", "b", Expressions.Int(1)); 90 | sut.ClearConnectionsToState("b", "1"); 91 | sut.AddConnection("a", "3", "b", Expressions.Int(1)); 92 | var result = sut.Build(); 93 | 94 | //Asset 95 | Assert.Empty(result.Network.GetConnections("a", "1")); 96 | Assert.Equal(1, result.Network.GetConnections("a", "2").Count()); 97 | Assert.Equal(1, result.Network.GetConnections("a", "3").Count()); 98 | Assert.Equal(2, result.Network.GetConnections("a").Count()); 99 | } 100 | 101 | [Fact] 102 | public void ClearConnectionsToState_SuccessfullyClearsConnection_AfterBuild() 103 | { 104 | //Arrange 105 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 106 | //Act 107 | sut.SetStartState("a"); 108 | sut.AddConnection("a", "1", "b", Expressions.Int(1)); 109 | sut.AddConnection("a", "2", "b", Expressions.Int(1)); 110 | sut.ClearConnectionsToState("b"); 111 | sut.AddConnection("a", "3", "b", Expressions.Int(1)); 112 | var result = sut.Build(); 113 | 114 | //Asset 115 | Assert.Empty(result.Network.GetConnections("a", "1")); 116 | Assert.Empty(result.Network.GetConnections("a", "2")); 117 | Assert.Equal(1, result.Network.GetConnections("a").Count()); 118 | Assert.Equal(1, result.Network.GetConnections("a", "3").Count()); 119 | } 120 | 121 | [Fact] 122 | public void RemoveInput_SuccesfullyRemovesInput_AfterBuild() 123 | { 124 | //Arrange 125 | var sut = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 126 | //Act 127 | sut.SetStartState("a"); 128 | sut.AddConnection("a", "1", "b", Expressions.Int(1)); 129 | sut.AddConnection("a", "2", "b", Expressions.Int(1)); 130 | sut.RemoveInputOnState("1", "a"); 131 | var result = sut.Build(); 132 | 133 | //Asset 134 | Assert.Empty(result.Network.GetConnections("a", "1")); 135 | Assert.Equal(1, result.Network.GetConnections("a").Count()); 136 | Assert.Equal(1, result.Network.GetConnections("a", "2").Count()); 137 | } 138 | 139 | [Fact] 140 | public void RemoveState_SuccesfullyRemovesState_AfterBuild() 141 | { 142 | //Arrange 143 | var networkBuilder = StateNetworkBuilder_Helpers.Minimal_Valid_Connected_StaticWeight_NetworkBuilder; 144 | //Act 145 | var sut = networkBuilder.RemoveState("b").Build(); 146 | 147 | //Assert 148 | Assert.DoesNotContain("b", sut.Network.GetAllStates()); 149 | } 150 | 151 | [Fact] 152 | public void StartState_IsSet_AfterBuild() 153 | { 154 | //Arrange 155 | var networkBuilder = StateNetworkBuilder_Helpers.Empty_NetworkBuilder; 156 | //Act 157 | var sut = networkBuilder.SetStartState("A").Build(); 158 | 159 | //Assert 160 | Assert.Equal("A", sut.Network.StartState); 161 | } 162 | 163 | //[Fact] 164 | //public void AddPatterns_Successfully_Adds 165 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/StateNetwork_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Aptacode.Expressions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | using Aptacode.StateNet.Network; 7 | using Aptacode.StateNet.PatternMatching; 8 | using StateNet.Tests.Network.Data; 9 | using StateNet.Tests.Network.Helpers; 10 | using Xunit; 11 | 12 | namespace StateNet.Tests.Network; 13 | 14 | public class StateNetwork_Tests 15 | { 16 | private readonly ExpressionFactory _expressions = new(); 17 | 18 | [Theory] 19 | [ClassData(typeof(StateNetwork_Constructor_TestData))] 20 | public void Constructor_Throws_Exception_Tests(Type exception, 21 | Dictionary>> networkDictionary, 22 | string start) //Tests for each of the 4 cases in which an exception should be thrown by StateNetwork's constructor 23 | { 24 | //Assert 25 | Assert.Throws(exception, () => 26 | { 27 | //Act 28 | var sut = new StateNetwork(start, 29 | networkDictionary, new Pattern[0]); 30 | }); 31 | } 32 | 33 | [Fact] 34 | public void GetAllInputs_Successfully_Returns_ListOfInputs() 35 | { 36 | //Arrange 37 | var network = StateNetwork_Helpers.State_WithMultiple_Inputs_Network; 38 | //Act 39 | var sut = network.GetAllInputs(); 40 | //Assert 41 | Assert.Contains("1", sut); 42 | Assert.Contains("2", sut); 43 | } 44 | 45 | [Fact] 46 | public void GetConnections_Returns_EmptyList_WhenInputIsNotDefinedForState() 47 | { 48 | //Arrange 49 | var sut = StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network; 50 | 51 | //Act 52 | var connections = sut.GetConnections("a", "2"); 53 | 54 | //Assert 55 | Assert.Empty(connections); 56 | } 57 | 58 | 59 | [Fact] 60 | public void GetConnections_Returns_EmptyList_WhenNoConnectionsExistForStateAndInput() 61 | { 62 | //Arrange 63 | var sut = StateNetwork_Helpers.Invalid_UnusableInput_Network; 64 | 65 | //Act 66 | var connections = sut.GetConnections("b", "2"); 67 | 68 | //Assert 69 | Assert.Empty(connections); 70 | } 71 | 72 | [Fact] 73 | public void GetConnections_Returns_List_WhenConnectionsExistForStateAndInput() 74 | { 75 | //Arrange 76 | var sut = StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network; 77 | //Act 78 | var connections = sut.GetConnections("a", "1"); 79 | 80 | //Assert 81 | Assert.Equal(1, connections.Count()); 82 | } 83 | 84 | [Fact] 85 | public void GetConnections_Returns_List_WhenInputsAreDefined() 86 | { 87 | //Arrange 88 | var sut = StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network; 89 | 90 | //Act 91 | var connections = sut.GetConnections("a", "1"); 92 | 93 | //Assert 94 | Assert.Equal(1, connections.Count()); 95 | } 96 | 97 | 98 | [Fact] 99 | public void GetInputs_Returns_EmptyList_WhenStateDoesNotExist() 100 | { 101 | //Arrange 102 | var network = StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network; 103 | 104 | //Act 105 | var sut = network.GetInputs("c"); 106 | 107 | //Assert 108 | Assert.Empty(sut); 109 | } 110 | 111 | [Fact] 112 | public void GetInputs_Returns_List_WhenStateDoesExist() 113 | { 114 | //Arrange 115 | var network = StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network; 116 | //Act 117 | var sut = network.GetInputs("a"); 118 | 119 | //Assert 120 | Assert.Contains("1", sut); 121 | } 122 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Validator/StateNetworkValidator_TestData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using Aptacode.StateNet; 4 | using StateNet.Tests.Network.Helpers; 5 | 6 | namespace StateNet.Tests.Network.Validator; 7 | 8 | public class StateNetworkValidator_TestData : IEnumerable 9 | { 10 | private readonly List _data = new() 11 | { 12 | new object[] 13 | { 14 | StateNetwork_Helpers.Invalid_ConnectionPatternInput_Network, 15 | Resources.INVALID_DEPENDENCY, false 16 | }, 17 | new object[] 18 | { 19 | StateNetwork_Helpers.Invalid_ConnectionPatternState_Network, 20 | Resources.INVALID_DEPENDENCY, false 21 | }, 22 | new object[] 23 | { 24 | StateNetwork_Helpers.Invalid_ConnectionTargetState_Network, Resources.INVALID_CONNECTION_TARGET, 25 | false 26 | }, 27 | new object[] 28 | { StateNetwork_Helpers.Invalid_StartState_Network, Resources.INVALID_START_STATE, false }, 29 | new object[] { StateNetwork_Helpers.Minimal_Valid_Connected_StaticWeight_Network, Resources.SUCCESS, true }, 30 | new object[] 31 | { 32 | StateNetwork_Helpers.Invalid_Unreachable_State_Network, Resources.UNREACHABLE_STATES, 33 | false 34 | }, 35 | new object[] 36 | { StateNetwork_Helpers.Invalid_UnusableInput_Network, Resources.UNUSABLE_INPUTS, false } 37 | }; 38 | 39 | public IEnumerator GetEnumerator() 40 | { 41 | return _data.GetEnumerator(); 42 | } 43 | 44 | IEnumerator IEnumerable.GetEnumerator() 45 | { 46 | return GetEnumerator(); 47 | } 48 | } -------------------------------------------------------------------------------- /StateNet.Tests/Network/Validator/StateNetworkValidator_Tests.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.StateNet.Network; 2 | using Aptacode.StateNet.Network.Validator; 3 | using Xunit; 4 | 5 | namespace StateNet.Tests.Network.Validator; 6 | 7 | public class StateNetworkValidator_Tests 8 | { 9 | [Theory] 10 | [ClassData(typeof(StateNetworkValidator_TestData))] 11 | public void StateNetworkValidator_IsValidTests(StateNetwork stateNetwork, string message, bool isValid) 12 | { 13 | //Arrange 14 | //Act 15 | var stateNetworkValidationResult = stateNetwork.IsValid(); 16 | 17 | //Assert 18 | Assert.Equal(isValid, stateNetworkValidationResult.Success); 19 | Assert.Equal(message, stateNetworkValidationResult.Message); 20 | } 21 | } -------------------------------------------------------------------------------- /StateNet.Tests/StateNet.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 10.0 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | all 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /StateNet.Tests/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | "net6.0": { 5 | "Aptacode.Expressions": { 6 | "type": "Direct", 7 | "requested": "[1.0.13, )", 8 | "resolved": "1.0.13", 9 | "contentHash": "tQR8ZYVXjflRmDmZbCmY2y6+f+TqmYGB5fhEu2XD6bUUUEx0kt/2apgAq2Su4+f0bUbshuSVNUADsq/aYEnA8w==" 10 | }, 11 | "coverlet.collector": { 12 | "type": "Direct", 13 | "requested": "[3.1.2, )", 14 | "resolved": "3.1.2", 15 | "contentHash": "wuLDIDKD5XMt0A7lE31JPenT7QQwZPFkP5rRpdJeblyXZ9MGLI8rYjvm5fvAKln+2/X+4IxxQDxBtwdrqKNLZw==" 16 | }, 17 | "Microsoft.NET.Test.Sdk": { 18 | "type": "Direct", 19 | "requested": "[17.1.0, )", 20 | "resolved": "17.1.0", 21 | "contentHash": "MVKvOsHIfrZrvg+8aqOF5dknO/qWrR1sWZjMPQ1N42MKMlL/zQL30FQFZxPeWfmVKWUWAOmAHYsqB5OerTKziw==", 22 | "dependencies": { 23 | "Microsoft.CodeCoverage": "17.1.0", 24 | "Microsoft.TestPlatform.TestHost": "17.1.0" 25 | } 26 | }, 27 | "Moq": { 28 | "type": "Direct", 29 | "requested": "[4.17.2, )", 30 | "resolved": "4.17.2", 31 | "contentHash": "HytUPJ3/uks2UgJ9hIcyXm3YxpFAR4OJzbQwTHltbKGun3lFLhEHs97hiiPj1dY8jV/kasXeihTzDxct6Zf3iQ==", 32 | "dependencies": { 33 | "Castle.Core": "4.4.1", 34 | "System.Threading.Tasks.Extensions": "4.5.4" 35 | } 36 | }, 37 | "xunit": { 38 | "type": "Direct", 39 | "requested": "[2.4.1, )", 40 | "resolved": "2.4.1", 41 | "contentHash": "XNR3Yz9QTtec16O0aKcO6+baVNpXmOnPUxDkCY97J+8krUYxPvXT1szYYEUdKk4sB8GOI2YbAjRIOm8ZnXRfzQ==", 42 | "dependencies": { 43 | "xunit.analyzers": "0.10.0", 44 | "xunit.assert": "[2.4.1]", 45 | "xunit.core": "[2.4.1]" 46 | } 47 | }, 48 | "xunit.runner.visualstudio": { 49 | "type": "Direct", 50 | "requested": "[2.4.3, )", 51 | "resolved": "2.4.3", 52 | "contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ==" 53 | }, 54 | "Castle.Core": { 55 | "type": "Transitive", 56 | "resolved": "4.4.1", 57 | "contentHash": "zanbjWC0Y05gbx4eGXkzVycOQqVOFVeCjVsDSyuao9P4mtN1w3WxxTo193NGC7j3o2u3AJRswaoC6hEbnGACnQ==", 58 | "dependencies": { 59 | "NETStandard.Library": "1.6.1", 60 | "System.Collections.Specialized": "4.3.0", 61 | "System.ComponentModel": "4.3.0", 62 | "System.ComponentModel.TypeConverter": "4.3.0", 63 | "System.Diagnostics.TraceSource": "4.3.0", 64 | "System.Dynamic.Runtime": "4.3.0", 65 | "System.Reflection": "4.3.0", 66 | "System.Reflection.Emit": "4.3.0", 67 | "System.Reflection.TypeExtensions": "4.3.0", 68 | "System.Xml.XmlDocument": "4.3.0" 69 | } 70 | }, 71 | "Microsoft.CodeCoverage": { 72 | "type": "Transitive", 73 | "resolved": "17.1.0", 74 | "contentHash": "0N/ZJ71ncCxQWhgtkEYKOgu2oMHa8h1tsOUbhmIKXF8UwtSUCe4vHAsJ3DVcNWRwNfQzSTy263ZE+QF6MdIhhQ==" 75 | }, 76 | "Microsoft.CSharp": { 77 | "type": "Transitive", 78 | "resolved": "4.0.1", 79 | "contentHash": "17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==", 80 | "dependencies": { 81 | "System.Collections": "4.0.11", 82 | "System.Diagnostics.Debug": "4.0.11", 83 | "System.Dynamic.Runtime": "4.0.11", 84 | "System.Globalization": "4.0.11", 85 | "System.Linq": "4.1.0", 86 | "System.Linq.Expressions": "4.1.0", 87 | "System.ObjectModel": "4.0.12", 88 | "System.Reflection": "4.1.0", 89 | "System.Reflection.Extensions": "4.0.1", 90 | "System.Reflection.Primitives": "4.0.1", 91 | "System.Reflection.TypeExtensions": "4.1.0", 92 | "System.Resources.ResourceManager": "4.0.1", 93 | "System.Runtime": "4.1.0", 94 | "System.Runtime.Extensions": "4.1.0", 95 | "System.Runtime.InteropServices": "4.1.0", 96 | "System.Threading": "4.0.11" 97 | } 98 | }, 99 | "Microsoft.NETCore.Platforms": { 100 | "type": "Transitive", 101 | "resolved": "1.1.0", 102 | "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" 103 | }, 104 | "Microsoft.NETCore.Targets": { 105 | "type": "Transitive", 106 | "resolved": "1.1.0", 107 | "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" 108 | }, 109 | "Microsoft.TestPlatform.ObjectModel": { 110 | "type": "Transitive", 111 | "resolved": "17.1.0", 112 | "contentHash": "OMo/FYnKGy3lZEK0gfitskRM3ga/YBt6MyCyFPq0xNLeybGOQ6HnYNAAvzyePo5WPuMiw3LX+HiuRWNjnas1fA==", 113 | "dependencies": { 114 | "NuGet.Frameworks": "5.11.0", 115 | "System.Reflection.Metadata": "1.6.0" 116 | } 117 | }, 118 | "Microsoft.TestPlatform.TestHost": { 119 | "type": "Transitive", 120 | "resolved": "17.1.0", 121 | "contentHash": "JS0JDLniDhIzkSPLHz7N/x1CG8ywJOtwInFDYA3KQvbz+ojGoT5MT2YDVReL1b86zmNRV8339vsTSm/zh0RcMg==", 122 | "dependencies": { 123 | "Microsoft.TestPlatform.ObjectModel": "17.1.0", 124 | "Newtonsoft.Json": "9.0.1" 125 | } 126 | }, 127 | "Microsoft.Win32.Primitives": { 128 | "type": "Transitive", 129 | "resolved": "4.3.0", 130 | "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", 131 | "dependencies": { 132 | "Microsoft.NETCore.Platforms": "1.1.0", 133 | "Microsoft.NETCore.Targets": "1.1.0", 134 | "System.Runtime": "4.3.0" 135 | } 136 | }, 137 | "NETStandard.Library": { 138 | "type": "Transitive", 139 | "resolved": "1.6.1", 140 | "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", 141 | "dependencies": { 142 | "Microsoft.NETCore.Platforms": "1.1.0", 143 | "Microsoft.Win32.Primitives": "4.3.0", 144 | "System.AppContext": "4.3.0", 145 | "System.Collections": "4.3.0", 146 | "System.Collections.Concurrent": "4.3.0", 147 | "System.Console": "4.3.0", 148 | "System.Diagnostics.Debug": "4.3.0", 149 | "System.Diagnostics.Tools": "4.3.0", 150 | "System.Diagnostics.Tracing": "4.3.0", 151 | "System.Globalization": "4.3.0", 152 | "System.Globalization.Calendars": "4.3.0", 153 | "System.IO": "4.3.0", 154 | "System.IO.Compression": "4.3.0", 155 | "System.IO.Compression.ZipFile": "4.3.0", 156 | "System.IO.FileSystem": "4.3.0", 157 | "System.IO.FileSystem.Primitives": "4.3.0", 158 | "System.Linq": "4.3.0", 159 | "System.Linq.Expressions": "4.3.0", 160 | "System.Net.Http": "4.3.0", 161 | "System.Net.Primitives": "4.3.0", 162 | "System.Net.Sockets": "4.3.0", 163 | "System.ObjectModel": "4.3.0", 164 | "System.Reflection": "4.3.0", 165 | "System.Reflection.Extensions": "4.3.0", 166 | "System.Reflection.Primitives": "4.3.0", 167 | "System.Resources.ResourceManager": "4.3.0", 168 | "System.Runtime": "4.3.0", 169 | "System.Runtime.Extensions": "4.3.0", 170 | "System.Runtime.Handles": "4.3.0", 171 | "System.Runtime.InteropServices": "4.3.0", 172 | "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", 173 | "System.Runtime.Numerics": "4.3.0", 174 | "System.Security.Cryptography.Algorithms": "4.3.0", 175 | "System.Security.Cryptography.Encoding": "4.3.0", 176 | "System.Security.Cryptography.Primitives": "4.3.0", 177 | "System.Security.Cryptography.X509Certificates": "4.3.0", 178 | "System.Text.Encoding": "4.3.0", 179 | "System.Text.Encoding.Extensions": "4.3.0", 180 | "System.Text.RegularExpressions": "4.3.0", 181 | "System.Threading": "4.3.0", 182 | "System.Threading.Tasks": "4.3.0", 183 | "System.Threading.Timer": "4.3.0", 184 | "System.Xml.ReaderWriter": "4.3.0", 185 | "System.Xml.XDocument": "4.3.0" 186 | } 187 | }, 188 | "Newtonsoft.Json": { 189 | "type": "Transitive", 190 | "resolved": "9.0.1", 191 | "contentHash": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", 192 | "dependencies": { 193 | "Microsoft.CSharp": "4.0.1", 194 | "System.Collections": "4.0.11", 195 | "System.Diagnostics.Debug": "4.0.11", 196 | "System.Dynamic.Runtime": "4.0.11", 197 | "System.Globalization": "4.0.11", 198 | "System.IO": "4.1.0", 199 | "System.Linq": "4.1.0", 200 | "System.Linq.Expressions": "4.1.0", 201 | "System.ObjectModel": "4.0.12", 202 | "System.Reflection": "4.1.0", 203 | "System.Reflection.Extensions": "4.0.1", 204 | "System.Resources.ResourceManager": "4.0.1", 205 | "System.Runtime": "4.1.0", 206 | "System.Runtime.Extensions": "4.1.0", 207 | "System.Runtime.Serialization.Primitives": "4.1.1", 208 | "System.Text.Encoding": "4.0.11", 209 | "System.Text.Encoding.Extensions": "4.0.11", 210 | "System.Text.RegularExpressions": "4.1.0", 211 | "System.Threading": "4.0.11", 212 | "System.Threading.Tasks": "4.0.11", 213 | "System.Xml.ReaderWriter": "4.0.11", 214 | "System.Xml.XDocument": "4.0.11" 215 | } 216 | }, 217 | "NuGet.Frameworks": { 218 | "type": "Transitive", 219 | "resolved": "5.11.0", 220 | "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" 221 | }, 222 | "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 223 | "type": "Transitive", 224 | "resolved": "4.3.0", 225 | "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" 226 | }, 227 | "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 228 | "type": "Transitive", 229 | "resolved": "4.3.0", 230 | "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" 231 | }, 232 | "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 233 | "type": "Transitive", 234 | "resolved": "4.3.0", 235 | "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" 236 | }, 237 | "runtime.native.System": { 238 | "type": "Transitive", 239 | "resolved": "4.3.0", 240 | "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", 241 | "dependencies": { 242 | "Microsoft.NETCore.Platforms": "1.1.0", 243 | "Microsoft.NETCore.Targets": "1.1.0" 244 | } 245 | }, 246 | "runtime.native.System.IO.Compression": { 247 | "type": "Transitive", 248 | "resolved": "4.3.0", 249 | "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", 250 | "dependencies": { 251 | "Microsoft.NETCore.Platforms": "1.1.0", 252 | "Microsoft.NETCore.Targets": "1.1.0" 253 | } 254 | }, 255 | "runtime.native.System.Net.Http": { 256 | "type": "Transitive", 257 | "resolved": "4.3.0", 258 | "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", 259 | "dependencies": { 260 | "Microsoft.NETCore.Platforms": "1.1.0", 261 | "Microsoft.NETCore.Targets": "1.1.0" 262 | } 263 | }, 264 | "runtime.native.System.Security.Cryptography.Apple": { 265 | "type": "Transitive", 266 | "resolved": "4.3.0", 267 | "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", 268 | "dependencies": { 269 | "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" 270 | } 271 | }, 272 | "runtime.native.System.Security.Cryptography.OpenSsl": { 273 | "type": "Transitive", 274 | "resolved": "4.3.0", 275 | "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", 276 | "dependencies": { 277 | "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 278 | "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 279 | "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 280 | "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 281 | "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 282 | "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 283 | "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 284 | "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 285 | "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", 286 | "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 287 | } 288 | }, 289 | "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 290 | "type": "Transitive", 291 | "resolved": "4.3.0", 292 | "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" 293 | }, 294 | "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 295 | "type": "Transitive", 296 | "resolved": "4.3.0", 297 | "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" 298 | }, 299 | "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { 300 | "type": "Transitive", 301 | "resolved": "4.3.0", 302 | "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" 303 | }, 304 | "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 305 | "type": "Transitive", 306 | "resolved": "4.3.0", 307 | "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" 308 | }, 309 | "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 310 | "type": "Transitive", 311 | "resolved": "4.3.0", 312 | "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" 313 | }, 314 | "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 315 | "type": "Transitive", 316 | "resolved": "4.3.0", 317 | "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" 318 | }, 319 | "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 320 | "type": "Transitive", 321 | "resolved": "4.3.0", 322 | "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" 323 | }, 324 | "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { 325 | "type": "Transitive", 326 | "resolved": "4.3.0", 327 | "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" 328 | }, 329 | "System.AppContext": { 330 | "type": "Transitive", 331 | "resolved": "4.3.0", 332 | "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", 333 | "dependencies": { 334 | "System.Runtime": "4.3.0" 335 | } 336 | }, 337 | "System.Buffers": { 338 | "type": "Transitive", 339 | "resolved": "4.3.0", 340 | "contentHash": "ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==", 341 | "dependencies": { 342 | "System.Diagnostics.Debug": "4.3.0", 343 | "System.Diagnostics.Tracing": "4.3.0", 344 | "System.Resources.ResourceManager": "4.3.0", 345 | "System.Runtime": "4.3.0", 346 | "System.Threading": "4.3.0" 347 | } 348 | }, 349 | "System.Collections": { 350 | "type": "Transitive", 351 | "resolved": "4.3.0", 352 | "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", 353 | "dependencies": { 354 | "Microsoft.NETCore.Platforms": "1.1.0", 355 | "Microsoft.NETCore.Targets": "1.1.0", 356 | "System.Runtime": "4.3.0" 357 | } 358 | }, 359 | "System.Collections.Concurrent": { 360 | "type": "Transitive", 361 | "resolved": "4.3.0", 362 | "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", 363 | "dependencies": { 364 | "System.Collections": "4.3.0", 365 | "System.Diagnostics.Debug": "4.3.0", 366 | "System.Diagnostics.Tracing": "4.3.0", 367 | "System.Globalization": "4.3.0", 368 | "System.Reflection": "4.3.0", 369 | "System.Resources.ResourceManager": "4.3.0", 370 | "System.Runtime": "4.3.0", 371 | "System.Runtime.Extensions": "4.3.0", 372 | "System.Threading": "4.3.0", 373 | "System.Threading.Tasks": "4.3.0" 374 | } 375 | }, 376 | "System.Collections.Immutable": { 377 | "type": "Transitive", 378 | "resolved": "6.0.0", 379 | "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", 380 | "dependencies": { 381 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 382 | } 383 | }, 384 | "System.Collections.NonGeneric": { 385 | "type": "Transitive", 386 | "resolved": "4.3.0", 387 | "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", 388 | "dependencies": { 389 | "System.Diagnostics.Debug": "4.3.0", 390 | "System.Globalization": "4.3.0", 391 | "System.Resources.ResourceManager": "4.3.0", 392 | "System.Runtime": "4.3.0", 393 | "System.Runtime.Extensions": "4.3.0", 394 | "System.Threading": "4.3.0" 395 | } 396 | }, 397 | "System.Collections.Specialized": { 398 | "type": "Transitive", 399 | "resolved": "4.3.0", 400 | "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", 401 | "dependencies": { 402 | "System.Collections.NonGeneric": "4.3.0", 403 | "System.Globalization": "4.3.0", 404 | "System.Globalization.Extensions": "4.3.0", 405 | "System.Resources.ResourceManager": "4.3.0", 406 | "System.Runtime": "4.3.0", 407 | "System.Runtime.Extensions": "4.3.0", 408 | "System.Threading": "4.3.0" 409 | } 410 | }, 411 | "System.ComponentModel": { 412 | "type": "Transitive", 413 | "resolved": "4.3.0", 414 | "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", 415 | "dependencies": { 416 | "System.Runtime": "4.3.0" 417 | } 418 | }, 419 | "System.ComponentModel.Primitives": { 420 | "type": "Transitive", 421 | "resolved": "4.3.0", 422 | "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", 423 | "dependencies": { 424 | "System.ComponentModel": "4.3.0", 425 | "System.Resources.ResourceManager": "4.3.0", 426 | "System.Runtime": "4.3.0" 427 | } 428 | }, 429 | "System.ComponentModel.TypeConverter": { 430 | "type": "Transitive", 431 | "resolved": "4.3.0", 432 | "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", 433 | "dependencies": { 434 | "System.Collections": "4.3.0", 435 | "System.Collections.NonGeneric": "4.3.0", 436 | "System.Collections.Specialized": "4.3.0", 437 | "System.ComponentModel": "4.3.0", 438 | "System.ComponentModel.Primitives": "4.3.0", 439 | "System.Globalization": "4.3.0", 440 | "System.Linq": "4.3.0", 441 | "System.Reflection": "4.3.0", 442 | "System.Reflection.Extensions": "4.3.0", 443 | "System.Reflection.Primitives": "4.3.0", 444 | "System.Reflection.TypeExtensions": "4.3.0", 445 | "System.Resources.ResourceManager": "4.3.0", 446 | "System.Runtime": "4.3.0", 447 | "System.Runtime.Extensions": "4.3.0", 448 | "System.Threading": "4.3.0" 449 | } 450 | }, 451 | "System.Console": { 452 | "type": "Transitive", 453 | "resolved": "4.3.0", 454 | "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", 455 | "dependencies": { 456 | "Microsoft.NETCore.Platforms": "1.1.0", 457 | "Microsoft.NETCore.Targets": "1.1.0", 458 | "System.IO": "4.3.0", 459 | "System.Runtime": "4.3.0", 460 | "System.Text.Encoding": "4.3.0" 461 | } 462 | }, 463 | "System.Diagnostics.Debug": { 464 | "type": "Transitive", 465 | "resolved": "4.3.0", 466 | "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", 467 | "dependencies": { 468 | "Microsoft.NETCore.Platforms": "1.1.0", 469 | "Microsoft.NETCore.Targets": "1.1.0", 470 | "System.Runtime": "4.3.0" 471 | } 472 | }, 473 | "System.Diagnostics.DiagnosticSource": { 474 | "type": "Transitive", 475 | "resolved": "4.3.0", 476 | "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", 477 | "dependencies": { 478 | "System.Collections": "4.3.0", 479 | "System.Diagnostics.Tracing": "4.3.0", 480 | "System.Reflection": "4.3.0", 481 | "System.Runtime": "4.3.0", 482 | "System.Threading": "4.3.0" 483 | } 484 | }, 485 | "System.Diagnostics.Tools": { 486 | "type": "Transitive", 487 | "resolved": "4.3.0", 488 | "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", 489 | "dependencies": { 490 | "Microsoft.NETCore.Platforms": "1.1.0", 491 | "Microsoft.NETCore.Targets": "1.1.0", 492 | "System.Runtime": "4.3.0" 493 | } 494 | }, 495 | "System.Diagnostics.TraceSource": { 496 | "type": "Transitive", 497 | "resolved": "4.3.0", 498 | "contentHash": "VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==", 499 | "dependencies": { 500 | "Microsoft.NETCore.Platforms": "1.1.0", 501 | "System.Collections": "4.3.0", 502 | "System.Diagnostics.Debug": "4.3.0", 503 | "System.Globalization": "4.3.0", 504 | "System.Resources.ResourceManager": "4.3.0", 505 | "System.Runtime": "4.3.0", 506 | "System.Runtime.Extensions": "4.3.0", 507 | "System.Threading": "4.3.0", 508 | "runtime.native.System": "4.3.0" 509 | } 510 | }, 511 | "System.Diagnostics.Tracing": { 512 | "type": "Transitive", 513 | "resolved": "4.3.0", 514 | "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", 515 | "dependencies": { 516 | "Microsoft.NETCore.Platforms": "1.1.0", 517 | "Microsoft.NETCore.Targets": "1.1.0", 518 | "System.Runtime": "4.3.0" 519 | } 520 | }, 521 | "System.Dynamic.Runtime": { 522 | "type": "Transitive", 523 | "resolved": "4.3.0", 524 | "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", 525 | "dependencies": { 526 | "System.Collections": "4.3.0", 527 | "System.Diagnostics.Debug": "4.3.0", 528 | "System.Linq": "4.3.0", 529 | "System.Linq.Expressions": "4.3.0", 530 | "System.ObjectModel": "4.3.0", 531 | "System.Reflection": "4.3.0", 532 | "System.Reflection.Emit": "4.3.0", 533 | "System.Reflection.Emit.ILGeneration": "4.3.0", 534 | "System.Reflection.Primitives": "4.3.0", 535 | "System.Reflection.TypeExtensions": "4.3.0", 536 | "System.Resources.ResourceManager": "4.3.0", 537 | "System.Runtime": "4.3.0", 538 | "System.Runtime.Extensions": "4.3.0", 539 | "System.Threading": "4.3.0" 540 | } 541 | }, 542 | "System.Globalization": { 543 | "type": "Transitive", 544 | "resolved": "4.3.0", 545 | "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", 546 | "dependencies": { 547 | "Microsoft.NETCore.Platforms": "1.1.0", 548 | "Microsoft.NETCore.Targets": "1.1.0", 549 | "System.Runtime": "4.3.0" 550 | } 551 | }, 552 | "System.Globalization.Calendars": { 553 | "type": "Transitive", 554 | "resolved": "4.3.0", 555 | "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", 556 | "dependencies": { 557 | "Microsoft.NETCore.Platforms": "1.1.0", 558 | "Microsoft.NETCore.Targets": "1.1.0", 559 | "System.Globalization": "4.3.0", 560 | "System.Runtime": "4.3.0" 561 | } 562 | }, 563 | "System.Globalization.Extensions": { 564 | "type": "Transitive", 565 | "resolved": "4.3.0", 566 | "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", 567 | "dependencies": { 568 | "Microsoft.NETCore.Platforms": "1.1.0", 569 | "System.Globalization": "4.3.0", 570 | "System.Resources.ResourceManager": "4.3.0", 571 | "System.Runtime": "4.3.0", 572 | "System.Runtime.Extensions": "4.3.0", 573 | "System.Runtime.InteropServices": "4.3.0" 574 | } 575 | }, 576 | "System.IO": { 577 | "type": "Transitive", 578 | "resolved": "4.3.0", 579 | "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", 580 | "dependencies": { 581 | "Microsoft.NETCore.Platforms": "1.1.0", 582 | "Microsoft.NETCore.Targets": "1.1.0", 583 | "System.Runtime": "4.3.0", 584 | "System.Text.Encoding": "4.3.0", 585 | "System.Threading.Tasks": "4.3.0" 586 | } 587 | }, 588 | "System.IO.Compression": { 589 | "type": "Transitive", 590 | "resolved": "4.3.0", 591 | "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", 592 | "dependencies": { 593 | "Microsoft.NETCore.Platforms": "1.1.0", 594 | "System.Buffers": "4.3.0", 595 | "System.Collections": "4.3.0", 596 | "System.Diagnostics.Debug": "4.3.0", 597 | "System.IO": "4.3.0", 598 | "System.Resources.ResourceManager": "4.3.0", 599 | "System.Runtime": "4.3.0", 600 | "System.Runtime.Extensions": "4.3.0", 601 | "System.Runtime.Handles": "4.3.0", 602 | "System.Runtime.InteropServices": "4.3.0", 603 | "System.Text.Encoding": "4.3.0", 604 | "System.Threading": "4.3.0", 605 | "System.Threading.Tasks": "4.3.0", 606 | "runtime.native.System": "4.3.0", 607 | "runtime.native.System.IO.Compression": "4.3.0" 608 | } 609 | }, 610 | "System.IO.Compression.ZipFile": { 611 | "type": "Transitive", 612 | "resolved": "4.3.0", 613 | "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", 614 | "dependencies": { 615 | "System.Buffers": "4.3.0", 616 | "System.IO": "4.3.0", 617 | "System.IO.Compression": "4.3.0", 618 | "System.IO.FileSystem": "4.3.0", 619 | "System.IO.FileSystem.Primitives": "4.3.0", 620 | "System.Resources.ResourceManager": "4.3.0", 621 | "System.Runtime": "4.3.0", 622 | "System.Runtime.Extensions": "4.3.0", 623 | "System.Text.Encoding": "4.3.0" 624 | } 625 | }, 626 | "System.IO.FileSystem": { 627 | "type": "Transitive", 628 | "resolved": "4.3.0", 629 | "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", 630 | "dependencies": { 631 | "Microsoft.NETCore.Platforms": "1.1.0", 632 | "Microsoft.NETCore.Targets": "1.1.0", 633 | "System.IO": "4.3.0", 634 | "System.IO.FileSystem.Primitives": "4.3.0", 635 | "System.Runtime": "4.3.0", 636 | "System.Runtime.Handles": "4.3.0", 637 | "System.Text.Encoding": "4.3.0", 638 | "System.Threading.Tasks": "4.3.0" 639 | } 640 | }, 641 | "System.IO.FileSystem.Primitives": { 642 | "type": "Transitive", 643 | "resolved": "4.3.0", 644 | "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", 645 | "dependencies": { 646 | "System.Runtime": "4.3.0" 647 | } 648 | }, 649 | "System.Linq": { 650 | "type": "Transitive", 651 | "resolved": "4.3.0", 652 | "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", 653 | "dependencies": { 654 | "System.Collections": "4.3.0", 655 | "System.Diagnostics.Debug": "4.3.0", 656 | "System.Resources.ResourceManager": "4.3.0", 657 | "System.Runtime": "4.3.0", 658 | "System.Runtime.Extensions": "4.3.0" 659 | } 660 | }, 661 | "System.Linq.Expressions": { 662 | "type": "Transitive", 663 | "resolved": "4.3.0", 664 | "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", 665 | "dependencies": { 666 | "System.Collections": "4.3.0", 667 | "System.Diagnostics.Debug": "4.3.0", 668 | "System.Globalization": "4.3.0", 669 | "System.IO": "4.3.0", 670 | "System.Linq": "4.3.0", 671 | "System.ObjectModel": "4.3.0", 672 | "System.Reflection": "4.3.0", 673 | "System.Reflection.Emit": "4.3.0", 674 | "System.Reflection.Emit.ILGeneration": "4.3.0", 675 | "System.Reflection.Emit.Lightweight": "4.3.0", 676 | "System.Reflection.Extensions": "4.3.0", 677 | "System.Reflection.Primitives": "4.3.0", 678 | "System.Reflection.TypeExtensions": "4.3.0", 679 | "System.Resources.ResourceManager": "4.3.0", 680 | "System.Runtime": "4.3.0", 681 | "System.Runtime.Extensions": "4.3.0", 682 | "System.Threading": "4.3.0" 683 | } 684 | }, 685 | "System.Net.Http": { 686 | "type": "Transitive", 687 | "resolved": "4.3.0", 688 | "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", 689 | "dependencies": { 690 | "Microsoft.NETCore.Platforms": "1.1.0", 691 | "System.Collections": "4.3.0", 692 | "System.Diagnostics.Debug": "4.3.0", 693 | "System.Diagnostics.DiagnosticSource": "4.3.0", 694 | "System.Diagnostics.Tracing": "4.3.0", 695 | "System.Globalization": "4.3.0", 696 | "System.Globalization.Extensions": "4.3.0", 697 | "System.IO": "4.3.0", 698 | "System.IO.FileSystem": "4.3.0", 699 | "System.Net.Primitives": "4.3.0", 700 | "System.Resources.ResourceManager": "4.3.0", 701 | "System.Runtime": "4.3.0", 702 | "System.Runtime.Extensions": "4.3.0", 703 | "System.Runtime.Handles": "4.3.0", 704 | "System.Runtime.InteropServices": "4.3.0", 705 | "System.Security.Cryptography.Algorithms": "4.3.0", 706 | "System.Security.Cryptography.Encoding": "4.3.0", 707 | "System.Security.Cryptography.OpenSsl": "4.3.0", 708 | "System.Security.Cryptography.Primitives": "4.3.0", 709 | "System.Security.Cryptography.X509Certificates": "4.3.0", 710 | "System.Text.Encoding": "4.3.0", 711 | "System.Threading": "4.3.0", 712 | "System.Threading.Tasks": "4.3.0", 713 | "runtime.native.System": "4.3.0", 714 | "runtime.native.System.Net.Http": "4.3.0", 715 | "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 716 | } 717 | }, 718 | "System.Net.Primitives": { 719 | "type": "Transitive", 720 | "resolved": "4.3.0", 721 | "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", 722 | "dependencies": { 723 | "Microsoft.NETCore.Platforms": "1.1.0", 724 | "Microsoft.NETCore.Targets": "1.1.0", 725 | "System.Runtime": "4.3.0", 726 | "System.Runtime.Handles": "4.3.0" 727 | } 728 | }, 729 | "System.Net.Sockets": { 730 | "type": "Transitive", 731 | "resolved": "4.3.0", 732 | "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", 733 | "dependencies": { 734 | "Microsoft.NETCore.Platforms": "1.1.0", 735 | "Microsoft.NETCore.Targets": "1.1.0", 736 | "System.IO": "4.3.0", 737 | "System.Net.Primitives": "4.3.0", 738 | "System.Runtime": "4.3.0", 739 | "System.Threading.Tasks": "4.3.0" 740 | } 741 | }, 742 | "System.ObjectModel": { 743 | "type": "Transitive", 744 | "resolved": "4.3.0", 745 | "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", 746 | "dependencies": { 747 | "System.Collections": "4.3.0", 748 | "System.Diagnostics.Debug": "4.3.0", 749 | "System.Resources.ResourceManager": "4.3.0", 750 | "System.Runtime": "4.3.0", 751 | "System.Threading": "4.3.0" 752 | } 753 | }, 754 | "System.Reflection": { 755 | "type": "Transitive", 756 | "resolved": "4.3.0", 757 | "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", 758 | "dependencies": { 759 | "Microsoft.NETCore.Platforms": "1.1.0", 760 | "Microsoft.NETCore.Targets": "1.1.0", 761 | "System.IO": "4.3.0", 762 | "System.Reflection.Primitives": "4.3.0", 763 | "System.Runtime": "4.3.0" 764 | } 765 | }, 766 | "System.Reflection.Emit": { 767 | "type": "Transitive", 768 | "resolved": "4.3.0", 769 | "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", 770 | "dependencies": { 771 | "System.IO": "4.3.0", 772 | "System.Reflection": "4.3.0", 773 | "System.Reflection.Emit.ILGeneration": "4.3.0", 774 | "System.Reflection.Primitives": "4.3.0", 775 | "System.Runtime": "4.3.0" 776 | } 777 | }, 778 | "System.Reflection.Emit.ILGeneration": { 779 | "type": "Transitive", 780 | "resolved": "4.3.0", 781 | "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", 782 | "dependencies": { 783 | "System.Reflection": "4.3.0", 784 | "System.Reflection.Primitives": "4.3.0", 785 | "System.Runtime": "4.3.0" 786 | } 787 | }, 788 | "System.Reflection.Emit.Lightweight": { 789 | "type": "Transitive", 790 | "resolved": "4.3.0", 791 | "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", 792 | "dependencies": { 793 | "System.Reflection": "4.3.0", 794 | "System.Reflection.Emit.ILGeneration": "4.3.0", 795 | "System.Reflection.Primitives": "4.3.0", 796 | "System.Runtime": "4.3.0" 797 | } 798 | }, 799 | "System.Reflection.Extensions": { 800 | "type": "Transitive", 801 | "resolved": "4.3.0", 802 | "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", 803 | "dependencies": { 804 | "Microsoft.NETCore.Platforms": "1.1.0", 805 | "Microsoft.NETCore.Targets": "1.1.0", 806 | "System.Reflection": "4.3.0", 807 | "System.Runtime": "4.3.0" 808 | } 809 | }, 810 | "System.Reflection.Metadata": { 811 | "type": "Transitive", 812 | "resolved": "1.6.0", 813 | "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" 814 | }, 815 | "System.Reflection.Primitives": { 816 | "type": "Transitive", 817 | "resolved": "4.3.0", 818 | "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", 819 | "dependencies": { 820 | "Microsoft.NETCore.Platforms": "1.1.0", 821 | "Microsoft.NETCore.Targets": "1.1.0", 822 | "System.Runtime": "4.3.0" 823 | } 824 | }, 825 | "System.Reflection.TypeExtensions": { 826 | "type": "Transitive", 827 | "resolved": "4.3.0", 828 | "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", 829 | "dependencies": { 830 | "System.Reflection": "4.3.0", 831 | "System.Runtime": "4.3.0" 832 | } 833 | }, 834 | "System.Resources.ResourceManager": { 835 | "type": "Transitive", 836 | "resolved": "4.3.0", 837 | "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", 838 | "dependencies": { 839 | "Microsoft.NETCore.Platforms": "1.1.0", 840 | "Microsoft.NETCore.Targets": "1.1.0", 841 | "System.Globalization": "4.3.0", 842 | "System.Reflection": "4.3.0", 843 | "System.Runtime": "4.3.0" 844 | } 845 | }, 846 | "System.Runtime": { 847 | "type": "Transitive", 848 | "resolved": "4.3.0", 849 | "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", 850 | "dependencies": { 851 | "Microsoft.NETCore.Platforms": "1.1.0", 852 | "Microsoft.NETCore.Targets": "1.1.0" 853 | } 854 | }, 855 | "System.Runtime.CompilerServices.Unsafe": { 856 | "type": "Transitive", 857 | "resolved": "6.0.0", 858 | "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" 859 | }, 860 | "System.Runtime.Extensions": { 861 | "type": "Transitive", 862 | "resolved": "4.3.0", 863 | "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", 864 | "dependencies": { 865 | "Microsoft.NETCore.Platforms": "1.1.0", 866 | "Microsoft.NETCore.Targets": "1.1.0", 867 | "System.Runtime": "4.3.0" 868 | } 869 | }, 870 | "System.Runtime.Handles": { 871 | "type": "Transitive", 872 | "resolved": "4.3.0", 873 | "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", 874 | "dependencies": { 875 | "Microsoft.NETCore.Platforms": "1.1.0", 876 | "Microsoft.NETCore.Targets": "1.1.0", 877 | "System.Runtime": "4.3.0" 878 | } 879 | }, 880 | "System.Runtime.InteropServices": { 881 | "type": "Transitive", 882 | "resolved": "4.3.0", 883 | "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", 884 | "dependencies": { 885 | "Microsoft.NETCore.Platforms": "1.1.0", 886 | "Microsoft.NETCore.Targets": "1.1.0", 887 | "System.Reflection": "4.3.0", 888 | "System.Reflection.Primitives": "4.3.0", 889 | "System.Runtime": "4.3.0", 890 | "System.Runtime.Handles": "4.3.0" 891 | } 892 | }, 893 | "System.Runtime.InteropServices.RuntimeInformation": { 894 | "type": "Transitive", 895 | "resolved": "4.3.0", 896 | "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", 897 | "dependencies": { 898 | "System.Reflection": "4.3.0", 899 | "System.Reflection.Extensions": "4.3.0", 900 | "System.Resources.ResourceManager": "4.3.0", 901 | "System.Runtime": "4.3.0", 902 | "System.Runtime.InteropServices": "4.3.0", 903 | "System.Threading": "4.3.0", 904 | "runtime.native.System": "4.3.0" 905 | } 906 | }, 907 | "System.Runtime.Numerics": { 908 | "type": "Transitive", 909 | "resolved": "4.3.0", 910 | "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", 911 | "dependencies": { 912 | "System.Globalization": "4.3.0", 913 | "System.Resources.ResourceManager": "4.3.0", 914 | "System.Runtime": "4.3.0", 915 | "System.Runtime.Extensions": "4.3.0" 916 | } 917 | }, 918 | "System.Runtime.Serialization.Primitives": { 919 | "type": "Transitive", 920 | "resolved": "4.1.1", 921 | "contentHash": "HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==", 922 | "dependencies": { 923 | "System.Resources.ResourceManager": "4.0.1", 924 | "System.Runtime": "4.1.0" 925 | } 926 | }, 927 | "System.Security.Cryptography.Algorithms": { 928 | "type": "Transitive", 929 | "resolved": "4.3.0", 930 | "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", 931 | "dependencies": { 932 | "Microsoft.NETCore.Platforms": "1.1.0", 933 | "System.Collections": "4.3.0", 934 | "System.IO": "4.3.0", 935 | "System.Resources.ResourceManager": "4.3.0", 936 | "System.Runtime": "4.3.0", 937 | "System.Runtime.Extensions": "4.3.0", 938 | "System.Runtime.Handles": "4.3.0", 939 | "System.Runtime.InteropServices": "4.3.0", 940 | "System.Runtime.Numerics": "4.3.0", 941 | "System.Security.Cryptography.Encoding": "4.3.0", 942 | "System.Security.Cryptography.Primitives": "4.3.0", 943 | "System.Text.Encoding": "4.3.0", 944 | "runtime.native.System.Security.Cryptography.Apple": "4.3.0", 945 | "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 946 | } 947 | }, 948 | "System.Security.Cryptography.Cng": { 949 | "type": "Transitive", 950 | "resolved": "4.3.0", 951 | "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", 952 | "dependencies": { 953 | "Microsoft.NETCore.Platforms": "1.1.0", 954 | "System.IO": "4.3.0", 955 | "System.Resources.ResourceManager": "4.3.0", 956 | "System.Runtime": "4.3.0", 957 | "System.Runtime.Extensions": "4.3.0", 958 | "System.Runtime.Handles": "4.3.0", 959 | "System.Runtime.InteropServices": "4.3.0", 960 | "System.Security.Cryptography.Algorithms": "4.3.0", 961 | "System.Security.Cryptography.Encoding": "4.3.0", 962 | "System.Security.Cryptography.Primitives": "4.3.0", 963 | "System.Text.Encoding": "4.3.0" 964 | } 965 | }, 966 | "System.Security.Cryptography.Csp": { 967 | "type": "Transitive", 968 | "resolved": "4.3.0", 969 | "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", 970 | "dependencies": { 971 | "Microsoft.NETCore.Platforms": "1.1.0", 972 | "System.IO": "4.3.0", 973 | "System.Reflection": "4.3.0", 974 | "System.Resources.ResourceManager": "4.3.0", 975 | "System.Runtime": "4.3.0", 976 | "System.Runtime.Extensions": "4.3.0", 977 | "System.Runtime.Handles": "4.3.0", 978 | "System.Runtime.InteropServices": "4.3.0", 979 | "System.Security.Cryptography.Algorithms": "4.3.0", 980 | "System.Security.Cryptography.Encoding": "4.3.0", 981 | "System.Security.Cryptography.Primitives": "4.3.0", 982 | "System.Text.Encoding": "4.3.0", 983 | "System.Threading": "4.3.0" 984 | } 985 | }, 986 | "System.Security.Cryptography.Encoding": { 987 | "type": "Transitive", 988 | "resolved": "4.3.0", 989 | "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", 990 | "dependencies": { 991 | "Microsoft.NETCore.Platforms": "1.1.0", 992 | "System.Collections": "4.3.0", 993 | "System.Collections.Concurrent": "4.3.0", 994 | "System.Linq": "4.3.0", 995 | "System.Resources.ResourceManager": "4.3.0", 996 | "System.Runtime": "4.3.0", 997 | "System.Runtime.Extensions": "4.3.0", 998 | "System.Runtime.Handles": "4.3.0", 999 | "System.Runtime.InteropServices": "4.3.0", 1000 | "System.Security.Cryptography.Primitives": "4.3.0", 1001 | "System.Text.Encoding": "4.3.0", 1002 | "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 1003 | } 1004 | }, 1005 | "System.Security.Cryptography.OpenSsl": { 1006 | "type": "Transitive", 1007 | "resolved": "4.3.0", 1008 | "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", 1009 | "dependencies": { 1010 | "System.Collections": "4.3.0", 1011 | "System.IO": "4.3.0", 1012 | "System.Resources.ResourceManager": "4.3.0", 1013 | "System.Runtime": "4.3.0", 1014 | "System.Runtime.Extensions": "4.3.0", 1015 | "System.Runtime.Handles": "4.3.0", 1016 | "System.Runtime.InteropServices": "4.3.0", 1017 | "System.Runtime.Numerics": "4.3.0", 1018 | "System.Security.Cryptography.Algorithms": "4.3.0", 1019 | "System.Security.Cryptography.Encoding": "4.3.0", 1020 | "System.Security.Cryptography.Primitives": "4.3.0", 1021 | "System.Text.Encoding": "4.3.0", 1022 | "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 1023 | } 1024 | }, 1025 | "System.Security.Cryptography.Primitives": { 1026 | "type": "Transitive", 1027 | "resolved": "4.3.0", 1028 | "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", 1029 | "dependencies": { 1030 | "System.Diagnostics.Debug": "4.3.0", 1031 | "System.Globalization": "4.3.0", 1032 | "System.IO": "4.3.0", 1033 | "System.Resources.ResourceManager": "4.3.0", 1034 | "System.Runtime": "4.3.0", 1035 | "System.Threading": "4.3.0", 1036 | "System.Threading.Tasks": "4.3.0" 1037 | } 1038 | }, 1039 | "System.Security.Cryptography.X509Certificates": { 1040 | "type": "Transitive", 1041 | "resolved": "4.3.0", 1042 | "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", 1043 | "dependencies": { 1044 | "Microsoft.NETCore.Platforms": "1.1.0", 1045 | "System.Collections": "4.3.0", 1046 | "System.Diagnostics.Debug": "4.3.0", 1047 | "System.Globalization": "4.3.0", 1048 | "System.Globalization.Calendars": "4.3.0", 1049 | "System.IO": "4.3.0", 1050 | "System.IO.FileSystem": "4.3.0", 1051 | "System.IO.FileSystem.Primitives": "4.3.0", 1052 | "System.Resources.ResourceManager": "4.3.0", 1053 | "System.Runtime": "4.3.0", 1054 | "System.Runtime.Extensions": "4.3.0", 1055 | "System.Runtime.Handles": "4.3.0", 1056 | "System.Runtime.InteropServices": "4.3.0", 1057 | "System.Runtime.Numerics": "4.3.0", 1058 | "System.Security.Cryptography.Algorithms": "4.3.0", 1059 | "System.Security.Cryptography.Cng": "4.3.0", 1060 | "System.Security.Cryptography.Csp": "4.3.0", 1061 | "System.Security.Cryptography.Encoding": "4.3.0", 1062 | "System.Security.Cryptography.OpenSsl": "4.3.0", 1063 | "System.Security.Cryptography.Primitives": "4.3.0", 1064 | "System.Text.Encoding": "4.3.0", 1065 | "System.Threading": "4.3.0", 1066 | "runtime.native.System": "4.3.0", 1067 | "runtime.native.System.Net.Http": "4.3.0", 1068 | "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" 1069 | } 1070 | }, 1071 | "System.Text.Encoding": { 1072 | "type": "Transitive", 1073 | "resolved": "4.3.0", 1074 | "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", 1075 | "dependencies": { 1076 | "Microsoft.NETCore.Platforms": "1.1.0", 1077 | "Microsoft.NETCore.Targets": "1.1.0", 1078 | "System.Runtime": "4.3.0" 1079 | } 1080 | }, 1081 | "System.Text.Encoding.Extensions": { 1082 | "type": "Transitive", 1083 | "resolved": "4.3.0", 1084 | "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", 1085 | "dependencies": { 1086 | "Microsoft.NETCore.Platforms": "1.1.0", 1087 | "Microsoft.NETCore.Targets": "1.1.0", 1088 | "System.Runtime": "4.3.0", 1089 | "System.Text.Encoding": "4.3.0" 1090 | } 1091 | }, 1092 | "System.Text.RegularExpressions": { 1093 | "type": "Transitive", 1094 | "resolved": "4.3.0", 1095 | "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", 1096 | "dependencies": { 1097 | "System.Runtime": "4.3.0" 1098 | } 1099 | }, 1100 | "System.Threading": { 1101 | "type": "Transitive", 1102 | "resolved": "4.3.0", 1103 | "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", 1104 | "dependencies": { 1105 | "System.Runtime": "4.3.0", 1106 | "System.Threading.Tasks": "4.3.0" 1107 | } 1108 | }, 1109 | "System.Threading.Tasks": { 1110 | "type": "Transitive", 1111 | "resolved": "4.3.0", 1112 | "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", 1113 | "dependencies": { 1114 | "Microsoft.NETCore.Platforms": "1.1.0", 1115 | "Microsoft.NETCore.Targets": "1.1.0", 1116 | "System.Runtime": "4.3.0" 1117 | } 1118 | }, 1119 | "System.Threading.Tasks.Extensions": { 1120 | "type": "Transitive", 1121 | "resolved": "4.5.4", 1122 | "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" 1123 | }, 1124 | "System.Threading.Timer": { 1125 | "type": "Transitive", 1126 | "resolved": "4.3.0", 1127 | "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", 1128 | "dependencies": { 1129 | "Microsoft.NETCore.Platforms": "1.1.0", 1130 | "Microsoft.NETCore.Targets": "1.1.0", 1131 | "System.Runtime": "4.3.0" 1132 | } 1133 | }, 1134 | "System.ValueTuple": { 1135 | "type": "Transitive", 1136 | "resolved": "4.5.0", 1137 | "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" 1138 | }, 1139 | "System.Xml.ReaderWriter": { 1140 | "type": "Transitive", 1141 | "resolved": "4.3.0", 1142 | "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", 1143 | "dependencies": { 1144 | "System.Collections": "4.3.0", 1145 | "System.Diagnostics.Debug": "4.3.0", 1146 | "System.Globalization": "4.3.0", 1147 | "System.IO": "4.3.0", 1148 | "System.IO.FileSystem": "4.3.0", 1149 | "System.IO.FileSystem.Primitives": "4.3.0", 1150 | "System.Resources.ResourceManager": "4.3.0", 1151 | "System.Runtime": "4.3.0", 1152 | "System.Runtime.Extensions": "4.3.0", 1153 | "System.Runtime.InteropServices": "4.3.0", 1154 | "System.Text.Encoding": "4.3.0", 1155 | "System.Text.Encoding.Extensions": "4.3.0", 1156 | "System.Text.RegularExpressions": "4.3.0", 1157 | "System.Threading.Tasks": "4.3.0", 1158 | "System.Threading.Tasks.Extensions": "4.3.0" 1159 | } 1160 | }, 1161 | "System.Xml.XDocument": { 1162 | "type": "Transitive", 1163 | "resolved": "4.3.0", 1164 | "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", 1165 | "dependencies": { 1166 | "System.Collections": "4.3.0", 1167 | "System.Diagnostics.Debug": "4.3.0", 1168 | "System.Diagnostics.Tools": "4.3.0", 1169 | "System.Globalization": "4.3.0", 1170 | "System.IO": "4.3.0", 1171 | "System.Reflection": "4.3.0", 1172 | "System.Resources.ResourceManager": "4.3.0", 1173 | "System.Runtime": "4.3.0", 1174 | "System.Runtime.Extensions": "4.3.0", 1175 | "System.Text.Encoding": "4.3.0", 1176 | "System.Threading": "4.3.0", 1177 | "System.Xml.ReaderWriter": "4.3.0" 1178 | } 1179 | }, 1180 | "System.Xml.XmlDocument": { 1181 | "type": "Transitive", 1182 | "resolved": "4.3.0", 1183 | "contentHash": "lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", 1184 | "dependencies": { 1185 | "System.Collections": "4.3.0", 1186 | "System.Diagnostics.Debug": "4.3.0", 1187 | "System.Globalization": "4.3.0", 1188 | "System.IO": "4.3.0", 1189 | "System.Resources.ResourceManager": "4.3.0", 1190 | "System.Runtime": "4.3.0", 1191 | "System.Runtime.Extensions": "4.3.0", 1192 | "System.Text.Encoding": "4.3.0", 1193 | "System.Threading": "4.3.0", 1194 | "System.Xml.ReaderWriter": "4.3.0" 1195 | } 1196 | }, 1197 | "xunit.abstractions": { 1198 | "type": "Transitive", 1199 | "resolved": "2.0.3", 1200 | "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" 1201 | }, 1202 | "xunit.analyzers": { 1203 | "type": "Transitive", 1204 | "resolved": "0.10.0", 1205 | "contentHash": "4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==" 1206 | }, 1207 | "xunit.assert": { 1208 | "type": "Transitive", 1209 | "resolved": "2.4.1", 1210 | "contentHash": "O/Oe0BS5RmSsM+LQOb041TzuPo5MdH2Rov+qXGS37X+KFG1Hxz7kopYklM5+1Y+tRGeXrOx5+Xne1RuqLFQoyQ==", 1211 | "dependencies": { 1212 | "NETStandard.Library": "1.6.1" 1213 | } 1214 | }, 1215 | "xunit.core": { 1216 | "type": "Transitive", 1217 | "resolved": "2.4.1", 1218 | "contentHash": "Zsj5OMU6JasNGERXZy8s72+pcheG6Q15atS5XpZXqAtULuyQiQ6XNnUsp1gyfC6WgqScqMvySiEHmHcOG6Eg0Q==", 1219 | "dependencies": { 1220 | "xunit.extensibility.core": "[2.4.1]", 1221 | "xunit.extensibility.execution": "[2.4.1]" 1222 | } 1223 | }, 1224 | "xunit.extensibility.core": { 1225 | "type": "Transitive", 1226 | "resolved": "2.4.1", 1227 | "contentHash": "yKZKm/8QNZnBnGZFD9SewkllHBiK0DThybQD/G4PiAmQjKtEZyHi6ET70QPU9KtSMJGRYS6Syk7EyR2EVDU4Kg==", 1228 | "dependencies": { 1229 | "NETStandard.Library": "1.6.1", 1230 | "xunit.abstractions": "2.0.3" 1231 | } 1232 | }, 1233 | "xunit.extensibility.execution": { 1234 | "type": "Transitive", 1235 | "resolved": "2.4.1", 1236 | "contentHash": "7e/1jqBpcb7frLkB6XDrHCGXAbKN4Rtdb88epYxCSRQuZDRW8UtTfdTEVpdTl8s4T56e07hOBVd4G0OdCxIY2A==", 1237 | "dependencies": { 1238 | "NETStandard.Library": "1.6.1", 1239 | "xunit.extensibility.core": "[2.4.1]" 1240 | } 1241 | }, 1242 | "Aptacode.StateNet": { 1243 | "type": "Project", 1244 | "dependencies": { 1245 | "Aptacode.Expressions": "1.0.13", 1246 | "System.Collections.Immutable": "6.0.0", 1247 | "System.ValueTuple": "4.5.0" 1248 | } 1249 | } 1250 | } 1251 | } 1252 | } -------------------------------------------------------------------------------- /StateNet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StateNet", "StateNet\StateNet.csproj", "{E77792B2-6683-4F76-95FB-3AA79F0BF1A8}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StateNet.Tests", "StateNet.Tests\StateNet.Tests.csproj", "{3424B55B-9FB2-43E6-825B-671F4DE3F1E0}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E77792B2-6683-4F76-95FB-3AA79F0BF1A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {E77792B2-6683-4F76-95FB-3AA79F0BF1A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {E77792B2-6683-4F76-95FB-3AA79F0BF1A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {E77792B2-6683-4F76-95FB-3AA79F0BF1A8}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {3424B55B-9FB2-43E6-825B-671F4DE3F1E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {3424B55B-9FB2-43E6-825B-671F4DE3F1E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {3424B55B-9FB2-43E6-825B-671F4DE3F1E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {3424B55B-9FB2-43E6-825B-671F4DE3F1E0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {BB33CEE6-5D6E-4C2E-A020-433C5B26A297} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /StateNet/Documentation/core_classes.md: -------------------------------------------------------------------------------- 1 | ### `StateNetwork` 2 | 3 | The data structure containing each state, input and connection between them. 4 | 5 | ### `StateNetworkEditor` 6 | 7 | Allows a user to modify the StateNetwork by adding/removing States, Inputs and Connections. 8 | 9 | ### `Input` 10 | 11 | Applied to the StateNetEngine to cause a transition from the current state to a target state. 12 | 13 | ### `State` 14 | 15 | A node in the network, each state has a list of output connections. 16 | 17 | ### `Connection` 18 | 19 | An edge in the network. Each connection has a Source State, Input, Target State and Connection Weight. 20 | When an input is applied to the StateNetEngine each connection whose source state and input matches the 21 | Engines Current state and applied input will have their ConnectionWeight evaluated and collected in a ConnectionDistribution. 22 | 23 | ### `ConnectionWeight` 24 | 25 | An expression which searches the EngineHistory at runtime to determine the weight of a connection. 26 | The expression can be a static weight such as '0' or '1' or a dynamic weight such as 'StateVisitCount("D2") >= 2 ? 1 : 0'. 27 | 28 | ### `ConnectionDistribution` 29 | 30 | A collection of Connections and their evaluated weights. 31 | 32 | ### `ConnectionChooser` 33 | 34 | Takes a ConnectionDistribution and randomly chooses a connection influenced by its weight. 35 | 36 | ### `StateNetEngine` 37 | 38 | Traverses the StateNetwork at runtime based on the inputs the users apply. 39 | 40 | ### `EngineHistory` 41 | 42 | Records each transition for the StateNetwork. -------------------------------------------------------------------------------- /StateNet/Documentation/game_playing_bot_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/StateNet/Documentation/game_playing_bot_machine.png -------------------------------------------------------------------------------- /StateNet/Documentation/planned_features.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/StateNet/Documentation/planned_features.md -------------------------------------------------------------------------------- /StateNet/Documentation/sample_project.md: -------------------------------------------------------------------------------- 1 | ## StateNet overview 2 | 3 | This library allows a user to configure a network describing the flow through states in their application and then traverse the network at runtime in response to the inputs generated by their application. 4 | 5 | Where a standard Finite State Machine (FSM) creates one link between two states for a given input, StateNet allows multiple connections for a single input between two states. These connections each have an expression which gives a weight when evaluated at runtime based on the previous inputs applied and states visited. The Engine randomly chooses one of these connections influenced by its weight and transitions into the selected connection's target state. 6 | 7 | The network's States, Inputs and Connections can be modified at runtime if the user wishes. 8 | 9 | ## Sample Project 10 | 11 | Consider a generic automation tool - lets call it ABot for this project. A simple version of ABot might deal with one (linear) task at a time. But when you want ABot to try and immitate a user (for testing, performance-evaluation, bot-detection-prevention, or any other purpose), strictly linear behaviours are often no longer enough. 12 | 13 | For simplicity, lets make ABot a less generic game-playing automaton that follows this set of rules: 14 | 15 | ![alt text](game_playing_bot_machine.png "StateNet Defined Bot Actions") 16 | 17 | You can use StateNet to define a series of states for the bot to move through as it plays - transitions that would be ~~impossible~~ tricky to define in a regular FSM. For example, in a regular FSM `Choose Response` (and all of it's target states) would involve several cascading conditionals that explicitly invoke an RNG. Our StateNet defined version allows us to declare 3 possible sets of actions, and the odds of each one being picked. As each state reaches completion, StateNet handles the invokation of whatever comes next. 18 | 19 | ### ABot code 20 | 21 | > TODO - add a series of states and explanations to match the game-bot image above. -------------------------------------------------------------------------------- /StateNet/Engine/StateNetEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Aptacode.StateNet.Engine.Transitions; 5 | using Aptacode.StateNet.Network; 6 | using Aptacode.StateNet.Random; 7 | 8 | namespace Aptacode.StateNet.Engine; 9 | 10 | public class StateNetEngine 11 | { 12 | private readonly StateNetwork _network; 13 | private readonly IRandomNumberGenerator _randomNumberGenerator; 14 | 15 | public readonly TransitionHistory TransitionHistory; 16 | 17 | public StateNetEngine(StateNetwork network, IRandomNumberGenerator randomNumberGenerator) 18 | { 19 | _network = network ?? throw new ArgumentNullException(nameof(network)); 20 | _randomNumberGenerator = 21 | randomNumberGenerator ?? throw new ArgumentNullException(nameof(randomNumberGenerator)); 22 | CurrentState = _network.StartState; 23 | TransitionHistory = new TransitionHistory(_network); 24 | } 25 | 26 | public string CurrentState { get; private set; } 27 | 28 | public event EventHandler? OnTransition; 29 | 30 | public IEnumerable GetAvailableInputs() 31 | { 32 | return _network.GetInputs(CurrentState); 33 | } 34 | 35 | public IEnumerable GetAvailableConnections(string input) 36 | { 37 | return _network.GetConnections(CurrentState, input); 38 | } 39 | 40 | public TransitionResult Apply(string input) 41 | { 42 | var connections = _network.GetConnections(CurrentState, input); 43 | 44 | if (!connections.Any()) 45 | { 46 | return TransitionResult.Fail(Resources.NO_AVAILABLE_CONNECTION(CurrentState, input)); 47 | } 48 | 49 | var weightedConnections = new List(); 50 | foreach (var connection in connections) 51 | { 52 | var connectionWeight = connection.Expression.Interpret(TransitionHistory); 53 | if (connectionWeight <= 0) 54 | { 55 | continue; 56 | } 57 | 58 | for (var i = 0; i < connectionWeight; i++) 59 | { 60 | weightedConnections.Add(connection.Target); 61 | } 62 | } 63 | 64 | if (weightedConnections.Count == 0) 65 | { 66 | return TransitionResult.Fail(Resources.NO_AVAILABLE_CONNECTION(CurrentState, input)); 67 | } 68 | 69 | var connectionIndex = _randomNumberGenerator.Generate(0, weightedConnections.Count); 70 | var nextState = weightedConnections[connectionIndex]; 71 | var transition = new Transition(CurrentState, input, nextState); 72 | TransitionHistory.Add(transition.Input, transition.Destination); 73 | 74 | CurrentState = nextState; 75 | 76 | OnTransition?.Invoke(this, transition); 77 | 78 | return TransitionResult.Ok(transition, Resources.SUCCESS); 79 | } 80 | } -------------------------------------------------------------------------------- /StateNet/Engine/Transitions/Transition.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Engine.Transitions; 2 | 3 | public record Transition(string Source, string Input, string Destination); -------------------------------------------------------------------------------- /StateNet/Engine/Transitions/TransitionHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Aptacode.StateNet.Network; 4 | using Aptacode.StateNet.PatternMatching; 5 | 6 | namespace Aptacode.StateNet.Engine.Transitions; 7 | 8 | public class TransitionHistory 9 | { 10 | private readonly StateNetwork _network; 11 | 12 | private readonly Dictionary 13 | _patternMatches = new(); 14 | 15 | private readonly List _stringTransitionHistory = new(); 16 | private readonly List _transitionHistory = new(); 17 | 18 | public TransitionHistory(StateNetwork network) 19 | { 20 | _network = network ?? throw new ArgumentNullException(nameof(network)); 21 | 22 | if (string.IsNullOrEmpty(network?.StartState)) 23 | { 24 | throw new ArgumentNullException(nameof(network)); 25 | } 26 | 27 | _transitionHistory.Add(_network.StartState.GetDeterministicHashCode()); 28 | _stringTransitionHistory.Add(_network.StartState); 29 | CreateMatchTrackers(); 30 | } 31 | 32 | public int TransitionCount { get; private set; } 33 | 34 | private void CreateMatchTrackers() 35 | { 36 | foreach (var pattern in _network.Patterns) 37 | { 38 | var matchTracker = new PatternMatcher(pattern); 39 | matchTracker.Add(0, _network.StartState.GetDeterministicHashCode()); 40 | _patternMatches.Add(pattern, matchTracker); 41 | } 42 | } 43 | 44 | public IReadOnlyList GetTransitionHistory() 45 | { 46 | return _transitionHistory.AsReadOnly(); 47 | } 48 | 49 | public IEnumerable GetMatches(Pattern pattern) 50 | { 51 | if (_patternMatches.TryGetValue(pattern, out var matchTracker)) 52 | { 53 | return matchTracker.MatchList; 54 | } 55 | 56 | return Array.Empty(); 57 | } 58 | 59 | public void Add(string input, string destination) 60 | { 61 | var inputHashCode = input.GetDeterministicHashCode(); 62 | var destinationHashCode = destination.GetDeterministicHashCode(); 63 | _stringTransitionHistory.Add(input); 64 | _stringTransitionHistory.Add(destination); 65 | _transitionHistory.Add(inputHashCode); 66 | _transitionHistory.Add(destinationHashCode); 67 | 68 | TransitionCount++; 69 | 70 | foreach (var patternMatcher in _patternMatches) 71 | { 72 | patternMatcher.Value.Add(TransitionCount, inputHashCode); 73 | patternMatcher.Value.Add(TransitionCount, destinationHashCode); 74 | } 75 | } 76 | 77 | public override string ToString() 78 | { 79 | return string.Join(",", _stringTransitionHistory); 80 | } 81 | } -------------------------------------------------------------------------------- /StateNet/Engine/Transitions/TransitionHistoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Aptacode.StateNet.Engine.Transitions; 6 | 7 | public static class TransitionHistoryExtensions 8 | { 9 | public static IEnumerable TakeLast(this IEnumerable source, int count) 10 | { 11 | var tempList = source as T[] ?? source.ToArray(); 12 | return tempList.Skip(Math.Max(0, tempList.Length - count)); 13 | } 14 | } -------------------------------------------------------------------------------- /StateNet/Engine/Transitions/TransitionResult.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Engine.Transitions; 2 | 3 | public record TransitionResult(string Message, bool Success, Transition? Transition) 4 | { 5 | public static TransitionResult Fail(string message) 6 | { 7 | return new TransitionResult(message, false, null); 8 | } 9 | 10 | public static TransitionResult Ok(Transition transition, string message) 11 | { 12 | return new TransitionResult(message, true, transition); 13 | } 14 | } -------------------------------------------------------------------------------- /StateNet/Logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aptacode/StateNet/8c637af4501524546e4ae88dbe5e38397358f186/StateNet/Logo.ico -------------------------------------------------------------------------------- /StateNet/Network/Connection.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions; 2 | using Aptacode.StateNet.Engine.Transitions; 3 | 4 | namespace Aptacode.StateNet.Network; 5 | 6 | public record Connection(string Target, IExpression Expression); -------------------------------------------------------------------------------- /StateNet/Network/NetworkBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Aptacode.Expressions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | using Aptacode.StateNet.Network.Validator; 7 | using Aptacode.StateNet.PatternMatching; 8 | 9 | namespace Aptacode.StateNet.Network; 10 | 11 | public class NetworkBuilder 12 | { 13 | private readonly List<(string, string, Connection)> _connections; 14 | private readonly HashSet _inputs; 15 | private readonly HashSet _patterns; 16 | private readonly HashSet _states; 17 | private string _startState; 18 | 19 | protected NetworkBuilder() 20 | { 21 | _startState = string.Empty; 22 | _states = new HashSet(); 23 | _inputs = new HashSet(); 24 | _patterns = new HashSet(); 25 | _connections = new List<(string, string, Connection)>(); 26 | } 27 | 28 | public static NetworkBuilder New => new(); 29 | 30 | public NetworkBuilder SetStartState(string startState) 31 | { 32 | _startState = startState; 33 | _states.Add(_startState); 34 | return this; 35 | } 36 | 37 | public NetworkBuilder AddState(string state) 38 | { 39 | _states.Add(state); 40 | return this; 41 | } 42 | 43 | public NetworkBuilder RemoveState(string state) 44 | { 45 | ClearConnectionsFromState(state); 46 | ClearConnectionsToState(state); 47 | _states.Remove(state); 48 | return this; 49 | } 50 | 51 | private NetworkBuilder AddInput(string input) 52 | { 53 | _inputs.Add(input); 54 | return this; 55 | } 56 | 57 | public NetworkBuilder RemoveInputOnState(string input, string state) 58 | { 59 | ClearConnectionsFromState(state, input); 60 | _inputs.Remove(input); 61 | return this; 62 | } 63 | 64 | public NetworkBuilder AddConnection(string source, string input, string destination, 65 | IExpression expression) 66 | { 67 | AddState(source); 68 | AddInput(input); 69 | AddState(destination); 70 | 71 | _connections.Add((source, input, new Connection(destination, expression))); 72 | return this; 73 | } 74 | 75 | public NetworkBuilder ClearConnectionsFromState(string state, string input) 76 | { 77 | var connectionToRemove = _connections.Where(c => c.Item1 == state && c.Item2 == input); 78 | foreach (var connection in connectionToRemove.ToList()) 79 | { 80 | _connections.Remove(connection); 81 | } 82 | 83 | return this; 84 | } 85 | 86 | public NetworkBuilder ClearConnectionsFromState(string state) 87 | { 88 | var connectionToRemove = _connections.Where(c => c.Item1 == state); 89 | foreach (var connection in connectionToRemove.ToList()) 90 | { 91 | _connections.Remove(connection); 92 | } 93 | 94 | return this; 95 | } 96 | 97 | public NetworkBuilder ClearConnectionsToState(string state, string input) 98 | { 99 | var connectionToRemove = _connections.Where(c => c.Item2 == input && c.Item3.Target == state); 100 | foreach (var connection in connectionToRemove.ToList()) 101 | { 102 | _connections.Remove(connection); 103 | } 104 | 105 | return this; 106 | } 107 | 108 | public NetworkBuilder ClearConnectionsToState(string state) 109 | { 110 | var connectionToRemove = _connections.Where(c => c.Item3.Target == state); 111 | foreach (var connection in connectionToRemove.ToList()) 112 | { 113 | _connections.Remove(connection); 114 | } 115 | 116 | return this; 117 | } 118 | 119 | public NetworkBuilder AddPattern(params Pattern[] patterns) 120 | { 121 | foreach (var pattern in patterns) 122 | { 123 | _patterns.Add(pattern); 124 | } 125 | 126 | return this; 127 | } 128 | 129 | public NetworkBuilder RemovePattern(params Pattern[] patterns) 130 | { 131 | foreach (var pattern in patterns) 132 | { 133 | _patterns.Remove(pattern); 134 | } 135 | 136 | return this; 137 | } 138 | 139 | private StateNetworkResult CreateStateNetwork() 140 | { 141 | try 142 | { 143 | var stateDictionary = 144 | new Dictionary>>(); 145 | 146 | foreach (var state in _states) 147 | { 148 | var inputDictionary = new Dictionary>(); 149 | 150 | foreach (var input in _inputs) 151 | { 152 | inputDictionary.Add(input, 153 | new List()); //This requires every input to have at least an empty connection associated to it 154 | } 155 | 156 | var connectionsFromState = _connections.Where(c => c.Item1 == state).GroupBy(c => c.Item2); 157 | foreach (var connectionGroup in connectionsFromState) 158 | { 159 | var connections = connectionGroup.Select(c => c.Item3); 160 | inputDictionary[connectionGroup.Key] = connections; 161 | 162 | foreach (var connection in connections) 163 | { 164 | var matchesVisitor = new MatchesVisitor(); 165 | 166 | matchesVisitor.Schedule(connection.Expression); 167 | foreach (var matchesVisitorPattern in matchesVisitor.Patterns) 168 | { 169 | _patterns.Add(matchesVisitorPattern); 170 | } 171 | } 172 | } 173 | 174 | var emptyInputs = inputDictionary.Where(i => i.Value.Count() == 0).Select(i => i.Key); 175 | foreach (var emptyInput in emptyInputs) 176 | { 177 | inputDictionary.Remove(emptyInput); 178 | } 179 | 180 | stateDictionary.Add(state, inputDictionary); 181 | } 182 | 183 | var network = new StateNetwork(_startState, stateDictionary, _patterns.ToList()); 184 | return StateNetworkResult.Ok(network, Resources.SUCCESS); 185 | } 186 | catch (Exception ex) 187 | { 188 | return StateNetworkResult.Fail(ex.Message); 189 | } 190 | } 191 | 192 | public StateNetworkResult Build() 193 | { 194 | var networkResult = CreateStateNetwork(); 195 | var network = networkResult.Network; 196 | 197 | Reset(); 198 | 199 | if (!networkResult.Success || network == null) 200 | { 201 | return networkResult; 202 | } 203 | 204 | var stateNetworkValidationResult = network.IsValid(); 205 | return !stateNetworkValidationResult.Success 206 | ? StateNetworkResult.Fail(stateNetworkValidationResult.Message) 207 | : StateNetworkResult.Ok(network, Resources.SUCCESS); 208 | } 209 | 210 | public void Reset() 211 | { 212 | _startState = string.Empty; 213 | _states.Clear(); 214 | _inputs.Clear(); 215 | _connections.Clear(); 216 | _patterns.Clear(); 217 | } 218 | } -------------------------------------------------------------------------------- /StateNet/Network/StateNetwork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Aptacode.StateNet.PatternMatching; 5 | 6 | namespace Aptacode.StateNet.Network; 7 | 8 | public sealed class StateNetwork : IEquatable 9 | { 10 | public StateNetwork(string startState, 11 | Dictionary>> stateDictionary, 12 | IEnumerable patterns) 13 | { 14 | if (string.IsNullOrEmpty(startState)) 15 | { 16 | throw new ArgumentNullException(Resources.INVALID_START_STATE); 17 | } 18 | 19 | StateDictionary = stateDictionary ?? throw new ArgumentNullException(Resources.NO_STATES); 20 | 21 | if (!StateDictionary.Keys.Any()) 22 | { 23 | throw new ArgumentException(Resources.NO_STATES); 24 | } 25 | 26 | Patterns = patterns; 27 | StartState = startState; 28 | } 29 | 30 | public Dictionary>> StateDictionary { get; set; } 31 | 32 | public IEnumerable Patterns { get; set; } 33 | 34 | public string StartState { get; set; } 35 | 36 | public IEnumerable GetConnections(string state, string input) 37 | { 38 | if (!StateDictionary.TryGetValue(state, out var inputs)) 39 | { 40 | return Array.Empty(); 41 | } 42 | 43 | return inputs.TryGetValue(input, out var connections) ? connections : Array.Empty(); 44 | } 45 | 46 | public IEnumerable GetConnections(string state) 47 | { 48 | return !StateDictionary.TryGetValue(state, out var inputs) 49 | ? Array.Empty() 50 | : inputs.Values.SelectMany(c => c); 51 | } 52 | 53 | public IEnumerable GetInputs(string state) 54 | { 55 | if (!StateDictionary.TryGetValue(state, out var inputs)) 56 | { 57 | return Array.Empty(); 58 | } 59 | 60 | return inputs.Keys; 61 | } 62 | 63 | public IEnumerable GetAllConnections() 64 | { 65 | return StateDictionary.Values.SelectMany(c => c.Values.SelectMany(c => c)); 66 | } 67 | 68 | public IEnumerable GetAllInputs() 69 | { 70 | return StateDictionary.Values.SelectMany(c => c.Keys); 71 | } 72 | 73 | public IEnumerable GetAllStates() 74 | { 75 | return StateDictionary.Keys; 76 | } 77 | 78 | 79 | #region IEquatable 80 | 81 | public override bool Equals(object obj) 82 | { 83 | return obj is StateNetwork stateNetwork && Equals(stateNetwork); 84 | } 85 | 86 | public bool Equals(StateNetwork other) 87 | { 88 | return this == other; 89 | } 90 | 91 | public static bool operator ==(StateNetwork lhs, StateNetwork rhs) 92 | { 93 | if (lhs is null || rhs is null) 94 | { 95 | return lhs is null && rhs is null; 96 | } 97 | 98 | if (lhs.StartState != rhs.StartState) 99 | { 100 | return false; 101 | } 102 | 103 | if (!lhs.GetAllStates().SequenceEqual(rhs.GetAllStates())) 104 | { 105 | return false; 106 | } 107 | 108 | if (!lhs.GetAllInputs().SequenceEqual(rhs.GetAllInputs())) 109 | { 110 | return false; 111 | } 112 | 113 | if (!lhs.GetAllConnections().SequenceEqual(rhs.GetAllConnections())) 114 | { 115 | return false; 116 | } 117 | 118 | return true; 119 | } 120 | 121 | public static bool operator !=(StateNetwork lhs, StateNetwork rhs) 122 | { 123 | return !(lhs == rhs); 124 | } 125 | 126 | #endregion 127 | } -------------------------------------------------------------------------------- /StateNet/Network/StateNetworkResult.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Network; 2 | 3 | public record StateNetworkResult(string Message, bool Success, StateNetwork? Network) 4 | { 5 | public static StateNetworkResult Fail(string message) 6 | { 7 | return new StateNetworkResult(message, false, null); 8 | } 9 | 10 | public static StateNetworkResult Ok(StateNetwork network, string message) 11 | { 12 | return new StateNetworkResult(message, true, network); 13 | } 14 | } -------------------------------------------------------------------------------- /StateNet/Network/Validator/MatchesVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Aptacode.Expressions.List; 4 | using Aptacode.Expressions.Visitor; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | using Aptacode.StateNet.PatternMatching; 7 | using Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | namespace Aptacode.StateNet.Network.Validator; 10 | 11 | public class MatchesVisitor : ExpressionVisitor 12 | { 13 | private readonly HashSet _dependencies = new(); 14 | private readonly HashSet _patterns = new(); 15 | public IReadOnlyList Dependencies => _dependencies.ToList(); 16 | public IReadOnlyList Patterns => _patterns.ToList(); 17 | public override void Visit(TerminalListExpression expression) 18 | { 19 | if (expression is Matches matches) 20 | { 21 | _patterns.Add(matches.Pattern); 22 | 23 | foreach (var dependency in matches.Pattern.Elements) 24 | { 25 | if (string.IsNullOrEmpty(dependency)) 26 | { 27 | continue; 28 | } 29 | 30 | _dependencies.Add(dependency); 31 | } 32 | } 33 | 34 | base.Visit(expression); 35 | } 36 | } -------------------------------------------------------------------------------- /StateNet/Network/Validator/StateNetworkValidationResult.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Network.Validator; 2 | 3 | public class StateNetworkValidationResult 4 | { 5 | private StateNetworkValidationResult(string message, bool success) 6 | { 7 | Message = message; 8 | Success = success; 9 | } 10 | 11 | public string Message { get; } 12 | public bool Success { get; } 13 | 14 | public static StateNetworkValidationResult Fail(string message) 15 | { 16 | return new StateNetworkValidationResult(message, false); 17 | } 18 | 19 | public static StateNetworkValidationResult Ok(string message) 20 | { 21 | return new StateNetworkValidationResult(message, true); 22 | } 23 | } -------------------------------------------------------------------------------- /StateNet/Network/Validator/StateNetworkValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Aptacode.StateNet.Network.Validator; 5 | 6 | public static class StateNetworkExtensions 7 | { 8 | public static StateNetworkValidationResult IsValid(this StateNetwork network) 9 | { 10 | if (string.IsNullOrEmpty(network.StartState)) 11 | { 12 | return StateNetworkValidationResult.Fail(Resources.UNSET_START_STATE); 13 | } 14 | 15 | var connections = network.GetAllConnections(); 16 | var states = network.GetAllStates(); 17 | 18 | if (!states.Contains(network.StartState)) 19 | { 20 | return StateNetworkValidationResult.Fail(Resources.INVALID_START_STATE); 21 | } 22 | 23 | var inputs = network.GetAllInputs(); 24 | var allStatesAndInputs = states.Concat(inputs); 25 | foreach (var connection in connections) 26 | { 27 | if (!states.Contains(connection.Target)) 28 | { 29 | return StateNetworkValidationResult.Fail(Resources.INVALID_CONNECTION_TARGET); 30 | } 31 | 32 | var matchesVisitor = new MatchesVisitor(); 33 | matchesVisitor.Schedule(connection.Expression); 34 | 35 | var allDependenciesAreValid = 36 | matchesVisitor.Dependencies.All(state => allStatesAndInputs.Contains(state)); 37 | if (!allDependenciesAreValid) 38 | { 39 | return StateNetworkValidationResult.Fail(Resources.INVALID_DEPENDENCY); 40 | } 41 | } 42 | 43 | var visitedStates = new HashSet(); 44 | var usableInputs = new HashSet(); 45 | GetVisitedStatesAndUsableInputs(network, network.StartState, visitedStates, usableInputs); 46 | var unvisitedStates = states.Where(s => !visitedStates.Contains(s)); 47 | 48 | if (unvisitedStates.Any()) 49 | { 50 | return StateNetworkValidationResult.Fail(Resources.UNREACHABLE_STATES); 51 | } 52 | 53 | var unusedInputs = inputs.Where(s => !usableInputs.Contains(s)); 54 | return unusedInputs.Any() 55 | ? StateNetworkValidationResult.Fail(Resources.UNUSABLE_INPUTS) 56 | : StateNetworkValidationResult.Ok(Resources.SUCCESS); 57 | } 58 | 59 | private static IEnumerable GetUsableInputs(StateNetwork network, string state, 60 | IEnumerable inputs) 61 | { 62 | return inputs.Where(input => network.GetConnections(state, input).Any()); 63 | } 64 | 65 | public static void GetVisitedStatesAndUsableInputs(StateNetwork network, string state, 66 | HashSet visitedStates, 67 | HashSet usableInputs) //This is quite big and could maybe be separated out? 68 | { 69 | visitedStates.Add(state); 70 | var inputs = network.GetInputs(state); 71 | foreach (var input in GetUsableInputs(network, state, inputs) 72 | ) //This defines a valid input as one which has connections, not sure if this is a strong enough definition. 73 | { 74 | usableInputs.Add(input); 75 | } 76 | 77 | var connectedStates = inputs 78 | .SelectMany(i => network.GetConnections(state, i)) 79 | .Select(c => c.Target); 80 | 81 | var unvisitedConnectedStates = 82 | connectedStates.Distinct() 83 | .Where(s => !visitedStates.Contains(s)); 84 | 85 | foreach (var unvisitedState in unvisitedConnectedStates) 86 | { 87 | GetVisitedStatesAndUsableInputs(network, unvisitedState, visitedStates, usableInputs); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/Matches.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Aptacode.Expressions.List; 3 | using Aptacode.StateNet.Engine.Transitions; 4 | 5 | namespace Aptacode.StateNet.PatternMatching.Expressions; 6 | 7 | public record Matches(Pattern Pattern) : TerminalListExpression 8 | { 9 | public override int[] Interpret(TransitionHistory context) 10 | { 11 | return context.GetMatches(Pattern).ToArray(); 12 | } 13 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/StateCount.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 3 | using Aptacode.StateNet.Engine.Transitions; 4 | 5 | namespace Aptacode.StateNet.PatternMatching.Expressions; 6 | 7 | public record StateCount : UnaryExpression 8 | { 9 | public StateCount(string state) : base( 10 | new Matches( 11 | new Pattern(state) 12 | ).Count() 13 | ) 14 | { 15 | } 16 | 17 | public override int Interpret(TransitionHistory context) 18 | { 19 | return Expression.Interpret(context); 20 | } 21 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/StateCountFromEnd.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.Integer; 3 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 4 | using Aptacode.Expressions.List.ListOperators.Extensions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | 7 | namespace Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | public record StateCountFromEnd : UnaryExpression 10 | { 11 | public StateCountFromEnd(string state, int takeLast) : base( 12 | new Matches(new Pattern(state)).TakeLast(new ConstantInteger(takeLast)).Count()) 13 | { 14 | } 15 | 16 | public override int Interpret(TransitionHistory context) 17 | { 18 | return Expression.Interpret(context); 19 | } 20 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/StateCountFromStart.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.Integer; 3 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 4 | using Aptacode.Expressions.List.ListOperators.Extensions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | 7 | namespace Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | public record StateCountStart : UnaryExpression 10 | { 11 | public StateCountStart(string state, int takeFirst) : base( 12 | new Matches(new Pattern(state)).TakeFirst(new ConstantInteger(takeFirst)).Count() 13 | ) 14 | { 15 | } 16 | 17 | public override int Interpret(TransitionHistory context) 18 | { 19 | return Expression.Interpret(context); 20 | } 21 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/TransitionCount.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 3 | using Aptacode.StateNet.Engine.Transitions; 4 | 5 | namespace Aptacode.StateNet.PatternMatching.Expressions; 6 | 7 | public record TransitionCount : UnaryExpression 8 | { 9 | public TransitionCount(string state, string input) : base(new Matches(new Pattern(state, input)).Count()) 10 | { 11 | } 12 | 13 | public override int Interpret(TransitionHistory context) 14 | { 15 | return Expression.Interpret(context); 16 | } 17 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/TransitionCountFromEnd.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.Integer; 3 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 4 | using Aptacode.Expressions.List.ListOperators.Extensions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | 7 | namespace Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | public record TransitionCountFromEnd : UnaryExpression 10 | { 11 | public TransitionCountFromEnd(string state, string input, int takeLast) : base( 12 | new Matches(new Pattern(state, input)).TakeLast(new ConstantInteger(takeLast)) 13 | .Count()) 14 | { 15 | } 16 | 17 | public override int Interpret(TransitionHistory context) 18 | { 19 | return Expression.Interpret(context); 20 | } 21 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Expressions/TransitionCountFromStart.cs: -------------------------------------------------------------------------------- 1 | using Aptacode.Expressions.GenericExpressions; 2 | using Aptacode.Expressions.Integer; 3 | using Aptacode.Expressions.List.IntegerListOperators.Extensions; 4 | using Aptacode.Expressions.List.ListOperators.Extensions; 5 | using Aptacode.StateNet.Engine.Transitions; 6 | 7 | namespace Aptacode.StateNet.PatternMatching.Expressions; 8 | 9 | public record TransitionCountFromStart : UnaryExpression 10 | { 11 | public TransitionCountFromStart(string state, string input, int takeFirst) : base( 12 | new Matches(new Pattern(state, input)).TakeFirst(new ConstantInteger(takeFirst)) 13 | .Count()) 14 | { 15 | } 16 | 17 | public override int Interpret(TransitionHistory context) 18 | { 19 | return Expression.Interpret(context); 20 | } 21 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/Pattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Aptacode.StateNet.PatternMatching; 6 | 7 | public class Pattern : IEquatable 8 | { 9 | public static readonly Pattern Empty = new(); 10 | 11 | public Pattern(params string[] elements) 12 | { 13 | if (elements == null) 14 | { 15 | elements = Array.Empty(); 16 | } 17 | 18 | Elements = elements; 19 | HashedElements = elements.Select(x => x?.GetDeterministicHashCode()); 20 | Length = elements.Length; 21 | } 22 | 23 | public IEnumerable Elements { get; set; } 24 | public IEnumerable HashedElements { get; set; } 25 | public int Length { get; set; } 26 | 27 | #region IEquatable 28 | 29 | public override int GetHashCode() 30 | { 31 | return HashedElements 32 | .Select(item => item.GetHashCode()) 33 | .Aggregate((total, nextCode) => total ^ nextCode); 34 | } 35 | 36 | public override bool Equals(object obj) 37 | { 38 | return obj is Pattern pattern && Equals(pattern); 39 | } 40 | 41 | public bool Equals(Pattern other) 42 | { 43 | return this == other; 44 | } 45 | 46 | public static bool operator ==(Pattern lhs, Pattern rhs) 47 | { 48 | if (lhs?.Length != rhs?.Length) 49 | { 50 | return false; 51 | } 52 | 53 | return lhs?.Length == null || lhs.HashedElements.SequenceEqual(rhs?.HashedElements); 54 | } 55 | 56 | public static bool operator !=(Pattern lhs, Pattern rhs) 57 | { 58 | return !(lhs == rhs); 59 | } 60 | 61 | #endregion 62 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/PatternMatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Aptacode.StateNet.PatternMatching; 5 | 6 | public class PatternMatcher 7 | { 8 | public readonly List MatchList = new(); 9 | 10 | public readonly Pattern Pattern; 11 | 12 | public PatternMatcher(Pattern pattern) 13 | { 14 | Pattern = pattern; 15 | } 16 | 17 | public int PatternIndex { get; private set; } 18 | 19 | public void Add(int index, int hashCode) 20 | { 21 | if (!IsNextInPattern(hashCode)) 22 | { 23 | PatternIndex = 0; 24 | return; 25 | } 26 | 27 | if (++PatternIndex < Pattern.Length) 28 | { 29 | return; 30 | } 31 | 32 | PatternIndex = 0; 33 | MatchList.Add(index); 34 | } 35 | 36 | private bool IsNextInPattern(int hashCode) 37 | { 38 | var patternElement = Pattern.HashedElements.ElementAt(PatternIndex); 39 | if (patternElement is not null && patternElement == hashCode) 40 | { 41 | return true; 42 | } 43 | 44 | PatternIndex = 0; 45 | return false; 46 | } 47 | } -------------------------------------------------------------------------------- /StateNet/PatternMatching/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.PatternMatching; 2 | 3 | public static class StringExtensions 4 | { 5 | public static int GetDeterministicHashCode(this string str) 6 | { 7 | unchecked 8 | { 9 | var hash1 = (5381 << 16) + 5381; 10 | var hash2 = hash1; 11 | 12 | for (var i = 0; i < str.Length; i += 2) 13 | { 14 | hash1 = ((hash1 << 5) + hash1) ^ str[i]; 15 | if (i == str.Length - 1) 16 | { 17 | break; 18 | } 19 | 20 | hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; 21 | } 22 | 23 | return hash1 + hash2 * 1566083941; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /StateNet/Random/IRandomNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Random; 2 | 3 | public interface IRandomNumberGenerator 4 | { 5 | int Generate(int min, int max); 6 | } -------------------------------------------------------------------------------- /StateNet/Random/SystemRandomNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet.Random; 2 | 3 | public class SystemRandomNumberGenerator : IRandomNumberGenerator 4 | { 5 | private static readonly System.Random RandomGenerator = new(); 6 | 7 | public int Generate(int min, int max) 8 | { 9 | return RandomGenerator.Next(min, max); 10 | } 11 | } -------------------------------------------------------------------------------- /StateNet/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace Aptacode.StateNet; 2 | 3 | public static class Resources 4 | { 5 | public static readonly string UNSET_START_STATE = "Start state was not set."; 6 | public static readonly string INVALID_START_STATE = "Start state was set to invalid state."; 7 | public static readonly string INVALID_CONNECTION_TARGET = "Connection target is not a valid state."; 8 | public static readonly string INVALID_DEPENDENCY = "Connection had an invalid state or input dependency."; 9 | public static readonly string UNREACHABLE_STATES = "Unreachable states exist in the network."; 10 | public static readonly string UNUSABLE_INPUTS = "Unusable inputs exist in the network."; 11 | public static readonly string SUCCESS = "Success."; 12 | public static readonly string NO_STATES = "No states were given."; 13 | 14 | public static string NO_AVAILABLE_CONNECTION(string currentState, string input) 15 | { 16 | return $"There are no available connections from {currentState} for {input}."; 17 | } 18 | } -------------------------------------------------------------------------------- /StateNet/StateNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | 10.0 6 | enable 7 | Aptacode.StateNet 8 | Aptacode.StateNet 9 | Timothy Jones 10 | Aptacode 11 | A small .Net Standard library used to model simple State Machines 12 | MIT 13 | MIT 14 | https://github.com/Aptacode/StateNet 15 | Logo.png 16 | 17 | https://github.com/Aptacode/StateNet 18 | 19 | Aptacode Probabilistic State Machine 20 | true 21 | 2.0.7 22 | 2.0.7 23 | Logo.ico 24 | 2.0.7 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | True 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /StateNet/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | "net6.0": { 5 | "Aptacode.Expressions": { 6 | "type": "Direct", 7 | "requested": "[1.0.13, )", 8 | "resolved": "1.0.13", 9 | "contentHash": "tQR8ZYVXjflRmDmZbCmY2y6+f+TqmYGB5fhEu2XD6bUUUEx0kt/2apgAq2Su4+f0bUbshuSVNUADsq/aYEnA8w==" 10 | }, 11 | "System.Collections.Immutable": { 12 | "type": "Direct", 13 | "requested": "[6.0.0, )", 14 | "resolved": "6.0.0", 15 | "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", 16 | "dependencies": { 17 | "System.Runtime.CompilerServices.Unsafe": "6.0.0" 18 | } 19 | }, 20 | "System.ValueTuple": { 21 | "type": "Direct", 22 | "requested": "[4.5.0, )", 23 | "resolved": "4.5.0", 24 | "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" 25 | }, 26 | "System.Runtime.CompilerServices.Unsafe": { 27 | "type": "Transitive", 28 | "resolved": "6.0.0", 29 | "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - Development 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | project: '**/StateNet.csproj' 17 | testProject: '**/StateNet.Tests.csproj' 18 | NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages 19 | steps: 20 | - task: Cache@2 21 | inputs: 22 | key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**' 23 | restoreKeys: | 24 | nuget | "$(Agent.OS)" 25 | path: $(NUGET_PACKAGES) 26 | displayName: Cache NuGet packages 27 | 28 | - task: UseDotNet@2 29 | displayName: 'Use .NET 5' 30 | inputs: 31 | version: 5.x 32 | 33 | - task: DotNetCoreCLI@2 34 | displayName: 'Build' 35 | inputs: 36 | command: 'build' 37 | arguments: '--configuration $(buildConfiguration)' 38 | projects: '$(project)' 39 | 40 | - task: DotNetCoreCLI@2 41 | displayName: 'Test' 42 | inputs: 43 | command: 'test' 44 | projects: '$(testProject)' 45 | arguments: --collect "Code Coverage" 46 | 47 | - task: DotNetCoreCLI@2 48 | displayName: "Pack" 49 | inputs: 50 | command: 'pack' 51 | arguments: '--configuration $(buildConfiguration)' 52 | packagesToPack: '$(project)' 53 | nobuild: true 54 | versioningScheme: 'byPrereleaseNumber' 55 | majorVersion: '2' 56 | minorVersion: '0' 57 | patchVersion: '1' 58 | 59 | - task: NuGetCommand@2 60 | displayName: 'Push' 61 | inputs: 62 | command: 'push' 63 | packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg' 64 | nuGetFeedType: 'external' 65 | publishFeedCredentials: 'Nuget.org' 66 | allowPackageConflicts: true 67 | --------------------------------------------------------------------------------