├── .github
├── FUNDING.yml
└── workflows
│ └── dart.yml
├── analysis_options.yaml
├── .gitignore
├── run_coverage.sh
├── pubspec.yaml
├── dart_test.yaml
├── statistics.iml
├── lib
├── statistics.dart
└── src
│ ├── statistics_equality_simd.dart
│ ├── statistics_equality.dart
│ ├── statistics_benchmark.dart
│ ├── statistics_extension_simd.dart
│ ├── statistics_prime.dart
│ ├── statistics_combination.dart
│ ├── statistics_tools_csv.dart
│ ├── statistics_metric.dart
│ ├── statistics_tools.dart
│ └── statistics_forecast.dart
├── example
├── statistics_example.dart
├── dynamic_int_benchmark_example.dart
├── bayesnet_example.dart
└── bayesnet_dependency_example.dart
├── test
├── statistics_equality_test.dart
├── statistics_benchmark_test.dart
├── statistics_tools_csv_test.dart
├── statistics_extension_simd_test.dart
├── statistics_forecast_even_xor_test.dart
├── statistics_metric_test.dart
├── statistics_forecast_even_product_test.dart
├── statistics_prime_test.dart
├── statistics_combination_test.dart
└── statistics_tools_test.dart
├── CHANGELOG.md
├── LICENSE
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [gmpassos]
2 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 |
2 | include: package:lints/recommended.yaml
3 |
4 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
5 |
6 | # Uncomment to specify additional rules.
7 | # linter:
8 | # rules:
9 | # - camel_case_types
10 |
11 | # analyzer:
12 | # exclude:
13 | # - path/to/excluded/files/**
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Files and directories created by pub.
2 | .dart_tool/
3 | .packages
4 |
5 | # Conventional directory for build outputs.
6 | build/
7 |
8 | # test_cov files:
9 | coverage/
10 | test/.test_cov.dart
11 |
12 | # Omit committing pubspec.lock for library packages; see
13 | # https://dart.dev/guides/libraries/private-files#pubspeclock.
14 | pubspec.lock
15 |
16 | .DS_Store
17 |
--------------------------------------------------------------------------------
/run_coverage.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf ./coverage/test
4 | rm -rf ./coverage/report
5 | rm ./coverage/lcov.info
6 |
7 | dart run test --coverage=./coverage
8 |
9 | dart pub global run coverage:format_coverage --packages=.dart_tool/package_config.json --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage
10 |
11 | genhtml -o ./coverage/report ./coverage/lcov.info
12 | open ./coverage/report/index.html
13 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: statistics
2 | description: Statistics package for easy and efficient data manipulation with built-in Bayesian Network (Bayes Net), many mathematical functions and tools.
3 | version: 1.2.0
4 | homepage: https://github.com/gmpassos/statistics
5 |
6 | environment:
7 | sdk: '>=3.6.0 <4.0.0'
8 |
9 | dependencies:
10 | intl: ^0.20.2
11 | collection: ^1.19.0
12 | data_serializer: ^1.2.1
13 |
14 | dev_dependencies:
15 | lints: ^5.1.1
16 | test: ^1.25.15
17 | dependency_validator: ^3.2.3
18 | coverage: ^1.11.1
19 |
20 | #dependency_overrides:
21 | # data_serializer:
22 | # path: ../data_serializer
23 |
24 |
--------------------------------------------------------------------------------
/dart_test.yaml:
--------------------------------------------------------------------------------
1 |
2 | tags:
3 | # Number tests.
4 | num:
5 | timeout: 30s
6 | # Decimal tests.
7 | decimal:
8 | timeout: 30s
9 | # Specific browser tests.
10 | browser:
11 | timeout: 30s
12 | # Platform tests.
13 | platform:
14 | timeout: 30s
15 |
16 | timeout: 3x
17 |
18 | concurrency: 1
19 |
20 | override_platforms:
21 | chrome:
22 | settings:
23 | headless: true
24 | firefox:
25 | settings:
26 | arguments: -headless
27 |
28 | define_platforms:
29 | firefox-esr:
30 | name: Firefox-ESR
31 | extends: firefox
32 | settings:
33 | executable:
34 | linux: firefox-esr
35 |
36 |
37 |
--------------------------------------------------------------------------------
/statistics.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/lib/statistics.dart:
--------------------------------------------------------------------------------
1 | /// Statistics library.
2 | library;
3 |
4 | export 'package:data_serializer/data_serializer.dart';
5 |
6 | export 'src/statistics_base.dart';
7 | export 'src/statistics_bayesnet.dart';
8 | export 'src/statistics_benchmark.dart';
9 | export 'src/statistics_combination.dart';
10 | export 'src/statistics_decimal.dart';
11 | export 'src/statistics_dynamic_int.dart';
12 | export 'src/statistics_equality.dart';
13 | export 'src/statistics_equality_simd.dart';
14 | export 'src/statistics_extension.dart';
15 | export 'src/statistics_extension_num.dart';
16 | export 'src/statistics_extension_simd.dart';
17 | export 'src/statistics_forecast.dart';
18 | export 'src/statistics_metric.dart';
19 | export 'src/statistics_prime.dart';
20 | export 'src/statistics_tools.dart';
21 | export 'src/statistics_tools_csv.dart';
22 |
--------------------------------------------------------------------------------
/lib/src/statistics_equality_simd.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:collection/collection.dart';
4 |
5 | import 'statistics_extension_simd.dart';
6 |
7 | /// [Equality] for [Int32x4].
8 | class Int32x4Equality implements Equality {
9 | @override
10 | bool equals(Int32x4 e1, Int32x4 e2) => e1.equalsValues(e2);
11 |
12 | @override
13 | int hash(Int32x4 e) =>
14 | e.x.hashCode ^ e.y.hashCode ^ e.z.hashCode ^ e.w.hashCode;
15 |
16 | @override
17 | bool isValidKey(Object? o) {
18 | return o is Int32x4;
19 | }
20 | }
21 |
22 | /// [Equality] for [Float32x4].
23 | class Float32x4Equality implements Equality {
24 | @override
25 | bool equals(Float32x4 e1, Float32x4 e2) => e1.equalsValues(e2);
26 |
27 | @override
28 | int hash(Float32x4 e) =>
29 | e.x.hashCode ^ e.y.hashCode ^ e.z.hashCode ^ e.w.hashCode;
30 |
31 | @override
32 | bool isValidKey(Object? o) {
33 | return o is Float32x4;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/src/statistics_equality.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 |
3 | /// [Equality] for 'double'.
4 | class DoubleEquality implements Equality {
5 | @override
6 | bool equals(double e1, double e2) => e1 == e2;
7 |
8 | @override
9 | int hash(double e) => e.hashCode;
10 |
11 | @override
12 | bool isValidKey(Object? o) {
13 | return o is double;
14 | }
15 | }
16 |
17 | /// [Equality] for 'int'.
18 | class IntEquality implements Equality {
19 | @override
20 | bool equals(int e1, int e2) => e1 == e2;
21 |
22 | @override
23 | int hash(int e) => e.hashCode;
24 |
25 | @override
26 | bool isValidKey(Object? o) {
27 | return o is int;
28 | }
29 | }
30 |
31 | /// [Equality] for 'num'.
32 | class NumEquality implements Equality {
33 | @override
34 | bool equals(num e1, num e2) => e1 == e2;
35 |
36 | @override
37 | int hash(num e) => e.hashCode;
38 |
39 | @override
40 | bool isValidKey(Object? o) {
41 | return o is num;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/statistics_benchmark.dart:
--------------------------------------------------------------------------------
1 | import 'statistics_tools.dart';
2 |
3 | typedef BenchMarkFunction = BenchmarkFunctionResult Function(int loops);
4 |
5 | /// Performas a benchmark of a [set] of [BenchMarkFunction]s.
6 | List> benchmarkSet(
7 | int loops, Map> set) {
8 | var results = set.entries.map((e) {
9 | var result = benchmark(e.key, loops, e.value);
10 | return result;
11 | }).toList();
12 | results.sort();
13 | return results;
14 | }
15 |
16 | /// Performas a benchmark of [BenchMarkFunction] [f].
17 | BenchmarkResult benchmark(String name, int loops, BenchMarkFunction f,
18 | {bool verbose = false, Function(Object? o)? printer}) {
19 | var chronometer = BenchmarkResult(name)..start();
20 | var result = f(loops);
21 | chronometer.stop(operations: result.operations);
22 | chronometer._result = result.result;
23 |
24 | if (verbose) {
25 | if (printer != null) {
26 | printer(chronometer);
27 | } else {
28 | print(chronometer);
29 | }
30 | }
31 |
32 | return chronometer;
33 | }
34 |
35 | class BenchmarkResult extends Chronometer {
36 | late R _result;
37 |
38 | BenchmarkResult(super.name);
39 |
40 | R get result => _result;
41 |
42 | Duration get duration => elapsedTime;
43 |
44 | @override
45 | String toString({bool withStartTime = true}) {
46 | var s = super.toString(withStartTime: withStartTime);
47 | return '$s -> $result';
48 | }
49 | }
50 |
51 | class BenchmarkFunctionResult {
52 | final int operations;
53 | final R result;
54 |
55 | BenchmarkFunctionResult(this.operations, this.result);
56 | }
57 |
--------------------------------------------------------------------------------
/example/statistics_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 |
3 | void main() {
4 | var ns = [10, 20.0, 25, 30];
5 | print('ns: $ns');
6 |
7 | // Numeric extension:
8 |
9 | var mean = ns.mean;
10 | print('mean: $mean');
11 |
12 | var sdv = ns.standardDeviation;
13 | print('sdv: $sdv');
14 |
15 | var squares = ns.square;
16 | print('squares: $squares');
17 |
18 | // Statistics:
19 |
20 | var statistics = ns.statistics;
21 |
22 | print('Statistics.max: ${statistics.max}');
23 | print('Statistics.min: ${statistics.min}');
24 | print('Statistics.mean: ${statistics.mean}');
25 | print('Statistics.standardDeviation: ${statistics.standardDeviation}');
26 | print('Statistics.sum: ${statistics.sum}');
27 | print('Statistics.center: ${statistics.center}');
28 | print(
29 | 'Statistics.median: ${statistics.median} -> ${statistics.medianLow} , ${statistics.medianHigh}');
30 | print('Statistics.squaresSum: ${statistics.squaresSum}');
31 |
32 | print('Statistics: $statistics');
33 |
34 | // CSV:
35 |
36 | var categories = >{
37 | 'a': [10.0, 20.0, null],
38 | 'b': [100.0, 200.0, 300.0]
39 | };
40 |
41 | var csv = categories.generateCSV();
42 | print('---');
43 | print('CSV:');
44 | print(csv);
45 | }
46 |
47 | // ---------------------------------------------
48 | // OUTPUT:
49 | // ---------------------------------------------
50 | // ns: [10, 20.0, 25, 30]
51 | // mean: 21.25
52 | // sdv: 6.931585316505886
53 | // squares: [100, 400.0, 625, 900]
54 | // Statistics.max: 30
55 | // Statistics.min: 10
56 | // Statistics.mean: 21.25
57 | // Statistics.standardDeviation: 7.39509972887452
58 | // Statistics.sum: 85.0
59 | // Statistics.center: 25
60 | // Statistics.median: 22.5 -> 20.0 , 25
61 | // Statistics.squaresSum: 2025.0
62 | // Statistics: {~21.25 +-7.3950 [10..(25)..30] #4}
63 | // ---
64 | // CSV:
65 | // #,a,b
66 | // 1,10,100
67 | // 2,20,200
68 | // 3,0,300
69 | //
70 |
--------------------------------------------------------------------------------
/.github/workflows/dart.yml:
--------------------------------------------------------------------------------
1 | name: Dart CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: dart-lang/setup-dart@v1
15 | - name: Dart version
16 | run: |
17 | dart --version
18 | uname -a
19 | - name: Install dependencies
20 | run: dart pub get
21 | - name: Upgrade dependencies
22 | run: dart pub upgrade
23 | - name: dart format
24 | run: dart format -o none --set-exit-if-changed .
25 | - name: dart analyze
26 | run: dart analyze --fatal-infos --fatal-warnings .
27 | - name: dependency_validator
28 | run: dart run dependency_validator
29 | - name: dart doc
30 | run: dart doc --dry-run
31 | - name: dart pub publish --dry-run
32 | run: dart pub publish --dry-run
33 |
34 | test_vm:
35 | runs-on: ubuntu-latest
36 | steps:
37 | - uses: actions/checkout@v3
38 | - uses: dart-lang/setup-dart@v1
39 | - name: Dart version
40 | run: |
41 | dart --version
42 | uname -a
43 | - name: Install dependencies
44 | run: dart pub get
45 | - name: Upgrade dependencies
46 | run: dart pub upgrade
47 | - name: Run tests (VM)
48 | run: dart test --platform vm --coverage=./coverage
49 | - name: Generate coverage report
50 | run: |
51 | dart pub global activate coverage
52 | dart pub global run coverage:format_coverage --packages=.dart_tool/package_config.json --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage
53 | - name: Upload coverage to Codecov
54 | uses: codecov/codecov-action@v3
55 | env:
56 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
57 | with:
58 | directory: ./coverage/
59 | flags: unittests
60 | env_vars: OS,DART
61 | fail_ci_if_error: true
62 | verbose: true
63 |
64 |
65 | test_chrome:
66 | runs-on: ubuntu-latest
67 | steps:
68 | - uses: actions/checkout@v3
69 | - uses: dart-lang/setup-dart@v1
70 | - name: Dart version
71 | run: |
72 | dart --version
73 | uname -a
74 | - name: Install dependencies
75 | run: dart pub get
76 | - name: Upgrade dependencies
77 | run: dart pub upgrade
78 | - name: Run tests (Chrome)
79 | run: dart test --platform chrome
80 |
--------------------------------------------------------------------------------
/test/statistics_equality_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | import 'package:statistics/statistics.dart';
4 | import 'package:test/test.dart';
5 |
6 | void main() {
7 | group('Equality', () {
8 | setUp(() {});
9 |
10 | test('IntEquality', () {
11 | var eq = IntEquality();
12 |
13 | expect(eq.equals(10, 10), isTrue);
14 | expect(eq.equals(10, -10), isFalse);
15 |
16 | expect(eq.hash(10) == eq.hash(10), isTrue);
17 | expect(eq.hash(10) == eq.hash(11), isFalse);
18 |
19 | expect(eq.isValidKey(10), isTrue);
20 | expect(eq.isValidKey(10.1), isFalse);
21 | });
22 |
23 | test('DoubleEquality', () {
24 | var eq = DoubleEquality();
25 |
26 | expect(eq.equals(10.0, 10.0), isTrue);
27 | expect(eq.equals(10.0, -10.0), isFalse);
28 | expect(eq.equals(10.0, 10.00001), isFalse);
29 |
30 | expect(eq.hash(10) == eq.hash(10), isTrue);
31 | expect(eq.hash(10) == eq.hash(11), isFalse);
32 |
33 | expect(eq.isValidKey(10.0), isTrue);
34 | expect(eq.isValidKey('x'), isFalse);
35 | });
36 |
37 | test('NumEquality', () {
38 | var eq = NumEquality();
39 |
40 | expect(eq.equals(10, 10), isTrue);
41 | expect(eq.equals(10, -10), isFalse);
42 |
43 | expect(eq.hash(10) == eq.hash(10), isTrue);
44 | expect(eq.hash(10) == eq.hash(11), isFalse);
45 |
46 | expect(eq.isValidKey(10), isTrue);
47 | expect(eq.isValidKey(10.0), isTrue);
48 |
49 | expect(eq.isValidKey('10'), isFalse);
50 | });
51 |
52 | test('Int32x4Equality', () {
53 | var eq = Int32x4Equality();
54 |
55 | expect(
56 | eq.equals(Int32x4(10, 20, 30, 40), Int32x4(10, 20, 30, 40)), isTrue);
57 | expect(eq.equals(Int32x4(10, 20, 30, 40), Int32x4(-10, 20, 30, 40)),
58 | isFalse);
59 |
60 | expect(eq.hash(Int32x4(10, 20, 30, 40)),
61 | equals(eq.hash(Int32x4(10, 20, 30, 40))));
62 | expect(
63 | eq.hash(Int32x4(10, 20, 30, 40)) == eq.hash(Int32x4(-10, 20, 30, 40)),
64 | isFalse);
65 |
66 | expect(eq.isValidKey(Int32x4(10, 20, 30, 40)), isTrue);
67 | expect(eq.isValidKey(Float32x4(10, 20, 30, 40)), isFalse);
68 | expect(eq.isValidKey('10'), isFalse);
69 | });
70 |
71 | test('Float32x4Equality', () {
72 | var eq = Float32x4Equality();
73 |
74 | expect(eq.equals(Float32x4(10, 20, 30, 40), Float32x4(10, 20, 30, 40)),
75 | isTrue);
76 | expect(eq.equals(Float32x4(10, 20, 30, 40), Float32x4(-10, 20, 30, 40)),
77 | isFalse);
78 |
79 | expect(eq.hash(Float32x4(10, 20, 30, 40)),
80 | equals(eq.hash(Float32x4(10, 20, 30, 40))));
81 | expect(
82 | eq.hash(Float32x4(10, 20, 30, 40)) ==
83 | eq.hash(Float32x4(-10, 20, 30, 40)),
84 | isFalse);
85 |
86 | expect(eq.isValidKey(Float32x4(10, 20, 30, 40)), isTrue);
87 | expect(eq.isValidKey(Int32x4(10, 20, 30, 40)), isFalse);
88 | expect(eq.isValidKey('10'), isFalse);
89 | });
90 | });
91 | }
92 |
--------------------------------------------------------------------------------
/example/dynamic_int_benchmark_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 |
3 | void main() {
4 | var set = {
5 | 'int': testInt,
6 | 'DynamicInt': testDynamicInt,
7 | 'BigInt': testBigInt,
8 | };
9 |
10 | // Warm up:
11 | benchmarkSet(2000000, set);
12 |
13 | var results = benchmarkSet(2000000, set);
14 |
15 | print('RESULTS (${results.length}):');
16 | for (var r in results) {
17 | print('-> $r');
18 | }
19 |
20 | var fastest = results.last;
21 |
22 | print('\nFASTEST: $fastest');
23 | }
24 |
25 | const m1Value = 2;
26 | const m2Value = 3;
27 | const m3Value = 4;
28 | const m4Value = 10;
29 |
30 | BenchmarkFunctionResult testInt(int loops) {
31 | var ops = 0;
32 |
33 | var total = 0;
34 | ops++;
35 |
36 | var m1 = m1Value;
37 | ops++;
38 | var m2 = m2Value;
39 | ops++;
40 | var m3 = m3Value;
41 | ops++;
42 | var m4 = m4Value;
43 | ops++;
44 |
45 | for (var i = 0; i < loops; ++i) {
46 | var n1 = i;
47 | ops++;
48 |
49 | var n2 = n1 * m1;
50 | ops++;
51 |
52 | var n3 = n1 * m2;
53 | ops++;
54 |
55 | var n4 = n1 * m3;
56 | ops++;
57 |
58 | var n5 = (n1 * n3) + (n2 * n4);
59 | ops += 3;
60 |
61 | var n6 = n5 % m4;
62 | ops++;
63 |
64 | total += n6;
65 | ops++;
66 | }
67 | return BenchmarkFunctionResult(ops, total.toBigInt());
68 | }
69 |
70 | BenchmarkFunctionResult testDynamicInt(int loops) {
71 | var ops = 0;
72 |
73 | var total = DynamicInt.zero;
74 | ops++;
75 |
76 | var m1 = DynamicInt.fromInt(m1Value);
77 | ops++;
78 | var m2 = DynamicInt.fromInt(m2Value);
79 | ops++;
80 | var m3 = DynamicInt.fromInt(m3Value);
81 | ops++;
82 | var m4 = DynamicInt.fromInt(m4Value);
83 | ops++;
84 |
85 | for (var i = 0; i < loops; ++i) {
86 | var n1 = DynamicInt.fromInt(i);
87 | ops++;
88 |
89 | var n2 = n1 * m1;
90 | ops++;
91 |
92 | var n3 = n1 * m2;
93 | ops++;
94 |
95 | var n4 = n1 * m3;
96 | ops++;
97 |
98 | var n5 = (n1 * n3) + (n2 * n4);
99 | ops += 3;
100 |
101 | var n6 = n5 % m4;
102 | ops++;
103 |
104 | total = (total + n6).toDynamicInt();
105 | ops++;
106 | }
107 | return BenchmarkFunctionResult(ops, total.toBigInt());
108 | }
109 |
110 | BenchmarkFunctionResult testBigInt(int loops) {
111 | var ops = 0;
112 |
113 | var total = BigInt.zero;
114 | ops++;
115 |
116 | var m1 = BigInt.from(m1Value);
117 | ops++;
118 | var m2 = BigInt.from(m2Value);
119 | ops++;
120 | var m3 = BigInt.from(m3Value);
121 | ops++;
122 | var m4 = BigInt.from(m4Value);
123 | ops++;
124 |
125 | for (var i = 0; i < loops; ++i) {
126 | var n1 = BigInt.from(i);
127 | ops++;
128 |
129 | var n2 = n1 * m1;
130 | ops++;
131 |
132 | var n3 = n1 * m2;
133 | ops++;
134 |
135 | var n4 = n1 * m3;
136 | ops++;
137 |
138 | var n5 = (n1 * n3) + (n2 * n4);
139 | ops += 3;
140 |
141 | var n6 = n5 % m4;
142 | ops++;
143 |
144 | total += n6;
145 | ops++;
146 | }
147 |
148 | return BenchmarkFunctionResult(ops, total);
149 | }
150 |
151 | // ---------------------------------------------
152 | // OUTPUT:
153 | // ---------------------------------------------
154 | // RESULTS (3):
155 | // -> BigInt{ 892.00 ms · hertz: 20,179,377 Hz · ops: 18,000,005 · start: 2022-03-30 04:24:29.924215 .. 30.822949 } -> 9000000
156 | // -> DynamicInt{ 252.00 ms · hertz: 71,428,591 Hz · ops: 18,000,005 · start: 2022-03-30 04:24:29.672963 .. 30.842626 } -> 9000000
157 | // -> int{ 30.00 ms · hertz: 600,000,166 Hz · ops: 18,000,005 · start: 2022-03-30 04:24:29.642038 .. 30.842715 } -> 9000000
158 | //
159 | // FASTEST: int{ 30.00 ms · hertz: 600,000,166 Hz · ops: 18,000,005 · start: 2022-03-30 04:24:29.642038 .. 30.842783 } -> 9000000
160 | //
161 |
--------------------------------------------------------------------------------
/test/statistics_benchmark_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 | import 'package:test/expect.dart';
3 | import 'package:test/scaffolding.dart';
4 |
5 | void main() {
6 | group('Benchmark', () {
7 | test('benchmarkSet', () {
8 | var set = {
9 | 'int': testInt,
10 | 'DynamicInt': testDynamicInt,
11 | 'BigInt': testBigInt,
12 | };
13 |
14 | benchmarkSet(2000000, set);
15 |
16 | var results = benchmarkSet(2000000, set);
17 |
18 | for (var e in results) {
19 | print('-> $e');
20 | }
21 |
22 | expect(
23 | results.map((e) => e.name), equals(['BigInt', 'DynamicInt', 'int']));
24 | });
25 |
26 | test('benchmark: verbose', () {
27 | var result =
28 | benchmark('DynamicInt', 2000000, testDynamicInt, verbose: true);
29 |
30 | expect(result.hertz, greaterThan(1000));
31 | expect(result.duration.inMilliseconds, greaterThan(1));
32 | });
33 |
34 | test('benchmark: verbose+printer', () {
35 | var result = benchmark('DynamicInt', 2000000, testDynamicInt,
36 | verbose: true, printer: (o) => print('>> $o'));
37 | expect(result.hertz, greaterThan(1000));
38 | });
39 | });
40 | }
41 |
42 | const m1Value = 2;
43 | const m2Value = 3;
44 | const m3Value = 4;
45 | const m4Value = 10;
46 |
47 | BenchmarkFunctionResult testInt(int loops) {
48 | var ops = 0;
49 |
50 | var total = 0;
51 | ops++;
52 |
53 | var m1 = m1Value;
54 | ops++;
55 | var m2 = m2Value;
56 | ops++;
57 | var m3 = m3Value;
58 | ops++;
59 | var m4 = m4Value;
60 | ops++;
61 |
62 | for (var i = 0; i < loops; ++i) {
63 | var n1 = i;
64 | ops++;
65 |
66 | var n2 = n1 * m1;
67 | ops++;
68 |
69 | var n3 = n1 * m2;
70 | ops++;
71 |
72 | var n4 = n1 * m3;
73 | ops++;
74 |
75 | var n5 = (n1 * n3) + (n2 * n4);
76 | ops += 3;
77 |
78 | var n6 = n5 % m4;
79 | ops++;
80 |
81 | total += n6;
82 | ops++;
83 | }
84 | return BenchmarkFunctionResult(ops, total.toBigInt());
85 | }
86 |
87 | BenchmarkFunctionResult testDynamicInt(int loops) {
88 | var ops = 0;
89 |
90 | var total = DynamicInt.zero;
91 | ops++;
92 |
93 | var m1 = DynamicInt.fromInt(m1Value);
94 | ops++;
95 | var m2 = DynamicInt.fromInt(m2Value);
96 | ops++;
97 | var m3 = DynamicInt.fromInt(m3Value);
98 | ops++;
99 | var m4 = DynamicInt.fromInt(m4Value);
100 | ops++;
101 |
102 | for (var i = 0; i < loops; ++i) {
103 | var n1 = DynamicInt.fromInt(i);
104 | ops++;
105 |
106 | var n2 = n1 * m1;
107 | ops++;
108 |
109 | var n3 = n1 * m2;
110 | ops++;
111 |
112 | var n4 = n1 * m3;
113 | ops++;
114 |
115 | var n5 = (n1 * n3) + (n2 * n4);
116 | ops += 3;
117 |
118 | var n6 = n5 % m4;
119 | ops++;
120 |
121 | total = (total + n6).toDynamicInt();
122 | ops++;
123 | }
124 | return BenchmarkFunctionResult(ops, total.toBigInt());
125 | }
126 |
127 | BenchmarkFunctionResult testBigInt(int loops) {
128 | var ops = 0;
129 |
130 | var total = BigInt.zero;
131 | ops++;
132 |
133 | var m1 = BigInt.from(m1Value);
134 | ops++;
135 | var m2 = BigInt.from(m2Value);
136 | ops++;
137 | var m3 = BigInt.from(m3Value);
138 | ops++;
139 | var m4 = BigInt.from(m4Value);
140 | ops++;
141 |
142 | for (var i = 0; i < loops; ++i) {
143 | var n1 = BigInt.from(i);
144 | ops++;
145 |
146 | var n2 = n1 * m1;
147 | ops++;
148 |
149 | var n3 = n1 * m2;
150 | ops++;
151 |
152 | var n4 = n1 * m3;
153 | ops++;
154 |
155 | var n5 = (n1 * n3) + (n2 * n4);
156 | ops += 3;
157 |
158 | var n6 = n5 % m4;
159 | ops++;
160 |
161 | total += n6;
162 | ops++;
163 | }
164 |
165 | return BenchmarkFunctionResult(ops, total);
166 | }
167 |
--------------------------------------------------------------------------------
/example/bayesnet_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 |
3 | void main() {
4 | // Monitor events to then build a Bayesian Network:
5 | // ** Note that this example is NOT USING REAL probabilities for Cancer!
6 | var eventMonitor = BayesEventMonitor('cancer');
7 |
8 | // The prevalence of Cancer in the population:
9 | // - 1% (10:990):
10 |
11 | for (var i = 0; i < 990; ++i) {
12 | eventMonitor.notifyEvent(['CANCER=false']);
13 | }
14 |
15 | for (var i = 0; i < 10; ++i) {
16 | eventMonitor.notifyEvent(['CANCER=true']);
17 | }
18 |
19 | // The Exam performance when the person have cancer:
20 | // - 90% Sensitivity.
21 | // - 10% false negative (1:9).
22 |
23 | for (var i = 0; i < 9; ++i) {
24 | eventMonitor.notifyEvent(['EXAM=positive', 'CANCER=true']);
25 | }
26 |
27 | for (var i = 0; i < 1; ++i) {
28 | eventMonitor.notifyEvent(['EXAM=negative', 'CANCER=true']);
29 | }
30 |
31 | // The Exam performance when the person doesn't have cancer:
32 | // - 91% Specificity
33 | // - 9% false positive (89:901).
34 |
35 | for (var i = 0; i < 901; ++i) {
36 | eventMonitor.notifyEvent(['EXAM=negative', 'CANCER=false']);
37 | }
38 | for (var i = 0; i < 89; ++i) {
39 | eventMonitor.notifyEvent(['EXAM=positive', 'CANCER=false']);
40 | }
41 |
42 | var bayesNet = eventMonitor.buildBayesianNetwork();
43 |
44 | print('$bayesNet\n');
45 |
46 | var analyser = bayesNet.analyser;
47 |
48 | print('- Analyser: $analyser\n');
49 |
50 | var answerCancerWithoutExam = analyser.ask('P(cancer)');
51 | print('- Cancer probability without an Exam:');
52 | print(' $answerCancerWithoutExam');
53 |
54 | var answerNoCancerWithoutExam = analyser.ask('P(-cancer)');
55 | print('- Not having Cancer probability without an Exam:');
56 | print(' $answerNoCancerWithoutExam');
57 |
58 | var answerCancerWithPositiveExam = analyser.ask('P(cancer|exam)');
59 | print('- Cancer probability with a positive Exam:');
60 | print(' $answerCancerWithPositiveExam');
61 |
62 | var answerCancerWithNegativeExam = analyser.ask('P(cancer|-exam)');
63 | print('- Cancer probability with a negative Exam:');
64 | print(' $answerCancerWithNegativeExam');
65 |
66 | var answerNoCancerWithPositiveExam = analyser.ask('P(-cancer|exam)');
67 | print('- Not having Cancer probability with a positive Exam:');
68 | print(' $answerNoCancerWithPositiveExam');
69 |
70 | var answerNoCancerWithNegativeExam = analyser.ask('P(-cancer|-exam)');
71 | print('- Not having Cancer probability with a negative Exam:');
72 | print(' $answerNoCancerWithNegativeExam');
73 |
74 | print('\n** NOTE: This example is NOT USING REAL probabilities for Cancer!');
75 | }
76 |
77 | // ---------------------------------------------
78 | // OUTPUT:
79 | // ---------------------------------------------
80 | // BayesianNetwork[cancer]{ variables: 2 }<
81 | // CANCER: []
82 | // CANCER = FALSE: 0.99
83 | // CANCER = TRUE: 0.01
84 | // EXAM: [CANCER]
85 | // EXAM = NEGATIVE, CANCER = FALSE: 0.9101010101010101
86 | // EXAM = POSITIVE, CANCER = FALSE: 0.0898989898989899
87 | // EXAM = NEGATIVE, CANCER = TRUE: 0.1
88 | // EXAM = POSITIVE, CANCER = TRUE: 0.9
89 | //
90 | // >
91 | //
92 | // - Analyser: BayesAnalyserVariableElimination{network: cancer}
93 | //
94 | // - Cancer probability without an Exam:
95 | // P(cancer) -> CANCER = TRUE | -> 0.01 >> 100.00%
96 | // - Not having Cancer probability without an Exam:
97 | // P(-cancer) -> CANCER = FALSE | -> 0.99 >> 100.00%
98 | // - Cancer probability with a positive Exam:
99 | // P(cancer|exam) -> CANCER = TRUE | EXAM = POSITIVE -> 0.09183673469387756 (0.009000000000000001) >> 918.37%
100 | // - Cancer probability with a negative Exam:
101 | // P(cancer|-exam) -> CANCER = TRUE | EXAM = NEGATIVE -> 0.0011086474501108647 (0.001) >> 11.09%
102 | // - Not having Cancer probability with a positive Exam:
103 | // P(-cancer|exam) -> CANCER = FALSE | EXAM = POSITIVE -> 0.9081632653061223 (0.089) >> 91.73%
104 | // - Not having Cancer probability with a negative Exam:
105 | // P(-cancer|-exam) -> CANCER = FALSE | EXAM = NEGATIVE -> 0.9988913525498891 (0.901) >> 100.90%
106 | //
107 | // ** NOTE: This example is NOT USING REAL probabilities for Cancer!
108 | //
109 |
--------------------------------------------------------------------------------
/example/bayesnet_dependency_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 |
3 | void main() {
4 | // ** Note that this example is NOT USING REAL probabilities for Cancer!
5 |
6 | var bayesNet = BayesianNetwork('cancer');
7 |
8 | // C (cancer) = T (true) ; F (false)
9 | bayesNet.addVariable("C", [
10 | 'F',
11 | 'T',
12 | ], [], [
13 | "C = F: 0.99",
14 | "C = T: 0.01",
15 | ]);
16 |
17 | // X (exam) = P (positive) ; N (negative)
18 | bayesNet.addVariable("X", [
19 | '+P',
20 | '-N',
21 | ], [
22 | "C"
23 | ], [
24 | "X = N, C = F: 0.91",
25 | "X = P, C = F: 0.09",
26 | "X = N, C = T: 0.10",
27 | "X = P, C = T: 0.90",
28 | ]);
29 |
30 | // D (Doctor diagnosis) = P (positive) ; N (negative)
31 | bayesNet.addVariable("D", [
32 | '+P',
33 | '-N',
34 | ], [
35 | "C"
36 | ], [
37 | "D = N, C = F: 0.99",
38 | "D = P, C = F: 0.01",
39 | "D = N, C = T: 0.75",
40 | "D = P, C = T: 0.25",
41 | ]);
42 |
43 | // Add dependency between D (Doctor diagnosis) and X (Exam),
44 | // where the probability of a correct diagnosis is improved:
45 | bayesNet.addDependency([
46 | 'D',
47 | 'X'
48 | ], [
49 | "D = N, X = N, C = F: 0.364",
50 | "D = P, X = N, C = F: 0.546",
51 | "D = N, X = P, C = F: 0.036",
52 | "D = P, X = P, C = F: 0.054",
53 | "D = N, X = N, C = T: 0.025",
54 | "D = N, X = P, C = T: 0.075",
55 | "D = P, X = N, C = T: 0.225",
56 | "D = P, X = P, C = T: 0.675",
57 | ]);
58 |
59 | // Show the network nodes and probabilities:
60 | print(bayesNet);
61 |
62 | var analyser = bayesNet.analyser;
63 |
64 | // Ask the probability to have cancer with a positive exame (X = P):
65 | var answer1 = analyser.ask('P(c|x)');
66 | print(
67 | answer1); // P(c|x) -> C = T | X = P -> 0.09174311926605506 (0.009000000000000001) >> 917.43%
68 |
69 | // Ask the probability to have cancer with a negative exame (X = N):
70 | var answer2 = analyser.ask('P(c|-x)');
71 | print(
72 | answer2); // P(c|-x) -> C = T | X = N -> 0.0011087703736556158 (0.001) >> 11.09%
73 |
74 | // Ask the probability to have cancer with a positive diagnosis from the Doctor (D = P):
75 | var answer3 = analyser.ask('P(c|d)');
76 | print(
77 | answer3); // P(c|d) -> C = T | D = P -> 0.20161290322580644 (0.0025) >> 2016.13%
78 |
79 | // Ask the probability to have cancer with a negative diagnosis from the Doctor (D = N):
80 | var answer4 = analyser.ask('P(c|-d)');
81 | print(
82 | answer4); // P(c|-d) -> C = T | D = N -> 0.007594167679222358 (0.0075) >> 75.94%
83 |
84 | // Ask the probability to have cancer with a positive diagnosis from the Doctor and a positive exame (D = P, X = P):
85 | var answer5 = analyser.ask('P(c|d,x)');
86 | print(
87 | answer5); // P(c|d,x) -> C = T | D = P, X = P -> 0.11210762331838567 (0.006750000000000001) >> 1121.08%
88 |
89 | // Ask the probability to have cancer with a negative diagnosis from the Doctor and a negative exame (D = N, X = N):
90 | var answer6 = analyser.ask('P(c|-d,-x)');
91 | print(
92 | answer6); // P(c|-d,-x) -> C = T | D = N, X = N -> 0.0006932697373894235 (0.00025) >> 6.93%
93 |
94 | print('-- Generating all possible questions:');
95 |
96 | var questions = analyser.generateQuestions('C',
97 | addPriorQuestions: true, combinationsLevel: 2);
98 | var answers = analyser.quiz(questions);
99 |
100 | answers.sortByQuery();
101 |
102 | for (var answer in answers) {
103 | print(answer);
104 | }
105 | }
106 |
107 | // ---------------------------------------------
108 | // OUTPUT:
109 | // ---------------------------------------------
110 | // BayesianNetwork[cancer]{ variables: 3 }<
111 | // C: []
112 | // C = F: 0.99
113 | // C = T: 0.01
114 | // X: [C]
115 | // X = N, C = F: 0.91
116 | // X = P, C = F: 0.09
117 | // X = N, C = T: 0.1
118 | // X = P, C = T: 0.9
119 | // D: [C]
120 | // D = N, C = F: 0.99
121 | // D = P, C = F: 0.01
122 | // D = N, C = T: 0.75
123 | // D = P, C = T: 0.25
124 | // D <-> X:
125 | // D = N, C = F, X = N: 0.364
126 | // D = N, C = F, X = P: 0.036
127 | // D = P, C = F, X = N: 0.546
128 | // D = P, C = F, X = P: 0.054
129 | // D = N, C = T, X = N: 0.025
130 | // D = N, C = T, X = P: 0.075
131 | // D = P, C = T, X = N: 0.225
132 | // D = P, C = T, X = P: 0.675
133 | // >
134 | // P(c|x) -> C = T | X = P -> 0.09174311926605506 (0.009000000000000001) >> 917.43%
135 | // P(c|-x) -> C = T | X = N -> 0.0011087703736556158 (0.001) >> 11.09%
136 | // P(c|d) -> C = T | D = P -> 0.20161290322580644 (0.0025) >> 2016.13%
137 | // P(c|-d) -> C = T | D = N -> 0.007594167679222358 (0.0075) >> 75.94%
138 | // P(c|d,x) -> C = T | D = P, X = P -> 0.11210762331838567 (0.006750000000000001) >> 1121.08%
139 | // P(c|-d,-x) -> C = T | D = N, X = N -> 0.0006932697373894235 (0.00025) >> 6.93%
140 | // -- Generating all possible questions:
141 | // P(-C) -> C = F | -> 0.99 >> 100.00%
142 | // P(C) -> C = T | -> 0.01 >> 100.00%
143 | // P(C|-D) -> C = T | D = N -> 0.007594167679222358 (0.0075) >> 75.94%
144 | // P(C|-X,-D) -> C = T | D = N, X = N -> 0.0006932697373894235 (0.00025) >> 6.93%
145 | // P(C|X,-D) -> C = T | D = N, X = P -> 0.020610057708161583 (0.00075) >> 206.10%
146 | // P(C|D) -> C = T | D = P -> 0.20161290322580644 (0.0025) >> 2016.13%
147 | // P(C|-X,D) -> C = T | D = P, X = N -> 0.00414524954402255 (0.0022500000000000003) >> 41.45%
148 | // P(C|X,D) -> C = T | D = P, X = P -> 0.11210762331838567 (0.006750000000000001) >> 1121.08%
149 | // P(C|-X) -> C = T | X = N -> 0.0011087703736556158 (0.001) >> 11.09%
150 | // P(C|X) -> C = T | X = P -> 0.09174311926605506 (0.009000000000000001) >> 917.43%
151 | //
152 |
--------------------------------------------------------------------------------
/test/statistics_tools_csv_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:statistics/statistics.dart';
2 | import 'package:test/test.dart';
3 |
4 | void main() {
5 | group('CSV', () {
6 | setUp(() {});
7 |
8 | test('>', () {
9 | var categories = >{
10 | 'a': [10, 20, 30],
11 | 'b': [100, 200, 300]
12 | };
13 |
14 | var csv1 = categories.generateCSV();
15 |
16 | expect(
17 | csv1,
18 | equals('#,a,b\n'
19 | '1,10,100\n'
20 | '2,20,200\n'
21 | '3,30,300\n'));
22 |
23 | var csv2 = categories.generateCSV(valueNormalizer: (v) => v * 2);
24 |
25 | expect(
26 | csv2,
27 | equals('#,a,b\n'
28 | '1,20,200\n'
29 | '2,40,400\n'
30 | '3,60,600\n'));
31 |
32 | expect(categories.csvFileName('test', 'list'),
33 | matches(RegExp(r'^test--list--\d+\.csv$')));
34 | });
35 |
36 | test('>', () {
37 | var categories = >{
38 | 'a': [10, 20, null],
39 | 'b': [100, 200, 300]
40 | };
41 |
42 | var csv1 = categories.generateCSV();
43 |
44 | expect(
45 | csv1,
46 | equals('#,a,b\n'
47 | '1,10,100\n'
48 | '2,20,200\n'
49 | '3,0,300\n'));
50 |
51 | var csv2 = categories.generateCSV(valueNormalizer: (v) => (v ?? 0) * 2);
52 |
53 | expect(
54 | csv2,
55 | equals('#,a,b\n'
56 | '1,20,200\n'
57 | '2,40,400\n'
58 | '3,0,600\n'));
59 |
60 | expect(categories.csvFileName('test', 'list'),
61 | matches(RegExp(r'^test--list--\d+\.csv$')));
62 | });
63 |
64 | test('>', () {
65 | var categories = >{
66 | 'a': [10.0, 20.512345, null],
67 | 'b': [100.0, 200.0, 300.0]
68 | };
69 |
70 | var csv1 = categories.generateCSV();
71 |
72 | expect(
73 | csv1,
74 | equals('#,a,b\n'
75 | '1,10,100\n'
76 | '2,20.5123,200\n'
77 | '3,0,300\n'));
78 |
79 | var csv2 = categories.generateCSV(valueNormalizer: (v) => (v ?? 0) * 2);
80 |
81 | expect(
82 | csv2.replaceAll('.0,', ',').replaceAll('.0\n', '\n'),
83 | equals('#,a,b\n'
84 | '1,20,200\n'
85 | '2,41.02469,400\n'
86 | '3,0,600\n'));
87 |
88 | expect(categories.csvFileName('test', 'list'),
89 | matches(RegExp(r'^test--list--\d+\.csv$')));
90 |
91 | expect(categories.csvFileName('test', 'foo'),
92 | matches(RegExp(r'^test--foo--\d+.csv$')));
93 | });
94 |
95 | test('>', () {
96 | var list = >[
97 | [10, 20, 30],
98 | [30, 40, 50]
99 | ];
100 |
101 | var list2 = list.map((e) => e.statistics);
102 |
103 | var csv1 = list2.generateCSV(decimalPrecision: -1);
104 |
105 | expect(
106 | csv1,
107 | equals('mean,standardDeviation,length,min,max,sum,squaresSum\n'
108 | '20,8.16496580927726,3,10,30,60,1400\n'
109 | '40,8.16496580927726,3,30,50,120,5000\n'));
110 |
111 | var csv2 = list2.generateCSV(decimalPrecision: 2);
112 |
113 | expect(
114 | csv2,
115 | equals('mean,standardDeviation,length,min,max,sum,squaresSum\n'
116 | '20,8.16,3,10,30,60,1400\n'
117 | '40,8.16,3,30,50,120,5000\n'));
118 |
119 | var csv3 = list2.generateCSV(decimalPrecision: 0);
120 |
121 | expect(
122 | csv3,
123 | equals('mean,standardDeviation,length,min,max,sum,squaresSum\n'
124 | '20,8,3,10,30,60,1400\n'
125 | '40,8,3,30,50,120,5000\n'));
126 |
127 | var csv4 =
128 | list2.generateCSV(decimalPrecision: 2, commaAsDecimalSeparator: true);
129 |
130 | expect(
131 | csv4,
132 | equals('mean,standardDeviation,length,min,max,sum,squaresSum\n'
133 | '20,"8,16",3,10,30,60,1400\n'
134 | '40,"8,16",3,30,50,120,5000\n'));
135 |
136 | var csv5 =
137 | list2.generateCSV(valueNormalizer: (v) => v is num ? v * 2 : v!);
138 |
139 | expect(
140 | csv5.replaceAll('.0,', ',').replaceAll('.0\n', '\n'),
141 | equals('mean,standardDeviation,length,min,max,sum,squaresSum\n'
142 | '40,16.32993161855452,6,20,60,120,2800\n'
143 | '80,16.32993161855452,6,60,100,240,10000\n'));
144 |
145 | expect(list2.csvFileName('test', 'list'),
146 | matches(RegExp(r'^test--list--\d+\.csv$')));
147 |
148 | expect(list2.csvFileName('test', 'foo'),
149 | matches(RegExp(r'^test--foo--\d+.csv$')));
150 | });
151 |
152 | test('[]', () {
153 | var l = [
154 | {'a': 1, 'b': 2},
155 | {'a': 10, 'b': 20},
156 | {'a': 100, 'b': 200}
157 | ];
158 |
159 | var csv1 = l.generateCSV();
160 |
161 | expect(
162 | csv1,
163 | equals('a,b\n'
164 | '1,2\n'
165 | '10,20\n'
166 | '100,200\n'));
167 |
168 | var csv2 = l.generateCSV(valueNormalizer: (v) => int.parse('$v') * 2);
169 |
170 | expect(
171 | csv2,
172 | equals('a,b\n'
173 | '2,4\n'
174 | '20,40\n'
175 | '200,400\n'));
176 |
177 | var csv3 = l.generateCSV(fieldsNames: ['b', 'a']);
178 |
179 | expect(
180 | csv3,
181 | equals('b,a\n'
182 | '2,1\n'
183 | '20,10\n'
184 | '200,100\n'));
185 |
186 | var csv4 = l.generateCSV(
187 | fieldsNames: ['b', 'a'], valueNormalizer: (v) => int.parse('$v') * 2);
188 |
189 | expect(
190 | csv4,
191 | equals('b,a\n'
192 | '4,2\n'
193 | '40,20\n'
194 | '400,200\n'));
195 |
196 | expect(l.csvFileName('test', 'foo'),
197 | matches(RegExp(r'^test--foo--\d+.csv$')));
198 | });
199 | });
200 | }
201 |
--------------------------------------------------------------------------------
/lib/src/statistics_extension_simd.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | /// extension for [Int32x4].
4 | extension Int32x4Extension on Int32x4 {
5 | /// Converts to a [Float32x4].
6 | Float32x4 toFloat32x4() =>
7 | Float32x4(x.toDouble(), y.toDouble(), z.toDouble(), w.toDouble());
8 |
9 | /// Filter this with [mapper].
10 | Int32x4 filter(Int32x4 Function(Int32x4 e) mapper) => mapper(this);
11 |
12 | /// Filter each value with [mapper] and return a [Int32x4].
13 | Int32x4 filterValues(int Function(int e) mapper) {
14 | return Int32x4(
15 | mapper(x),
16 | mapper(y),
17 | mapper(z),
18 | mapper(w),
19 | );
20 | }
21 |
22 | /// Filter each value with [mapper] and return a [Float32x4].
23 | Float32x4 filterToDoubleValues(double Function(int e) mapper) {
24 | return Float32x4(
25 | mapper(x),
26 | mapper(y),
27 | mapper(z),
28 | mapper(w),
29 | );
30 | }
31 |
32 | /// Map using [mapper].
33 | T map(T Function(Int32x4 e) mapper) => mapper(this);
34 |
35 | /// Returns values as `List`.
36 | List toInts() => [x, y, z, w];
37 |
38 | Int32x4 operator *(Int32x4 other) => Int32x4(
39 | x * other.x,
40 | y * other.y,
41 | z * other.z,
42 | w * other.w,
43 | );
44 |
45 | Int32x4 operator ~/(Int32x4 other) => Int32x4(
46 | x ~/ other.x,
47 | y ~/ other.y,
48 | z ~/ other.z,
49 | w ~/ other.w,
50 | );
51 |
52 | /// Returns the minimal lane value.
53 | int get minInLane {
54 | var min = x;
55 | if (y < min) min = y;
56 | if (z < min) min = z;
57 | if (w < min) min = w;
58 | return min;
59 | }
60 |
61 | /// Returns the maximum lane value.
62 | int get maxInLane {
63 | var max = x;
64 | if (y > max) max = y;
65 | if (z > max) max = z;
66 | if (w > max) max = w;
67 | return max;
68 | }
69 |
70 | /// Sum lane.
71 | int get sumLane => x + y + z + w;
72 |
73 | /// Sum part of the lane, until [size].
74 | int sumLanePartial(int size) {
75 | switch (size) {
76 | case 1:
77 | return x;
78 | case 2:
79 | return x + y;
80 | case 3:
81 | return x + y + z;
82 | case 4:
83 | return x + y + z + w;
84 | default:
85 | throw StateError('Invalid length: $size / 4');
86 | }
87 | }
88 |
89 | /// Sum lane squares.
90 | int get sumSquaresLane => (x * x) + (y * y) + (z * z) + (w * w);
91 |
92 | /// Sum part of the lane squares, until [size].
93 | int sumSquaresLanePartial(int size) {
94 | switch (size) {
95 | case 1:
96 | return (x * x);
97 | case 2:
98 | return (x * x) + (y * y);
99 | case 3:
100 | return (x * x) + (y * y) + (z * z);
101 | case 4:
102 | return (x * x) + (y * y) + (z * z) + (w * w);
103 | default:
104 | throw StateError('Invalid length: $size / 4');
105 | }
106 | }
107 |
108 | /// Returns true if equals to [other] values.
109 | bool equalsValues(Int32x4 other, {num tolerance = 0.0}) {
110 | var diff = this - other;
111 |
112 | if (tolerance == 0) {
113 | return diff.x == 0 && diff.y == 0 && diff.z == 0 && diff.w == 0;
114 | } else {
115 | tolerance = tolerance.abs();
116 | return diff.x.abs() <= tolerance &&
117 | diff.y.abs() <= tolerance &&
118 | diff.z.abs() <= tolerance &&
119 | diff.w.abs() <= tolerance;
120 | }
121 | }
122 | }
123 |
124 | /// extension for [Float32x4].
125 | extension Float32x4Extension on Float32x4 {
126 | /// Converts to a [Int32x4].
127 | Int32x4 toInt32x4() => Int32x4(x.toInt(), y.toInt(), z.toInt(), w.toInt());
128 |
129 | /// Perform a `toInt()` in each value and return a [Float32x4].
130 | Float32x4 toIntAsFloat32x4() => Float32x4(x.toInt().toDouble(),
131 | y.toInt().toDouble(), z.toInt().toDouble(), w.toInt().toDouble());
132 |
133 | /// Filter this with [mapper].
134 | Float32x4 filter(Float32x4 Function(Float32x4 e) filter) => filter(this);
135 |
136 | /// Filter each value with [mapper] and return a [Float32x4].
137 | Float32x4 filterValues(double Function(double e) mapper) {
138 | return Float32x4(
139 | mapper(x),
140 | mapper(y),
141 | mapper(z),
142 | mapper(w),
143 | );
144 | }
145 |
146 | /// Filter each value with [mapper] and return a [Int32x4].
147 | Int32x4 filterToIntValues(int Function(double e) mapper) {
148 | return Int32x4(
149 | mapper(x),
150 | mapper(y),
151 | mapper(z),
152 | mapper(w),
153 | );
154 | }
155 |
156 | /// Map using [mapper].
157 | T map(T Function(Float32x4 e) mapper) => mapper(this);
158 |
159 | /// Returns values as `List`.
160 | List toDoubles() => [x, y, z, w];
161 |
162 | Int32x4 operator ~/(Float32x4 other) => Int32x4(
163 | x ~/ other.x,
164 | y ~/ other.y,
165 | z ~/ other.z,
166 | w ~/ other.w,
167 | );
168 |
169 | /// Returns the minimum lane value.
170 | double get minInLane {
171 | var min = x;
172 | if (y < min) min = y;
173 | if (z < min) min = z;
174 | if (w < min) min = w;
175 | return min;
176 | }
177 |
178 | /// Returns the maximum lane value.
179 | double get maxInLane {
180 | var max = x;
181 | if (y > max) max = y;
182 | if (z > max) max = z;
183 | if (w > max) max = w;
184 | return max;
185 | }
186 |
187 | /// Sum lane.
188 | double get sumLane => x + y + z + w;
189 |
190 | /// Sum part of the lane, until [size].
191 | double sumLanePartial(int size) {
192 | switch (size) {
193 | case 1:
194 | return x;
195 | case 2:
196 | return x + y;
197 | case 3:
198 | return x + y + z;
199 | case 4:
200 | return x + y + z + w;
201 | default:
202 | throw StateError('Invalid length: $size / 4');
203 | }
204 | }
205 |
206 | /// Sum lane squares.
207 | double get sumSquaresLane => (x * x) + (y * y) + (z * z) + (w * w);
208 |
209 | /// Sum part of the lane squares, until [size].
210 | double sumSquaresLanePartial(int size) {
211 | switch (size) {
212 | case 1:
213 | return (x * x);
214 | case 2:
215 | return (x * x) + (y * y);
216 | case 3:
217 | return (x * x) + (y * y) + (z * z);
218 | case 4:
219 | return (x * x) + (y * y) + (z * z) + (w * w);
220 | default:
221 | throw StateError('Invalid length: $size / 4');
222 | }
223 | }
224 |
225 | /// Returns true if equals to [other] values.
226 | bool equalsValues(Float32x4 other, {num tolerance = 0.0}) {
227 | var diff = this - other;
228 |
229 | if (tolerance == 0) {
230 | return diff.x == 0.0 && diff.y == 0.0 && diff.z == 0.0 && diff.w == 0.0;
231 | } else {
232 | tolerance = tolerance.abs();
233 | return diff.x.abs() <= tolerance &&
234 | diff.y.abs() <= tolerance &&
235 | diff.z.abs() <= tolerance &&
236 | diff.w.abs() <= tolerance;
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/lib/src/statistics_prime.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:statistics/statistics.dart';
3 |
4 | /// Utils for prime numbers.
5 | class PrimeUtils {
6 | static final List _knownPrimes = [
7 | 2,
8 | 3,
9 | 5,
10 | 7,
11 | 11,
12 | 13,
13 | 17,
14 | 19,
15 | 23,
16 | 29,
17 | 31,
18 | 37,
19 | 41,
20 | 43,
21 | 47,
22 | 53,
23 | 59,
24 | 61,
25 | 67,
26 | 71,
27 | 73,
28 | 79,
29 | 83,
30 | 89,
31 | 97
32 | ];
33 |
34 | /// A list of known primes.
35 | /// Used to compute if a number is prime or not.
36 | static UnmodifiableListView get knownPrimes =>
37 | UnmodifiableListView(_knownPrimes);
38 |
39 | static int get knownPrimesLength => _knownPrimes.length;
40 |
41 | static Iterator get knownPrimesIterator => _knownPrimes.iterator;
42 |
43 | /// Returns the last known prime in the [knownPrimes] list.
44 | static int get lastKnownPrime {
45 | assert(_knownPrimes.isNotEmpty, '`_knownPrimes` must never be empty.');
46 | return _knownPrimes.last;
47 | }
48 |
49 | /// Expands the [knownPrimes] list to length [knownPrimesLength].
50 | static void expandKnownPrimes(int knownPrimesLength) {
51 | if (_knownPrimes.length >= knownPrimesLength) return;
52 |
53 | while (_knownPrimes.length < knownPrimesLength) {
54 | var n = knownPrimes.last + 2;
55 |
56 | do {
57 | if (n.isPrime) {
58 | _knownPrimes.add(n);
59 | break;
60 | } else {
61 | n += 2;
62 | }
63 | } while (n > 1);
64 | }
65 | }
66 |
67 | /// Contracts the [knownPrimes] list length to [knownPrimesLength].
68 | static void contractKnownPrimes(int knownPrimesLength) {
69 | if (knownPrimesLength < 5) {
70 | knownPrimesLength = 5;
71 | }
72 |
73 | if (_knownPrimes.length <= knownPrimesLength) return;
74 |
75 | _knownPrimes.removeRange(knownPrimesLength, _knownPrimes.length);
76 | }
77 |
78 | /// Returns `true` if [n] is in the [knownPrimes] list.
79 | static bool? isKnownPrime(int n) {
80 | assert(_knownPrimes.isNotEmpty, '`_knownPrimes` must never be empty.');
81 |
82 | if (n > _knownPrimes.last) {
83 | return null;
84 | }
85 |
86 | var idx = _knownPrimes.binarySearch(n, (a, b) => a.compareTo(b));
87 | return idx >= 0;
88 | }
89 |
90 | /// Generates a [List] of prime numbers of [length] and below [primeLimit] (if provided).
91 | static List generatePrimes(int length, {int? primeLimit}) {
92 | if (length <= 0) return [];
93 |
94 | if (length == 1) return [2];
95 | if (length == 2) return [2, 3];
96 |
97 | if (primeLimit == null || primeLimit <= 0) {
98 | primeLimit = Statistics.maxSafeInt;
99 | }
100 |
101 | if (primeLimit <= 3) {
102 | primeLimit = 3;
103 | }
104 |
105 | var primes = [2, 3];
106 |
107 | for (var n = 5; n < primeLimit && primes.length < length; n += 2) {
108 | if (n.isPrime) {
109 | primes.add(n);
110 | }
111 | }
112 |
113 | return primes;
114 | }
115 | }
116 |
117 | /// Prime numbers extension on [int].
118 | extension PrimeIntExtension on int {
119 | /// Returns `true` if this [int] is a prime number.
120 | bool get isPrime {
121 | var n = this;
122 |
123 | if (n <= 0) return false;
124 | if (n == 1) return false;
125 | if (n == 2) return true;
126 |
127 | var knownPrime = PrimeUtils.isKnownPrime(n);
128 |
129 | if (knownPrime != null) {
130 | return knownPrime;
131 | }
132 |
133 | var b = n.squareRoot;
134 |
135 | var itr = PrimeUtils.knownPrimesIterator;
136 | itr.moveNext(); // it's never empty.
137 |
138 | var p = itr.current;
139 | if (n % p == 0) return false;
140 |
141 | while (itr.moveNext()) {
142 | p = itr.current;
143 | if (p > b) break;
144 |
145 | if (n % p == 0) return false;
146 | }
147 |
148 | for (p += 2; p <= b; p += 2) {
149 | if (n % p == 0) return false;
150 | }
151 |
152 | return true;
153 | }
154 |
155 | /// Returns [val] when this number [isPrime] otherwise returns [def].
156 | T? whenPrime(T val, [T? def]) {
157 | if (isPrime) {
158 | return val;
159 | } else {
160 | return def;
161 | }
162 | }
163 | }
164 |
165 | /// Prime numbers extension on [DynamicNumber].
166 | extension PrimeDynamicNumberExtension on DynamicNumber {
167 | /// Returns `true` if this [DynamicNumber] is a prime number.
168 | bool get isPrime {
169 | var self = this;
170 | if (self is DynamicInt) {
171 | return self.isPrime;
172 | } else if (self is Decimal) {
173 | return self.isPrime;
174 | } else {
175 | throw StateError("Unknown type: $runtimeType");
176 | }
177 | }
178 |
179 | /// Returns [val] when this number [isPrime] otherwise returns [def].
180 | T? whenPrime(T val, [T? def]) {
181 | if (isPrime) {
182 | return val;
183 | } else {
184 | return def;
185 | }
186 | }
187 | }
188 |
189 | extension PrimeDecimalExtension on Decimal {
190 | /// Returns `true` if this [Decimal] [isWholeNumber] and is a prime number.
191 | bool get isPrime {
192 | if (isWholeNumber) {
193 | return toDynamicInt().isPrime;
194 | } else {
195 | return false;
196 | }
197 | }
198 | }
199 |
200 | /// Prime numbers extension on [DynamicInt].
201 | extension PrimeDynamicIntExtension on DynamicInt {
202 | /// Returns `true` if this [DynamicInt] is a prime number.
203 | bool get isPrime {
204 | var n = this;
205 |
206 | if (n.isZero) return false;
207 | if (n.isNegative) return false;
208 | if (n.isOne) return false;
209 | if (n == DynamicInt.two) return true;
210 |
211 | if (n.isSafeInteger) {
212 | return n.toInt().isPrime;
213 | }
214 |
215 | var b = n.squareRoot.toDynamicInt();
216 |
217 | // Faster:
218 | if (b.isSafeInteger) {
219 | var bI = b.toInt();
220 |
221 | var itr = PrimeUtils.knownPrimesIterator;
222 | itr.moveNext(); // it's never empty.
223 |
224 | var p = itr.current;
225 | if (n.moduloInt(p).isZero) return false;
226 |
227 | while (itr.moveNext()) {
228 | p = itr.current;
229 | if (p > bI) break;
230 |
231 | if (n.moduloInt(p).isZero) return false;
232 | }
233 |
234 | for (p = p + 2; p <= bI; p += 2) {
235 | if (n.moduloInt(p).isZero) return false;
236 | }
237 | }
238 | // For a very big `n`.
239 | // Only using `DynamicInt` (slower):
240 | else {
241 | var itr = PrimeUtils.knownPrimesIterator;
242 | itr.moveNext(); // it's never empty.
243 |
244 | var p = itr.current.toDynamicInt();
245 | if (n.moduloDynamicInt(p).isZero) return false;
246 |
247 | while (itr.moveNext()) {
248 | p = itr.current.toDynamicInt();
249 | if (p > b) break;
250 |
251 | if (n.moduloDynamicInt(p).isZero) return false;
252 | }
253 |
254 | for (p = p.sumInt(2); p <= b; p = p.sumInt(2)) {
255 | if (n.moduloDynamicInt(p).isZero) return false;
256 | }
257 | }
258 |
259 | return true;
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/lib/src/statistics_combination.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 |
3 | /// A combination cache. See [generateCombinations].
4 | class CombinationCache {
5 | final bool allowRepetition;
6 | final bool allowSharedCombinations;
7 | final Iterable Function(T e)? mapper;
8 | final bool Function(List combination)? validator;
9 |
10 | CombinationCache(
11 | {required this.allowRepetition,
12 | this.mapper,
13 | this.validator,
14 | this.allowSharedCombinations = false});
15 |
16 | final Map<_CombinationCacheKey, List>> _cache =
17 | <_CombinationCacheKey, List>>{};
18 |
19 | /// Returns the total number of combinations in the cache.
20 | int get totalCachedCombinations => _cache.length;
21 |
22 | /// Clears the combinations cache.
23 | void clear() => _cache.clear();
24 |
25 | /// Returns a cached combination or generates it.
26 | List> getCombinations(
27 | Set alphabet, int minimumSize, int maximumSize) =>
28 | _getCombinationsImpl(alphabet, minimumSize, maximumSize)
29 | .map((e) => e.toList())
30 | .toList();
31 |
32 | /// Same of [getCombinations], but the returned [List] won't be isolated from the
33 | /// cache (is the actual instance inside the cache).
34 | ///
35 | /// - [allowSharedCombinations] needs to be true, or a [StateError] will be thrown.
36 | ///
37 | /// - Note: any modification in the returned list can corrupt the combination
38 | /// integrity for a future return of this cached combination cache.
39 | List> getCombinationsShared(
40 | Set alphabet, int minimumSize, int maximumSize) {
41 | if (!allowSharedCombinations) {
42 | throw StateError('Shared combinations not allowed: $this');
43 | }
44 |
45 | return _getCombinationsImpl(alphabet, minimumSize, maximumSize);
46 | }
47 |
48 | List> _getCombinationsImpl(
49 | Set alphabet, int minimumSize, int maximumSize) {
50 | return _cache.putIfAbsent(
51 | _CombinationCacheKey(alphabet, minimumSize, maximumSize),
52 | () => _generateCombinationsImpl(alphabet, minimumSize, maximumSize));
53 | }
54 |
55 | int _computedCombinations = 0;
56 |
57 | /// Returns the number of computed combinations.
58 | ///
59 | /// - [clear] won't reset this value.
60 | int get computedCombinations => _computedCombinations;
61 |
62 | List> _generateCombinationsImpl(
63 | Set alphabet, int minimumSize, int maximumSize) {
64 | ++_computedCombinations;
65 | return generateCombinations(alphabet, minimumSize, maximumSize,
66 | allowRepetition: allowRepetition,
67 | checkAlphabet: false,
68 | mapper: mapper,
69 | validator: validator);
70 | }
71 |
72 | @override
73 | String toString() {
74 | return 'CombinationCache<$T,$E>{ allowRepetition: $allowRepetition, allowSharedCombinations: $allowSharedCombinations, cache: $totalCachedCombinations, computedCombinations: $_computedCombinations }';
75 | }
76 | }
77 |
78 | class _CombinationCacheKey {
79 | final Set _alphabet;
80 |
81 | final int _minimumSize;
82 | final int _maximumSize;
83 |
84 | late final int _alphabetHash = _setEquality.hash(_alphabet);
85 |
86 | _CombinationCacheKey(this._alphabet, this._minimumSize, this._maximumSize);
87 |
88 | static final _setEquality = SetEquality();
89 |
90 | @override
91 | bool operator ==(Object other) =>
92 | identical(this, other) ||
93 | other is _CombinationCacheKey &&
94 | runtimeType == other.runtimeType &&
95 | _minimumSize == other._minimumSize &&
96 | _maximumSize == other._maximumSize &&
97 | _setEquality.equals(_alphabet, other._alphabet);
98 |
99 | @override
100 | int get hashCode =>
101 | _alphabetHash ^ _minimumSize.hashCode ^ _maximumSize.hashCode;
102 | }
103 |
104 | /// Generate combinations using the [alphabet] elements.
105 | ///
106 | /// - [alphabet] of possible elements per combination.
107 | /// - The [minimumSize] of the generated combinations.
108 | /// - The [maximumSize] of the generated combinations.
109 | /// - If [allowRepetition] is `true` will allow the repetition of elements for each combination.
110 | /// - If [checkAlphabet] is `true` it will check if the [alphabet] has duplicated elements.
111 | /// - An optional [mapper] can be used to expand or map each [alphabet] element.
112 | /// - An optional combination [validator].
113 | /// - Note that an alphabet can't have duplicated elements.
114 | List> generateCombinations(
115 | Iterable alphabet, int minimumSize, int maximumSize,
116 | {bool allowRepetition = true,
117 | bool checkAlphabet = true,
118 | Iterable Function(T e)? mapper,
119 | bool Function(List combination)? validator}) {
120 | if (minimumSize < 1) minimumSize = 1;
121 |
122 | var combinations = >[];
123 | if (alphabet.isEmpty || maximumSize <= 0) return combinations;
124 |
125 | if (alphabet is! List && alphabet is! Set) {
126 | alphabet = alphabet.toList();
127 | }
128 |
129 | if (checkAlphabet && alphabet is! Set) {
130 | var set = alphabet.toSet();
131 | if (set.length != alphabet.length) {
132 | throw ArgumentError('Invalid alphabet: found duplicated element!');
133 | }
134 | }
135 |
136 | mapper ??= (e) => [e as E];
137 | validator ??= (c) => true;
138 |
139 | if (allowRepetition) {
140 | for (var size = minimumSize; size <= maximumSize; ++size) {
141 | _fillWithRepetition(
142 | alphabet, [], size, combinations, mapper, validator);
143 | }
144 | } else {
145 | if (maximumSize > alphabet.length) {
146 | maximumSize = alphabet.length;
147 | }
148 |
149 | for (var size = minimumSize; size <= maximumSize; ++size) {
150 | _fillNoRepetition(
151 | alphabet, [], 0, size, combinations, mapper, validator);
152 | }
153 | }
154 |
155 | return combinations;
156 | }
157 |
158 | void _fillNoRepetition(
159 | Iterable alphabet,
160 | List dst,
161 | int offset,
162 | int limit,
163 | List> output,
164 | Iterable Function(T e) mapper,
165 | bool Function(List combination) validator) {
166 | var length = alphabet.length;
167 |
168 | for (var i = offset; i < length; ++i) {
169 | var e = alphabet.elementAt(i);
170 |
171 | var values = mapper(e);
172 |
173 | for (var v in values) {
174 | var dst2 = [...dst, v];
175 | if (dst2.length < limit) {
176 | _fillNoRepetition(
177 | alphabet, dst2, i + 1, limit, output, mapper, validator);
178 | } else if (validator(dst2)) {
179 | output.add(dst2);
180 | }
181 | }
182 | }
183 | }
184 |
185 | void _fillWithRepetition(
186 | Iterable alphabet,
187 | List dst,
188 | int limit,
189 | List> output,
190 | Iterable Function(T e) mapper,
191 | bool Function(List combination) validator) {
192 | var length = alphabet.length;
193 |
194 | for (var i = 0; i < length; ++i) {
195 | var e = alphabet.elementAt(i);
196 |
197 | var values = mapper(e);
198 |
199 | for (var v in values) {
200 | var dst2 = [...dst, v];
201 | if (dst2.length < limit) {
202 | _fillWithRepetition(alphabet, dst2, limit, output, mapper, validator);
203 | } else if (validator(dst2)) {
204 | output.add(dst2);
205 | }
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/lib/src/statistics_tools_csv.dart:
--------------------------------------------------------------------------------
1 | import 'statistics_base.dart';
2 | import 'statistics_extension.dart';
3 | import 'statistics_extension_num.dart';
4 |
5 | extension DataEntryExtension on Iterable {
6 | /// Generates a `CSV` document.
7 | String generateCSV(
8 | {String separator = ',',
9 | List? fieldsNames,
10 | Object Function(Object? e)? valueNormalizer,
11 | bool commaAsDecimalSeparator = false,
12 | int decimalPrecision = 4}) {
13 | if (isEmpty) return '';
14 |
15 | var csv = StringBuffer();
16 |
17 | fieldsNames ??= first.getDataFields();
18 |
19 | {
20 | var head = fieldsNames.map(_normalizeLine).join(separator);
21 | csv.write(head);
22 | csv.write('\n');
23 | }
24 |
25 | if (valueNormalizer != null) {
26 | for (var e in this) {
27 | var values = e.getDataValues();
28 | var line = values
29 | .map((e) => _normalizeLine(valueNormalizer(e).toString()))
30 | .join(separator);
31 | csv.write(line);
32 | csv.write('\n');
33 | }
34 | } else {
35 | for (var e in this) {
36 | var values = e.getDataValues();
37 | var line = values.map((e) {
38 | var s = normalizeCSVValue(e,
39 | separator: separator,
40 | commaAsDecimalSeparator: commaAsDecimalSeparator,
41 | decimalPrecision: decimalPrecision);
42 | return _normalizeLine(s);
43 | }).join(separator);
44 | csv.write(line);
45 | csv.write('\n');
46 | }
47 | }
48 |
49 | return csv.toString();
50 | }
51 |
52 | /// Creates a `CSV` file name.
53 | String csvFileName(String prefix, String name) => _csvFileName(prefix, name);
54 | }
55 |
56 | extension IterableMapExtensionCSV on Iterable