using System; using System.Collections; namespace Holo.Virtual.Rooms.Pathfinding { /// /// The Heap allows to maintain a list sorted as long as needed. /// If no IComparer interface has been provided at construction, then the list expects the Objects to implement IComparer. /// If the list is not sorted it behaves like an ordinary list. /// When sorted, the list's "Add" method will put new objects at the right place. /// As well the "Contains" and "IndexOf" methods will perform a binary search. /// public class Heap : IList, ICloneable { #region Private Members private ArrayList FList; private IComparer FComparer = null; private bool FUseObjectsComparison; #endregion #region Constructors /// /// Default constructor. /// Since no IComparer is provided here, added objects must implement the IComparer interface. /// public Heap() { InitProperties(null, 0); } /// /// Constructor. /// Since no IComparer is provided, added objects must implement the IComparer interface. /// /// Capacity of the list (ArrayList.Capacity) public Heap(int Capacity) { InitProperties(null, Capacity); } /// /// Constructor. /// /// Will be used to compare added elements for sort and search operations. public Heap(IComparer Comparer) { InitProperties(Comparer, 0); } /// /// Constructor. /// /// Will be used to compare added elements for sort and search operations. /// Capacity of the list (ArrayList.Capacity) public Heap(IComparer Comparer, int Capacity) { InitProperties(Comparer, Capacity); } #endregion #region Properties /// /// If set to true, it will not be possible to add an object to the list if its value is already in the list. /// public bool AddDuplicates { set { FAddDuplicates = value; } get { return FAddDuplicates; } } private bool FAddDuplicates; /// /// Idem ArrayList /// public int Capacity { get { return FList.Capacity; } set { FList.Capacity = value; } } #endregion #region IList Members /// /// IList implementation. /// Gets object's value at a specified index. /// The set operation is impossible on a Heap. /// /// Index is less than zero or Index is greater than Count. /// [] operator cannot be used to set a value on a Heap. public object this[int Index] { get { if (Index >= FList.Count || Index < 0) throw new ArgumentOutOfRangeException("Index is less than zero or Index is greater than Count."); return FList[Index]; } set { throw new InvalidOperationException("[] operator cannot be used to set a value in a Heap."); } } /// /// IList implementation. /// Adds the object at the right place. /// /// The object to add. /// The index where the object has been added. /// The Heap is set to use object's IComparable interface, and the specifed object does not implement this interface. public int Add(object O) { int Return = -1; if (ObjectIsCompliant(O)) { int Index = IndexOf(O); int NewIndex = Index >= 0 ? Index : -Index - 1; if (NewIndex >= Count) FList.Add(O); else FList.Insert(NewIndex, O); Return = NewIndex; } return Return; } /// /// IList implementation. /// Search for a specified object in the list. /// If the list is sorted, a BinarySearch is performed using IComparer interface. /// Else the Object.Equals implementation is used. /// /// The object to look for /// true if the object is in the list, otherwise false. public bool Contains(object O) { return FList.BinarySearch(O, FComparer) >= 0; } /// /// IList implementation. /// Returns the index of the specified object in the list. /// If the list is sorted, a BinarySearch is performed using IComparer interface. /// Else the Object.Equals implementation of objects is used. /// /// The object to locate. /// /// If the object has been found, a positive integer corresponding to its position. /// If the objects has not been found, a negative integer which is the bitwise complement of the index of the next element. /// public int IndexOf(object O) { int Result = -1; Result = FList.BinarySearch(O, FComparer); while (Result > 0 && FList[Result - 1].Equals(O)) Result--; return Result; } /// /// IList implementation. /// Idem ArrayList /// public bool IsFixedSize { get { return FList.IsFixedSize; } } /// /// IList implementation. /// Idem ArrayList /// public bool IsReadOnly { get { return FList.IsReadOnly; } } /// /// IList implementation. /// Idem ArrayList /// public void Clear() { FList.Clear(); } /// /// IList implementation. /// Cannot be used on a Heap. /// /// The index before which the object must be added. /// The object to add. /// Insert method cannot be called on a Heap. public void Insert(int Index, object O) { throw new InvalidOperationException("Insert method cannot be called on a Heap."); } /// /// IList implementation. /// Idem ArrayList /// /// The object whose value must be removed if found in the list. public void Remove(object Value) { FList.Remove(Value); } /// /// IList implementation. /// Idem ArrayList /// /// Index of object to remove. public void RemoveAt(int Index) { FList.RemoveAt(Index); } #endregion #region IList.ICollection Members /// /// IList.ICollection implementation. /// Idem ArrayList /// /// /// public void CopyTo(Array array, int arrayIndex) { FList.CopyTo(array, arrayIndex); } /// /// IList.ICollection implementation. /// Idem ArrayList /// public int Count { get { return FList.Count; } } /// /// IList.ICollection implementation. /// Idem ArrayList /// public bool IsSynchronized { get { return FList.IsSynchronized; } } /// /// IList.ICollection implementation. /// Idem ArrayList /// public object SyncRoot { get { return FList.SyncRoot; } } #endregion #region IList.IEnumerable Members /// /// IList.IEnumerable implementation. /// Idem ArrayList /// /// Enumerator on the list. public IEnumerator GetEnumerator() { return FList.GetEnumerator(); } #endregion #region IClonable Members /// /// ICloneable implementation. /// Idem ArrayList /// /// Cloned object. public object Clone() { Heap Clone = new Heap(FComparer, FList.Capacity); Clone.FList = (ArrayList)FList.Clone(); Clone.FAddDuplicates = FAddDuplicates; return Clone; } #endregion #region Delegate Members /// /// Defines an equality for two objects /// public delegate bool Equality(object Object1, object Object2); #endregion #region Overridden Members /// /// Object.ToString() override. /// Build a string to represent the list. /// /// The string refecting the list. public override string ToString() { string OutString = "{"; for (int i = 0; i < FList.Count; i++) OutString += FList[i].ToString() + (i != FList.Count - 1 ? "; " : "}"); return OutString; } /// /// Object.Equals() override. /// /// true if object is equal to this, otherwise false. public override bool Equals(object Object) { Heap SL = (Heap)Object; if (SL.Count != Count) return false; for (int i = 0; i < Count; i++) if (!SL[i].Equals(this[i])) return false; return true; } /// /// Object.GetHashCode() override. /// /// Hash code for this. public override int GetHashCode() { return FList.GetHashCode(); } #endregion #region Public Members /// /// Idem IndexOf(object), but starting at a specified position in the list /// /// The object to locate. /// The index for start position. /// public int IndexOf(object Object, int Start) { int Result = -1; Result = FList.BinarySearch(Start, FList.Count - Start, Object, FComparer); while (Result > Start && FList[Result - 1].Equals(Object)) Result--; return Result; } /// /// Idem IndexOf(object), but with a specified equality function /// /// The object to locate. /// Equality function to use for the search. /// public int IndexOf(object Object, Equality AreEqual) { for (int i = 0; i < FList.Count; i++) if (AreEqual(FList[i], Object)) return i; return -1; } /// /// Idem IndexOf(object), but with a start index and a specified equality function /// /// The object to locate. /// The index for start position. /// Equality function to use for the search. /// public int IndexOf(object Object, int Start, Equality AreEqual) { if (Start < 0 || Start >= FList.Count) throw new ArgumentException("Start index must belong to [0; Count-1]."); for (int i = Start; i < FList.Count; i++) if (AreEqual(FList[i], Object)) return i; return -1; } /// /// The objects will be added at the right place. /// /// The object to add. /// The index where the object has been added. /// The Heap is set to use object's IComparable interface, and the specifed object does not implement this interface. public void AddRange(ICollection C) { foreach (object Object in C) Add(Object); } /// /// Cannot be called on a Heap. /// /// The index before which the objects must be added. /// The object to add. /// Insert cannot be called on a Heap. public void InsertRange(int Index, ICollection C) { throw new InvalidOperationException("Insert cannot be called on a Heap."); } /// /// Limits the number of occurrences of a specified value. /// Same values are equals according to the Equals() method of objects in the list. /// The first occurrences encountered are kept. /// /// Value whose occurrences number must be limited. /// Number of occurrences to keep public void LimitOccurrences(object Value, int NumberToKeep) { if (Value == null) throw new ArgumentNullException("Value"); int Pos = 0; while ((Pos = IndexOf(Value, Pos)) >= 0) { if (NumberToKeep <= 0) FList.RemoveAt(Pos); else { Pos++; NumberToKeep--; } if (FComparer.Compare(FList[Pos], Value) > 0) break; } } /// /// Removes all duplicates in the list. /// Each value encountered will have only one representant. /// public void RemoveDuplicates() { int PosIt; PosIt = 0; while (PosIt < Count - 1) { if (FComparer.Compare(this[PosIt], this[PosIt + 1]) == 0) RemoveAt(PosIt); else PosIt++; } } /// /// Returns the object of the list whose value is minimum /// /// The minimum object in the list public int IndexOfMin() { int RetInt = -1; if (FList.Count > 0) { RetInt = 0; object RetObj = FList[0]; } return RetInt; } /// /// Returns the object of the list whose value is maximum /// /// The maximum object in the list public int IndexOfMax() { int RetInt = -1; if (FList.Count > 0) { RetInt = FList.Count - 1; object RetObj = FList[FList.Count - 1]; } return RetInt; } /// /// Returns the topmost object on the list and removes it from the list /// /// Returns the topmost object on the list public object Pop() { if (FList.Count == 0) throw new InvalidOperationException("The heap is empty."); object Object = FList[Count - 1]; FList.RemoveAt(Count - 1); return (Object); } /// /// Pushes an object on list. It will be inserted at the right spot. /// /// Object to add to the list /// public int Push(object Object) { return (Add(Object)); } #endregion #region Private Members private bool ObjectIsCompliant(object Object) { if (FUseObjectsComparison && !(Object is IComparable)) throw new ArgumentException("The Heap is set to use the IComparable interface of objects, and the object to add does not implement the IComparable interface."); if (!FAddDuplicates && Contains(Object)) return false; return true; } private class Comparison : IComparer { public int Compare(object Object1, object Object2) { IComparable C = Object1 as IComparable; return C.CompareTo(Object2); } } private void InitProperties(IComparer Comparer, int Capacity) { if (Comparer != null) { FComparer = Comparer; FUseObjectsComparison = false; } else { FComparer = new Comparison(); FUseObjectsComparison = true; } FList = Capacity > 0 ? new ArrayList(Capacity) : new ArrayList(); FAddDuplicates = true; } #endregion } }