trackler 2.1.0.26 → 2.1.0.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/common/CONTRIBUTING.md +0 -15
  3. data/common/exercises/secret-handshake/canonical-data.json +14 -7
  4. data/lib/trackler/version.rb +1 -1
  5. data/tracks/dlang/config.json +18 -0
  6. data/tracks/dlang/exercises/leap/leap.d +8 -0
  7. data/tracks/dlang/exercises/leap/leap_example.d +15 -0
  8. data/tracks/dlang/exercises/react/react.d +211 -0
  9. data/tracks/dlang/exercises/react/react_example.d +291 -0
  10. data/tracks/factor/README.md +7 -7
  11. data/tracks/factor/config.json +13 -4
  12. data/tracks/factor/exercises/hello-world/hello-world-example.factor +2 -3
  13. data/tracks/factor/exercises/hello-world/hello-world-tests.factor +1 -5
  14. data/tracks/factor/exercises/hello-world/hello-world.factor +1 -1
  15. data/tracks/factor/exercises/two-fer/two-fer-example.factor +6 -0
  16. data/tracks/factor/exercises/two-fer/two-fer-tests.factor +8 -0
  17. data/tracks/fortran/Makefile +1 -2
  18. data/tracks/fortran/config.json +6 -0
  19. data/tracks/fortran/exercises/bob/bob.fun +18 -18
  20. data/tracks/fortran/exercises/hamming/example.f90 +25 -0
  21. data/tracks/fortran/exercises/hamming/hamming.fun +75 -0
  22. data/tracks/go/exercises/poker/.meta/gen.go +76 -0
  23. data/tracks/go/exercises/poker/cases_test.go +240 -0
  24. data/tracks/go/exercises/poker/example.go +10 -1
  25. data/tracks/go/exercises/poker/poker_test.go +1 -224
  26. data/tracks/go/exercises/sum-of-multiples/.meta/gen.go +43 -0
  27. data/tracks/go/exercises/sum-of-multiples/cases_test.go +24 -0
  28. data/tracks/go/exercises/sum-of-multiples/example.go +1 -1
  29. data/tracks/go/exercises/sum-of-multiples/sum_of_multiples_test.go +1 -18
  30. data/tracks/groovy/exercises/robot-name/RobotSpec.groovy +38 -0
  31. data/tracks/kotlin/config.json +10 -0
  32. data/tracks/kotlin/exercises/collatz-conjecture/build.gradle +28 -0
  33. data/tracks/kotlin/exercises/collatz-conjecture/src/example/kotlin/CollatzCalculator.kt +12 -0
  34. data/tracks/kotlin/exercises/collatz-conjecture/src/main/kotlin/.keep +0 -0
  35. data/tracks/kotlin/exercises/collatz-conjecture/src/test/kotlin/CollatzCalculatorTest.kt +51 -0
  36. data/tracks/kotlin/exercises/diamond/build.gradle +28 -0
  37. data/tracks/kotlin/exercises/diamond/src/example/kotlin/DiamondPrinter.kt +35 -0
  38. data/tracks/kotlin/exercises/diamond/src/main/kotlin/DiamondPrinter.kt +5 -0
  39. data/tracks/kotlin/exercises/diamond/src/test/kotlin/DiamondPrinterTest.kt +121 -0
  40. data/tracks/kotlin/exercises/settings.gradle +2 -0
  41. data/tracks/ocaml/.gitignore +1 -0
  42. data/tracks/ocaml/.vscode/launch.json +14 -0
  43. data/tracks/ocaml/tools/test-generator/src/controller.ml +15 -16
  44. data/tracks/ocaml/tools/test-generator/src/languages.ml +30 -0
  45. data/tracks/ocaml/tools/test-generator/src/ocaml_special_cases.ml +137 -0
  46. data/tracks/ocaml/tools/test-generator/src/purescript_special_cases.ml +13 -0
  47. data/tracks/ocaml/tools/test-generator/src/special_cases.ml +7 -132
  48. data/tracks/ocaml/tools/test-generator/src/template.ml +3 -3
  49. data/tracks/ocaml/tools/test-generator/src/test_gen.ml +13 -3
  50. data/tracks/ocaml/tools/test-generator/templates/{acronym → ocaml/acronym}/template.ml +0 -0
  51. data/tracks/ocaml/tools/test-generator/templates/{all-your-base → ocaml/all-your-base}/template.ml +0 -0
  52. data/tracks/ocaml/tools/test-generator/templates/{anagram → ocaml/anagram}/template.ml +0 -0
  53. data/tracks/ocaml/tools/test-generator/templates/{atbash-cipher → ocaml/atbash-cipher}/template.ml +0 -0
  54. data/tracks/ocaml/tools/test-generator/templates/{beer-song → ocaml/beer-song}/template.ml +0 -0
  55. data/tracks/ocaml/tools/test-generator/templates/{binary-search → ocaml/binary-search}/template.ml +0 -0
  56. data/tracks/ocaml/tools/test-generator/templates/{bob → ocaml/bob}/template.ml +0 -0
  57. data/tracks/ocaml/tools/test-generator/templates/{bowling → ocaml/bowling}/template.ml +0 -0
  58. data/tracks/ocaml/tools/test-generator/templates/{bracket-push → ocaml/bracket-push}/template.ml +0 -0
  59. data/tracks/ocaml/tools/test-generator/templates/{change → ocaml/change}/template.ml +0 -0
  60. data/tracks/ocaml/tools/test-generator/templates/{connect → ocaml/connect}/template.ml +0 -0
  61. data/tracks/ocaml/tools/test-generator/templates/{difference-of-squares → ocaml/difference-of-squares}/template.ml +0 -0
  62. data/tracks/ocaml/tools/test-generator/templates/{dominoes → ocaml/dominoes}/template.ml +0 -0
  63. data/tracks/ocaml/tools/test-generator/templates/{etl → ocaml/etl}/template.ml +0 -0
  64. data/tracks/ocaml/tools/test-generator/templates/{forth → ocaml/forth}/template.ml +0 -0
  65. data/tracks/ocaml/tools/test-generator/templates/{hamming → ocaml/hamming}/template.ml +0 -0
  66. data/tracks/ocaml/tools/test-generator/templates/{hello-world → ocaml/hello-world}/template.ml +0 -0
  67. data/tracks/ocaml/tools/test-generator/templates/{leap → ocaml/leap}/template.ml +0 -0
  68. data/tracks/ocaml/tools/test-generator/templates/{luhn → ocaml/luhn}/template.ml +0 -0
  69. data/tracks/ocaml/tools/test-generator/templates/{minesweeper → ocaml/minesweeper}/template.ml +0 -0
  70. data/tracks/ocaml/tools/test-generator/templates/{pangram → ocaml/pangram}/template.ml +0 -0
  71. data/tracks/ocaml/tools/test-generator/templates/{phone-number → ocaml/phone-number}/template.ml +0 -0
  72. data/tracks/ocaml/tools/test-generator/templates/{prime-factors → ocaml/prime-factors}/template.ml +0 -0
  73. data/tracks/ocaml/tools/test-generator/templates/{raindrops → ocaml/raindrops}/template.ml +0 -0
  74. data/tracks/ocaml/tools/test-generator/templates/{roman-numerals → ocaml/roman-numerals}/template.ml +0 -0
  75. data/tracks/ocaml/tools/test-generator/templates/{run-length-encoding → ocaml/run-length-encoding}/template.ml +0 -0
  76. data/tracks/ocaml/tools/test-generator/templates/{say → ocaml/say}/template.ml +0 -0
  77. data/tracks/ocaml/tools/test-generator/templates/{space-age → ocaml/space-age}/template.ml +0 -0
  78. data/tracks/ocaml/tools/test-generator/templates/{triangle → ocaml/triangle}/template.ml +0 -0
  79. data/tracks/ocaml/tools/test-generator/templates/{word-count → ocaml/word-count}/template.ml +0 -0
  80. data/tracks/ocaml/tools/test-generator/templates/purescript/hamming/Main.purs +18 -0
  81. data/tracks/ocaml/tools/test-generator/test/all_tests.ml +2 -2
  82. data/tracks/ocaml/tools/test-generator/test/{special_cases_test.ml → ocaml_special_cases_test.ml} +3 -3
  83. data/tracks/ocaml/tools/test-generator/test/template_test.ml +1 -0
  84. metadata +59 -34
  85. data/tracks/groovy/exercises/robot-name/RobotTest.groovy +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ea219b914ea27b18a90012564e931c53e884090
