// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Jonathan Berry, Darren Lim (Rensselaer Polytechnic Institute)
// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.
// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.
// Last File Update: 7-Aug-1997
// 
#ifndef MSet_CC
#define MSet_CC
#include <iostream.h>
template <class Item, class Impl>
MSet<Item,Impl>::MSet(const MSet<Item, Impl>& m)
{
    if (&m == this)
	return;
    store = m.store;
    ((Container<Item>*)store)->incrRefCount(); //ref_count++;
}
template <class Item, class Impl>
MSet<Item,Impl>::MSet(const Collection<Item> &col)
{
    Iterator<Item> get_next(&col);
    Item item; 
    store = new Impl;
    ((Container<Item>*)store)->initRefCount();  //ref_count = 1;
    while (get_next(item))
	insert(item);
}
template <class Item, class Impl>
MSet<Item,Impl>::MSet(const Container<Item> &con)
{
    Iterator<Item> get_next(&con);
    Item item; 
    store = new Impl;
    ((Container<Item>*)store)->initRefCount();  //ref_count = 1;
    while (get_next(item))
	insert(item);
}
template <class Item, class Impl>
MSet<Item,Impl>::MSet() : Collection<Item>()
{ 
    store = new Impl;
    ((Container<Item>*)store)->initRefCount();  //ref_count = 1;
}
//
// destructor for an implementation of Set uses reference counting
//
template <class Item, class Impl>
MSet<Item,Impl>::~MSet() 
{ 
    ((Container<Item>*)store)->decrRefCount(); 
    if (((Container<Item>*)store)->getRefCount() == 0)
	delete store;
}
//
// copy one Set Class instance to another
//
template <class Item, class Impl>
MSet<Item, Impl>&
MSet<Item,Impl>::operator=(const MSet<Item,Impl>& m)
{
    if (&m == this)
        return *this;
    ((Container<Item>*)store)->decrRefCount(); 
    if (((Container<Item>*)store)->getRefCount() == 0)
	delete store; 
    store = m.store;
    ((Container<Item>*)store)->incrRefCount(); 
    return *this;
}
template <class Item, class Impl>
MSet<Item, Impl>&
MSet<Item,Impl>::operator=(const Collection<Item>& c)
{
    if (&c == this)
        return *this;
    newStore();
    Iterator<Item> get_next(&c);
    Item item;
    while (get_next(item))
        insert(item);
    return *this;
}
   
