// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Helper functions for doing intersections and iteration
* over sorted arrays and intervals.
*
*/
cr.define('tracing', function() {
/**
* Finds the first index in the array whose value is >= loVal.
*
* The key for the search is defined by the mapFn. This array must
* be prearranged such that ary.map(mapFn) would also be sorted in
* ascending order.
*
* @param {Array} ary An array of arbitrary objects.
* @param {function():*} mapFn Callback that produces a key value
* from an element in ary.
* @param {number} loVal Value for which to search.
* @return {Number} Offset o into ary where all ary[i] for i <= o
* are < loVal, or ary.length if loVal is greater than all elements in
* the array.
*/
function findLowIndexInSortedArray(ary, mapFn, loVal) {
if (ary.length == 0)
return 1;
var low = 0;
var high = ary.length - 1;
var i, comparison;
var hitPos = -1;
while (low <= high) {
i = Math.floor((low + high) / 2);
comparison = mapFn(ary[i]) - loVal;
if (comparison < 0) {
low = i + 1; continue;
} else if (comparison > 0) {
high = i - 1; continue;
} else {
hitPos = i;
high = i - 1;
}
}
// return where we hit, or failing that the low pos
return hitPos != -1 ? hitPos : low;
}
/**
* Finds an index in an array of intervals that either
* intersects the provided loVal, or if no intersection is found,
* the index of the first interval whose start is > loVal.
*
* The array of intervals is defined implicitly via two mapping functions
* over the provided ary. mapLoFn determines the lower value of the interval,
* mapWidthFn the width. Intersection is lower-inclusive, e.g. [lo,lo+w).
*
* The array of intervals formed by this mapping must be non-overlapping and
* sorted in ascending order by loVal.
*
* @param {Array} ary An array of objects that can be converted into sorted
* nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
* @param {function():*} mapLoFn Callback that produces the low value for the
* interval represented by an element in the array.
* @param {function():*} mapLoFn Callback that produces the width for the
* interval represented by an element in the array.
* @param {number} loVal The low value for the search.
* @return {Number} An index in the array that intersects or is first-above
* loVal, -1 if none found and loVal is below than all the intervals,
* ary.length if loVal is greater than all the intervals.
*/
function findLowIndexInSortedIntervals(ary, mapLoFn, mapWidthFn, loVal) {
var first = findLowIndexInSortedArray(ary, mapLoFn, loVal);
if (first == 0) {
if (loVal >= mapLoFn(ary[0]) &&
loVal < mapLoFn(ary[0] + mapWidthFn(ary[0]))) {
return 0;
} else {
return -1;
}
} else if (first <= ary.length &&
loVal >= mapLoFn(ary[first - 1]) &&
loVal < mapLoFn(ary[first - 1]) + mapWidthFn(ary[first - 1])) {
return first - 1;
} else {
return ary.length;
}
}
/**
* Calls cb for all intervals in the implicit array of intervals
* defnied by ary, mapLoFn and mapHiFn that intersect the range
* [loVal,hiVal)
*
* This function uses the same scheme as findLowIndexInSortedArray
* to define the intervals. The same restrictions on sortedness and
* non-overlappingness apply.
*
* @param {Array} ary An array of objects that can be converted into sorted
* nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
* @param {function():*} mapLoFn Callback that produces the low value for the
* interval represented by an element in the array.
* @param {function():*} mapLoFn Callback that produces the width for the
* interval represented by an element in the array.
* @param {number} The low value for the search, inclusive.
* @param {number} loVal The high value for the search, non inclusive.
* @param {function():*} cb The function to run for intersecting intervals.
*/
function iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal,
hiVal, cb) {
if (loVal > hiVal) return;
var i = findLowIndexInSortedArray(ary, mapLoFn, loVal);
if (i == -1) {
return;
}
if (i > 0) {
var hi = mapLoFn(ary[i - 1]) + mapWidthFn(ary[i - 1]);
if (hi >= loVal) {
cb(ary[i - 1]);
}
}
if (i == ary.length) {
return;
}
for (var n = ary.length; i < n; i++) {
var lo = mapLoFn(ary[i]);
if (lo >= hiVal)
break;
cb(ary[i]);
}
}
/**
* Non iterative version of iterateOverIntersectingIntervals.
*
* @return {Array} Array of elements in ary that intersect loVal, hiVal.
*/
function getIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal) {
var tmp = [];
iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal,
function(d) {
tmp.push(d);
});
return tmp;
}
return {
findLowIndexInSortedArray: findLowIndexInSortedArray,
findLowIndexInSortedIntervals: findLowIndexInSortedIntervals,
iterateOverIntersectingIntervals: iterateOverIntersectingIntervals,
getIntersectingIntervals: getIntersectingIntervals
};
});