4
- data.tar.gz: df9c745c274ad3550a164005870ea87fd21f3280
3
+ metadata.gz: e1cea3469c06413e200a4474c5867dc75dedf961
4
+ data.tar.gz: 66119d1d8e808237e971dcaeb737b52adcfc227a
5
5
  SHA512:
6
- metadata.gz: a88822ba8bd2eeab47286b16a9fb78aa3b0e6fdc0c99052f864e4f2ae9bd2d1c55a29066273aa8b4d2b8aac598de04cac5a0dd871a18b13bba7cb2271562e4de
7
- data.tar.gz: 2f52002a03364a07944ff01a903c4da38d9859362179e043acbb7701ab03beae1eb03a800b9bc034518b732029889e86c4603a1b85c1190d5eb5755d0489c9e9
6
+ metadata.gz: 6557449b11d4872505295d57268867e5f4cdcb2b6aa6e13be6ac28b3ad2bf97c20cc51bb39a076425ebc0f004ef70d4d26d57b9a738e389e9d070efe6547f1cc
7
+ data.tar.gz: 977e03371cbdeddb0307091430f425b039be3a52705353b27a0ad0bf272e13211d10fdd246ef576c7c56d5338551e2e3810c52608a29d2a829c78f29f2841df9
@@ -691,21 +691,6 @@ Optional keys:
691
691
  * `ignore_pattern` - A (case insensitive) regex pattern that will cause files matching it to not be served to the student by `exercism fetch`. The default value used if this key is not present is `example`
