├── .github
└── workflows
│ ├── codeql-analysis.yml
│ ├── dotnet.yml
│ └── nuget.yml
├── .gitignore
├── Directory.Build.props
├── LICENSE
├── ManagedCode.TimeSeries.Benchmark
├── Benchmarks
│ ├── Bench1.cs
│ └── CollectionBenchmark.cs
├── ManagedCode.TimeSeries.Benchmark.csproj
└── Program.cs
├── ManagedCode.TimeSeries.Orleans
├── Accumulators
│ ├── Converters
│ │ ├── DoubleTimeSeriesAccumulatorConverter.cs
│ │ ├── FloatTimeSeriesAccumulatorConverter.cs
│ │ └── IntTimeSeriesAccumulatorConverter.cs
│ └── TimeSeriesAccumulatorsSurrogate.cs
├── ManagedCode.TimeSeries.Orleans.csproj
└── Summers
│ ├── Converters
│ ├── DoubleTimeSeriesSummerConverter.cs
│ ├── FloatTimeSeriesSummerConverter.cs
│ └── IntTimeSeriesSummerConverter.cs
│ └── TimeSeriesSummerSurrogate.cs
├── ManagedCode.TimeSeries.Tests
├── AccumulatorsTests.cs
├── ManagedCode.TimeSeries.Tests.csproj
└── SummersTests.cs
├── ManagedCode.TimeSeries.sln
├── ManagedCode.TimeSeries
├── Abstractions
│ ├── BaseGroupNumberTimeSeriesSummer.cs
│ ├── BaseNumberTimeSeriesSummer.cs
│ ├── BaseTimeSeries.cs
│ ├── BaseTimeSeriesAccumulator.cs
│ ├── BaseTimeSeriesByValueAccumulator.cs
│ ├── BaseTimeSeriesSummer.cs
│ ├── ISummerItem.cs
│ ├── ITimeSeries.cs
│ └── Strategy.cs
├── Accumulators
│ ├── DoubleTimeSeriesAccumulator.cs
│ ├── FloatTimeSeriesAccumulator.cs
│ ├── IntTimeSeriesAccumulator.cs
│ └── StringTimeSeriesAccumulator.cs
├── Extensions
│ └── RoundDateTimeAndTimeSpanExtensions.cs
├── ManagedCode.TimeSeries.csproj
├── MarkupDirection.cs
└── Summers
│ ├── DoubleGroupTimeSeriesSummer.cs
│ ├── DoubleTimeSeriesSummer.cs
│ ├── FloatGroupNumberTimeSeriesSummer.cs
│ ├── FloatTimeSeriesSummer.cs
│ ├── IntGroupNumberTimeSeriesSummer.cs
│ └── IntTimeSeriesSummer.cs
├── README.md
└── logo.png
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '35 11 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'csharp' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v2
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: .NET
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 |
10 | # Allows you to run this workflow manually from the Actions tab
11 | workflow_dispatch:
12 |
13 | jobs:
14 |
15 | build-and-test:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 |
20 | - uses: actions/checkout@v2
21 | - name: Setup .NET
22 | uses: actions/setup-dotnet@v1
23 | with:
24 | dotnet-version: 7.0.x
25 |
26 | # run build and test
27 | - name: Restore dependencies
28 | run: dotnet restore
29 | - name: Build
30 | run: dotnet build --no-restore
31 | - name: Test and сollect Code Coverage
32 | run: dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=lcov -p:CoverletOutput=ManagedCode.Communication.Tests/
33 | - name: coveralls
34 | uses: coverallsapp/github-action@master
35 | with:
36 | github-token: ${{secrets.GITHUB_TOKEN }}
37 | path-to-lcov: ManagedCode.TimeSeries.Tests/lcov.info
--------------------------------------------------------------------------------
/.github/workflows/nuget.yml:
--------------------------------------------------------------------------------
1 | name: nuget
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | jobs:
11 | nuget-pack:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Setup .NET
18 | uses: actions/setup-dotnet@v1
19 | with:
20 | dotnet-version: 7.0.x
21 |
22 | - name: Restore dependencies
23 | run: dotnet restore
24 | - name: Build
25 | run: dotnet build --configuration Release
26 | - name: Pack
27 | run: dotnet pack -p:IncludeSymbols=false -p:SymbolPackageFormat=snupkg --configuration Release
28 |
29 | - name: publish nuget packages
30 | run: |
31 | shopt -s globstar
32 | for file in **/*.nupkg
33 | do
34 | dotnet nuget push "$file" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
35 | done
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/intellij,intellij+all,macos,linux,windows,visualstudio,visualstudiocode,rider
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=intellij,intellij+all,macos,linux,windows,visualstudio,visualstudiocode,rider
4 |
5 | ### Intellij ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/**/workspace.xml
11 | .idea/**/tasks.xml
12 | .idea/**/usage.statistics.xml
13 | .idea/**/dictionaries
14 | .idea/**/shelf
15 |
16 | # Generated files
17 | .idea/**/contentModel.xml
18 |
19 | # Sensitive or high-churn files
20 | .idea/**/dataSources/
21 | .idea/**/dataSources.ids
22 | .idea/**/dataSources.local.xml
23 | .idea/**/sqlDataSources.xml
24 | .idea/**/dynamic.xml
25 | .idea/**/uiDesigner.xml
26 | .idea/**/dbnavigator.xml
27 |
28 | # Gradle
29 | .idea/**/gradle.xml
30 | .idea/**/libraries
31 |
32 | # Gradle and Maven with auto-import
33 | # When using Gradle or Maven with auto-import, you should exclude module files,
34 | # since they will be recreated, and may cause churn. Uncomment if using
35 | # auto-import.
36 | # .idea/artifacts
37 | # .idea/compiler.xml
38 | # .idea/jarRepositories.xml
39 | # .idea/modules.xml
40 | # .idea/*.iml
41 | # .idea/modules
42 | # *.iml
43 | # *.ipr
44 |
45 | # CMake
46 | cmake-build-*/
47 |
48 | # Mongo Explorer plugin
49 | .idea/**/mongoSettings.xml
50 |
51 | # File-based project format
52 | *.iws
53 |
54 | # IntelliJ
55 | out/
56 |
57 | # mpeltonen/sbt-idea plugin
58 | .idea_modules/
59 |
60 | # JIRA plugin
61 | atlassian-ide-plugin.xml
62 |
63 | # Cursive Clojure plugin
64 | .idea/replstate.xml
65 |
66 | # Crashlytics plugin (for Android Studio and IntelliJ)
67 | com_crashlytics_export_strings.xml
68 | crashlytics.properties
69 | crashlytics-build.properties
70 | fabric.properties
71 |
72 | # Editor-based Rest Client
73 | .idea/httpRequests
74 |
75 | # Android studio 3.1+ serialized cache file
76 | .idea/caches/build_file_checksums.ser
77 |
78 | ### Intellij Patch ###
79 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
80 |
81 | # *.iml
82 | # modules.xml
83 | # .idea/misc.xml
84 | # *.ipr
85 |
86 | # Sonarlint plugin
87 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
88 | .idea/**/sonarlint/
89 |
90 | # SonarQube Plugin
91 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
92 | .idea/**/sonarIssues.xml
93 |
94 | # Markdown Navigator plugin
95 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
96 | .idea/**/markdown-navigator.xml
97 | .idea/**/markdown-navigator-enh.xml
98 | .idea/**/markdown-navigator/
99 |
100 | # Cache file creation bug
101 | # See https://youtrack.jetbrains.com/issue/JBR-2257
102 | .idea/$CACHE_FILE$
103 |
104 | # CodeStream plugin
105 | # https://plugins.jetbrains.com/plugin/12206-codestream
106 | .idea/codestream.xml
107 |
108 | ### Intellij+all ###
109 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
110 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
111 |
112 | # User-specific stuff
113 |
114 | # Generated files
115 |
116 | # Sensitive or high-churn files
117 |
118 | # Gradle
119 |
120 | # Gradle and Maven with auto-import
121 | # When using Gradle or Maven with auto-import, you should exclude module files,
122 | # since they will be recreated, and may cause churn. Uncomment if using
123 | # auto-import.
124 | # .idea/artifacts
125 | # .idea/compiler.xml
126 | # .idea/jarRepositories.xml
127 | # .idea/modules.xml
128 | # .idea/*.iml
129 | # .idea/modules
130 | # *.iml
131 | # *.ipr
132 |
133 | # CMake
134 |
135 | # Mongo Explorer plugin
136 |
137 | # File-based project format
138 |
139 | # IntelliJ
140 |
141 | # mpeltonen/sbt-idea plugin
142 |
143 | # JIRA plugin
144 |
145 | # Cursive Clojure plugin
146 |
147 | # Crashlytics plugin (for Android Studio and IntelliJ)
148 |
149 | # Editor-based Rest Client
150 |
151 | # Android studio 3.1+ serialized cache file
152 |
153 | ### Intellij+all Patch ###
154 | # Ignores the whole .idea folder and all .iml files
155 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
156 |
157 | .idea/
158 |
159 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
160 |
161 | *.iml
162 | modules.xml
163 | .idea/misc.xml
164 | *.ipr
165 |
166 | # Sonarlint plugin
167 | .idea/sonarlint
168 |
169 | ### Linux ###
170 | *~
171 |
172 | # temporary files which can be created if a process still has a handle open of a deleted file
173 | .fuse_hidden*
174 |
175 | # KDE directory preferences
176 | .directory
177 |
178 | # Linux trash folder which might appear on any partition or disk
179 | .Trash-*
180 |
181 | # .nfs files are created when an open file is removed but is still being accessed
182 | .nfs*
183 |
184 | ### macOS ###
185 | # General
186 | .DS_Store
187 | .AppleDouble
188 | .LSOverride
189 |
190 | # Icon must end with two \r
191 | Icon
192 |
193 |
194 | # Thumbnails
195 | ._*
196 |
197 | # Files that might appear in the root of a volume
198 | .DocumentRevisions-V100
199 | .fseventsd
200 | .Spotlight-V100
201 | .TemporaryItems
202 | .Trashes
203 | .VolumeIcon.icns
204 | .com.apple.timemachine.donotpresent
205 |
206 | # Directories potentially created on remote AFP share
207 | .AppleDB
208 | .AppleDesktop
209 | Network Trash Folder
210 | Temporary Items
211 | .apdisk
212 |
213 | ### Rider ###
214 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
215 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
216 |
217 | # User-specific stuff
218 |
219 | # Generated files
220 |
221 | # Sensitive or high-churn files
222 |
223 | # Gradle
224 |
225 | # Gradle and Maven with auto-import
226 | # When using Gradle or Maven with auto-import, you should exclude module files,
227 | # since they will be recreated, and may cause churn. Uncomment if using
228 | # auto-import.
229 | # .idea/artifacts
230 | # .idea/compiler.xml
231 | # .idea/jarRepositories.xml
232 | # .idea/modules.xml
233 | # .idea/*.iml
234 | # .idea/modules
235 | # *.iml
236 | # *.ipr
237 |
238 | # CMake
239 |
240 | # Mongo Explorer plugin
241 |
242 | # File-based project format
243 |
244 | # IntelliJ
245 |
246 | # mpeltonen/sbt-idea plugin
247 |
248 | # JIRA plugin
249 |
250 | # Cursive Clojure plugin
251 |
252 | # Crashlytics plugin (for Android Studio and IntelliJ)
253 |
254 | # Editor-based Rest Client
255 |
256 | # Android studio 3.1+ serialized cache file
257 |
258 | ### VisualStudioCode ###
259 | .vscode/*
260 | !.vscode/tasks.json
261 | !.vscode/launch.json
262 | *.code-workspace
263 |
264 | ### VisualStudioCode Patch ###
265 | # Ignore all local history of files
266 | .history
267 | .ionide
268 |
269 | ### Windows ###
270 | # Windows thumbnail cache files
271 | Thumbs.db
272 | Thumbs.db:encryptable
273 | ehthumbs.db
274 | ehthumbs_vista.db
275 |
276 | # Dump file
277 | *.stackdump
278 |
279 | # Folder config file
280 | [Dd]esktop.ini
281 |
282 | # Recycle Bin used on file shares
283 | $RECYCLE.BIN/
284 |
285 | # Windows Installer files
286 | *.cab
287 | *.msi
288 | *.msix
289 | *.msm
290 | *.msp
291 |
292 | # Windows shortcuts
293 | *.lnk
294 |
295 | ### VisualStudio ###
296 | ## Ignore Visual Studio temporary files, build results, and
297 | ## files generated by popular Visual Studio add-ons.
298 | ##
299 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
300 |
301 | # User-specific files
302 | *.rsuser
303 | *.suo
304 | *.user
305 | *.userosscache
306 | *.sln.docstates
307 |
308 | # User-specific files (MonoDevelop/Xamarin Studio)
309 | *.userprefs
310 |
311 | # Mono auto generated files
312 | mono_crash.*
313 |
314 | # Build results
315 | [Dd]ebug/
316 | [Dd]ebugPublic/
317 | [Rr]elease/
318 | [Rr]eleases/
319 | x64/
320 | x86/
321 | [Aa][Rr][Mm]/
322 | [Aa][Rr][Mm]64/
323 | bld/
324 | [Bb]in/
325 | [Oo]bj/
326 | [Ll]og/
327 | [Ll]ogs/
328 |
329 | # Visual Studio 2015/2017 cache/options directory
330 | .vs/
331 | # Uncomment if you have tasks that create the project's static files in wwwroot
332 | #wwwroot/
333 |
334 | # Visual Studio 2017 auto generated files
335 | Generated\ Files/
336 |
337 | # MSTest test Results
338 | [Tt]est[Rr]esult*/
339 | [Bb]uild[Ll]og.*
340 |
341 | # NUnit
342 | *.VisualState.xml
343 | TestResult.xml
344 | nunit-*.xml
345 |
346 | # Build Results of an ATL Project
347 | [Dd]ebugPS/
348 | [Rr]eleasePS/
349 | dlldata.c
350 |
351 | # Benchmark Results
352 | BenchmarkDotNet.Artifacts/
353 |
354 | # .NET Core
355 | project.lock.json
356 | project.fragment.lock.json
357 | artifacts/
358 |
359 | # StyleCop
360 | StyleCopReport.xml
361 |
362 | # Files built by Visual Studio
363 | *_i.c
364 | *_p.c
365 | *_h.h
366 | *.ilk
367 | *.meta
368 | *.obj
369 | *.iobj
370 | *.pch
371 | *.pdb
372 | *.ipdb
373 | *.pgc
374 | *.pgd
375 | *.rsp
376 | *.sbr
377 | *.tlb
378 | *.tli
379 | *.tlh
380 | *.tmp
381 | *.tmp_proj
382 | *_wpftmp.csproj
383 | *.log
384 | *.vspscc
385 | *.vssscc
386 | .builds
387 | *.pidb
388 | *.svclog
389 | *.scc
390 |
391 | # Chutzpah Test files
392 | _Chutzpah*
393 |
394 | # Visual C++ cache files
395 | ipch/
396 | *.aps
397 | *.ncb
398 | *.opendb
399 | *.opensdf
400 | *.sdf
401 | *.cachefile
402 | *.VC.db
403 | *.VC.VC.opendb
404 |
405 | # Visual Studio profiler
406 | *.psess
407 | *.vsp
408 | *.vspx
409 | *.sap
410 |
411 | # Visual Studio Trace Files
412 | *.e2e
413 |
414 | # TFS 2012 Local Workspace
415 | $tf/
416 |
417 | # Guidance Automation Toolkit
418 | *.gpState
419 |
420 | # ReSharper is a .NET coding add-in
421 | _ReSharper*/
422 | *.[Rr]e[Ss]harper
423 | *.DotSettings.user
424 |
425 | # TeamCity is a build add-in
426 | _TeamCity*
427 |
428 | # DotCover is a Code Coverage Tool
429 | *.dotCover
430 |
431 | # AxoCover is a Code Coverage Tool
432 | .axoCover/*
433 | !.axoCover/settings.json
434 |
435 | # Coverlet is a free, cross platform Code Coverage Tool
436 | coverage*[.json, .xml, .info]
437 |
438 | # Visual Studio code coverage results
439 | *.coverage
440 | *.coveragexml
441 |
442 | # NCrunch
443 | _NCrunch_*
444 | .*crunch*.local.xml
445 | nCrunchTemp_*
446 |
447 | # MightyMoose
448 | *.mm.*
449 | AutoTest.Net/
450 |
451 | # Web workbench (sass)
452 | .sass-cache/
453 |
454 | # Installshield output folder
455 | [Ee]xpress/
456 |
457 | # DocProject is a documentation generator add-in
458 | DocProject/buildhelp/
459 | DocProject/Help/*.HxT
460 | DocProject/Help/*.HxC
461 | DocProject/Help/*.hhc
462 | DocProject/Help/*.hhk
463 | DocProject/Help/*.hhp
464 | DocProject/Help/Html2
465 | DocProject/Help/html
466 |
467 | # Click-Once directory
468 | publish/
469 |
470 | # Publish Web Output
471 | *.[Pp]ublish.xml
472 | *.azurePubxml
473 | # Note: Comment the next line if you want to checkin your web deploy settings,
474 | # but database connection strings (with potential passwords) will be unencrypted
475 | *.pubxml
476 | *.publishproj
477 |
478 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
479 | # checkin your Azure Web App publish settings, but sensitive information contained
480 | # in these scripts will be unencrypted
481 | PublishScripts/
482 |
483 | # NuGet Packages
484 | *.nupkg
485 | # NuGet Symbol Packages
486 | *.snupkg
487 | # The packages folder can be ignored because of Package Restore
488 | **/[Pp]ackages/*
489 | # except build/, which is used as an MSBuild target.
490 | !**/[Pp]ackages/build/
491 | # Uncomment if necessary however generally it will be regenerated when needed
492 | #!**/[Pp]ackages/repositories.config
493 | # NuGet v3's project.json files produces more ignorable files
494 | *.nuget.props
495 | *.nuget.targets
496 |
497 | # Microsoft Azure Build Output
498 | csx/
499 | *.build.csdef
500 |
501 | # Microsoft Azure Emulator
502 | ecf/
503 | rcf/
504 |
505 | # Windows Store app package directories and files
506 | AppPackages/
507 | BundleArtifacts/
508 | Package.StoreAssociation.xml
509 | _pkginfo.txt
510 | *.appx
511 | *.appxbundle
512 | *.appxupload
513 |
514 | # Visual Studio cache files
515 | # files ending in .cache can be ignored
516 | *.[Cc]ache
517 | # but keep track of directories ending in .cache
518 | !?*.[Cc]ache/
519 |
520 | # Others
521 | ClientBin/
522 | ~$*
523 | *.dbmdl
524 | *.dbproj.schemaview
525 | *.jfm
526 | *.pfx
527 | *.publishsettings
528 | orleans.codegen.cs
529 |
530 | # Including strong name files can present a security risk
531 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
532 | #*.snk
533 |
534 | # Since there are multiple workflows, uncomment next line to ignore bower_components
535 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
536 | #bower_components/
537 |
538 | # RIA/Silverlight projects
539 | Generated_Code/
540 |
541 | # Backup & report files from converting an old project file
542 | # to a newer Visual Studio version. Backup files are not needed,
543 | # because we have git ;-)
544 | _UpgradeReport_Files/
545 | Backup*/
546 | UpgradeLog*.XML
547 | UpgradeLog*.htm
548 | ServiceFabricBackup/
549 | *.rptproj.bak
550 |
551 | # SQL Server files
552 | *.mdf
553 | *.ldf
554 | *.ndf
555 |
556 | # Business Intelligence projects
557 | *.rdl.data
558 | *.bim.layout
559 | *.bim_*.settings
560 | *.rptproj.rsuser
561 | *- [Bb]ackup.rdl
562 | *- [Bb]ackup ([0-9]).rdl
563 | *- [Bb]ackup ([0-9][0-9]).rdl
564 |
565 | # Microsoft Fakes
566 | FakesAssemblies/
567 |
568 | # GhostDoc plugin setting file
569 | *.GhostDoc.xml
570 |
571 | # Node.js Tools for Visual Studio
572 | .ntvs_analysis.dat
573 | node_modules/
574 |
575 | # Visual Studio 6 build log
576 | *.plg
577 |
578 | # Visual Studio 6 workspace options file
579 | *.opt
580 |
581 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
582 | *.vbw
583 |
584 | # Visual Studio LightSwitch build output
585 | **/*.HTMLClient/GeneratedArtifacts
586 | **/*.DesktopClient/GeneratedArtifacts
587 | **/*.DesktopClient/ModelManifest.xml
588 | **/*.Server/GeneratedArtifacts
589 | **/*.Server/ModelManifest.xml
590 | _Pvt_Extensions
591 |
592 | # Paket dependency manager
593 | .paket/paket.exe
594 | paket-files/
595 |
596 | # FAKE - F# Make
597 | .fake/
598 |
599 | # CodeRush personal settings
600 | .cr/personal
601 |
602 | # Python Tools for Visual Studio (PTVS)
603 | __pycache__/
604 | *.pyc
605 |
606 | # Cake - Uncomment if you are using it
607 | # tools/**
608 | # !tools/packages.config
609 |
610 | # Tabs Studio
611 | *.tss
612 |
613 | # Telerik's JustMock configuration file
614 | *.jmconfig
615 |
616 | # BizTalk build output
617 | *.btp.cs
618 | *.btm.cs
619 | *.odx.cs
620 | *.xsd.cs
621 |
622 | # OpenCover UI analysis results
623 | OpenCover/
624 |
625 | # Azure Stream Analytics local run output
626 | ASALocalRun/
627 |
628 | # MSBuild Binary and Structured Log
629 | *.binlog
630 |
631 | # NVidia Nsight GPU debugger configuration file
632 | *.nvuser
633 |
634 | # MFractors (Xamarin productivity tool) working folder
635 | .mfractor/
636 |
637 | # Local History for Visual Studio
638 | .localhistory/
639 |
640 | # BeatPulse healthcheck temp database
641 | healthchecksdb
642 |
643 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
644 | MigrationBackup/
645 |
646 | # Ionide (cross platform F# VS Code tools) working folder
647 | .ionide/
648 |
649 | # End of https://www.toptal.com/developers/gitignore/api/intellij,intellij+all,macos,linux,windows,visualstudio,visualstudiocode,rider
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ManagedCode
5 | Copyright © 2021-$([System.DateTime]::Now.ToString(`yyyy`)) ManagedCode SAS
6 | true
7 | true
8 | true
9 | snupkg
10 | Github
11 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
12 | logo.png
13 | MIT
14 | true
15 | README.md
16 |
17 | https://github.com/managedcode/TimeSeries
18 | https://github.com/managedcode/TimeSeries
19 | Managed Code - TimeSeries
20 | 0.0.18
21 | 0.0.18
22 |
23 |
24 |
25 | true
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | all
34 | runtime; build; native; contentfiles; analyzers; buildtransitive
35 |
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Managed-Code
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 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Benchmark/Benchmarks/Bench1.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using ManagedCode.TimeSeries.Accumulators;
3 |
4 | namespace ManagedCode.TimeSeries.Benchmark.Benchmarks;
5 |
6 |
7 | [SimpleJob]
8 | [MemoryDiagnoser]
9 | public class Bench1
10 | {
11 | [Benchmark]
12 | public async Task Int_1000()
13 | {
14 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(50));
15 | for (var i = 0; i < 1000; i++)
16 | {
17 | series.AddNewData(i);
18 | }
19 | }
20 |
21 | [Benchmark]
22 | public async Task Int_100_000()
23 | {
24 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(50));
25 | for (var i = 0; i < 100_000; i++)
26 | {
27 | series.AddNewData(i);
28 | }
29 | }
30 |
31 | [Benchmark]
32 | public async Task Int_10_000_000()
33 | {
34 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(50));
35 | for (var i = 0; i < 10_000_000; i++)
36 | {
37 | series.AddNewData(i);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Benchmark/Benchmarks/CollectionBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using ManagedCode.TimeSeries.Accumulators;
3 |
4 | namespace ManagedCode.TimeSeries.Benchmark.Benchmarks;
5 |
6 |
7 | [SimpleJob]
8 | [MemoryDiagnoser]
9 | public class CollectionBenchmark
10 | {
11 | [Benchmark]
12 | public void Queue_1000()
13 | {
14 | var series = new Queue();
15 | for (var i = 0; i < 1000; i++)
16 | {
17 | series.Enqueue(i);
18 | if (series.Count > 100)
19 | series.Dequeue();
20 | }
21 | }
22 |
23 | [Benchmark]
24 | public void Queue_100_000()
25 | {
26 | var series = new Queue();
27 | for (var i = 0; i < 100_000; i++)
28 | {
29 | series.Enqueue(i);
30 | if (series.Count > 100)
31 | series.Dequeue();
32 | }
33 | }
34 |
35 | [Benchmark]
36 | public void Queue_10_000_000()
37 | {
38 | var series = new Queue();
39 | for (var i = 0; i < 10_000_000; i++)
40 | {
41 | series.Enqueue(i);
42 | if (series.Count > 100)
43 | series.Dequeue();
44 | }
45 | }
46 |
47 | [Benchmark]
48 | public void LinkedList_1000()
49 | {
50 | var series = new LinkedList();
51 | for (var i = 0; i < 1000; i++)
52 | {
53 | series.AddLast(i);
54 | if (series.Count > 100)
55 | series.RemoveFirst();
56 | }
57 | }
58 |
59 | [Benchmark]
60 | public void LinkedList_100_000()
61 | {
62 | var series = new LinkedList();
63 | for (var i = 0; i < 100_000; i++)
64 | {
65 | series.AddLast(i);
66 | if (series.Count > 100)
67 | series.RemoveFirst();
68 | }
69 | }
70 |
71 | [Benchmark]
72 | public void LinkedList_10_000_000()
73 | {
74 | var series = new LinkedList();
75 | for (var i = 0; i < 10_000_000; i++)
76 | {
77 | series.AddLast(i);
78 | if (series.Count > 100)
79 | series.RemoveFirst();
80 | }
81 | }
82 |
83 | [Benchmark]
84 | public void List_1000()
85 | {
86 | var series = new List();
87 | for (var i = 0; i < 1000; i++)
88 | {
89 | series.Add(i);
90 | if (series.Count > 100)
91 | series.RemoveAt(0);
92 | }
93 | }
94 |
95 | [Benchmark]
96 | public void List_100_000()
97 | {
98 | var series = new List();
99 | for (var i = 0; i < 100_000; i++)
100 | {
101 | series.Add(i);
102 | if (series.Count > 100)
103 | series.RemoveAt(0);
104 | }
105 | }
106 |
107 | [Benchmark]
108 | public void List_10_000_000()
109 | {
110 | var series = new List();
111 | for (var i = 0; i < 10_000_000; i++)
112 | {
113 | series.Add(i);
114 | if (series.Count > 100)
115 | series.RemoveAt(0);
116 | }
117 | }
118 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Benchmark/ManagedCode.TimeSeries.Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | 11
7 | enable
8 | enable
9 | false
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection.Emit;
2 | using BenchmarkDotNet.Running;
3 |
4 | var summary = BenchmarkRunner.Run(typeof(Program).Assembly);
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Accumulators/Converters/DoubleTimeSeriesAccumulatorConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Accumulators;
2 | using Orleans;
3 |
4 | namespace ManagedCode.TimeSeries.Orleans;
5 |
6 | [RegisterConverter]
7 | public sealed class DoubleTimeSeriesAccumulatorConverter : IConverter>
8 | {
9 | public DoubleTimeSeriesAccumulator ConvertFromSurrogate(in TimeSeriesAccumulatorsSurrogate surrogate)
10 | {
11 | var series = new DoubleTimeSeriesAccumulator(surrogate.SampleInterval, surrogate.MaxSamplesCount);
12 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
13 | return series;
14 | }
15 |
16 | public TimeSeriesAccumulatorsSurrogate ConvertToSurrogate(in DoubleTimeSeriesAccumulator value)
17 | {
18 | return new TimeSeriesAccumulatorsSurrogate(value.Samples, value.Start, value.End,
19 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount);
20 | }
21 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Accumulators/Converters/FloatTimeSeriesAccumulatorConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Accumulators;
2 | using Orleans;
3 |
4 | namespace ManagedCode.TimeSeries.Orleans;
5 |
6 | [RegisterConverter]
7 | public sealed class FloatTimeSeriesAccumulatorConverter : IConverter>
8 | {
9 | public FloatTimeSeriesAccumulator ConvertFromSurrogate(in TimeSeriesAccumulatorsSurrogate surrogate)
10 | {
11 | var series = new FloatTimeSeriesAccumulator(surrogate.SampleInterval, surrogate.MaxSamplesCount);
12 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
13 | return series;
14 | }
15 |
16 | public TimeSeriesAccumulatorsSurrogate ConvertToSurrogate(in FloatTimeSeriesAccumulator value)
17 | {
18 | return new TimeSeriesAccumulatorsSurrogate(value.Samples, value.Start, value.End,
19 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount);
20 | }
21 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Accumulators/Converters/IntTimeSeriesAccumulatorConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 | using ManagedCode.TimeSeries.Accumulators;
3 | using ManagedCode.TimeSeries.Summers;
4 | using Orleans;
5 |
6 | namespace ManagedCode.TimeSeries.Orleans;
7 |
8 | // This is a converter which converts between the surrogate and the foreign type.
9 | [RegisterConverter]
10 | public sealed class IntTimeSeriesAccumulatorConverter : IConverter>
11 | {
12 | public IntTimeSeriesAccumulator ConvertFromSurrogate(in TimeSeriesAccumulatorsSurrogate surrogate)
13 | {
14 | var series = new IntTimeSeriesAccumulator(surrogate.SampleInterval, surrogate.MaxSamplesCount);
15 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
16 | return series;
17 | }
18 |
19 | public TimeSeriesAccumulatorsSurrogate ConvertToSurrogate(in IntTimeSeriesAccumulator value)
20 | {
21 | return new TimeSeriesAccumulatorsSurrogate(value.Samples, value.Start, value.End,
22 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount);
23 | }
24 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Accumulators/TimeSeriesAccumulatorsSurrogate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Numerics;
4 | using System.Runtime.InteropServices.JavaScript;
5 | using ManagedCode.TimeSeries.Abstractions;
6 | using Orleans;
7 |
8 | namespace ManagedCode.TimeSeries.Orleans;
9 |
10 | // This is the surrogate which will act as a stand-in for the foreign type.
11 | // Surrogates should use plain fields instead of properties for better perfomance.
12 | [Immutable]
13 | [GenerateSerializer]
14 | public struct TimeSeriesAccumulatorsSurrogate
15 | {
16 | public TimeSeriesAccumulatorsSurrogate(Dictionary> samples,
17 | DateTimeOffset start,
18 | DateTimeOffset end,
19 | TimeSpan sampleInterval,
20 | int maxSamplesCount,
21 | DateTimeOffset lastDate,
22 | ulong dataCount)
23 | {
24 | Samples = samples;
25 | Start = start;
26 | End = end;
27 | SampleInterval = sampleInterval;
28 | MaxSamplesCount = maxSamplesCount;
29 | LastDate = lastDate;
30 | DataCount = dataCount;
31 | }
32 |
33 | [Id(0)]
34 | public Dictionary> Samples;
35 | [Id(1)]
36 | public DateTimeOffset Start;
37 | [Id(2)]
38 | public DateTimeOffset End;
39 | [Id(3)]
40 | public TimeSpan SampleInterval;
41 | [Id(4)]
42 | public int MaxSamplesCount;
43 | [Id(5)]
44 | public DateTimeOffset LastDate;
45 | [Id(6)]
46 | public ulong DataCount;
47 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/ManagedCode.TimeSeries.Orleans.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11
5 | enable
6 | true
7 | net7.0
8 |
9 |
10 |
11 |
12 | ManagedCode.TimeSeries.Orleans
13 | ManagedCode.TimeSeries.Orleans
14 | TimeSeries
15 | managedcode, TimeSeries, Orleans
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Summers/Converters/DoubleTimeSeriesSummerConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Summers;
2 | using Orleans;
3 |
4 | namespace ManagedCode.TimeSeries.Orleans;
5 |
6 | [RegisterConverter]
7 | public sealed class DoubleTimeSeriesSummerConverter : IConverter>
8 | {
9 | public DoubleTimeSeriesSummer ConvertFromSurrogate(in TimeSeriesSummerSurrogate surrogate)
10 | {
11 | var series = new DoubleTimeSeriesSummer(surrogate.SampleInterval, surrogate.MaxSamplesCount, surrogate.Strategy);
12 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
13 | return series;
14 | }
15 |
16 | public TimeSeriesSummerSurrogate ConvertToSurrogate(in DoubleTimeSeriesSummer value)
17 | {
18 | return new TimeSeriesSummerSurrogate(value.Samples, value.Start, value.End,
19 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount, value.Strategy);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Summers/Converters/FloatTimeSeriesSummerConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Summers;
2 | using Orleans;
3 |
4 | namespace ManagedCode.TimeSeries.Orleans;
5 |
6 | [RegisterConverter]
7 | public sealed class FloatTimeSeriesSummerConverter : IConverter>
8 | {
9 | public FloatTimeSeriesSummer ConvertFromSurrogate(in TimeSeriesSummerSurrogate surrogate)
10 | {
11 | var series = new FloatTimeSeriesSummer(surrogate.SampleInterval, surrogate.MaxSamplesCount, surrogate.Strategy);
12 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
13 | return series;
14 | }
15 |
16 | public TimeSeriesSummerSurrogate ConvertToSurrogate(in FloatTimeSeriesSummer value)
17 | {
18 | return new TimeSeriesSummerSurrogate(value.Samples, value.Start, value.End,
19 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount, value.Strategy);
20 | }
21 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Summers/Converters/IntTimeSeriesSummerConverter.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Summers;
2 | using Orleans;
3 |
4 | namespace ManagedCode.TimeSeries.Orleans;
5 |
6 | // This is a converter which converts between the surrogate and the foreign type.
7 | [RegisterConverter]
8 | public sealed class IntTimeSeriesSummerConverter : IConverter>
9 | {
10 | public IntTimeSeriesSummer ConvertFromSurrogate(in TimeSeriesSummerSurrogate surrogate)
11 | {
12 | var series = new IntTimeSeriesSummer(surrogate.SampleInterval, surrogate.MaxSamplesCount, surrogate.Strategy);
13 | series.InitInternal(surrogate.Samples, surrogate.Start, surrogate.End, surrogate.LastDate, surrogate.DataCount);
14 | return series;
15 | }
16 |
17 | public TimeSeriesSummerSurrogate ConvertToSurrogate(in IntTimeSeriesSummer value)
18 | {
19 | return new TimeSeriesSummerSurrogate(value.Samples, value.Start, value.End,
20 | value.SampleInterval, value.MaxSamplesCount, value.LastDate, value.DataCount, value.Strategy);
21 | }
22 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Orleans/Summers/TimeSeriesSummerSurrogate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Numerics;
4 | using System.Runtime.InteropServices.JavaScript;
5 | using ManagedCode.TimeSeries.Abstractions;
6 | using Orleans;
7 |
8 | namespace ManagedCode.TimeSeries.Orleans;
9 |
10 | // This is the surrogate which will act as a stand-in for the foreign type.
11 | // Surrogates should use plain fields instead of properties for better perfomance.
12 | [Immutable]
13 | [GenerateSerializer]
14 | public struct TimeSeriesSummerSurrogate
15 | {
16 | public TimeSeriesSummerSurrogate(Dictionary samples,
17 | DateTimeOffset start,
18 | DateTimeOffset end,
19 | TimeSpan sampleInterval,
20 | int maxSamplesCount,
21 | DateTimeOffset lastDate,
22 | ulong dataCount,
23 | Strategy strategy)
24 | {
25 | Samples = samples;
26 | Start = start;
27 | End = end;
28 | SampleInterval = sampleInterval;
29 | MaxSamplesCount = maxSamplesCount;
30 | LastDate = lastDate;
31 | DataCount = dataCount;
32 | Strategy = strategy;
33 | }
34 |
35 | [Id(0)]
36 | public Dictionary Samples;
37 | [Id(1)]
38 | public DateTimeOffset Start;
39 | [Id(2)]
40 | public DateTimeOffset End;
41 | [Id(3)]
42 | public TimeSpan SampleInterval;
43 | [Id(4)]
44 | public int MaxSamplesCount;
45 | [Id(5)]
46 | public DateTimeOffset LastDate;
47 | [Id(6)]
48 | public ulong DataCount;
49 | [Id(7)]
50 | public Strategy Strategy;
51 |
52 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Tests/AccumulatorsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using ManagedCode.TimeSeries.Accumulators;
3 | using Xunit;
4 |
5 | namespace ManagedCode.TimeSeries.Tests;
6 |
7 | public class AccumulatorsTests
8 | {
9 |
10 | [Fact]
11 | public async Task IntTimeSeriesAccumulator()
12 | {
13 | int count = 1050;
14 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
15 | for (int i = 0; i < count; i++)
16 | {
17 | await Task.Delay(new Random().Next(1, 5));
18 | series.AddNewData(i);
19 | }
20 |
21 | series.DataCount.Should().Be(Convert.ToUInt64(count));
22 |
23 | var step = 0;
24 | foreach (var queue in series.Samples)
25 | {
26 | foreach (var item in queue.Value)
27 | {
28 | item.Should().Be(step);
29 | step++;
30 | }
31 | }
32 | }
33 |
34 | [Fact(Skip = "Need fix")]
35 | public async Task IntTimeSeriesAccumulatorMaxSamplesCount()
36 | {
37 | int samplesCount = 105;
38 | int count = 1050;
39 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(0.1), samplesCount);
40 | for (int i = 0; i < count; i++)
41 | {
42 | await Task.Delay(new Random().Next(1, 5));
43 | series.AddNewData(i);
44 | }
45 |
46 | series.DataCount.Should().Be(Convert.ToUInt64(count)); //because it's total; number of samples
47 | series.Samples.Count.Should().Be(samplesCount); //because it's total; number of samples
48 |
49 | var step = count - samplesCount - 1;
50 | foreach (var queue in series.Samples)
51 | {
52 | foreach (var item in queue.Value)
53 | {
54 | item.Should().Be(step);
55 | step++;
56 | }
57 | }
58 | }
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | [Fact]
72 | public async Task Accumulator()
73 | {
74 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
75 | for (var i = 0; i < 1000; i++)
76 | {
77 | await Task.Delay(new Random().Next(1, 5));
78 | series.AddNewData(i);
79 | }
80 |
81 | series.DataCount.Should().Be(1000);
82 |
83 | var step = 0;
84 | foreach (var queue in series.Samples)
85 | {
86 | foreach (var item in queue.Value)
87 | {
88 | item.Should().Be(step);
89 | step++;
90 | }
91 | }
92 | }
93 |
94 | // [Fact]
95 | // public async Task AccumulatorByString()
96 | // {
97 | // var rnd = new Random();
98 | // var series = new StringTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
99 | //
100 | // var dt = DateTimeOffset.Now;
101 | // series.AddNewData(dt, "1");
102 | // series.AddNewData(dt, "1");
103 | // series.AddNewData(dt, "2");
104 | // series.AddNewData(dt, "3");
105 | // series.AddNewData(dt, "3");
106 | // series.AddNewData(dt, "2");
107 | //
108 | //
109 | // dt = dt.AddHours(5);
110 | // series.AddNewData(dt, "1");
111 | // series.AddNewData(dt, "1");
112 | // series.AddNewData(dt, "2");
113 | // series.AddNewData(dt, "3");
114 | // series.AddNewData(dt, "3");
115 | // series.AddNewData(dt, "2");
116 | //
117 | // series.DataCount.Should().Be(12);
118 | // series.Samples.First().Value.Count.Should().Be(3);
119 | // series.Samples.Last().Value.Count.Should().Be(3);
120 | // }
121 |
122 | [Fact]
123 | public async Task AccumulatorLimit()
124 | {
125 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
126 |
127 | for (var i = 0; i < 1000; i++)
128 | {
129 | await Task.Delay(new Random().Next(1, 5));
130 | series.AddNewData(i);
131 | }
132 |
133 | series.Samples.Count.Should().Be(10);
134 | }
135 |
136 | [Fact]
137 | public async Task IsFull()
138 | {
139 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
140 |
141 | for (var i = 0; i < 1000; i++)
142 | {
143 | await Task.Delay(new Random().Next(1, 5));
144 |
145 | if (series.IsFull)
146 | {
147 | break;
148 | }
149 |
150 | series.AddNewData(i);
151 | }
152 |
153 | series.IsFull.Should().BeTrue();
154 | }
155 |
156 | [Fact]
157 | public void IsEmpty()
158 | {
159 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
160 | series.IsEmpty.Should().BeTrue();
161 | }
162 |
163 | [Fact]
164 | public async Task AccumulatorMerge()
165 | {
166 | Func> FillFunc = async () =>
167 | {
168 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
169 | for (var i = 0; i < 1000; i++)
170 | {
171 | await Task.Delay(new Random().Next(1, 5));
172 | series.AddNewData(i);
173 | }
174 |
175 | return series;
176 | };
177 |
178 | var seriesA = FillFunc();
179 | var seriesB = FillFunc();
180 |
181 | await Task.WhenAll(seriesA, seriesB);
182 |
183 | seriesA.Result.Samples.Count.Should().Be(10);
184 | seriesB.Result.Samples.Count.Should().Be(10);
185 |
186 | seriesA.Result.Merge(seriesB.Result);
187 |
188 | seriesA.Result.Samples.Count.Should().Be(10);
189 |
190 | var seriesList = new List();
191 | seriesList.Add(await FillFunc());
192 | seriesList.Add(await FillFunc());
193 | seriesList.Add(await FillFunc());
194 | seriesList.Add(new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10));
195 |
196 | IntTimeSeriesAccumulator onlineExpertsPerHourTimeSeries = null;
197 | foreach (var item in seriesList.ToArray())
198 | {
199 | if (onlineExpertsPerHourTimeSeries == null)
200 | {
201 | onlineExpertsPerHourTimeSeries = item;
202 | }
203 | else
204 | {
205 | onlineExpertsPerHourTimeSeries.Merge(item);
206 | }
207 | }
208 |
209 | onlineExpertsPerHourTimeSeries.Samples.Count.Should().Be(10);
210 | }
211 |
212 |
213 | [Fact]
214 | public async Task Resample()
215 | {
216 | var seriesFeature = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(2), 100);
217 |
218 | for (var i = 0; i < 100; i++)
219 | {
220 | seriesFeature.AddNewData(i);
221 |
222 | await Task.Delay(1);
223 | }
224 |
225 | seriesFeature.Resample(TimeSpan.FromMilliseconds(4), 100);
226 | var sad = seriesFeature;
227 | }
228 |
229 |
230 |
231 | [Fact]
232 | public void MarkupAllSamples()
233 | {
234 | var seriesFeature = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10), 100);
235 | seriesFeature.MarkupAllSamples(MarkupDirection.Feature);
236 | seriesFeature.AddNewData(1);
237 | (seriesFeature.Samples.Keys.Max() - seriesFeature.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
238 | (seriesFeature.Samples.Keys.Max() - seriesFeature.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
239 | var seriesFeatureOrdered = seriesFeature.Samples.OrderBy(o => o.Key).Take(10);
240 | seriesFeatureOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
241 |
242 | var seriesPast = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10));
243 | seriesPast.MarkupAllSamples();
244 | seriesPast.AddNewData(1);
245 | (seriesPast.Samples.Keys.Max() - seriesPast.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
246 | (seriesPast.Samples.Keys.Max() - seriesPast.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
247 | var seriesPastOrdered = seriesPast.Samples.OrderBy(o => o.Key).TakeLast(10);
248 | seriesPastOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
249 |
250 | var seriesMiddle = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10), 100);
251 | seriesMiddle.MarkupAllSamples(MarkupDirection.Middle);
252 | seriesMiddle.AddNewData(1);
253 | (seriesMiddle.Samples.Keys.Max() - seriesMiddle.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
254 | (seriesMiddle.Samples.Keys.Max() - seriesMiddle.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
255 | var seriesMiddleOrdered = seriesMiddle.Samples.OrderBy(o => o.Key).Skip(45).Take(10);
256 | seriesMiddleOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
257 | }
258 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Tests/ManagedCode.TimeSeries.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | 11
6 | enable
7 | enable
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 | all
19 |
20 |
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 | all
23 |
24 |
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 | all
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.Tests/SummersTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using ManagedCode.TimeSeries.Accumulators;
3 | using ManagedCode.TimeSeries.Summers;
4 | using Xunit;
5 |
6 | namespace ManagedCode.TimeSeries.Tests;
7 |
8 | public class SummersTests
9 | {
10 | [Fact]
11 | public async Task Accumulator()
12 | {
13 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
14 | for (var i = 0; i < 1000; i++)
15 | {
16 | await Task.Delay(new Random().Next(1, 5));
17 | series.AddNewData(i);
18 | }
19 |
20 | series.DataCount.Should().Be(1000);
21 |
22 | var step = 0;
23 | foreach (var queue in series.Samples)
24 | {
25 | foreach (var item in queue.Value)
26 | {
27 | item.Should().Be(step);
28 | step++;
29 | }
30 | }
31 | }
32 |
33 | // [Fact]
34 | // public async Task AccumulatorByString()
35 | // {
36 | // var rnd = new Random();
37 | // var series = new StringTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
38 | //
39 | // var dt = DateTimeOffset.Now;
40 | // series.AddNewData(dt, "1");
41 | // series.AddNewData(dt, "1");
42 | // series.AddNewData(dt, "2");
43 | // series.AddNewData(dt, "3");
44 | // series.AddNewData(dt, "3");
45 | // series.AddNewData(dt, "2");
46 | //
47 | //
48 | // dt = dt.AddHours(5);
49 | // series.AddNewData(dt, "1");
50 | // series.AddNewData(dt, "1");
51 | // series.AddNewData(dt, "2");
52 | // series.AddNewData(dt, "3");
53 | // series.AddNewData(dt, "3");
54 | // series.AddNewData(dt, "2");
55 | //
56 | // series.DataCount.Should().Be(12);
57 | // series.Samples.First().Value.Count.Should().Be(3);
58 | // series.Samples.Last().Value.Count.Should().Be(3);
59 | // }
60 |
61 | [Fact]
62 | public async Task AccumulatorLimit()
63 | {
64 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
65 |
66 | for (var i = 0; i < 1000; i++)
67 | {
68 | await Task.Delay(new Random().Next(1, 5));
69 | series.AddNewData(i);
70 | }
71 |
72 | series.Samples.Count.Should().Be(10);
73 | }
74 |
75 | [Fact]
76 | public async Task IsFull()
77 | {
78 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
79 |
80 | for (var i = 0; i < 1000; i++)
81 | {
82 | await Task.Delay(new Random().Next(1, 5));
83 |
84 | if (series.IsFull)
85 | {
86 | break;
87 | }
88 |
89 | series.AddNewData(i);
90 | }
91 |
92 | series.IsFull.Should().BeTrue();
93 | }
94 |
95 | [Fact]
96 | public void IsEmpty()
97 | {
98 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
99 | series.IsEmpty.Should().BeTrue();
100 | }
101 |
102 | [Fact]
103 | public async Task AccumulatorMerge()
104 | {
105 | Func> FillFunc = async () =>
106 | {
107 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10);
108 | for (var i = 0; i < 1000; i++)
109 | {
110 | await Task.Delay(new Random().Next(1, 5));
111 | series.AddNewData(i);
112 | }
113 |
114 | return series;
115 | };
116 |
117 | var seriesA = FillFunc();
118 | var seriesB = FillFunc();
119 |
120 | await Task.WhenAll(seriesA, seriesB);
121 |
122 | seriesA.Result.Samples.Count.Should().Be(10);
123 | seriesB.Result.Samples.Count.Should().Be(10);
124 |
125 | seriesA.Result.Merge(seriesB.Result);
126 |
127 | seriesA.Result.Samples.Count.Should().Be(10);
128 |
129 | var seriesList = new List();
130 | seriesList.Add(await FillFunc());
131 | seriesList.Add(await FillFunc());
132 | seriesList.Add(await FillFunc());
133 | seriesList.Add(new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1), 10));
134 |
135 | IntTimeSeriesAccumulator onlineExpertsPerHourTimeSeries = null;
136 | foreach (var item in seriesList.ToArray())
137 | {
138 | if (onlineExpertsPerHourTimeSeries == null)
139 | {
140 | onlineExpertsPerHourTimeSeries = item;
141 | }
142 | else
143 | {
144 | onlineExpertsPerHourTimeSeries.Merge(item);
145 | }
146 | }
147 |
148 | onlineExpertsPerHourTimeSeries.Samples.Count.Should().Be(10);
149 | }
150 |
151 | [Fact]
152 | public void IntTimeSeriesSummerIncrementDecrement()
153 | {
154 | var series = new IntTimeSeriesSummer(TimeSpan.FromMinutes(1), 10);
155 | for (var i = 0; i < 100; i++)
156 | {
157 | series.Increment();
158 | }
159 |
160 | for (var i = 0; i < 50; i++)
161 | {
162 | series.Decrement();
163 | }
164 |
165 | series.DataCount.Should().Be(150);
166 | series.Samples.First().Value.Should().Be(50);
167 | }
168 |
169 | [Fact]
170 | public async Task Summer()
171 | {
172 | var series = new IntTimeSeriesSummer(TimeSpan.FromSeconds(0.1));
173 | var count = 0;
174 | for (var i = 0; i < 100; i++)
175 | {
176 | await Task.Delay(new Random().Next(10, 50));
177 | series.AddNewData(i);
178 | count++;
179 | }
180 |
181 | series.DataCount.Should().Be((ulong) count);
182 | }
183 |
184 | // [Fact]
185 | // public async Task SummerGroup()
186 | // {
187 | // var interval = TimeSpan.FromSeconds(0.1);
188 | // var series = new IntGroupTimeSeriesSummer(interval, 100, Strategy.Sum, true);
189 | // var count = 0;
190 | // for (var i = 0; i < 100; i++)
191 | // {
192 | // await Task.Delay(new Random().Next(10, 50));
193 | // series.AddNewData((i % 10).ToString(), i);
194 | // count++;
195 | // }
196 | //
197 | // series.TimeSeries.Count.Should().BeGreaterThan(0);
198 | // await Task.Delay(interval * 102);
199 | //
200 | // series.TimeSeries.Count.Should().Be(0);
201 | // }
202 | //
203 | // [Fact]
204 | // public async Task SummerGroupMax()
205 | // {
206 | // var interval = TimeSpan.FromSeconds(5);
207 | // var series = new IntGroupTimeSeriesSummer(interval, 100, Strategy.Max, true);
208 | // var count = 0;
209 | // for (var i = 0; i < 100; i++)
210 | // {
211 | // series.AddNewData("host", i);
212 | // count++;
213 | // }
214 | //
215 | // series.TimeSeries.Count.Should().Be(1);
216 | // series.TimeSeries.Values.SingleOrDefault().Samples.SingleOrDefault().Value.Should().Be(99);
217 | // }
218 |
219 | [Fact]
220 | public async Task SummerMerge()
221 | {
222 | Func> FillFunc = async () =>
223 | {
224 | var series = new IntTimeSeriesSummer(TimeSpan.FromSeconds(0.1));
225 |
226 | for (var i = 0; i < 100; i++)
227 | {
228 | await Task.Delay(new Random().Next(10, 50));
229 | series.AddNewData(1);
230 | }
231 |
232 | return series;
233 | };
234 |
235 | var seriesA = FillFunc();
236 | var seriesB = FillFunc();
237 |
238 | await Task.WhenAll(seriesA, seriesB);
239 |
240 | seriesA.Result.DataCount.Should().Be(100);
241 | seriesB.Result.DataCount.Should().Be(100);
242 |
243 | seriesA.Result.Merge(seriesB.Result);
244 |
245 | seriesA.Result.DataCount.Should().Be(200);
246 |
247 | seriesA.Result.Samples.Select(s => s.Value).Sum().Should().Be(200);
248 | }
249 |
250 | [Fact]
251 | public async Task Resample()
252 | {
253 | var seriesFeature = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(2), 100);
254 |
255 | for (var i = 0; i < 100; i++)
256 | {
257 | seriesFeature.AddNewData(i);
258 |
259 | await Task.Delay(1);
260 | }
261 |
262 | seriesFeature.Resample(TimeSpan.FromMilliseconds(4), 100);
263 | var sad = seriesFeature;
264 | }
265 |
266 | [Fact]
267 | public async Task ResampleSummer()
268 | {
269 | var seriesFeature = new IntTimeSeriesSummer(TimeSpan.FromMilliseconds(2), 100);
270 |
271 | for (var i = 0; i < 100; i++)
272 | {
273 | seriesFeature.AddNewData(i);
274 |
275 | await Task.Delay(1);
276 | }
277 |
278 | seriesFeature.Resample(TimeSpan.FromMilliseconds(4), 100);
279 | }
280 |
281 |
282 | [Fact]
283 | public void MarkupAllSamples()
284 | {
285 | var seriesFeature = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10), 100);
286 | seriesFeature.MarkupAllSamples(MarkupDirection.Feature);
287 | seriesFeature.AddNewData(1);
288 | (seriesFeature.Samples.Keys.Max() - seriesFeature.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
289 | (seriesFeature.Samples.Keys.Max() - seriesFeature.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
290 | var seriesFeatureOrdered = seriesFeature.Samples.OrderBy(o => o.Key).Take(10);
291 | seriesFeatureOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
292 |
293 | var seriesPast = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10));
294 | seriesPast.MarkupAllSamples();
295 | seriesPast.AddNewData(1);
296 | (seriesPast.Samples.Keys.Max() - seriesPast.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
297 | (seriesPast.Samples.Keys.Max() - seriesPast.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
298 | var seriesPastOrdered = seriesPast.Samples.OrderBy(o => o.Key).TakeLast(10);
299 | seriesPastOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
300 |
301 | var seriesMiddle = new IntTimeSeriesAccumulator(TimeSpan.FromMilliseconds(10), 100);
302 | seriesMiddle.MarkupAllSamples(MarkupDirection.Middle);
303 | seriesMiddle.AddNewData(1);
304 | (seriesMiddle.Samples.Keys.Max() - seriesMiddle.Samples.Keys.Min()).TotalMilliseconds.Should().BeGreaterThanOrEqualTo(990);
305 | (seriesMiddle.Samples.Keys.Max() - seriesMiddle.Samples.Keys.Min()).TotalMilliseconds.Should().BeLessThanOrEqualTo(1000);
306 | var seriesMiddleOrdered = seriesMiddle.Samples.OrderBy(o => o.Key).Skip(45).Take(10);
307 | seriesMiddleOrdered.Any(a => a.Value.Count == 1).Should().BeTrue();
308 | }
309 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.TimeSeries", "ManagedCode.TimeSeries\ManagedCode.TimeSeries.csproj", "{76A0D190-83D9-41EE-8086-B01502CE9AAC}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.TimeSeries.Tests", "ManagedCode.TimeSeries.Tests\ManagedCode.TimeSeries.Tests.csproj", "{507FA370-F823-47AA-B304-8F623B3B10F1}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.TimeSeries.Benchmark", "ManagedCode.TimeSeries.Benchmark\ManagedCode.TimeSeries.Benchmark.csproj", "{8A7D8630-5F54-4B88-89BD-3385F4FA01AA}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.TimeSeries.Orleans", "ManagedCode.TimeSeries.Orleans\ManagedCode.TimeSeries.Orleans.csproj", "{30AFA0FC-C87B-411F-B71C-35C92ECDF401}"
10 | EndProject
11 | Global
12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
13 | Debug|Any CPU = Debug|Any CPU
14 | Release|Any CPU = Release|Any CPU
15 | EndGlobalSection
16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 | {76A0D190-83D9-41EE-8086-B01502CE9AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {76A0D190-83D9-41EE-8086-B01502CE9AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {76A0D190-83D9-41EE-8086-B01502CE9AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {76A0D190-83D9-41EE-8086-B01502CE9AAC}.Release|Any CPU.Build.0 = Release|Any CPU
21 | {507FA370-F823-47AA-B304-8F623B3B10F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {507FA370-F823-47AA-B304-8F623B3B10F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {507FA370-F823-47AA-B304-8F623B3B10F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {507FA370-F823-47AA-B304-8F623B3B10F1}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {8A7D8630-5F54-4B88-89BD-3385F4FA01AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {8A7D8630-5F54-4B88-89BD-3385F4FA01AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {8A7D8630-5F54-4B88-89BD-3385F4FA01AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {8A7D8630-5F54-4B88-89BD-3385F4FA01AA}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {30AFA0FC-C87B-411F-B71C-35C92ECDF401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {30AFA0FC-C87B-411F-B71C-35C92ECDF401}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {30AFA0FC-C87B-411F-B71C-35C92ECDF401}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {30AFA0FC-C87B-411F-B71C-35C92ECDF401}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseGroupNumberTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 |
3 | namespace ManagedCode.TimeSeries.Abstractions;
4 |
5 | public abstract class BaseGroupNumberTimeSeriesSummer : IDisposable
6 | where TSummer : BaseNumberTimeSeriesSummer
7 | where TNumber : INumber
8 | where TSelf : BaseGroupNumberTimeSeriesSummer
9 | {
10 | internal readonly Timer? _timer;
11 | public readonly Dictionary TimeSeries = new();
12 |
13 | protected BaseGroupNumberTimeSeriesSummer(TimeSpan sampleInterval, bool deleteOverdueSamples)
14 | {
15 | _timer = deleteOverdueSamples ? new Timer(Callback, null, sampleInterval, sampleInterval) : null;
16 | }
17 |
18 | private void Callback(object? state)
19 | {
20 | foreach (var summer in TimeSeries.ToArray())
21 | {
22 | summer.Value.DeleteOverdueSamples();
23 | lock (TimeSeries)
24 | {
25 | if (summer.Value.IsEmpty)
26 | {
27 | TimeSeries.Remove(summer.Key);
28 | }
29 | }
30 | }
31 | }
32 |
33 | public virtual void AddNewData(string key, TNumber value)
34 | {
35 | lock (TimeSeries)
36 | {
37 | if (TimeSeries.TryGetValue(key, out var summer))
38 | {
39 | summer.AddNewData(value);
40 | }
41 | else
42 | {
43 | var newSummer = CreateSummer();
44 | newSummer.AddNewData(value);
45 | TimeSeries[key] = newSummer;
46 | }
47 | }
48 | }
49 |
50 | public virtual void Increment(string key)
51 | {
52 | lock (TimeSeries)
53 | {
54 | if (TimeSeries.TryGetValue(key, out var summer))
55 | {
56 | summer.Increment();
57 | }
58 | else
59 | {
60 | var newSummer = CreateSummer();
61 | newSummer.Increment();
62 | TimeSeries[key] = newSummer;
63 | }
64 | }
65 | }
66 |
67 | public virtual void Decrement(string key)
68 | {
69 | lock (TimeSeries)
70 | {
71 | if (TimeSeries.TryGetValue(key, out var summer))
72 | {
73 | summer.Decrement();
74 | }
75 | else
76 | {
77 | var newSummer = CreateSummer();
78 | newSummer.Decrement();
79 | TimeSeries[key] = newSummer;
80 | }
81 | }
82 | }
83 |
84 | public abstract TNumber Average();
85 |
86 | public abstract TNumber Min();
87 |
88 | public abstract TNumber Max();
89 |
90 | public abstract TNumber Sum();
91 |
92 | protected abstract TSummer CreateSummer();
93 |
94 | public void Dispose()
95 | {
96 | _timer?.Dispose();
97 | }
98 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseNumberTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace ManagedCode.TimeSeries.Abstractions;
5 |
6 | public abstract class BaseNumberTimeSeriesSummer : BaseTimeSeries
7 | where TNumber : INumber where TSelf : BaseTimeSeries
8 | {
9 | protected BaseNumberTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount, Strategy strategy) : base(sampleInterval, maxSamplesCount)
10 | {
11 | Strategy = strategy;
12 | }
13 |
14 | public Strategy Strategy { get; protected set; }
15 |
16 | protected override void AddData(DateTimeOffset date, TNumber data)
17 | {
18 | if (!Samples.ContainsKey(date))
19 | {
20 | Samples.Add(date, TNumber.Zero);
21 | }
22 |
23 | Samples[date] = Update(Samples[date], data);
24 | }
25 |
26 | public override void Merge(TSelf accumulator)
27 | {
28 | DataCount += accumulator.DataCount;
29 | foreach (var sample in accumulator.Samples.ToArray())
30 | {
31 | if (Samples.ContainsKey(sample.Key))
32 | {
33 | Samples[sample.Key] = Update(Samples[sample.Key], sample.Value);
34 | }
35 | else
36 | {
37 | Samples.Add(sample.Key, sample.Value);
38 | }
39 | }
40 |
41 | CheckSamplesSize();
42 | }
43 |
44 | public override void Resample(TimeSpan sampleInterval, int samplesCount)
45 | {
46 | if (sampleInterval <= SampleInterval)
47 | {
48 | throw new InvalidOperationException();
49 | }
50 |
51 | SampleInterval = sampleInterval;
52 | MaxSamplesCount = samplesCount;
53 |
54 | var samples = Samples;
55 |
56 | Samples = new Dictionary();
57 |
58 | foreach (var (key, value) in samples)
59 | {
60 | AddNewData(key, value);
61 | }
62 | }
63 |
64 | private TNumber Update(TNumber left, TNumber right)
65 | {
66 | return Strategy switch
67 | {
68 | Strategy.Sum => left + right,
69 | Strategy.Min => TNumber.Min(left, right),
70 | Strategy.Max => TNumber.Max(left, right),
71 | Strategy.Replace => right,
72 | _ => throw new ArgumentOutOfRangeException()
73 | };
74 | }
75 |
76 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
77 | public virtual void Increment()
78 | {
79 | AddNewData(TNumber.One);
80 | }
81 |
82 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
83 | public virtual void Decrement()
84 | {
85 | AddNewData(-TNumber.One);
86 | }
87 |
88 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
89 | public virtual TNumber Average()
90 | {
91 | var sum = Sum();
92 | return TNumber.CreateChecked(sum) / TNumber.CreateChecked(Samples.Count);
93 | }
94 |
95 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
96 | public virtual TNumber? Min()
97 | {
98 | lock (Samples)
99 | {
100 | return Samples.Values.Min();
101 | }
102 | }
103 |
104 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
105 | public virtual TNumber? Max()
106 | {
107 | lock (Samples)
108 | {
109 | return Samples.Values.Max();
110 | }
111 | }
112 |
113 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
114 | public virtual TNumber Sum()
115 | {
116 | lock (Samples)
117 | {
118 | return Samples.Aggregate(TNumber.Zero, (current, sample) => current + sample.Value);
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseTimeSeries.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using ManagedCode.TimeSeries.Extensions;
3 |
4 | namespace ManagedCode.TimeSeries.Abstractions;
5 |
6 | public abstract class BaseTimeSeries :
7 | ITimeSeries where TSelf : BaseTimeSeries
8 | {
9 | private const int DefaultSampleCount = 100;
10 | private readonly object _sync = new();
11 |
12 | protected BaseTimeSeries(TimeSpan sampleInterval, int maxSamplesCount)
13 | {
14 | MaxSamplesCount = maxSamplesCount;
15 | SampleInterval = sampleInterval;
16 | Start = DateTimeOffset.UtcNow.Round(SampleInterval);
17 | End = Start;
18 | }
19 |
20 | protected BaseTimeSeries(TimeSpan sampleInterval, int maxSamplesCount, DateTimeOffset start, DateTimeOffset end, DateTimeOffset lastDate)
21 | {
22 | MaxSamplesCount = maxSamplesCount;
23 | SampleInterval = sampleInterval;
24 | Start = start.Round(SampleInterval);
25 | End = end.Round(SampleInterval);
26 | LastDate = lastDate;
27 | }
28 |
29 | public Dictionary Samples { get; protected set; } = new();
30 | public DateTimeOffset Start { get; internal set; }
31 | public DateTimeOffset End { get; protected set; }
32 |
33 | public TimeSpan SampleInterval { get; protected set; }
34 | public int MaxSamplesCount { get; protected set; }
35 |
36 | public DateTimeOffset LastDate { get; protected set; }
37 |
38 | public ulong DataCount { get; protected set; }
39 |
40 | internal void InitInternal(Dictionary samples,
41 | DateTimeOffset start , DateTimeOffset end,
42 | DateTimeOffset lastDate,
43 | ulong dataCount)
44 | {
45 | Samples = samples;
46 | Start = start;
47 | End = end;
48 | LastDate = lastDate;
49 | DataCount = dataCount;
50 | }
51 |
52 | public bool IsFull => Samples.Count >= MaxSamplesCount;
53 | public bool IsEmpty => Samples.Count == 0;
54 | public bool IsOverflow => Samples.Count > MaxSamplesCount;
55 |
56 | public abstract void Resample(TimeSpan sampleInterval, int samplesCount);
57 |
58 | public static TSelf Empty(TimeSpan? sampleInterval = null, int maxSamplesCount = 0)
59 | {
60 | return (TSelf) Activator.CreateInstance(typeof(TSelf), sampleInterval ?? TimeSpan.Zero, maxSamplesCount)!;
61 | }
62 |
63 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
64 | public abstract void Merge(TSelf accumulator);
65 |
66 | public void AddNewData(T data)
67 | {
68 | var rounded = DateTimeOffset.UtcNow.Round(SampleInterval);
69 | lock (_sync)
70 | {
71 | DataCount += 1;
72 | AddData(rounded, data);
73 | End = rounded;
74 | LastDate = DateTimeOffset.UtcNow;
75 | CheckSamplesSize();
76 | }
77 | }
78 |
79 | public void AddNewData(DateTimeOffset dateTimeOffset, T data)
80 | {
81 | lock (_sync)
82 | {
83 | DataCount += 1;
84 | AddData(dateTimeOffset.Round(SampleInterval), data);
85 | CheckSamplesSize();
86 | }
87 | }
88 |
89 | public void MarkupAllSamples(MarkupDirection direction = MarkupDirection.Past)
90 | {
91 | var samples = MaxSamplesCount > 0 ? MaxSamplesCount : DefaultSampleCount;
92 |
93 | if (direction is MarkupDirection.Past or MarkupDirection.Feature)
94 | {
95 | var now = Start;
96 | for (var i = 0; i < samples; i++)
97 | {
98 | now = now.Round(SampleInterval);
99 | if (!Samples.ContainsKey(now))
100 | {
101 | Samples.Add(now, Activator.CreateInstance());
102 | }
103 |
104 | now = direction is MarkupDirection.Feature ? now.Add(SampleInterval) : now.Subtract(SampleInterval);
105 | }
106 | }
107 | else
108 | {
109 | var nowForFeature = Start;
110 | var nowForPast = Start;
111 |
112 | for (var i = 0; i < samples / 2 + 1; i++)
113 | {
114 | nowForFeature = nowForFeature.Round(SampleInterval);
115 | nowForPast = nowForPast.Round(SampleInterval);
116 |
117 | if (!Samples.ContainsKey(nowForFeature))
118 | {
119 | Samples.Add(nowForFeature, Activator.CreateInstance());
120 | }
121 |
122 | if (!Samples.ContainsKey(nowForPast))
123 | {
124 | Samples.Add(nowForPast, Activator.CreateInstance());
125 | }
126 |
127 | nowForFeature = nowForFeature.Add(SampleInterval);
128 | nowForPast = nowForPast.Subtract(SampleInterval);
129 | }
130 | }
131 | }
132 |
133 | public void DeleteOverdueSamples()
134 | {
135 | var dateTime = DateTimeOffset.UtcNow.Round(SampleInterval);
136 | for (int i = 0; i < MaxSamplesCount; i++)
137 | {
138 | dateTime = dateTime.Subtract(SampleInterval);
139 | }
140 |
141 | lock (_sync)
142 | {
143 | foreach (var date in Samples.Keys.ToArray())
144 | {
145 | if (date < dateTime)
146 | {
147 | Samples.Remove(date);
148 | }
149 | }
150 | }
151 | }
152 |
153 | public TSelf Rebase(IEnumerable accumulators)
154 | {
155 | var empty = (TSelf) Activator.CreateInstance(typeof(TSelf), SampleInterval, MaxSamplesCount)!;
156 |
157 | foreach (var accumulator in accumulators)
158 | {
159 | Merge(accumulator);
160 | }
161 |
162 | return empty;
163 | }
164 |
165 | public void Merge(IEnumerable accumulators)
166 | {
167 | foreach (var accumulator in accumulators)
168 | {
169 | Merge(accumulator);
170 | }
171 | }
172 |
173 | public TSelf Rebase(TSelf accumulator)
174 | {
175 | var empty = (TSelf) Activator.CreateInstance(typeof(TSelf), SampleInterval, MaxSamplesCount)!;
176 | empty.Merge(accumulator);
177 |
178 | return empty;
179 | }
180 |
181 | public static TSelf operator +(BaseTimeSeries left, TSelf right)
182 | {
183 | return left.Rebase(right);
184 | }
185 |
186 | public static TSelf operator checked +(BaseTimeSeries left, TSelf right)
187 | {
188 | return left.Rebase(right);
189 | }
190 |
191 |
192 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
193 | protected abstract void AddData(DateTimeOffset now, T data);
194 |
195 | protected void CheckSamplesSize()
196 | {
197 | lock (_sync)
198 | {
199 | if (MaxSamplesCount <= 0)
200 | {
201 | return;
202 | }
203 |
204 | while (IsOverflow)
205 | {
206 | Samples.Remove(Samples.Keys.MinBy(o => o)); //check performance here
207 | }
208 | }
209 | }
210 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseTimeSeriesAccumulator.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Extensions;
2 |
3 | namespace ManagedCode.TimeSeries.Abstractions;
4 |
5 | public abstract class BaseTimeSeriesAccumulator : BaseTimeSeries, TSelf> where TSelf : BaseTimeSeries, TSelf>
6 | {
7 | protected BaseTimeSeriesAccumulator(TimeSpan sampleInterval, int maxSamplesCount) : base(sampleInterval, maxSamplesCount)
8 | {
9 | }
10 |
11 | protected BaseTimeSeriesAccumulator(TimeSpan sampleInterval, int maxSamplesCount, DateTimeOffset start, DateTimeOffset end,
12 | DateTimeOffset lastDate)
13 | : base(sampleInterval, maxSamplesCount, start, end, lastDate)
14 | {
15 | }
16 |
17 | protected override void AddData(DateTimeOffset date, T data)
18 | {
19 | if (!Samples.ContainsKey(date))
20 | {
21 | Samples.Add(date, new Queue());
22 | }
23 |
24 | Samples[date].Enqueue(data);
25 | }
26 |
27 | public void Trim()
28 | {
29 | TrimStart();
30 | TrimEnd();
31 | }
32 |
33 | public void TrimStart()
34 | {
35 | foreach (var item in Samples.ToArray())
36 | {
37 | if (item.Value.Count > 0)
38 | {
39 | break;
40 | }
41 |
42 | Samples.Remove(item.Key);
43 | }
44 | }
45 |
46 | public void TrimEnd()
47 | {
48 | foreach (var item in Samples.Reverse().ToArray())
49 | {
50 | if (item.Value.Count > 0)
51 | {
52 | break;
53 | }
54 |
55 | Samples.Remove(item.Key);
56 | }
57 | }
58 |
59 | public override void Merge(TSelf accumulator)
60 | {
61 | DataCount += accumulator.DataCount;
62 | LastDate = accumulator.LastDate > LastDate ? accumulator.LastDate : LastDate;
63 | foreach (var sample in accumulator.Samples.ToArray())
64 | {
65 | if (Samples.TryGetValue(sample.Key, out var queue))
66 | {
67 | foreach (var q in sample.Value.ToArray())
68 | {
69 | queue.Enqueue(q);
70 | }
71 | }
72 | else
73 | {
74 | Samples.Add(sample.Key, sample.Value);
75 | }
76 | }
77 |
78 | CheckSamplesSize();
79 | }
80 |
81 | public override void Resample(TimeSpan sampleInterval, int samplesCount)
82 | {
83 | if (sampleInterval <= SampleInterval)
84 | {
85 | throw new InvalidOperationException();
86 | }
87 |
88 | SampleInterval = sampleInterval;
89 | MaxSamplesCount = samplesCount;
90 |
91 | var samples = Samples;
92 |
93 | Samples = new Dictionary>();
94 |
95 | foreach (var (key, value) in samples)
96 | {
97 | foreach (var v in value)
98 | {
99 | AddNewData(key, v);
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseTimeSeriesByValueAccumulator.cs:
--------------------------------------------------------------------------------
1 | namespace ManagedCode.TimeSeries.Abstractions;
2 |
3 | public abstract class BaseTimeSeriesByValueAccumulator : BaseTimeSeries, TSelf>
4 | where TSelf : BaseTimeSeries, TSelf>
5 | {
6 | protected BaseTimeSeriesByValueAccumulator(TimeSpan sampleInterval, int samplesCount) : base(sampleInterval, samplesCount)
7 | {
8 | }
9 |
10 | protected BaseTimeSeriesByValueAccumulator(TimeSpan sampleInterval, int samplesCount, DateTimeOffset start, DateTimeOffset end,
11 | DateTimeOffset lastDate)
12 | : base(sampleInterval, samplesCount, start, end, lastDate)
13 | {
14 | }
15 |
16 | protected override void AddData(DateTimeOffset date, T data)
17 | {
18 | if (!Samples.ContainsKey(date))
19 | {
20 | Samples.Add(date, new HashSet());
21 | }
22 |
23 | Samples[date].Add(data);
24 | }
25 |
26 | // public BaseTimeSeriesByValueAccumulator Trim()
27 | // {
28 | // TrimStart();
29 | // TrimEnd();
30 | // return this;
31 | // }
32 | //
33 | // public BaseTimeSeriesByValueAccumulator TrimStart()
34 | // {
35 | // foreach (var item in Samples.ToArray())
36 | // {
37 | // if (item.Value.Count > 0)
38 | // {
39 | // break;
40 | // }
41 | //
42 | // Samples.Remove(item.Key);
43 | // }
44 | //
45 | // return this;
46 | // }
47 | //
48 | // public BaseTimeSeriesByValueAccumulator TrimEnd()
49 | // {
50 | // foreach (var item in Samples.Reverse().ToArray())
51 | // {
52 | // if (item.Value.Count > 0)
53 | // {
54 | // break;
55 | // }
56 | //
57 | // Samples.Remove(item.Key);
58 | // }
59 | //
60 | // return this;
61 | // }
62 |
63 | public override void Merge(TSelf accumulator)
64 | {
65 | DataCount += accumulator.DataCount;
66 | LastDate = accumulator.LastDate > LastDate ? accumulator.LastDate : LastDate;
67 | foreach (var sample in accumulator.Samples.ToArray())
68 | {
69 | if (Samples.TryGetValue(sample.Key, out var hashSet))
70 | {
71 | foreach (var v in sample.Value.ToArray())
72 | {
73 | hashSet.Add(v);
74 | }
75 | }
76 | else
77 | {
78 | Samples.Add(sample.Key, sample.Value);
79 | }
80 | }
81 |
82 | CheckSamplesSize();
83 | }
84 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/BaseTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace ManagedCode.TimeSeries.Abstractions;
4 |
5 | public abstract class BaseTimeSeriesSummer : BaseTimeSeries
6 | where TSummerItem : ISummerItem where TSelf : BaseTimeSeries
7 | {
8 | private readonly Strategy _strategy;
9 |
10 | protected BaseTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount, Strategy strategy) : base(sampleInterval, maxSamplesCount)
11 | {
12 | _strategy = strategy;
13 | }
14 |
15 | protected override void AddData(DateTimeOffset date, TSummerItem data)
16 | {
17 | if (!Samples.ContainsKey(date))
18 | {
19 | Samples.Add(date, TSummerItem.Zero);
20 | }
21 |
22 | Samples[date] = Update(Samples[date], data);
23 | }
24 |
25 | public override void Merge(TSelf accumulator)
26 | {
27 | DataCount += accumulator.DataCount;
28 | foreach (var sample in accumulator.Samples.ToArray())
29 | {
30 | if (Samples.ContainsKey(sample.Key))
31 | {
32 | Samples[sample.Key] = Update(Samples[sample.Key], sample.Value);
33 | }
34 | else
35 | {
36 | Samples.Add(sample.Key, sample.Value);
37 | }
38 | }
39 |
40 | CheckSamplesSize();
41 | }
42 |
43 | public override void Resample(TimeSpan sampleInterval, int samplesCount)
44 | {
45 | if (sampleInterval <= SampleInterval)
46 | {
47 | throw new InvalidOperationException();
48 | }
49 |
50 | SampleInterval = sampleInterval;
51 | MaxSamplesCount = samplesCount;
52 |
53 | var samples = Samples;
54 |
55 | Samples = new Dictionary();
56 |
57 | foreach (var (key, value) in samples)
58 | {
59 | AddNewData(key, value);
60 | }
61 | }
62 |
63 | private TSummerItem Update(TSummerItem left, TSummerItem right)
64 | {
65 | return _strategy switch
66 | {
67 | Strategy.Sum => left + right,
68 | Strategy.Min => TSummerItem.Min(left, right),
69 | Strategy.Max => TSummerItem.Max(left, right),
70 | Strategy.Replace => right,
71 | _ => throw new ArgumentOutOfRangeException()
72 | };
73 | }
74 |
75 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
76 | public virtual void Increment()
77 | {
78 | AddNewData(TSummerItem.One);
79 | }
80 |
81 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
82 | public virtual void Decrement()
83 | {
84 | AddNewData(-TSummerItem.One);
85 | }
86 |
87 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
88 | public virtual TSummerItem? Min()
89 | {
90 | lock (Samples)
91 | {
92 | return Samples.Values.Min();
93 | }
94 | }
95 |
96 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
97 | public virtual TSummerItem? Max()
98 | {
99 | lock (Samples)
100 | {
101 | return Samples.Values.Max();
102 | }
103 | }
104 |
105 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
106 | public virtual TSummerItem Sum()
107 | {
108 | lock (Samples)
109 | {
110 | return Samples.Aggregate(TSummerItem.Zero, (current, sample) => current + sample.Value);
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/ISummerItem.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 |
3 | namespace ManagedCode.TimeSeries.Abstractions;
4 |
5 | public interface ISummerItem :
6 | IUnaryNegationOperators,
7 | IAdditionOperators
8 | where TSelf : ISummerItem
9 | {
10 | /// Gets the radix, or base, for the type.
11 | static abstract TSelf Zero { get; }
12 |
13 | /// Gets the value 1 for the type.
14 | static abstract TSelf One { get; }
15 |
16 | /// Compares two values to compute which is lesser.
17 | /// The value to compare with .
18 | /// The value to compare with .
19 | /// if it is less than ; otherwise, .
20 | static abstract TSelf Min(TSelf x, TSelf y);
21 |
22 | /// Compares two values to compute which is greater.
23 | /// The value to compare with .
24 | /// The value to compare with .
25 | /// if it is greater than ; otherwise, .
26 | static abstract TSelf Max(TSelf x, TSelf y);
27 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/ITimeSeries.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.Contracts;
2 |
3 | namespace ManagedCode.TimeSeries.Abstractions;
4 |
5 | public interface ITimeSeries where TSelf : ITimeSeries
6 | {
7 | void AddNewData(T data);
8 | void AddNewData(DateTimeOffset dateTimeOffset, T data);
9 |
10 | void DeleteOverdueSamples();
11 | void MarkupAllSamples(MarkupDirection direction = MarkupDirection.Past);
12 |
13 | [Pure]
14 | TSelf Rebase(TSelf accumulator);
15 |
16 | [Pure]
17 | TSelf Rebase(IEnumerable accumulators);
18 |
19 | void Merge(TSelf accumulator);
20 | void Merge(IEnumerable accumulators);
21 |
22 | void Resample(TimeSpan sampleInterval, int samplesCount);
23 |
24 | [Pure]
25 | static abstract TSelf Empty(TimeSpan? sampleInterval = null, int maxSamplesCount = 0);
26 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Abstractions/Strategy.cs:
--------------------------------------------------------------------------------
1 | namespace ManagedCode.TimeSeries.Abstractions;
2 |
3 | public enum Strategy
4 | {
5 | Sum,
6 | Min,
7 | Max,
8 | Replace,
9 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Accumulators/DoubleTimeSeriesAccumulator.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Accumulators;
4 |
5 | public class DoubleTimeSeriesAccumulator : BaseTimeSeriesAccumulator
6 | {
7 | public DoubleTimeSeriesAccumulator(TimeSpan sampleInterval, int maxSamplesCount = 0) : base(sampleInterval, maxSamplesCount)
8 | {
9 | }
10 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Accumulators/FloatTimeSeriesAccumulator.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Accumulators;
4 |
5 | public class FloatTimeSeriesAccumulator : BaseTimeSeriesAccumulator
6 | {
7 | public FloatTimeSeriesAccumulator(TimeSpan sampleInterval, int maxSamplesCount = 0) : base(sampleInterval, maxSamplesCount)
8 | {
9 | }
10 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Accumulators/IntTimeSeriesAccumulator.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Accumulators;
4 |
5 | public class IntTimeSeriesAccumulator : BaseTimeSeriesAccumulator
6 | {
7 | public IntTimeSeriesAccumulator(TimeSpan sampleInterval, int maxSamplesCount = 0) : base(sampleInterval, maxSamplesCount)
8 | {
9 | }
10 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Accumulators/StringTimeSeriesAccumulator.cs:
--------------------------------------------------------------------------------
1 | // namespace ManagedCode.TimeSeries.Accumulators;
2 | //
3 | // public class StringTimeSeriesAccumulator : BaseTimeSeriesByValueAccumulator
4 | // {
5 | // public StringTimeSeriesAccumulator(TimeSpan sampleInterval, int samplesCount = 0) : base(sampleInterval, samplesCount)
6 | // {
7 | // }
8 | // }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Extensions/RoundDateTimeAndTimeSpanExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace ManagedCode.TimeSeries.Extensions;
2 |
3 | public static class RoundDateTimeAndTimeSpanExtensions
4 | {
5 | public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType)
6 | {
7 | return new TimeSpan(Convert.ToInt64(Math.Round(time.Ticks / (decimal)roundingInterval.Ticks, roundingType)) * roundingInterval.Ticks);
8 | }
9 |
10 | public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval)
11 | {
12 | return Round(time, roundingInterval, MidpointRounding.ToEven);
13 | }
14 |
15 | public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval)
16 | {
17 | return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
18 | }
19 |
20 | public static DateTimeOffset Round(this DateTimeOffset dateTimeOffset, TimeSpan roundingInterval)
21 | {
22 | var datetime = dateTimeOffset.UtcDateTime.Round(roundingInterval);
23 |
24 | return new DateTimeOffset(datetime, dateTimeOffset.Offset);
25 | }
26 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/ManagedCode.TimeSeries.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | 11
6 | enable
7 | enable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | true
17 | GeneratedCodeAttribute
18 | [*]*.Migrations.*
19 | **/MyFile.cs
20 | lcov
21 |
22 |
23 |
24 |
25 | ManagedCode.TimeSeries
26 | ManagedCode.TimeSeries
27 | TimeSeries
28 | managedcode, TimeSeries
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/MarkupDirection.cs:
--------------------------------------------------------------------------------
1 | namespace ManagedCode.TimeSeries;
2 |
3 | public enum MarkupDirection
4 | {
5 | Middle,
6 | Past,
7 | Feature
8 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/DoubleGroupTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class DoubleGroupTimeSeriesSummer : BaseGroupNumberTimeSeriesSummer
6 | {
7 | private readonly TimeSpan _sampleInterval;
8 | private readonly int _samplesCount;
9 | private readonly Strategy _strategy;
10 |
11 | public DoubleGroupTimeSeriesSummer(TimeSpan sampleInterval, int samplesCount, Strategy strategy, bool deleteOverdueSamples) : base(sampleInterval,
12 | deleteOverdueSamples)
13 | {
14 | _sampleInterval = sampleInterval;
15 | _samplesCount = samplesCount;
16 | _strategy = strategy;
17 | }
18 |
19 | public override double Average()
20 | {
21 | lock (TimeSeries)
22 | {
23 | if (TimeSeries.Count == 0)
24 | return 0;
25 |
26 | return TimeSeries.Select(s => s.Value.Average()).Average();
27 | }
28 | }
29 |
30 | public override double Min()
31 | {
32 | lock (TimeSeries)
33 | {
34 | if (TimeSeries.Count == 0)
35 | return 0;
36 |
37 | return TimeSeries.Select(s => s.Value.Min()).Min();
38 | }
39 | }
40 |
41 | public override double Max()
42 | {
43 | lock (TimeSeries)
44 | {
45 | if (TimeSeries.Count == 0)
46 | return 0;
47 |
48 | return TimeSeries.Select(s => s.Value.Max()).Max();
49 | }
50 | }
51 |
52 | public override double Sum()
53 | {
54 | lock (TimeSeries)
55 | {
56 | if (TimeSeries.Count == 0)
57 | return 0;
58 |
59 | return TimeSeries.Select(s => s.Value.Sum()).Sum();
60 | }
61 | }
62 |
63 | protected override DoubleTimeSeriesSummer CreateSummer()
64 | {
65 | throw new NotImplementedException();
66 | }
67 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/DoubleTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class DoubleTimeSeriesSummer : BaseNumberTimeSeriesSummer
6 | {
7 | public DoubleTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount, Strategy strategy) : base(sampleInterval, maxSamplesCount, strategy)
8 | {
9 | }
10 |
11 | public DoubleTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount) : base(sampleInterval, maxSamplesCount, Strategy.Sum)
12 | {
13 | }
14 |
15 | public DoubleTimeSeriesSummer(TimeSpan sampleInterval) : base(sampleInterval, 0, Strategy.Sum)
16 | {
17 | }
18 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/FloatGroupNumberTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class FloatGroupNumberTimeSeriesSummer : BaseGroupNumberTimeSeriesSummer
6 | {
7 | private readonly TimeSpan _sampleInterval;
8 | private readonly int _samplesCount;
9 | private readonly Strategy _strategy;
10 |
11 | public FloatGroupNumberTimeSeriesSummer(TimeSpan sampleInterval, int samplesCount, Strategy strategy, bool deleteOverdueSamples) : base(sampleInterval,
12 | deleteOverdueSamples)
13 | {
14 | _sampleInterval = sampleInterval;
15 | _samplesCount = samplesCount;
16 | _strategy = strategy;
17 | }
18 |
19 | public override float Average()
20 | {
21 | lock (TimeSeries)
22 | {
23 | if (TimeSeries.Count == 0)
24 | return 0;
25 |
26 | return TimeSeries.Select(s => s.Value.Average()).Average();
27 | }
28 | }
29 |
30 | public override float Min()
31 | {
32 | lock (TimeSeries)
33 | {
34 | if (TimeSeries.Count == 0)
35 | return 0;
36 |
37 | return TimeSeries.Select(s => s.Value.Min()).Min();
38 | }
39 | }
40 |
41 | public override float Max()
42 | {
43 | lock (TimeSeries)
44 | {
45 | if (TimeSeries.Count == 0)
46 | return 0;
47 |
48 | return TimeSeries.Select(s => s.Value.Max()).Max();
49 | }
50 | }
51 |
52 | public override float Sum()
53 | {
54 | lock (TimeSeries)
55 | {
56 | if (TimeSeries.Count == 0)
57 | return 0;
58 |
59 | return TimeSeries.Select(s => s.Value.Sum()).Sum();
60 | }
61 | }
62 |
63 | protected override FloatTimeSeriesSummer CreateSummer()
64 | {
65 | return new FloatTimeSeriesSummer(_sampleInterval, _samplesCount, _strategy);
66 | }
67 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/FloatTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class FloatTimeSeriesSummer : BaseNumberTimeSeriesSummer
6 | {
7 | public FloatTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount, Strategy strategy) : base(sampleInterval, maxSamplesCount, strategy)
8 | {
9 | }
10 |
11 | public FloatTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount) : base(sampleInterval, maxSamplesCount, Strategy.Sum)
12 | {
13 | }
14 |
15 | public FloatTimeSeriesSummer(TimeSpan sampleInterval) : base(sampleInterval, 0, Strategy.Sum)
16 | {
17 | }
18 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/IntGroupNumberTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class IntGroupNumberTimeSeriesSummer : BaseGroupNumberTimeSeriesSummer
6 | {
7 | private readonly TimeSpan _sampleInterval;
8 | private readonly int _samplesCount;
9 | private readonly Strategy _strategy;
10 |
11 | public IntGroupNumberTimeSeriesSummer(TimeSpan sampleInterval, int samplesCount, Strategy strategy, bool deleteOverdueSamples) : base(sampleInterval, deleteOverdueSamples)
12 | {
13 | _sampleInterval = sampleInterval;
14 | _samplesCount = samplesCount;
15 | _strategy = strategy;
16 | }
17 |
18 | public override int Average()
19 | {
20 | lock (TimeSeries)
21 | {
22 | if (TimeSeries.Count == 0)
23 | return 0;
24 |
25 | return Convert.ToInt32(Math.Round(TimeSeries.Select(s => s.Value.Average()).Average()));
26 | }
27 | }
28 |
29 | public override int Min()
30 | {
31 | lock (TimeSeries)
32 | {
33 | if (TimeSeries.Count == 0)
34 | return 0;
35 |
36 | return TimeSeries.Select(s => s.Value.Min()).Min();
37 | }
38 | }
39 |
40 | public override int Max()
41 | {
42 | lock (TimeSeries)
43 | {
44 | if (TimeSeries.Count == 0)
45 | return 0;
46 |
47 | return TimeSeries.Select(s => s.Value.Max()).Max();
48 | }
49 | }
50 |
51 | public override int Sum()
52 | {
53 | lock (TimeSeries)
54 | {
55 | if (TimeSeries.Count == 0)
56 | return 0;
57 |
58 | return TimeSeries.Select(s => s.Value.Sum()).Sum();
59 | }
60 | }
61 |
62 | protected override IntTimeSeriesSummer CreateSummer()
63 | {
64 | return new IntTimeSeriesSummer(_sampleInterval, _samplesCount, _strategy);
65 | }
66 | }
--------------------------------------------------------------------------------
/ManagedCode.TimeSeries/Summers/IntTimeSeriesSummer.cs:
--------------------------------------------------------------------------------
1 | using ManagedCode.TimeSeries.Abstractions;
2 |
3 | namespace ManagedCode.TimeSeries.Summers;
4 |
5 | public class IntTimeSeriesSummer : BaseNumberTimeSeriesSummer
6 | {
7 | public IntTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount, Strategy strategy) : base(sampleInterval, maxSamplesCount, strategy)
8 | {
9 | }
10 |
11 | public IntTimeSeriesSummer(TimeSpan sampleInterval, int maxSamplesCount) : base(sampleInterval, maxSamplesCount, Strategy.Sum)
12 | {
13 | }
14 |
15 | public IntTimeSeriesSummer(TimeSpan sampleInterval) : base(sampleInterval, 0, Strategy.Sum)
16 | {
17 | }
18 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # TimeSeries
4 |
5 | [](https://github.com/managedcode/TimeSeries/actions/workflows/dotnet.yml)
6 | [](https://coveralls.io/github/managedcode/TimeSeries?branch=main)
7 | [](https://github.com/managedcode/TimeSeries/actions/workflows/nuget.yml)
8 | [](https://github.com/managedcode/TimeSeries/actions/workflows/codeql-analysis.yml)
9 |
10 | | Version | Package | Description |
11 | | ------- |-------------------------------------------------------------------------------------------------------------------------------------|-----------------|
12 | |[](https://www.nuget.org/packages/ManagedCode.TimeSeries) | [ManagedCode.TimeSeries](https://www.nuget.org/packages/ManagedCode.TimeSeries) | Core |
13 |
14 | ---
15 |
16 | ## Motivation
17 | Time series data is a common type of data in many applications, such as finance, physics, and engineering.
18 | It is often necessary to store and manipulate large amounts of time series data efficiently in order to perform analysis and make predictions.
19 |
20 | Our C# library, TimeSeries, provides convenient tools for working with time series data in C#.
21 | It includes classes for accumulating and summarizing data in time frames, as well as storing and compressing the data efficiently. This makes it easy to add and manage time series data in your C# projects.
22 |
23 | ## Features
24 | - Accumulators for adding data to time frames.
25 | - Summers for summarizing data in time frames.
26 | - Efficient storage and compression of time series data.
27 |
28 | ## Example
29 | Here's an example of how you might use the TimeSeries library to accumulate and summarize data in a time frame:
30 |
31 | ```csharp
32 | using ManagedCode.TimeSeries;
33 |
34 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(5)); // step
35 | for (int i = 0; i < count; i++)
36 | {
37 | series.AddNewData(i);
38 | }
39 | ```
40 |
41 | ```csharp
42 | using ManagedCode.TimeSeries;
43 |
44 | var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(0.1));
45 | for (var i = 0; i < 1000; i++)
46 | {
47 | await Task.Delay(new Random().Next(1, 5));
48 | series.AddNewData(i);
49 | }
50 |
51 | series.DataCount; // 1000
52 | ```
53 |
54 | ## Installation
55 | To install the TimeSeries library, you can use NuGet:
56 |
57 | ```bash
58 | dotnet add package ManagedCode.TimeSeries
59 | ```
60 |
61 |
62 | Conclusion
63 | In summary, the TimeSeries library provides convenient tools for working with time series data in C#.
64 | Its accumulators and summers make it easy to add and summarize data in time frames, and its efficient storage and compression capabilities ensure.
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/managedcode/TimeSeries/8ab0054c9dfa1094143abdc49d006609fe5fd4fd/logo.png
--------------------------------------------------------------------------------