├── .azure └── pipelines │ ├── ci-benchmarks.yml │ ├── ci-demos.yml │ ├── ci-docs.yml │ ├── ci.yml │ └── jobs │ ├── benchmarks.yml │ ├── build_and_test.yml │ ├── build_docs.yml │ ├── coverage.yml │ ├── demos.yml │ ├── deploy_docs.yml │ ├── deploy_nuget.yml │ └── steps │ └── dotnet-install.yml ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── pull_request_template.md ├── .gitignore ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE ├── README.md ├── api-doc ├── .gitignore ├── api │ ├── .gitignore │ └── index.md ├── articles │ ├── DeadBand.md │ ├── SwingingDoor.md │ ├── images │ │ ├── DeadBand_01.jpg │ │ ├── DeadBand_02.png │ │ ├── dead-band_error.png │ │ ├── dead-band_maxDelta.png │ │ ├── dead-band_trend.png │ │ ├── demo_01.png │ │ ├── demo_02.png │ │ ├── demo_03.png │ │ ├── swinging-door_01.png │ │ ├── swinging-door_02.png │ │ ├── swinging-door_error.png │ │ ├── swinging-door_maxDelta.png │ │ └── swinging-door_trend.png │ └── toc.yml ├── docfx.json ├── filterConfig.yml ├── images │ └── logo_32.png ├── index.md └── toc.yml ├── build.sh ├── data ├── dead-band │ ├── maxDelta.plt │ ├── maxDelta_compressed.csv │ ├── maxDelta_raw.csv │ ├── trend.plt │ ├── trend_compressed.csv │ └── trend_raw.csv └── swinging-door │ ├── maxDelta.plt │ ├── maxDelta_compressed.csv │ ├── maxDelta_raw.csv │ ├── trend.plt │ ├── trend1.plt │ ├── trend1_compressed.csv │ ├── trend1_raw.csv │ ├── trend2.plt │ ├── trend2_compressed.csv │ ├── trend2_raw.csv │ ├── trend3.plt │ ├── trend3_compressed.csv │ ├── trend3_mini.plt │ ├── trend3_mini_compressed.csv │ ├── trend3_mini_raw.csv │ ├── trend3_raw.csv │ ├── trend_compressed.csv │ └── trend_raw.csv ├── demos ├── .gitignore ├── Directory.Build.props ├── Dockerfiles │ └── dotnet-gnuplot5 │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ └── build.sh ├── gfoidl.DataCompression.Demos.Async │ ├── Program.cs │ └── gfoidl.DataCompression.Demos.Async.csproj ├── gfoidl.DataCompression.Demos.DeadBand.Stats │ ├── Program.cs │ ├── data │ │ └── error.plt │ └── gfoidl.DataCompression.Demos.DeadBand.Stats.csproj ├── gfoidl.DataCompression.Demos.DeadBand │ ├── Program.cs │ ├── data │ │ ├── coolant-temp.csv │ │ └── coolant-temp.plt │ └── gfoidl.DataCompression.Demos.DeadBand.csproj ├── gfoidl.DataCompression.Demos.LiveData │ ├── Program.cs │ ├── data │ │ ├── agt_n_awt1.csv │ │ ├── agt_n_awt1.plt │ │ ├── agt_zyl_6.csv │ │ ├── agt_zyl_6.plt │ │ ├── erregerspannung.csv │ │ └── erregerspannung.plt │ └── gfoidl.DataCompression.Demos.LiveData.csproj ├── gfoidl.DataCompression.Demos.SwingingDoor.Stats │ ├── Program.cs │ ├── data │ │ └── error.plt │ └── gfoidl.DataCompression.Demos.SwingingDoor.Stats.csproj ├── gfoidl.DataCompression.Demos.SwingingDoor │ ├── Program.cs │ ├── data │ │ ├── coolant-temp.csv │ │ └── coolant-temp.plt │ └── gfoidl.DataCompression.Demos.SwingingDoor.csproj ├── gfoidl.DataCompression.Demos.sln └── run.sh ├── gfoidl.DataCompression.sln ├── perf ├── .gitignore ├── Directory.Build.props ├── gfoidl.DataCompression.Benchmarks │ ├── Base.cs │ ├── Categories.cs │ ├── DeadBandCompression.cs │ ├── DeadBandCompressionAsync.cs │ ├── Infrastructure │ │ └── CompressionFactories.cs │ ├── IteratorInstantiaton.cs │ ├── Program.cs │ ├── SwingingDoorCompression.cs │ ├── SwingingDoorCompressionAsync.cs │ ├── SwingingDoorCompressionIterator │ │ └── SwingingDoorCompressionIteratorCloseTheDoorBenchmarks.cs │ └── gfoidl.DataCompression.Benchmarks.csproj ├── gfoidl.DataCompression.ProfilingDemo │ ├── Program.cs │ └── gfoidl.DataCompression.ProfilingDemo.csproj └── run-benchmarks.sh ├── source ├── Directory.Build.props ├── Directory.Build.targets └── gfoidl.DataCompression │ ├── Builders │ ├── ArrayBuilder.cs │ ├── ICollectionBuilder.cs │ └── ListBuilder.cs │ ├── Compression.cs │ ├── Compression │ ├── Compression.cd │ ├── DeadBandCompression │ │ ├── AsyncEnumerableIterator.cs │ │ ├── DeadBandCompression.cs │ │ ├── DeadBandCompressionIterator.cs │ │ ├── IndexedIterator.cs │ │ └── SequentialEnumerableIterator.cs │ ├── Iterators.cd │ ├── NoCompression │ │ ├── AsyncEnumerableIterator.cs │ │ ├── EnumerableIterator.cs │ │ ├── NoCompression.cs │ │ └── NoCompressionIterator.cs │ └── SwingingDoorCompression │ │ ├── AsyncEnumerableIterator.cs │ │ ├── IndexedIterator.cs │ │ ├── SequentialEnumerableIterator.cs │ │ ├── SwingingDoorCompression.cs │ │ └── SwingingDoorCompressionIterator.cs │ ├── DataPoint.cs │ ├── DataPointIndexedIterator.cs │ ├── DataPointIterator.Async.cs │ ├── DataPointIterator.Enumerable.cs │ ├── DataPointIterator.cs │ ├── DataPointSerializer.cs │ ├── EmptyDataPointIterator.cs │ ├── ExtensionMethods.cs │ ├── ICompression.cs │ ├── Strings.Designer.cs │ ├── Strings.resx │ ├── ThrowHelper.cs │ ├── Wrappers │ ├── ArrayWrapper.cs │ └── ListWrapper.cs │ └── gfoidl.DataCompression.csproj └── tests ├── .editorconfig ├── Directory.Build.props └── gfoidl.DataCompression.Tests ├── Builders └── ArrayBuilderTests │ ├── Add.cs │ └── AddRange.cs ├── Compression ├── Base.cs ├── DeadBandCompressionTests │ ├── Base.cs │ ├── Clone.cs │ ├── IteratorCaching.cs │ ├── MoveNext.cs │ ├── MoveNextAsync.cs │ ├── ProcessAsyncCore.cs │ ├── ProcessCore.cs │ ├── ToArray.cs │ ├── ToArrayAsync.cs │ ├── ToList.cs │ └── ToListAsync.cs ├── DisposeTests.cs ├── NoCompressionTests │ ├── Base.cs │ ├── Clone.cs │ ├── MoveNext.cs │ ├── MoveNextAsync.cs │ ├── ProcessAsyncCore.cs │ ├── ProcessCore.cs │ ├── ToArray.cs │ ├── ToArrayAsync.cs │ ├── ToList.cs │ └── ToListAsync.cs └── SwingingDoorCompressionTests │ ├── Base.cs │ ├── Clone.cs │ ├── IteratorCaching.cs │ ├── MoveNext.cs │ ├── MoveNextAsync.cs │ ├── ProcessAsyncCore.cs │ ├── ProcessCore.cs │ ├── ToArray.cs │ ├── ToArrayAsync.cs │ ├── ToList.cs │ └── ToListAsync.cs ├── Constants.cs ├── DataPointIteratorTests └── Empty.cs ├── DataPointSerializerTests └── Roundtrip.cs ├── DataPointTests ├── CalculatePoint.cs ├── Ctor.cs ├── Equals.cs ├── GetHashCode.cs ├── Gradient.cs └── ToTimeValue.cs ├── ExtensionMethodsTests ├── Base.cs ├── DeadBandCompression.cs ├── NoCompression.cs └── SwingingDoorCompression.cs ├── MySetUpClass.cs ├── Wrappers ├── ArrayWrapperTests │ ├── Ctor.cs │ ├── Indexer.cs │ └── NotImplementedMembers.cs └── ListWrapperTests │ ├── Ctor.cs │ ├── Indexer.cs │ └── NotImplementedMembers.cs └── gfoidl.DataCompression.Tests.csproj /.azure/pipelines/ci-benchmarks.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_NOLOGO: 1 3 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 4 | CI_BUILD_NUMBER: $(Build.BuildId) 5 | BRANCH_NAME: $(Build.SourceBranchName) 6 | TAG_NAME: $(Build.SourceBranchName) 7 | 8 | trigger: none 9 | # pr trigger must not be excluded, but in the UI for the pipeline definition a setting has to be made. 10 | # See https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#comment-triggers 11 | # for further info. 12 | 13 | schedules: 14 | - cron: "0 5 * * *" # 05:00 each day 15 | displayName: "Daily build" 16 | branches: 17 | include: 18 | - master 19 | 20 | stages: 21 | - stage: Benchmarks 22 | jobs: 23 | - template: jobs/benchmarks.yml 24 | -------------------------------------------------------------------------------- /.azure/pipelines/ci-demos.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_NOLOGO: 1 3 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 4 | CI_BUILD_NUMBER: $(Build.BuildId) 5 | BRANCH_NAME: $(Build.SourceBranchName) 6 | TAG_NAME: $(Build.SourceBranchName) 7 | 8 | trigger: none 9 | # pr trigger must not be excluded, but in the UI for the pipeline definition a setting has to be made. 10 | # See https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#comment-triggers 11 | # for further info. 12 | 13 | schedules: 14 | - cron: "15 4 * * *" # 04:15 each day 15 | displayName: "Daily build" 16 | branches: 17 | include: 18 | - master 19 | 20 | resources: 21 | containers: 22 | - container: dotnet-sdk-gnuplot5 23 | image: ghcr.io/gfoidl/datacompression/dotnet-gnuplot5 24 | 25 | stages: 26 | - stage: Demos 27 | jobs: 28 | - template: jobs/demos.yml 29 | -------------------------------------------------------------------------------- /.azure/pipelines/ci-docs.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_NOLOGO: 1 3 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 4 | CI_BUILD_NUMBER: $(Build.BuildId) 5 | BRANCH_NAME: $(Build.SourceBranchName) 6 | TAG_NAME: $(Build.SourceBranchName) 7 | 8 | # We don't need a CI trigger here, as the pipeline trigger starts this pipeline for master anyway. 9 | trigger: none 10 | 11 | resources: 12 | pipelines: 13 | - pipeline: ci-build 14 | source: DataCompression 15 | trigger: 16 | branches: 17 | include: 18 | - master 19 | 20 | stages: 21 | - stage: Build_Docs 22 | jobs: 23 | - template: jobs/build_docs.yml 24 | 25 | - stage: Deploy 26 | dependsOn: 27 | - Build_Docs 28 | condition: and( succeeded(), startsWith( variables['Build.SourceBranch'], 'refs/tags' ) ) 29 | jobs: 30 | - template: jobs/deploy_docs.yml 31 | -------------------------------------------------------------------------------- /.azure/pipelines/ci.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_NOLOGO: 1 3 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 4 | CI_BUILD_NUMBER: $(Build.BuildId) 5 | BRANCH_NAME: $(Build.SourceBranchName) 6 | TAG_NAME: $(Build.SourceBranchName) 7 | 8 | trigger: 9 | - master 10 | - refs/tags/v* 11 | 12 | pr: 13 | branches: 14 | include: 15 | - master 16 | 17 | stages: 18 | - stage: Build_Test 19 | jobs: 20 | - template: jobs/build_and_test.yml 21 | parameters: 22 | # Need for tests .NET Framework 4.8 23 | name: windows 24 | vmImage: 'windows-2022' 25 | 26 | - stage: Code_Coverage 27 | dependsOn: 28 | - Build_Test 29 | jobs: 30 | - template: jobs/coverage.yml 31 | 32 | - stage: Deploy 33 | dependsOn: 34 | - Build_Test 35 | condition: and( succeeded(), startsWith( variables['Build.SourceBranch'], 'refs/tags' ) ) 36 | jobs: 37 | - template: jobs/deploy_nuget.yml 38 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/benchmarks.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: benchmarks 3 | pool: 4 | vmImage: ubuntu-20.04 5 | steps: 6 | # ~SDKs already installed~ 7 | - template: steps/dotnet-install.yml 8 | 9 | - bash: | 10 | cd perf/gfoidl.DataCompression.Benchmarks 11 | dotnet restore 12 | dotnet build -c Release --no-restore 13 | displayName: build 14 | 15 | - bash: | 16 | cd perf 17 | chmod +x run-benchmarks.sh 18 | ./run-benchmarks.sh 19 | displayName: run benchmarks 20 | 21 | - publish: perf/gfoidl.DataCompression.Benchmarks/BenchmarkDotNet.Artifacts/results 22 | artifact: bench-results 23 | displayName: publish benchmark results 24 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/build_and_test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | name: '' 3 | vmImage: '' 4 | 5 | jobs: 6 | - job: ${{ parameters.name }}_build 7 | displayName: '${{ parameters.name }} build and test' 8 | pool: 9 | vmImage: ${{ parameters.vmImage }} 10 | strategy: 11 | matrix: 12 | debug-build: 13 | BUILD_CONFIG: Debug 14 | release-build: 15 | BUILD_CONFIG: Release 16 | steps: 17 | # SDKs already installed 18 | #- template: steps/dotnet-install.yml 19 | 20 | - bash: | 21 | echo 'installed sdks:' 22 | dotnet --list-sdks 23 | echo "-------------------------------------------------" 24 | echo 'environment variables:' 25 | env | sort 26 | 27 | chmod u+x ./build.sh 28 | displayName: init 29 | 30 | - bash: ./build.sh build 31 | displayName: build 32 | 33 | # coverlet.msbuild must be added as package to the tests 34 | - bash: ./build.sh test-coverage 35 | displayName: test 36 | 37 | - task: PublishTestResults@2 38 | condition: always() 39 | inputs: 40 | testRunner: VSTest 41 | testResultsFiles: '**/*.trx' 42 | 43 | - bash: | 44 | cd tests/Coverage 45 | 46 | workDir="$(System.DefaultWorkingDirectory)" 47 | 48 | if [[ "$AGENT_OS" == "Windows_NT" ]]; then 49 | # Windows needs special treetment for the substitution due the \ 50 | workDir="${workDir//\\/\\\\}\\\\" 51 | else 52 | workDir+="/" 53 | fi 54 | 55 | # Mac has a different sed, so special case it (hooray for standars ;-)) 56 | if [[ "$AGENT_OS" != "Darwin" ]]; then 57 | sed -i 's|[^<]*|/|g' "Cobertura.xml" 58 | sed -i "s|${workDir}||g" "Cobertura.xml" 59 | else 60 | sed -i '' 's|[^<]+|/|g' "Cobertura.xml" 61 | sed -i '' "s|${workDir}||g" "Cobertura.xml" 62 | fi 63 | displayName: make Cobertura-paths relative 64 | 65 | # shortcut for PublishPipelineArtifact 66 | # Coverage report will be created later in a different stage 67 | - publish: tests/Coverage/Cobertura.xml 68 | artifact: 'Coverage-${{ parameters.name }}-${{ parameters.vmImage }}-$(BUILD_CONFIG)' 69 | displayName: publish artifact code coverage 70 | 71 | - bash: ./build.sh pack 72 | displayName: pack 73 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['BUILD_CONFIG'], 'Release')) 74 | 75 | - publish: 'NuGet-Packed' 76 | artifact: 'NuGet-Packed' 77 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['BUILD_CONFIG'], 'Release'), ne(variables['Build.Reason'], 'PullRequest')) 78 | displayName: publish artifact NuGet package 79 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/build_docs.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: docs_build 3 | pool: 4 | vmImage: 'windows-2022' 5 | steps: 6 | - bash: | 7 | export PATH="$(pwd)/dotnet:$PATH" 8 | echo 'installed sdks:' 9 | dotnet --list-sdks 10 | echo "-------------------------------------------------" 11 | 12 | chmod u+x ./build.sh 13 | ./build.sh build 14 | displayName: build 15 | 16 | - powershell: | 17 | choco install docfx -y 18 | 19 | if ($lastexitcode -ne 0) { 20 | throw ("Error generating document") 21 | } 22 | displayName: 'install docfx' 23 | 24 | - powershell: | 25 | cd api-doc 26 | docfx 27 | 28 | if ($lastexitcode -ne 0) { 29 | throw ("Error generating document") 30 | } 31 | displayName: 'docfx build' 32 | 33 | - publish: 'api-doc/_site' 34 | artifact: 'docs' 35 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/coverage.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: coverage_report_quality 3 | pool: 4 | vmImage: 'ubuntu-20.04' 5 | steps: 6 | # checkout is needed for file-references 7 | #- checkout: none 8 | 9 | - task: DownloadPipelineArtifact@2 10 | inputs: 11 | targetPath: './coverage' 12 | 13 | - task: PublishCodeCoverageResults@1 14 | inputs: 15 | codeCoverageTool: Cobertura 16 | summaryFileLocation: 'coverage/**/Cobertura.xml' 17 | pathToSources: $(System.DefaultWorkingDirectory) 18 | 19 | # extension from Marketplace https://marketplace.visualstudio.com/acquisition?itemName=mspremier.BuildQualityChecks 20 | - task: BuildQualityChecks@6 21 | displayName: 'Check build quality' 22 | inputs: 23 | checkCoverage: true 24 | coverageFailOption: 'build' 25 | coverageType: 'lines' 26 | allowCoverageVariance: true 27 | coverageVariance: '0.5' 28 | baseBranchRef: 'master' 29 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/demos.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: demos 3 | pool: 4 | vmImage: ubuntu-20.04 5 | container: dotnet-sdk-gnuplot5 6 | variables: 7 | SKIP_PLOT_DISPLAY: true 8 | steps: 9 | - bash: | 10 | cd demos 11 | dotnet restore 12 | dotnet build --no-restore 13 | displayName: build 14 | 15 | - bash: | 16 | cd demos 17 | chmod +x run.sh 18 | ./run.sh 19 | displayName: run demos 20 | 21 | - bash: | 22 | cd demos 23 | mkdir plots 24 | 25 | for plot in */**/*.png; do 26 | mv $plot plots 27 | done 28 | 29 | ls -la --color plots 30 | displayName: collect plots 31 | 32 | - publish: demos/plots 33 | artifact: plots 34 | displayName: publish plots 35 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/deploy_docs.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: deploy_docs 3 | pool: 4 | vmImage: 'ubuntu-20.04' 5 | variables: 6 | GH_USER: gfoidl 7 | GH_EMAIL: $(GITHUB_EMAIL) 8 | steps: 9 | - checkout: self 10 | persistCredentials: true 11 | 12 | - bash: | 13 | git config user.name $GH_USER 14 | git config user.email $GH_EMAIL 15 | displayName: '[git] Configure User' 16 | 17 | - bash: git checkout gh-pages 18 | displayName: '[git] Set development branch' 19 | 20 | - bash: git rm -rf . 21 | displayName: '[git] Clean working dir' 22 | 23 | - task: DownloadPipelineArtifact@2 24 | inputs: 25 | artifactName: 'docs' 26 | targetPath: './' 27 | 28 | - bash: | 29 | git add --all 30 | git commit -m "Pipelines-Bot: Updated site via $(Build.SourceVersion)" 31 | echo "" 32 | git log --oneline 33 | displayName: '[git] Creating commit' 34 | 35 | - bash: 'git push origin gh-pages' 36 | displayName: '[git] Push changes to remote' 37 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/deploy_nuget.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: deploy_nuget 3 | pool: 4 | vmImage: 'ubuntu-20.04' 5 | steps: 6 | - checkout: none 7 | 8 | - task: DownloadPipelineArtifact@2 9 | inputs: 10 | artifactName: 'NuGet-Packed' 11 | targetPath: './' 12 | 13 | - bash: | 14 | echo "NuGet-Packed:" 15 | ls -la . 16 | 17 | if [[ "$TAG_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(preview-[0-9]+))$ ]]; then 18 | mkdir deploy_custom 19 | 20 | for package in ./*.*nupkg; do 21 | mv $package deploy_custom 22 | done 23 | 24 | echo "##vso[task.setvariable variable=deploy_custom;]1" 25 | elif [[ "$TAG_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then 26 | mkdir deploy_nuget 27 | 28 | for package in ./*.*nupkg; do 29 | mv $package deploy_nuget 30 | done 31 | 32 | echo "##vso[task.setvariable variable=deploy_nuget;]1" 33 | else 34 | echo "no deploy, as $TAG_NAME does not match" 35 | echo ##vso[task.complete result=Skipped;]tag does not match for deploy 36 | fi 37 | 38 | echo "-------------------------------------------------" 39 | echo "custom:" 40 | ls -la deploy_custom 41 | echo "-------------------------------------------------" 42 | echo "nuget:" 43 | ls -la deploy_nuget 44 | echo "-------------------------------------------------" 45 | displayName: 'deploy to nuget / custom' 46 | 47 | - task: NuGetAuthenticate@0 48 | condition: eq(variables['deploy_custom'], '1') 49 | 50 | - task: NuGetCommand@2 51 | condition: eq(variables['deploy_custom'], '1') 52 | inputs: 53 | command: push 54 | packagesToPush: deploy_custom/*.nupkg 55 | nuGetFeedType: 'internal' 56 | publishVstsFeed: 'github-Projects/gfoidl-public' 57 | displayName: deploy to custom feed 58 | 59 | - task: NuGetCommand@2 60 | condition: eq(variables['deploy_nuget'], '1') 61 | inputs: 62 | command: push 63 | packagesToPush: deploy_nuget/*.nupkg 64 | nuGetFeedType: external 65 | publishFeedCredentials: 'nuget - gfoidl' 66 | displayName: deploy to nuget 67 | -------------------------------------------------------------------------------- /.azure/pipelines/jobs/steps/dotnet-install.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: UseDotNet@2 3 | displayName: 'Use .NET SDK 6.0' 4 | inputs: 5 | version: 6.x 6 | includePreviewVersions: true 7 | installationPath: $(Agent.ToolsDirectory)/dotnet 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # L I N E E N D I N G S 3 | 4 | # Set the default behavior, in case people don't have core.autocrlf set. 5 | * text=auto 6 | 7 | # Must keep LF line ending 8 | Dockerfile text eol=lf 9 | .dockerignore text eol=lf 10 | 11 | /**/*.sh text eol=lf 12 | /**/*.conf text eol=lf 13 | /**/*.crontab text eol=lf 14 | /**/*.yml text eol=lf 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @gfoidl 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [gfoidl] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. Ideally with stack-traces. 9 | 10 | ### Platform / Runtime 11 | - OS: [e.g. win-x64, linux-x64, mac-x64] 12 | - .NET version [run `dotnet --info`] 13 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Describe the changes this PR does (and why, link to issue, if any). 4 | If alternative solutions are evaluated, discuss them briefly. 5 | 6 | ## Benchmarks 7 | 8 | Show your benchmark results. 9 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 4 | 2 5 | 0 6 | 150 7 | dev 8 | gfoidl 9 | Foidl Günther 10 | gfoidl.DataCompression 11 | Copyright © Foidl Günther 2017-2021 12 | $(VersionMajor).$(VersionMinor).$(VersionPatch) 13 | $(VersionMajor).$(VersionMinor).$(BuildNumber).$(VersionPatch) 14 | 15 | 16 | 17 | latest 18 | net6.0 19 | 20 | 21 | 22 | $(MSBuildThisFileDirectory)NuGet-Packed 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2019 Günther M. FOIDL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | | CI | Coverage | NuGet | 2 | | -- | -- | -- | 3 | | [![Build Status](https://dev.azure.com/gh-gfoidl/github-Projects/_apis/build/status/.NET/DataCompression?branchName=master)](https://dev.azure.com/gh-gfoidl/github-Projects/_build/latest?definitionId=36&branchName=master) | ![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/gh-gfoidl/github-Projects/36?style=flat-square) | [![NuGet](https://img.shields.io/nuget/v/gfoidl.DataCompression.svg?style=flat-square)](https://www.nuget.org/packages/gfoidl.DataCompression/) | 4 | 5 | # gfoidl.DataCompression 6 | 7 | ## Algorithms 8 | 9 | * [Dead band](./api-doc/articles/DeadBand.md) 10 | * [Swinging Door](./api-doc/articles/SwingingDoor.md) 11 | 12 | ## Demos 13 | 14 | See `./demos` for code. 15 | 16 | _Note_: the graphs in the documentation are created by the code from `./demos`, so you can see which config-values got used. 17 | 18 | ![](./api-doc/articles/images/demo_01.png) 19 | 20 | ![](./api-doc/articles/images/demo_02.png) 21 | 22 | ![](./api-doc/articles/images/demo_03.png) 23 | 24 | ## Development channel 25 | 26 | To get packages from the development channel use a `nuget.config` similar to this one: 27 | ```xml 28 | 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /api-doc/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # folder # 3 | ############### 4 | /**/DROP/ 5 | /**/TEMP/ 6 | /**/packages/ 7 | /**/bin/ 8 | /**/obj/ 9 | _site 10 | -------------------------------------------------------------------------------- /api-doc/api/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # temp file # 3 | ############### 4 | *.yml 5 | .manifest 6 | -------------------------------------------------------------------------------- /api-doc/api/index.md: -------------------------------------------------------------------------------- 1 | # PLACEHOLDER 2 | TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! 3 | -------------------------------------------------------------------------------- /api-doc/articles/DeadBand.md: -------------------------------------------------------------------------------- 1 | # Dead band 2 | 3 | ## Purpose 4 | 5 | Filtering noise caused by measure- or device-errors (i.e. instrument precision). 6 | 7 | ## Description 8 | 9 | Values that lie inbetween the dead band, defined by _ExDev_, get ommited because they are not meaningful. 10 | Decisions should not be based on these value. They are just noise, therefore they can be filtered out. 11 | 12 | ![](./images/DeadBand_01.jpg) 13 | 14 | When a value is outside of the dead band, that value and the previous value are recored in order to maintain the trend. 15 | 16 | ![](./images/DeadBand_02.png) 17 | 18 | ## Parameters 19 | 20 | | Name | Description | 21 | | ---- | ------------------------------------------------------- | 22 | | ExDev | (absolut) instrument precision | 23 | | ExMax | length of x/time before for sure a value gets recoreded | 24 | 25 | ## Examples 26 | 27 | ### Trend 28 | 29 | ![](./images/dead-band_trend.png) 30 | 31 | ### Max Delta 32 | 33 | ![](./images/dead-band_maxDelta.png) 34 | 35 | ### Error and Statistics 36 | 37 | ![](./images/dead-band_error.png) 38 | 39 | | Data | # datapoints | average | sigma | skewness | kurtosis | 40 | | ---------- | ------------ | ------- | ------ | -------- | -------- | 41 | | raw | 1000 | 19.6584 | 0.1960 | -0.0330 | 2.3442 | 42 | | compressed | 490 | 19.6623 | 0.2007 | -0.1021 | 2.4672 | 43 | 44 | As can be seen statistics didn't change significantally, but the count of recorded datapoints was reduced -- by filtering noise -- by 51%. 45 | 46 | ## Literature 47 | 48 | * [OSIsoft: Exception and Compression Full Details](https://www.youtube.com/watch?v=89hg2mme7S0) 49 | -------------------------------------------------------------------------------- /api-doc/articles/SwingingDoor.md: -------------------------------------------------------------------------------- 1 | # Swinging Door 2 | 3 | ## Purpose 4 | 5 | Data reduction by using the swinging door algorithm. 6 | 7 | ## Description 8 | 9 | ![](./images/swinging-door_02.png) 10 | 11 | Beginning at the last archived value (1) and the next snapshots (2, 3, ...) a _swinging door_ is constructed, that is only allowed to close and not to open. Green area in the figure below. 12 | 13 | ![](./images/swinging-door_01.png) 14 | 15 | When an incoming value (6) lies outside the allowed area, so the last snapshot (5) get stored, and beginning at this snapshot (5) a new _swinging door_ to the incoming (6) value gets opened. 16 | Therefore maintaining the trend in the data. 17 | 18 | ## Parameters 19 | 20 | | Name | Description | 21 | | ------- | ------------------------------------------------------------------------------ | 22 | | CompDev | (absolut) compression deviation | 23 | | ExMax | length of x/time before for sure a value gets recoreded | 24 | | ExMin | length of x/time within no value gets recorded (after the last archived value) | 25 | 26 | ## Examples 27 | 28 | ### Trend 29 | 30 | ![](./images/swinging-door_trend.png) 31 | 32 | ### Max Delta 33 | 34 | ![](./images/swinging-door_maxDelta.png) 35 | 36 | ### Error and Statistics 37 | 38 | ![](./images/swinging-door_error.png) 39 | 40 | | Data | # datapoints | average | sigma | skewness | kurtosis | 41 | | ---------- | ------------ | ------- | ------ | -------- | -------- | 42 | | raw | 1000 | 19.2854 | 1.2968 | -2.1689 | 7.0397 | 43 | | compressed | 418 | 19.2833 | 1.2984 | -2.1682 | 7.0428 | 44 | 45 | As can be seen statistics didn't change significantally, but the count of recorded datapoints was reduced -- by filtering noise -- by 58%. 46 | 47 | ## Literature 48 | 49 | * [OSIsoft: Exception and Compression Full Details](https://www.youtube.com/watch?v=89hg2mme7S0) 50 | -------------------------------------------------------------------------------- /api-doc/articles/images/DeadBand_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/DeadBand_01.jpg -------------------------------------------------------------------------------- /api-doc/articles/images/DeadBand_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/DeadBand_02.png -------------------------------------------------------------------------------- /api-doc/articles/images/dead-band_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/dead-band_error.png -------------------------------------------------------------------------------- /api-doc/articles/images/dead-band_maxDelta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/dead-band_maxDelta.png -------------------------------------------------------------------------------- /api-doc/articles/images/dead-band_trend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/dead-band_trend.png -------------------------------------------------------------------------------- /api-doc/articles/images/demo_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/demo_01.png -------------------------------------------------------------------------------- /api-doc/articles/images/demo_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/demo_02.png -------------------------------------------------------------------------------- /api-doc/articles/images/demo_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/demo_03.png -------------------------------------------------------------------------------- /api-doc/articles/images/swinging-door_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/swinging-door_01.png -------------------------------------------------------------------------------- /api-doc/articles/images/swinging-door_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/swinging-door_02.png -------------------------------------------------------------------------------- /api-doc/articles/images/swinging-door_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/swinging-door_error.png -------------------------------------------------------------------------------- /api-doc/articles/images/swinging-door_maxDelta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/swinging-door_maxDelta.png -------------------------------------------------------------------------------- /api-doc/articles/images/swinging-door_trend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/articles/images/swinging-door_trend.png -------------------------------------------------------------------------------- /api-doc/articles/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Dead band 2 | href: DeadBand.md 3 | 4 | - name: Swinging Door 5 | href: SwingingDoor.md 6 | -------------------------------------------------------------------------------- /api-doc/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "cwd": "../", 7 | "files": [ 8 | "source/**.csproj" 9 | ] 10 | } 11 | ], 12 | "dest": "api", 13 | "filter": "filterConfig.yml", 14 | "disableGitFeatures": false, 15 | "disableDefaultFilter": false 16 | } 17 | ], 18 | "build": { 19 | "content": [ 20 | { 21 | "files": [ 22 | "api/**.yml", 23 | "api/index.md" 24 | ] 25 | }, 26 | { 27 | "files": [ 28 | "articles/**.md", 29 | "articles/**/toc.yml", 30 | "toc.yml", 31 | "*.md" 32 | ] 33 | } 34 | ], 35 | "resource": [ 36 | { 37 | "files": [ 38 | "images/**", 39 | "articles/images/**" 40 | ] 41 | } 42 | ], 43 | "overwrite": [ 44 | { 45 | "files": [ 46 | "apidoc/**.md" 47 | ], 48 | "exclude": [ 49 | "obj/**", 50 | "_site/**" 51 | ] 52 | } 53 | ], 54 | "globalMetadata": { 55 | "_appTitle": "gfoidl.DataCompression", 56 | "_appFooter": "Copyright © Foidl Günther 2017-2021", 57 | "_appLogoPath": "images/logo_32.png", 58 | "_appFaviconPath": "null" 59 | }, 60 | "dest": "_site", 61 | "globalMetadataFiles": [], 62 | "fileMetadataFiles": [], 63 | "template": [ 64 | "default" 65 | ], 66 | "postProcessors": [], 67 | "markdownEngineName": "markdig", 68 | "noLangKeyword": false, 69 | "keepFileLink": false, 70 | "cleanupCacheHistory": false, 71 | "disableGitFeatures": false 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /api-doc/filterConfig.yml: -------------------------------------------------------------------------------- 1 | apiRules: 2 | - exclude: 3 | uidRegex: ^gfoidl\.DataCompression\.Internal$ 4 | type: Namespace 5 | -------------------------------------------------------------------------------- /api-doc/images/logo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gfoidl/DataCompression/e47ae3d5bf2de70533c63aa1dd99971de1bd6340/api-doc/images/logo_32.png -------------------------------------------------------------------------------- /api-doc/index.md: -------------------------------------------------------------------------------- 1 | # gfoidl.DataCompression 2 | 3 | ## Algorithms 4 | 5 | * [Dead band](./articles/DeadBand.md) 6 | * [Swinging Door](./articles/SwingingDoor.md) 7 | 8 | ## Demos 9 | 10 | See `./demos` for code. 11 | 12 | _Note_: the graphs in the documentation are created by the code from `./demos`, so you can see which config-values got used. 13 | 14 | ![](./articles/images/demo_01.png) 15 | 16 | ![](./articles/images/demo_02.png) 17 | 18 | ![](./articles/images/demo_03.png) 19 | -------------------------------------------------------------------------------- /api-doc/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Algorithms 2 | href: articles/ 3 | 4 | - name: Api Documentation 5 | href: api/ 6 | #homepage: api/index.md 7 | -------------------------------------------------------------------------------- /data/dead-band/maxDelta.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Dead band compression -- maxDelta' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | set key bottom right 10 | 11 | #set yrange [0.2:1.5] 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'maxDelta_raw.csv' with linespoints title 'raw', \ 17 | 'maxDelta_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /data/dead-band/maxDelta_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 0.5 3 | 4 0.6 4 | 6 0.5 5 | 7 1.25 6 | 9 1.3 7 | -------------------------------------------------------------------------------- /data/dead-band/maxDelta_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 0.5 3 | 1 0.6 4 | 2 0.4 5 | 3 0.55 6 | 4 0.6 7 | 5 0.4 8 | 6 0.5 9 | 7 1.25 10 | 8 1.2 11 | 9 1.3 12 | -------------------------------------------------------------------------------- /data/dead-band/trend.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Dead band compression -- Trend' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | set key bottom right 10 | 11 | set yrange [0.2:1.5] 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'trend_raw.csv' with linespoints title 'raw', \ 17 | 'trend_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /data/dead-band/trend_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 0.5 3 | 3 0.55 4 | 4 1.2 5 | 9 1.2 6 | -------------------------------------------------------------------------------- /data/dead-band/trend_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 0.5 3 | 1 0.6 4 | 2 0.4 5 | 3 0.55 6 | 4 1.2 7 | 5 1.1 8 | 6 1.3 9 | 7 1.25 10 | 8 1.2 11 | 9 1.2 12 | -------------------------------------------------------------------------------- /data/swinging-door/maxDelta.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- maxDelta' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | set key bottom right 10 | set xrange [0:21] 11 | set yrange [0:5] 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'maxDelta_raw.csv' with linespoints title 'raw', \ 17 | 'maxDelta_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /data/swinging-door/maxDelta_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 2 2 3 | 8 3.5 4 | 14 4 5 | 16 4.5 6 | 18 1.5 7 | 20 2.5 8 | -------------------------------------------------------------------------------- /data/swinging-door/maxDelta_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 2 2 3 | 4 3 4 | 6 3 5 | 8 3.5 6 | 10 3.5 7 | 12 4.5 8 | 14 4 9 | 16 4.5 10 | 18 1.5 11 | 20 2.5 12 | -------------------------------------------------------------------------------- /data/swinging-door/trend.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- Trend' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | 10 | set xrange [0:17] 11 | set yrange [0:6] 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'trend_raw.csv' with linespoints title 'raw', \ 17 | 'trend_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /data/swinging-door/trend1.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- Trend1' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | 10 | set xrange [0:7.5] 11 | set yrange [0:2.5] 12 | 13 | set style line 2 lc rgb 'green' pt 9 # triangle 14 | 15 | #set datafile separator ";" 16 | 17 | # replot is also possible for the second plot 18 | plot 'trend1_raw.csv' with linespoints title 'raw', \ 19 | 'trend1_compressed.csv' with points ls 2 title 'compressed' 20 | -------------------------------------------------------------------------------- /data/swinging-door/trend1_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 1 3 | 2 1.2 4 | 4 2 5 | 6 2 6 | 7 1.2 7 | -------------------------------------------------------------------------------- /data/swinging-door/trend1_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 1 3 | 1 1.1 4 | 2 1.2 5 | 3 1.6 6 | 4 2 7 | 5 2 8 | 6 2 9 | 7 1.2 10 | -------------------------------------------------------------------------------- /data/swinging-door/trend2.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- Trend2' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | 10 | set xrange [0:7.5] 11 | set yrange [0:2.5] 12 | 13 | set style line 2 lc rgb 'green' pt 9 # triangle 14 | 15 | #set datafile separator ";" 16 | 17 | # replot is also possible for the second plot 18 | plot 'trend2_raw.csv' with linespoints title 'raw', \ 19 | 'trend2_compressed.csv' with points ls 2 title 'compressed' 20 | -------------------------------------------------------------------------------- /data/swinging-door/trend2_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 1 3 | 2 1.2 4 | 4 2 5 | 7 2 6 | -------------------------------------------------------------------------------- /data/swinging-door/trend2_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 0 1 3 | 1 1.1 4 | 2 1.2 5 | 3 1.6 6 | 4 2 7 | 5 2 8 | 6 2 9 | 7 2 10 | -------------------------------------------------------------------------------- /data/swinging-door/trend3.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- Trend3' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | 10 | set xrange [0:10] 11 | set yrange [-3:6] 12 | 13 | set style line 2 lc rgb 'green' pt 9 # triangle 14 | 15 | #set datafile separator ";" 16 | 17 | # replot is also possible for the second plot 18 | plot 'trend3_raw.csv' with linespoints title 'raw', \ 19 | 'trend3_compressed.csv' with points ls 2 title 'compressed' 20 | -------------------------------------------------------------------------------- /data/swinging-door/trend3_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 1 0 3 | 4 5 4 | 5 -2 5 | 6 5 6 | 8 3 7 | 9 5 8 | -------------------------------------------------------------------------------- /data/swinging-door/trend3_mini.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | 5 | set grid 6 | set title 'Swinging door compression -- Trend3 (minimal repro)' 7 | set xlabel 'Time' 8 | set ylabel 'Value' 9 | 10 | set xrange [4:10] 11 | set yrange [-3:6] 12 | 13 | set style line 2 lc rgb 'green' pt 9 # triangle 14 | 15 | #set datafile separator ";" 16 | 17 | # replot is also possible for the second plot 18 | plot 'trend3_mini_raw.csv' with linespoints title 'raw', \ 19 | 'trend3_mini_compressed.csv' with points ls 2 title 'compressed' 20 | -------------------------------------------------------------------------------- /data/swinging-door/trend3_mini_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 5 -2 3 | 6 5 4 | 8 3 5 | 9 5 6 | -------------------------------------------------------------------------------- /data/swinging-door/trend3_mini_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 5 -2 3 | 6 5 4 | 7 4 5 | 8 3 6 | 9 5 7 | -------------------------------------------------------------------------------- /data/swinging-door/trend3_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 1 0 3 | 2 1 4 | 3 2 5 | 4 5 6 | 5 -2 7 | 6 5 8 | 7 4 9 | 8 3 10 | 9 5 11 | -------------------------------------------------------------------------------- /data/swinging-door/trend_compressed.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 2 2 3 | 8 3.5 4 | 10 5.5 5 | 14 1 6 | 16 1 7 | -------------------------------------------------------------------------------- /data/swinging-door/trend_raw.csv: -------------------------------------------------------------------------------- 1 | #x y 2 | 2 2 3 | 4 3 4 | 6 2.5 5 | 8 3.5 6 | 10 5.5 7 | 12 2.5 8 | 14 1 9 | 16 1 10 | -------------------------------------------------------------------------------- /demos/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.png 3 | -------------------------------------------------------------------------------- /demos/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Exe 7 | enable 8 | 9 | 10 | 11 | $(DefineConstants);SKIP_PLOT_DISPLAY;CI_BUILD 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/Dockerfiles/dotnet-gnuplot5/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | build.sh 3 | -------------------------------------------------------------------------------- /demos/Dockerfiles/dotnet-gnuplot5/Dockerfile: -------------------------------------------------------------------------------- 1 | # .NET Core 6 SDK + gnuplot 5 2 | 3 | FROM mcr.microsoft.com/dotnet/sdk:6.0 4 | 5 | LABEL org.opencontainers.image.source https://github.com/gfoidl/DataCompression 6 | 7 | RUN apt update \ 8 | && apt install -y gnuplot \ 9 | && rm -rf /var/lib/apt/lists/* /tmp/* 10 | -------------------------------------------------------------------------------- /demos/Dockerfiles/dotnet-gnuplot5/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker build -t ghcr.io/gfoidl/datacompression/dotnet-gnuplot5 . 4 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.Async/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Demos.Async 10 | { 11 | class Program 12 | { 13 | static async Task Main(string[] args) 14 | { 15 | #if CI_BUILD 16 | var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1)); 17 | #else 18 | var cts = new CancellationTokenSource(); 19 | #endif 20 | IAsyncEnumerable source = Source(cts.Token); 21 | //DataPointIterator filtered = source.DeadBandCompressionAsync(0.01); 22 | DataPointIterator filtered = source.SwingingDoorCompressionAsync(0.01); 23 | ValueTask sinkTask = Sink(filtered, cts.Token); 24 | 25 | #if !CI_BUILD 26 | Console.WriteLine("any key to stop..."); 27 | Console.ReadKey(); 28 | cts.Cancel(); 29 | #endif 30 | 31 | double sum = default; 32 | try 33 | { 34 | sum = await sinkTask; 35 | } 36 | catch (OperationCanceledException) { } 37 | 38 | Console.WriteLine($"sum: {sum}"); 39 | } 40 | //--------------------------------------------------------------------- 41 | private static async IAsyncEnumerable Source([EnumeratorCancellation] CancellationToken ct = default) 42 | { 43 | await Task.Yield(); 44 | 45 | int i = 0; 46 | var rnd = new Random(42); 47 | 48 | while (!ct.IsCancellationRequested) 49 | { 50 | await Task.Yield(); 51 | double x = i++; 52 | double y = rnd.NextDouble(); 53 | 54 | yield return new DataPoint(x, y); 55 | } 56 | } 57 | //--------------------------------------------------------------------- 58 | private static async ValueTask Sink(DataPointIterator data, CancellationToken ct = default) 59 | { 60 | await Task.Yield(); 61 | 62 | double sum = 0; 63 | uint count = 0; 64 | 65 | await foreach (DataPoint dp in data.WithCancellation(ct)) 66 | { 67 | sum += dp.Y; 68 | 69 | if (count % (1 << 15) == 0) 70 | { 71 | Console.WriteLine($"count: {count}, sum: {sum}"); 72 | } 73 | count++; 74 | } 75 | 76 | return sum; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.Async/gfoidl.DataCompression.Demos.Async.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand.Stats/data/error.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | set term pngcairo size 1200,1000 enhanced 4 | set output 'error-deadband.png' 5 | 6 | set multiplot layout 4, 1 title "Error for dead band compression\n" 7 | 8 | unset title 9 | set grid 10 | set key top right 11 | 12 | set ytics 10.0 13 | plot 'result.csv' using 1:2 with lines title 'raw' 14 | plot 'compressed.csv' with dots title 'compressed' 15 | plot 'result.csv' using 1:3 with lines title 'compressed reconstructed' 16 | 17 | #set xlabel 'x' 18 | set ytics 0.2 19 | set yrange [-0.3:0.3] 20 | plot 'result.csv' using 1:4 with lines title 'error' 21 | 22 | unset multiplot 23 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand.Stats/gfoidl.DataCompression.Demos.DeadBand.Stats.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | 8 | namespace gfoidl.DataCompression.Demos.DeadBand 9 | { 10 | static class Program 11 | { 12 | static void Main() 13 | { 14 | Environment.CurrentDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 15 | 16 | var dataPointReader = new DataPointSerializer(); 17 | 18 | IEnumerable compressed = dataPointReader 19 | .Read("coolant-temp.csv") 20 | .DeadBandCompression(1d); 21 | 22 | dataPointReader.Write("coolant-temp_compressed.csv", compressed, header: ("Oh", "Temp")); 23 | 24 | GnuPlot(); 25 | ShowChart(); 26 | } 27 | //--------------------------------------------------------------------- 28 | private static void GnuPlot() 29 | { 30 | var gnuPlot = new Process(); 31 | //gnuPlot.StartInfo.WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 32 | gnuPlot.StartInfo.FileName = "gnuplot"; 33 | gnuPlot.StartInfo.Arguments = "coolant-temp.plt"; 34 | gnuPlot.Start(); 35 | gnuPlot.WaitForExit(); 36 | } 37 | //--------------------------------------------------------------------- 38 | private static void ShowChart() 39 | { 40 | #if !SKIP_PLOT_DISPLAY 41 | var png = new Process(); 42 | png.StartInfo.UseShellExecute = true; // defaults to false in in .NET (Core) 43 | png.StartInfo.FileName = "coolant-temp-deadband.png"; 44 | png.Start(); 45 | #endif 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand/data/coolant-temp.csv: -------------------------------------------------------------------------------- 1 | # Oh Temp [°C] 2 | 27830 86 3 | 28332 85 4 | 28641 85 5 | 29135 85 6 | 29749 83 7 | 29940 84 8 | 30743 83 9 | 31266 80 10 | 31384 80 11 | 31943 84 12 | 32039 84 13 | 32728 87 14 | 32775 85 15 | 33415 86 16 | 34157 87 17 | 34351 88 18 | 34610 86 19 | 35291 86 20 | 36061 82 21 | 36584 82 22 | 36630 82 23 | 37305 82 24 | 37398 82 25 | 38103 83 26 | 38643 83 27 | 38886 82 28 | 38930 82 29 | 39570 82 30 | 40249 83 31 | 40747 83 32 | 40890 89 33 | 41671 87 34 | 42343 86 35 | 42530 88 36 | 42812 86 37 | 42831 88 38 | 42973 88 39 | 43158 89 40 | 43828 86 41 | 44287 86 42 | 44618 86 43 | 45048 83 44 | 45188 86 45 | 46145 86 46 | 46817 85 47 | 46970 86 48 | 47127 86 49 | 47315 86 50 | 48153 84 51 | 48154 81 52 | 48851 82 53 | 48868 81 54 | 49181 83 55 | 49681 85 56 | 50196 83 57 | 50197 83 58 | 50976 85 59 | 51310 85 60 | 51693 85 61 | 52126 81 62 | 52939 80 63 | 52985 80 64 | 53746 80 65 | 54178 80 66 | 54345 80 67 | 54849 80 68 | 55109 79 69 | 55250 79 70 | 55762 79 71 | 56238 79 72 | 56523 79 73 | 56993 84 74 | 57278 84 75 | 57618 83 76 | 57730 83 77 | 57849 84 78 | 58013 83 79 | 58348 86 80 | 58755 85 81 | 59395 82 82 | 60167 79 83 | 60333 79 84 | 60667 78 85 | 60862 78 86 | 61313 76 87 | 61640 78 88 | 62236 78 89 | 62712 71 90 | 62733 78 91 | 62877 79 92 | 63065 80 93 | 63701 82 94 | 63960 82 95 | 64373 81 96 | 65032 84 97 | 65613 79 98 | 65633 83 99 | 65951 81 100 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand/data/coolant-temp.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | set term pngcairo size 800,600 enhanced 5 | set output 'coolant-temp-deadband.png' 6 | 7 | set grid 8 | set title 'Coolant temp over Oh' 9 | set xlabel 'Oh' 10 | set ylabel 'temp [°C]' 11 | set key bottom right 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'coolant-temp.csv' with linespoints title 'raw', \ 17 | 'coolant-temp_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.DeadBand/gfoidl.DataCompression.Demos.DeadBand.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.LiveData/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | 8 | namespace gfoidl.DataCompression.Demos.LiveData 9 | { 10 | static class Program 11 | { 12 | static void Main() 13 | { 14 | Environment.CurrentDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 15 | 16 | Run("agt_n_awt1", 0.25, 0.5); 17 | #if !CI_BUILD 18 | Console.WriteLine("hit key to continue..."); 19 | Console.ReadKey(); 20 | #endif 21 | 22 | Run("agt_zyl_6", 0.75, 1.5); 23 | #if !CI_BUILD 24 | Console.WriteLine("hit key to continue..."); 25 | Console.ReadKey(); 26 | #endif 27 | 28 | Run("erregerspannung", 0.4, 0.8); 29 | #if !CI_BUILD 30 | Console.WriteLine("hit key to continue..."); 31 | Console.ReadKey(); 32 | #endif 33 | } 34 | //--------------------------------------------------------------------- 35 | private static void Run(string name, double instrumentPrecision, double compressionDeviation) 36 | { 37 | var dataPointReader = new DataPointSerializer(); 38 | 39 | IEnumerable compressed = dataPointReader 40 | .Read($"{name}.csv", dateTimeFormat: "yyyy-MM-dd_HH:mm:ss") 41 | .DeadBandCompression(instrumentPrecision) 42 | .SwingingDoorCompression(compressionDeviation); 43 | 44 | dataPointReader.Write($"{name}_compressed.csv", compressed, dateTimeFormat: "yyyy-MM-dd_HH:mm:ss"); 45 | 46 | GnuPlot(name); 47 | ShowChart(name); 48 | } 49 | //--------------------------------------------------------------------- 50 | private static void GnuPlot(string name) 51 | { 52 | var gnuPlot = new Process(); 53 | //gnuPlot.StartInfo.WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 54 | gnuPlot.StartInfo.FileName = "gnuplot"; 55 | gnuPlot.StartInfo.Arguments = $"{name}.plt"; 56 | gnuPlot.Start(); 57 | gnuPlot.WaitForExit(); 58 | } 59 | //--------------------------------------------------------------------- 60 | private static void ShowChart(string name) 61 | { 62 | #if !SKIP_PLOT_DISPLAY 63 | var png = new Process(); 64 | png.StartInfo.UseShellExecute = true; // defaults to false in .NET (Core) 65 | png.StartInfo.FileName = $"{name}.png"; 66 | png.Start(); 67 | #endif 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.LiveData/data/agt_n_awt1.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | set term pngcairo size 1200,600 enhanced 5 | set output 'agt_n_awt1.png' 6 | 7 | set grid 8 | set title 'AGT n AWT1' 9 | set xlabel 'Time' 10 | set ylabel '[°C]' 11 | #set key bottom right 12 | 13 | set xdata time 14 | set timefmt "%Y-%m-%d_%H:%M:%S" 15 | set format x "%H:%M" 16 | 17 | #set datafile separator ";" 18 | 19 | # replot is also possible for the second plot 20 | plot 'agt_n_awt1.csv' using 1:2 with lines title 'raw', \ 21 | 'agt_n_awt1_compressed.csv' using 1:2 with lines title 'compressed' 22 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.LiveData/data/agt_zyl_6.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | set term pngcairo size 1200,600 enhanced 5 | set output 'agt_zyl_6.png' 6 | 7 | set grid 8 | set title 'AGT Zyl. 6' 9 | set xlabel 'Time' 10 | set ylabel '[°C]' 11 | #set key bottom right 12 | 13 | #set yrange [14:25] 14 | 15 | set xdata time 16 | set timefmt "%Y-%m-%d_%H:%M:%S" 17 | set format x "%H:%M" 18 | 19 | #set datafile separator ";" 20 | 21 | # replot is also possible for the second plot 22 | plot 'agt_zyl_6.csv' using 1:2 with lines title 'raw', \ 23 | 'agt_zyl_6_compressed.csv' using 1:2 with lines title 'compressed' 24 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.LiveData/data/erregerspannung.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | set term pngcairo size 1200,600 enhanced 5 | set output 'erregerspannung.png' 6 | 7 | set grid 8 | set title 'Erregerspannung' 9 | set xlabel 'Time' 10 | set ylabel '[V]' 11 | set key bottom right 12 | 13 | set yrange [14:25] 14 | 15 | set xdata time 16 | set timefmt "%Y-%m-%d_%H:%M:%S" 17 | set format x "%H:%M" 18 | 19 | #set datafile separator ";" 20 | 21 | # replot is also possible for the second plot 22 | plot 'erregerspannung.csv' using 1:2 with lines title 'raw', \ 23 | 'erregerspannung_compressed.csv' using 1:2 with lines title 'compressed' 24 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.LiveData/gfoidl.DataCompression.Demos.LiveData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor.Stats/data/error.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | set term pngcairo size 1200,1000 enhanced 4 | set output 'error-swingingdoor.png' 5 | 6 | set multiplot layout 4, 1 title "Error for swinging door compression\n" 7 | 8 | unset title 9 | set grid 10 | set key top right 11 | 12 | set ytics 10.0 13 | plot 'result.csv' using 1:2 with lines title 'raw' 14 | plot 'compressed.csv' with dots title 'compressed' 15 | plot 'result.csv' using 1:3 with lines title 'compressed reconstructed' 16 | 17 | #set xlabel 'x' 18 | set ytics 0.2 19 | set yrange [-0.3:0.3] 20 | plot 'result.csv' using 1:4 with lines title 'error' 21 | 22 | unset multiplot 23 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor.Stats/gfoidl.DataCompression.Demos.SwingingDoor.Stats.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | 8 | namespace gfoidl.DataCompression.Demos.SwingingDoor 9 | { 10 | static class Program 11 | { 12 | static void Main() 13 | { 14 | Environment.CurrentDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 15 | 16 | var dataPointReader = new DataPointSerializer(); 17 | 18 | IEnumerable compressed = dataPointReader 19 | .Read("coolant-temp.csv") 20 | .SwingingDoorCompression(1.5); 21 | 22 | dataPointReader.Write("coolant-temp_compressed.csv", compressed, header: ("Oh", "Temp")); 23 | 24 | GnuPlot(); 25 | ShowChart(); 26 | } 27 | //--------------------------------------------------------------------- 28 | private static void GnuPlot() 29 | { 30 | var gnuPlot = new Process(); 31 | //gnuPlot.StartInfo.WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "data"); 32 | gnuPlot.StartInfo.FileName = "gnuplot"; 33 | gnuPlot.StartInfo.Arguments = "coolant-temp.plt"; 34 | gnuPlot.Start(); 35 | gnuPlot.WaitForExit(); 36 | } 37 | //--------------------------------------------------------------------- 38 | private static void ShowChart() 39 | { 40 | #if !SKIP_PLOT_DISPLAY 41 | var png = new Process(); 42 | png.StartInfo.UseShellExecute = true; // defaults to false in .NET (Core) 43 | png.StartInfo.FileName = "coolant-temp-swingingdoor.png"; 44 | png.Start(); 45 | #endif 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor/data/coolant-temp.csv: -------------------------------------------------------------------------------- 1 | # Oh Temp [°C] 2 | 27830 86 3 | 28332 85 4 | 28641 85 5 | 29135 85 6 | 29749 83 7 | 29940 84 8 | 30743 83 9 | 31266 80 10 | 31384 80 11 | 31943 84 12 | 32039 84 13 | 32728 87 14 | 32775 85 15 | 33415 86 16 | 34157 87 17 | 34351 88 18 | 34610 86 19 | 35291 86 20 | 36061 82 21 | 36584 82 22 | 36630 82 23 | 37305 82 24 | 37398 82 25 | 38103 83 26 | 38643 83 27 | 38886 82 28 | 38930 82 29 | 39570 82 30 | 40249 83 31 | 40747 83 32 | 40890 89 33 | 41671 87 34 | 42343 86 35 | 42530 88 36 | 42812 86 37 | 42831 88 38 | 42973 88 39 | 43158 89 40 | 43828 86 41 | 44287 86 42 | 44618 86 43 | 45048 83 44 | 45188 86 45 | 46145 86 46 | 46817 85 47 | 46970 86 48 | 47127 86 49 | 47315 86 50 | 48153 84 51 | 48154 81 52 | 48851 82 53 | 48868 81 54 | 49181 83 55 | 49681 85 56 | 50196 83 57 | 50197 83 58 | 50976 85 59 | 51310 85 60 | 51693 85 61 | 52126 81 62 | 52939 80 63 | 52985 80 64 | 53746 80 65 | 54178 80 66 | 54345 80 67 | 54849 80 68 | 55109 79 69 | 55250 79 70 | 55762 79 71 | 56238 79 72 | 56523 79 73 | 56993 84 74 | 57278 84 75 | 57618 83 76 | 57730 83 77 | 57849 84 78 | 58013 83 79 | 58348 86 80 | 58755 85 81 | 59395 82 82 | 60167 79 83 | 60333 79 84 | 60667 78 85 | 60862 78 86 | 61313 76 87 | 61640 78 88 | 62236 78 89 | 62712 71 90 | 62733 78 91 | 62877 79 92 | 63065 80 93 | 63701 82 94 | 63960 82 95 | 64373 81 96 | 65032 84 97 | 65613 79 98 | 65633 83 99 | 65951 81 100 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor/data/coolant-temp.plt: -------------------------------------------------------------------------------- 1 | reset 2 | 3 | #set terminal dumb 4 | set term pngcairo size 800,600 enhanced 5 | set output 'coolant-temp-swingingdoor.png' 6 | 7 | set grid 8 | set title 'Coolant temp over Oh' 9 | set xlabel 'Oh' 10 | set ylabel 'temp [°C]' 11 | set key bottom right 12 | 13 | #set datafile separator ";" 14 | 15 | # replot is also possible for the second plot 16 | plot 'coolant-temp.csv' with linespoints title 'raw', \ 17 | 'coolant-temp_compressed.csv' with linespoints title 'compressed' 18 | -------------------------------------------------------------------------------- /demos/gfoidl.DataCompression.Demos.SwingingDoor/gfoidl.DataCompression.Demos.SwingingDoor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for demoProject in ./**/*.csproj; do 4 | echo "" 5 | echo "-------------------------------------------------" 6 | echo "Running demo '$(basename $demoProject .csproj)'" 7 | 8 | cd $(dirname $demoProject) 9 | dotnet run --no-build 10 | cd - >/dev/null 11 | done 12 | -------------------------------------------------------------------------------- /perf/.gitignore: -------------------------------------------------------------------------------- 1 | BenchmarkDotNet.Artifacts 2 | -------------------------------------------------------------------------------- /perf/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Exe 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/Base.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using BenchmarkDotNet.Attributes; 7 | 8 | namespace gfoidl.DataCompression.Benchmarks; 9 | 10 | [MemoryDiagnoser] 11 | public abstract class Base 12 | { 13 | private const int Count = 1_000_000; 14 | 15 | private readonly Random _rnd = new(Seed: 42); 16 | //------------------------------------------------------------------------- 17 | protected IEnumerable Source(int count = Count) 18 | { 19 | for (int i = 0; i < count; ++i) 20 | { 21 | double x = i; 22 | double y = _rnd!.NextDouble(); 23 | 24 | yield return (x, y); 25 | } 26 | } 27 | //------------------------------------------------------------------------- 28 | protected async IAsyncEnumerable SourceAsync(int count = Count) 29 | { 30 | foreach (DataPoint dataPoint in this.Source(count)) 31 | { 32 | await Task.Yield(); 33 | yield return dataPoint; 34 | } 35 | } 36 | //------------------------------------------------------------------------- 37 | protected static double Consume(DataPointIterator iterator) 38 | { 39 | double sum = 0; 40 | 41 | foreach (DataPoint dataPoint in iterator) 42 | { 43 | sum += dataPoint.Y; 44 | } 45 | 46 | return sum; 47 | } 48 | //------------------------------------------------------------------------- 49 | protected static async ValueTask ConsumeAsync(DataPointIterator iterator) 50 | { 51 | double sum = 0; 52 | 53 | await foreach (DataPoint dataPoint in iterator) 54 | { 55 | sum += dataPoint.Y; 56 | } 57 | 58 | return sum; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/Categories.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | namespace gfoidl.DataCompression.Benchmarks; 4 | 5 | public static class Categories 6 | { 7 | public const string Compression = nameof(Compression); 8 | public const string Sync = nameof(Sync); 9 | public const string Async = nameof(Async); 10 | public const string Deadband = nameof(Deadband); 11 | public const string SwingingDoor = nameof(SwingingDoor); 12 | } 13 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/DeadBandCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using BenchmarkDotNet.Attributes; 5 | 6 | namespace gfoidl.DataCompression.Benchmarks; 7 | 8 | [BenchmarkCategory(Categories.Compression, Categories.Sync, Categories.Deadband)] 9 | public class DeadBandCompression : Base 10 | { 11 | [Benchmark] 12 | public double Enumerate() 13 | { 14 | IEnumerable source = this.Source(); 15 | using DataPointIterator filtered = source.DeadBandCompression(0.1); 16 | return Consume(filtered); 17 | } 18 | //------------------------------------------------------------------------- 19 | [Benchmark] 20 | public DataPoint[] ToArray() 21 | { 22 | IEnumerable source = this.Source(); 23 | using DataPointIterator filtered = source.DeadBandCompression(0.1); 24 | return filtered.ToArray(); 25 | } 26 | //------------------------------------------------------------------------- 27 | [Benchmark] 28 | public List ToList() 29 | { 30 | IEnumerable source = this.Source(); 31 | using DataPointIterator filtered = source.DeadBandCompression(0.1); 32 | return filtered.ToList(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/DeadBandCompressionAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using BenchmarkDotNet.Attributes; 6 | 7 | namespace gfoidl.DataCompression.Benchmarks; 8 | 9 | [BenchmarkCategory(Categories.Compression, Categories.Async, Categories.Deadband)] 10 | public class DeadBandCompressionAsync : Base 11 | { 12 | [Benchmark] 13 | public async ValueTask Enumerate() 14 | { 15 | IAsyncEnumerable source = this.SourceAsync(); 16 | using DataPointIterator filtered = source.DeadBandCompressionAsync(0.1); 17 | return await ConsumeAsync(filtered); 18 | } 19 | //--------------------------------------------------------------------- 20 | [Benchmark] 21 | public async ValueTask ToArray() 22 | { 23 | IAsyncEnumerable source = this.SourceAsync(); 24 | using DataPointIterator filtered = source.DeadBandCompressionAsync(0.1); 25 | return await filtered.ToArrayAsync(); 26 | } 27 | //--------------------------------------------------------------------- 28 | [Benchmark] 29 | public async ValueTask> ToList() 30 | { 31 | IAsyncEnumerable source = this.SourceAsync(); 32 | using DataPointIterator filtered = source.DeadBandCompressionAsync(0.1); 33 | return await filtered.ToListAsync(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/Infrastructure/CompressionFactories.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | namespace gfoidl.DataCompression.Benchmarks.Infrastructure 4 | { 5 | public interface ICompressionFactory 6 | { 7 | ICompression Create(); 8 | } 9 | //------------------------------------------------------------------------- 10 | public class DeadBandCompressionFactory : ICompressionFactory 11 | { 12 | public ICompression Create() => new DataCompression.DeadBandCompression(0.1); 13 | } 14 | //------------------------------------------------------------------------- 15 | public class SwingingDoorCompressionFactory : ICompressionFactory 16 | { 17 | public ICompression Create() => new DataCompression.SwingingDoorCompression(0.1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/IteratorInstantiaton.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using BenchmarkDotNet.Attributes; 6 | using gfoidl.DataCompression.Benchmarks.Infrastructure; 7 | 8 | namespace gfoidl.DataCompression.Benchmarks; 9 | 10 | [BenchmarkCategory("Instantiation")] 11 | [ShortRunJob] // Care only about allocations 12 | [GenericTypeArguments(typeof(DeadBandCompressionFactory))] 13 | [GenericTypeArguments(typeof(SwingingDoorCompressionFactory))] 14 | public class IteratorInstantiaton : Base where TFactory : ICompressionFactory, new() 15 | { 16 | private const int OperationsForMultiple = 1_000; 17 | private const int Count = 2; 18 | 19 | private readonly DataPoint[] _dataPoints = { new DataPoint((0, 1)), new DataPoint((1, 0)) }; 20 | private readonly ICompression _compression; 21 | //--------------------------------------------------------------------- 22 | public IteratorInstantiaton() 23 | { 24 | TFactory factory = new(); 25 | _compression = factory.Create(); 26 | } 27 | //--------------------------------------------------------------------- 28 | [Benchmark] 29 | public double SingleIteration() 30 | { 31 | using DataPointIterator iterator = _compression.Process(_dataPoints); 32 | return Consume(iterator); 33 | } 34 | //--------------------------------------------------------------------- 35 | [Benchmark] 36 | public async ValueTask SingleIterationASync() 37 | { 38 | IAsyncEnumerable source = this.SourceAsync(Count); 39 | using DataPointIterator iterator = _compression.ProcessAsync(source); 40 | return await ConsumeAsync(iterator); 41 | } 42 | //--------------------------------------------------------------------- 43 | [Benchmark(OperationsPerInvoke = OperationsForMultiple)] 44 | public double MultipleIterations() 45 | { 46 | double sum = 0; 47 | 48 | for (int i = 0; i < OperationsForMultiple; ++i) 49 | { 50 | using DataPointIterator iterator = _compression.Process(_dataPoints); 51 | sum += Consume(iterator); 52 | } 53 | 54 | return sum; 55 | } 56 | //--------------------------------------------------------------------- 57 | [Benchmark(OperationsPerInvoke = OperationsForMultiple)] 58 | public async ValueTask MultipleIterationsAsync() 59 | { 60 | double sum = 0; 61 | 62 | for (int i = 0; i < OperationsForMultiple; ++i) 63 | { 64 | IAsyncEnumerable source = this.SourceAsync(Count); 65 | using DataPointIterator iterator = _compression.ProcessAsync(source); 66 | sum += await ConsumeAsync(iterator); 67 | } 68 | 69 | return sum; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using BenchmarkDotNet.Running; 4 | using gfoidl.DataCompression.Benchmarks; 5 | 6 | BenchmarkSwitcher.FromAssembly(typeof(Base).Assembly).Run(args); 7 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/SwingingDoorCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using BenchmarkDotNet.Attributes; 5 | 6 | namespace gfoidl.DataCompression.Benchmarks; 7 | 8 | [BenchmarkCategory(Categories.Compression, Categories.Sync, Categories.SwingingDoor)] 9 | public class SwingingDoorCompression : Base 10 | { 11 | [Benchmark] 12 | public double Enumerate() 13 | { 14 | IEnumerable source = this.Source(); 15 | using DataPointIterator filtered = source.SwingingDoorCompression(0.1); 16 | return Consume(filtered); 17 | } 18 | //--------------------------------------------------------------------- 19 | [Benchmark] 20 | public DataPoint[] ToArray() 21 | { 22 | IEnumerable source = this.Source(); 23 | using DataPointIterator filtered = source.SwingingDoorCompression(0.1); 24 | return filtered.ToArray(); 25 | } 26 | //--------------------------------------------------------------------- 27 | [Benchmark] 28 | public List ToList() 29 | { 30 | IEnumerable source = this.Source(); 31 | using DataPointIterator filtered = source.SwingingDoorCompression(0.1); 32 | return filtered.ToList(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/SwingingDoorCompressionAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using BenchmarkDotNet.Attributes; 6 | 7 | namespace gfoidl.DataCompression.Benchmarks; 8 | 9 | [BenchmarkCategory(Categories.Compression, Categories.Async, Categories.SwingingDoor)] 10 | public class SwingingDoorCompressionAsync : Base 11 | { 12 | [Benchmark] 13 | public async ValueTask Enumerate() 14 | { 15 | IAsyncEnumerable source = this.SourceAsync(); 16 | using DataPointIterator filtered = source.SwingingDoorCompressionAsync(0.1); 17 | return await ConsumeAsync(filtered); 18 | } 19 | //--------------------------------------------------------------------- 20 | [Benchmark] 21 | public async ValueTask ToArray() 22 | { 23 | IAsyncEnumerable source = this.SourceAsync(); 24 | using DataPointIterator filtered = source.SwingingDoorCompressionAsync(0.1); 25 | return await filtered.ToArrayAsync(); 26 | } 27 | //--------------------------------------------------------------------- 28 | [Benchmark] 29 | public async ValueTask> ToList() 30 | { 31 | IAsyncEnumerable source = this.SourceAsync(); 32 | using DataPointIterator filtered = source.SwingingDoorCompressionAsync(0.1); 33 | return await filtered.ToListAsync(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.Benchmarks/gfoidl.DataCompression.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.ProfilingDemo/Program.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.ProfilingDemo 10 | { 11 | static class Program 12 | { 13 | private static readonly DataPoint[] s_dataPoints; 14 | private static List s_compressed = new List(1000); 15 | //--------------------------------------------------------------------- 16 | static Program() 17 | { 18 | Random rnd = new Random(); 19 | s_dataPoints = new DataPoint[1000]; 20 | 21 | for (int i = 0; i < s_dataPoints.Length; ++i) 22 | { 23 | s_dataPoints[i] = new DataPoint(i, rnd.NextDouble()); 24 | } 25 | } 26 | //--------------------------------------------------------------------- 27 | static async Task Main() 28 | { 29 | using CancellationTokenSource cts = new CancellationTokenSource(); 30 | int counter = 0; 31 | 32 | Task t = Task.Run(() => 33 | { 34 | // A single instance can be re-used over and over again 35 | DeadBandCompression deadBand = new DeadBandCompression(0.1); 36 | SwingingDoorCompression swingingDoor = new SwingingDoorCompression(0.1); 37 | 38 | while (!cts.IsCancellationRequested) 39 | { 40 | s_compressed.Clear(); 41 | counter++; 42 | 43 | // Allocates a new instance of compression each iteration 44 | //DataPointIterator compressed = s_dataPoints//.Select(d => d) 45 | // //.DeadBandCompression(0.05) 46 | // .SwingingDoorCompression(0.1); 47 | 48 | //DataPointIterator compressed = compression.Process(s_dataPoints.Select(d => d)); 49 | DataPointIterator deadBandCompressed = deadBand.Process(s_dataPoints); 50 | DataPointIterator swingingDoorCompressed = swingingDoor.Process(deadBandCompressed); 51 | 52 | foreach (DataPoint item in swingingDoorCompressed) 53 | { 54 | s_compressed.Add(item); 55 | } 56 | 57 | Console.Write("."); 58 | } 59 | }, cts.Token); 60 | 61 | Console.WriteLine("any key to stop..."); 62 | Console.ReadKey(); 63 | 64 | cts.Cancel(); 65 | 66 | try 67 | { 68 | await t; 69 | } 70 | catch (OperationCanceledException) 71 | { } 72 | 73 | Console.WriteLine($"\n\nend -- counter: {counter}"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /perf/gfoidl.DataCompression.ProfilingDemo/gfoidl.DataCompression.ProfilingDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(StandardTfm) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /perf/run-benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd gfoidl.DataCompression.Benchmarks 6 | 7 | # Running the benchmarks via --allCategories is much easier than via --filter and globbing. 8 | dotnet run -c Release --no-build --allCategories Compression Sync --join 9 | dotnet run -c Release --no-build --allCategories Compression Async --join 10 | dotnet run -c Release --no-build --allCategories Instantiation --join 11 | -------------------------------------------------------------------------------- /source/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | enable 7 | true 8 | 9 | 10 | 11 | https://gfoidl.github.io/DataCompression 12 | https://github.com/gfoidl/DataCompression 13 | MIT 14 | ReadMe.md 15 | git 16 | true 17 | 18 | 19 | 20 | true 21 | 22 | 23 | 24 | true 25 | $(NoWarn);CS1591 26 | 27 | 28 | 29 | embedded 30 | 31 | 32 | true 33 | snupkg 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /source/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Builders/ICollectionBuilder.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | namespace gfoidl.DataCompression.Builders 4 | { 5 | internal interface ICollectionBuilder 6 | { 7 | void Add(T item); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Builders/ListBuilder.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace gfoidl.DataCompression.Builders 6 | { 7 | internal struct ListBuilder : ICollectionBuilder 8 | { 9 | private readonly List _list; 10 | //--------------------------------------------------------------------- 11 | public ListBuilder(bool initialize) => _list = new List(); 12 | //--------------------------------------------------------------------- 13 | public void Add(T item) => _list.Add(item); 14 | //--------------------------------------------------------------------- 15 | public readonly List ToList() => _list; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace gfoidl.DataCompression 7 | { 8 | /// 9 | /// Defines the interface for the compression algorithms. 10 | /// 11 | public abstract class Compression : ICompression 12 | { 13 | private protected readonly double? _maxDeltaX; 14 | private protected readonly double? _minDeltaX; 15 | //--------------------------------------------------------------------- 16 | /// 17 | /// Creates a new instance of . 18 | /// 19 | /// 20 | /// Length of x before for sure a value gets recoreded. See . 21 | /// 22 | /// 23 | /// Length of x/time within no value gets recorded (after the last archived value). 24 | /// See . 25 | /// 26 | protected Compression(double? maxDeltaX = null, double? minDeltaX = null) 27 | { 28 | _maxDeltaX = maxDeltaX; 29 | _minDeltaX = minDeltaX; 30 | } 31 | //--------------------------------------------------------------------- 32 | /// 33 | public abstract bool ArchiveIncoming { get; } 34 | //--------------------------------------------------------------------- 35 | /// 36 | public double? MaxDeltaX => _maxDeltaX; 37 | //--------------------------------------------------------------------- 38 | /// 39 | public double? MinDeltaX => _minDeltaX; 40 | //--------------------------------------------------------------------- 41 | /// 42 | public DataPointIterator Process(IEnumerable data) 43 | { 44 | if (data is null) ThrowHelper.ThrowArgumentNull(ThrowHelper.ExceptionArgument.data); 45 | 46 | return this.ProcessCore(data); 47 | } 48 | //--------------------------------------------------------------------- 49 | #if NETSTANDARD2_1 50 | /// 51 | public DataPointIterator ProcessAsync(IAsyncEnumerable data) 52 | { 53 | if (data is null) ThrowHelper.ThrowArgumentNull(ThrowHelper.ExceptionArgument.data); 54 | 55 | return this.ProcessAsyncCore(data); 56 | } 57 | #endif 58 | //--------------------------------------------------------------------- 59 | /// 60 | /// Implementation of the compression / filtering. 61 | /// 62 | /// Input data 63 | /// The compressed / filtered data. 64 | protected abstract DataPointIterator ProcessCore(IEnumerable data); 65 | //--------------------------------------------------------------------- 66 | #if NETSTANDARD2_1 67 | /// 68 | /// Implementation of the compression / filtering. 69 | /// 70 | /// Input data 71 | /// The compressed / filtered data. 72 | protected abstract DataPointIterator ProcessAsyncCore(IAsyncEnumerable data); 73 | #endif 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/Compression.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | IAEAAAAAAAEAYAAAAgAAAAAAAAAEAAAAAAAAAAEAEAA= 7 | Compression.cs 8 | 9 | 10 | 11 | 12 | 13 | 14 | IAAAAAACAAAAAAAAIAAAAAAAAAAAAAAAAEAAAAAEEAA= 15 | Compression\DeadBandCompression\DeadBandCompression.cs 16 | 17 | 18 | 19 | 20 | 21 | IAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAEAA= 22 | Compression\NoCompression\NoCompression.cs 23 | 24 | 25 | 26 | 27 | 28 | IAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAEAEAAAEEAA= 29 | Compression\SwingingDoorCompression\SwingingDoorCompression.cs 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/DeadBandCompression/AsyncEnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | 8 | namespace gfoidl.DataCompression.Internal.DeadBand 9 | { 10 | internal sealed class AsyncEnumerableIterator : DeadBandCompressionIterator 11 | { 12 | public void SetData(DeadBandCompression deadBandCompression, IAsyncEnumerable source) 13 | { 14 | this.SetData(deadBandCompression as Compression, source); 15 | _deadBandCompression = deadBandCompression; 16 | } 17 | //--------------------------------------------------------------------- 18 | public override DataPointIterator Clone() => throw new NotSupportedException(); 19 | public override bool MoveNext() => throw new NotSupportedException(); 20 | public override DataPoint[] ToArray() => throw new NotSupportedException(); 21 | public override List ToList() => throw new NotSupportedException(); 22 | //--------------------------------------------------------------------- 23 | protected override void DisposeCore() 24 | { 25 | Debug.Assert(_deadBandCompression is not null); 26 | 27 | ref AsyncEnumerableIterator? cache = ref _deadBandCompression._cachedAsyncEnumerableIterator; 28 | Interlocked.CompareExchange(ref cache, this, null); 29 | 30 | base.DisposeCore(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/DeadBandCompression/DeadBandCompressionIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace gfoidl.DataCompression.Internal.DeadBand 8 | { 9 | internal abstract class DeadBandCompressionIterator : DataPointIterator 10 | { 11 | protected DeadBandCompression? _deadBandCompression; 12 | protected (double Min, double Max) _bounding; 13 | //--------------------------------------------------------------------- 14 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 15 | protected void GetBounding(in DataPoint dataPoint) 16 | { 17 | Debug.Assert(_deadBandCompression is not null); 18 | 19 | double y = dataPoint.Y; 20 | 21 | // Produces better code than updating _bounding directly 22 | ref (double Min, double Max) bounding = ref _bounding; 23 | 24 | bounding.Min = y - _deadBandCompression.InstrumentPrecision; 25 | bounding.Max = y + _deadBandCompression.InstrumentPrecision; 26 | } 27 | //--------------------------------------------------------------------- 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | protected internal sealed override ref (bool Archive, bool MaxDelta) IsPointToArchive(in DataPoint incoming, in DataPoint lastArchived) 30 | { 31 | ref (bool Archive, bool MaxDelta) archive = ref _archive; 32 | 33 | if (!this.IsMaxDeltaX(ref archive, lastArchived.X, incoming.X)) 34 | { 35 | archive.Archive = incoming.Y < _bounding.Min || _bounding.Max < incoming.Y; 36 | } 37 | 38 | return ref archive; 39 | } 40 | //--------------------------------------------------------------------- 41 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 42 | private void UpdatePoints(in DataPoint incoming) 43 | { 44 | if (!_archive.MaxDelta) 45 | { 46 | this.GetBounding(incoming); 47 | } 48 | } 49 | //--------------------------------------------------------------------- 50 | protected internal sealed override void Init(in DataPoint incoming) => this.UpdatePoints(incoming); 51 | 52 | // Override even if empty body, but this type is sealed so the virtual dispatch could be 53 | // eliminated by the JIT. 54 | protected internal sealed override void UpdateFilters(in DataPoint incoming, in DataPoint lastArchived) { } 55 | //--------------------------------------------------------------------- 56 | protected override void DisposeCore() 57 | { 58 | _deadBandCompression = null; 59 | _bounding = default; 60 | 61 | base.DisposeCore(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/DeadBandCompression/IndexedIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Internal.DeadBand 10 | { 11 | internal sealed class IndexedIterator : DeadBandCompressionIterator 12 | where TList : IList 13 | { 14 | private readonly DataPointIndexedIterator _inner = new(); 15 | private TList? _list; 16 | //--------------------------------------------------------------------- 17 | public void SetData(DeadBandCompression deadBandCompression, TList source) 18 | { 19 | Debug.Assert(source is not null); 20 | 21 | this.SetData(deadBandCompression); 22 | _deadBandCompression = deadBandCompression; 23 | _list = source; 24 | 25 | _inner.SetData(deadBandCompression, this, source); 26 | } 27 | //--------------------------------------------------------------------- 28 | public override DataPointIterator Clone() 29 | { 30 | Debug.Assert(_deadBandCompression is not null); 31 | Debug.Assert(_list is not null); 32 | 33 | IndexedIterator clone = new(); 34 | clone.SetData(_deadBandCompression, _list); 35 | 36 | return clone; 37 | } 38 | //--------------------------------------------------------------------- 39 | public override DataPointIterator GetEnumerator() => _inner!.GetEnumerator(); 40 | public override DataPoint[] ToArray() => _inner!.ToArray(); 41 | public override List ToList() => _inner!.ToList(); 42 | public override bool MoveNext() => throw new InvalidOperationException("Should operate on _inner"); 43 | //--------------------------------------------------------------------- 44 | #if NETSTANDARD2_1 45 | public override ValueTask ToArrayAsync(CancellationToken ct) => throw new NotSupportedException(); 46 | public override ValueTask> ToListAsync(CancellationToken ct) => throw new NotSupportedException(); 47 | #endif 48 | //--------------------------------------------------------------------- 49 | protected override void DisposeCore() 50 | { 51 | if (_state == DisposedState) 52 | { 53 | return; 54 | } 55 | 56 | Debug.Assert(_deadBandCompression is not null); 57 | 58 | //_inner?.Dispose(); !!! don't dispose _inner, as _inner disposes this instance 59 | 60 | _list = default; 61 | 62 | ref DataPointIterator? cache = ref _deadBandCompression._cachedIndexedIterator; 63 | Interlocked.CompareExchange(ref cache, this, null); 64 | 65 | base.DisposeCore(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/DeadBandCompression/SequentialEnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Internal.DeadBand 10 | { 11 | internal sealed class SequentialEnumerableIterator : DeadBandCompressionIterator 12 | { 13 | public void SetData(DeadBandCompression deadBandCompression, IEnumerable source) 14 | { 15 | this.SetData(deadBandCompression as Compression, source); 16 | _deadBandCompression = deadBandCompression; 17 | } 18 | //--------------------------------------------------------------------- 19 | public override DataPointIterator Clone() 20 | { 21 | Debug.Assert(_deadBandCompression is not null); 22 | Debug.Assert(_source is not null); 23 | 24 | SequentialEnumerableIterator clone = new(); 25 | clone.SetData(_deadBandCompression, _source); 26 | 27 | return clone; 28 | } 29 | //--------------------------------------------------------------------- 30 | #if NETSTANDARD2_1 31 | public override ValueTask ToArrayAsync(CancellationToken ct) => throw new NotSupportedException(); 32 | public override ValueTask> ToListAsync(CancellationToken ct) => throw new NotSupportedException(); 33 | #endif 34 | //--------------------------------------------------------------------- 35 | protected override void DisposeCore() 36 | { 37 | if (_state == DisposedState) 38 | { 39 | return; 40 | } 41 | 42 | Debug.Assert(_deadBandCompression is not null); 43 | 44 | ref SequentialEnumerableIterator? cache = ref _deadBandCompression._cachedSequentialEnumerableIterator; 45 | Interlocked.CompareExchange(ref cache, this, null); 46 | 47 | base.DisposeCore(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/NoCompression/AsyncEnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Internal.NoCompression 10 | { 11 | internal sealed class AsyncEnumerableIterator : NoCompressionIterator 12 | { 13 | public override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) 14 | => this.IterateCore(cancellationToken); 15 | //--------------------------------------------------------------------- 16 | private async IAsyncEnumerator IterateCore(CancellationToken cancellationToken) 17 | { 18 | Debug.Assert(_asyncSource != null); 19 | 20 | await foreach (DataPoint dataPoint in _asyncSource.WithCancellation(cancellationToken).ConfigureAwait(false)) 21 | { 22 | yield return dataPoint; 23 | } 24 | } 25 | //--------------------------------------------------------------------- 26 | private protected override async ValueTask BuildCollectionAsync(TBuilder builder, CancellationToken cancellationToken) 27 | { 28 | await foreach (DataPoint dataPoint in _asyncSource.WithCancellation(cancellationToken).ConfigureAwait(false)) 29 | { 30 | builder.Add(dataPoint); 31 | } 32 | } 33 | //--------------------------------------------------------------------- 34 | public override DataPointIterator Clone() => throw new NotSupportedException(); 35 | public override bool MoveNext() => throw new NotSupportedException(); 36 | public override DataPoint[] ToArray() => throw new NotSupportedException(); 37 | public override List ToList() => throw new NotSupportedException(); 38 | protected internal override void Init(in DataPoint incoming) => throw new NotSupportedException(); 39 | protected internal override ref (bool Archive, bool MaxDelta) IsPointToArchive(in DataPoint incoming, in DataPoint lastArchived) => throw new NotSupportedException(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/NoCompression/EnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using gfoidl.DataCompression.Builders; 9 | 10 | namespace gfoidl.DataCompression.Internal.NoCompression 11 | { 12 | internal sealed class EnumerableIterator : NoCompressionIterator 13 | { 14 | public override DataPointIterator Clone() 15 | { 16 | Debug.Assert(_algorithm is not null); 17 | Debug.Assert(_source is not null); 18 | 19 | EnumerableIterator clone = new(); 20 | clone.SetData(_algorithm, _source); 21 | 22 | return clone; 23 | } 24 | //--------------------------------------------------------------------- 25 | public override bool MoveNext() 26 | { 27 | if (_state == InitialState || _enumerator == null) 28 | ThrowHelper.ThrowInvalidOperation(ThrowHelper.ExceptionResource.GetEnumerator_must_be_called_first); 29 | 30 | if (_enumerator.MoveNext()) 31 | { 32 | _lastArchived = _enumerator.Current; 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | //--------------------------------------------------------------------- 39 | public override DataPoint[] ToArray() 40 | { 41 | Debug.Assert(_source != null); 42 | 43 | var arrayBuilder = new ArrayBuilder(true); 44 | arrayBuilder.AddRange(_source); 45 | 46 | return arrayBuilder.ToArray(); 47 | } 48 | //--------------------------------------------------------------------- 49 | public override List ToList() => new List(_source); 50 | //--------------------------------------------------------------------- 51 | protected internal override void Init(in DataPoint incoming) => throw new NotSupportedException(); 52 | protected internal override ref (bool Archive, bool MaxDelta) IsPointToArchive(in DataPoint incoming, in DataPoint lastArchived) => throw new NotSupportedException(); 53 | //--------------------------------------------------------------------- 54 | #if NETSTANDARD2_1 55 | public override ValueTask ToArrayAsync(CancellationToken ct) => throw new NotSupportedException(); 56 | public override ValueTask> ToListAsync(CancellationToken ct) => throw new NotSupportedException(); 57 | #endif 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/NoCompression/NoCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using gfoidl.DataCompression.Internal.NoCompression; 5 | 6 | namespace gfoidl.DataCompression 7 | { 8 | /// 9 | /// A filter that performs no compression 10 | /// 11 | public sealed class NoCompression : Compression 12 | { 13 | internal static readonly NoCompression s_instance = new NoCompression(); 14 | //--------------------------------------------------------------------- 15 | /// 16 | public override bool ArchiveIncoming => true; 17 | //------------------------------------------------------------------------- 18 | /// 19 | protected override DataPointIterator ProcessCore(IEnumerable data) 20 | { 21 | EnumerableIterator iter = new(); 22 | iter.SetData(this, data); 23 | 24 | return iter; 25 | } 26 | //--------------------------------------------------------------------- 27 | #if NETSTANDARD2_1 28 | /// 29 | /// Implementation of the compression / filtering. 30 | /// 31 | /// Input data 32 | /// The compressed / filtered data. 33 | protected override DataPointIterator ProcessAsyncCore(IAsyncEnumerable data) 34 | { 35 | AsyncEnumerableIterator iter = new(); 36 | iter.SetData(this, data); 37 | 38 | return iter; 39 | } 40 | #endif 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/NoCompression/NoCompressionIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | namespace gfoidl.DataCompression.Internal.NoCompression 4 | { 5 | internal abstract class NoCompressionIterator : DataPointIterator 6 | { } 7 | } 8 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/SwingingDoorCompression/AsyncEnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | 8 | namespace gfoidl.DataCompression.Internal.SwingingDoor 9 | { 10 | internal sealed class AsyncEnumerableIterator : SwingingDoorCompressionIterator 11 | { 12 | public void SetData(SwingingDoorCompression swingingDoorCompression, IAsyncEnumerable source) 13 | { 14 | this.SetData(swingingDoorCompression as Compression, source); 15 | _swingingDoorCompression = swingingDoorCompression; 16 | } 17 | //--------------------------------------------------------------------- 18 | public override DataPointIterator Clone() => throw new NotSupportedException(); 19 | public override bool MoveNext() => throw new NotSupportedException(); 20 | public override DataPoint[] ToArray() => throw new NotSupportedException(); 21 | public override List ToList() => throw new NotSupportedException(); 22 | //--------------------------------------------------------------------- 23 | protected override void DisposeCore() 24 | { 25 | Debug.Assert(_swingingDoorCompression is not null); 26 | 27 | ref AsyncEnumerableIterator? cache = ref _swingingDoorCompression._cachedAsyncEnumerableIterator; 28 | Interlocked.CompareExchange(ref cache, this, null); 29 | 30 | base.DisposeCore(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/SwingingDoorCompression/IndexedIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Internal.SwingingDoor 10 | { 11 | internal sealed class IndexedIterator : SwingingDoorCompressionIterator 12 | where TList : IList 13 | { 14 | private readonly DataPointIndexedIterator _inner = new(); 15 | private TList? _list; 16 | //--------------------------------------------------------------------- 17 | public void SetData(SwingingDoorCompression swingingDoorCompression, TList source) 18 | { 19 | Debug.Assert(source is not null); 20 | 21 | this.SetData(swingingDoorCompression); 22 | _swingingDoorCompression = swingingDoorCompression; 23 | _list = source; 24 | 25 | _inner.SetData(swingingDoorCompression, this, source); 26 | } 27 | //--------------------------------------------------------------------- 28 | public override DataPointIterator Clone() 29 | { 30 | Debug.Assert(_swingingDoorCompression is not null); 31 | Debug.Assert(_list is not null); 32 | 33 | IndexedIterator clone = new(); 34 | clone.SetData(_swingingDoorCompression, _list); 35 | 36 | return clone; 37 | } 38 | //--------------------------------------------------------------------- 39 | public override DataPointIterator GetEnumerator() => _inner!.GetEnumerator(); 40 | public override DataPoint[] ToArray() => _inner!.ToArray(); 41 | public override List ToList() => _inner!.ToList(); 42 | public override bool MoveNext() => throw new InvalidOperationException("Should operate on _inner"); 43 | //--------------------------------------------------------------------- 44 | #if NETSTANDARD2_1 45 | public override ValueTask ToArrayAsync(CancellationToken ct) => throw new NotSupportedException(); 46 | public override ValueTask> ToListAsync(CancellationToken ct) => throw new NotSupportedException(); 47 | #endif 48 | //--------------------------------------------------------------------- 49 | protected override void DisposeCore() 50 | { 51 | if (_state == DisposedState) 52 | { 53 | return; 54 | } 55 | 56 | Debug.Assert(_swingingDoorCompression is not null); 57 | 58 | //_inner?.Dispose(); !!! don't dispose _inner, as _inner disposes this instance 59 | 60 | _list = default; 61 | 62 | ref DataPointIterator? cache = ref _swingingDoorCompression._cachedIndexedIterator; 63 | Interlocked.CompareExchange(ref cache, this, null); 64 | 65 | base.DisposeCore(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/SwingingDoorCompression/SequentialEnumerableIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace gfoidl.DataCompression.Internal.SwingingDoor 10 | { 11 | internal sealed class SequentialEnumerableIterator : SwingingDoorCompressionIterator 12 | { 13 | public void SetData(SwingingDoorCompression swingingDoorCompression, IEnumerable source) 14 | { 15 | this.SetData(swingingDoorCompression as Compression, source); 16 | _swingingDoorCompression = swingingDoorCompression; 17 | } 18 | //--------------------------------------------------------------------- 19 | public override DataPointIterator Clone() 20 | { 21 | Debug.Assert(_swingingDoorCompression is not null); 22 | Debug.Assert(_source is not null); 23 | 24 | SequentialEnumerableIterator clone = new(); 25 | clone.SetData(_swingingDoorCompression, _source); 26 | 27 | return clone; 28 | } 29 | //--------------------------------------------------------------------- 30 | #if NETSTANDARD2_1 31 | public override ValueTask ToArrayAsync(CancellationToken ct) => throw new NotSupportedException(); 32 | public override ValueTask> ToListAsync(CancellationToken ct) => throw new NotSupportedException(); 33 | #endif 34 | //--------------------------------------------------------------------- 35 | protected override void DisposeCore() 36 | { 37 | if (_state == DisposedState) 38 | { 39 | return; 40 | } 41 | 42 | Debug.Assert(_swingingDoorCompression is not null); 43 | 44 | ref SequentialEnumerableIterator? cache = ref _swingingDoorCompression._cachedSequentialEnumerableIterator; 45 | Interlocked.CompareExchange(ref cache, this, null); 46 | 47 | base.DisposeCore(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Compression/SwingingDoorCompression/SwingingDoorCompressionIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Runtime.CompilerServices; 4 | using System; 5 | using System.Diagnostics; 6 | 7 | namespace gfoidl.DataCompression.Internal.SwingingDoor 8 | { 9 | internal abstract class SwingingDoorCompressionIterator : DataPointIterator 10 | { 11 | protected static readonly (double Max, double Min) s_newDoor = (double.PositiveInfinity, double.NegativeInfinity); 12 | //--------------------------------------------------------------------- 13 | protected SwingingDoorCompression? _swingingDoorCompression; 14 | protected (double Max, double Min) _slope; 15 | //--------------------------------------------------------------------- 16 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 17 | protected internal sealed override ref (bool Archive, bool MaxDelta) IsPointToArchive(in DataPoint incoming, in DataPoint lastArchived) 18 | { 19 | ref (bool Archive, bool MaxDelta) archive = ref _archive; 20 | 21 | if (!this.IsMaxDeltaX(ref archive, lastArchived.X, incoming.X)) 22 | { 23 | // Better to compare via gradient (1 calculation) than comparing to allowed y-values (2 calcuations) 24 | // Obviously, the result should be the same ;-) 25 | double slopeToIncoming = lastArchived.Gradient(incoming); 26 | archive.Archive = slopeToIncoming < _slope.Min || _slope.Max < slopeToIncoming; 27 | } 28 | 29 | return ref archive; 30 | } 31 | //--------------------------------------------------------------------- 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | protected void CloseTheDoor(in DataPoint incoming, in DataPoint lastArchived) 34 | { 35 | Debug.Assert(_swingingDoorCompression is not null); 36 | 37 | double delta_x = incoming.X - lastArchived.X; 38 | 39 | if (delta_x > 0) 40 | { 41 | double delta_y = incoming.Y - lastArchived.Y; 42 | double delta_yUpper = delta_y + _swingingDoorCompression.CompressionDeviation; 43 | double delta_yLower = delta_y - _swingingDoorCompression.CompressionDeviation; 44 | 45 | double upperSlope = delta_yUpper / delta_x; 46 | double lowerSlope = delta_yLower / delta_x; 47 | 48 | if (upperSlope < _slope.Max) _slope.Max = upperSlope; 49 | if (lowerSlope > _slope.Min) _slope.Min = lowerSlope; 50 | } 51 | else 52 | { 53 | GradientEquality(incoming, lastArchived); 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.NoInlining)] 57 | void GradientEquality(in DataPoint incoming, in DataPoint lastArchived) 58 | { 59 | double upperSlope = lastArchived.GradientEquality(incoming, return0OnEquality: true); 60 | double lowerSlope = lastArchived.GradientEquality(incoming, return0OnEquality: true); 61 | 62 | if (upperSlope < _slope.Max) _slope.Max = upperSlope; 63 | if (lowerSlope > _slope.Min) _slope.Min = lowerSlope; 64 | } 65 | } 66 | //--------------------------------------------------------------------- 67 | private void OpenNewDoor() => _slope = s_newDoor; 68 | //--------------------------------------------------------------------- 69 | protected internal sealed override void Init(in DataPoint incoming) => this.OpenNewDoor(); 70 | protected internal sealed override void UpdateFilters(in DataPoint incoming, in DataPoint lastArchived) => this.CloseTheDoor(incoming, lastArchived); 71 | //--------------------------------------------------------------------- 72 | protected override void DisposeCore() 73 | { 74 | _swingingDoorCompression = null; 75 | _slope = default; 76 | 77 | base.DisposeCore(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/EmptyDataPointIterator.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace gfoidl.DataCompression 9 | { 10 | internal sealed class EmptyDataPointIterator : DataPointIterator 11 | { 12 | public override DataPointIterator Clone() => this; 13 | public override bool MoveNext() => false; 14 | public override DataPoint[] ToArray() => Array.Empty(); 15 | public override List ToList() => new List(); 16 | //--------------------------------------------------------------------- 17 | #if NETSTANDARD2_1 18 | public override ValueTask ToArrayAsync(CancellationToken ct) => new ValueTask(Array.Empty()); 19 | public override ValueTask> ToListAsync(CancellationToken ct) => new ValueTask>(new List()); 20 | #endif 21 | //--------------------------------------------------------------------- 22 | protected internal override void Init(in DataPoint incoming) => throw new NotSupportedException(); 23 | protected internal override ref (bool Archive, bool MaxDelta) IsPointToArchive(in DataPoint incoming, in DataPoint lastArchived) => throw new NotSupportedException(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/ICompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace gfoidl.DataCompression 7 | { 8 | /// 9 | /// Defines the interface for the compression algorithms. 10 | /// 11 | public interface ICompression 12 | { 13 | /// 14 | /// When set to true the incoming value is archived in addition 15 | /// to the last snapshot. 16 | /// 17 | /// 18 | /// For instance in the the last snapshot 19 | /// is archived, as well as the current incoming value, therefore this property 20 | /// is set to true.
21 | /// In the only the last snapshot is 22 | /// archived, so this property is set to false. 23 | ///
24 | bool ArchiveIncoming { get; } 25 | //------------------------------------------------------------------------- 26 | /// 27 | /// Length of x before for sure a value gets recorded. 28 | /// 29 | /// 30 | /// Cf. ExMax in documentation.
31 | /// When specified as the 32 | /// are used. 33 | /// 34 | /// When value is null, no value -- except the first and last -- are 35 | /// guaranteed to be recorded. 36 | /// 37 | ///
38 | double? MaxDeltaX { get; } 39 | //--------------------------------------------------------------------- 40 | /// 41 | /// Length of x/time within no value gets recorded (after the last archived value) 42 | /// 43 | double? MinDeltaX { get; } 44 | //--------------------------------------------------------------------- 45 | /// 46 | /// Performs the compression / filtering of the input data. 47 | /// 48 | /// Input data 49 | /// The compressed / filtered data. 50 | /// 51 | /// is null. 52 | /// 53 | DataPointIterator Process(IEnumerable data); 54 | //--------------------------------------------------------------------- 55 | #if NETSTANDARD2_1 56 | /// 57 | /// Performs the compression / filtering of the input data. 58 | /// 59 | /// Input data 60 | /// The compressed / filtered data. 61 | /// 62 | /// is null. 63 | /// 64 | DataPointIterator ProcessAsync(IAsyncEnumerable data); 65 | #endif 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/ThrowHelper.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Diagnostics; 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace gfoidl.DataCompression 8 | { 9 | internal static class ThrowHelper 10 | { 11 | [DoesNotReturn] public static void ThrowArgumentNull(ExceptionArgument argument) => throw new ArgumentNullException(GetArgumentName(argument)); 12 | [DoesNotReturn] public static void ThrowArgumentOutOfRange(ExceptionArgument argument) => throw new ArgumentOutOfRangeException(GetArgumentName(argument)); 13 | [DoesNotReturn] public static void ThrowArgument(ExceptionResource resource) => throw new ArgumentException(GetResourceText(resource)); 14 | [DoesNotReturn] public static void ThrowNotSupported() => throw new NotSupportedException(); 15 | [DoesNotReturn] public static void ThrowIfDisposed(ExceptionArgument argument) => throw new ObjectDisposedException(argument.ToString()); 16 | [DoesNotReturn] public static void ThrowInvalidOperation(ExceptionResource resource) => throw new InvalidOperationException(GetResourceText(resource)); 17 | //--------------------------------------------------------------------- 18 | private static string GetArgumentName(ExceptionArgument argument) 19 | { 20 | Debug.Assert( 21 | Enum.IsDefined(typeof(ExceptionArgument), argument), 22 | "The enum value is not defined, please check the 'ExceptionArgument' enum."); 23 | 24 | return argument.ToString(); 25 | } 26 | //--------------------------------------------------------------------- 27 | private static string GetResourceName(ExceptionResource resource) 28 | { 29 | Debug.Assert( 30 | Enum.IsDefined(typeof(ExceptionResource), resource), 31 | "The enum value is not defined, please check the 'ExceptionResource' enum."); 32 | 33 | return resource.ToString(); 34 | } 35 | //--------------------------------------------------------------------- 36 | private static string GetResourceText(ExceptionResource resource) 37 | { 38 | string? tmp = Strings.ResourceManager.GetString(GetResourceName(resource), Strings.Culture); 39 | 40 | Debug.Assert(tmp != null); 41 | return tmp!; 42 | } 43 | //--------------------------------------------------------------------- 44 | public enum ExceptionArgument : byte 45 | { 46 | algorithm, 47 | data, 48 | iterator 49 | } 50 | //--------------------------------------------------------------------- 51 | public enum ExceptionResource : byte 52 | { 53 | GetEnumerator_must_be_called_first, 54 | Gradient_A_eq_B, 55 | Should_not_happen 56 | } 57 | } 58 | } 59 | //----------------------------------------------------------------------------- 60 | #if !NETSTANDARD2_1 61 | namespace System.Diagnostics.CodeAnalysis 62 | { 63 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 64 | internal sealed class DoesNotReturnAttribute : Attribute 65 | { } 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Wrappers/ArrayWrapper.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace gfoidl.DataCompression.Wrappers 8 | { 9 | #pragma warning disable CS1591 10 | public readonly struct ArrayWrapper : IList 11 | { 12 | private readonly T[] _array; 13 | //--------------------------------------------------------------------- 14 | public ArrayWrapper(T[]? array) => _array = array ?? throw new ArgumentNullException(nameof(array)); 15 | //--------------------------------------------------------------------- 16 | public T this[int index] 17 | { 18 | get => _array[index]; 19 | set => _array[index] = value; 20 | } 21 | //--------------------------------------------------------------------- 22 | public int Count => _array.Length; 23 | //--------------------------------------------------------------------- 24 | public bool IsReadOnly => throw new NotSupportedException(); 25 | public void Add(T item) => throw new NotSupportedException(); 26 | public void Clear() => throw new NotSupportedException(); 27 | public bool Contains(T item) => throw new NotSupportedException(); 28 | public void CopyTo(T[] array, int arrayIndex) => throw new NotSupportedException(); 29 | public IEnumerator GetEnumerator() => throw new NotSupportedException(); 30 | public int IndexOf(T item) => throw new NotSupportedException(); 31 | public void Insert(int index, T item) => throw new NotSupportedException(); 32 | public bool Remove(T item) => throw new NotSupportedException(); 33 | public void RemoveAt(int index) => throw new NotSupportedException(); 34 | IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); 35 | } 36 | #pragma warning restore CS1591 37 | } 38 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/Wrappers/ListWrapper.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace gfoidl.DataCompression.Wrappers 8 | { 9 | #pragma warning disable CS1591 10 | public readonly struct ListWrapper : IList 11 | { 12 | private readonly List _list; 13 | //--------------------------------------------------------------------- 14 | public ListWrapper(List? list) => _list = list ?? throw new ArgumentNullException(nameof(list)); 15 | //--------------------------------------------------------------------- 16 | public T this[int index] 17 | { 18 | get => _list[index]; 19 | set => _list[index] = value; 20 | } 21 | //--------------------------------------------------------------------- 22 | public int Count => _list.Count; 23 | //--------------------------------------------------------------------- 24 | public bool IsReadOnly => throw new NotSupportedException(); 25 | public void Add(T item) => throw new NotSupportedException(); 26 | public void Clear() => throw new NotSupportedException(); 27 | public bool Contains(T item) => throw new NotSupportedException(); 28 | public void CopyTo(T[] array, int arrayIndex) => throw new NotSupportedException(); 29 | public IEnumerator GetEnumerator() => throw new NotSupportedException(); 30 | public int IndexOf(T item) => throw new NotSupportedException(); 31 | public void Insert(int index, T item) => throw new NotSupportedException(); 32 | public bool Remove(T item) => throw new NotSupportedException(); 33 | public void RemoveAt(int index) => throw new NotSupportedException(); 34 | IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); 35 | } 36 | #pragma warning restore CS1591 37 | } 38 | -------------------------------------------------------------------------------- /source/gfoidl.DataCompression/gfoidl.DataCompression.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1;netstandard2.0 5 | 6 | 7 | 8 | false 9 | $(NoWarn);CS8600;CS8602;CS8604 10 | 11 | 12 | 13 | data compression;swinging-door-algorithm;dead-band-filtering;swinging-door;swinging 14 | Provides several data-compression algorithms: 15 | 16 | * error band elimination 17 | * swinging door algorithm 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | True 33 | True 34 | Strings.resx 35 | 36 | 37 | 38 | 39 | 40 | ResXFileCodeGenerator 41 | Strings.Designer.cs 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/.editorconfig: -------------------------------------------------------------------------------- 1 | root = false 2 | 3 | # C# files 4 | [*.cs] 5 | 6 | # NUnit2005: Consider using Assert.That(actual, Is.EqualTo(expected)) instead of Assert.AreEqual(expected, actual). 7 | dotnet_diagnostic.NUnit2005.severity = silent 8 | 9 | # NUnit2015: Consider using Assert.That(actual, Is.SameAs(expected)) instead of Assert.AreSame(expected, actual). 10 | dotnet_diagnostic.NUnit2015.severity = silent 11 | 12 | # NUnit2027: Consider using Assert.That(actual, Is.GreaterThan(expected)) instead of Assert.Greater(actual, expected). 13 | dotnet_diagnostic.NUnit2027.severity = silent 14 | 15 | # RCS1090: Call 'ConfigureAwait(false)'. 16 | dotnet_diagnostic.RCS1090.severity = silent 17 | 18 | # NUnit2003: Consider using Assert.That(expr, Is.True) instead of Assert.IsTrue(expr). 19 | dotnet_diagnostic.NUnit2003.severity = silent 20 | 21 | # NUnit2002: Consider using Assert.That(expr, Is.False) instead of Assert.IsFalse(expr). 22 | dotnet_diagnostic.NUnit2002.severity = silent 23 | 24 | # NUnit2038: Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual) 25 | dotnet_diagnostic.NUnit2038.severity = silent 26 | 27 | # NUnit2017: Consider using Assert.That(expr, Is.Null) instead of Assert.IsNull(expr) 28 | dotnet_diagnostic.NUnit2017.severity = silent 29 | 30 | # NUnit2031: Consider using Assert.That(actual, Is.Not.SameAs(expected)) instead of Assert.AreNotSame(expected, actual) 31 | dotnet_diagnostic.NUnit2031.severity = none 32 | -------------------------------------------------------------------------------- /tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(StandardTfm) 7 | $(StandardTestTfms);net48 8 | 9 | 10 | 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | all 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Builders/ArrayBuilderTests/Add.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using gfoidl.DataCompression.Builders; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.Builders.ArrayBuilderTests 8 | { 9 | [TestFixture] 10 | public class Add 11 | { 12 | [Test, TestCaseSource(nameof(Add_items_ToArray___correct_result_TestCases))] 13 | public void Add_items_ToArray___correct_result(int size) 14 | { 15 | var expected = new int[size]; 16 | var sut = new ArrayBuilder(true); 17 | 18 | for (int i = 0; i < size; ++i) 19 | { 20 | expected[i] = i; 21 | sut.Add(i); 22 | } 23 | 24 | int[] actual = sut.ToArray(); 25 | 26 | CollectionAssert.AreEqual(expected, actual); 27 | } 28 | //--------------------------------------------------------------------- 29 | private static IEnumerable Add_items_ToArray___correct_result_TestCases() 30 | { 31 | yield return new TestCaseData(0); 32 | yield return new TestCaseData(1); 33 | yield return new TestCaseData(2); 34 | yield return new TestCaseData(4); 35 | yield return new TestCaseData(5); // StartCapacity + 1 36 | yield return new TestCaseData(8); 37 | yield return new TestCaseData(9); // ResizeThreshold + 1 38 | yield return new TestCaseData(16); 39 | yield return new TestCaseData(100); 40 | yield return new TestCaseData(1_000); 41 | yield return new TestCaseData(1_001); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Builders/ArrayBuilderTests/AddRange.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using gfoidl.DataCompression.Builders; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Builders.ArrayBuilderTests 9 | { 10 | [TestFixture] 11 | public class AddRange 12 | { 13 | [Test] 14 | public void IEnumerable___correct_result() 15 | { 16 | int[] expected = Getvalues().ToArray(); 17 | var sut = new ArrayBuilder(true); 18 | 19 | sut.AddRange(Getvalues()); 20 | 21 | int[] actual = sut.ToArray(); 22 | 23 | CollectionAssert.AreEqual(expected, actual); 24 | } 25 | //--------------------------------------------------------------------- 26 | [Test] 27 | public void Array___correct_result() 28 | { 29 | int[] expected = Getvalues().ToArray(); 30 | var sut = new ArrayBuilder(true); 31 | 32 | sut.AddRange(expected); 33 | 34 | int[] actual = sut.ToArray(); 35 | 36 | CollectionAssert.AreEqual(expected, actual); 37 | } 38 | //--------------------------------------------------------------------- 39 | private static IEnumerable Getvalues() 40 | { 41 | yield return 0; 42 | yield return 1; 43 | yield return 2; 44 | yield return 4; 45 | yield return 8; 46 | yield return 16; 47 | yield return 100; 48 | yield return 1_000; 49 | yield return 1_001; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/Base.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | 5 | #if NETCOREAPP 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | #endif 10 | 11 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 12 | { 13 | public abstract class Base : Compression.Base 14 | { 15 | protected static IEnumerable RawDataForTrend() => s_ser.Read("data/dead-band/trend_raw.csv"); 16 | protected static IEnumerable ExpectedForTrend() => s_ser.Read("data/dead-band/trend_compressed.csv"); 17 | protected static IEnumerable RawDataForMaxDelta() => s_ser.Read("data/dead-band/maxDelta_raw.csv"); 18 | protected static IEnumerable ExpectedForMaxDelta() => s_ser.Read("data/dead-band/maxDelta_compressed.csv"); 19 | //--------------------------------------------------------------------- 20 | #if NETCOREAPP 21 | protected static async IAsyncEnumerable RawDataForTrendAsync([EnumeratorCancellation] CancellationToken ct = default) 22 | { 23 | foreach (DataPoint dp in RawDataForTrend()) 24 | { 25 | ct.ThrowIfCancellationRequested(); 26 | await Task.Yield(); 27 | yield return dp; 28 | } 29 | } 30 | //--------------------------------------------------------------------- 31 | protected static async IAsyncEnumerable RawDataForMaxDeltaAsync([EnumeratorCancellation] CancellationToken ct = default) 32 | { 33 | foreach (DataPoint dp in RawDataForMaxDelta()) 34 | { 35 | ct.ThrowIfCancellationRequested(); 36 | await Task.Yield(); 37 | yield return dp; 38 | } 39 | } 40 | #endif 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/Clone.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 7 | { 8 | public class Clone : Base 9 | { 10 | [Test] 11 | public void Cloning_iterates_over_fresh_set() 12 | { 13 | var sut = new DeadBandCompression(0.1); 14 | var data = KnownSequence().ToArray().Select(dp => dp); 15 | var expected = KnownSequenceExpected(swingingDoor: false).ToArray(); 16 | var filter = sut.Process(data); 17 | 18 | var iterator = filter.GetEnumerator(); 19 | iterator.MoveNext(); 20 | 21 | iterator = filter.Clone().GetEnumerator(); 22 | 23 | Assert.Multiple(() => 24 | { 25 | int step = 0; 26 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 27 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 28 | step++; 29 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 30 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 31 | step++; 32 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 33 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 34 | step++; 35 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 36 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 37 | step++; 38 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 39 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 40 | step++; 41 | Assert.IsFalse(iterator.MoveNext(), $"MoveNext step: {step++}"); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/MoveNextAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 9 | { 10 | public class MoveNextAsync : Base 11 | { 12 | [Test] 13 | public async Task Empty_IAsyncEnumerable___empty_result() 14 | { 15 | var sut = new DeadBandCompression(0.1); 16 | var data = EmptyAsync(); 17 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 18 | 19 | Assert.IsFalse(await iterator.MoveNextAsync()); 20 | } 21 | //--------------------------------------------------------------------- 22 | [Test] 23 | public async Task Empty_IAsyncEnumerable_foreach___empty_result() 24 | { 25 | var sut = new DeadBandCompression(0.1); 26 | var data = EmptyAsync(); 27 | 28 | int count = 0; 29 | await foreach (DataPoint db in sut.ProcessAsync(data)) 30 | { 31 | count++; 32 | } 33 | 34 | Assert.AreEqual(0, count); 35 | } 36 | //--------------------------------------------------------------------- 37 | [Test] 38 | public void Known_sequence___correct_result() 39 | { 40 | var sut = new DeadBandCompression(0.1); 41 | var data = KnownSequenceAsync(); 42 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 43 | var expected = KnownSequenceExpected(swingingDoor: false).ToArray(); 44 | 45 | Assert.Multiple(async () => 46 | { 47 | await iterator.MoveNextAsync(); 48 | Assert.AreEqual(expected[0], iterator.Current); 49 | await iterator.MoveNextAsync(); 50 | Assert.AreEqual(expected[1], iterator.Current); 51 | await iterator.MoveNextAsync(); 52 | Assert.AreEqual(expected[2], iterator.Current); 53 | await iterator.MoveNextAsync(); 54 | Assert.AreEqual(expected[3], iterator.Current); 55 | }); 56 | } 57 | //--------------------------------------------------------------------- 58 | [Test] 59 | public async Task Known_sequence_foreach___correct_result() 60 | { 61 | var sut = new DeadBandCompression(0.1); 62 | var data = KnownSequenceAsync(); 63 | var result = sut.ProcessAsync(data); 64 | var expected = KnownSequenceExpected(swingingDoor: false).ToArray(); 65 | var actual = new List(); 66 | 67 | await foreach (DataPoint dp in result) 68 | actual.Add(dp); 69 | 70 | CollectionAssert.AreEqual(expected, actual); 71 | } 72 | //--------------------------------------------------------------------- 73 | [Test] 74 | public async Task InstrumentPrecision_0___input_echoed() 75 | { 76 | var sut = new DeadBandCompression(0); 77 | var data = KnownSequenceAsync(); 78 | var result = sut.ProcessAsync(data); 79 | var expected = KnownSequence().ToArray(); 80 | var actual = new List(); 81 | 82 | await foreach (DataPoint dp in result) 83 | actual.Add(dp); 84 | 85 | CollectionAssert.AreEqual(expected, actual); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/ProcessAsyncCore.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 11 | { 12 | public class ProcessAsyncCore : Base 13 | { 14 | [Test] 15 | public async Task Data_given_as_IAsyncEnumerable___OK() 16 | { 17 | var sut = new DeadBandCompression(0.1); 18 | var data = RawDataForTrendAsync(); 19 | var expected = ExpectedForTrend().ToList(); 20 | 21 | var actual = new List(); 22 | await foreach (DataPoint dp in sut.ProcessAsync(data)) 23 | actual.Add(dp); 24 | 25 | Print(expected, "expected"); 26 | Print(actual , "actual"); 27 | CollectionAssert.AreEqual(expected, actual); 28 | } 29 | //--------------------------------------------------------------------- 30 | [Test] 31 | public void Cancellation_after_two_items___OK() 32 | { 33 | var sut = new DeadBandCompression(0.1); 34 | var data = KnownSequenceAsync(); 35 | var expected = KnownSequence().Take(2).ToList(); 36 | var cts = new CancellationTokenSource(); 37 | 38 | var actual = new List(); 39 | int idx = 0; 40 | 41 | Assert.ThrowsAsync(async () => 42 | { 43 | await foreach (DataPoint dp in sut.ProcessAsync(data).WithCancellation(cts.Token)) 44 | { 45 | actual.Add(dp); 46 | idx++; 47 | 48 | if (idx == 2) cts.Cancel(); 49 | } 50 | }); 51 | 52 | CollectionAssert.AreEqual(actual, expected); 53 | } 54 | //--------------------------------------------------------------------- 55 | [Test] 56 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 57 | { 58 | var sut = new DeadBandCompression(0.1, 4d); 59 | var data = RawDataForMaxDeltaAsync(); 60 | var expected = ExpectedForMaxDelta().ToList(); 61 | 62 | var actual = new List(); 63 | await foreach (DataPoint dp in sut.ProcessAsync(data)) 64 | actual.Add(dp); 65 | 66 | CollectionAssert.AreEqual(expected, actual); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/ToArrayAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 11 | { 12 | public class ToArrayAsync : Base 13 | { 14 | [Test] 15 | public async Task Empty_IAsyncEnumerable___empty_result() 16 | { 17 | var sut = new DeadBandCompression(0.1); 18 | var data = EmptyAsync(); 19 | 20 | var actual = sut.ProcessAsync(data); 21 | 22 | Assert.AreEqual(0, (await actual.ToListAsync()).Count); 23 | } 24 | //--------------------------------------------------------------------- 25 | [Test] 26 | public async Task Data_given_as_IAsyncEnumerable___OK() 27 | { 28 | var sut = new DeadBandCompression(0.1); 29 | var data = RawDataForTrendAsync(); 30 | var expected = ExpectedForTrend().ToList(); 31 | 32 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 33 | 34 | CollectionAssert.AreEqual(expected, actual); 35 | } 36 | //--------------------------------------------------------------------- 37 | [Test] 38 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 39 | { 40 | var sut = new DeadBandCompression(0.1, 4d); 41 | var data = RawDataForMaxDeltaAsync(); 42 | var expected = ExpectedForMaxDelta().ToList(); 43 | 44 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 45 | 46 | CollectionAssert.AreEqual(expected, actual); 47 | } 48 | //--------------------------------------------------------------------- 49 | [Test] 50 | public async Task IEnumerable_iterated_and_ToArray___OK() 51 | { 52 | var sut = new DeadBandCompression(0.1); 53 | var data = RawDataForTrendAsync(); 54 | var expected = ExpectedForTrend().ToList(); 55 | 56 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 57 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 58 | 59 | await enumerator.MoveNextAsync(); 60 | await enumerator.MoveNextAsync(); 61 | var actual = await dataPointIterator.ToArrayAsync(); 62 | 63 | CollectionAssert.AreEqual(expected, actual); 64 | } 65 | //--------------------------------------------------------------------- 66 | [Test] 67 | public async Task Cancellation___OK() 68 | { 69 | var sut = new DeadBandCompression(0.1); 70 | var data = RawDataForTrendAsync(); 71 | var expected = ExpectedForTrend().Take(2).ToList(); 72 | 73 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 74 | var cts = new CancellationTokenSource(); 75 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 76 | 77 | var actual = new List(); 78 | await enumerator.MoveNextAsync(); 79 | actual.Add(enumerator.Current); 80 | await enumerator.MoveNextAsync(); 81 | actual.Add(enumerator.Current); 82 | cts.Cancel(); 83 | 84 | DataPoint[] res = null; 85 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToArrayAsync(cts.Token)); 86 | 87 | CollectionAssert.AreEqual(expected, actual); 88 | Assert.IsNull(res); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DeadBandCompressionTests/ToListAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.DeadBandCompressionTests 11 | { 12 | public class ToListAsync : Base 13 | { 14 | [Test] 15 | public async Task Data_given_as_IAsyncEnumerable___OK() 16 | { 17 | var sut = new DeadBandCompression(0.1); 18 | var data = RawDataForTrendAsync(); 19 | var expected = ExpectedForTrend().ToList(); 20 | 21 | var actual = await sut.ProcessAsync(data).ToListAsync(); 22 | 23 | CollectionAssert.AreEqual(expected, actual); 24 | } 25 | //--------------------------------------------------------------------- 26 | [Test] 27 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 28 | { 29 | var sut = new DeadBandCompression(0.1, 4d); 30 | var data = RawDataForMaxDeltaAsync(); 31 | var expected = ExpectedForMaxDelta().ToList(); 32 | 33 | var actual = await sut.ProcessAsync(data).ToListAsync(); 34 | 35 | CollectionAssert.AreEqual(expected, actual); 36 | } 37 | //--------------------------------------------------------------------- 38 | [Test] 39 | public async Task IEnumerable_iterated_and_ToList___OK() 40 | { 41 | var sut = new DeadBandCompression(0.1); 42 | var data = RawDataForTrendAsync(); 43 | var expected = ExpectedForTrend().ToList(); 44 | 45 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 46 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 47 | 48 | await enumerator.MoveNextAsync(); 49 | await enumerator.MoveNextAsync(); 50 | var actual = await dataPointIterator.ToListAsync(); 51 | 52 | CollectionAssert.AreEqual(expected, actual); 53 | } 54 | //--------------------------------------------------------------------- 55 | [Test] 56 | public async Task Cancellation___OK() 57 | { 58 | var sut = new DeadBandCompression(0.1); 59 | var data = RawDataForTrendAsync(); 60 | var expected = ExpectedForTrend().Take(2).ToList(); 61 | 62 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 63 | var cts = new CancellationTokenSource(); 64 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 65 | 66 | var actual = new List(); 67 | await enumerator.MoveNextAsync(); 68 | actual.Add(enumerator.Current); 69 | await enumerator.MoveNextAsync(); 70 | actual.Add(enumerator.Current); 71 | cts.Cancel(); 72 | 73 | List res = null; 74 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToListAsync(cts.Token)); 75 | 76 | CollectionAssert.AreEqual(expected, actual); 77 | Assert.IsNull(res); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/DisposeTests.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.Compression; 8 | 9 | [TestFixture(typeof(DeadBandCompression))] 10 | [TestFixture(typeof(NoCompression))] 11 | [TestFixture(typeof(SwingingDoorCompression))] 12 | public class DisposeTests : Base where TCompression : ICompression, new() 13 | { 14 | private ICompression _sut = new TCompression(); 15 | //------------------------------------------------------------------------- 16 | [Test] 17 | public void Enumerable_Dispose_after_foreach___OK() 18 | { 19 | using DataPointIterator filtered = _sut.Process(KnownSequence().Take(1)); 20 | _ = Consume(filtered); 21 | } 22 | //------------------------------------------------------------------------- 23 | [Test] 24 | public void Array_Dispose_after_foreach___OK() 25 | { 26 | using DataPointIterator filtered = _sut.Process(KnownSequence().Take(1).ToArray()); 27 | _ = Consume(filtered); 28 | } 29 | //------------------------------------------------------------------------- 30 | [Test] 31 | public void List_Dispose_after_foreach___OK() 32 | { 33 | using DataPointIterator filtered = _sut.Process(KnownSequence().Take(1).ToList()); 34 | _ = Consume(filtered); 35 | } 36 | //------------------------------------------------------------------------- 37 | private static double Consume(DataPointIterator dataPointIterator) 38 | { 39 | double sum = 0; 40 | 41 | foreach (DataPoint dataPoint in dataPointIterator) 42 | { 43 | sum += dataPoint.Y; 44 | } 45 | 46 | return sum; 47 | } 48 | //------------------------------------------------------------------------- 49 | #if NETCOREAPP 50 | [Test] 51 | public async Task AsyncEnumerable_Dispose_after_foreach___OK() 52 | { 53 | using DataPointIterator filtered = _sut.ProcessAsync(KnownSequenceAsync()); 54 | _ = await ConsumeAsync(filtered); 55 | } 56 | //------------------------------------------------------------------------- 57 | private static async ValueTask ConsumeAsync(DataPointIterator dataPointIterator) 58 | { 59 | double sum = 0; 60 | 61 | await foreach (DataPoint dataPoint in dataPointIterator) 62 | { 63 | sum += dataPoint.Y; 64 | } 65 | 66 | return sum; 67 | } 68 | #endif 69 | } 70 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/Base.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | 5 | #if NETCOREAPP 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | #endif 10 | 11 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 12 | { 13 | public abstract class Base : Compression.Base 14 | { 15 | protected static IEnumerable RawDataForTrend() => s_ser.Read("data/dead-band/trend_raw.csv"); 16 | protected static IEnumerable RawDataForMaxDelta() => s_ser.Read("data/dead-band/maxDelta_raw.csv"); 17 | //--------------------------------------------------------------------- 18 | #if NETCOREAPP 19 | protected static async IAsyncEnumerable RawDataForTrendAsync([EnumeratorCancellation] CancellationToken ct = default) 20 | { 21 | foreach (DataPoint dp in RawDataForTrend()) 22 | { 23 | ct.ThrowIfCancellationRequested(); 24 | await Task.Yield(); 25 | yield return dp; 26 | } 27 | } 28 | //--------------------------------------------------------------------- 29 | protected static async IAsyncEnumerable RawDataForMaxDeltaAsync([EnumeratorCancellation] CancellationToken ct = default) 30 | { 31 | foreach (DataPoint dp in RawDataForMaxDelta()) 32 | { 33 | ct.ThrowIfCancellationRequested(); 34 | await Task.Yield(); 35 | yield return dp; 36 | } 37 | } 38 | #endif 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/Clone.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 7 | { 8 | public class Clone : Base 9 | { 10 | [Test] 11 | public void Cloning_iterates_over_fresh_set() 12 | { 13 | var sut = new NoCompression(); 14 | var data = KnownSequence().ToArray().Select(dp => dp); 15 | var expected = KnownSequence().ToArray(); 16 | var filter = sut.Process(data); 17 | 18 | var iterator = filter.GetEnumerator(); 19 | iterator.MoveNext(); 20 | 21 | iterator = filter.Clone().GetEnumerator(); 22 | 23 | Assert.Multiple(() => 24 | { 25 | int step = 0; 26 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 27 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 28 | step++; 29 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 30 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 31 | step++; 32 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 33 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 34 | step++; 35 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 36 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 37 | step++; 38 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 39 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 40 | step++; 41 | Assert.IsFalse(iterator.MoveNext(), $"MoveNext step: {step++}"); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/MoveNextAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 9 | { 10 | public class MoveNextAsync : Base 11 | { 12 | [Test] 13 | public async Task Empty_IAsyncEnumerable___empty_result() 14 | { 15 | var sut = new NoCompression(); 16 | var data = EmptyAsync(); 17 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 18 | 19 | Assert.IsFalse(await iterator.MoveNextAsync()); 20 | } 21 | //--------------------------------------------------------------------- 22 | [Test] 23 | public async Task Empty_IAsyncEnumerable_foreach___empty_result() 24 | { 25 | var sut = new NoCompression(); 26 | var data = EmptyAsync(); 27 | 28 | int count = 0; 29 | await foreach (DataPoint db in sut.ProcessAsync(data)) 30 | { 31 | count++; 32 | } 33 | 34 | Assert.AreEqual(0, count); 35 | } 36 | //--------------------------------------------------------------------- 37 | [Test] 38 | public async Task Known_sequence___correct_result() 39 | { 40 | var sut = new NoCompression(); 41 | var data = KnownSequenceAsync(); 42 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 43 | var expected = new List(); 44 | 45 | await foreach (DataPoint dp in KnownSequenceAsync()) 46 | expected.Add(dp); 47 | 48 | Assert.Multiple(async () => 49 | { 50 | await iterator.MoveNextAsync(); 51 | Assert.AreEqual(expected[0], iterator.Current); 52 | await iterator.MoveNextAsync(); 53 | Assert.AreEqual(expected[1], iterator.Current); 54 | await iterator.MoveNextAsync(); 55 | Assert.AreEqual(expected[2], iterator.Current); 56 | }); 57 | } 58 | //--------------------------------------------------------------------- 59 | [Test] 60 | public async Task Known_sequence_foreach___correct_result() 61 | { 62 | var sut = new NoCompression(); 63 | var data = KnownSequenceAsync(); 64 | var result = sut.ProcessAsync(data); 65 | var expected = new List(); 66 | var actual = new List(); 67 | 68 | await foreach (DataPoint dp in KnownSequenceAsync()) 69 | expected.Add(dp); 70 | 71 | await foreach (DataPoint dp in result) 72 | actual.Add(dp); 73 | 74 | CollectionAssert.AreEqual(expected, actual); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/ProcessAsyncCore.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 11 | { 12 | public class ProcessAsyncCore : Base 13 | { 14 | [Test] 15 | public async Task Data_given_as_IAsyncEnumerable___OK() 16 | { 17 | var sut = new NoCompression(); 18 | var data = RawDataForTrendAsync(); 19 | var expected = RawDataForTrend().ToList(); 20 | 21 | var actual = new List(); 22 | await foreach (DataPoint dp in sut.ProcessAsync(data)) 23 | actual.Add(dp); 24 | 25 | CollectionAssert.AreEqual(expected, actual); 26 | } 27 | //--------------------------------------------------------------------- 28 | [Test] 29 | public void Cancellation_after_two_items___OK() 30 | { 31 | var sut = new NoCompression(); 32 | var data = RawDataForTrendAsync(); 33 | var expected = RawDataForTrend().Take(2).ToList(); 34 | var cts = new CancellationTokenSource(); 35 | 36 | var actual = new List(); 37 | int idx = 0; 38 | 39 | Assert.ThrowsAsync(async () => 40 | { 41 | await foreach (DataPoint dp in sut.ProcessAsync(data).WithCancellation(cts.Token)) 42 | { 43 | actual.Add(dp); 44 | idx++; 45 | 46 | if (idx == 2) cts.Cancel(); 47 | } 48 | }); 49 | 50 | CollectionAssert.AreEqual(actual, expected); 51 | } 52 | //--------------------------------------------------------------------- 53 | [Test] 54 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 55 | { 56 | var sut = new NoCompression(); 57 | var data = RawDataForMaxDeltaAsync(); 58 | var expected = RawDataForMaxDelta().ToList(); 59 | 60 | var actual = new List(); 61 | await foreach (DataPoint dp in sut.ProcessAsync(data)) 62 | actual.Add(dp); 63 | 64 | CollectionAssert.AreEqual(expected, actual); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/ToArrayAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 11 | { 12 | public class ToArrayAsync : Base 13 | { 14 | [Test] 15 | public async Task Data_given_as_IAsyncEnumerable___OK() 16 | { 17 | var sut = new NoCompression(); 18 | var data = RawDataForTrendAsync(); 19 | var expected = RawDataForTrend().ToList(); 20 | 21 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 22 | 23 | Print(expected, "expected"); 24 | Print(actual , "actual"); 25 | CollectionAssert.AreEqual(expected, actual); 26 | } 27 | //--------------------------------------------------------------------- 28 | [Test] 29 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 30 | { 31 | var sut = new NoCompression(); 32 | var data = RawDataForMaxDeltaAsync(); 33 | var expected = RawDataForMaxDelta().ToList(); 34 | 35 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 36 | 37 | Print(expected, "expected"); 38 | Print(actual , "actual"); 39 | CollectionAssert.AreEqual(expected, actual); 40 | } 41 | //--------------------------------------------------------------------- 42 | [Test] 43 | public async Task IAsyncEnumerable_iterated_and_ToArray___OK() 44 | { 45 | var sut = new NoCompression(); 46 | var data = RawDataForTrendAsync(); 47 | var expected = RawDataForTrend().ToList(); 48 | 49 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 50 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 51 | 52 | await enumerator.MoveNextAsync(); 53 | await enumerator.MoveNextAsync(); 54 | var actual = await dataPointIterator.ToArrayAsync(); 55 | 56 | Print(expected, "expected"); 57 | Print(actual , "actual"); 58 | CollectionAssert.AreEqual(expected, actual); 59 | } 60 | //--------------------------------------------------------------------- 61 | [Test] 62 | public async Task Cancellation___OK() 63 | { 64 | var sut = new NoCompression(); 65 | var data = RawDataForTrendAsync(); 66 | var expected = RawDataForTrend().Take(2).ToList(); 67 | 68 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 69 | var cts = new CancellationTokenSource(); 70 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 71 | 72 | var actual = new List(); 73 | await enumerator.MoveNextAsync(); 74 | actual.Add(enumerator.Current); 75 | await enumerator.MoveNextAsync(); 76 | actual.Add(enumerator.Current); 77 | cts.Cancel(); 78 | 79 | DataPoint[] res = null; 80 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToArrayAsync(cts.Token)); 81 | 82 | CollectionAssert.AreEqual(expected, actual); 83 | Assert.IsNull(res); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/NoCompressionTests/ToListAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.NoCompressionTests 11 | { 12 | public class ToListAsync : Base 13 | { 14 | [Test] 15 | public async Task Data_given_as_IAsyncEnumerable___OK() 16 | { 17 | var sut = new NoCompression(); 18 | var data = RawDataForTrendAsync(); 19 | var expected = RawDataForTrend().ToList(); 20 | 21 | var actual = await sut.ProcessAsync(data).ToListAsync(); 22 | 23 | CollectionAssert.AreEqual(expected, actual); 24 | } 25 | //--------------------------------------------------------------------- 26 | [Test] 27 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 28 | { 29 | var sut = new NoCompression(); 30 | var data = RawDataForMaxDeltaAsync(); 31 | var expected = RawDataForMaxDelta().ToList(); 32 | 33 | var actual = await sut.ProcessAsync(data).ToListAsync(); 34 | 35 | CollectionAssert.AreEqual(expected, actual); 36 | } 37 | //--------------------------------------------------------------------- 38 | [Test] 39 | public async Task IEnumerable_iterated_and_ToList___OK() 40 | { 41 | var sut = new NoCompression(); 42 | var data = RawDataForTrendAsync(); 43 | var expected = RawDataForTrend().ToList(); 44 | 45 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 46 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 47 | 48 | await enumerator.MoveNextAsync(); 49 | await enumerator.MoveNextAsync(); 50 | var actual = await dataPointIterator.ToListAsync(); 51 | 52 | CollectionAssert.AreEqual(expected, actual); 53 | } 54 | //--------------------------------------------------------------------- 55 | [Test] 56 | public async Task Cancellation___OK() 57 | { 58 | var sut = new NoCompression(); 59 | var data = RawDataForTrendAsync(); 60 | var expected = RawDataForTrend().Take(2).ToList(); 61 | 62 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 63 | var cts = new CancellationTokenSource(); 64 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 65 | 66 | var actual = new List(); 67 | await enumerator.MoveNextAsync(); 68 | actual.Add(enumerator.Current); 69 | await enumerator.MoveNextAsync(); 70 | actual.Add(enumerator.Current); 71 | cts.Cancel(); 72 | 73 | List res = null; 74 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToListAsync(cts.Token)); 75 | 76 | CollectionAssert.AreEqual(expected, actual); 77 | Assert.IsNull(res); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/SwingingDoorCompressionTests/Clone.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.Compression.SwingingDoorCompressionTests 7 | { 8 | public class Clone : Base 9 | { 10 | [Test] 11 | public void Cloning_iterates_over_fresh_set() 12 | { 13 | var sut = new SwingingDoorCompression(1); 14 | var data = KnownSequence().ToArray().Select(dp => dp); 15 | var expected = KnownSequenceExpected().ToArray(); 16 | var filter = sut.Process(data); 17 | 18 | var iterator = filter.GetEnumerator(); 19 | iterator.MoveNext(); 20 | 21 | iterator = filter.Clone().GetEnumerator(); 22 | 23 | Assert.Multiple(() => 24 | { 25 | int step = 0; 26 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 27 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 28 | step++; 29 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 30 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 31 | step++; 32 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 33 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 34 | step++; 35 | Assert.IsTrue(iterator.MoveNext(), $"MoveNext step: {step}"); 36 | Assert.AreEqual(expected[step], iterator.Current, $"Equal step: {step}"); 37 | step++; 38 | Assert.IsFalse(iterator.MoveNext(), $"MoveNext step: {step++}"); 39 | }); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/SwingingDoorCompressionTests/MoveNextAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Compression.SwingingDoorCompressionTests 9 | { 10 | public class MoveNextAsync : Base 11 | { 12 | [Test] 13 | public async Task Empty_IAsyncEnumerable___empty_result() 14 | { 15 | var sut = new SwingingDoorCompression(1); 16 | var data = EmptyAsync(); 17 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 18 | 19 | Assert.IsFalse(await iterator.MoveNextAsync()); 20 | } 21 | //--------------------------------------------------------------------- 22 | [Test] 23 | public async Task Empty_IAsyncEnumerable_foreach___empty_result() 24 | { 25 | var sut = new SwingingDoorCompression(1); 26 | var data = EmptyAsync(); 27 | 28 | int count = 0; 29 | await foreach (DataPoint db in sut.ProcessAsync(data)) 30 | { 31 | count++; 32 | } 33 | 34 | Assert.AreEqual(0, count); 35 | } 36 | //--------------------------------------------------------------------- 37 | [Test] 38 | public void Known_sequence___correct_result() 39 | { 40 | var sut = new SwingingDoorCompression(1); 41 | var data = KnownSequenceAsync(); 42 | var iterator = sut.ProcessAsync(data).GetAsyncEnumerator(); 43 | var expected = KnownSequenceExpected().ToArray(); 44 | 45 | Assert.Multiple(async () => 46 | { 47 | await iterator.MoveNextAsync(); 48 | Assert.AreEqual(expected[0], iterator.Current); 49 | await iterator.MoveNextAsync(); 50 | Assert.AreEqual(expected[1], iterator.Current); 51 | await iterator.MoveNextAsync(); 52 | Assert.AreEqual(expected[2], iterator.Current); 53 | await iterator.MoveNextAsync(); 54 | Assert.AreEqual(expected[3], iterator.Current); 55 | }); 56 | } 57 | //--------------------------------------------------------------------- 58 | [Test] 59 | public async Task Known_sequence_foreach___correct_result() 60 | { 61 | var sut = new SwingingDoorCompression(1); 62 | var data = KnownSequenceAsync(); 63 | var result = sut.ProcessAsync(data); 64 | var expected = KnownSequenceExpected().ToArray(); 65 | var actual = new List(); 66 | 67 | await foreach (DataPoint dp in result) 68 | actual.Add(dp); 69 | 70 | CollectionAssert.AreEqual(expected, actual); 71 | } 72 | //--------------------------------------------------------------------- 73 | [Test] 74 | public async Task CompressionDeviation_0___input_echoed() 75 | { 76 | var sut = new SwingingDoorCompression(0); 77 | var data = KnownSequenceAsync(); 78 | var result = sut.ProcessAsync(data); 79 | var expected = KnownSequence().ToArray(); 80 | var actual = new List(); 81 | 82 | await foreach (DataPoint dp in result) 83 | actual.Add(dp); 84 | 85 | CollectionAssert.AreEqual(expected, actual); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/SwingingDoorCompressionTests/ToArrayAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using gfoidl.DataCompression.Wrappers; 9 | using NUnit.Framework; 10 | 11 | namespace gfoidl.DataCompression.Tests.Compression.SwingingDoorCompressionTests 12 | { 13 | public class ToArrayAsync : Base 14 | { 15 | [Test] 16 | public async Task Empty_IAsyncEnumerable___empty_result() 17 | { 18 | var sut = new SwingingDoorCompression(1d); 19 | var data = EmptyAsync(); 20 | 21 | var actual = sut.ProcessAsync(data); 22 | 23 | Assert.AreEqual(0, (await actual.ToListAsync()).Count); 24 | } 25 | //--------------------------------------------------------------------- 26 | [Test, TestCaseSource(typeof(Base), nameof(Base.IAsyncEnumerableTestCases))] 27 | public async Task Data_given_as_IAsyncEnumerable___OK(double compressionDeviation, IAsyncEnumerable rawData, IEnumerable expectedData) 28 | { 29 | var sut = new SwingingDoorCompression(compressionDeviation); 30 | var data = rawData; 31 | var expected = expectedData.ToList(); 32 | 33 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 34 | 35 | CollectionAssert.AreEqual(expected, actual); 36 | } 37 | //--------------------------------------------------------------------- 38 | [Test] 39 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 40 | { 41 | var sut = new SwingingDoorCompression(1d, 6d); 42 | var data = RawDataAsync(RawDataForMaxDelta()); 43 | var expected = ExpectedForMaxDelta().ToList(); 44 | 45 | var actual = await sut.ProcessAsync(data).ToArrayAsync(); 46 | 47 | CollectionAssert.AreEqual(expected, actual); 48 | } 49 | //--------------------------------------------------------------------- 50 | [Test, TestCaseSource(typeof(Base), nameof(Base.IAsyncEnumerableTestCases))] 51 | public async Task IEnumerable_iterated_and_ToArray___OK(double compressionDeviation, IAsyncEnumerable rawData, IEnumerable expectedData) 52 | { 53 | var sut = new SwingingDoorCompression(compressionDeviation); 54 | var data = rawData; 55 | var expected = expectedData.ToList(); 56 | 57 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 58 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 59 | 60 | await enumerator.MoveNextAsync(); 61 | await enumerator.MoveNextAsync(); 62 | var actual = await dataPointIterator.ToArrayAsync(); 63 | 64 | CollectionAssert.AreEqual(expected, actual); 65 | } 66 | //--------------------------------------------------------------------- 67 | [Test] 68 | public async Task Cancellation___OK() 69 | { 70 | var sut = new SwingingDoorCompression(1d); 71 | var data = RawDataAsync(RawDataForTrend()); 72 | var expected = ExpectedForTrend().Take(2).ToList(); 73 | 74 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 75 | var cts = new CancellationTokenSource(); 76 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 77 | 78 | var actual = new List(); 79 | await enumerator.MoveNextAsync(); 80 | actual.Add(enumerator.Current); 81 | await enumerator.MoveNextAsync(); 82 | actual.Add(enumerator.Current); 83 | cts.Cancel(); 84 | 85 | DataPoint[] res = null; 86 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToArrayAsync(cts.Token)); 87 | 88 | CollectionAssert.AreEqual(expected, actual); 89 | Assert.IsNull(res); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Compression/SwingingDoorCompressionTests/ToListAsync.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace gfoidl.DataCompression.Tests.Compression.SwingingDoorCompressionTests 11 | { 12 | public class ToListAsync : Base 13 | { 14 | [Test, TestCaseSource(typeof(Base), nameof(Base.IAsyncEnumerableTestCases))] 15 | public async Task Data_given_as_IAsyncEnumerable___OK(double compressionDeviation, IAsyncEnumerable rawData, IEnumerable expectedData) 16 | { 17 | var sut = new SwingingDoorCompression(compressionDeviation); 18 | var data = rawData; 19 | var expected = expectedData.ToList(); 20 | 21 | var actual = await sut.ProcessAsync(data).ToListAsync(); 22 | 23 | CollectionAssert.AreEqual(expected, actual); 24 | } 25 | //--------------------------------------------------------------------- 26 | [Test] 27 | public async Task Data_IAsyncEnumerable_with_maxDeltaX___OK() 28 | { 29 | var sut = new SwingingDoorCompression(1d, 6d); 30 | var data = RawDataAsync(RawDataForMaxDelta()); 31 | var expected = ExpectedForMaxDelta().ToList(); 32 | 33 | var actual = await sut.ProcessAsync(data).ToListAsync(); 34 | 35 | CollectionAssert.AreEqual(expected, actual); 36 | } 37 | //--------------------------------------------------------------------- 38 | [Test, TestCaseSource(typeof(Base), nameof(Base.IAsyncEnumerableTestCases))] 39 | public async Task IEnumerable_iterated_and_ToList___OK(double compressionDeviation, IAsyncEnumerable rawData, IEnumerable expectedData) 40 | { 41 | var sut = new SwingingDoorCompression(compressionDeviation); 42 | var data = rawData; 43 | var expected = expectedData.ToList(); 44 | 45 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 46 | var enumerator = dataPointIterator.GetAsyncEnumerator(); 47 | 48 | await enumerator.MoveNextAsync(); 49 | await enumerator.MoveNextAsync(); 50 | var actual = await dataPointIterator.ToListAsync(); 51 | 52 | CollectionAssert.AreEqual(expected, actual); 53 | } 54 | //--------------------------------------------------------------------- 55 | [Test] 56 | public async Task Cancellation___OK() 57 | { 58 | var sut = new SwingingDoorCompression(1d); 59 | var data = RawDataAsync(RawDataForTrend()); 60 | var expected = ExpectedForTrend().Take(2).ToList(); 61 | 62 | DataPointIterator dataPointIterator = sut.ProcessAsync(data); 63 | var cts = new CancellationTokenSource(); 64 | var enumerator = dataPointIterator.GetAsyncEnumerator(cts.Token); 65 | 66 | var actual = new List(); 67 | await enumerator.MoveNextAsync(); 68 | actual.Add(enumerator.Current); 69 | await enumerator.MoveNextAsync(); 70 | actual.Add(enumerator.Current); 71 | cts.Cancel(); 72 | 73 | List res = null; 74 | Assert.ThrowsAsync(async () => res = await dataPointIterator.ToListAsync(cts.Token)); 75 | 76 | CollectionAssert.AreEqual(expected, actual); 77 | Assert.IsNull(res); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Constants.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | namespace gfoidl.DataCompression.Tests 4 | { 5 | internal static class Constants 6 | { 7 | public const double Epsilon = 1e-8; 8 | } 9 | } -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointIteratorTests/Empty.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Threading.Tasks; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.DataPointIteratorTests 8 | { 9 | [TestFixture] 10 | public class Empty 11 | { 12 | [Test] 13 | public void MoveNext___false() 14 | { 15 | DataPointIterator sut = DataPointIterator.Empty; 16 | 17 | Assert.IsFalse(sut.MoveNext()); 18 | Assert.AreSame(sut, sut.Clone()); 19 | Assert.AreSame(Array.Empty(), sut.ToArray()); 20 | Assert.AreEqual(0, sut.ToList().Count); 21 | } 22 | //--------------------------------------------------------------------- 23 | #if NETCOREAPP 24 | [Test] 25 | public async Task MoveNextAsync___false() 26 | { 27 | DataPointIterator sut = DataPointIterator.Empty; 28 | 29 | Assert.AreSame(Array.Empty(), await sut.ToArrayAsync()); 30 | Assert.AreEqual(0, (await sut.ToListAsync()).Count); 31 | } 32 | #endif 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointSerializerTests/Roundtrip.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.DataPointSerializerTests 9 | { 10 | [TestFixture] 11 | public class Roundtrip 12 | { 13 | private string _tmpFile; 14 | //--------------------------------------------------------------------- 15 | [SetUp] 16 | public void SetUp() => _tmpFile = Path.GetTempFileName(); 17 | //--------------------------------------------------------------------- 18 | [TearDown] 19 | public void TearDown() 20 | { 21 | try 22 | { 23 | File.Delete(_tmpFile); 24 | } 25 | catch { } 26 | } 27 | //--------------------------------------------------------------------- 28 | [Test] 29 | public void Write_Read_roundtrip___OK([Values(true, false)] bool writeHeader) 30 | { 31 | DataPoint[] dataPoints = 32 | { 33 | DataPoint.Origin, 34 | new DataPoint(1, 2), 35 | new DataPoint(2, -1) 36 | }; 37 | 38 | var sut = new DataPointSerializer(); 39 | string file = _tmpFile; 40 | 41 | if (writeHeader) 42 | { 43 | sut.Write(file, dataPoints, header: ("x", "y")); 44 | } 45 | else 46 | { 47 | sut.Write(file, dataPoints); 48 | } 49 | DataPoint[] actual = sut.Read(file, firstLineIsHeader: writeHeader).ToArray(); 50 | 51 | CollectionAssert.AreEqual(dataPoints, actual); 52 | } 53 | //--------------------------------------------------------------------- 54 | [Test] 55 | public void Write_Read_roundtrip_with_datetime___OK([Values(true, false)] bool writeHeader) 56 | { 57 | DateTime now = new DateTime(2019, 12, 16, 21, 57, 13); 58 | DateTime[] dts = 59 | { 60 | now.AddMinutes(-12), 61 | now 62 | }; 63 | 64 | DataPoint[] dataPoints = 65 | { 66 | new DataPoint(dts[0], 2), 67 | new DataPoint(dts[1], -1) 68 | }; 69 | 70 | var sut = new DataPointSerializer(); 71 | string file = _tmpFile; 72 | 73 | const string datetimeFormat = "yyyy-MM-dd:HHmmss"; 74 | 75 | if (writeHeader) 76 | { 77 | sut.Write(file, dataPoints, header: ("x", "y"), dateTimeFormat: datetimeFormat); 78 | } 79 | else 80 | { 81 | sut.Write(file, dataPoints, dateTimeFormat: datetimeFormat); 82 | } 83 | DataPoint[] actual = sut.Read(file, firstLineIsHeader: writeHeader, dateTimeFormat: datetimeFormat).ToArray(); 84 | 85 | Assert.Multiple(() => 86 | { 87 | Assert.AreEqual(2, actual.Length); 88 | 89 | Assert.AreEqual(dts[0], new DateTime((long)(actual[0].X))); 90 | Assert.AreEqual(dataPoints[0].Y, actual[0].Y, 1e-3); 91 | 92 | Assert.AreEqual(dts[1], new DateTime((long)(actual[1].X))); 93 | Assert.AreEqual(dataPoints[1].Y, actual[1].Y, 1e-3); 94 | }); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/CalculatePoint.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.DataPointTests 8 | { 9 | [TestFixture] 10 | public class CalculatePoint 11 | { 12 | [Test] 13 | [TestCase(double.MinValue)] 14 | [TestCase(double.MaxValue)] 15 | [TestCase(double.NegativeInfinity)] 16 | [TestCase(double.PositiveInfinity)] 17 | [TestCase(double.NaN)] 18 | [TestCase(0)] 19 | [TestCase(1)] 20 | [TestCase(Math.PI)] 21 | public void X_same_as_of_Point___same_point_returned(double gradient) 22 | { 23 | DataPoint a = (42, 42); 24 | 25 | double actual = a.CalculatePoint(gradient, a.X); 26 | 27 | Assert.AreEqual(a.Y, actual); 28 | } 29 | //--------------------------------------------------------------------- 30 | [Test] 31 | [TestCaseSource(nameof(Point_gradient_x_given___correct_Point_returned_TestCases))] 32 | public void Point_gradient_x_given___correct_Point_returned(double gradient, double x, (double X, double Y) expected) 33 | { 34 | DataPoint a = (1d, 1d); 35 | 36 | double actual = a.CalculatePoint(gradient, x); 37 | 38 | Assert.AreEqual(expected.Y, actual, 1e-6, "y"); 39 | } 40 | //--------------------------------------------------------------------- 41 | private static IEnumerable Point_gradient_x_given___correct_Point_returned_TestCases() 42 | { 43 | yield return new TestCaseData( 1.5, 2d, (2d, 2.5)); 44 | yield return new TestCaseData( 1.5, 0d, (0d, -0.5)); 45 | yield return new TestCaseData(-1.5, 2d, (2d, -0.5)); 46 | yield return new TestCaseData(-1.5, 0d, (0d, 2.5)); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/Ctor.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.DataPointTests 7 | { 8 | [TestFixture] 9 | public class Ctor 10 | { 11 | [Test] 12 | public void X_and_Y_given___correct_property_values( 13 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double x, 14 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double y) 15 | { 16 | var actual = new DataPoint(x, y); 17 | 18 | Assert.AreEqual(x, actual.X, Constants.Epsilon); 19 | Assert.AreEqual(y, actual.Y, Constants.Epsilon); 20 | } 21 | //--------------------------------------------------------------------- 22 | [Test] 23 | public void Tuple_given___correct_property_values( 24 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double x, 25 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double y) 26 | { 27 | (double x, double y) tuple = (x, y); 28 | 29 | var actual = new DataPoint(tuple); 30 | 31 | Assert.AreEqual(x, actual.X, Constants.Epsilon); 32 | Assert.AreEqual(y, actual.Y, Constants.Epsilon); 33 | } 34 | //--------------------------------------------------------------------- 35 | [Test] 36 | public void Tuple_with_DateTime_implicit___correct_property_values() 37 | { 38 | (DateTime Now, double Y) tuple = (DateTime.Now, 1.23); 39 | 40 | DataPoint actual = tuple; 41 | 42 | Assert.AreEqual(tuple.Now.Ticks, actual.X); 43 | Assert.AreEqual(tuple.Y, actual.Y); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/Equals.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using NUnit.Framework; 4 | 5 | namespace gfoidl.DataCompression.Tests.DataPointTests 6 | { 7 | [TestFixture] 8 | public class Equals 9 | { 10 | [Test] 11 | public void Type_other_than_DataPoint_given___false() 12 | { 13 | var sut = new DataPoint(); 14 | object o = new object(); 15 | 16 | bool actual = sut.Equals(o); 17 | 18 | Assert.IsFalse(actual); 19 | } 20 | //--------------------------------------------------------------------- 21 | [Test] 22 | public void Same_other_given___true( 23 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double x, 24 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double y) 25 | { 26 | var sut = new DataPoint(x, y); 27 | var other = new DataPoint(x, y); 28 | 29 | bool actual = sut.Equals(other); 30 | Assert.IsTrue(actual, "Equals"); 31 | 32 | actual = sut == other; 33 | Assert.IsTrue(actual, "=="); 34 | } 35 | //--------------------------------------------------------------------- 36 | [Test] 37 | public void Same_other_as_object_given___true( 38 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double x, 39 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double y) 40 | { 41 | var sut = new DataPoint(x, y); 42 | object other = new DataPoint(x, y); 43 | 44 | bool actual = sut.Equals(other); 45 | 46 | Assert.IsTrue(actual); 47 | } 48 | //--------------------------------------------------------------------- 49 | [Test] 50 | public void Different_other_given___true() 51 | { 52 | var sut = new DataPoint(0, 0); 53 | var other = new DataPoint(1e-150, 1e-150); 54 | 55 | bool actual = sut.Equals(other); 56 | Assert.IsFalse(actual, "Equals"); 57 | 58 | actual = sut == other; 59 | Assert.IsFalse(actual, "=="); 60 | } 61 | //--------------------------------------------------------------------- 62 | [Test] 63 | public void Different_other_as_object_given___true() 64 | { 65 | var sut = new DataPoint(0, 0); 66 | var other = new DataPoint(1e-150, 1e-150); 67 | 68 | bool actual = sut.Equals(other); 69 | 70 | Assert.IsFalse(actual); 71 | } 72 | //--------------------------------------------------------------------- 73 | [Test] 74 | public void Test_with_allowedDelta() 75 | { 76 | var sut = new DataPoint(3, 3); 77 | var other = new DataPoint(3.1, 2.9); 78 | 79 | bool actual = sut.Equals(other, 0.1001); 80 | 81 | Assert.IsTrue(actual); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/GetHashCode.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using NUnit.Framework; 4 | 5 | namespace gfoidl.DataCompression.Tests.DataPointTests 6 | { 7 | [TestFixture] 8 | public class GetHashCode 9 | { 10 | /* 11 | * Rule: 12 | * Equal object must have the same hash code! 13 | * Nothing more, nothing less. 14 | */ 15 | [Test] 16 | public void Two_equal_DataPoints___same_hash_code( 17 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double x, 18 | [Values(double.MinValue, double.MinValue + 1, 0, double.MaxValue - 1, double.MaxValue)]double y) 19 | { 20 | var sut = new DataPoint(x, y); 21 | var other = new DataPoint(x, y); 22 | 23 | int h1 = sut.GetHashCode(); 24 | int h2 = sut.GetHashCode(); 25 | 26 | Assert.AreEqual(h1, h2); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/Gradient.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.DataPointTests 8 | { 9 | [TestFixture] 10 | public class Gradient 11 | { 12 | [Test] 13 | public void Gradient_to_same_point___throws_ArgumentException() 14 | { 15 | var a = new DataPoint(1, 1); 16 | var b = a; 17 | 18 | Assert.Throws(() => a.Gradient(b, false)); 19 | } 20 | //--------------------------------------------------------------------- 21 | [Test] 22 | [TestCaseSource(nameof(A_and_B_given___OK_TestCases))] 23 | public double A_and_B_given___OK((double, double) ta, (double, double) tb) 24 | { 25 | DataPoint a = ta; 26 | DataPoint b = tb; 27 | 28 | return a.Gradient(b); 29 | } 30 | //--------------------------------------------------------------------- 31 | private static IEnumerable A_and_B_given___OK_TestCases() 32 | { 33 | yield return new TestCaseData((0d, 0d), ( 0d, 0d)).Returns(0d); 34 | yield return new TestCaseData((0d, 0d), ( 1d, 1d)).Returns(1d); 35 | yield return new TestCaseData((0d, 0d), ( 1d, -1d)).Returns(-1d); 36 | yield return new TestCaseData((0d, 0d), ( 1d, 0d)).Returns(0d); 37 | yield return new TestCaseData((0d, 0d), (-1d, 0d)).Returns(0d); 38 | yield return new TestCaseData((0d, 0d), ( 0d, 1d)).Returns(double.PositiveInfinity); 39 | yield return new TestCaseData((0d, 0d), ( 0d, -1d)).Returns(double.NegativeInfinity); 40 | yield return new TestCaseData((0d, 0d), ( 4d, 3d)).Returns(3d / 4d); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/DataPointTests/ToTimeValue.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.DataPointTests 7 | { 8 | [TestFixture] 9 | public class ToTimeValue 10 | { 11 | [Test] 12 | public void DateTime_and_value_given___OK() 13 | { 14 | var dt = new DateTime(1982, 7, 22, 23, 35, 40); 15 | double value = Math.PI; 16 | 17 | var sut = new DataPoint(dt, value); 18 | 19 | var actual = sut.ToTimeValue(); 20 | 21 | Assert.AreEqual(dt, actual.Time); 22 | Assert.AreEqual(value, actual.Value); 23 | } 24 | //--------------------------------------------------------------------- 25 | [Test] 26 | public void Implicit_conversion___OK() 27 | { 28 | var dt = new DateTime(1982, 7, 22, 23, 35, 40); 29 | double value = Math.PI; 30 | 31 | var sut = new DataPoint(dt, value); 32 | 33 | (DateTime Time, double Value) actual = sut; 34 | 35 | Assert.AreEqual(dt, actual.Time); 36 | Assert.AreEqual(value, actual.Value); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/ExtensionMethodsTests/Base.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.ExtensionMethodsTests 8 | { 9 | [TestFixture] 10 | public abstract class Base 11 | { 12 | protected static IEnumerable GetDataPoints() 13 | { 14 | yield return new DataPoint(0, 0); 15 | yield return new DataPoint(1, 1); 16 | } 17 | //--------------------------------------------------------------------- 18 | #if NETCOREAPP 19 | protected static async IAsyncEnumerable GetDataPointsAsync() 20 | { 21 | foreach (DataPoint dp in GetDataPoints()) 22 | { 23 | await Task.Yield(); 24 | yield return dp; 25 | } 26 | } 27 | #endif 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/ExtensionMethodsTests/DeadBandCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.ExtensionMethodsTests 8 | { 9 | public class DeadBandCompression : Base 10 | { 11 | [Test] 12 | public void Data_is_null___throws_ArgumentNull() 13 | { 14 | IEnumerable data = null; 15 | 16 | Assert.Throws(() => data.DeadBandCompression(0.1)); 17 | } 18 | //--------------------------------------------------------------------- 19 | [Test] 20 | public void DataPoints_given___OK() 21 | { 22 | IEnumerable dataPoints = GetDataPoints(); 23 | 24 | DataPointIterator actual = dataPoints.DeadBandCompression(0.1); 25 | 26 | Assert.IsNotNull(actual); 27 | } 28 | //--------------------------------------------------------------------- 29 | [Test] 30 | public void DataPoints_given_with_TimeSpan___OK() 31 | { 32 | IEnumerable dataPoints = GetDataPoints(); 33 | 34 | DataPointIterator actual = dataPoints.DeadBandCompression(0.1, TimeSpan.FromSeconds(1)); 35 | 36 | Assert.IsNotNull(actual); 37 | } 38 | //--------------------------------------------------------------------- 39 | #if NETCOREAPP 40 | [Test] 41 | public void DataPoints_given_async___OK() 42 | { 43 | IAsyncEnumerable dataPoints = GetDataPointsAsync(); 44 | 45 | DataPointIterator actual = dataPoints.DeadBandCompressionAsync(0.1); 46 | 47 | Assert.IsNotNull(actual); 48 | } 49 | //--------------------------------------------------------------------- 50 | [Test] 51 | public void DataPoints_given_with_TimeSpan_async___OK() 52 | { 53 | IAsyncEnumerable dataPoints = GetDataPointsAsync(); 54 | 55 | DataPointIterator actual = dataPoints.DeadBandCompressionAsync(0.1, TimeSpan.FromSeconds(1)); 56 | 57 | Assert.IsNotNull(actual); 58 | } 59 | #endif 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/ExtensionMethodsTests/NoCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.ExtensionMethodsTests 9 | { 10 | public class NoCompression : Base 11 | { 12 | [Test] 13 | public void Data_is_null___throws_ArgumentNull() 14 | { 15 | IEnumerable data = null; 16 | 17 | Assert.Throws(() => data.NoCompression()); 18 | } 19 | //--------------------------------------------------------------------- 20 | [Test] 21 | public void DataPoints_given___OK() 22 | { 23 | IEnumerable dataPoints = GetDataPoints(); 24 | 25 | DataPointIterator actual = dataPoints.NoCompression(); 26 | 27 | Assert.IsNotNull(actual); 28 | } 29 | //--------------------------------------------------------------------- 30 | #if NETCOREAPP 31 | [Test] 32 | public void DataPoints_given_async___OK() 33 | { 34 | IAsyncEnumerable dataPoints = GetDataPointsAsync(); 35 | 36 | DataPointIterator actual = dataPoints.NoCompressionAsync(); 37 | 38 | Assert.IsNotNull(actual); 39 | } 40 | #endif 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/ExtensionMethodsTests/SwingingDoorCompression.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.ExtensionMethodsTests 8 | { 9 | public class SwingingDoorCompression : Base 10 | { 11 | [Test] 12 | public void Data_is_null___throws_ArgumentNull() 13 | { 14 | IEnumerable data = null; 15 | 16 | Assert.Throws(() => data.SwingingDoorCompression(0.1)); 17 | } 18 | //--------------------------------------------------------------------- 19 | [Test] 20 | public void DataPoints_given___OK() 21 | { 22 | IEnumerable dataPoints = GetDataPoints(); 23 | 24 | DataPointIterator actual = dataPoints.SwingingDoorCompression(0.1); 25 | 26 | Assert.IsNotNull(actual); 27 | } 28 | //--------------------------------------------------------------------- 29 | [Test] 30 | public void DataPoints_given_with_TimeSpan___OK() 31 | { 32 | IEnumerable dataPoints = GetDataPoints(); 33 | 34 | DataPointIterator actual = dataPoints.SwingingDoorCompression(0.1, TimeSpan.FromSeconds(1), minTime: null); 35 | 36 | Assert.IsNotNull(actual); 37 | } 38 | //--------------------------------------------------------------------- 39 | #if NETCOREAPP 40 | [Test] 41 | public void DataPoints_given_async___OK() 42 | { 43 | IAsyncEnumerable dataPoints = GetDataPointsAsync(); 44 | 45 | DataPointIterator actual = dataPoints.SwingingDoorCompressionAsync(0.1); 46 | 47 | Assert.IsNotNull(actual); 48 | } 49 | //--------------------------------------------------------------------- 50 | [Test] 51 | public void DataPoints_given_with_TimeSpan_async___OK() 52 | { 53 | IAsyncEnumerable dataPoints = GetDataPointsAsync(); 54 | 55 | DataPointIterator actual = dataPoints.SwingingDoorCompressionAsync(0.1, TimeSpan.FromSeconds(1), minTime: null); 56 | 57 | Assert.IsNotNull(actual); 58 | } 59 | #endif 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/MySetUpClass.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | #if !NETCOREAPP 4 | 5 | using System; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests 9 | { 10 | [SetUpFixture] 11 | public class MySetUpClass 12 | { 13 | [OneTimeSetUp] 14 | public void OneTimeSetUp() 15 | { 16 | Environment.CurrentDirectory = TestContext.CurrentContext.TestDirectory; 17 | TestContext.Progress.WriteLine($"Directory set to {Environment.CurrentDirectory}"); 18 | } 19 | } 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ArrayWrapperTests/Ctor.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using gfoidl.DataCompression.Wrappers; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.Wrappers.ArrayWrapperTests 8 | { 9 | [TestFixture] 10 | public class Ctor 11 | { 12 | [Test] 13 | public void List_is_null___throws_ArgumentNull() 14 | { 15 | Assert.Throws(() => new ArrayWrapper(null)); 16 | } 17 | //--------------------------------------------------------------------- 18 | [Test] 19 | public void List_is_not_null___OK() 20 | { 21 | int[] array = { 0, 1, 2 }; 22 | 23 | var actual = new ArrayWrapper(array); 24 | 25 | Assert.IsNotNull(actual); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ArrayWrapperTests/Indexer.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using gfoidl.DataCompression.Wrappers; 4 | using NUnit.Framework; 5 | 6 | namespace gfoidl.DataCompression.Tests.Wrappers.ArrayWrapperTests 7 | { 8 | [TestFixture] 9 | public class Indexer 10 | { 11 | [Test] 12 | public void Get___OK() 13 | { 14 | int[] array = { 0, 1, 2 }; 15 | var sut = new ArrayWrapper(array); 16 | 17 | int actual = sut[1]; 18 | 19 | Assert.AreEqual(array[1], actual); 20 | } 21 | //--------------------------------------------------------------------- 22 | [Test] 23 | public void Set___OK() 24 | { 25 | int[] array = { 0, 1, 2 }; 26 | var sut = new ArrayWrapper(array); 27 | 28 | sut[2] = 42; 29 | 30 | Assert.AreEqual(42, array[2]); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ArrayWrapperTests/NotImplementedMembers.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections; 5 | using gfoidl.DataCompression.Wrappers; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Wrappers.ArrayWrapperTests 9 | { 10 | [TestFixture] 11 | public class NotImplementedMembers 12 | { 13 | [Test] 14 | public void Not_implemented_members___throws_NotSupported() 15 | { 16 | int[] array = { 0, 1, 2 }; 17 | var sut = new ArrayWrapper(array); 18 | 19 | Assert.Throws(() => Assert.IsFalse(sut.IsReadOnly)); 20 | Assert.Throws(() => sut.Add(42)); 21 | Assert.Throws(() => sut.Clear()); 22 | Assert.Throws(() => sut.Contains(1)); 23 | Assert.Throws(() => sut.CopyTo(new int[3], 0)); 24 | Assert.Throws(() => sut.GetEnumerator()); 25 | Assert.Throws(() => sut.IndexOf(1)); 26 | Assert.Throws(() => sut.Insert(0, 0)); 27 | Assert.Throws(() => sut.Remove(1)); 28 | Assert.Throws(() => sut.RemoveAt(0)); 29 | Assert.Throws(() => (sut as IEnumerable)?.GetEnumerator()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ListWrapperTests/Ctor.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using gfoidl.DataCompression.Wrappers; 6 | using NUnit.Framework; 7 | 8 | namespace gfoidl.DataCompression.Tests.Wrappers.ListWrapperTests 9 | { 10 | [TestFixture] 11 | public class Ctor 12 | { 13 | [Test] 14 | public void List_is_null___throws_ArgumentNull() 15 | { 16 | Assert.Throws(() => new ListWrapper(null)); 17 | } 18 | //--------------------------------------------------------------------- 19 | [Test] 20 | public void List_is_not_null___OK() 21 | { 22 | var list = new List { 0, 1, 2 }; 23 | 24 | var actual = new ListWrapper(list); 25 | 26 | Assert.IsNotNull(actual); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ListWrapperTests/Indexer.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System.Collections.Generic; 4 | using gfoidl.DataCompression.Wrappers; 5 | using NUnit.Framework; 6 | 7 | namespace gfoidl.DataCompression.Tests.Wrappers.ListWrapperTests 8 | { 9 | [TestFixture] 10 | public class Indexer 11 | { 12 | [Test] 13 | public void Get___OK() 14 | { 15 | var list = new List { 0, 1, 2 }; 16 | var sut = new ListWrapper(list); 17 | 18 | int actual = sut[1]; 19 | 20 | Assert.AreEqual(list[1], actual); 21 | } 22 | //--------------------------------------------------------------------- 23 | [Test] 24 | public void Set___OK() 25 | { 26 | var list = new List { 0, 1, 2 }; 27 | var sut = new ListWrapper(list); 28 | 29 | sut[2] = 42; 30 | 31 | Assert.AreEqual(42, list[2]); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/Wrappers/ListWrapperTests/NotImplementedMembers.cs: -------------------------------------------------------------------------------- 1 | // (c) gfoidl, all rights reserved 2 | 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using gfoidl.DataCompression.Wrappers; 7 | using NUnit.Framework; 8 | 9 | namespace gfoidl.DataCompression.Tests.Wrappers.ListWrapperTests 10 | { 11 | [TestFixture] 12 | public class NotImplementedMembers 13 | { 14 | [Test] 15 | public void Not_implemented_members___throws_NotSupported() 16 | { 17 | var list = new List { 0, 1, 2 }; 18 | var sut = new ListWrapper(list); 19 | 20 | Assert.Throws(() => Assert.IsFalse(sut.IsReadOnly)); 21 | Assert.Throws(() => sut.Add(42)); 22 | Assert.Throws(() => sut.Clear()); 23 | Assert.Throws(() => sut.Contains(1)); 24 | Assert.Throws(() => sut.CopyTo(new int[3], 0)); 25 | Assert.Throws(() => sut.GetEnumerator()); 26 | Assert.Throws(() => sut.IndexOf(1)); 27 | Assert.Throws(() => sut.Insert(0, 0)); 28 | Assert.Throws(() => sut.Remove(1)); 29 | Assert.Throws(() => sut.RemoveAt(0)); 30 | Assert.Throws(() => (sut as IEnumerable)?.GetEnumerator()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/gfoidl.DataCompression.Tests/gfoidl.DataCompression.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(StandardTestTfms) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------