blob: f23b64af2f4b17262b5bd84a49979f1ff8f8cf91 [file] [log] [blame] [edit]
var copyrightInversions = "Copyright © 2000 Mark Davis. All Rights Reserved.";
// Define objects
Inversion.prototype.rangeArray = [];
Inversion.prototype.opposite = 0;
Inversion.prototype.getLeast = Inversion_getLeast;
Inversion.prototype.getLeast2 = Inversion_getLeast2;
Inversion.prototype.contains = Inversion_contains;
Inversion.prototype.previous = Inversion_previousDifferent;
Inversion.prototype.next = Inversion_nextDifferent;
Inversion.prototype.makeOpposite = Inversion_makeOpposite;
InversionMap.prototype.inversion = null;
InversionMap.prototype.values = null;
InversionMap.prototype.at = InversionMap_at;
/**
* Maps integers to a range (half-open).
* When used as a set, even indices are IN, and odd are OUT.
* @parameter rangeArray must be an array of monotonically increasing integer values, with at least one instance.
*/
function Inversion (rangeArray) {
this.rangeArray = rangeArray;
for (var i = 1; i < rangeArray.length; ++i) {
if (rangeArray[i] == null) {
rangeArray[i] = rangeArray[i-1] + 1;
} else if (!(rangeArray[i-1] < rangeArray[i])) {
alert("Array must be monotonically increasing! "
+ (i-1) + ": " + rangeArray[i-1]
+ ", " + i + ": " + rangeArray[i]);
return;
}
}
}
/**
* Creates opposite of this, that is: result.contains(c) iff !this.contains(c)
* @return reversal of this
*/
function Inversion_makeOpposite () {
var result = new Inversion(this.rangeArray);
result.opposite = 1 ^ this.opposite;
return result;
}
/**
* @intValue probe value
* @return true if probe is in the list, false otherwise.
* Uses the fact than an inversion list
* contains half-open ranges. An element is
* in the list iff the smallest index is even
*/
function Inversion_contains(intValue) {
return ((this.getLeast(intValue) & 1) == this.opposite);
}
/**
* @intValue probe value
* @return the largest index such that rangeArray[index] <= intValue.
* If intValue < rangeArray[0], returns -1.
*/
function Inversion_getLeast (intValue) {
var arr = this.rangeArray;
var low = 0;
var high = arr.length;
while (high - low > 8) {
var mid = (high + low) >> 1;
if (arr[mid] <= intValue) {
low = mid;
} else {
high = mid;
}
}
for (; low < high; ++low) {
if (intValue < arr[low]) {
break;
}
}
return low - 1;
}
/*document.mainForm.result.value = "intValue: " + intValue + "\u000D";
if (false) document.mainForm.result.value +=
"arr[" + low + "]=" + arr[low] +
" arr[" + mid + "]=" + arr[mid] +
" arr[" + high + "]=" + arr[high] + "\u000D";
if (false) document.mainForm.result.value +=
"arr[" + low + "]=" + arr[low] +
" arr[" + high + "]=" + arr[high] + "\u000D";
*/
/**
* @intValue probe value
* @return the largest index such that rangeArray[index] <= intValue.
* If intValue < rangeArray[0], returns -1.
*/
function Inversion_getLeast2 (intValue) {
var arr = this.rangeArray;
var low = 0;
var high = arr.length;
for (; low < high; ++low) {
if (intValue < arr[low]) {
break;
}
}
return low - 1;
}
/**
* @intValue probe value
* @return next greater probe value that would be different.
* or null if it would be out of range
*/
function Inversion_nextDifferent(intValue, delta) {
//alert(intValue + ", " + this.rangeArray[this.getLeast(intValue) + 1]);
return this.rangeArray[this.getLeast(intValue) + delta];
}
/**
* @intValue probe value
* @return previous lesser probe value that would be different.
* or null if it would be out of range
*/
function Inversion_previousDifferent(intValue, delta) {
return this.rangeArray[this.getLeast(intValue) - delta];
}
/**
* Maps ranges to values.
* @parameter rangeArray must be suitable for an Inversion.
* @parameter valueArray is the list of corresponding values.
* Length must be the same as rangeArray.
*/
function InversionMap (rangeArray, valueArray) {
if (rangeArray.length != valueArray.length) {
return; // error
}
this.inversion = new Inversion(rangeArray);
this.values = valueArray;
}
/**
* Gets value at range
* @parameter intValue. Any integer value.
* @return the value associated with that integer. null if before the first
* item in the range.
*/
function InversionMap_at (intValue) {
var index = this.inversion.getLeast(intValue);
if (index < 0) return null;
return this.values[index];
}