trackler 2.2.1.135 → 2.2.1.136

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/pov/canonical-data.json +33 -1
  4. data/tracks/c/exercises/acronym/test/test_acronym.c +25 -17
  5. data/tracks/csharp/exercises/binary-search-tree/BinarySearchTreeTest.cs +37 -28
  6. data/tracks/csharp/generators/Exercises/BinarySearchTree.cs +69 -0
  7. data/tracks/haskell/config.json +1 -0
  8. data/tracks/haskell/exercises/accumulate/package.yaml +1 -1
  9. data/tracks/haskell/exercises/acronym/package.yaml +1 -1
  10. data/tracks/haskell/exercises/all-your-base/package.yaml +1 -1
  11. data/tracks/haskell/exercises/allergies/package.yaml +1 -1
  12. data/tracks/haskell/exercises/alphametics/package.yaml +1 -1
  13. data/tracks/haskell/exercises/anagram/package.yaml +1 -1
  14. data/tracks/haskell/exercises/atbash-cipher/package.yaml +1 -1
  15. data/tracks/haskell/exercises/bank-account/package.yaml +1 -1
  16. data/tracks/haskell/exercises/beer-song/package.yaml +1 -1
  17. data/tracks/haskell/exercises/binary-search-tree/package.yaml +1 -1
  18. data/tracks/haskell/exercises/binary/package.yaml +1 -1
  19. data/tracks/haskell/exercises/bob/package.yaml +1 -1
  20. data/tracks/haskell/exercises/bowling/package.yaml +1 -1
  21. data/tracks/haskell/exercises/bracket-push/package.yaml +1 -1
  22. data/tracks/haskell/exercises/change/package.yaml +1 -1
  23. data/tracks/haskell/exercises/clock/package.yaml +1 -1
  24. data/tracks/haskell/exercises/collatz-conjecture/package.yaml +1 -1
  25. data/tracks/haskell/exercises/complex-numbers/package.yaml +1 -1
  26. data/tracks/haskell/exercises/connect/package.yaml +1 -1
  27. data/tracks/haskell/exercises/crypto-square/package.yaml +1 -1
  28. data/tracks/haskell/exercises/custom-set/package.yaml +1 -1
  29. data/tracks/haskell/exercises/diamond/package.yaml +1 -1
  30. data/tracks/haskell/exercises/difference-of-squares/package.yaml +1 -1
  31. data/tracks/haskell/exercises/dominoes/package.yaml +1 -1
  32. data/tracks/haskell/exercises/etl/package.yaml +1 -1
  33. data/tracks/haskell/exercises/food-chain/package.yaml +1 -1
  34. data/tracks/haskell/exercises/forth/package.yaml +1 -1
  35. data/tracks/haskell/exercises/gigasecond/package.yaml +1 -1
  36. data/tracks/haskell/exercises/go-counting/package.yaml +1 -1
  37. data/tracks/haskell/exercises/grade-school/package.yaml +1 -1
  38. data/tracks/haskell/exercises/grains/package.yaml +1 -1
  39. data/tracks/haskell/exercises/hamming/package.yaml +1 -1
  40. data/tracks/haskell/exercises/hello-world/package.yaml +1 -1
  41. data/tracks/haskell/exercises/hexadecimal/package.yaml +1 -1
  42. data/tracks/haskell/exercises/house/package.yaml +1 -1
  43. data/tracks/haskell/exercises/isbn-verifier/package.yaml +1 -1
  44. data/tracks/haskell/exercises/isogram/package.yaml +1 -1
  45. data/tracks/haskell/exercises/kindergarten-garden/examples/success-standard/src/Garden.hs +1 -10
  46. data/tracks/haskell/exercises/kindergarten-garden/package.yaml +2 -3
  47. data/tracks/haskell/exercises/kindergarten-garden/src/Garden.hs +3 -7
  48. data/tracks/haskell/exercises/kindergarten-garden/test/Tests.hs +10 -16
  49. data/tracks/haskell/exercises/largest-series-product/package.yaml +1 -1
  50. data/tracks/haskell/exercises/leap/package.yaml +1 -1
  51. data/tracks/haskell/exercises/lens-person/examples/success-standard/package.yaml +0 -1
  52. data/tracks/haskell/exercises/lens-person/package.yaml +1 -1
  53. data/tracks/haskell/exercises/linked-list/package.yaml +1 -1
  54. data/tracks/haskell/exercises/list-ops/package.yaml +1 -1
  55. data/tracks/haskell/exercises/luhn/package.yaml +1 -1
  56. data/tracks/haskell/exercises/matrix/package.yaml +1 -1
  57. data/tracks/haskell/exercises/meetup/package.yaml +1 -1
  58. data/tracks/haskell/exercises/minesweeper/package.yaml +1 -1
  59. data/tracks/haskell/exercises/nth-prime/package.yaml +1 -1
  60. data/tracks/haskell/exercises/nucleotide-count/package.yaml +1 -1
  61. data/tracks/haskell/exercises/ocr-numbers/package.yaml +1 -1
  62. data/tracks/haskell/exercises/octal/package.yaml +1 -1
  63. data/tracks/haskell/exercises/palindrome-products/package.yaml +1 -1
  64. data/tracks/haskell/exercises/pangram/package.yaml +1 -1
  65. data/tracks/haskell/exercises/parallel-letter-frequency/package.yaml +1 -1
  66. data/tracks/haskell/exercises/pascals-triangle/package.yaml +1 -1
  67. data/tracks/haskell/exercises/perfect-numbers/package.yaml +1 -1
  68. data/tracks/haskell/exercises/phone-number/package.yaml +1 -1
  69. data/tracks/haskell/exercises/pig-latin/package.yaml +1 -1
  70. data/tracks/haskell/exercises/poker/package.yaml +1 -1
  71. data/tracks/haskell/exercises/pov/package.yaml +2 -2
  72. data/tracks/haskell/exercises/pov/test/Tests.hs +15 -0
  73. data/tracks/haskell/exercises/prime-factors/package.yaml +1 -1
  74. data/tracks/haskell/exercises/protein-translation/package.yaml +1 -1
  75. data/tracks/haskell/exercises/pythagorean-triplet/package.yaml +1 -1
  76. data/tracks/haskell/exercises/queen-attack/package.yaml +1 -1
  77. data/tracks/haskell/exercises/rail-fence-cipher/package.yaml +1 -1
  78. data/tracks/haskell/exercises/raindrops/package.yaml +1 -1
  79. data/tracks/haskell/exercises/rna-transcription/package.yaml +1 -1
  80. data/tracks/haskell/exercises/robot-name/package.yaml +1 -1
  81. data/tracks/haskell/exercises/robot-simulator/package.yaml +1 -1
  82. data/tracks/haskell/exercises/roman-numerals/package.yaml +1 -1
  83. data/tracks/haskell/exercises/rotational-cipher/package.yaml +1 -1
  84. data/tracks/haskell/exercises/run-length-encoding/package.yaml +1 -1
  85. data/tracks/haskell/exercises/saddle-points/package.yaml +1 -1
  86. data/tracks/haskell/exercises/say/package.yaml +1 -1
  87. data/tracks/haskell/exercises/scrabble-score/package.yaml +1 -1
  88. data/tracks/haskell/exercises/secret-handshake/package.yaml +1 -1
  89. data/tracks/haskell/exercises/series/package.yaml +1 -1
  90. data/tracks/haskell/exercises/sgf-parsing/package.yaml +1 -1
  91. data/tracks/haskell/exercises/sieve/package.yaml +1 -1
  92. data/tracks/haskell/exercises/simple-cipher/package.yaml +1 -1
  93. data/tracks/haskell/exercises/simple-linked-list/package.yaml +1 -1
  94. data/tracks/haskell/exercises/space-age/package.yaml +1 -1
  95. data/tracks/haskell/exercises/spiral-matrix/package.yaml +1 -1
  96. data/tracks/haskell/exercises/strain/package.yaml +1 -1
  97. data/tracks/haskell/exercises/sublist/package.yaml +1 -1
  98. data/tracks/haskell/exercises/sum-of-multiples/package.yaml +1 -1
  99. data/tracks/haskell/exercises/transpose/package.yaml +1 -1
  100. data/tracks/haskell/exercises/triangle/package.yaml +1 -1
  101. data/tracks/haskell/exercises/trinary/package.yaml +1 -1
  102. data/tracks/haskell/exercises/twelve-days/package.yaml +1 -1
  103. data/tracks/haskell/exercises/word-count/package.yaml +1 -1
  104. data/tracks/haskell/exercises/wordy/package.yaml +1 -1
  105. data/tracks/haskell/exercises/zebra-puzzle/package.yaml +1 -1
  106. data/tracks/haskell/exercises/zipper/package.yaml +1 -1
  107. data/tracks/java/CONTRIBUTING.md +39 -3
  108. data/tracks/java/POLICIES.md +68 -3
  109. data/tracks/java/exercises/crypto-square/README.md +4 -5
  110. data/tracks/java/exercises/error-handling/README.md +38 -0
  111. data/tracks/java/exercises/series/README.md +6 -6
  112. data/tracks/java/exercises/sieve/README.md +5 -3
  113. data/tracks/java/scripts/canonical_data_check.sh +1 -1
  114. data/tracks/python/exercises/react/example.py +54 -63
  115. data/tracks/python/exercises/react/react.py +10 -10
  116. data/tracks/python/exercises/react/react_test.py +197 -131
  117. data/tracks/python/exercises/sieve/README.md +5 -3
  118. data/tracks/rust/exercises/react/example.rs +103 -64
  119. data/tracks/rust/exercises/react/src/lib.rs +31 -17
  120. data/tracks/rust/exercises/react/tests/react.rs +52 -61
  121. data/tracks/swift/config.json +11 -0
  122. data/tracks/swift/exercises/circular-buffer/Sources/CircularBufferExample.swift +12 -12
  123. data/tracks/swift/exercises/circular-buffer/Tests/CircularBufferTests/CircularBufferTests.swift +1 -1
  124. data/tracks/swift/exercises/proverb/Package.swift +5 -0
  125. data/tracks/swift/exercises/proverb/README.md +25 -0
  126. data/tracks/swift/exercises/proverb/Sources/Proverb.swift +1 -0
  127. data/tracks/swift/exercises/proverb/Sources/ProverbExample.swift +28 -0
  128. data/tracks/swift/exercises/proverb/Tests/LinuxMain.swift +6 -0
  129. data/tracks/swift/exercises/proverb/Tests/ProverbTests/ProverbTests.swift +61 -0
  130. data/tracks/typescript/config.json +14 -0
  131. data/tracks/typescript/exercises/palindrome-products/README.md +65 -0
  132. data/tracks/typescript/exercises/palindrome-products/package.json +36 -0
  133. data/tracks/typescript/exercises/palindrome-products/palindrome-products.example.ts +38 -0
  134. data/tracks/typescript/exercises/palindrome-products/palindrome-products.test.ts +69 -0
  135. data/tracks/typescript/exercises/palindrome-products/palindrome-products.ts +0 -0
  136. data/tracks/typescript/exercises/palindrome-products/tsconfig.json +22 -0
  137. data/tracks/typescript/exercises/palindrome-products/tslint.json +127 -0
  138. data/tracks/typescript/exercises/palindrome-products/yarn.lock +2624 -0
  139. metadata +17 -2
