flat-ranges
Version:
Utilities for managing flat range lists like [from1, to1, from2, to2, ...]
274 lines (229 loc) • 7.01 kB
JavaScript
function subtract_clip(baseRanges, subtractRanges) {
var result = [];
for (var i = 0; i < subtractRanges.length; i += 2) {
var from = subtractRanges[i];
var to = subtractRanges[i + 1];
var clipped = [from, to];
remove(clipped, baseRanges);
for (var j = 0; j < clipped.length; j += 2) {
result.push(clipped[j], clipped[j + 1]);
}
}
return result;
}
function merge(flatRanges) {
var result = [];
for (var i = 0; i < flatRanges.length; i += 2) {
var from = flatRanges[i];
var to = flatRanges[i + 1];
if (result.length === 0) {
result.push(from, to);
} else {
var lastTo = result[result.length - 1];
if (from <= lastTo) {
result[result.length - 1] = Math.max(lastTo, to);
} else {
result.push(from, to);
}
}
}
return result;
}
function set_have(knownHave, knownNotHave, newHave) {
var changed = false;
var becameNotHave = [];
for (var i = 0; i < knownHave.length; i += 2) {
var from = knownHave[i];
var to = knownHave[i + 1];
var piece = [from, to];
remove(piece, newHave);
for (var j = 0; j < piece.length; j += 2) {
becameNotHave.push(piece[j], piece[j + 1]);
}
}
if (becameNotHave.length > 0) {
if (add(knownNotHave, becameNotHave)) {
changed = true;
}
}
var clean = subtract_clip(knownNotHave, newHave);
// נבדוק אם knownHave השתנה
if (knownHave.length > 0) {
knownHave.length = 0;
changed = true;
}
if (add(knownHave, clean)) {
changed = true;
}
return changed;
}
function add_have(knownHave, knownNotHave, newHave) {
// לא יכולים להיות גם ב־have וגם ב־not_have
var clean = subtract_clip(knownNotHave, newHave);
return add(knownHave, clean);
}
function add_not_have(knownHave, knownNotHave, newNotHave) {
// לא מוסיפים מידע שסותר את מה שכבר ידוע כקיים
var clean = subtract_clip(knownHave, newNotHave);
return add(knownNotHave, clean);
}
function set_not_have(knownHave, knownNotHave, newNotHave) {
var changed = false;
var becameHave = [];
for (var i = 0; i < knownNotHave.length; i += 2) {
var from = knownNotHave[i];
var to = knownNotHave[i + 1];
var piece = [from, to];
remove(piece, newNotHave);
for (var j = 0; j < piece.length; j += 2) {
becameHave.push(piece[j], piece[j + 1]);
}
}
if (becameHave.length > 0) {
if (add(knownHave, becameHave)) {
changed = true;
}
}
var clean = subtract_clip(knownHave, newNotHave);
if (knownNotHave.length > 0) {
knownNotHave.length = 0;
changed = true;
}
if (add(knownNotHave, clean)) {
changed = true;
}
return changed;
}
function invert(ranges, fullStart, fullEnd) {
var result = [];
var last = fullStart;
for (var i = 0; i < ranges.length; i += 2) {
var from = ranges[i];
if (from > last) result.push(last, from);
last = Math.max(last, ranges[i + 1]);
}
if (last < fullEnd) result.push(last, fullEnd);
return result;
}
function remove(ranges, removeRanges) {
var result = [];
var i = 0, j = 0;
var changed = false;
while (i < ranges.length && j < removeRanges.length) {
var aFrom = ranges[i], aTo = ranges[i + 1];
var bFrom = removeRanges[j], bTo = removeRanges[j + 1];
// הפיכת טווחים ריקים לטווחים בודדים
if (aFrom === aTo) aTo = aFrom + 1;
if (bFrom === bTo) bTo = bFrom + 1;
if (aTo <= bFrom) {
// אין חפיפה
result.push(aFrom, aTo);
i += 2;
} else if (aFrom >= bTo) {
// הטווח להסרה עוד לא רלוונטי
j += 2;
} else {
// יש חפיפה
if (aFrom < bFrom) {
result.push(aFrom, bFrom);
}
if (aTo > bTo) {
result.push(bTo, aTo);
}
changed = true;
i += 2;
}
}
// הוספת טווחים שנשארו
while (i < ranges.length) {
var aFrom = ranges[i], aTo = ranges[i + 1];
if (aFrom === aTo) aTo = aFrom + 1; // גם פה
result.push(aFrom, aTo);
i += 2;
}
if (result.length !== ranges.length) changed = true;
for (var k = 0; k < result.length; k++) {
if (ranges[k] !== result[k]) changed = true;
ranges[k] = result[k];
}
ranges.length = result.length;
return changed;
}
function add(ranges, newRanges) {
var changed = false;
var all = [];
// שלב 1: איסוף כל הטווחים — הקיימים והחדשים
for (var i = 0; i < ranges.length; i += 2) {
all.push([ranges[i], ranges[i + 1]]);
}
for (var i = 0; i < newRanges.length; i += 2) {
all.push([newRanges[i], newRanges[i + 1]]);
}
// שלב 2: מיון לפי התחלה
all.sort(function (a, b) {
return a[0] - b[0];
});
// שלב 3: מיזוג טווחים רציפים או חופפים
var merged = [];
for (var i = 0; i < all.length; i++) {
var from = all[i][0];
var to = all[i][1];
if (merged.length === 0) {
merged.push(from, to);
} else {
var lastFrom = merged[merged.length - 2];
var lastTo = merged[merged.length - 1];
if (from <= lastTo + 1) {
// מיזוג
if (to > lastTo) {
merged[merged.length - 1] = to;
changed = true;
}
} else {
// טווח חדש
merged.push(from, to);
changed = true;
}
}
}
// השוואה מול הקודם
if (merged.length !== ranges.length) {
changed = true;
} else {
for (var i = 0; i < merged.length; i++) {
if (merged[i] !== ranges[i]) {
changed = true;
break;
}
}
}
// עדכון הטווחים
ranges.length = merged.length;
for (var i = 0; i < merged.length; i++) {
ranges[i] = merged[i];
}
return changed;
}
function length(ranges) {
var total = 0;
for (var i = 0; i < ranges.length; i += 2) {
total += ranges[i + 1] - ranges[i];
}
return total;
}
function unknow(have_ranges, not_have_ranges, min_length, max_length) {
return invert(merge(have_ranges.concat(not_have_ranges)), min_length, max_length);
}
module.exports = {
subtract_clip,
merge,
set_have,
add_have,
add_not_have,
set_not_have,
invert,
remove,
add,
length,
unknow
};