692
692
  * `solution_pattern` - A (case sensitive) regex pattern that matches solution files in the track repository. Used by [configlet](https://github.com/exercism/configlet) to check for the presence of an example solution for each problem implemented by the track. The default value used if this key is not present is `[Ee]xample`.
693
693
 
694
- ### Track-Level Linting With Configlet
695
-
696
- If the `config.json` file is incomplete or broken, a lot of other things break.
697
- To make things easier we made a small tool to help verify the config:
698
- https://github.com/exercism/configlet#configlet
699
-
700
- You can download the latest release from the releases page in the [configlet
701
- repo](https://github.com/exercism/configlet/releases), or you can use the
702
- `bin/fetch-configlet` command from the root of the language track repository,
703
- which will make a guess at what operating system and architecture you have and
704
- attempt to download the right one.
705
-
706
- Verify the config by calling `bin/configlet .` (notice the dot). This says
707
- _check the config of the language track that is stored right here).
708
-
709
694
  ### Git Basics
710
695
 
711
696
  If you're concerned that you haven't done it right, don't worry. Submit your pull request, and we'll help you get the details sorted out.
@@ -1,6 +1,19 @@
1
1
  {
2
2
  "exercise": "secret-handshake",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
+ "comments": [
5
+ " In a discussion in https://github.com/exercism/x-common/pull/794 and ",
6
+ " https://github.com/exercism/x-common/issues/335 it has been decided to ",
7
+ " only include numbers between 0 and 31 (00000 to 11111) in the canonical ",
8
+ " test data. ",
9
+ " ",
10
+ " This is to allow for different implementations in different tracks and ",
11
+ " not restrict solutions to bitwise or modulo-based algorithms. ",
12
+ " ",
13
+ " Tracks may include additional tests for numbers > 31 in their test ",
14
+ " suites. In this case, 32 (100000) should yield the same result as 0, ",
15
+ " 33 (100001) should yield the same result as 1, and so on. "
16
+ ],
4
17
  "cases": [
5
18
  {
6
19
  "description": "Create a handshake for a number",
@@ -70,12 +83,6 @@
70
83
  "property": "commands",
71
84
  "input": 0,
72
85
  "expected": []
73
- },
74
- {
75
- "description": "do nothing if lower 5 bits not set",
76
- "property": "commands",
77
- "input": 32,
78
- "expected": []
79
86
  }
80
87
  ]
81
88
  }
@@ -1,3 +1,3 @@
1
1
  module Trackler
2
- VERSION = "2.1.0.26"
2
+ VERSION = "2.1.0.27"
3
3
  end
@@ -12,6 +12,13 @@
12
12
  "language basics"
13
13
  ]
