//
// assembly:	System
// namespace:	System.Text.RegularExpressions
// file:	interval.cs
//
// author:	Dan Lewis (dlewis@gmx.co.uk)
// 		(c) 2002

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;

namespace System.Text.RegularExpressions {

	struct Interval : IComparable {
		public int low;
		public int high;
		public bool contiguous;

		public static Interval Empty {
			get {
				Interval i;
				i.low = 0;
				i.high = i.low - 1;
				i.contiguous = true;

				return i;
			}
		}

		public static Interval Entire {
			get { return new Interval (Int32.MinValue, Int32.MaxValue); }
		}

		public Interval (int low, int high) {
			if (low > high) {
				int t = low;
				low = high;
				high = t;
			}
		
			this.low = low;
			this.high = high;
			this.contiguous = true;
		}

		public bool IsDiscontiguous {
			get { return !contiguous; }
		}
		
		public bool IsSingleton {
			get { return contiguous && low == high; }
		}

		public bool IsRange {
			get { return !IsSingleton && !IsEmpty; }
		}

		public bool IsEmpty {
			get { return low > high; }
		}

		public int Size {
			get {
				if (IsEmpty)
					return 0;
				
				return high - low + 1;
			}
		}

		public bool IsDisjoint (Interval i) {
			if (IsEmpty || i.IsEmpty)
				return true;
			
			return !(low <= i.high && i.low <= high);
		}

		public bool IsAdjacent (Interval i) {
			if (IsEmpty || i.IsEmpty)
				return false;
		
			return low == i.high + 1 || high == i.low - 1;
		}

		public bool Contains (Interval i) {
			if (!IsEmpty && i.IsEmpty)
				return true;
			if (IsEmpty)
				return false;
		
			return low <= i.low && i.high <= high;
		}

		public bool Contains (int i) {
			return low <= i && i <= high;
		}

		public bool Intersects (Interval i) {
 			if (IsEmpty || i.IsEmpty)
 				return false;
 			
 			return ((Contains (i.low) && !Contains (i.high)) ||
				(Contains (i.high) && !Contains (i.low)));
 		}	

		public void Merge (Interval i) {
			if (i.IsEmpty)
				return;
			if (IsEmpty) {
				this.low = i.low;
				this.high = i.high;
			}
		
			if (i.low < low)
				low = i.low;
			if (i.high > high)
				high = i.high;
		}

		public void Intersect (Interval i) {
			if (IsDisjoint (i)) {
				low = 0;
				high = low - 1;
				return;
			}
		
			if (i.low > low)
				low = i.low;
			if (i.high > high)
				high = i.high;
		}

		public int CompareTo (object o) {
			return low - ((Interval)o).low;
		}

		public new string ToString () {
			if (IsEmpty)
				return "(EMPTY)";
			else if (!contiguous)
				return "{" + low + ", " + high + "}";
			else if (IsSingleton)
				return "(" + low + ")";
			else
				return "(" + low + ", " + high + ")";
		}
	}

	class IntervalCollection : ICollection, IEnumerable {
		public IntervalCollection () {
			intervals = new ArrayList ();
		}

		public Interval this[int i] {
			get { return (Interval)intervals[i]; }
			set { intervals[i] = value; }
		}

		public void Add (Interval i) {
			intervals.Add (i);
		}
			
		public void Clear () {
			intervals.Clear ();
		}

		public void Sort () {
			intervals.Sort ();
		}
		
		public void Normalize () {
			intervals.Sort ();

			int j = 0;
			while (j < intervals.Count - 1) {
				Interval a = (Interval)intervals[j];
				Interval b = (Interval)intervals[j + 1];

				if (!a.IsDisjoint (b) || a.IsAdjacent (b)) {
					a.Merge (b);
					intervals[j] = a;
					intervals.RemoveAt (j + 1);
				}
				else
					++ j;
			}

		}

		public delegate double CostDelegate (Interval i);

		public IntervalCollection GetMetaCollection (CostDelegate cost_del) {
			IntervalCollection meta = new IntervalCollection ();
		
			Normalize ();
			Optimize (0, Count - 1, meta, cost_del);
			meta.intervals.Sort ();

			return meta;
		}

		private void Optimize (int begin, int end, IntervalCollection meta, CostDelegate cost_del) {
			Interval set;
			set.contiguous = false;
		
			int best_set_begin = -1;
			int best_set_end = -1;
			double best_set_cost = 0;

			for (int i = begin; i <= end; ++ i) {
				set.low = this[i].low;

				double cost = 0.0;
				for (int j = i; j <= end; ++ j) {
					set.high = this[j].high;
					cost += cost_del (this[j]);
					
					double set_cost = cost_del (set);
					if (set_cost < cost && cost > best_set_cost) {
						best_set_begin = i;
						best_set_end = j;
						best_set_cost = cost;
					}
				}
			}

			if (best_set_begin < 0) {
				// didn't find an optimal set: add original members

				for (int i = begin; i <= end; ++ i)
					meta.Add (this[i]);
			}
			else {
				// found set: add it ...

				set.low = this[best_set_begin].low;
				set.high = this[best_set_end].high;
				
				meta.Add (set);

				// ... and optimize to the left and right

				if (best_set_begin > begin)
					Optimize (begin, best_set_begin - 1, meta, cost_del);
				if (best_set_end < end)
					Optimize (best_set_end + 1, end, meta, cost_del);
			}
		}

		// ICollection implementation

		public int Count {
			get { return intervals.Count; }
		}

		public bool IsSynchronized {
			get { return false; }
		}

		public object SyncRoot {
			get { return intervals; }
		}

		public void CopyTo (Array array, int index) {
			foreach (Interval i in intervals) {
				if (index > array.Length)
					break;
				
				array.SetValue (i, index ++);
			}
		}

		// IEnumerator implementation

		public IEnumerator GetEnumerator () {
			return new Enumerator (intervals);
		}

		private class Enumerator : IEnumerator {
			public Enumerator (IList list) {
				this.list = list;
				Reset ();
			}

			public object Current {
				get {
					if (ptr >= list.Count)
						throw new InvalidOperationException ();

					return list[ptr];
				}
			}

			public bool MoveNext () {
				if (ptr > list.Count)
					throw new InvalidOperationException ();
				
				return ++ ptr < list.Count;
			}

			public void Reset () {
				ptr = -1;
			}

			private IList list;
			private int ptr;
		}

		// private fields

		private ArrayList intervals;
	}
}
