/*
 * Copyright (c) 1997,1998
 * Babes-Bolyai University, Cluj-Napoca
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Babes-Bolyai University makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 */

#ifndef __OCL_COLLECTION__
#define __OCL_COLLECTION__

/////////////////////////////////////////////////////////////////////////////

#include <oclbasic>
#include <set>
#include <vector>

#ifndef __OCL_NO_NAMESPACES
namespace ocl {
#endif

#ifndef __OCL_NO_STD
using namespace std;
#endif

/////////////////////////////////////////////////////////////////////////////
// OCL Helper Classes - definitions

template <class Type>
class __OclPred
{
public:
    bool operator () (const Type& v1, const Type& v2) const
        { return (v1 < v2); }
    // You must specialize this template class
    // when you want to use sets or bags
    // containing user-defined type objects.
};

template <class Type>
class __OclAllocator : public allocator<Type>
{};

/////////////////////////////////////////////////////////////////////////////
// OCL Collection Types - declarations

template <class Type, class Container>
class Collection;

template <class Type,
          class Pred = __OclPred<Type>,
          class Allocator = __OclAllocator<Type> >
class Set;

template <class Type,
          class Pred = __OclPred<Type>,
          class Allocator = __OclAllocator<Type> >
class Bag;

template <class Type,
          class Allocator = __OclAllocator<Type> >
class Sequence;

/////////////////////////////////////////////////////////////////////////////
// OCL Collection - definition

template <class Type, class Container>
class Collection : public Container
{
public:
    Integer size() const;
    Integer count(const Type& an_object) const;
    Boolean includes(const Type& an_object) const;
    Boolean includesAll(const Collection<Type, Container>& a_collection)
        const;
    Boolean isEmpty() const;
    Boolean notEmpty() const;
    Type sum() const;

    template <class ExpressionType>
    Boolean exists(ExpressionType (*expression)());

    template <class ExpressionType>
    Boolean forAll(ExpressionType (*expression)());

    template <class ExpressionType>
    ExpressionType iterate(ExpressionType (*expression)());

    template <class ExpressionType>
    Collection<Type, Container> select(ExpressionType (*expression)());

    template <class ExpressionType>
    Collection<Type, Container> reject(ExpressionType (*expression)());

    template <class ExpressionType,
              class ExpressionPred = __OclPred<ExpressionType>,
              class ExpressionAllocator = __OclAllocator<ExpressionType> >
    Bag<ExpressionType, ExpressionPred, ExpressionAllocator>
        collect(ExpressionType (*expression)());
};

/////////////////////////////////////////////////////////////////////////////
// OCL Set - definition

template <class Type,
          class Pred = __OclPred<Type>,
          class Allocator = __OclAllocator<Type> >
class Set : public Collection<Type, set<Type, Pred, Allocator> >
{
public:
    Set<Type, Pred, Allocator>
        reunion(const Set<Type, Pred, Allocator>& a_set) const;
    Set<Type, Pred, Allocator>
        reunion(const Bag<Type, Pred, Allocator>& a_bag) const;
    Set<Type, Pred, Allocator>
        intersection(const Set<Type, Pred, Allocator>& a_set) const;
    Set<Type, Pred, Allocator>
        intersection(const Bag<Type, Pred, Allocator>& a_bag) const;
    Set<Type, Pred, Allocator>
        difference(const Set<Type, Pred, Allocator>& a_set) const;
    Set<Type, Pred, Allocator>
        symmetricDifference(const Set<Type, Pred, Allocator>& a_set) const;
    Set<Type, Pred, Allocator>
        including(const Type& an_object) const;
    Set<Type, Pred, Allocator>
        excluding(const Type& an_object) const;
    Bag<Type, Pred, Allocator> asBag() const;
    Sequence<Type, Allocator> asSequence() const;
};

/////////////////////////////////////////////////////////////////////////////
// OCL Bag - definition

template <class Type,
          class Pred = __OclPred<Type>,
          class Allocator = __OclAllocator<Type> >
class Bag : public Collection<Type, multiset<Type, Pred, Allocator> >
{
public:
    Bag<Type, Pred, Allocator>
        reunion(const Bag<Type, Pred, Allocator>& a_bag) const;
    Bag<Type, Pred, Allocator>
        reunion(const Set<Type, Pred, Allocator>& a_set) const;
    Bag<Type, Pred, Allocator>
        intersection(const Bag<Type, Pred, Allocator>& a_bag) const;
    Bag<Type, Pred, Allocator>
        intersection(const Set<Type, Pred, Allocator>& a_set) const;
    Bag<Type, Pred, Allocator>
        including(const Type& an_object) const;
    Bag<Type, Pred, Allocator>
        excluding(const Type& an_object) const;
    Set<Type, Pred, Allocator> asSet() const;
    Sequence<Type, Allocator> asSequence() const;
};

/////////////////////////////////////////////////////////////////////////////
// OCL Sequence - definition

template <class Type,
          class Allocator = __OclAllocator<Type> >
class Sequence : public Collection<Type, vector<Type, Allocator> >
{
public:
    Sequence<Type, Allocator>
        reunion(const Sequence<Type, Allocator>& a_sequence) const;
    Sequence<Type, Allocator>
        append(const Type& an_object) const;
    Sequence<Type, Allocator>
        prepend(const Type& an_object) const;
    Sequence<Type, Allocator>
        including(const Type& an_object) const;
    Sequence<Type, Allocator>
        excluding(const Type& an_object) const;
    Type& operator [] (const Integer position);
    const Type& operator [] (const Integer position) const;
    Type& at(const Integer position);
    const Type& at(const Integer n) const;
    Sequence<Type, Allocator>
        subSequence(const Integer lower, const Integer upper) const;
    Type& first() const;
    Type& last() const;
    Set<Type, __OclPred<Type>, Allocator> asSet() const;
    Bag<Type, __OclPred<Type>, Allocator> asBag() const;
};

/////////////////////////////////////////////////////////////////////////////
// OCL Collection Types - global functions - declarations

template <class FirstType, class SecondType, class ResultType>
ResultType reunion(
    const FirstType& first_collection, const SecondType& second_collection);

template <class FirstType, class SecondType, class ResultType>
ResultType intersection(
    const FirstType& first_collection, const SecondType& second_collection);

template <class FirstType, class SecondType, class ResultType>
ResultType difference(
    const FirstType& first_collection, const SecondType& second_collection);

template <class FirstType, class SecondType, class ResultType>
ResultType symmetricDifference(
    const FirstType& first_collection, const SecondType& second_collection);

template <class SourceType, class DestinationType>
DestinationType __clone(const SourceType& a_collection);

/////////////////////////////////////////////////////////////////////////////

#ifndef __OCL_NO_NAMESPACES
}  // namespace ocl
#endif

#ifndef __OCL_NO_INSTANTIATE
#include <oclcoll.cc>
#endif

/////////////////////////////////////////////////////////////////////////////

#endif  // __OCL_COLLECTION__