14
14
  },
15
+ {
16
+ "slug": "leap",
17
+ "difficulty": 1,
18
+ "topics": [
19
+ "language basics"
20
+ ]
21
+ },
15
22
  {
16
23
  "slug": "gigasecond" ,
17
24
  "difficulty": 1,
@@ -139,6 +146,17 @@
139
146
  "string manipulation",
140
147
  "control-flow"
141
148
  ]
149
+ },
150
+ {
151
+ "slug": "react" ,
152
+ "difficulty": 10,
153
+ "topics": [
154
+ "reactive programming",
155
+ "generics",
156
+ "nested classes",
157
+ "higher-order functions",
158
+ "delegates"
159
+ ]
142
160
  }
143
161
  ],
144
162
  "deprecated": [
@@ -0,0 +1,8 @@
1
+ unittest {
2
+ assert(!is_leap(2015));
3
+ assert(is_leap(2016));
4
+ assert(!is_leap(2100));
5
+ assert(is_leap(2000));
6
+ }
7
+
8
+ void main() {}
@@ -0,0 +1,15 @@
1
+ bool is_leap(uint year) {
2
+ bool div_by(uint n) {
3
+ return year % n == 0;
4
+ }
5
+ return div_by(4) && (!div_by(100) || div_by(400));
6
+ }
7
+
8
+ unittest {
9
+ assert(!is_leap(2015));
10
+ assert(is_leap(2016));
11
+ assert(!is_leap(2100));
12
+ assert(is_leap(2000));
13
+ }
14
+
15
+ void main() {}
@@ -0,0 +1,211 @@
1
+ module react;
2
+
3
+ unittest {
4
+ const int allTestsEnabled = 0;
5
+
6
+ {
7
+ // input cells have a value
8
+ Reactor!(int) r;
9
+ auto input = r.new InputCell(10);
10
+
11
+ assert(input.value == 10);
12
+ }
13
+ static if (allTestsEnabled) {
14
+ {
15
+ // an input cell's value can be set
16
+ Reactor!(int) r;
17
+ auto input = r.new InputCell(4);
18
+
19
+ input.value = 20;
20
+ assert(input.value == 20);
21
+ }
22
+ {
23
+ // compute cells calculate initial value
24
+ Reactor!(int) r;
25
+ auto input = r.new InputCell(1);
26
+ auto output = r.new ComputeCell(input, (x) => x + 1);
27
+
28
+ assert(output.value == 2);
29
+ }
30
+ {
31
+ // compute cells take inputs in the right order
32
+ Reactor!(int) r;
33
+ auto one = r.new InputCell(1);
34
+ auto two = r.new InputCell(2);
35
+ auto output = r.new ComputeCell(one, two, (x, y) => x + y * 10);
36
+
37
+ assert(output.value == 21);
38
+ }
39
+ {
40
+ // compute cells update value when dependencies are changed
41
+ Reactor!(int) r;
42
+ auto input = r.new InputCell(1);
43
+ auto output = r.new ComputeCell(input, (x) => x + 1);
44
+
45
+ input.value = 3;
46
+ assert(output.value == 4);
47
+ }
48
+ {
49
+ // compute cells can depend on other compute cells
50
+ Reactor!(int) r;
51
+ auto input = r.new InputCell(1);
52
+ auto timesTwo = r.new ComputeCell(input, (x) => x * 2);
53
+ auto timesThirty = r.new ComputeCell(input, (x) => x * 30);
54
+ auto output = r.new ComputeCell(timesTwo, timesThirty, (x, y) => x + y);
55
+
56
+ assert(output.value == 32);
57
+ input.value = 3;
58
+ assert(output.value == 96);
59
+ }
60
+ {
61
+ // compute cells fire callbacks
62
+ Reactor!(int) r;
63
+ auto input = r.new InputCell(1);
64
+ auto output = r.new ComputeCell(input, (x) => x + 1);
65
+ int[] vals;
66
+
67
+ output.addCallback((int x) { vals ~= [x]; return; });
68
+
69
+ input.value = 3;
70
+ assert(vals.length == 1);
71
+ assert(vals[0] == 4);
72
+ }
73
+ {
74
+ // compute cells only fire on change
75
+ Reactor!(int) r;
76
+ auto input = r.new InputCell(1);
77
+ auto output = r.new ComputeCell(input, (x) => x < 3 ? 111 : 222);
78
+ int[] vals;
79
+
80
+ output.addCallback((int x) { vals ~= [x]; return; });
81
+
82
+ input.value = 2;
83
+ assert(vals.length == 0);
84
+ input.value = 3;
85
+ assert(vals.length == 1);
86
+ assert(vals[0] == 222);
87
+ }
88
+ {
89
+ // callbacks can be added and removed
90
+ Reactor!(int) r;
91
+ auto input = r.new InputCell(11);
92
+ auto output = r.new ComputeCell(input, (x) => x + 1);
93
+ int[] vals1;
94
+ int[] vals2;
95
+ int[] vals3;
96
+
97
+ void delegate() cancel1 = output.addCallback((int x) { vals1 ~= [x]; return; });
98
+ output.addCallback((int x) { vals2 ~= [x]; return; });
99
+
100
+ input.value = 31;
101
+
102
+ cancel1();
103
+ output.addCallback((int x) { vals3 ~= [x]; return; });
104
+
105
+ input.value = 41;
106
+
107
+ assert(vals1.length == 1);
108
+ assert(vals1[0] == 32);
109
+ assert(vals2.length == 2);
110
+ assert(vals2[0] == 32);
111
+ assert(vals2[1] == 42);
112
+ assert(vals3.length == 1);
113
+ assert(vals3[0] == 42);
114
+ }
115
+ {
116
+ // removing a callback multiple times doesn't interfere with other callbacks
117
+ Reactor!(int) r;
118
+ auto input = r.new InputCell(1);
119
+ auto output = r.new ComputeCell(input, (x) => x + 1);
120
+ int[] vals1;
121
+ int[] vals2;
122
+
123
+ void delegate() cancel1 = output.addCallback((int x) { vals1 ~= [x]; return; });
124
+ output.addCallback((int x) { vals2 ~= [x]; return; });
125
+
126
+ foreach (i; 0 .. 10) {
127
+ cancel1();
128
+ }
129
+
130
+ input.value = 2;
131
+
132
+ assert(vals1.length == 0);
133
+ assert(vals2.length == 1);
134
+ assert(vals2[0] == 3);
135
+ }
136
+ {
137
+ // callbacks should only be called once even if multiple dependencies change
138
+ Reactor!(int) r;
139
+ auto input = r.new InputCell(1);
140
+ auto plusOne = r.new ComputeCell(input, (x) => x + 1);
141
+ auto minusOne1 = r.new ComputeCell(input, (x) => x - 1);
142
+ auto minusOne2 = r.new ComputeCell(minusOne1, (x) => x - 1);
143
+ auto output = r.new ComputeCell(plusOne, minusOne2, (x, y) => x * y);
144
+ int[] vals;
145
+
146
+ output.addCallback((int x) { vals ~= [x]; return; });
147
+
148
+ input.value = 4;
149
+
150
+ assert(vals.length == 1);
151
+ assert(vals[0] == 10);
152
+ }
153
+ {
154
+ // callbacks should not be called if dependencies change but output value doesn't change
155
+ Reactor!(int) r;
156
+ auto input = r.new InputCell(1);
157
+ auto plusOne = r.new ComputeCell(input, (x) => x + 1);
158
+ auto minusOne = r.new ComputeCell(input, (x) => x - 1);
159
+ auto alwaysTwo = r.new ComputeCell(plusOne, minusOne, (x, y) => x - y);
160
+ int[] vals;
161
+
162
+ alwaysTwo.addCallback((int x) { vals ~= [x]; return; });
163
+
164
+ foreach (i; 0 .. 10) {
165
+ input.value = i;
166
+ }
167
+
168
+ assert(vals.length == 0);
169
+ }
170
+ {
171
+ // This is a digital logic circuit called an adder:
172
+ // https://en.wikipedia.org/wiki/Adder_(electronics)
173
+ Reactor!(bool) r;
174
+ auto a = r.new InputCell(false);
175
+ auto b = r.new InputCell(false);
176
+ auto carryIn = r.new InputCell(false);
177
+
178
+ auto aXorB = r.new ComputeCell(a, b, (x, y) => x != y);
179
+ auto sum = r.new ComputeCell(aXorB, carryIn, (x, y) => x != y);
180
+
181
+ auto aXorBAndCin = r.new ComputeCell(aXorB, carryIn, (x, y) => x && y);
182
+ auto aAndB = r.new ComputeCell(a, b, (x, y) => x && y);
183
+ auto carryOut = r.new ComputeCell(aXorBAndCin, aAndB, (x, y) => x || y);
184
+
185
+ bool[5][] tests = [
186
+ // inputs, expected
187
+ // a, b, cin, cout, sum
188
+ [false, false, false, false, false],
189
+ [false, false, true, false, true],
190
+ [false, true, false, false, true],
191
+ [false, true, true, true, false],
192
+ [ true, false, false, false, true],
193
+ [ true, false, true, true, false],
194
+ [ true, true, false, true, false],
195
+ [ true, true, true, true, true],
196
+ ];
197
+
198
+ foreach (test; tests) {
199
+ a.value = test[0];
200
+ b.value = test[1];
201
+ carryIn.value = test[2];
202
+
203
+ assert(carryOut.value == test[3]);
204
+ assert(sum.value == test[4]);
205
+ }
206
+ }
207
+ }
208
+
209
+ }
210
+
211
+ void main() {}
@@ -0,0 +1,291 @@
1
+ module react;
2
+
3
+ class Reactor(T) {
4
+ class Cell {
5
+ @property T value() { return m_value; }
6
+ private:
7
+ ComputeCell[] dependents;
8
+ T m_value;
9
+ }
10
+
11
+ class InputCell: Cell {
12
+ // If this override getter is not present,
13
+ // then value becomes write-only.
14
+ override @property T value() { return super.value; }
15
+ @property T value(T newValue) {
16
+ m_value = newValue;
17
+ foreach (dep; dependents) {
18
+ dep.propagate();
19
+ }
20
+ foreach (dep; dependents) {
21
+ dep.fireCallbacks();
22
+ }
23
+ return newValue;
24
+ }
25
+
26
+ this(T initialValue) {
27
+ m_value = initialValue;
28
+ }
29
+ }
30
+
31
+ class ComputeCell: Cell {
32
+ this(Cell c, T function(T) f) {
33
+ c.dependents ~= [this];
34
+ this(() => f(c.value));
35
+ }
36
+
37
+ this(Cell c1, Cell c2, T function(T, T) f) {
38
+ c1.dependents ~= [this];
39
+ c2.dependents ~= [this];
40
+ this(() => f(c1.value, c2.value));
41
+ }
42
+
43
+ void delegate() addCallback(void delegate(T) cb) {
44
+ int id = callbacksIssued++;
45
+ callbacks[id] = cb;
46
+ return () { callbacks.remove(id); };
47
+ }
48
+
49
+ private:
50
+
51
+ T delegate() compute;
52
+ T lastCallbackValue;
53
+ void delegate(T)[int] callbacks;
54
+ int callbacksIssued = 0;
55
+
56
+ this(T delegate() f) {
57
+ compute = f;
58
+ m_value = compute();
59
+ lastCallbackValue = m_value;
60
+ }
61
+
62
+ void propagate() {
63
+ T newValue = compute();
64
+ if (value == newValue) {
65
+ return;
66
+ }
67
+ m_value = newValue;
68
+ foreach (dep; dependents) {
69
+ dep.propagate();
70
+ }
71
+ }
72
+
73
+ void fireCallbacks() {
74
+ if (value == lastCallbackValue) {
75
+ return;
76
+ }
77
+ lastCallbackValue = value;
78
+ foreach (cb; callbacks) {
79
+ cb(value);
80
+ }
81
+ foreach (dep; dependents) {
82
+ dep.fireCallbacks();
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ unittest {
89
+ {
90
+ // input cells have a value
91
+ Reactor!(int) r;
92
+ auto input = r.new InputCell(10);
93
+
94
+ assert(input.value == 10);
95
+ }
96
+ {
97
+ // an input cell's value can be set
98
+ Reactor!(int) r;
99
+ auto input = r.new InputCell(4);
100
+
101
+ input.value = 20;
102
+ assert(input.value == 20);
103
+ }
104
+ {
105
+ // compute cells calculate initial value
106
+ Reactor!(int) r;
107
+ auto input = r.new InputCell(1);
108
+ auto output = r.new ComputeCell(input, (x) => x + 1);
109
+
110
+ assert(output.value == 2);
111
+ }
112
+ {
113
+ // compute cells take inputs in the right order
114
+ Reactor!(int) r;
115
+ auto one = r.new InputCell(1);
116
+ auto two = r.new InputCell(2);
117
+ auto output = r.new ComputeCell(one, two, (x, y) => x + y * 10);
118
+
119
+ assert(output.value == 21);
120
+ }
121
+ {
122
+ // compute cells update value when dependencies are changed
123
+ Reactor!(int) r;
124
+ auto input = r.new InputCell(1);
125
+ auto output = r.new ComputeCell(input, (x) => x + 1);
126
+
127
+ input.value = 3;
128
+ assert(output.value == 4);
129
+ }
130
+ {
131
+ // compute cells can depend on other compute cells
132
+ Reactor!(int) r;
133
+ auto input = r.new InputCell(1);
134
+ auto timesTwo = r.new ComputeCell(input, (x) => x * 2);
135
+ auto timesThirty = r.new ComputeCell(input, (x) => x * 30);
136
+ auto output = r.new ComputeCell(timesTwo, timesThirty, (x, y) => x + y);
137
+
138
+ assert(output.value == 32);
139
+ input.value = 3;
140
+ assert(output.value == 96);
141
+ }
142
+ {
143
+ // compute cells fire callbacks
144
+ Reactor!(int) r;
145
+ auto input = r.new InputCell(1);
146
+ auto output = r.new ComputeCell(input, (x) => x + 1);
147
+ int[] vals;
148
+
149
+ output.addCallback((int x) { vals ~= [x]; return; });
150
+
151
+ input.value = 3;
152
+ assert(vals.length == 1);
153
+ assert(vals[0] == 4);
154
+ }
155
+ {
156
+ // compute cells only fire on change
157
+ Reactor!(int) r;
158
+ auto input = r.new InputCell(1);
159
+ auto output = r.new ComputeCell(input, (x) => x < 3 ? 111 : 222);
160
+ int[] vals;
161
+
162
+ output.addCallback((int x) { vals ~= [x]; return; });
163
+
164
+ input.value = 2;
165
+ assert(vals.length == 0);
166
+ input.value = 3;
167
+ assert(vals.length == 1);
168
+ assert(vals[0] == 222);
169
+ }
170
+ {
171
+ // callbacks can be added and removed
172
+ Reactor!(int) r;
173
+ auto input = r.new InputCell(11);
174
+ auto output = r.new ComputeCell(input, (x) => x + 1);
175
+ int[] vals1;
176
+ int[] vals2;
177
+ int[] vals3;
178
+
179
+ void delegate() cancel1 = output.addCallback((int x) { vals1 ~= [x]; return; });
180
+ output.addCallback((int x) { vals2 ~= [x]; return; });
181
+
182
+ input.value = 31;
183
+
184
+ cancel1();
185
+ output.addCallback((int x) { vals3 ~= [x]; return; });
186
+
187
+ input.value = 41;
188
+
189
+ assert(vals1.length == 1);
190
+ assert(vals1[0] == 32);
191
+ assert(vals2.length == 2);
192
+ assert(vals2[0] == 32);
193
+ assert(vals2[1] == 42);
194
+ assert(vals3.length == 1);
195
+ assert(vals3[0] == 42);
196
+ }
197
+ {
198
+ // removing a callback multiple times doesn't interfere with other callbacks
199
+ Reactor!(int) r;
200
+ auto input = r.new InputCell(1);
201
+ auto output = r.new ComputeCell(input, (x) => x + 1);
202
+ int[] vals1;
203
+ int[] vals2;
204
+
205
+ void delegate() cancel1 = output.addCallback((int x) { vals1 ~= [x]; return; });
206
+ output.addCallback((int x) { vals2 ~= [x]; return; });
207
+
208
+ foreach (i; 0 .. 10) {
209
+ cancel1();
210
+ }
211
+
212
+ input.value = 2;
213
+
214
+ assert(vals1.length == 0);
215
+ assert(vals2.length == 1);
216
+ assert(vals2[0] == 3);
217
+ }
218
+ {
219
+ // callbacks should only be called once even if multiple dependencies change
220
+ Reactor!(int) r;
221
+ auto input = r.new InputCell(1);
222
+ auto plusOne = r.new ComputeCell(input, (x) => x + 1);
223
+ auto minusOne1 = r.new ComputeCell(input, (x) => x - 1);
224
+ auto minusOne2 = r.new ComputeCell(minusOne1, (x) => x - 1);
225
+ auto output = r.new ComputeCell(plusOne, minusOne2, (x, y) => x * y);
226
+ int[] vals;
227
+
228
+ output.addCallback((int x) { vals ~= [x]; return; });
229
+
230
+ input.value = 4;
231
+
232
+ assert(vals.length == 1);
233
+ assert(vals[0] == 10);
234
+ }
235
+ {
236
+ // callbacks should not be called if dependencies change but output value doesn't change
237
+ Reactor!(int) r;
238
+ auto input = r.new InputCell(1);
239
+ auto plusOne = r.new ComputeCell(input, (x) => x + 1);
240
+ auto minusOne = r.new ComputeCell(input, (x) => x - 1);
241
+ auto alwaysTwo = r.new ComputeCell(plusOne, minusOne, (x, y) => x - y);
242
+ int[] vals;
243
+
244
+ alwaysTwo.addCallback((int x) { vals ~= [x]; return; });
245
+
246
+ foreach (i; 0 .. 10) {
247
+ input.value = i;
248
+ }
249
+
250
+ assert(vals.length == 0);
251
+ }
252
+ {
253
+ // This is a digital logic circuit called an adder:
254
+ // https://en.wikipedia.org/wiki/Adder_(electronics)
255
+ Reactor!(bool) r;
256
+ auto a = r.new InputCell(false);
257
+ auto b = r.new InputCell(false);
258
+ auto carryIn = r.new InputCell(false);
259
+
260
+ auto aXorB = r.new ComputeCell(a, b, (x, y) => x != y);
261
+ auto sum = r.new ComputeCell(aXorB, carryIn, (x, y) => x != y);
262
+
263
+ auto aXorBAndCin = r.new ComputeCell(aXorB, carryIn, (x, y) => x && y);
264
+ auto aAndB = r.new ComputeCell(a, b, (x, y) => x && y);
265
+ auto carryOut = r.new ComputeCell(aXorBAndCin, aAndB, (x, y) => x || y);
266
+
267
+ bool[5][] tests = [
268
+ // inputs, expected
269
+ // a, b, cin, cout, sum
270
+ [false, false, false, false, false],
271
+ [false, false, true, false, true],
272
+ [false, true, false, false, true],
273
+ [false, true, true, true, false],
274
+ [ true, false, false, false, true],
275
+ [ true, false, true, true, false],
276
+ [ true, true, false, true, false],
277
+ [ true, true, true, true, true],
278
+ ];
279
+
280
+ foreach (test; tests) {
281
+ a.value = test[0];
282
+ b.value = test[1];
283
+ carryIn.value = test[2];
284
+
285
+ assert(carryOut.value == test[3]);
286
+ assert(sum.value == test[4]);
287
+ }
288
+ }
289
+ }
290
+
291
+ void main() {}