Skip to content

Commit

Permalink
Use Boost ICL for RangeSet
Browse files Browse the repository at this point in the history
  • Loading branch information
bachase committed May 11, 2017
1 parent 1dbc5a5 commit 3d25fab
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 295 deletions.
66 changes: 19 additions & 47 deletions src/ripple/basics/RangeSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,72 +20,44 @@
#ifndef RIPPLE_BASICS_RANGESET_H_INCLUDED
#define RIPPLE_BASICS_RANGESET_H_INCLUDED

#include <boost/icl/closed_interval.hpp>
#include <boost/icl/interval_set.hpp>
#include <cstdint>
#include <map>
#include <string>

namespace ripple {

namespace ripple
{
/** A sparse set of integers. */
// VFALCO TODO Replace with juce::SparseSet
class RangeSet
{
public:
static const std::uint32_t absent = static_cast <std::uint32_t> (-1);
static const std::uint32_t absent = static_cast<std::uint32_t>(-1);

public:
RangeSet () { }
RangeSet() = default;

bool hasValue (std::uint32_t) const;
bool hasValue(std::uint32_t) const;

std::uint32_t getFirst () const;
std::uint32_t getNext (std::uint32_t) const;
std::uint32_t getLast () const;
std::uint32_t getPrev (std::uint32_t) const;
// First number in the set
std::uint32_t getFirst() const;

// largest number not in the set that is less than the given number
std::uint32_t prevMissing (std::uint32_t) const;
// Largest number not in the set that is less than the given number
std::uint32_t prevMissing(std::uint32_t) const;

// Add an item to the set
void setValue (std::uint32_t);
void setValue(std::uint32_t);

// Add the closed interval to the set
void setRange (std::uint32_t, std::uint32_t);

void clearValue (std::uint32_t);

std::string toString () const;

/** Returns the sum of the Lebesgue measures of all sub-ranges. */
std::size_t
lebesgue_sum() const;
void setRange(std::uint32_t, std::uint32_t);

/** Check invariants of the data.
// Remove the item from the set
void clearValue(std::uint32_t);

This is for diagnostics, and does nothing in release builds.
*/
void checkInternalConsistency () const noexcept;
std::string toString() const;

private:
void simplify ();

private:
using Map = std::map <std::uint32_t, std::uint32_t>;

using const_iterator = Map::const_iterator;
using const_reverse_iterator = Map::const_reverse_iterator;
using value_type = Map::value_type;
using iterator = Map::iterator;

static bool contains (value_type const& it, std::uint32_t v)
{
return (it.first <= v) && (it.second >= v);
}

// First is lowest value in range, last is highest value in range
using Interval = boost::icl::closed_interval<std::uint32_t>;
using Map = boost::icl::interval_set<std::uint32_t, std::less, Interval>;
Map mRanges;
};

} // ripple

} // namespace ripple
#endif
252 changes: 40 additions & 212 deletions src/ripple/basics/impl/RangeSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,253 +18,81 @@
//==============================================================================

#include <BeastConfig.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/RangeSet.h>
#include <ripple/beast/core/LexicalCast.h>
#include <boost/foreach.hpp>
#include <cassert>
#include <cstdint>

