pastel/sys/sequence/exponential_binary_search/
// Description: Exponential binary search
// Documentation: binary_search.txt
#ifndef PASTELSYS_EXPONENTIAL_BINARY_SEARCH_H
#define PASTELSYS_EXPONENTIAL_BINARY_SEARCH_H
#include "pastel/sys/indicator/step_indicator_concept.h"
#include "pastel/sys/director/director_concept.h"
// Implementation
#include "pastel/sys/sequence/binary_search.h"
#include "pastel/sys/math/powers.h"
#include "pastel/sys/director/step_indicator_director.h"
namespace Pastel
{
//! Exponential binary-search.
/*!
This is a convenience function which calls
directedExponentialBinarySearch(minLevel, maxLevel,
stepIndicatorDirector(indicator)).
See the documentation for directedBinarySearch().
*/
template <
typename Integer,
typename Integer_Step_Indicator,
Requires<
Models<Integer, Integer_Concept>,
Models<Integer_Step_Indicator, Step_Indicator_Concept(Integer)>
> ConceptCheck = 0
>
Integer exponentialBinarySearch(
const Integer& minLevel,
const Integer& maxLevel,
Integer_Step_Indicator indicator)
{
return directedExponentialBinarySearch(
minLevel, maxLevel,
stepIndicatorDirector(indicator));
}
//! Directed exponential binary-search.
/*!
Preconditions:
minLevel <= maxLevel
Time complexity:
O(f log(min(k, maxLevel) - minLevel + 2)),
where
k is the returned element, and
f is the time taken by a single test.
Number of tests:
ceil(2 log(k + 1)) <= m <= floor(2 log(k) + 2),
where
k is the returned element, and
m is the number of performed tests.
minLevel, maxLevel:
The searched range is [minLevel, maxLevel).
director:
Each level will be tested at most once by the director.
returns:
An element k in [minLevel, maxLevel] such that
the induced indicator is false on [minLevel, k) and
true on [k, maxLevel).
*/
template <
typename Integer,
typename Integer_Director,
Requires<
Models<Integer, Integer_Concept>,
Models<Integer_Director, Director_Concept(Integer)>
> ConceptCheck = 0>
Integer directedExponentialBinarySearch(
const Integer& minLevel,
const Integer& maxLevel,
Integer_Director director)
{
ENSURE(minLevel <= maxLevel);
// Handle the empty case.
if (minLevel == maxLevel)
{
return maxLevel;
}
// For every director there is a unique
// corresponding step-indicator. In the following
// we will reason in terms of the induced
// step-indicator.
// This is deliberately the native integer,
// since we want to support Integer being
// a random-access iterator, and those can't
// compute the power of two.
integer k = 0;
Integer min = minLevel;
while (min < maxLevel)
{
// We will maintain the loop invariant that
// the indicator is false on the range
// [minLevel, min).
// While searching for the first true element, we
// take exponential steps. Note that when 'k == 0',
// it holds that 'mid == minLevel', and thus 'minLevel' also
// gets tested.
// The step to take varies as 2^k.
// At some point the integer overflows, which can be
// detected by value 0 (due to mod arithmetic).
Integer power = powerOfTwo<Integer>(k);
Integer mid = zero(power) ? maxLevel - 1 : minLevel + (power - 1);
// Restrict tests to the given interval.
// The case mid < minLevel happens when the
// integer overflows.
if (mid >= maxLevel || mid < minLevel)
{
// This element will be correctly in range because
// we tested the empty case in the beginning.
mid = maxLevel - 1;
}
Integer directed = director(mid);
PENSURE(directed >= min);
if (directed > maxLevel)
{
directed = maxLevel;
}
// See if the indicator holds at 'mid'.
if (directed <= mid)
{
// The indicator is true on [directed, mid]. By the
// loop invariant, the indicator is false on
// [minLevel, min). Thus there exists a smallest
// element 'level' in the range [min, directed] such
// that the indicator is false on [minLevel, level),
// and true at 'level'.
// Search the range [min, directed] using binary search.
// If the indicator is false on [min, directed), then
// by the binary search, and the loop invariant,
// the indicator is false on [minLevel, directed), and
// true at 'directed', so 'directed' is the correct result.
return directedBinarySearch(min, directed, director);
}
// The indicator is false on [mid, directed). Therefore
// the indicator is also false on [minLevel, directed).
min = directed;
++k;
}
// If we get here, then by the loop invariant the
// indicator is false on [minLevel, maxLevel),
// that is, for all elements.
return maxLevel;
}
}
#endif