/** Common methods and helpers to implement the {@link RangeType} interface
* @namespace CommonType
*/
/** Basic implementation of {@link RangeType.create}. This uses {@link RangeType.setStart} and
* {@link RangeType.setEnd} to initialize the range.
* @memberof CommonType
* @param {any} start starting bound
* @param {any} end ending bound
* @param {?boolean} startExcl whether starting bound is exclusive
* @param {?boolean} endExcl whether ending bound is exclusive
* @returns {Range}
*/
function create(start, end, startExcl, endExcl){
const r = {};
// initialize?
if (arguments.length){
this.setStart(r, start, startExcl);
this.setEnd(r, end, endExcl);
}
return r;
}
/** Basic implementation of {@link RangeType.copy}. This uses `Object.assign` to copy the object
* @memberof CommonType
*/
function copy(range){
return Object.assign({}, range);
}
/** Basic implementation of {@link RangeType.setStart}. This deletes {@link Range#startExcl}, rather
* than set it to `false`
* @memberof CommonType
*/
function setStart(range, start, startExcl){
range.start = start;
if (startExcl)
range.startExcl = startExcl;
else delete range.startExcl;
return range;
}
/** Basic implementation of {@link RangeType.setEnd}. This deletes {@link Range#endExcl}, rather
* than set it to `false`
* @memberof CommonType
*/
function setEnd(range, end, endExcl){
range.end = end;
if (endExcl)
range.endExcl = endExcl;
else delete range.endExcl;
return range;
}
/** Basic implementation of {@link RangeType.compare}. This uses the numeric difference for
* `distance`, and the sign of that value for `side`. The inputs are [coerced to
* numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_coercion)
* prior to calculation.
*
* This isn't useful on its own (technically a normalized, real type) but can be used to build other
* types.
* @memberof CommonType
*/
function compare(mode, a, b){
// prefixed + to do explicit cast to number
const distance = +a - +b;
const side = Math.sign(distance);
return {distance, side};
}
/** Basic implementation of {@link RangeType.size}. This simply takes the difference in
* {@link Range#end} and {@link Range#start}, with values [coerced to
* numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_coercion).
* This is suitable for continuous types, but not for discrete.
* @memberof CommonType
*/
function size(r){
return +r.end - +r.start;
}
/** Simple decorator that adds fuzzy comparison to an existing {@link RangeType}. This causes ranges
* to be merged if their endpoints are within `epsilon` distance. It can be especially useful if
* using {@link RealType} or {@link FloatNormType}, where imprecision in floating point
* arithmetic can mean adjacent ranges don't quite line up.
* @memberof CommonType
* @param {number} epsilon the absolute threshold at which {@link RangeType~CompareResult}
* `distance` is clamped to zero
* @param {RangeType} type the type whose {@link RangeType.compare} method should be wrapped
* @param {boolean} [subclass=true] if true, a new {@link RangeType} is created that extends `type`;
* if false, the {@link RangeType.compare} method of `type` is simply overwritten
* @returns {RangeType} depending on the value of `extend`, either `type` or a new type that
* inherits from `type`
*/
function compareEpsilon(epsilon, type, subclass=true){
const compare = type.compare;
return wrap(type, subclass, function(...args){
const out = compare(...args)
if (Math.abs(out.distance) <= epsilon)
out.distance = 0;
return out;
});
}
/** Simple decorator that forces the {@link RangeType.compare} method to opt in to binary search,
* rather than interpolation search. It does this by forcing non-zero `distance` to have the same
* magnitude.
* @memberof CommonType
* @param {RangeType} type the type whose {@link RangeType.compare} method should be wrapped
* @param {boolean} [subclass=true] if true, a new {@link RangeType} is created that extends `type`;
* if false, the {@link RangeType.compare} method of `type` is simply overwritten
* @returns {RangeType} depending on the value of `extend`, either `type` or a new type that
* inherits from `type`
*/
function compareBinarySearch(type, subclass=true){
const compare = type.compare;
return wrap(type, subclass, function(...args){
const out = compare(...args)
out.distance = Math.sign(out.distance);
return out;
});
}
function wrap(type, subclass, wrapper){
if (subclass)
type = Object.create(type);
type.compare = wrapper;
return type;
}
export { create, copy, setStart, setEnd, compare, size, compareEpsilon, compareBinarySearch};