//
// create a new store
//
template <class Item, class Impl>
void
MSet<Item,Impl>::newStore()
{
    Impl* nstore = new Impl();
    ((Container<Item>*)nstore)->initRefCount(); 
    ((Container<Item>*)store)->decrRefCount(); 
    if (((Container<Item>*)store)->getRefCount() == 0)
	delete store; 
    store = nstore;
}
//
// create a new multi-set
//
template <class Item, class Impl>
void
MSet<Item,Impl>::newMSet(MSet<Item,Impl>* m)
{
    Item item; 
    Impl* x = m->store;
    Iterator<Item> get_next(x);
    if (((Container<Item>*)store)->getRefCount() > 1) {
    //if (store->ref_count > 1) {
	newStore();
	while (get_next(item))
	    insert(item);
    }
}
template <class Item, class Impl>
Item
MSet<Item,Impl>::numOccurrences(Iterator<Item>& it, Item& cur_val, 
                                    int& count, int &not_done) const
{
	Item next_val;
	count = 1;
	while ((not_done=it(next_val)) && 
		(ElementOps<Item>::compareItems(cur_val,next_val)==0))
		count++;
	return next_val;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::sortedUnions(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    int s1, s2;
    Item e1, e2;
    Iterator<Item> get_next1(this), get_next2(&col);
    s1 = get_next1(e1); s2 = get_next2(e2);
    while (s1 || s2) {
	while (s1 && (!s2 || (ElementOps<Item>::compareItems(e1,e2)<0))) {
		result.append(e1);
   		s1 = get_next1(e1);
	}
	while (s2 && (!s1 || (ElementOps<Item>::compareItems(e2, e1)<0))) {
		result.append(e2);
   		s2 = get_next2(e2);
	}
	if (s1 && s2 && ElementOps<Item>::compareItems(e1, e2)==0) {
		result.append(e1);
		result.append(e2);
   		s1 = get_next1(e1);
   		s2 = get_next2(e2);
	}
    }
    return result;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::unsortedUnions(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    Iterator<Item> get_next1(this);
    Iterator<Item> get_next2(&col);
    Item item; 
    while (get_next1(item))  
      result.insert(item);
    while (get_next2(item))  
      result.insert(item);
    return result;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::unions(const Collection<Item>& col)
{
    if (sortedQ() && col.sortedQ())
    	return sortedUnions(col);
    else
    	return unsortedUnions(col);
}
//
// create a new multi-set which is the union of the two multi-sets
//
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::operator+(const Collection<Item>& col)
{
    return unions(col);
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::sortedIntersection(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    int s1, s2, count1, count2;
    Item e1, e2, next_e1, next_e2;
    Iterator<Item> get_next1(this), get_next2(&col);
    s1 = get_next1(e1); s2 = get_next2(e2);
    while (s1 && s2) {
	while (s1 && s2 && (ElementOps<Item>::compareItems(e1,e2)<0)) {
   		s1 = get_next1(e1);
	}
	while (s1 && s2 && (ElementOps<Item>::compareItems(e2,e1)<0)) {
   		s2 = get_next2(e2);
	}
	if (s1 && s2 && ElementOps<Item>::compareItems(e1,e2)==0) {
		next_e1 = e1;  
		next_e2 = e2;
		next_e1 = numOccurrences(get_next1, e1, count1, s1);
		next_e2 = numOccurrences(get_next2, e1, count2, s2);
		count1 = (count1 > count2) ? count2 : count1;
		for (int i=0; i< count1; i++)
			result.append(e1);
		e1 = next_e1;
		e2 = next_e2;
	}
	//if ((!s2&&(ElementOps<Item>::compareItems(e2,e1)<0)) || 
	//    (!s1&&(ElementOps<Item>::compareItems(e1,e2)<0)))
	//	return result;
    }
    return result;
}
//
// This could be implemented much more efficiently by building a 
// frequency table.
//
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::unsortedIntersection(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    Iterator<Item> get_next(this);
    Item item;
    int count, minFreq, temp;
    while (get_next(item)) 
       if (!result.memberQ(item)) {
          minFreq = occurrences(item);
          temp = col.occurrences(item);
          if (minFreq > temp)
            minFreq = temp;
          for (count = 0; count < minFreq; count++)
            result.insert(item);
       }
    return result;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::intersection(const Collection<Item>& col)
{
    if (sortedQ() && col.sortedQ())
    	return sortedIntersection(col);
    else
    	return unsortedIntersection(col);
}
//
// create a new multi-set which is the intersection of the two multi-sets
//
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::operator^(const Collection<Item>& col)
{
    return intersection(col);
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::sortedDifference(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    int s1, s2, count1, count2, diff;
    Item e1, e2;
    Iterator<Item> get_next1(this), get_next2(&col);
    s1 = get_next1(e1); s2 = get_next2(e2);
    while (s1) {
	while (s1 && (!s2 || (ElementOps<Item>::compareItems(e1,e2)<0))) {
		result.append(e1);
   		s1 = get_next1(e1);
	}
	if (s1) {
		while (s2 && (ElementOps<Item>::compareItems(e2,e1)<0)) 
   			s2 = get_next2(e2);
	}
	if (s1 && s2 && (ElementOps<Item>::compareItems(e1,e2)==0)) {
		Item cur = e1;
		e1 = numOccurrences(get_next1, e1, count1, s1);
		e2 = numOccurrences(get_next2, e2, count2, s2);
		count1 = ((diff=(count1 - count2))>=0) ? diff : 0;
		for (int i=0; i< diff; i++)
			result.append(cur);
	}
    }
    return result;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::unsortedDifference(const Collection<Item>& col)
{
    MSet<Item,DEFAULT_SET_IMPL<Item> > result;
    Iterator<Item> get_next(this);
    Item item;
    int count, monFreq;
    while (get_next(item))
      if (!result.memberQ(item))
        {
          monFreq = occurrences(item) - col.occurrences(item);
          for (count = 1; count <= monFreq; count++)
            result.insert(item);
        };
    return result;
}
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::difference(const Collection<Item>& col)
{
    if (sortedQ() && col.sortedQ())
    	return sortedDifference(col);
    else
    	return unsortedDifference(col);
}
//
// create a new multi-set which is the original minus the collection argument
//
template <class Item, class Impl>
MSet<Item,DEFAULT_SET_IMPL<Item> >
MSet<Item,Impl>::operator-(const Collection<Item>& col)
{
    return difference(col);
}

template <class Item, class Impl>
Bool
MSet<Item,Impl>::subsetQ(const Collection<Item>& col)
{
    return difference(col).emptyQ();
}
//
// return the number of occurrences of item in the collection. 
//
template <class Item, class Impl>
int
MSet<Item, Impl>::occurrences(const Item item) const
{
    Iterator<Item> get_next(this);
    Item it; 
    int freq = 0;
    while (get_next(it))
      if (ElementOps<Item>::compareItems(item, it) == 0)
        freq++;
    return freq;
}
template <class Item, class Impl>
int
MSet<Item, Impl>::rank(Item item) const
{
    Iterator<Item> get_next(this);
    Item it; 
    int pos = 0;
    while (get_next(it)) {
      if (ElementOps<Item>::compareItems(item, it) == 0)
        return pos;
      pos++;
    }
    return -1;
}
template <class Item, class Impl>
Item
MSet<Item, Impl>::ref(int k) const
{
    static Item i;
    if ((k < 0) || (k >= size())) {
	return i;
    }
    int pos=0;
    Iterator<Item> get_next(this);
    Item it; 
    while (get_next(it)) {
      
      if (pos == k)
        return it;
      pos++;
    }
    return i;
}
template <class Item, class Impl>
Bool
MSet<Item, Impl>::permutationQ() const
{
    Iterator<Item> get_next(this);
    Item it;
    if (sortedQ()) {
    	Item prev=first(); 
    	if (get_next(it)) {
    		while (get_next(it)) {
			if (ElementOps<Item>::compareItems(it, prev) == 0)
				return FALSE;
			prev = it;
		}
	}
	return TRUE;
    } else {
	while (get_next(it))
		if (occurrences(it) > 1)
			return FALSE;
	return TRUE;
    }
}
template <class Item, class Impl>
Bool
MSet<Item,Impl>::properSubsetQ(const Collection<Item>& col)
{
    if (store->size() < col.size())
        return subsetQ(col);
    else return FALSE;
}
//
// display a human-readable representation of the multi-set
//
template <class Item, class Impl>
ostream&
MSet<Item,Impl>::display(ostream& os) const
{
    os << "{";    
    Iterator<Item> get_next(this);
    Item i;
    Bool start = FALSE;
    while (get_next(i))
         if (!start) {
                ElementOps<Item>::displayItem(os, i);
                start = TRUE;
         } else {
                os << " ";
                ElementOps<Item>::displayItem(os, i);
	}
    os << "}";    
    return os;
}

#endif
