2016-02-03 02:11:50 +00:00
|
|
|
"use strict";
|
|
|
|
|
2016-05-08 15:33:39 +00:00
|
|
|
var Trie = require("./trie.js");
|
|
|
|
var Struct = require("./struct.js");
|
2016-01-31 21:55:24 +00:00
|
|
|
var Immutable = require("immutable");
|
|
|
|
|
2016-05-08 15:33:39 +00:00
|
|
|
var __ = Trie.__;
|
|
|
|
var _$ = Trie._$;
|
2016-01-31 21:55:24 +00:00
|
|
|
|
|
|
|
function Patch(added, removed) {
|
|
|
|
this.added = added;
|
|
|
|
this.removed = removed;
|
|
|
|
}
|
|
|
|
|
2016-05-08 15:33:39 +00:00
|
|
|
var emptyPatch = new Patch(Trie.emptyTrie, Trie.emptyTrie);
|
|
|
|
var removeEverythingPatch = new Patch(Trie.emptyTrie, Trie.compilePattern(true, __));
|
|
|
|
var trueLabel = Trie.trieSuccess(true);
|
2016-01-31 21:55:24 +00:00
|
|
|
|
2016-05-10 04:40:53 +00:00
|
|
|
var observe = Struct.makeConstructor('observe', ['assertion']);
|
|
|
|
var atMeta = Struct.makeConstructor('at-meta', ['assertion']);
|
|
|
|
var advertise = Struct.makeConstructor('advertise', ['assertion']);
|
2016-02-02 23:22:29 +00:00
|
|
|
|
2016-01-31 21:55:24 +00:00
|
|
|
function prependAtMeta(p, level) {
|
|
|
|
while (level--) {
|
|
|
|
p = atMeta(p);
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2016-02-06 19:32:42 +00:00
|
|
|
function stripAtMeta(p, level) {
|
|
|
|
while (level--) {
|
2016-05-08 15:11:16 +00:00
|
|
|
if (atMeta.isClassOf(p)) {
|
2016-05-10 04:40:53 +00:00
|
|
|
p = p[0];
|
2016-02-06 19:32:42 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2016-01-31 21:55:24 +00:00
|
|
|
function observeAtMeta(p, level) {
|
|
|
|
if (level === 0) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.compilePattern(true, observe(p));
|
2016-01-31 21:55:24 +00:00
|
|
|
} else {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie._union(
|
|
|
|
Trie.compilePattern(true, observe(prependAtMeta(p, level))),
|
|
|
|
Trie.compilePattern(true, atMeta(Trie.embeddedTrie(observeAtMeta(p, level - 1)))));
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 12:41:11 +00:00
|
|
|
function _check(p) {
|
|
|
|
if (p instanceof Patch) {
|
|
|
|
throw new Error("Cannot construct patch pattern using an embedded patch");
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2016-01-31 21:55:24 +00:00
|
|
|
function assert(p, metaLevel) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.compilePattern(true, prependAtMeta(_check(p), metaLevel || 0)),
|
|
|
|
Trie.emptyTrie);
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function retract(p, metaLevel) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.emptyTrie,
|
|
|
|
Trie.compilePattern(true, prependAtMeta(_check(p), metaLevel || 0)));
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function sub(p, metaLevel) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(observeAtMeta(_check(p), metaLevel || 0), Trie.emptyTrie);
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function unsub(p, metaLevel) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.emptyTrie, observeAtMeta(_check(p), metaLevel || 0));
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function pub(p, metaLevel) {
|
2016-02-06 12:41:11 +00:00
|
|
|
return assert(advertise(_check(p)), metaLevel);
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function unpub(p, metaLevel) {
|
2016-02-06 12:41:11 +00:00
|
|
|
return retract(advertise(_check(p)), metaLevel);
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-02-02 20:52:48 +00:00
|
|
|
Patch.prototype.equals = function (other) {
|
|
|
|
if (!(other instanceof Patch)) return false;
|
|
|
|
return Immutable.is(this.added, other.added) && Immutable.is(this.removed, other.removed);
|
|
|
|
};
|
|
|
|
|
2016-01-31 21:55:24 +00:00
|
|
|
Patch.prototype.isEmpty = function () {
|
2016-05-08 15:33:39 +00:00
|
|
|
return this.added === Trie.emptyTrie && this.removed === Trie.emptyTrie;
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.isNonEmpty = function () {
|
|
|
|
return !this.isEmpty();
|
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.hasAdded = function () {
|
2016-05-10 04:40:53 +00:00
|
|
|
return !Trie.is_emptyTrie(this.added);
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.hasRemoved = function () {
|
2016-05-10 04:40:53 +00:00
|
|
|
return !Trie.is_emptyTrie(this.removed);
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.lift = function () {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.compilePattern(true, atMeta(Trie.embeddedTrie(this.added))),
|
|
|
|
Trie.compilePattern(true, atMeta(Trie.embeddedTrie(this.removed))));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
2016-05-08 15:11:16 +00:00
|
|
|
var atMetaProj = atMeta(_$);
|
2016-01-31 21:55:24 +00:00
|
|
|
Patch.prototype.drop = function () {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.project(this.added, atMetaProj),
|
|
|
|
Trie.project(this.removed, atMetaProj));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.strip = function () {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.relabel(this.added, function (v) { return true; }),
|
|
|
|
Trie.relabel(this.removed, function (v) { return true; }));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.label = function (labelValue) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.relabel(this.added, function (v) { return labelValue; }),
|
|
|
|
Trie.relabel(this.removed, function (v) { return labelValue; }));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.limit = function (bound) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.subtract(this.added, bound, function (v1, v2) { return Trie.emptyTrie; }),
|
|
|
|
Trie.intersect(this.removed, bound,
|
|
|
|
function (v1, v2) { return Trie.trieSuccess(v1); }));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var metaLabelSet = Immutable.Set(["meta"]);
|
2016-02-02 19:36:47 +00:00
|
|
|
Patch.prototype.computeAggregate = function (label, base, removeMeta /* optional flag */) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.subtract(this.added, base, addCombiner),
|
|
|
|
Trie.subtract(this.removed, base, removeCombiner));
|
2016-01-31 21:55:24 +00:00
|
|
|
|
|
|
|
function addCombiner(v1, v2) {
|
|
|
|
if (removeMeta && Immutable.is(v2, metaLabelSet)) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.trieSuccess(v1);
|
2016-01-31 21:55:24 +00:00
|
|
|
} else {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.emptyTrie;
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeCombiner(v1, v2) {
|
|
|
|
if (v2.size === 1) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.trieSuccess(v1);
|
2016-01-31 21:55:24 +00:00
|
|
|
} else {
|
|
|
|
if (removeMeta && v2.size === 2 && v2.has("meta")) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.trieSuccess(v1);
|
2016-01-31 21:55:24 +00:00
|
|
|
} else {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.emptyTrie;
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.applyTo = function (base) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie._union(Trie.subtract(base, this.removed), this.added);
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.updateInterests = function (base) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie._union(Trie.subtract(base,
|
|
|
|
this.removed,
|
|
|
|
function (v1, v2) { return Trie.emptyTrie; }),
|
|
|
|
this.added,
|
|
|
|
function (v1, v2) { return trueLabel; });
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.unapplyTo = function (base) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie._union(Trie.subtract(base, this.added), this.removed);
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.andThen = function (nextPatch) {
|
|
|
|
return new Patch(nextPatch.updateInterests(this.added),
|
2016-05-08 15:33:39 +00:00
|
|
|
Trie._union(Trie.subtract(this.removed,
|
|
|
|
nextPatch.added,
|
|
|
|
function (v1, v2) { return Trie.emptyTrie; }),
|
|
|
|
nextPatch.removed,
|
|
|
|
function (v1, v2) { return trueLabel; }));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
function patchSeq(/* patch, patch, ... */) {
|
|
|
|
var p = emptyPatch;
|
|
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
|
|
p = p.andThen(arguments[i]);
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
function computePatch(oldBase, newBase) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.subtract(newBase, oldBase),
|
|
|
|
Trie.subtract(oldBase, newBase));
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function biasedIntersection(object, subject) {
|
2016-05-10 04:40:53 +00:00
|
|
|
subject = Trie.trieStep(subject, observe.meta.arity, observe.meta);
|
2016-05-08 15:33:39 +00:00
|
|
|
return Trie.intersect(object, subject, function (v1, v2) { return Trie.trieSuccess(v1); });
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Patch.prototype.viewFrom = function (interests) {
|
|
|
|
return new Patch(biasedIntersection(this.added, interests),
|
|
|
|
biasedIntersection(this.removed, interests));
|
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.unsafeUnion = function (other) {
|
|
|
|
// Unsafe because does not necessarily preserve invariant that added
|
|
|
|
// and removed are disjoint.
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie._union(this.added, other.added),
|
|
|
|
Trie._union(this.removed, other.removed));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.project = function (compiledProjection) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return new Patch(Trie.project(this.added, compiledProjection),
|
|
|
|
Trie.project(this.removed, compiledProjection));
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Patch.prototype.projectObjects = function (compiledProjection) {
|
2016-05-08 15:33:39 +00:00
|
|
|
return [Trie.projectObjects(this.added, compiledProjection),
|
|
|
|
Trie.projectObjects(this.removed, compiledProjection)];
|
2016-01-31 21:55:24 +00:00
|
|
|
};
|
|
|
|
|
2016-02-03 02:02:55 +00:00
|
|
|
Patch.prototype.pretty = function () {
|
2016-05-08 15:33:39 +00:00
|
|
|
return ("<<<<<<<< Removed:\n" + Trie.prettyTrie(this.removed) + "\n" +
|
|
|
|
"======== Added:\n" + Trie.prettyTrie(this.added) + "\n" +
|
2016-02-07 02:56:47 +00:00
|
|
|
">>>>>>>>");
|
2016-05-10 04:40:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Completely ignores success-values in t.
|
|
|
|
Patch.prototype.prunedBy = function (t) {
|
|
|
|
return new Patch(Trie.subtract(this.added, t, function (v1, v2) { return Trie.emptyTrie; }),
|
|
|
|
Trie.subtract(this.removed, t, function (v1, v2) { return Trie.emptyTrie; }));
|
|
|
|
};
|
|
|
|
|
|
|
|
var atMetaEverything = Trie.compilePattern(true, atMeta.pattern);
|
|
|
|
Patch.prototype.withoutAtMeta = function () {
|
|
|
|
return this.prunedBy(atMetaEverything);
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Patch.prototype.toJSON = function () {
|
|
|
|
return [Trie.trieToJSON(this.added), Trie.trieToJSON(this.removed)];
|
|
|
|
};
|
|
|
|
|
|
|
|
function fromJSON(j) {
|
|
|
|
return new Patch(Trie.trieFromJSON(j[0]), Trie.trieFromJSON(j[1]));
|
2016-01-31 21:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
module.exports.Patch = Patch;
|
|
|
|
module.exports.emptyPatch = emptyPatch;
|
2016-02-02 19:36:31 +00:00
|
|
|
module.exports.removeEverythingPatch = removeEverythingPatch;
|
2016-01-31 21:55:24 +00:00
|
|
|
|
|
|
|
module.exports.observe = observe;
|
|
|
|
module.exports.atMeta = atMeta;
|
|
|
|
module.exports.advertise = advertise;
|
|
|
|
|
|
|
|
module.exports.prependAtMeta = prependAtMeta;
|
2016-02-06 19:32:42 +00:00
|
|
|
module.exports.stripAtMeta = stripAtMeta;
|
2016-01-31 21:55:24 +00:00
|
|
|
module.exports.observeAtMeta = observeAtMeta;
|
|
|
|
module.exports.assert = assert;
|
|
|
|
module.exports.retract = retract;
|
|
|
|
module.exports.sub = sub;
|
|
|
|
module.exports.unsub = unsub;
|
|
|
|
module.exports.pub = pub;
|
|
|
|
module.exports.unpub = unpub;
|
|
|
|
|
|
|
|
module.exports.patchSeq = patchSeq;
|
|
|
|
module.exports.computePatch = computePatch;
|
|
|
|
module.exports.biasedIntersection = biasedIntersection;
|
2016-05-10 04:40:53 +00:00
|
|
|
|
|
|
|
module.exports.fromJSON = fromJSON;
|