/* $Id$
 *
 * RangeSet defines operation for a Range and sets of ranges, like checking if
 * one range is a subset of another.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */


#ifndef __RANGE_SET_HPP_INCLUDED
#define __RANGE_SET_HPP_INCLUDED

#include <list>
#include <iostream>
#include "frontend/ast/Types.hpp"
#include "frontend/ast/DiscreteRange.hpp"

namespace ast {

//! operation on ranges / sets of ranges.
/** The class RangeSet is useful to determine subsets of ranges and 
 *  being able to iterate over those.
 */
class RangeSet {
public:
	//! c'tor
	/** @param l left bound of the RangeSet. 
	 *  @param r right bound of the RangeSet.
	 *  l <= r must be true.
	 */
	RangeSet(universal_integer l, universal_integer r);

	//! alternate c'tor
	/** construct from DiscreteRange.
	 *  @param dr source discrete range. Must not be a NULL range.
	 */
	RangeSet(const DiscreteRange &dr);

	/** substract a range from l to r from the current range.
	 *  @param l left bound of the range to substract.
	 *  @param r right bound of the range to substract.
	 *  @return true, if [l, r] is a subset of this Range, false 
	 *  	    otherwise.
	 *  l <= r must be true.
	 */
	bool minus(universal_integer l, universal_integer r);

	/** substract a range from the current range.
	 *  Does the same as the other minus, just for a DiscreteRange.
	 *  @param dr range to substract. Must not be a null range.
	 *  @return true if [l, r] is a subset of this Range, false
	 *          otherwise.
	 */
	bool minus(const DiscreteRange &dr);

	/** unify with given Range.
	 *  @param dr DiscreteRange to unify with.
	 */
	void plus(const DiscreteRange &dr);

	/** unify with a range from l to r.
	 *  @param l left bound of the range.
	 *  @param r right bound of the range.
	 *  l <= r must be true.
	 */
	void plus(universal_integer l, universal_integer r);

	/** is the range set empty?
	 *  @return true, if there are no ranges in the set.
	 */
	bool empty(void) const;

	/** clear all elements from the RangeSet. */
	void clear(void);

	/** put a textual representation of the RangeSet to stream.
	 *  @param stream target stream to put RangeSet to.
	 */
	void put(std::ostream &stream) const;

	/** return the upper bound.
	 *  Must not be called, when empty is true.
	 *  @return highest number still part of the Range.
	 */
	universal_integer getUpperBound(void) const;

	/** return the lower bound.
	 *  Must not be called, when empty is true.
	 *  @return lowest number still part of the Range.
	 */
	universal_integer getLowerBound(void) const;

	/** is value part of the RangeSet? */
	bool contains(universal_integer value) const;

private:

	/** a single range. */
	class Range {
	public:
		/** c'tor.
		 *  The following must be true: l <= r
		 *  @param l left bound of the range (part of the range).
		 *  @param r right bound of the range (part of the range).
		 */
		Range(universal_integer l, universal_integer r);

		/** calculate the distance from this to other.
		 *  @param number of empty fields between this and other.
		 *         Negative numbers mean that other is left of
		 *         this, positive numbers mean that it is right
		 *         of this. 0 means that the ranges are adjacent or
		 *         overlap.
		 */
		universal_integer distance(const Range &other) const;

		/** is this a subset of other?
		 *  @param other check if this is a subset other.
		 *  @return true, if it is a subset, false if not. It will 
		 *          also return true, other is equal to this
		 *          (non-true subset).
		 */
		bool isSubset(const Range &other) const;

		/** is the value inside the Range?
		 *  @param value value to check if inside the Range.
		 *  @return true if value is inside the Range, false otherwise.
		 */
		bool contains(universal_integer value) const;

		/** is this equal to other?
		 *  @param other range to check for equality.
		 *  @return true, if both bounds match, false otherwise.
		 */
		bool operator ==(const Range &other) const;

		/** is this Range left of the other range?
		 *  @param other other range to check.
		 *  @return true, if this Range is left of the other
		 *          Range. In particular an overlapping Range is
		 *          not considered left of.
		 */
		bool operator <(const Range &other) const;

		/** is this Range right of the other range?
		 *  @param other other range to check.
		 *  @return true, if this Range is right of the other
		 *          Range. In particular an overlapping Range is
		 *          not considered right of.
		 */
		bool operator >(const Range &other) const;

		/** substract other from this range.
		 *  @param other range to substract from this range.
		 *  @return list containing 0, 1 or 2 ranges (depending
		 *          if is equal to this, has one common boundary or
		 *          is a true subset of this with no common boundary.
		 */
		std::list<Range> operator -(const Range &other) const;

		/** put a textual representation of the Range to stream.
		 *  @param stream target stream to put Range to.
		 */
		void put(std::ostream &stream) const;

		/** left bound of the range. */
		universal_integer left;
		/** right bound of the range. */
		universal_integer right;
	};

	//! list of different ranges forming this RangeSet.
	std::list<Range> ranges;
};

//! overloaded << operator to easily output a RangeSet
/** @param stream stream to put a rs to.
 *  @param rs put rs to stream.
 *  @return modified stream.
 */
std::ostream &
operator <<(std::ostream &stream, const RangeSet &rs);

}; /* namespace ast */

#endif /* __RANGE_SET_HPP_INCLUDED */