@@ -1,17 +1,17 @@
1
- class Cell(object):
2
- def set_value(value):
3
- pass
1
+ class InputCell(object):
2
+ def __init__(self, initial_value):
3
+ self.value = None
4
4
 
5
- def add_watcher(self, watcher_callback):
6
- pass
7
5
 
8
- def remove_watcher(self, watcher_callback):
9
- pass
6
+ class ComputeCell(object):
7
+ def __init__(self, inputs, compute_function):
8
+ self.value = None
10
9
 
10
+ def add_callback(self, callback):
11
+ pass
11
12
 
12
- class Reactor(object):
13
- def create_input_cell(self, value):
13
+ def remove_callback(self, callback):
14
14
  pass
15
15
 
16
- def create_compute_cell(self, dependencies, updater_callback):
16
+ def expect_callback_values(self, callback):
17
17
  pass
@@ -1,135 +1,201 @@
1
1
  import unittest
2
-
3
- from react import Reactor
4
-
5
-
6
- class CallbackManager(object):
7
- counter = 0
8
- observed1 = []
9
- observed2 = []
10
-
11
- @staticmethod
12
- def reset():
13
- CallbackManager.counter = 0
14
- CallbackManager.observed1 = []
15
- CallbackManager.observed2 = []
16
-
17
- @staticmethod
18
- def count(sender, value):
19
- CallbackManager.counter += 1
20
-
21
- @staticmethod
22
- def observe1(sender, value):
23
- CallbackManager.observed1.append(value)
24
-
25
- @staticmethod
26
- def observe2(sender, value):
27
- CallbackManager.observed2.append(value)
28
-
29
-
30
- def increment(values):
31
- return values[0] + 1
32
-
33
-
34
- def decrement(values):
35
- return values[0] - 1
36
-
37
-
38
- def product(values):
39
- return values[0] * values[1]
40
-
41
-
42
- def minimum_of_2(values):
43
- return values[0] + 1 if values[0] > 2 else 2
44
-
45
-
46
- class ReactTest(unittest.TestCase):
47
- def test_setting_input_changes_value(self):
48
- reactor = Reactor()
49
- inputCell1 = reactor.create_input_cell(1)
50
- self.assertEqual(inputCell1.value, 1)
51
- inputCell1.set_value(2)
52
- self.assertEqual(inputCell1.value, 2)
53
-
54
- def test_computed_cell_determined_by_dependencies(self):
55
- reactor = Reactor()
56
- inputCell1 = reactor.create_input_cell(1)
57
- computeCell1 = reactor.create_compute_cell({inputCell1}, increment)
58
-
59
- self.assertEqual(computeCell1.value, 2)
60
- inputCell1.set_value(2)
61
- self.assertEqual(computeCell1.value, 3)
62
-
63
- def test_compute_cells_determined_by_other_compute_cells(self):
64
- reactor = Reactor()
65
- inputCell1 = reactor.create_input_cell(1)
66
- computeCell1 = reactor.create_compute_cell({inputCell1}, increment)
67
- computeCell2 = reactor.create_compute_cell({inputCell1}, decrement)
68
- computeCell3 = reactor.create_compute_cell({computeCell1,
69
- computeCell2},
70
- product)
71
- self.assertEqual(computeCell3.value, 0)
72
- inputCell1.set_value(3)
73
- self.assertEqual(computeCell3.value, 8)
74
-
75
- def test_compute_cells_can_have_callbacks(self):
76
- reactor = Reactor()
77
- inputCell1 = reactor.create_input_cell(1)
78
- computeCell1 = reactor.create_compute_cell({inputCell1}, increment)
79
- observed = []
80
- computeCell1.add_watcher(lambda sender, value: observed.append(value))
81
- self.assertEqual(observed, [])
82
- inputCell1.set_value(2)
83
- self.assertEqual(observed, [3])
84
-
85
- def test_callbacks__only_trigger_on_change(self):
86
- reactor = Reactor()
87
- inputCell1 = reactor.create_input_cell(1)
88
- computeCell1 = reactor.create_compute_cell({inputCell1}, minimum_of_2)
89
-
90
- CallbackManager.reset()
91
- computeCell1.add_watcher(CallbackManager.count)
92
-
93
- inputCell1.set_value(1)
94
- self.assertEqual(CallbackManager.counter, 0)
95
- inputCell1.set_value(2)
96
- self.assertEqual(CallbackManager.counter, 0)
97
- inputCell1.set_value(3)
98
- self.assertEqual(CallbackManager.counter, 1)
99
-
100
- def test_callbacks_can_be_removed(self):
101
- reactor = Reactor()
102
- inputCell1 = reactor.create_input_cell(1)
103
- computeCell1 = reactor.create_compute_cell({inputCell1}, increment)
104
-
105
- CallbackManager.reset()
106
- computeCell1.add_watcher(CallbackManager.observe1)
107
- computeCell1.add_watcher(CallbackManager.observe2)
108
-
109
- inputCell1.set_value(2)
110
- self.assertEqual(CallbackManager.observed1, [3])
111
- self.assertEqual(CallbackManager.observed2, [3])
112
-
113
- computeCell1.remove_watcher(CallbackManager.observe1)
114
- inputCell1.set_value(3)
115
- self.assertEqual(CallbackManager.observed1, [3])
116
- self.assertEqual(CallbackManager.observed2, [3, 4])
117
-
118
- def test_callbacks_only_called_once(self):
119
- reactor = Reactor()
120
- inputCell1 = reactor.create_input_cell(1)
121
- computeCell1 = reactor.create_compute_cell({inputCell1}, increment)
122
- computeCell2 = reactor.create_compute_cell({inputCell1}, decrement)
123
- computeCell3 = reactor.create_compute_cell({computeCell2}, decrement)
124
- computeCell4 = reactor.create_compute_cell({computeCell1,
125
- computeCell3},
126
- product)
127
-
128
- CallbackManager.reset()
129
- computeCell4.add_watcher(CallbackManager.count)
130
-
131
- inputCell1.set_value(3)
132
- self.assertEqual(CallbackManager.counter, 1)
2
+ from functools import partial
3
+
4
+ from react import InputCell, ComputeCell
5
+
6
+
7
+ # Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0
8
+
9
+ class ReactTests(unittest.TestCase):
10
+
11
+ def test_input_cells_have_a_value(self):
12
+ input_ = InputCell(10)
13
+ self.assertEqual(input_.value, 10)
14
+
15
+ def test_can_set_input_cell_value(self):
16
+ input_ = InputCell(4)
17
+ input_.value = 20
18
+ self.assertEqual(input_.value, 20)
19
+
20
+ def test_compute_cells_calculate_initial_value(self):
21
+ input_ = InputCell(1)
22
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
23
+ self.assertEqual(output.value, 2)
24
+
25
+ def test_compute_cells_take_inputs_in_right_order(self):
26
+ one = InputCell(1)
27
+ two = InputCell(2)
28
+ output = ComputeCell(
29
+ [one, two],
30
+ lambda inputs: inputs[0] + inputs[1]*10
31
+ )
32
+ self.assertEqual(output.value, 21)
33
+
34
+ def test_compute_cells_update_value_when_dependencies_are_changed(self):
35
+ input_ = InputCell(1)
36
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
37
+
38
+ input_.value = 3
39
+ self.assertEqual(output.value, 4)
40
+
41
+ def test_compute_cells_can_depend_on_other_compute_cells(self):
42
+ input_ = InputCell(1)
43
+ times_two = ComputeCell([input_], lambda inputs: inputs[0] * 2)
44
+ times_thirty = ComputeCell([input_], lambda inputs: inputs[0] * 30)
45
+ output = ComputeCell(
46
+ [times_two, times_thirty],
47
+ lambda inputs: inputs[0] + inputs[1]
48
+ )
49
+
50
+ self.assertEqual(output.value, 32)
51
+ input_.value = 3
52
+ self.assertEqual(output.value, 96)
53
+
54
+ def test_compute_cells_fire_callbacks(self):
55
+ input_ = InputCell(1)
56
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
57
+
58
+ observer = []
59
+ callback1 = self.callback_factory(observer)
60
+
61
+ output.add_callback(callback1)
62
+ input_.value = 3
63
+ self.assertEqual(observer[-1], 4)
64
+
65
+ def test_callbacks_only_fire_on_change(self):
66
+ input_ = InputCell(1)
67
+ output = ComputeCell(
68
+ [input_],
69
+ lambda inputs: 111 if inputs[0] < 3 else 222
70
+ )
71
+
72
+ observer = []
73
+ callback1 = self.callback_factory(observer)
74
+
75
+ output.add_callback(callback1)
76
+ input_.value = 2
77
+ self.assertEqual(observer, [])
78
+ input_.value = 4
79
+ self.assertEqual(observer[-1], 222)
80
+
81
+ def test_callbacks_do_not_report_already_reported_values(self):
82
+ input_ = InputCell(1)
83
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
84
+
85
+ observer = []
86
+ callback1 = self.callback_factory(observer)
87
+
88
+ output.add_callback(callback1)
89
+ input_.value = 2
90
+ self.assertEqual(observer[-1], 3)
91
+ input_.value = 3
92
+ self.assertEqual(observer[-1], 4)
93
+
94
+ def test_callbacks_can_fire_from_multiple_cells(self):
95
+ input_ = InputCell(1)
96
+ plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
97
+ minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
98
+
99
+ cb1_observer, cb2_observer = [], []
100
+ callback1 = self.callback_factory(cb1_observer)
101
+ callback2 = self.callback_factory(cb2_observer)
102
+
103
+ plus_one.add_callback(callback1)
104
+ minus_one.add_callback(callback2)
105
+ input_.value = 10
106
+
107
+ self.assertEqual(cb1_observer[-1], 11)
108
+ self.assertEqual(cb2_observer[-1], 9)
109
+
110
+ def test_callbacks_can_be_added_and_removed(self):
111
+ input_ = InputCell(11)
112
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
113
+
114
+ cb1_observer, cb2_observer, cb3_observer = [], [], []
115
+ callback1 = self.callback_factory(cb1_observer)
116
+ callback2 = self.callback_factory(cb2_observer)
117
+ callback3 = self.callback_factory(cb3_observer)
118
+
119
+ output.add_callback(callback1)
120
+ output.add_callback(callback2)
121
+ input_.value = 31
122
+ self.assertEqual(cb1_observer[-1], 32)
123
+ self.assertEqual(cb2_observer[-1], 32)
124
+
125
+ output.remove_callback(callback1)
126
+ output.add_callback(callback3)
127
+ input_.value = 41
128
+ self.assertEqual(cb2_observer[-1], 42)
129
+ self.assertEqual(cb3_observer[-1], 42)
130
+
131
+ # Expect callback1 not to be called.
132
+ self.assertEqual(len(cb1_observer), 1)
133
+
134
+ def test_removing_a_callback_multiple_times(self):
135
+ """Guard against incorrect implementations which store their
136
+ callbacks in an array."""
137
+ input_ = InputCell(1)
138
+ output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
139
+
140
+ cb1_observer, cb2_observer = [], []
141
+ callback1 = self.callback_factory(cb1_observer)
142
+ callback2 = self.callback_factory(cb2_observer)
143
+
144
+ output.add_callback(callback1)
145
+ output.add_callback(callback2)
146
+ output.remove_callback(callback1)
147
+ output.remove_callback(callback1)
148
+ output.remove_callback(callback1)
149
+ input_.value = 2
150
+
151
+ self.assertEqual(cb1_observer, [])
152
+ self.assertEqual(cb2_observer[-1], 3)
153
+
154
+ def test_callbacks_should_only_be_called_once(self):
155
+ """Guard against incorrect implementations which call a callback
156
+ function multiple times when multiple dependencies change."""
157
+ input_ = InputCell(1)
158
+ plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
159
+ minus_one1 = ComputeCell([input_], lambda inputs: inputs[0] - 1)
160
+ minus_one2 = ComputeCell([minus_one1], lambda inputs: inputs[0] - 1)
161
+ output = ComputeCell(
162
+ [plus_one, minus_one2],
163
+ lambda inputs: inputs[0] * inputs[1]
164
+ )
165
+
166
+ observer = []
167
+ callback1 = self.callback_factory(observer)
168
+
169
+ output.add_callback(callback1)
170
+ input_.value = 4
171
+ self.assertEqual(observer[-1], 10)
172
+
173
+ def test_callbacks_not_called_so_long_as_output_not_changed(self):
174
+ """Guard against incorrect implementations which call callbacks
175
+ if dependencies change but output value doesn't change."""
176
+ input_ = InputCell(1)
177
+ plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
178
+ minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
179
+ always_two = ComputeCell(
180
+ [plus_one, minus_one],
181
+ lambda inputs: inputs[0] - inputs[1]
182
+ )
183
+
184
+ observer = []
185
+ callback1 = self.callback_factory(observer)
186
+
187
+ always_two.add_callback(callback1)
188
+ input_.value = 2
189
+ input_.value = 3
190
+ input_.value = 4
191
+ input_.value = 5
192
+ self.assertEqual(observer, [])
193
+
194
+ # Utility functions.
195
+ def callback_factory(self, observer):
196
+ def callback(observer, value):
197
+ observer.append(value)
198
+ return partial(callback, observer)
133
199
 
