can-query-logic
Version:
978 lines (876 loc) • 26.6 kB
JavaScript
var set = require("../set");
var arrayUnionIntersectionDifference = require("../array-union-intersection-difference");
var common = require("./comparisons-common");
var arrayComparisons = require("./array-comparisons");
var canReflect = require("can-reflect");
var canSymbol = require("can-symbol");
var isMemberSymbol = canSymbol.for("can.isMember");
// $ne Matches all values that are not equal to a specified value.
// $eq Matches values that are equal to a specified value.
//
// $gt Matches values that are greater than a specified value.
// $gte Matches values that are greater than or equal to a specified value.
// $lt Matches values that are less than a specified value.
// $lte Matches values that are less than or equal to a specified value.
// $in Matches any of the values specified in an array.
// $nin Matches none of the values specified in an array.
var comparisons = canReflect.assign(arrayComparisons.comparisons, {
In: function In(values) {
// TODO: change this to store as `Set` later.
this.values = values;
},
NotIn: function NotIn(values) {
this.values = values;
},
GreaterThan: function GreaterThan(value) {
this.value = value;
},
GreaterThanEqual: function GreaterThanEqual(value) {
this.value = value;
},
LessThan: function LessThan(value) {
this.value = value;
},
LessThanEqual: function LessThanEqual(value) {
this.value = value;
},
// This is used to And something like `GT(3)` n `LT(4)`.
// These are all value comparisons.
And: function ValueAnd(ands) {
this.values = ands;
},
// This is used to OR something like `GT(4)` n `LT(3)`.
// These are all value comparisons.
Or: function ValueOr(ors) {
this.values = ors;
}
});
comparisons.Or.prototype.orValues = function() {
return this.values;
};
comparisons.In.test = function(values, b) {
return values.some(function(value) {
var values = set.ownAndMemberValue(value, b);
return values.own === values.member;
});
};
comparisons.NotIn.test = function(values, b) {
return !comparisons.In.test(values, b);
};
comparisons.NotIn.testValue = function(value, b) {
return !comparisons.In.testValue(value, b);
};
function nullIsFalse(test) {
return function(arg1, arg2) {
if (arg1 == null || arg2 == null) {
return false;
} else {
return test(arg1, arg2);
}
};
}
function nullIsFalseTwoIsOk(test) {
return function(arg1, arg2) {
if (arg1 === arg2) {
return true;
} else if (arg1 == null || arg2 == null) {
return false;
} else {
return test(arg1, arg2);
}
};
}
comparisons.GreaterThan.test = nullIsFalse(function(a, b) {
return a > b;
});
comparisons.GreaterThanEqual.test = nullIsFalseTwoIsOk(function(a, b) {
return a >= b;
});
comparisons.LessThan.test = nullIsFalse(function(a, b) {
return a < b;
});
comparisons.LessThanEqual.test = nullIsFalseTwoIsOk(function(a, b) {
return a <= b;
});
function isMemberThatUsesTest(value) {
var values = set.ownAndMemberValue(this.value, value);
return this.constructor.test(values.member, values.own);
}
[comparisons.GreaterThan, comparisons.GreaterThanEqual, comparisons.LessThan, comparisons.LessThanEqual, comparisons.LessThan].forEach(function(Type) {
Type.prototype.isMember = isMemberThatUsesTest;
});
[comparisons.In, comparisons.NotIn].forEach(function(Type) {
Type.prototype.isMember = common.isMemberThatUsesTestOnValues;
});
comparisons.And.prototype.isMember = function(value) {
return this.values.every(function(and) {
return and.isMember(value);
});
};
comparisons.Or.prototype.isMember = function(value) {
return this.values.some(function(and) {
return and.isMember(value);
});
};
Object.keys(comparisons).forEach(function(name) {
comparisons[name].prototype[isMemberSymbol] = comparisons[name].prototype.isMember;
});
var is = comparisons;
function makeNot(Type) {
return {
test: function(vA, vB) {
return !Type.test(vA, vB);
}
};
}
function makeEnum(type, Type, emptyResult) {
return function(a, b) {
var result = arrayUnionIntersectionDifference(a.values, b.values);
if (result[type].length) {
return new Type(result[type]);
} else {
return emptyResult || set.EMPTY;
}
};
}
function swapArgs(fn) {
return function(a, b) {
return fn(b, a);
};
}
function makeSecondValue(Type, prop) {
return function(universe, value) {
return new Type(value[prop || "value"]);
};
}
function returnBiggerValue(gtA, gtB) {
if (gtA.value < gtB.value) {
return gtB;
} else {
return gtA;
}
}
function returnSmallerValue(gtA, gtB) {
if (gtA.value > gtB.value) {
return gtB;
} else {
return gtA;
}
}
function makeAndIf(Comparison, Type) {
return function(ltA, ltB) {
if (Comparison.test(ltA.value, ltB.value)) {
return makeAnd([ltA, new Type(ltB.value)]);
} else {
return set.EMPTY;
}
};
}
function make_InIfEqual_else_andIf(Comparison, Type) {
var elseCase = makeAndIf(Comparison, Type);
return function(a, b) {
if (a.value === b.value) {
return new is.In([a.value]);
} else {
return elseCase(a, b);
}
};
}
function make_filterFirstValueAgainstSecond(Comparison, Type, defaultReturn) {
return function(inSet, gt) {
var values = inSet.values.filter(function(value) {
return Comparison.test(gt, value);
});
return values.length ?
new Type(values) : defaultReturn || set.EMPTY;
};
}
var isMemberTest = {
test: function isMemberTest(set, value) {
return set.isMember(value);
}
};
function isOr(value) {
return (value instanceof is.Or);
}
function isAnd(value) {
return (value instanceof is.And);
}
function isAndOrOr(value) {
return isAnd(value) || isOr(value);
}
// `value` - has a test function to check values
// `with` - the type we use to combined with the "other" value.
// `combinedUsing` - If there are values, how do we stick it together with `with`
function combineFilterFirstValuesAgainstSecond(options) {
return function(inSet, gt) {
var values = inSet.values.filter(function(value) {
return options.values.test(gt, value);
});
var range;
if (options.complement) {
range = set.difference(set.UNIVERSAL, gt);
} else if (options.with) {
range = new options.with(gt.value);
} else {
range = gt;
}
return values.length ?
options.combinedUsing([new options.arePut(values), range]) : range;
};
}
function makeOrUnless(Comparison, result) {
return function(setA, setB) {
if (Comparison.test(setA.value, setB.value)) {
return result || set.UNIVERSAL;
} else {
return makeOr([setA, setB]);
}
};
}
function makeAndUnless(Comparison, result) {
return function(setA, setB) {
if (Comparison.test(setA.value, setB.value)) {
return result || set.EMPTY;
} else {
return makeAnd([setA, setB]);
}
};
}
function makeComplementSecondArgIf(Comparison) {
return function(setA, setB) {
if (Comparison.test(setA.value, setB.value)) {
return set.difference(set.UNIVERSAL, setB);
} else {
return setA;
}
};
}
function makeAnd(ands) {
return comparisons.And ? new comparisons.And(ands) : set.UNDEFINABLE;
}
function makeOr(ors) {
return comparisons.Or ? new comparisons.Or(ors) : set.UNDEFINABLE;
}
function combineValueWithRangeCheck(inSet, rangeSet, RangeOrEqType) {
var gte = new RangeOrEqType(rangeSet.value);
var leftValues = inSet.values.filter(function(value) {
return !gte.isMember(value);
});
if (!leftValues.length) {
return gte;
}
if (leftValues.length < inSet.values.length) {
return makeOr([new is.In(leftValues), gte]);
} else {
return makeOr([inSet, rangeSet]);
}
}
// This tries to unify In([1]) with GT(1) -> GTE(1)
function makeOrWithInAndRange(inSet, rangeSet) {
if (rangeSet instanceof is.Or) {
var firstResult = makeOrWithInAndRange(inSet, rangeSet.values[0]);
if ( !(firstResult instanceof is.Or) ) {
return set.union(firstResult, rangeSet.values[1]);
}
var secondResult = makeOrWithInAndRange(inSet, rangeSet.values[1]);
if ( !(secondResult instanceof is.Or) ) {
return set.union(secondResult, rangeSet.values[0]);
}
return makeOr([inSet, rangeSet]);
} else {
if (rangeSet instanceof is.GreaterThan) {
return combineValueWithRangeCheck(inSet, rangeSet, is.GreaterThanEqual);
}
if (rangeSet instanceof is.LessThan) {
return combineValueWithRangeCheck(inSet, rangeSet, is.LessThanEqual);
}
return makeOr([inSet, rangeSet]);
}
}
var In_RANGE = {
union: combineFilterFirstValuesAgainstSecond({
values: makeNot(isMemberTest),
arePut: is.In,
combinedUsing: function(ors) {
return makeOrWithInAndRange(ors[0], ors[1]);
}
}),
intersection: make_filterFirstValueAgainstSecond(isMemberTest, is.In, set.EMPTY),
difference: make_filterFirstValueAgainstSecond(makeNot(isMemberTest), is.In, set.EMPTY)
};
var RANGE_IN = {
difference: swapArgs(combineFilterFirstValuesAgainstSecond({
values: isMemberTest,
arePut: is.NotIn,
combinedUsing: makeAnd
}))
};
var NotIn_RANGE = function() {
return {
union: make_filterFirstValueAgainstSecond(makeNot(isMemberTest), is.NotIn, set.UNIVERSAL),
intersection: combineFilterFirstValuesAgainstSecond({
values: isMemberTest,
arePut: is.NotIn,
combinedUsing: makeAnd
}),
difference: combineFilterFirstValuesAgainstSecond({
values: makeNot(isMemberTest),
arePut: is.NotIn,
combinedUsing: makeAnd,
complement: true
})
};
};
var RANGE_NotIn = {
difference: swapArgs(make_filterFirstValueAgainstSecond(isMemberTest, is.In, set.EMPTY))
};
var RANGE_And_Union = function(gt, and) {
var union1 = set.union(gt, and.values[0]);
var union2 = set.union(gt, and.values[1]);
if (!isAndOrOr(union1) && !isAndOrOr(union2)) {
return set.intersection(union1, union2);
} else {
return new is.Or([gt, and]);
}
};
var RANGE_And_Intersection = function(gt, and) {
var and1 = and.values[0],
and2 = and.values[1];
var intersection1 = set.intersection(gt, and1);
var intersection2 = set.intersection(gt, and2);
if (intersection1 === set.EMPTY || intersection2 === set.EMPTY) {
return set.EMPTY;
}
if (!isAndOrOr(intersection1)) {
return new set.intersection(intersection1, and2);
}
if (!isAndOrOr(intersection2)) {
return new set.intersection(intersection2, and1);
} else {
return new is.And([gt, and]);
}
};
var RANGE_And_Difference = function(gt, and) {
var and1 = and.values[0],
and2 = and.values[1];
var difference1 = set.difference(gt, and1);
var difference2 = set.difference(gt, and2);
if (difference1 === set.EMPTY) {
return difference2;
}
if (difference2 === set.EMPTY) {
return difference1;
}
return new is.Or([difference1, difference2]);
};
var And_RANGE_Difference = function(and, gt) {
var and1 = and.values[0],
and2 = and.values[1];
var difference1 = set.difference(and1, gt);
var difference2 = set.difference(and2, gt);
return set.intersection(difference1, difference2);
};
var RANGE_Or = {
union: function(gt, or) {
var or1 = or.values[0],
or2 = or.values[1];
var union1 = set.union(gt, or1);
if (!isAndOrOr(union1)) {
return set.union(union1, or2);
}
var union2 = set.union(gt, or2);
if (!isAndOrOr(union2)) {
return set.union(or1, union2);
} else {
return new is.Or([gt, or]);
}
},
intersection: function(gt, or) {
var or1 = or.values[0],
or2 = or.values[1];
var intersection1 = set.intersection(gt, or1);
var intersection2 = set.intersection(gt, or2);
if (intersection1 === set.EMPTY) {
return intersection2;
}
if (intersection2 === set.EMPTY) {
return intersection1;
}
return set.union(intersection1, intersection2);
},
// v \ (a || b) -> (v \ a) n (v \ b)
difference: function(gt, or) {
var or1 = or.values[0],
or2 = or.values[1];
var difference1 = set.difference(gt, or1);
var difference2 = set.difference(gt, or2);
return set.intersection(difference1, difference2);
}
};
var Or_RANGE = {
// ( a || b ) \ v -> (a \ v) U (b \ v)
difference: function(or, gt) {
var or1 = or.values[0],
or2 = or.values[1];
var difference1 = set.difference(or1, gt);
var difference2 = set.difference(or2, gt);
return set.union(difference1, difference2);
}
};
var comparators = canReflect.assign(arrayComparisons.comparators, {
// In
In_In: {
union: makeEnum("union", is.In),
intersection: makeEnum("intersection", is.In),
difference: makeEnum("difference", is.In)
},
UNIVERSAL_In: {
difference: makeSecondValue(is.NotIn, "values")
},
In_NotIn: {
union: swapArgs(makeEnum("difference", is.NotIn, set.UNIVERSAL)),
// what does In have on its own
intersection: makeEnum("difference", is.In),
difference: makeEnum("intersection", is.In)
},
NotIn_In: {
difference: makeEnum("union", is.NotIn)
},
In_GreaterThan: In_RANGE,
GreaterThan_In: RANGE_IN,
In_GreaterThanEqual: In_RANGE,
GreaterThanEqual_In: RANGE_IN,
In_LessThan: In_RANGE,
LessThan_In: RANGE_IN,
In_LessThanEqual: In_RANGE,
LessThanEqual_In: RANGE_IN,
In_And: In_RANGE,
And_In: RANGE_IN,
In_Or: In_RANGE,
Or_In: RANGE_IN,
// NotIn ===============================
NotIn_NotIn: {
union: makeEnum("intersection", is.NotIn, set.UNIVERSAL),
intersection: makeEnum("union", is.NotIn),
difference: makeEnum("difference", is.In)
},
UNIVERSAL_NotIn: {
difference: makeSecondValue(is.In, "values")
},
NotIn_GreaterThan: NotIn_RANGE(),
GreaterThan_NotIn: RANGE_NotIn,
NotIn_GreaterThanEqual: NotIn_RANGE(),
GreaterThanEqual_NotIn: RANGE_NotIn,
NotIn_LessThan: NotIn_RANGE(),
LessThan_NotIn: RANGE_NotIn,
NotIn_LessThanEqual: NotIn_RANGE(),
LessThanEqual_NotIn: RANGE_NotIn,
NotIn_And: NotIn_RANGE(),
And_NotIn: RANGE_NotIn,
NotIn_Or: NotIn_RANGE(),
Or_NotIn: RANGE_NotIn,
// GreaterThan ===============================
GreaterThan_GreaterThan: {
union: returnSmallerValue,
intersection: returnBiggerValue,
// {$gt:5} \ {gt: 6} -> AND( {$gt:5}, {$lte: 6} )
difference: makeAndIf(is.LessThan, is.LessThanEqual)
},
UNIVERSAL_GreaterThan: {
difference: makeSecondValue(is.LessThanEqual)
},
GreaterThan_GreaterThanEqual: {
union: returnSmallerValue,
intersection: returnBiggerValue,
// {$gt:5} \ {gte: 6} -> AND( {$gt:5}, {$lt: 6} )
difference: makeAndIf(is.LessThan, is.LessThan)
},
GreaterThanEqual_GreaterThan: {
difference: make_InIfEqual_else_andIf(is.LessThan, is.LessThanEqual)
},
GreaterThan_LessThan: {
union: (function() {
var makeOrUnlessLessThan = makeOrUnless(is.LessThan);
return function greaterThan_lessThan_union(a, b) {
if ( comparisons.In.test([a.value], b.value) ) {
return new is.NotIn([a.value]);
} else {
return makeOrUnlessLessThan(a, b);
}
};
})(),
intersection: makeAndUnless(is.GreaterThan),
difference: makeComplementSecondArgIf(is.LessThan)
},
LessThan_GreaterThan: {
difference: makeComplementSecondArgIf(is.GreaterThan)
},
GreaterThan_LessThanEqual: {
union: makeOrUnless(is.LessThanEqual),
intersection: makeAndUnless(is.GreaterThanEqual),
difference: makeComplementSecondArgIf(is.LessThanEqual)
},
LessThanEqual_GreaterThan: {
difference: makeComplementSecondArgIf(is.GreaterThanEqual)
},
GreaterThan_And: {
union: RANGE_And_Union,
intersection: RANGE_And_Intersection,
difference: RANGE_And_Difference
},
And_GreaterThan: {
difference: And_RANGE_Difference
},
GreaterThan_Or: RANGE_Or,
Or_GreaterThan: Or_RANGE,
// GreaterThanEqual =========
GreaterThanEqual_GreaterThanEqual: {
union: returnSmallerValue,
intersection: returnBiggerValue,
// {gte: 2} \ {gte: 3} = {gte: 2} AND {lt: 3}
difference: makeAndIf(is.LessThan, is.LessThan)
},
UNIVERSAL_GreaterThanEqual: {
difference: makeSecondValue(is.LessThan)
},
GreaterThanEqual_LessThan: {
union: makeOrUnless(is.LessThanEqual),
intersection: makeAndUnless(is.GreaterThanEqual),
difference: makeComplementSecondArgIf(is.LessThanEqual)
},
LessThan_GreaterThanEqual: {
difference: makeComplementSecondArgIf(is.GreaterThanEqual)
},
GreaterThanEqual_LessThanEqual: {
union: makeOrUnless(is.LessThanEqual),
// intersect on a number
intersection: (function() {
var makeAnd = makeAndUnless(is.GreaterThan);
return function gte_lte_intersection(gte, lte) {
var inSet = new is.In([gte.value]);
if (inSet.isMember(lte.value)) {
return inSet;
} else {
return makeAnd(gte, lte);
}
};
})(),
difference: makeComplementSecondArgIf(is.LessThanEqual)
},
LessThanEqual_GreaterThanEqual: {
difference: makeComplementSecondArgIf(is.GreaterThanEqual)
},
GreaterThanEqual_And: {
union: RANGE_And_Union,
intersection: RANGE_And_Intersection,
difference: RANGE_And_Difference
},
And_GreaterThanEqual: {
difference: And_RANGE_Difference
},
GreaterThanEqual_Or: RANGE_Or,
Or_GreaterThanEqual: Or_RANGE,
// LessThan
LessThan_LessThan: {
union: returnBiggerValue,
intersection: returnSmallerValue,
difference: makeAndIf(is.GreaterThan, is.GreaterThanEqual)
},
UNIVERSAL_LessThan: {
difference: makeSecondValue(is.GreaterThanEqual)
},
LessThan_LessThanEqual: {
union: returnBiggerValue,
intersection: returnSmallerValue,
// {lt: 3} \ {lte: 2} -> {lt: 3} AND {gt: 2}
difference: makeAndIf(is.GreaterThan, is.GreaterThan)
},
LessThanEqual_LessThan: {
difference: make_InIfEqual_else_andIf(is.GreaterThanEqual, is.GreaterThanEqual)
},
LessThan_And: {
union: RANGE_And_Union,
intersection: RANGE_And_Intersection,
difference: RANGE_And_Difference
},
And_LessThan: {
difference: And_RANGE_Difference
},
LessThan_Or: RANGE_Or,
Or_LessThan: Or_RANGE,
// LessThanEqual
LessThanEqual_LessThanEqual: {
union: returnBiggerValue,
intersection: returnSmallerValue,
difference: function(lteA, lteB) {
if (lteA.value >= lteB.value) {
return makeAnd([lteA, new is.GreaterThan(lteB.value)]);
} else {
return set.EMPTY;
}
}
},
UNIVERSAL_LessThanEqual: {
difference: makeSecondValue(is.GreaterThan)
},
LessThanEqual_And: {
union: RANGE_And_Union,
intersection: RANGE_And_Intersection,
difference: RANGE_And_Difference
},
And_LessThanEqual: {
difference: And_RANGE_Difference
},
LessThanEqual_Or: RANGE_Or,
Or_LessThanEqual: Or_RANGE,
// AND =====
And_And: {
// (a n b) U (c n d) => (a U c) n (b U d)?
// union both ways ... if one is unviersal, the other is the result.
// (a ∩ b) ∪ (c ∩ d) where Z = (a ∩ b)
// -> Z ∪ (c ∩ d)
// -> (Z ∪ c) ∩ (Z ∪ d)
// -> ((a ∩ b) ∪ c) ∪ ((a ∩ b) ∪ d)
union: function(and1, and2) {
var union1 = set.union(and1, and2.values[0]);
var union2 = set.union(and1, and2.values[1]);
if (isAndOrOr(union1) || isAndOrOr(union2)) {
// try the other direction
union1 = set.union(and2, and1.values[0]);
union2 = set.union(and2, and1.values[1]);
}
if (isAndOrOr(union1) || isAndOrOr(union2)) {
return new is.Or([and1, and2]);
} else {
return set.intersection(union1, union2);
}
/*
var combo1 = [
set.union(and1.values[0], and2.values[0]),
set.union(and1.values[1], and2.values[1])
],
combo2 = [
set.union(and1.values[0], and2.values[1]),
set.union(and1.values[1], and2.values[0])
];
if (combo1.every(function(aSet) {
return set.isEqual(set.UNIVERSAL, aSet);
})) {
return set.intersection.apply(set, combo2);
}
if (combo2.every(function(aSet) {
return set.isEqual(set.UNIVERSAL, aSet);
})) {
return set.intersection.apply(set, combo1);
}
return new is.Or([and1, and2]);*/
},
intersection: function(and1, and2) {
var intersection1 = set.intersection(and1.values[0], and2.values[0]);
var intersection2 = set.intersection(and1.values[1], and2.values[1]);
if (!isAndOrOr(intersection1) || !isAndOrOr(intersection2)) {
return set.intersection(intersection1, intersection2);
}
intersection1 = set.intersection(and1.values[0], and2.values[1]);
intersection2 = set.intersection(and1.values[1], and2.values[0]);
if (!isAndOrOr(intersection1) || !isAndOrOr(intersection2)) {
return set.intersection(intersection1, intersection2);
} else {
return new is.And([and1, and2]);
}
},
// (a ∩ b) \ (c ∩ d) where Z = (a ∩ b)
// -> Z \ (c ∩ d)
// -> (Z \ c) ∪ (Z \ d)
// -> ((a ∩ b) \ c) ∪ ((a ∩ b) \ d)
difference: (function() {
return function(and1, and2) {
var d1 = set.difference(and1, and2.values[0]);
var d2 = set.difference(and1, and2.values[1]);
return set.union(d1, d2);
};
/*
function getDiffIfPartnerIsEmptyAndOtherComboNotDisjoint(inOrderDiffs, reverseOrderDiffs, diffedAnd) {
var diff;
if (inOrderDiffs[0] === set.EMPTY) {
diff = inOrderDiffs[1];
}
if (inOrderDiffs[1] === set.EMPTY) {
diff = inOrderDiffs[0];
}
if (diff) {
// check if a diff equals itself (and therefor is disjoint)
if (set.isEqual(diffedAnd.values[0], reverseOrderDiffs[0] ) ) {
// is disjoint
return diffedAnd;
}
if ( set.isEqual(diffedAnd.values[1], reverseOrderDiffs[1] ) ) {
return diffedAnd;
}
return diff;
}
}
return function(and1, and2) {
var inOrderDiffs = [
set.difference(and1.values[0], and2.values[0]),
set.difference(and1.values[1], and2.values[1])
],
reverseOrderDiffs = [
set.difference(and1.values[0], and2.values[1]),
set.difference(and1.values[1], and2.values[0])
];
var diff = getDiffIfPartnerIsEmptyAndOtherComboNotDisjoint(inOrderDiffs, reverseOrderDiffs, and1);
if (diff) {
return diff;
}
diff = getDiffIfPartnerIsEmptyAndOtherComboNotDisjoint(reverseOrderDiffs, inOrderDiffs, and1);
if (diff) {
return diff;
} else {
// if one is a double And ... that's the outer \\ inner
if (isAndOrOr(inOrderDiffs[0]) && isAndOrOr(inOrderDiffs[1])) {
return new is.Or([inOrderDiffs[0], inOrderDiffs[1]]);
} else if ( isAndOrOr(reverseOrderDiffs[0]) && isAndOrOr(reverseOrderDiffs[1]) ) {
return new is.Or([reverseOrderDiffs[0], reverseOrderDiffs[1]]);
}
return set.UNKNOWABLE;
}
};*/
})()
},
And_Or: {
// (a ∩ b) ∪ (c u d) where Z = (c u d)
// -> Z u (a ∩ b)
// -> (Z u a) ∩ (Z u b)
// -> ((c u d) u a) ∩ ((c u d) u b)
union: function(and, or) {
var aUnion = set.union(and.values[0], or);
var bUnion = set.union(and.values[1], or);
if (!isAndOrOr(aUnion) || !isAndOrOr(bUnion)) {
return set.intersection(aUnion, bUnion);
}
return new is.Or([and, or]);
},
// (a ∩ b) ∩ (c u d) where Z = (a ∩ b)
// -> Z ∩ (c u d)
// -> (Z ∩ c) u (Z ∩ d)
// -> (a ∩ b ∩ c) u (a ∩ b ∩ d)
intersection: function(and, or) {
var aIntersection = set.intersection(and, or.values[0]);
var bIntersection = set.intersection(and, or.values[1]);
if (!isOr(aIntersection) && !isOr(bIntersection)) {
return set.union(aIntersection, bIntersection);
}
return new is.And([and, or]);
},
// (a ∩ b) \ (c u d) where Z = (a ∩ b)
// -> Z \ (c u d)
// -> (Z \ c) ∩ (Z \ d)
// -> ((a ∩ b) \ c) ∩ ((a ∩ b) \ d)
difference: function(and, or) {
var aDiff = set.difference(and, or.values[0]);
var bDiff = set.difference(and, or.values[1]);
return set.intersection(aDiff, bDiff);
}
},
Or_And: {
// (a ∪ b) \ (c ∩ d) where Z = (a ∪ b)
// -> Z \ (c ∩ d)
// -> (Z \ c) ∪ (Z \ d)
// -> ((a ∪ b) \ c) ∪ ((a ∪ b) \ d)
difference: function(or, and) {
var aDiff = set.difference(or, and.values[0]);
var bDiff = set.difference(or, and.values[1]);
return set.union(aDiff, bDiff);
}
},
UNIVERSAL_And: {
difference: function(universe, and) {
var inverseFirst = set.difference(universe, and.values[0]),
inverseSecond = set.difference(universe, and.values[1]);
return set.union(inverseFirst, inverseSecond);
}
},
Or_Or: {
// (a ∪ b) ∪ (c ∪ d)
union: function(or1, or2) {
var union1 = set.union(or1.values[0], or2.values[0]);
var union2 = set.union(or1.values[1], or2.values[1]);
if (!isAndOrOr(union1) || !isAndOrOr(union2)) {
return set.union(union1, union2);
}
union1 = set.union(or1.values[0], or2.values[1]);
union2 = set.union(or1.values[1], or2.values[0]);
if (!isAndOrOr(union1) || !isAndOrOr(union2)) {
return set.union(union1, union2);
} else {
return new is.Or([or1, or2]);
}
},
// (a ∪ b) ∩ (c ∪ d) where Z = (a ∪ b)
// -> Z ∩ (c ∪ d)
// -> (Z ∩ c) ∪ (Z ∪ d)
// -> ((a ∪ b) ∩ c) ∪ ((a ∪ b) ∩ d)
intersection: function(or1, or2) {
var c = or2.values[0],
d = or2.values[1];
var intersection1 = set.intersection(or1, c);
var intersection2 = set.intersection(or1, d);
if (!isOr(intersection1) || !isOr(intersection2)) {
return set.union(intersection1, intersection2);
}
intersection1 = set.union(or2, or1.values[0]);
intersection2 = set.union(or2, or1.values[1]);
if (!isOr(intersection1) || !isOr(intersection2)) {
return set.union(intersection1, intersection2);
} else {
return new is.Or([or1, or2]);
}
},
// (a ∪ b) \ (c ∪ d) where Z = (a ∪ b)
// -> Z \ (c ∪ d)
// -> (Z \ c) ∩ (Z \ d)
// -> ((a ∪ b) \ c) ∩ ((a ∪ b) \ d)
difference: function(or1, or2) {
var d1 = set.difference(or1, or2.values[0]);
var d2 = set.difference(or1, or2.values[1]);
return set.intersection(d1, d2);
}
},
UNIVERSAL_Or: {
difference: function(universe, or) {
var inverseFirst = set.difference(universe, or.values[0]),
inverseSecond = set.difference(universe, or.values[1]);
return set.intersection(inverseFirst, inverseSecond);
}
}
});
// Registers all the comparisons above
var names = Object.keys(comparisons);
names.forEach(function(name1, i) {
if (!comparators[name1 + "_" + name1]) {
console.warn("no " + name1 + "_" + name1);
} else {
set.defineComparison(comparisons[name1], comparisons[name1], comparators[name1 + "_" + name1]);
}
if (!comparators["UNIVERSAL_" + name1]) {
console.warn("no UNIVERSAL_" + name1);
} else {
set.defineComparison(set.UNIVERSAL, comparisons[name1], comparators["UNIVERSAL_" + name1]);
}
for (var j = i + 1; j < names.length; j++) {
var name2 = names[j];
if (!comparators[name1 + "_" + name2]) {
console.warn("no " + name1 + "_" + name2);
} else {
set.defineComparison(comparisons[name1], comparisons[name2], comparators[name1 + "_" + name2]);
}
if (!comparators[name2 + "_" + name1]) {
console.warn("no " + name2 + "_" + name1);
} else {
set.defineComparison(comparisons[name2], comparisons[name1], comparators[name2 + "_" + name1]);
}
}
});
module.exports = comparisons;