trackler 2.0.5.14 → 2.0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/common/exercises/perfect-numbers/metadata.yml +1 -1
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/config.json +17 -0
- data/tracks/csharp/exercises/exercises.csproj +1 -1
- data/tracks/csharp/exercises/react/Example.cs +124 -0
- data/tracks/csharp/exercises/react/HINTS.md +3 -0
- data/tracks/csharp/exercises/react/ReactTest.cs +121 -0
- data/tracks/csharp/exercises/tree-building/Example.cs +73 -0
- data/tracks/csharp/exercises/tree-building/TreeBuilding.cs +67 -0
- data/tracks/csharp/exercises/tree-building/TreeBuildingTest.cs +228 -0
- data/tracks/ecmascript/exercises/accumulate/package.json +1 -1
- data/tracks/ecmascript/exercises/acronym/package.json +1 -1
- data/tracks/ecmascript/exercises/all-your-base/package.json +1 -1
- data/tracks/ecmascript/exercises/allergies/package.json +1 -1
- data/tracks/ecmascript/exercises/anagram/package.json +1 -1
- data/tracks/ecmascript/exercises/atbash-cipher/package.json +1 -1
- data/tracks/ecmascript/exercises/beer-song/package.json +1 -1
- data/tracks/ecmascript/exercises/binary-search-tree/package.json +1 -1
- data/tracks/ecmascript/exercises/binary-search/package.json +1 -1
- data/tracks/ecmascript/exercises/binary/package.json +1 -1
- data/tracks/ecmascript/exercises/bob/package.json +1 -1
- data/tracks/ecmascript/exercises/bracket-push/package.json +1 -1
- data/tracks/ecmascript/exercises/circular-buffer/package.json +1 -1
- data/tracks/ecmascript/exercises/clock/package.json +1 -1
- data/tracks/ecmascript/exercises/crypto-square/package.json +1 -1
- data/tracks/ecmascript/exercises/custom-set/package.json +1 -1
- data/tracks/ecmascript/exercises/diamond/package.json +1 -1
- data/tracks/ecmascript/exercises/difference-of-squares/package.json +1 -1
- data/tracks/ecmascript/exercises/etl/package.json +1 -1
- data/tracks/ecmascript/exercises/food-chain/package.json +1 -1
- data/tracks/ecmascript/exercises/gigasecond/package.json +1 -1
- data/tracks/ecmascript/exercises/grade-school/package.json +1 -1
- data/tracks/ecmascript/exercises/grains/package.json +1 -1
- data/tracks/ecmascript/exercises/hamming/package.json +1 -1
- data/tracks/ecmascript/exercises/hello-world/package.json +1 -1
- data/tracks/ecmascript/exercises/hexadecimal/package.json +1 -1
- data/tracks/ecmascript/exercises/isogram/package.json +1 -1
- data/tracks/ecmascript/exercises/kindergarten-garden/package.json +1 -1
- data/tracks/ecmascript/exercises/largest-series-product/package.json +1 -1
- data/tracks/ecmascript/exercises/leap/package.json +1 -1
- data/tracks/ecmascript/exercises/linked-list/package.json +1 -1
- data/tracks/ecmascript/exercises/luhn/package.json +1 -1
- data/tracks/ecmascript/exercises/matrix/package.json +1 -1
- data/tracks/ecmascript/exercises/meetup/package.json +1 -1
- data/tracks/ecmascript/exercises/nth-prime/package.json +1 -1
- data/tracks/ecmascript/exercises/ocr-numbers/package.json +1 -1
- data/tracks/ecmascript/exercises/octal/package.json +1 -1
- data/tracks/ecmascript/exercises/palindrome-products/package.json +1 -1
- data/tracks/ecmascript/exercises/pangram/package.json +1 -1
- data/tracks/ecmascript/exercises/pascals-triangle/package.json +1 -1
- data/tracks/ecmascript/exercises/phone-number/package.json +1 -1
- data/tracks/ecmascript/exercises/pig-latin/package.json +1 -1
- data/tracks/ecmascript/exercises/prime-factors/package.json +1 -1
- data/tracks/ecmascript/exercises/pythagorean-triplet/package.json +1 -1
- data/tracks/ecmascript/exercises/queen-attack/package.json +1 -1
- data/tracks/ecmascript/exercises/raindrops/package.json +1 -1
- data/tracks/ecmascript/exercises/rna-transcription/package.json +1 -1
- data/tracks/ecmascript/exercises/robot-name/package.json +1 -1
- data/tracks/ecmascript/exercises/robot-simulator/package.json +1 -1
- data/tracks/ecmascript/exercises/roman-numerals/package.json +1 -1
- data/tracks/ecmascript/exercises/saddle-points/package.json +1 -1
- data/tracks/ecmascript/exercises/say/package.json +1 -1
- data/tracks/ecmascript/exercises/scrabble-score/package.json +1 -1
- data/tracks/ecmascript/exercises/secret-handshake/package.json +1 -1
- data/tracks/ecmascript/exercises/series/package.json +1 -1
- data/tracks/ecmascript/exercises/sieve/package.json +1 -1
- data/tracks/ecmascript/exercises/simple-cipher/package.json +1 -1
- data/tracks/ecmascript/exercises/space-age/package.json +1 -1
- data/tracks/ecmascript/exercises/strain/package.json +1 -1
- data/tracks/ecmascript/exercises/sum-of-multiples/package.json +1 -1
- data/tracks/ecmascript/exercises/triangle/package.json +1 -1
- data/tracks/ecmascript/exercises/trinary/package.json +1 -1
- data/tracks/ecmascript/exercises/two-bucket/package.json +1 -1
- data/tracks/ecmascript/exercises/word-count/package.json +1 -1
- data/tracks/ecmascript/exercises/wordy/package.json +1 -1
- data/tracks/ecmascript/package.json +1 -1
- data/tracks/elixir/docs/ABOUT.md +1 -1
- data/tracks/haskell/config.json +6 -0
- data/tracks/haskell/exercises/run-length-encoding/examples/success-standard/package.yaml +16 -0
- data/tracks/haskell/exercises/run-length-encoding/examples/success-standard/src/RunLength.hs +21 -0
- data/tracks/haskell/exercises/run-length-encoding/package.yaml +19 -0
- data/tracks/haskell/exercises/run-length-encoding/src/RunLength.hs +7 -0
- data/tracks/haskell/exercises/run-length-encoding/stack.yaml +1 -0
- data/tracks/haskell/exercises/run-length-encoding/test/Tests.hs +75 -0
- data/tracks/javascript/config.json +8 -1
- data/tracks/javascript/exercises/run-length-encoding/example.js +11 -0
- data/tracks/javascript/exercises/run-length-encoding/run-length-encoding.spec.js +41 -0
- data/tracks/pascal/.gitignore +2 -0
- data/tracks/pascal/config.json +8 -8
- data/tracks/pascal/exercises/{Hello-World → hello-world}/uHelloWorldExample.pas +0 -0
- data/tracks/pascal/exercises/{Hello-World → hello-world}/uTestHelloWorld.pas +0 -0
- metadata +18 -36
- data/tracks/pascal/exercises/Hello-World/TestHelloWorld.dpr +0 -60
- data/tracks/pascal/exercises/Hello-World/TestHelloWorld.dproj +0 -176
- data/tracks/pascal/exercises/allergies/AllergyTests.dpr +0 -60
- data/tracks/pascal/exercises/allergies/AllergyTests.dproj +0 -176
- data/tracks/pascal/exercises/bank-account/BankAccountTests.dpr +0 -60
- data/tracks/pascal/exercises/bank-account/BankAccountTests.dproj +0 -176
- data/tracks/pascal/exercises/beer-song/BeerSongTests.dpr +0 -60
- data/tracks/pascal/exercises/beer-song/BeerSongTests.dproj +0 -176
- data/tracks/pascal/exercises/binary-search/BinarySearchTest.dpr +0 -60
- data/tracks/pascal/exercises/binary-search/BinarySearchTest.dproj +0 -176
- data/tracks/pascal/exercises/bob/BobTests.dpr +0 -60
- data/tracks/pascal/exercises/bob/BobTests.dproj +0 -176
- data/tracks/pascal/exercises/book-store/BookStoreTests.dpr +0 -60
- data/tracks/pascal/exercises/book-store/BookStoreTests.dproj +0 -176
- data/tracks/pascal/exercises/bowling/BowlingTests.dpr +0 -60
- data/tracks/pascal/exercises/bowling/BowlingTests.dproj +0 -176
- data/tracks/pascal/exercises/circular-buffer/CircularBufferTests.dpr +0 -60
- data/tracks/pascal/exercises/circular-buffer/CircularBufferTests.dproj +0 -176
- data/tracks/pascal/exercises/clock/ClockTest.dpr +0 -60
- data/tracks/pascal/exercises/clock/ClockTest.dproj +0 -176
- data/tracks/pascal/exercises/etl/ETLtests.dpr +0 -60
- data/tracks/pascal/exercises/etl/ETLtests.dproj +0 -176
- data/tracks/pascal/exercises/hamming/Hamming.dpr +0 -60
- data/tracks/pascal/exercises/hamming/Hamming.dproj +0 -176
- data/tracks/pascal/exercises/leap/LeapTest.dpr +0 -60
- data/tracks/pascal/exercises/leap/LeapTest.dproj +0 -176
- data/tracks/pascal/exercises/nucleotide-count/NucleotideCountTest.dpr +0 -60
- data/tracks/pascal/exercises/nucleotide-count/NucleotideCountTest.dproj +0 -176
- data/tracks/pascal/exercises/perfect-numbers/PerfectNumbersTest.dpr +0 -60
- data/tracks/pascal/exercises/perfect-numbers/PerfectNumbersTest.dproj +0 -176
- data/tracks/pascal/exercises/phone-number/PhoneNumberTests.dpr +0 -60
- data/tracks/pascal/exercises/phone-number/PhoneNumberTests.dproj +0 -176
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 640bf7c491e8a9613de8e88ee23185a2ccfe7114
|
|
4
|
+
data.tar.gz: dc1a5b2fd961f1bea01631d313ce1ebf9ef8497f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90d0a3630526b437476b6a2a3c08b9a4b538550972b606f503ad8092b344e668d5642aa52d02ad5b93f99eb6bd6c17fdd69f271274f0da60c77b2482f5103b65
|
|
7
|
+
data.tar.gz: 77cb6e64a0f978ee41e62bb23d86b49c0ed9cdaa789f5cfab0167735ab9058138da1b5326992ac87f6afc0cd03fd0790f9ce2c2066aee028c6512bfb0d790c6f
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
blurb: "
|
|
2
|
+
blurb: "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for natural numbers."
|
|
3
3
|
source: "Taken from Chapter 2 of Functional Thinking by Neal Ford."
|
|
4
4
|
source_url: "http://shop.oreilly.com/product/0636920029687.do"
|
data/lib/trackler/version.rb
CHANGED
data/tracks/csharp/config.json
CHANGED
|
@@ -486,6 +486,14 @@
|
|
|
486
486
|
"Lists"
|
|
487
487
|
]
|
|
488
488
|
},
|
|
489
|
+
{
|
|
490
|
+
"slug": "tree-building",
|
|
491
|
+
"difficulty": 5,
|
|
492
|
+
"topics": [
|
|
493
|
+
"Trees",
|
|
494
|
+
"Refactoring"
|
|
495
|
+
]
|
|
496
|
+
},
|
|
489
497
|
{
|
|
490
498
|
"slug": "scale-generator",
|
|
491
499
|
"difficulty": 5,
|
|
@@ -739,6 +747,15 @@
|
|
|
739
747
|
"Text formatting"
|
|
740
748
|
]
|
|
741
749
|
},
|
|
750
|
+
{
|
|
751
|
+
"slug": "react",
|
|
752
|
+
"difficulty": 9,
|
|
753
|
+
"topics": [
|
|
754
|
+
"Reactive programming",
|
|
755
|
+
"Classes",
|
|
756
|
+
"Events"
|
|
757
|
+
]
|
|
758
|
+
},
|
|
742
759
|
{
|
|
743
760
|
"slug": "variable-length-quantity",
|
|
744
761
|
"difficulty": 9,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<Reference Include="Microsoft.CSharp" />
|
|
39
39
|
</ItemGroup>
|
|
40
40
|
<ItemGroup>
|
|
41
|
-
<Compile Include="**\*.cs" Exclude="sgf-parsing\SgfParsing.cs;markdown\Markdown.cs" />
|
|
41
|
+
<Compile Include="**\*.cs" Exclude="sgf-parsing\SgfParsing.cs;markdown\Markdown.cs;tree-building\TreeBuilding.cs" />
|
|
42
42
|
</ItemGroup>
|
|
43
43
|
<ItemGroup>
|
|
44
44
|
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
|
|
6
|
+
public delegate void ChangedEventHandler(object sender, int value);
|
|
7
|
+
|
|
8
|
+
public class Reactor
|
|
9
|
+
{
|
|
10
|
+
private readonly Dictionary<int, Cell> cells = new Dictionary<int, Cell>();
|
|
11
|
+
|
|
12
|
+
public InputCell CreateInputCell(int value)
|
|
13
|
+
{
|
|
14
|
+
var inputCell = new InputCell(cells.Count, value);
|
|
15
|
+
inputCell.Changed += CellChanged;
|
|
16
|
+
|
|
17
|
+
cells[inputCell.Id] = inputCell;
|
|
18
|
+
|
|
19
|
+
return inputCell;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public ComputeCell CreateComputeCell(IEnumerable<Cell> producers, Func<int[], int> compute)
|
|
23
|
+
{
|
|
24
|
+
var computeCell = new ComputeCell(cells.Count, producers, compute);
|
|
25
|
+
cells[computeCell.Id] = computeCell;
|
|
26
|
+
|
|
27
|
+
return computeCell;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private void CellChanged(object sender, int value)
|
|
31
|
+
{
|
|
32
|
+
var cell = (Cell)sender;
|
|
33
|
+
var consumers = new BitArray(cells.Count);
|
|
34
|
+
|
|
35
|
+
foreach (var consumer in cell.Consumers)
|
|
36
|
+
consumers.Set(consumer.Id, true);
|
|
37
|
+
|
|
38
|
+
for (var id = cell.Id + 1; id < cells.Count; id++)
|
|
39
|
+
{
|
|
40
|
+
if (!consumers.Get(id))
|
|
41
|
+
continue;
|
|
42
|
+
|
|
43
|
+
var consumer = (ComputeCell)cells[id];
|
|
44
|
+
consumer.Recompute();
|
|
45
|
+
|
|
46
|
+
foreach (var transitiveConsumer in consumer.Consumers)
|
|
47
|
+
consumers.Set(transitiveConsumer.Id, true);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public abstract class Cell
|
|
53
|
+
{
|
|
54
|
+
public Cell(int id)
|
|
55
|
+
{
|
|
56
|
+
Id = id;
|
|
57
|
+
Consumers = new List<Cell>();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public int Id { get; }
|
|
61
|
+
public List<Cell> Consumers { get; }
|
|
62
|
+
|
|
63
|
+
public abstract int Value { get; set; }
|
|
64
|
+
public abstract event ChangedEventHandler Changed;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public class InputCell : Cell
|
|
68
|
+
{
|
|
69
|
+
private int _value;
|
|
70
|
+
|
|
71
|
+
public InputCell(int id, int value) : base(id)
|
|
72
|
+
{
|
|
73
|
+
_value = value;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public override event ChangedEventHandler Changed;
|
|
77
|
+
|
|
78
|
+
public override int Value
|
|
79
|
+
{
|
|
80
|
+
get
|
|
81
|
+
{
|
|
82
|
+
return _value;
|
|
83
|
+
}
|
|
84
|
+
set
|
|
85
|
+
{
|
|
86
|
+
if (_value == value)
|
|
87
|
+
return;
|
|
88
|
+
|
|
89
|
+
_value = value;
|
|
90
|
+
Changed?.Invoke(this, _value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public class ComputeCell : Cell
|
|
96
|
+
{
|
|
97
|
+
private readonly IEnumerable<Cell> producers;
|
|
98
|
+
private readonly Func<int[], int> compute;
|
|
99
|
+
|
|
100
|
+
public ComputeCell(int id, IEnumerable<Cell> producers, Func<int[], int> compute) : base(id)
|
|
101
|
+
{
|
|
102
|
+
this.producers = producers;
|
|
103
|
+
this.compute = compute;
|
|
104
|
+
|
|
105
|
+
foreach (var producer in producers)
|
|
106
|
+
producer.Consumers.Add(this);
|
|
107
|
+
|
|
108
|
+
Recompute();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public override int Value { get; set; }
|
|
112
|
+
public override event ChangedEventHandler Changed;
|
|
113
|
+
|
|
114
|
+
public void Recompute()
|
|
115
|
+
{
|
|
116
|
+
var updatedValue = compute(producers.Select(producer => producer.Value).ToArray());
|
|
117
|
+
|
|
118
|
+
if (updatedValue == Value)
|
|
119
|
+
return;
|
|
120
|
+
|
|
121
|
+
Value = updatedValue;
|
|
122
|
+
Changed?.Invoke(this, updatedValue);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
using NUnit.Framework;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
|
|
4
|
+
public class ReactTest
|
|
5
|
+
{
|
|
6
|
+
[Test]
|
|
7
|
+
public void Setting_the_value_of_an_input_cell_changes_the_observable_value()
|
|
8
|
+
{
|
|
9
|
+
var reactor = new Reactor();
|
|
10
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
11
|
+
|
|
12
|
+
Assert.That(inputCell1.Value, Is.EqualTo(1));
|
|
13
|
+
inputCell1.Value = 2;
|
|
14
|
+
Assert.That(inputCell1.Value, Is.EqualTo(2));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
[Ignore("Remove to run test")]
|
|
18
|
+
[Test]
|
|
19
|
+
public void The_value_of_a_compute_is_determined_by_the_value_of_the_dependencies()
|
|
20
|
+
{
|
|
21
|
+
var reactor = new Reactor();
|
|
22
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
23
|
+
var computeCell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] + 1);
|
|
24
|
+
|
|
25
|
+
Assert.That(computeCell1.Value, Is.EqualTo(2));
|
|
26
|
+
inputCell1.Value = 2;
|
|
27
|
+
Assert.That(computeCell1.Value, Is.EqualTo(3));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[Ignore("Remove to run test")]
|
|
31
|
+
[Test]
|
|
32
|
+
public void Compute_cells_can_depend_on_other_compute_cells()
|
|
33
|
+
{
|
|
34
|
+
var reactor = new Reactor();
|
|
35
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
36
|
+
var computeCell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] + 1);
|
|
37
|
+
var computeCell2 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] - 1);
|
|
38
|
+
var computeCell3 = reactor.CreateComputeCell(new[] { computeCell1, computeCell2 }, (values) => values[0] * values[1]);
|
|
39
|
+
|
|
40
|
+
Assert.That(computeCell3.Value, Is.EqualTo(0));
|
|
41
|
+
inputCell1.Value = 3;
|
|
42
|
+
Assert.That(computeCell3.Value, Is.EqualTo(8));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
[Ignore("Remove to run test")]
|
|
46
|
+
[Test]
|
|
47
|
+
public void Compute_cells_can_have_callbacks()
|
|
48
|
+
{
|
|
49
|
+
var reactor = new Reactor();
|
|
50
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
51
|
+
var computeCell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] + 1);
|
|
52
|
+
var observed = new List<int>();
|
|
53
|
+
computeCell1.Changed += (sender, value) => observed.Add(value);
|
|
54
|
+
|
|
55
|
+
Assert.That(observed, Is.Empty);
|
|
56
|
+
inputCell1.Value = 2;
|
|
57
|
+
Assert.That(observed, Is.EquivalentTo(new[] { 3 }));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[Ignore("Remove to run test")]
|
|
61
|
+
[Test]
|
|
62
|
+
public void Callbacks_only_trigger_on_change()
|
|
63
|
+
{
|
|
64
|
+
var reactor = new Reactor();
|
|
65
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
66
|
+
var computecell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] > 2 ? values[0] + 1 : 2);
|
|
67
|
+
var observerCalled = 0;
|
|
68
|
+
computecell1.Changed += (sender, value) => observerCalled++;
|
|
69
|
+
|
|
70
|
+
inputCell1.Value = 1;
|
|
71
|
+
Assert.That(observerCalled, Is.EqualTo(0));
|
|
72
|
+
inputCell1.Value = 2;
|
|
73
|
+
Assert.That(observerCalled, Is.EqualTo(0));
|
|
74
|
+
inputCell1.Value = 3;
|
|
75
|
+
Assert.That(observerCalled, Is.EqualTo(1));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[Ignore("Remove to run test")]
|
|
79
|
+
[Test]
|
|
80
|
+
public void Callbacks_can_be_removed()
|
|
81
|
+
{
|
|
82
|
+
var reactor = new Reactor();
|
|
83
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
84
|
+
var computeCell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] + 1);
|
|
85
|
+
var observed1 = new List<int>();
|
|
86
|
+
var observed2 = new List<int>();
|
|
87
|
+
|
|
88
|
+
ChangedEventHandler changedHandler1 = (object sender, int value) => observed1.Add(value);
|
|
89
|
+
ChangedEventHandler changedHandler2 = (object sender, int value) => observed2.Add(value);
|
|
90
|
+
|
|
91
|
+
computeCell1.Changed += changedHandler1;
|
|
92
|
+
computeCell1.Changed += changedHandler2;
|
|
93
|
+
|
|
94
|
+
inputCell1.Value = 2;
|
|
95
|
+
Assert.That(observed1, Is.EquivalentTo(new[] { 3 }));
|
|
96
|
+
Assert.That(observed2, Is.EquivalentTo(new[] { 3 }));
|
|
97
|
+
|
|
98
|
+
computeCell1.Changed -= changedHandler1;
|
|
99
|
+
inputCell1.Value = 3;
|
|
100
|
+
Assert.That(observed1, Is.EquivalentTo(new[] { 3 }));
|
|
101
|
+
Assert.That(observed2, Is.EquivalentTo(new[] { 3, 4 }));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
[Ignore("Remove to run test")]
|
|
105
|
+
[Test]
|
|
106
|
+
public void Callbacks_should_only_be_called_once_even_if_multiple_dependencies_have_changed()
|
|
107
|
+
{
|
|
108
|
+
var reactor = new Reactor();
|
|
109
|
+
var inputCell1 = reactor.CreateInputCell(1);
|
|
110
|
+
var computeCell1 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] + 1);
|
|
111
|
+
var computeCell2 = reactor.CreateComputeCell(new[] { inputCell1 }, (values) => values[0] - 1);
|
|
112
|
+
var computeCell3 = reactor.CreateComputeCell(new[] { computeCell2 }, (values) => values[0] - 1);
|
|
113
|
+
var computeCell4 = reactor.CreateComputeCell(new[] { computeCell1, computeCell3 }, (values) => values[0] * values[1]);
|
|
114
|
+
|
|
115
|
+
var changed4 = 0;
|
|
116
|
+
computeCell4.Changed += (sender, value) => changed4++;
|
|
117
|
+
|
|
118
|
+
inputCell1.Value = 3;
|
|
119
|
+
Assert.That(changed4, Is.EqualTo(1));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
|
|
5
|
+
public class TreeBuildingRecord
|
|
6
|
+
{
|
|
7
|
+
private const int RootRecordId = 0;
|
|
8
|
+
|
|
9
|
+
public int ParentId { get; set; }
|
|
10
|
+
public int RecordId { get; set; }
|
|
11
|
+
|
|
12
|
+
public bool IsRoot => RecordId == RootRecordId;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public class Tree
|
|
16
|
+
{
|
|
17
|
+
public Tree(int id)
|
|
18
|
+
{
|
|
19
|
+
Id = id;
|
|
20
|
+
Children = new List<Tree>();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public int Id { get; }
|
|
24
|
+
|
|
25
|
+
public List<Tree> Children { get; }
|
|
26
|
+
|
|
27
|
+
public bool IsLeaf => Children.Count == 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public static class TreeBuilder
|
|
31
|
+
{
|
|
32
|
+
private const int RootRecordId = 0;
|
|
33
|
+
|
|
34
|
+
public static Tree BuildTree(IEnumerable<TreeBuildingRecord> records)
|
|
35
|
+
{
|
|
36
|
+
var orderedRecords = GetOrderedRecords(records);
|
|
37
|
+
|
|
38
|
+
if (orderedRecords.Count == 0)
|
|
39
|
+
throw new ArgumentException();
|
|
40
|
+
|
|
41
|
+
var nodes = new Dictionary<int, Tree>();
|
|
42
|
+
var previousRecordId = -1;
|
|
43
|
+
|
|
44
|
+
foreach (var record in orderedRecords)
|
|
45
|
+
{
|
|
46
|
+
ValidateRecord(record, previousRecordId);
|
|
47
|
+
|
|
48
|
+
nodes[record.RecordId] = new Tree(record.RecordId);
|
|
49
|
+
|
|
50
|
+
if (!record.IsRoot)
|
|
51
|
+
nodes[record.ParentId].Children.Add(nodes[record.RecordId]);
|
|
52
|
+
|
|
53
|
+
previousRecordId++;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return nodes[RootRecordId];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private static void ValidateRecord(TreeBuildingRecord record, int previousRecordId)
|
|
60
|
+
{
|
|
61
|
+
if (record.IsRoot && record.ParentId != RootRecordId)
|
|
62
|
+
throw new ArgumentException();
|
|
63
|
+
else if (!record.IsRoot && record.ParentId >= record.RecordId)
|
|
64
|
+
throw new ArgumentException();
|
|
65
|
+
else if (!record.IsRoot && record.RecordId != previousRecordId + 1)
|
|
66
|
+
throw new ArgumentException();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private static List<TreeBuildingRecord> GetOrderedRecords(IEnumerable<TreeBuildingRecord> records)
|
|
70
|
+
{
|
|
71
|
+
return records.OrderBy(record => record.RecordId).ToList();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
|
|
5
|
+
public class TreeBuildingRecord
|
|
6
|
+
{
|
|
7
|
+
public int ParentId { get; set; }
|
|
8
|
+
public int RecordId { get; set; }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public class Tree
|
|
12
|
+
{
|
|
13
|
+
public int Id { get; set; }
|
|
14
|
+
public int ParentId { get; set; }
|
|
15
|
+
|
|
16
|
+
public List<Tree> Children { get; set; }
|
|
17
|
+
|
|
18
|
+
public bool IsLeaf => Children.Count == 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static class TreeBuilder
|
|
22
|
+
{
|
|
23
|
+
public static Tree BuildTree(IEnumerable<TreeBuildingRecord> records)
|
|
24
|
+
{
|
|
25
|
+
var ordered = new SortedList<int, TreeBuildingRecord>();
|
|
26
|
+
|
|
27
|
+
foreach (var record in records)
|
|
28
|
+
{
|
|
29
|
+
ordered.Add(record.RecordId, record);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
records = ordered.Values;
|
|
33
|
+
|
|
34
|
+
var trees = new List<Tree>();
|
|
35
|
+
var previousRecordId = -1;
|
|
36
|
+
|
|
37
|
+
foreach (var record in records)
|
|
38
|
+
{
|
|
39
|
+
var t = new Tree { Children = new List<Tree>(), Id = record.RecordId, ParentId = record.ParentId };
|
|
40
|
+
trees.Add(t);
|
|
41
|
+
|
|
42
|
+
if ((t.Id == 0 && t.ParentId != 0) ||
|
|
43
|
+
(t.Id != 0 && t.ParentId >= t.Id) ||
|
|
44
|
+
(t.Id != 0 && t.Id != previousRecordId + 1))
|
|
45
|
+
{
|
|
46
|
+
throw new ArgumentException();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
++previousRecordId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (trees.Count == 0)
|
|
53
|
+
{
|
|
54
|
+
throw new ArgumentException();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (int i = 1; i < trees.Count; i++)
|
|
58
|
+
{
|
|
59
|
+
var t = trees.First(x => x.Id == i);
|
|
60
|
+
var parent = trees.First(x => x.Id == t.ParentId);
|
|
61
|
+
parent.Children.Add(t);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var r = trees.First(t => t.Id == 0);
|
|
65
|
+
return r;
|
|
66
|
+
}
|
|
67
|
+
}
|