134
200
 
135
201
  if __name__ == '__main__':
@@ -5,8 +5,8 @@ number.
5
5
 
6
6
  The Sieve of Eratosthenes is a simple, ancient algorithm for finding all
7
7
  prime numbers up to any given limit. It does so by iteratively marking as
8
- composite (i.e. not prime) the multiples of each prime,
9
- starting with the multiples of 2.
8
+ composite (i.e. not prime) the multiples of each prime, starting with the
9
+ multiples of 2. It does not use any division or remainder operation.
10
10
 
11
11
  Create your range, starting at two and continuing up to and including the given limit. (i.e. [2, limit])
12
12
 
@@ -25,7 +25,9 @@ https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
25
25
 
26
26
  Notice that this is a very specific algorithm, and the tests don't check
27
27
  that you've implemented the algorithm, only that you've come up with the
28
- correct list of primes.
28
+ correct list of primes. A good first test is to check that you do not use
29
+ division or remainder operations (div, /, mod or % depending on the
30
+ language).
29
31
 
30
32
  ## Exception messages
31
33
 
@@ -1,12 +1,31 @@
1
1
  use std::collections::HashMap;
2
2
 
3
- pub type CellID = usize;
4
- pub type CallbackID = usize;
5
-
6
- #[derive(Debug, PartialEq)]
7
- pub enum SetValueError {
8
- NonexistentCell,
9
- ComputeCell,
3
+ /// `InputCellID` is a unique identifier for an input cell.
4
+ #[derive(Clone, Copy, Debug, PartialEq)]
5
+ pub struct InputCellID(usize);
6
+ /// `ComputeCellID` is a unique identifier for a compute cell.
7
+ /// Values of type `InputCellID` and `ComputeCellID` should not be mutually assignable,
8
+ /// demonstrated by the following tests:
9
+ ///
10
+ /// ```compile_fail
11
+ /// let mut r = react::Reactor::new();
12
+ /// let input: react::ComputeCellID = r.create_input(111);
13
+ /// ```
14
+ ///
15
+ /// ```compile_fail
16
+ /// let mut r = react::Reactor::new();
17
+ /// let input = r.create_input(111);
18
+ /// let compute: react::InputCellID = r.create_compute(&[react::CellID::Input(input)], |_| 222).unwrap();
19
+ /// ```
20
+ #[derive(Clone, Copy, Debug, PartialEq)]
21
+ pub struct ComputeCellID(usize);
22
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
23
+ pub struct CallbackID(usize);
24
+
25
+ #[derive(Clone, Copy, Debug, PartialEq)]
26
+ pub enum CellID {
27
+ Input(InputCellID),
28
+ Compute(ComputeCellID),
10
29
  }
11
30
 
12
31
  #[derive(Debug, PartialEq)]
@@ -15,27 +34,38 @@ pub enum RemoveCallbackError {
15
34
  NonexistentCallback,
16
35
  }
17
36
 
18
- struct Cell<'a, T: Copy> {
37
+ struct Cell<T: Copy> {
19
38
  value: T,
20
39
  last_value: T,
21
- dependents: Vec<CellID>,
22
- cell_type: CellType<'a, T>,
23
- callbacks_issued: usize,
24
- callbacks: HashMap<CallbackID, Box<FnMut(T) -> () + 'a>>,
40
+ dependents: Vec<ComputeCellID>,
25
41
  }
26
42
 
27
- enum CellType<'a, T: Copy> {
28
- Input,
29
- Compute(Vec<CellID>, Box<Fn(&[T]) -> T + 'a>),
43
+ struct ComputeCell<'a, T: Copy> {
44
+ cell: Cell<T>,
45
+
46
+ dependencies: Vec<CellID>,
47
+ f: Box<Fn(&[T]) -> T + 'a>,
48
+ callbacks_issued: usize,
49
+ callbacks: HashMap<CallbackID, Box<FnMut(T) -> () + 'a>>,
30
50
  }
31
51
 
32
- impl <'a, T: Copy> Cell<'a, T> {
33
- fn new(initial: T, cell_type: CellType<'a, T>) -> Self {
52
+ impl <T: Copy> Cell<T> {
53
+ fn new(initial: T) -> Self {
34
54
  Cell {
35
55
  value: initial,
36
56
  last_value: initial,
37
57
  dependents: Vec::new(),
38
- cell_type: cell_type,
58
+ }
59
+ }
60
+ }
61
+
62
+ impl <'a, T: Copy> ComputeCell<'a, T> {
63
+ fn new<F: Fn(&[T]) -> T + 'a>(initial: T, dependencies: Vec<CellID>, f: F) -> Self {
64
+ ComputeCell {
65
+ cell: Cell::new(initial),
66
+
67
+ dependencies,
68
+ f: Box::new(f),
39
69
  callbacks_issued: 0,
40
70
  callbacks: HashMap::new(),
41
71
  }
@@ -43,52 +73,58 @@ impl <'a, T: Copy> Cell<'a, T> {
43
73
  }
44
74
 
45
75
  pub struct Reactor<'a, T: Copy> {
46
- cells: Vec<Cell<'a, T>>,
76
+ inputs: Vec<Cell<T>>,
77
+ computes: Vec<ComputeCell<'a, T>>,
47
78
  }
48
79
 
49
80
  impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
50
81
  pub fn new() -> Self {
51
82
  Reactor{
52
- cells: Vec::new(),
83
+ inputs: Vec::new(),
84
+ computes: Vec::new(),
53
85
  }
54
86
  }
55
87
 
56
- pub fn create_input(&mut self, initial: T) -> CellID {
57
- self.cells.push(Cell::new(initial, CellType::Input));
58
- self.cells.len() - 1
88
+ pub fn create_input(&mut self, initial: T) -> InputCellID {
89
+ self.inputs.push(Cell::new(initial));
90
+ InputCellID(self.inputs.len() - 1)
59
91
  }
60
92
 
61
- pub fn create_compute<F: Fn(&[T]) -> T + 'a>(&mut self, dependencies: &[CellID], compute_func: F) -> Result<CellID, CellID> {
93
+ pub fn create_compute<F: Fn(&[T]) -> T + 'a>(&mut self, dependencies: &[CellID], compute_func: F) -> Result<ComputeCellID, CellID> {
62
94
  // Check all dependencies' validity before modifying any of them,
63
95
  // so that we don't perform an incorrect partial write.
64
- if let Some(&invalid) = dependencies.iter().find(|&dep| *dep >= self.cells.len()) {
65
- return Err(invalid);
96
+ for &dep in dependencies {
97
+ match dep {
98
+ CellID::Input(InputCellID(id)) => if id >= self.inputs.len() { return Err(dep) },
99
+ CellID::Compute(ComputeCellID(id)) => if id >= self.computes.len() { return Err(dep) },
100
+ }
66
101
  }
67
- let new_id = self.cells.len();
68
- for &id in dependencies {
69
- self.cells.get_mut(id).unwrap().dependents.push(new_id);
102
+ let new_id = ComputeCellID(self.computes.len());
103
+ for &dep in dependencies {
104
+ match dep {
105
+ CellID::Input(InputCellID(id)) => self.inputs[id].dependents.push(new_id),
106
+ CellID::Compute(ComputeCellID(id)) => self.computes[id].cell.dependents.push(new_id),
107
+ }
70
108
  }
71
109
  let inputs: Vec<_> = dependencies.iter().map(|&id| self.value(id).unwrap()).collect();
72
110
  let initial = compute_func(&inputs);
73
- self.cells.push(Cell::new(initial, CellType::Compute(dependencies.iter().cloned().collect(), Box::new(compute_func))));
111
+ self.computes.push(ComputeCell::new(initial, dependencies.to_vec(), compute_func));
74
112
  Ok(new_id)
75
113
  }
76
114
 
77
115
  pub fn value(&self, id: CellID) -> Option<T> {
78
- self.cells.get(id).map(|c| c.value)
116
+ match id {
117
+ CellID::Input(InputCellID(id)) => self.inputs.get(id).map(|c| c.value),
118
+ CellID::Compute(ComputeCellID(id)) => self.computes.get(id).map(|c| c.cell.value),
119
+ }
79
120
  }
80
121
 
81
- pub fn set_value(&mut self, id: CellID, new_value: T) -> Result<(), SetValueError> {
82
- match self.cells.get_mut(id) {
83
- Some(c) => match c.cell_type {
84
- CellType::Input => {
85
- c.value = new_value;
86
- Ok(c.dependents.clone())
87
- },
88
- CellType::Compute(_, _) => Err(SetValueError::ComputeCell),
89
- },
90
- None => Err(SetValueError::NonexistentCell),
91
- }.map(|deps| {
122
+ pub fn set_value(&mut self, id: InputCellID, new_value: T) -> bool {
123
+ let InputCellID(id) = id;
124
+ self.inputs.get_mut(id).map(|c| {
125
+ c.value = new_value;
126
+ c.dependents.clone()
127
+ }).map(|deps| {
92
128
  for &d in deps.iter() {
93
129
  self.update_dependent(d);
94
130
  }
@@ -97,19 +133,22 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
97
133
  for d in deps {
98
134
  self.fire_callbacks(d);
99
135
  }
100
- })
136
+ }).is_some()
101
137
  }
102
138
 
103
- pub fn add_callback<F: FnMut(T) -> () + 'a>(&mut self, id: CellID, callback: F) -> Option<CallbackID> {
104
- self.cells.get_mut(id).map(|c| {
139
+ pub fn add_callback<F: FnMut(T) -> () + 'a>(&mut self, id: ComputeCellID, callback: F) -> Option<CallbackID> {
140
+ let ComputeCellID(id) = id;
141
+ self.computes.get_mut(id).map(|c| {
105
142
  c.callbacks_issued += 1;
106
- c.callbacks.insert(c.callbacks_issued, Box::new(callback));
107
- c.callbacks_issued
143
+ let cbid = CallbackID(c.callbacks_issued);
144
+ c.callbacks.insert(cbid, Box::new(callback));
145
+ cbid
108
146
  })
109
147
  }
110
148
 
111
- pub fn remove_callback(&mut self, cell: CellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
112
- match self.cells.get_mut(cell) {
149
+ pub fn remove_callback(&mut self, cell: ComputeCellID, callback: CallbackID) -> Result<(), RemoveCallbackError> {
150
+ let ComputeCellID(cell) = cell;
151
+ match self.computes.get_mut(cell) {
113
152
  Some(c) => match c.callbacks.remove(&callback) {
114
153
  Some(_) => Ok(()),
115
154
  None => Err(RemoveCallbackError::NonexistentCallback),
@@ -118,29 +157,28 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
118
157
  }
119
158
  }
120
159
 
121
- fn update_dependent(&mut self, id: CellID) {
160
+ fn update_dependent(&mut self, id: ComputeCellID) {
161
+ let ComputeCellID(id) = id;
162
+
122
163
  let (new_value, dependents) = {
123
164
  // This block limits the scope of the self.cells borrow.
124
165
  // This is necessary becaue we borrow it mutably below.
125
- let (dependencies, f, dependents) = match self.cells.get(id) {
126
- Some(c) => match c.cell_type {
127
- CellType::Input => panic!("Input cell can't be a dependent"),
128
- CellType::Compute(ref dependencies, ref f) => (dependencies, f, c.dependents.clone()),
129
- },
166
+ let (dependencies, f, dependents) = match self.computes.get(id) {
167
+ Some(c) => (&c.dependencies, &c.f, c.cell.dependents.clone()),
130
168
  None => panic!("Cell to update disappeared while querying"),
131
169
  };
132
170
  let inputs: Vec<_> = dependencies.iter().map(|&id| self.value(id).unwrap()).collect();
133
171
  (f(&inputs), dependents)
134
172
  };
135
173
 
136
- match self.cells.get_mut(id) {
174
+ match self.computes.get_mut(id) {
137
175
  Some(c) => {
138
- if c.value == new_value {
176
+ if c.cell.value == new_value {
139
177
  // No change here, we don't need to update our dependents.
140
178
  // (It wouldn't hurt to, but it would be unnecessary work)
141
179
  return;
142
180
  }
143
- c.value = new_value;
181
+ c.cell.value = new_value;
144
182
  },
145
183
  None => panic!("Cell to update disappeared while updating"),
146
184
  }
@@ -150,19 +188,20 @@ impl <'a, T: Copy + PartialEq> Reactor<'a, T> {
150
188
  }
151
189
  }
152
190
 
153
- fn fire_callbacks(&mut self, id: CellID) {
154
- let dependents = match self.cells.get_mut(id) {
191
+ fn fire_callbacks(&mut self, id: ComputeCellID) {
192
+ let ComputeCellID(id) = id;
193
+ let dependents = match self.computes.get_mut(id) {
155
194
  Some(c) => {
156
- if c.value == c.last_value {
195
+ if c.cell.value == c.cell.last_value {
157
196
  // Value hasn't changed since last callback fire.
158
197
  // We thus shouldn't fire the callbacks.
159
198
  return
160
199
  }
161
200
  for cb in c.callbacks.values_mut() {
162
- cb(c.value);
201
+ cb(c.cell.value);
163
202
  }
164
- c.last_value = c.value;
165
- c.dependents.clone()
203
+ c.cell.last_value = c.cell.value;
204
+ c.cell.dependents.clone()
166
205
  },
167
206
  None => panic!("Callback cell disappeared"),
168
207
  };