// WARNING: Changes to this file must be contributed back to Sawyer or else they will // be clobbered by the next update from Sawyer. The Sawyer repository is at // https://github.com/matzke1/sawyer. #ifndef Sawyer_Container_IntervalSetMap_H #define Sawyer_Container_IntervalSetMap_H #include #include #include "IntervalMap.h" #include "Sawyer.h" namespace Sawyer { namespace Container { /** Mapping from integers to sets. * * This container maps integer keys to sets of values and is optimized to store the same set across adjacent keys by using * intervals of the key. For instance, an IntervalSetMap that maps integer keys to sets of characters is declared like this: * * @code * typedef Interval IntRange; * typedef Set CharSet; * typedef IntervalSetMap IntCharMap; * IntCharMap icmap; * @endcode * * Such a map stores a set of characters for each integer key, but does so efficiently when the same set is stored across many * consecutive keys. For instance, one can store the set {'a', 'b'} across a few million keys and use very little storage: * * @code * icmap.insert(IntRange::baseSize(0,5000000), 'a'); * icmap.insert(IntRange::baseSize(0,5000000), 'b'); * @endcode * * At this point @c icmap is storing 'a' and 'b' at every key from 0 through 4999999, inclusive. This could also have been * done by constructing the set first and then inserting the set. The real power of this container comes from the fact that * one can insert values without regard for what intervals currently exist. For instance, we now insert a few more characters: * * @code * icmap.insert(5, 'c'); // 5 is a singleton range * icmap.insert(IntRange::hull(10,19), 'd'); * @endcode * * Now @p icmap stores {'a', 'b'} at keys 0 through 4, {'a', 'b', 'c'} at key 5, {'a', 'b'} at keys 6 through 9, {'a', 'b', * 'd'} at keys 10 through 19, and {'a', 'b'} at keys 20 through 4999999. * * Erasing values works similarly: one can erase a character from an interval or single key without regard for what intervals * already exist. Attempting to erase a character from a set that doesn't contain the character is a no-op. Here we erase * 'b' and 'e' from large parts of the map key space: * * @code * icmap.erase(icmap.hull(), 'b'); // erase 'b' from everywhere * icmap.erase(IntRange::hull(-1000000,1000000), 'e'); * @endcode * * Querying is also quite efficient. Here we obtain the set of all values stored in the map's sets: * * @code * Set allValues = icmap.getUnion(icmap.hull()); * @endcode * * There are also predicates to determine whether a key or value is present in the map. * * @code * icmap.contains(IntRange::hull(10,19)); // Do keys 10 through 19 all have non-empty sets? * icmap.containsAnywhere(icmap.hull(), 'b'); // Is value 'b' present anywhere in the map? * icmap.containsEverywhere(IntRange::hull(10,19), 'a'); // Is value 'a' present for all keys 10 through 19? * @endcode * * The @p S template parameter is the set type and must implement the API defined by @ref Sawyer::Container::Set. * * @sa * * See @ref IntervalMap for a similar container whose values don't act like sets. */ template class IntervalSetMap: public IntervalMap { typedef IntervalMap Super; public: typedef I Interval; /**< Interval type for keys. */ typedef S Set; /**< Set type for values. */ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Iterators //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public: /** Union of values over an interval of keys. * * Returns the union of the sets stored across an interval of keys. */ Set getUnion(const Interval &interval) const { Set retval; DYN_FOREACH (const typename Super::Node &node, this->findAll(interval)) { DYN_FOREACH (const typename Set::Value &member, node.value().values()) { retval.insert(member); } } return retval; } /** Intersection of values over an interval of keys. * * Returns the set of values that are present for all keys in the interval. */ Set getIntersection(const Interval &interval) const { Set retval; size_t nNodes = 0; DYN_FOREACH (const typename Super::Node &node, this->findAll(interval)) { const Set &set = this->get(node.key().least()); if (1 == ++nNodes) { retval = set; } else { retval &= set; } } return retval; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Predicates and queries //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public: /** Determines if values are stored for an interval. * * Returns true if get(interval) would return a non-empty set. */ bool exists(const Interval &interval) const { return Super::contains(interval); } /** Determines if a particular value is stored in an interval. * * Returns true if getUnion(interval) would return a set containing @p value as a member. In particular, this * returns false if the @p interval is empty. This is more efficient than calling getUnion(interval) and * checking whether it contains @p value. */ bool existsAnywhere(const Interval &interval, const typename Set::Value &value) const { DYN_FOREACH (const typename Super::Node &node, this->findAll(interval)) { if (node.value().exists(value)) return true; } return false; } /** Determines if a particular value is stored everywhere in the interval. * * Returns true if getIntersection(interval) would return a set containing @p value as a member. In * particular, this returns false if the @p interval is empty. This is more efficient than calling * getIntersection(interval) and checking whether it contains @p value. */ bool existsEverywhere(const Interval &interval, const typename Set::Value &value) const { if (interval.isEmpty()) return false; DYN_FOREACH (const typename Super::Node &node, this->findAll(interval)) { if (!node.value().exists(value)) return false; } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Mutators //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public: /** Erase sets for an interval. * * Erases the sets associated with the given interval of keys. */ void erase(const Interval &interval) { Super::erase(interval); } /** Erases one value from a set over an interval. * * Erases the specified @p value from all sets over the specified @p interval of keys. Any sets that become empty are * removed from the map as if @c erase had been called on that sub-interval. */ bool erase(const Interval &interval, const typename Set::Value &value) { Set set; set.insert(value); return erase(interval, set); } /** Erase specified values from the sets of an interval. * * Erases the specified values from all sets over the specified @p interval of keys. Any sets that become empty are * removed from the map as if single-argument @c erase hd been called on that sub-interval. Returns true if any values * were erased, false if none of the values were members of the affected sets. */ bool erase(const Interval &interval, const Set &values) { bool isErased = false; Interval worklist = interval; while (!worklist.isEmpty()) { typename Super::ConstNodeIterator iter = this->findFirstOverlap(worklist); if (iter == this->nodes().end()) { break; } else if (worklist.least() < iter->key().least()) { worklist = Interval::hull(iter->key().least(), worklist.greatest()); } else { Interval work = worklist.intersection(iter->key()); Set set = this->get(work.least()); if (set.erase(values)) { replace(work, set); isErased = true; } if (work == worklist) break; worklist = Interval::hull(work.greatest()+1, worklist.greatest()); } } return isErased; } /** Insert one value to the sets of an interval. * * Inserts the specified @p value to all sets in the @p interval of keys. Returns true if the value was inserted anywhere, * false if the value already existed everywhere. */ bool insert(const Interval &interval, const typename Set::Value &value) { Set set; set.insert(value); return insert(interval, set); } /** Insert a set of values into the sets of an interval. * * Inserts the specified values into all sets in the @p interval of keys. Returns true if any value was inserted * anywhere, false if all values already existed in the sets of all specified keys. */ bool insert(const Interval &interval, const Set &values) { bool isInserted = false; Interval worklist = interval; while (!worklist.isEmpty()) { typename Super::ConstNodeIterator iter = this->findFirstOverlap(worklist); Set set; Interval work; if (iter == this->nodes().end()) { work = worklist; } else if (worklist.least() < iter->key().least()) { work = Interval::hull(worklist.least(), iter->key().least() - 1); } else { work = worklist.intersection(iter->key()); set = this->get(work.least()); } if (set.insert(values)) { Super::insert(work, set); isInserted = true; } if (work == worklist) break; worklist = Interval::hull(work.greatest()+1, worklist.greatest()); } return isInserted; } /** Replace sets with a new set. * * Replaces sets for keys in the specified @p interval with the specified @p set. */ void replace(const Interval &interval, const Set &set) { if (set.isEmpty()) { Super::erase(interval); } else { Super::insert(interval, set); } } }; } // namespace } // namespace #endif