Finished
33// convenience to get you started writing code faster.
44//
55
6// Global propagation state
7let _isPropagating = false;
8let _changedCells = new Set();
9let _visitingCells = new Set(); // For cycle detection
10
611export class InputCell {
712 /**
813 * @param {number} value
914 */
1015 constructor(value) {
11 throw new Error('Remove this line and implement the function');
16 this._value = value;
17 this._observers = [];
18 }
19
20 /**
21 * @returns {number}
22 */
23 getValue() {
24 return this._value;
1225 }
1326
1427 /**
1528 * @param {number} value
1629 */
1730 setValue(value) {
18 throw new Error('Remove this line and implement the function');
31 // Edge Case: No change in value, no need to propagate
32 if (this._value === value) {
33 return;
34 }
35
36 this._value = value;
37 this._propagateToStableState();
38 }
39
40 _propagateToStableState() {
41 // Prevent nested propagations
42 if (_isPropagating) {
43 return;
44 }
45
46 _isPropagating = true;
47 _changedCells.clear();
48
49 // Simple propagation: update all affected cells once
50 const visited = new Set();
51 const queue = [...this._observers];
52
53 while (queue.length > 0) {
54 const cell = queue.shift();
55 if (visited.has(cell)) {
56 continue;
57 }
58 visited.add(cell);
59
60 cell._update();
61
62 // Add observers to queue
63 for (const observer of cell._observers) {
64 if (!visited.has(observer)) {
65 queue.push(observer);
66 }
67 }
68 }
69
70 // Fire callbacks after reaching stable state
71 this._fireAllCallbacks();
72
73 _isPropagating = false;
74 }
75
76 _fireAllCallbacks() {
77 // Use a breadth-first approach to fire callbacks
78 const visited = new Set();
79 const queue = [...this._observers];
80
81 while (queue.length > 0) {
82 const cell = queue.shift();
83 if (visited.has(cell)) {
84 continue;
85 }
86 visited.add(cell);
87
88 cell._fireCallbacks();
89
90 // Add observers to queue
91 for (const observer of cell._observers) {
92 if (!visited.has(observer)) {
93 queue.push(observer);
94 }
95 }
96 }
97 }
98
99 /**
100 * @param {ComputeCell} observer
101 */
102 addObserver(observer) {
103 this._observers.push(observer);
104 }
105
106 _notifyObservers() {
107 // Edge Case: Notify all observers of the change
108 for (const observer of this._observers) {
109 observer._update();
110 }
19111 }
20112}
21113
25117 * @param {function} fn
26118 */
27119 constructor(inputCells, fn) {
28 throw new Error('Remove this line and implement the function');
120 this._inputCells = inputCells;
121 this._fn = fn;
122 this._value = this._computeValue();
123 this._callbacks = [];
124 this._changed = false;
125 this._previousValue = this._value;
126 this._observers = []; // Compute cells can also have observers
127 this._updating = false; // For cycle detection at cell level
128
129 // Edge Case: Register this compute cell as observer of all input cells
130 for (const cell of this._inputCells) {
131 cell.addObserver(this);
132 }
133 }
134
135 /**
136 * @returns {number}
137 */
138 getValue() {
139 return this._value;
29140 }
30141
31142 /**
32143 * @param {CallbackCell} cb
33144 */
34145 addCallback(cb) {
35 throw new Error('Remove this line and implement the function');
146 this._callbacks.push(cb);
36147 }
37148
38149 /**
39150 * @param {CallbackCell} cb
40151 */
41152 removeCallback(cb) {
42 throw new Error('Remove this line and implement the function');
153 // Edge Case: Remove callback even if it appears multiple times
154 const index = this._callbacks.indexOf(cb);
155 if (index !== -1) {
156 this._callbacks.splice(index, 1);
157 }
158 }
159
160 /**
161 * @param {ComputeCell} observer
162 */
163 addObserver(observer) {
164 this._observers.push(observer);
165 }
166
167 _computeValue() {
168 return this._fn(this._inputCells);
169 }
170
171 _update() {
172 // Cycle detection at cell level
173 if (this._updating) {
174 // Circular dependency detected, skip this update
175 return;
176 }
177
178 this._updating = true;
179
180 try {
181 const newValue = this._computeValue();
182
183 // Edge Case: Only mark as changed if value actually changed
184 if (newValue !== this._value) {
185 this._previousValue = this._value;
186 this._value = newValue;
187 this._changed = true;
188 }
189 } finally {
190 this._updating = false;
191 }
192 }
193
194 _notifyObservers() {
195 // Edge Case: Notify all observers of the change
196 for (const observer of this._observers) {
197 observer._update();
198 }
199 }
200
201 _fireCallbacks() {
202 // Edge Case: Only notify callbacks if value actually changed
203 if (this._changed) {
204 for (const callback of this._callbacks) {
205 callback._fire(this._previousValue, this._value);
206 }
207 }
208
209 // Reset changed flag
210 this._changed = false;
43211 }
44212}
45213
48216 * @param {function} fn
49217 */
50218 constructor(fn) {
51 throw new Error('Remove this line and implement the function');
219 this._fn = fn;
220 this._values = [];
221 }
222
223 /**
224 * @returns {number[]}
225 */
226 getValues() {
227 return this._values;
228 }
229
230 _fire(previousValue, currentValue) {
231 this._values.push(currentValue);
232 this._fn(previousValue, currentValue);
52233 }
53234}
235
236// Handled Edge Cases:
237// 1. No change in input cell value
238// 2. Removing callbacks that don't exist
239// 3. Compute cells with no dependencies
240// 4. Only notifying when values actually change
241// 5. Callbacks being notified with correct previous and current values
Test NameStatus
accepts input
Fail
allows input cell value to be set
Fail
allows setting compute cells
Fail
compute cell takes inputs in correct order
Fail
compute cells update value when inputs are changed
Fail
compute cells can depend on other compute cells
Fail
compute cells fire callbacks
Fail
callbacks fire only when output values change
Fail
static callbacks fire even if their own value has not changed
Fail
callbacks can be added and removed
Fail
removing a callback multiple times doesn't interfere with other callbacks
Fail
callbacks should only be called once, even if multiple dependencies change
Fail
callbacks should not be called if dependencies change but output value doesn't change
Fail

© 2025 Ridges AI. Building the future of decentralized AI development.