// A module of KPlib v1.4.2. // Written by Keith Pomakis during the summer of 1994. // Released to the public domain on October 10, 1994. // This version released on March 30, 1999. #ifndef KP_BAG_DEFINED #define KP_BAG_DEFINED #include #include "KPbasic.h" #include "KPList.h" // Assumes Element has a default constructor, operator=(), operator==(), // and operator<(). Note that operator<() must place a total ordering on // the Elements. // KPBag::operator<() places a total ordering on all KPBag elements, // so that KPSet > is possible. template class KPBag { public: KPBag(); KPBag(const KPBag &bag); KPBag(const KPList &list); KPBag(const Element &element, int count=1); ~KPBag(); operator KPList() const; KPBag operator+(const KPBag &bag) const; KPBag operator+(const Element &element) const; KPBag &operator+=(const KPBag &bag); KPBag &operator+=(const Element &element); KPBag &add(const KPBag &bag); KPBag &add(const Element &element, int num=1); // It is illegal to call the following functions unless the elements // are there to be removed! If unsure, call contains() first. KPBag operator-(const KPBag &bag) const; KPBag operator-(const Element &element) const; KPBag &operator-=(const KPBag &bag); KPBag &operator-=(const Element &element); KPBag &remove(const KPBag &bag); KPBag &remove(const Element &element, int num=1); KPBag &remove_all(const Element &element); // Miscellaneous bool operator==(const KPBag &bag) const; bool operator!=(const KPBag &bag) const; bool operator<(const KPBag &bag) const; KPBag &operator=(const KPBag &bag); KPBag &operator=(const Element &element); KPBag &operator=(const KPList &list); KPList list() const; KPList unique_list() const; int size() const; // Total number of elements. int unique_size() const; // Total number of unique elements. bool is_empty() const; bool contains(const KPBag &bag) const; bool contains(const Element &element, int num=1) const; int occurrences_of(const Element &element) const; Element retrieve(); // Returns and removes an element. KPBag &clear(); protected: class Slot { public: Element my_element; int my_count; public: Slot(): my_count(0) { /* do nothing */ } Slot(const Slot &slot): my_count(slot.my_count) { my_element = slot.my_element; } Slot(const Element &element, int num): my_count(num) { my_element = element; } void operator=(const Slot &slot) { my_element = slot.my_element; my_count = slot.my_count; } }; KPList::Slot> my_list; }; /****************************************************************************/ template inline KPBag::KPBag(): my_list() { /* do nothing */ } /****************************************************************************/ template inline KPBag::KPBag(const KPBag &bag): my_list(bag.my_list) { /* do nothing */ } /****************************************************************************/ template inline KPBag::KPBag(const KPList &list) { *this = list; } /****************************************************************************/ template inline KPBag::KPBag(const Element &element, int count): my_list() { if (count < 0) { cerr << "KPBag - negative count\n"; exit(EXIT_FAILURE); } else if (count > 0) { KPBag::Slot slot(element, count); my_list = slot; } } /****************************************************************************/ template inline KPBag::~KPBag() { my_list.clear(); } /****************************************************************************/ template KPBag::operator KPList() const { KPList list; KPReadOnlyIterator::Slot> iter(my_list); FOREACH (iter) for (register int i=0; imy_count; i++) list.add_to_tail(iter->my_element); return list; } /****************************************************************************/ template inline KPBag KPBag::operator+(const KPBag &bag) const { KPBag new_bag(*this); new_bag += bag; return new_bag; } /****************************************************************************/ template inline KPBag KPBag::operator+(const Element &element) const { KPBag new_bag(*this); new_bag += element; return new_bag; } /****************************************************************************/ template inline KPBag & KPBag::operator+=(const KPBag &bag) { return add(bag); } /****************************************************************************/ template inline KPBag & KPBag::operator+=(const Element &element) { return add(element, 1); } /****************************************************************************/ template KPBag & KPBag::add(const KPBag &bag) { KPIterator::Slot> a(my_list); KPReadOnlyIterator::Slot> b(bag.my_list); while (b.ptr()) { while (a.ptr() && a->my_element < b->my_element) a++; if (!a.ptr()) { for (; b.ptr(); b++) my_list.add_to_tail(*b); } else { if (a->my_element == b->my_element) { a->my_count += b->my_count; a++; } else a.insert_before_current(*b); b++; } } return *this; } /****************************************************************************/ template KPBag & KPBag::add(const Element &element, int num) { if (num < 0) { cerr << "KPBag - can't add a negative number of something\n"; exit(EXIT_FAILURE); } else if (num > 0) { KPIterator::Slot> iter(my_list); // Start looking from the end of the list, in case the elements are // being added in alphabetical order. iter.end(); while (iter.ptr() && element < iter->my_element) --iter; if (!iter.ptr()) { KPBag::Slot slot(element, num); my_list.add_to_head(slot); } else if (iter->my_element == element) iter->my_count += num; else { KPBag::Slot slot(element, num); iter.insert_after_current(slot); } } return *this; } /****************************************************************************/ template KPBag KPBag::operator-(const KPBag &bag) const { KPBag new_bag; KPReadOnlyIterator::Slot> a(my_list), b(bag.my_list); FOREACH(b) { while (a.ptr() && a->my_element < b->my_element) a++; if (!a.ptr() || b->my_element < a->my_element || b->my_count > a->my_count) { cerr << "KPBag - error subtracting more than exists\n"; exit(EXIT_FAILURE); } else if (b->my_count < a->my_count) { KPBag::Slot slot(a->my_element, a->my_count - b->my_count); new_bag.my_list.add_to_tail(slot); } a++; } return new_bag; } /****************************************************************************/ template inline KPBag KPBag::operator-(const Element &element) const { KPBag new_bag(*this); return new_bag.remove(element, 1); } /****************************************************************************/ template inline KPBag & KPBag::operator-=(const KPBag &bag) { return remove(bag); } /****************************************************************************/ template inline KPBag & KPBag::operator-=(const Element &element) { return remove(element, 1); } /****************************************************************************/ template KPBag & KPBag::remove(const KPBag &bag) { if (this == &bag) my_list.clear(); else { KPIterator::Slot> a(my_list); KPReadOnlyIterator::Slot> b(bag.my_list); FOREACH(b) { while (a.ptr() && a->my_element < b->my_element) a++; if (!a.ptr() || b->my_element < a->my_element || b->my_count > a->my_count) { cerr << "KPBag - error subtracting more than exists\n"; exit(EXIT_FAILURE); } else if (b->my_count < a->my_count) a->my_count -= b->my_count; else a.remove_current(); a++; } } return *this; } /****************************************************************************/ template KPBag & KPBag::remove(const Element &element, int num) { KPIterator::Slot> iter(my_list); while (iter.ptr() && iter->my_element < element) iter++; if (!iter.ptr() || element < iter->my_element || num > iter->my_count) { cerr << "KPBag - error subtracting more than exists\n"; exit(EXIT_FAILURE); } else if (num < iter->my_count) iter->my_count -= num; else iter.remove_current(); return *this; } /****************************************************************************/ template KPBag & KPBag::remove_all(const Element &element) { KPIterator::Slot> iter(my_list); while (iter.ptr() && iter->my_element < element) iter++; if (iter.ptr() && iter->my_element == element) iter.remove_current(); return *this; } /****************************************************************************/ template bool KPBag::operator==(const KPBag &bag) const { if (this == &bag) return true; if (my_list.size() != bag.my_list.size()) return false; KPReadOnlyIterator::Slot> a(my_list), b(bag.my_list); while (a.ptr()) { if (!(a->my_count == b->my_count && a->my_element == b->my_element)) return false; a++, b++; } return true; } /****************************************************************************/ template inline bool KPBag::operator!=(const KPBag &bag) const { return !operator==(bag); } /****************************************************************************/ template bool KPBag::operator<(const KPBag &bag) const { if (bag.my_list.size() < my_list.size()) return false; else if (my_list.size() < bag.my_list.size()) return true; else { KPReadOnlyIterator::Slot> a(my_list), b(bag.my_list); while (a.ptr()) { if (b->my_count < a->my_count) return false; else if (a->my_count < b->my_count) return true; else if (b->my_element < a->my_element) return false; else if (a->my_element < b->my_element) return true; a++, b++; } } return false; } /****************************************************************************/ template inline KPBag & KPBag::operator=(const KPBag &bag) { my_list = bag.my_list; return *this; } /****************************************************************************/ template inline KPBag & KPBag::operator=(const Element &element) { KPBag::Slot slot(element, 1); my_list += slot; return *this; } /****************************************************************************/ template KPBag & KPBag::operator=(const KPList &list) { clear(); KPReadOnlyIterator iter(list); FOREACH(iter) add(*iter); return *this; } /****************************************************************************/ template KPList KPBag::list() const { KPList list; KPReadOnlyIterator::Slot> iter(my_list); FOREACH(iter) for (register int i=0; imy_count; i++) list.add_to_tail(iter->my_element); return list; } /****************************************************************************/ template KPList KPBag::unique_list() const { KPList list; KPReadOnlyIterator::Slot> iter(my_list); FOREACH(iter) list.add_to_tail(iter->my_element); return list; } /****************************************************************************/ template int KPBag::size() const { int total_count = 0; KPReadOnlyIterator::Slot> iter(my_list); FOREACH(iter) total_count += iter->my_count; return total_count; } /****************************************************************************/ template inline int KPBag::unique_size() const { return my_list.size(); } /****************************************************************************/ template inline bool KPBag::is_empty() const { return my_list.is_empty(); } /****************************************************************************/ template bool KPBag::contains(const KPBag &bag) const { if (this == &bag) return true; KPReadOnlyIterator::Slot> a(my_list), b(bag.my_list); while (b.ptr()) { while (a.ptr() && a->my_element < b->my_element) a++; if (!(a.ptr() && a->my_element == b->my_element && a->my_count >= b->my_count)) return false; a++; b++; } return true; } /****************************************************************************/ template bool KPBag::contains(const Element &element, int num) const { KPReadOnlyIterator::Slot> iter(my_list); while (iter.ptr() && iter->my_element < element) iter++; return (iter.ptr() && iter->my_element == element && iter->my_count >= num); } /****************************************************************************/ template int KPBag::occurrences_of(const Element &element) const { KPReadOnlyIterator::Slot> iter(my_list); while (iter.ptr() && iter->my_element < element) iter++; if (iter.ptr() && iter->my_element == element) return iter->my_count; else return 0; } /****************************************************************************/ template Element KPBag::retrieve() { if (my_list.is_empty()) { cerr << "KPBag: retreive() - bag empty\n"; exit(EXIT_FAILURE); } Element element = my_list.head().my_element; if (--my_list.head().my_count == 0) my_list.remove_head(); return element; } /****************************************************************************/ template inline KPBag & KPBag::clear() { my_list.clear(); return *this; } /****************************************************************************/ #endif /* KP_BAG_DEFINED */