namespace ripple {

// VFALCO NOTE std::min and std::max not good enough?
// NOTE Why isn't this written as a template?
// TODO Replace this with std calls.
//
inline std::uint32_t min (std::uint32_t x, std::uint32_t y)
namespace ripple
{
return (x < y) ? x : y;
}
inline std::uint32_t max (std::uint32_t x, std::uint32_t y)
bool
RangeSet::hasValue(std::uint32_t v) const
{
return (x > y) ? x : y;
return boost::icl::contains(mRanges, v);
}

bool RangeSet::hasValue (std::uint32_t v) const
std::uint32_t
RangeSet::getFirst() const
{
for (auto const& it : mRanges)
{
if (contains (it, v))
return true;
}
return false;
}

std::uint32_t RangeSet::getFirst () const
{
const_iterator it = mRanges.begin ();

if (it == mRanges.end ())
if (mRanges.empty())
return absent;

return it->first;
}

std::uint32_t RangeSet::getNext (std::uint32_t v) const
{
for (auto const& it : mRanges)
{
if (it.first > v)
return it.first;

if (contains (it, v + 1))
return v + 1;
}
return absent;
return boost::icl::first(mRanges);
}

std::uint32_t RangeSet::getLast () const
// largest number not in the set that is less than the given number
std::uint32_t
RangeSet::prevMissing(std::uint32_t v) const
{
const_reverse_iterator it = mRanges.rbegin ();

if (it == mRanges.rend ())
if (v == 0)
return absent;

return it->second;
}

std::uint32_t RangeSet::getPrev (std::uint32_t v) const
{
BOOST_REVERSE_FOREACH (const value_type & it, mRanges)
{
if (it.second < v)
return it.second;

if (contains (it, v + 1))
return v - 1;
}
return absent;
}

// Return the largest number not in the set that is less than the given number
//
std::uint32_t RangeSet::prevMissing (std::uint32_t v) const
{
std::uint32_t result = absent;

if (v != 0)
{
checkInternalConsistency ();

// Handle the case where the loop reaches the terminating condition
//
result = v - 1;

for (const_reverse_iterator cur = mRanges.rbegin (); cur != mRanges.rend (); ++cur)
{
// See if v-1 is in the range
if (contains (*cur, result))
{
result = cur->first - 1;
break;
}
}
}

assert (result == absent || !hasValue (result));

return result;
// Find returns an iterator pointing to the interval containing v-1
auto it = mRanges.find(v - 1);
if (it != mRanges.end())
return it->first() - 1;
// If no interval overlaps, then v-1 is the largest number less than v
// not in the set
return v - 1;
}

void RangeSet::setValue (std::uint32_t v)
// Add an item to the set
void
RangeSet::setValue(std::uint32_t v)
{
if (!hasValue (v))
{
mRanges[v] = v;

simplify ();
}
mRanges.insert(Interval(v));
}

void RangeSet::setRange (std::uint32_t minV, std::uint32_t maxV)
// Add the closed interval to the set
void
RangeSet::setRange(std::uint32_t minv, std::uint32_t maxv)
{
while (hasValue (minV))
{
++minV;

if (minV >= maxV)
return;
}

mRanges[minV] = maxV;

simplify ();
mRanges.insert(Interval(minv, maxv));
}

void RangeSet::clearValue (std::uint32_t v)
// Remove the item from the set
void
RangeSet::clearValue(std::uint32_t v)
{
for (iterator it = mRanges.begin (); it != mRanges.end (); ++it)
{
if (contains (*it, v))
{
if (it->first == v)
{
if (it->second == v)
{
mRanges.erase (it);
}
else
{
std::uint32_t oldEnd = it->second;
mRanges.erase(it);
mRanges[v + 1] = oldEnd;
}
}
else if (it->second == v)
{
-- (it->second);
}
else
{
std::uint32_t oldEnd = it->second;
it->second = v - 1;
mRanges[v + 1] = oldEnd;
}

checkInternalConsistency();
return;
}
}
mRanges.erase(Interval(v));
}

std::string RangeSet::toString () const
std::string
RangeSet::toString() const
{
std::string ret;
for (auto const& it : mRanges)
{
if (!ret.empty ())
if (!ret.empty())
ret += ",";

if (it.first == it.second)
ret += beast::lexicalCastThrow <std::string> ((it.first));
if (it.first() == it.last())
ret += beast::lexicalCastThrow<std::string>((it.first()));
else
ret += beast::lexicalCastThrow <std::string> (it.first) + "-"
+ beast::lexicalCastThrow <std::string> (it.second);
ret += beast::lexicalCastThrow<std::string>(it.first()) + "-" +
beast::lexicalCastThrow<std::string>(it.last());
}

if (ret.empty ())
if (ret.empty())
return "empty";

return ret;
}

void RangeSet::simplify ()
{
iterator it = mRanges.begin ();

while (1)
{
iterator nit = it;

if (++nit == mRanges.end ())
{
checkInternalConsistency();
return;
}

if (it->second >= (nit->first - 1))
{
// ranges overlap
it->second = std::max(it->second, nit->second);
mRanges.erase (nit);
}
else
{
it = nit;
}
}
}

std::size_t
RangeSet::lebesgue_sum() const
{
std::size_t sum = mRanges.size();
for (auto const& e : mRanges)
sum += e.second - e.first;
return sum;
}

void RangeSet::checkInternalConsistency () const noexcept
{
#ifndef NDEBUG
if (mRanges.size () > 1)
{
const_iterator const last = std::prev (mRanges.end ());

for (const_iterator cur = mRanges.begin (); cur != last; ++cur)
{
const_iterator const next = std::next (cur);
assert (cur->first <= cur->second);
assert (next->first <= next->second);
assert (cur->second + 1 < next->first);
}
}
else if (mRanges.size () == 1)
{
const_iterator const iter = mRanges.begin ();
assert (iter->first <= iter->second);
}
#endif
}

} // ripple
} // ripple
Loading

0 comments on commit 3d25fab

Please sign in to comment.