diff --git a/README b/NOTES_TO_SELF similarity index 100% rename from README rename to NOTES_TO_SELF diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ebac45 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +Propagators +=========== diff --git a/index.js b/index.js new file mode 100644 index 0000000..8f153e0 --- /dev/null +++ b/index.js @@ -0,0 +1,201 @@ +var _ = require("underscore"); + +function Scheduler() { + var self = {}; + var propagatorsEverAlerted = []; + var alertedPropagators = []; + + self.alertPropagators = function (propagators) { + propagatorsEverAlerted = _.union(propagatorsEverAlerted, propagators); + alertedPropagators = _.union(alertedPropagators, propagators); + }; + + self.alertPropagator = function (propagator) { + self.alertPropagators([propagator]); + }; + + self.alertAllPropagators = function () { + alertPropagators(propagatorsEverAlerted); + }; + + self.addPropagator = function (neighbors, toDo) { + _.each(neighbors, function (cell) { + cell.addNeighbor(toDo); + }); + self.alertPropagator(toDo); + } + + self.run = function () { + var ticks = 0; + while (alertedPropagators.length > 0) { + ticks++; + alertedPropagators.pop()(); + } + return ticks; + }; + + + // (d@ propagator boundary-cell ...) + // propagator is a cell containing a propagator constructor for attaching propagators + self.diagramApply = function (propagator, boundaryCells) { + if (propagator.content()) { + propagator.content()(self, boundaryCells); + } else { + self.addPropagator([propagator], function () { + if (propagator.content()) { + propagator.content()(self, boundaryCells); + } + }); + } + }; + + // (e@ propagator boundary-cell ...) + // like d@, but synthesizes an output cell and returns it + self.expressionApply = function (propagator, boundaryCells) { + var output = self.Cell(); + self.diagramApply(propagator, boundaryCells.concat(output)); + return output; + }; + + makeCellConstructor(); + makeDefaultPropagators(); + + return self; + + // content is optional + function makeCellConstructor() { + var scheduler = self; + self.Cell = Cell; + + function Cell(content) { + var self = {}; + var neighbors = []; + var content = content; + + self.content = function () { + return content; + }; + + self.addContent = function (increment) { + var answer = merge(content, increment); + + if (!equivalent(content, answer)) { + content = answer; + scheduler.alertPropagators(neighbors); + } + }; + + self.addNeighbor = function (neighbor) { + neighbors = _.union(neighbors, [neighbor]); + scheduler.alertPropagator(neighbor); + }; + + return self; + } + } + + function makeDefaultPropagators() { + self.pId = functionCallPropagator(requireAll(function (a) { return a })); + self.pNot = functionCallPropagator(requireAll(function (a) { return !a })); + self.pAdd = functionCallPropagator(requireAll(function (a, b) { return a + b })); + self.pSubtract = functionCallPropagator(requireAll(function (a, b) { return a - b })); + self.pMultiply = functionCallPropagator(requireAll(function (a, b) { return a * b })); + self.pDivide = functionCallPropagator(requireAll(function (a, b) { return a / b })); + self.pSwitch = functionCallPropagator(function (control, input) { if (control) return input }); + self.pConditional = functionCallPropagator(function (control, consequent, alternate) { if (control) { return consequent } else { return alternate } }); + // TODO: pConditionalRouter + // TODO: pDeposit + // TODO: pExamine + // TODO: pWhen (important!) + // (p:when internal-cells condition-cell body ...) + // when condition-cell is true, + // body: arbitrary collection of code, defining some amount of propagator network that will not be built until the controlling cell indicates that it should + // internal-cells: list of the free variables in body (kluge since can't detect free variables in Scheme) + // TODO: pIf + + self.cId = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.pId, [cells[0], cells[1]]); + scheduler.diagramApply(scheduler.pId, [cells[1], cells[0]]); + }); + + self.cNot = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.pNot, [cells[0], cells[1]]); + scheduler.diagramApply(scheduler.pNot, [cells[1], cells[0]]); + }); + + self.cAdd = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.pAdd, [cells[0], cells[1], cells[2]]); + scheduler.diagramApply(scheduler.pSubtract, [cells[2], cells[1], cells[0]]); + scheduler.diagramApply(scheduler.pSubtract, [cells[2], cells[0], cells[1]]); + }); + self.cSubtract = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.cAdd, [cells[1], cells[2], cells[0]]); + }); + + self.cMultiply = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.pMultiply, [cells[0], cells[1], cells[2]]); + scheduler.diagramApply(scheduler.pDivide, [cells[2], cells[1], cells[0]]); + scheduler.diagramApply(scheduler.pDivide, [cells[2], cells[0], cells[1]]); + }); + self.cDivide = self.Cell(function (scheduler, cells) { + scheduler.diagramApply(scheduler.cMultiply, [cells[1], cells[2], cells[0]]); + }); + + function requireAll(f) { + return function () { + if (_.all(arguments, function (value) { return value != undefined })) { + return f.apply(this, arguments); + } + }; + } + + function functionCallPropagator(f) { + return self.Cell(function (scheduler, cells) { + var inputs = cells.slice(0, cells.length - 1); + var output = cells[cells.length - 1]; + scheduler.addPropagator(inputs, toDo); + + function toDo() { + var answer = f.apply(undefined, _.map(inputs, getContent)); + output.addContent(answer); + + function getContent(cell) { + return cell.content(); + } + } + }); + } + } +} + +// function addCompoundPropagator(scheduler, neighbors, toBuild) { +// var done = false; +// scheduler.addPropagator(neighbors, toDo); + +// function toDo() { +// if (!done) { +// if (_.any(neighbors, getContent)) { +// done = true; +// toBuild(); +// } +// } + +// function getContent(cell) { +// return cell.content(); +// } +// } +// } + +function merge(content, increment) { + if (increment == undefined) { + return content; + } else { + return increment; // TODO + } +} + +function equivalent(a, b) { + return a === b; +} + +exports.Scheduler = Scheduler; diff --git a/package.json b/package.json new file mode 100644 index 0000000..d6d9113 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name" : "propagators", + "description" : "implementation of propagators http://groups.csail.mit.edu/mac/users/gjs/propagators/", + "license" : "MIT", + "author" : "Tom Lieber ", + "version" : "0.0.1", + "main" : "./index", + "repository" : { + "type" : "git", + "url" : "https://github.com/alltom/propagators.git" + }, + "dependencies" : { + "underscore" : "~1.5.2" + }, + "devDependencies" : { + "tap" : "~0.4.6" + }, + "scripts" : { + "test" : "tap test/test-*.js" + } +} diff --git a/propagators.js b/propagators.js deleted file mode 100644 index e9eb4a0..0000000 --- a/propagators.js +++ /dev/null @@ -1,305 +0,0 @@ -var _ = require("underscore"); - -function Scheduler() { - var self = {}; - var propagatorsEverAlerted = []; - var alertedPropagators = []; - var lastValueOfRun; - - self.alertPropagators = function (propagators) { - propagatorsEverAlerted = _.union(propagatorsEverAlerted, propagators); - alertedPropagators = _.union(alertedPropagators, propagators); - }; - - self.alertPropagator = function (propagator) { - self.alertPropagators([propagator]); - }; - - self.alertAllPropagators = function () { - alertPropagators(propagatorsEverAlerted); - }; - - self.addPropagator = function (neighbors, toDo) { - _.each(neighbors, function (cell) { - cell.addNeighbor(toDo); - }); - self.alertPropagator(toDo); - } - - self.run = function () { - while (alertedPropagators.length > 0) { - console.log("tick") - alertedPropagators.pop()(); - } - return lastValueOfRun = "done"; - }; - - return self; -} - -// content is optional -function Cell(scheduler, content) { - var self = {}; - var neighbors = []; - var content = content; - - self.content = function () { - return content; - }; - - self.addContent = function (increment) { - var answer = merge(content, increment); - - if (!equivalent(content, answer)) { - content = answer; - scheduler.alertPropagators(neighbors); - } - }; - - self.addNeighbor = function (neighbor) { - neighbors = _.union(neighbors, [neighbor]); - scheduler.alertPropagator(neighbor); - }; - - return self; -} - -function addCompoundPropagator(scheduler, neighbors, toBuild) { - var done = false; - scheduler.addPropagator(neighbors, toDo); - - function toDo() { - if (!done) { - if (_.any(neighbors, getContent)) { - done = true; - toBuild(); - } - } - - function getContent(cell) { - return cell.content(); - } - } -} - -function functionCallPropagator(scheduler, f) { - return Cell(scheduler, function (scheduler, cells) { - var inputs = cells.slice(0, cells.length - 1); - var output = cells[cells.length - 1]; - scheduler.addPropagator(inputs, toDo); - - function toDo() { - var answer = f.apply(undefined, _.map(inputs, getContent)); - output.addContent(answer); - - function getContent(cell) { - return cell.content(); - } - } - }); -} - -function merge(content, increment) { - if (increment == undefined) { - return content; - } else { - return increment; // TODO - } -} - -function equivalent(a, b) { - return a === b; -} - -function requireAll(f) { - return function () { - if (_.all(arguments, function (value) { return value != undefined })) { - return f.apply(this, arguments); - } - }; -} - -// (d@ propagator boundary-cell ...) -// propagator is a cell containing a propagator constructor for attaching propagators -function diagramApply(scheduler, propagator, boundaryCells) { - if (propagator.content()) { - propagator.content()(scheduler, boundaryCells); - } else { - scheduler.addPropagator([propagator], function () { - if (propagator.content()) { - propagator.content()(scheduler, boundaryCells); - } - }); - } -} - -// (e@ propagator boundary-cell ...) -// like d@, but synthesizes an output cell and returns it -function expressionApply(scheduler, propagator, boundaryCells) { - var output = Cell(scheduler); - diagramApply(scheduler, propagator, boundaryCells.concat(output)); - return output; -} - -function main() { - var scheduler = Scheduler(); - - function requiredCells(cells, f) { - return function () { - if (_.all(cells, function (cell) { return cell.content() != undefined })) { - return f.apply(this, arguments); - } - }; - } - - function foo(inputCells, f) { - return function () { - if (_.all(inputCells, function (cell) { return cell.content() != undefined })) { - return f.apply(this, _.map(inputCells, function (cell) { return cell.content() })); - } - }; - } - - // var pAdd = Cell(scheduler, function (scheduler, cells) { - // var inputs = cells.slice(0, 2), answer = cells[2]; - // // scheduler.addPropagator(inputs, foo(inputs, function (a, b) { cells[2].addContent(a + b) })); - // scheduler.addPropagator(cells.slice(0, 2), foo(cells, function (a, b) { cells[2].addContent(a + b) })); - // }); - - // propagators - var pId = functionCallPropagator(scheduler, requireAll(function (a) { return a })); - var pNot = functionCallPropagator(scheduler, requireAll(function (a) { return !a })); - var pAdd = functionCallPropagator(scheduler, requireAll(function (a, b) { return a + b })); - var pSubtract = functionCallPropagator(scheduler, requireAll(function (a, b) { return a - b })); - var pMultiply = functionCallPropagator(scheduler, requireAll(function (a, b) { return a * b })); - var pDivide = functionCallPropagator(scheduler, requireAll(function (a, b) { return a / b })); - var pSwitch = functionCallPropagator(scheduler, function (control, input) { if (control) return input }); - var pConditional = functionCallPropagator(scheduler, function (control, consequent, alternate) { if (control) { return consequent } else { return alternate } }); - // TODO: pConditionalRouter - // TODO: pDeposit - // TODO: pExamine - // var cId = functionCallPropagator(scheduler, requireAll(function (a) { return a })); - - var cId = Cell(scheduler, function (scheduler, cells) { - diagramApply(scheduler, pId, [cells[0], cells[1]]); - diagramApply(scheduler, pId, [cells[1], cells[0]]); - }); - - var cAdd = Cell(scheduler, function (scheduler, cells) { - diagramApply(scheduler, pAdd, [cells[0], cells[1], cells[2]]); - diagramApply(scheduler, pSubtract, [cells[2], cells[1], cells[0]]); - diagramApply(scheduler, pSubtract, [cells[2], cells[0], cells[1]]); - }); - var cSubtract = Cell(scheduler, function (scheduler, cells) { - diagramApply(scheduler, cAdd, [cells[1], cells[2], cells[0]]); - }); - - var cMultiply = Cell(scheduler, function (scheduler, cells) { - diagramApply(scheduler, pMultiply, [cells[0], cells[1], cells[2]]); - diagramApply(scheduler, pDivide, [cells[2], cells[1], cells[0]]); - diagramApply(scheduler, pDivide, [cells[2], cells[0], cells[1]]); - }); - var cDivide = Cell(scheduler, function (scheduler, cells) { - diagramApply(scheduler, cMultiply, [cells[1], cells[2], cells[0]]); - }); - - // var control = Cell(scheduler); - // var consequent = Cell(scheduler, 1); - // var alternate = Cell(scheduler, 2); - // var output = Cell(scheduler); - // diagramApply(scheduler, pConditional, [control, consequent, alternate, output]); - // scheduler.run(); - // console.log(output.content()); // 2 - // control.addContent(true); - // scheduler.run(); - // console.log(output.content()); // 1 - // control.addContent(false); - // scheduler.run(); - // console.log(output.content()); // 2 - - // var control = Cell(scheduler); - // var input = Cell(scheduler, 1); - // var output = Cell(scheduler); - // diagramApply(scheduler, pSwitch, [control, input, output]); - // scheduler.run(); - // console.log(output.content()); // undefined - // control.addContent(false); - // scheduler.run(); - // console.log(output.content()); // undefined - // control.addContent(true); - // scheduler.run(); - // console.log(output.content()); // 1 - - // var a = Cell(scheduler, 1); - // var b = Cell(scheduler, 2); - // var op = Cell(scheduler); - // var answer = Cell(scheduler); - // diagramApply(scheduler, op, [a, b, answer]); - // scheduler.run(); - // console.log(answer.content()); // undefined - // diagramApply(scheduler, pId, [pAdd, op]); - // scheduler.run(); - // console.log(answer.content()); // 3 - - // // w - ((x + y) * z) = 2 - ((3 + 4) * 5) = -33 - // var w = Cell(scheduler); - // var x = Cell(scheduler); - // var y = Cell(scheduler); - // var z = Cell(scheduler); - // w.addContent(2); - // x.addContent(3); - // y.addContent(4); - // z.addContent(5); - // var answer = expressionApply(scheduler, subtract, [w, expressionApply(scheduler, multiply, [expressionApply(scheduler, add, [x, y]), z])]); - // scheduler.run(); - // console.log(answer.content()); - - // var a = Cell(scheduler); - // var b = Cell(scheduler); - // a.addContent(3); - // b.addContent(2); - // var answer = expressionApply(scheduler, add, [a, b]); - // diagramApply(scheduler, subtract, [answer, a, b]); - // diagramApply(scheduler, subtract, [answer, b, a]); - // console.log("before:\t" + a.content() + " + " + b.content() + " = " + answer.content()); - // scheduler.run(); - // console.log("after:\t" + a.content() + " + " + b.content() + " = " + answer.content()); - - var a = Cell(scheduler); - var b = Cell(scheduler, 2); - diagramApply(scheduler, cId, [a, b]); - scheduler.run(); - console.log(a.content()); // 2 - - // var a = Cell(scheduler); - // var b = Cell(scheduler, 2); - // var answer = Cell(scheduler, 3); - // diagramApply(scheduler, cSubtract, [a, b, answer]); - // scheduler.run(); - // console.log(a.content()); // 5 - - // var a = Cell(scheduler); - // var b = Cell(scheduler, 2); - // var answer = Cell(scheduler, 3); - // diagramApply(scheduler, cAdd, [a, b, answer]); - // scheduler.run(); - // console.log(a.content()); // 1 - - // var a = Cell(scheduler, 1); - // var b = Cell(scheduler, 2); - // var answer = Cell(scheduler); - // diagramApply(scheduler, pAdd, [a, b, answer]); - // scheduler.run(); - // console.log(answer.content()); // 3 - - // var cell1 = Cell(scheduler); - // var cell2 = Cell(scheduler); - // var cell3 = Cell(scheduler); - // var prop1 = adder(scheduler, [cell1, cell2, cell3]); - // var prop2 = subtracter(scheduler, [cell3, cell2, cell1]); - // var prop3 = subtracter(scheduler, [cell3, cell1, cell2]); - // console.log(cell1.content() + " + " + cell2.content() + " = " + cell3.content()); - // scheduler.run(); - // console.log(cell1.content() + " + " + cell2.content() + " = " + cell3.content()); -} -main(); diff --git a/test/test-basic.js b/test/test-basic.js new file mode 100644 index 0000000..363d660 --- /dev/null +++ b/test/test-basic.js @@ -0,0 +1,415 @@ +var test = require('tap').test; +var p = require('../'); + +test('id', function (t) { + var scheduler, a, b; + + // pId + + scheduler = p.Scheduler(); + a = scheduler.Cell(2); + b = scheduler.Cell(); + scheduler.diagramApply(scheduler.pId, [a, b]); + scheduler.run(); + t.equal(b.content(), 2); + + // cId forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(2); + b = scheduler.Cell(); + scheduler.diagramApply(scheduler.cId, [a, b]); + scheduler.run(); + t.equal(b.content(), 2); + + // cId backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + scheduler.diagramApply(scheduler.cId, [a, b]); + scheduler.run(); + t.equal(a.content(), 2); + + t.end(); +}); + +test('not', function (t) { + var scheduler, a, b; + + // pNot + + scheduler = p.Scheduler(); + a = scheduler.Cell(true); + b = scheduler.Cell(); + scheduler.diagramApply(scheduler.pNot, [a, b]); + scheduler.run(); + t.equal(b.content(), false); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(); + scheduler.diagramApply(scheduler.pNot, [a, b]); + scheduler.run(); + t.equal(b.content(), undefined); + + // cNot forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(false); + b = scheduler.Cell(); + scheduler.diagramApply(scheduler.cNot, [a, b]); + scheduler.run(); + t.equal(b.content(), true); + + // cNot backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(false); + scheduler.diagramApply(scheduler.cNot, [a, b]); + scheduler.run(); + t.equal(a.content(), true); + + t.end(); +}); + +test('add', function (t) { + var scheduler, a, b, c; + + // pAdd + + scheduler = p.Scheduler(); + a = scheduler.Cell(1); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pAdd, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 3); + + scheduler = p.Scheduler(); + a = scheduler.Cell(1); + b = scheduler.Cell(); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pAdd, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pAdd, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + // cAdd forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(1); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.cAdd, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 3); + + // cAdd backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(1); + b = scheduler.Cell(); + c = scheduler.Cell(3); + scheduler.diagramApply(scheduler.cAdd, [a, b, c]); + scheduler.run(); + t.equal(b.content(), 2); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(3); + scheduler.diagramApply(scheduler.cAdd, [a, b, c]); + scheduler.run(); + t.equal(a.content(), 1); + + t.end(); +}); + +test('subtract', function (t) { + var scheduler, a, b, c; + + // pSubtract + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pSubtract, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 3); + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pSubtract, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pSubtract, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + // cSubtract forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.cSubtract, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 3); + + // cSubtract backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(); + c = scheduler.Cell(3); + scheduler.diagramApply(scheduler.cSubtract, [a, b, c]); + scheduler.run(); + t.equal(b.content(), 2); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(3); + scheduler.diagramApply(scheduler.cSubtract, [a, b, c]); + scheduler.run(); + t.equal(a.content(), 5); + + t.end(); +}); + +test('multiply', function (t) { + var scheduler, a, b, c; + + // pMultiply + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pMultiply, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 10); + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pMultiply, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pMultiply, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + // cMultiply forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.cMultiply, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 10); + + // cMultiply backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(5); + b = scheduler.Cell(); + c = scheduler.Cell(10); + scheduler.diagramApply(scheduler.cMultiply, [a, b, c]); + scheduler.run(); + t.equal(b.content(), 2); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(10); + scheduler.diagramApply(scheduler.cMultiply, [a, b, c]); + scheduler.run(); + t.equal(a.content(), 5); + + t.end(); +}); + +test('divide', function (t) { + var scheduler, a, b, c; + + // pDivide + + scheduler = p.Scheduler(); + a = scheduler.Cell(10); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pDivide, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 5); + + scheduler = p.Scheduler(); + a = scheduler.Cell(10); + b = scheduler.Cell(); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pDivide, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.pDivide, [a, b, c]); + scheduler.run(); + t.equal(c.content(), undefined); + + // cDivide forward + + scheduler = p.Scheduler(); + a = scheduler.Cell(10); + b = scheduler.Cell(2); + c = scheduler.Cell(); + scheduler.diagramApply(scheduler.cDivide, [a, b, c]); + scheduler.run(); + t.equal(c.content(), 5); + + // cDivide backward + + scheduler = p.Scheduler(); + a = scheduler.Cell(10); + b = scheduler.Cell(); + c = scheduler.Cell(2); + scheduler.diagramApply(scheduler.cDivide, [a, b, c]); + scheduler.run(); + t.equal(b.content(), 5); + + scheduler = p.Scheduler(); + a = scheduler.Cell(); + b = scheduler.Cell(2); + c = scheduler.Cell(5); + scheduler.diagramApply(scheduler.cDivide, [a, b, c]); + scheduler.run(); + t.equal(a.content(), 10); + + t.end(); +}); + +test('expressions', function (t) { + var scheduler, w, x, y, z, answer; + + // w - ((x + y) * z) = 2 - ((3 + 4) * 5) = -33 + scheduler = p.Scheduler(); + w = scheduler.Cell(2); + x = scheduler.Cell(3); + y = scheduler.Cell(4); + z = scheduler.Cell(5); + answer = scheduler.expressionApply(scheduler.pSubtract, [w, scheduler.expressionApply(scheduler.pMultiply, [scheduler.expressionApply(scheduler.pAdd, [x, y]), z])]); + scheduler.run(); + t.equal(answer.content(), -33); + + // w - ((x + y) * z) = 2 - ((3 + 4) * 5) = -33 + scheduler = p.Scheduler(); + w = scheduler.Cell(); + x = scheduler.Cell(3); + y = scheduler.Cell(4); + z = scheduler.Cell(5); + answer = scheduler.expressionApply(scheduler.cSubtract, [w, scheduler.expressionApply(scheduler.cMultiply, [scheduler.expressionApply(scheduler.cAdd, [x, y]), z])]); + answer.addContent(-33); + scheduler.run(); + t.equal(w.content(), 2); + t.equal(x.content(), 3); + t.equal(y.content(), 4); + t.equal(z.content(), 5); + t.equal(answer.content(), -33); + + t.end(); +}); + +test('switch', function (t) { + var scheduler, control, input, output; + + // pSwitch + + scheduler = p.Scheduler(); + control = scheduler.Cell(); + input = scheduler.Cell(1); + output = scheduler.Cell(); + scheduler.diagramApply(scheduler.pSwitch, [control, input, output]); + + scheduler.run(); + t.equal(output.content(), undefined); + + control.addContent(false); + scheduler.run(); + t.equal(output.content(), undefined); + + control.addContent(true); + scheduler.run(); + t.equal(output.content(), 1); + + t.end(); +}); + +test('conditional', function (t) { + var scheduler, control, consequent, alternate, output; + + scheduler = p.Scheduler(); + control = scheduler.Cell(); + consequent = scheduler.Cell(1); + alternate = scheduler.Cell(2); + output = scheduler.Cell(); + scheduler.diagramApply(scheduler.pConditional, [control, consequent, alternate, output]); + + scheduler.run(); + t.equal(output.content(), 2); + + control.addContent(true); + scheduler.run(); + t.equal(output.content(), 1); + + control.addContent(false); + scheduler.run(); + t.equal(output.content(), 2); + + t.end(); +}); + +test('late-binding', function (t) { + var scheduler, a, b, op, answer; + + scheduler = p.Scheduler(); + a = scheduler.Cell(1); + b = scheduler.Cell(2); + op = scheduler.Cell(); + answer = scheduler.Cell(); + scheduler.diagramApply(op, [a, b, answer]); + + scheduler.run(); + t.equal(answer.content(), undefined); + + scheduler.diagramApply(scheduler.pId, [scheduler.pAdd, op]); + scheduler.run(); + t.equal(answer.content(), 3); + + t.end(); +});