blob: 86b97bc8b4d0a6df2c93328075cba9515b62af8c [file] [log] [blame]
// This class was sourced from the Apache Harmony project
// https://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/
using System;
using System.Collections;
using System.Collections.Generic;
namespace Lucene.Net.Support
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// A <see cref="PriorityQueue{T}"/> holds elements on a priority heap, which orders the elements
/// according to their natural order or according to the comparator specified at
/// construction time. If the queue uses natural ordering, only elements that are
/// comparable are permitted to be inserted into the queue.
/// <para/>
/// The least element of the specified ordering is stored at the head of the
/// queue and the greatest element is stored at the tail of the queue.
/// <para/>
/// A <see cref="PriorityQueue{T}"/> is not synchronized.
/// </summary>
/// <typeparam name="T">Type of elements</typeparam>
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class PriorityQueue<T> : ICollection<T> where T : class // LUCENENET: added constraint so we can return null like the original code
{
private static readonly int DEFAULT_CAPACITY = 11;
private static readonly double DEFAULT_INIT_CAPACITY_RATIO = 1.1;
private static readonly int DEFAULT_CAPACITY_RATIO = 2;
private int count;
private IComparer<T> comparer;
private T[] elements;
#region Constructors
/// <summary>
/// Constructs a priority queue with an initial capacity of 11 and natural
/// ordering.
/// </summary>
public PriorityQueue()
: this(DEFAULT_CAPACITY)
{
}
/// <summary>
/// Constructs a priority queue with the specified capacity and natural
/// ordering.
/// </summary>
/// <param name="initialCapacity">initial capacity</param>
/// <exception cref="ArgumentOutOfRangeException">if the <paramref name="initialCapacity"/> is less than 1.</exception>
public PriorityQueue(int initialCapacity)
: this(initialCapacity, null)
{
}
/// <summary>
/// Creates a <see cref="PriorityQueue{T}"/> with the specified initial capacity
/// that orders its elements according to the specified comparer.
/// </summary>
/// <param name="initialCapacity">the initial capacity for this priority queue</param>
/// <param name="comparer">
/// The <see cref="IComparer{T}"/> that will be used to order this
/// priority queue. If <c>null</c>, the <see cref="IComparable{T}"/>
/// natural ordering of the elements will be used.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">if the <paramref name="initialCapacity"/> is less than 1</exception>
public PriorityQueue(int initialCapacity, IComparer<T> comparer)
{
if (initialCapacity < 1)
{
throw new ArgumentOutOfRangeException("initialCapacity must be at least 1");
}
elements = new T[initialCapacity];
this.comparer = comparer;
}
/// <summary>
/// Constructs a priority queue with the specified capacity and comparer.
/// </summary>
/// <param name="comparer">
/// The <see cref="IComparer{T}"/> that will be used to order this
/// priority queue. If <c>null</c>, the <see cref="IComparable{T}"/>
/// natural ordering of the elements will be used.
/// </param>
public PriorityQueue(IComparer<T> comparer)
: this(DEFAULT_CAPACITY, comparer)
{
}
/// <summary>
/// Creates a <see cref="PriorityQueue{T}"/> containing the elements in the
/// specified collection. If the specified collection is an instance of
/// a <see cref="SortedSet{T}"/>, <see cref="TreeSet{T}"/>,
/// or is another <see cref="PriorityQueue{T}"/>, this
/// priority queue will be ordered according to the same ordering.
/// Otherwise, this priority queue will be ordered according to the
/// <see cref="IComparable{T}"/> natural ordering of its elements.
/// </summary>
/// <param name="collection">collection to be inserted into priority queue</param>
/// <exception cref="InvalidCastException">
/// if elements of the specified collection
/// cannot be compared to one another according to the priority
/// queue's ordering
/// </exception>
/// <see cref="ArgumentNullException">if the specified collection or any
/// of its elements are null</see>
public PriorityQueue(ICollection<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
if (collection is PriorityQueue<T>)
{
InitFrom((PriorityQueue<T>)collection);
}
else if (collection is SortedSet<T>)
{
InitFrom((SortedSet<T>)collection);
}
else if (collection is TreeSet<T>)
{
InitFrom((TreeSet<T>)collection);
}
else
{
InitFrom(collection);
}
}
/// <summary>
/// Creates a <see cref="PriorityQueue{T}"/> containing the elements in the
/// specified priority queue. This priority queue will be
/// ordered according to the same ordering as the given priority
/// queue.
/// </summary>
/// <param name="collection">the priority queue whose elements are to be placed
/// into this priority queue</param>
/// <exception cref="InvalidCastException">
/// if elements of <paramref name="collection"/>
/// cannot be compared to one another according to <paramref name="collection"/>'s
/// ordering
/// </exception>
/// <see cref="NullReferenceException">if the specified collection or any
/// of its elements are null</see>
public PriorityQueue(PriorityQueue<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
InitFrom(collection);
}
/// <summary>
/// Creates a <see cref="PriorityQueue{T}"/> containing the elements in the
/// specified <see cref="SortedSet{T}"/>. This priority queue will be
/// ordered according to the same ordering as the given <see cref="SortedSet{T}"/>.
/// <para/>
/// The constructed priority queue has the initial capacity of 110% of the
/// size of the sorted set. The priority queue will have the same comparator
/// as the sorted set.
/// </summary>
/// <param name="collection">the sorted set whose elements are to be placed
/// into this priority queue</param>
/// <exception cref="InvalidCastException">
/// if elements of <paramref name="collection"/>
/// cannot be compared to one another according to <paramref name="collection"/>'s
/// ordering
/// </exception>
/// <exception cref="ArgumentNullException">if the specified collection or any
/// of its elements are null</exception>
public PriorityQueue(SortedSet<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
InitFrom(collection);
}
/// <summary>
/// Creates a <see cref="PriorityQueue{T}"/> containing the elements in the
/// specified <see cref="TreeSet{T}"/>. This priority queue will be
/// ordered according to the same ordering as the given <see cref="TreeSet{T}"/>.
/// <para/>
/// The constructed priority queue has the initial capacity of 110% of the
/// size of the sorted set. The priority queue will have the same comparator
/// as the sorted set.
/// </summary>
/// <param name="collection">the sorted set whose elements are to be placed
/// into this priority queue</param>
/// <exception cref="InvalidCastException">
/// if elements of <paramref name="collection"/>
/// cannot be compared to one another according to <paramref name="collection"/>'s
/// ordering
/// </exception>
/// <exception cref="ArgumentNullException">if the specified collection or any
/// of its elements are null</exception>
public PriorityQueue(TreeSet<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
InitFrom(collection);
}
#endregion Constructors
#region Initialization
private void InitFrom(PriorityQueue<T> collection)
{
InitSize(collection);
comparer = collection.Comparer;
System.Array.Copy(collection.elements, 0, elements, 0, collection.Count);
count = collection.Count;
}
private void InitFrom(SortedSet<T> collection)
{
InitSize(collection);
comparer = collection.Comparer;
foreach (var value in collection)
{
elements[count++] = value;
}
}
private void InitFrom(TreeSet<T> collection)
{
InitSize(collection);
comparer = collection.Comparer;
foreach (var value in collection)
{
elements[count++] = value;
}
}
private void InitFrom(ICollection<T> collection)
{
InitSize(collection);
AddAll(collection);
}
#endregion Initialization
#region Priority queue operations
/// <summary>
/// Gets the enumerator of the priority queue, which will not return elements
/// in any specified ordering.
/// </summary>
/// <returns>The enumerator of the priority queue.</returns>
/// <remarks>
/// Returned enumerator does not iterate elements in sorted order.</remarks>
public virtual IEnumerator<T> GetEnumerator()
{
return new PriorityEnumerator(this);
}
/// <summary>
/// Gets the size of the priority queue. If the size of the queue is greater
/// than the <see cref="int.MaxValue"/>, then it returns <see cref="int.MaxValue"/>.
/// </summary>
public virtual int Count
{
get { return count; }
}
/// <summary>
/// Removes all of the elements from this priority queue.
/// </summary>
public virtual void Clear()
{
Arrays.Fill(elements, null);
count = 0;
}
/// <summary>
/// Inserts the specified element into this priority queue.
/// </summary>
/// <param name="item">the element to add to the priority queue.</param>
/// <returns>always <c>true</c></returns>
/// <exception cref="InvalidCastException">if the specified element cannot be
/// compared with elements currently in this priority queue
/// according to the priority queue's ordering</exception>
/// <exception cref="ArgumentNullException">if the specified element is null</exception>
public virtual bool Offer(T item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
GrowToSize(count + 1);
elements[count] = item;
SiftUp(count++);
return true;
}
/// <summary>
/// Gets and removes the head of the queue.
/// </summary>
/// <returns>the head of the queue or null if the queue is empty.</returns>
public virtual T Poll()
{
if (count == 0)
{
return null;
}
T result = elements[0];
RemoveAt(0);
return result;
}
/// <summary>
/// Gets but does not remove the head of the queue.
/// </summary>
/// <returns>the head of the queue or null if the queue is empty.</returns>
public virtual T Peek()
{
return (count == 0) ? null : elements[0];
}
/// <summary>
/// Gets the <see cref="IComparer{T}"/> used to order the elements in this
/// queue, or <see cref="Util.ArrayUtil.NaturalComparer{T}"/> if this queue is sorted according to
/// the <see cref="IComparable{T}"/> natural ordering of its elements.
/// </summary>
public virtual IComparer<T> Comparer { get { return this.comparer; } }
/// <summary>
/// Removes the specified object from the priority queue.
/// </summary>
/// <param name="item">the object to be removed.</param>
/// <returns><c>true</c> if the object was in the priority queue, <c>false</c> if the object</returns>
public virtual bool Remove(T item)
{
if (item == null || count == 0)
{
return false;
}
for (int i = 0; i < count; i++)
{
if (item.Equals(elements[i]))
{
RemoveAt(i);
return true;
}
}
return false;
}
/// <summary>
/// Adds the specified object to the priority queue.
/// </summary>
/// <param name="item">the object to be added.</param>
/// <exception cref="InvalidCastException">if the specified element cannot be
/// compared with elements currently in this priority queue
/// according to the priority queue's ordering</exception>
/// <exception cref="ArgumentNullException">if <paramref name="item"/> is <c>null</c>.</exception>
public virtual void Add(T item) // LUCENENET NOTE: No return value because we need to implement ICollection<T>
{
Offer(item);
}
private sealed class PriorityEnumerator : IEnumerator<T>
{
private int currentIndex = -1;
private T current;
private readonly PriorityQueue<T> outerInstance;
public PriorityEnumerator(PriorityQueue<T> outerInstance)
{
this.outerInstance = outerInstance;
}
public T Current
{
get { return current; }
}
object IEnumerator.Current
{
get { return current; }
}
public void Dispose()
{
current = null;
}
public bool MoveNext()
{
if (currentIndex < outerInstance.count - 1)
{
current = outerInstance.elements[++currentIndex];
return true;
}
return false;
}
// LUCENENET NOTE: Remove() not implemented
public void Reset()
{
throw new NotSupportedException();
}
}
// LUCENENET NOTE: readObject omitted
private T[] NewElementArray(int capacity)
{
return new T[capacity];
}
// LUCENENET NOTE: writeObject omitted
/// <summary>
/// Removes the element at the specified <paramref name="index"/> from queue.
/// <para/>
/// Normally this method leaves the elements at up to <paramref name="index"/>-1,
/// inclusive, untouched. Under these circumstances, it returns
/// <c>null</c>. Occasionally, in order to maintain the heap invariant,
/// it must swap a later element of the list with one earlier than
/// <paramref name="index"/>. Under these circumstances, this method returns the element
/// that was previously at the end of the list and is now at some
/// position before <paramref name="index"/>.
/// </summary>
private void RemoveAt(int index)
{
count--;
elements[index] = elements[count];
SiftDown(index);
elements[count] = null;
}
private int Compare(T o1, T o2)
{
if (null != comparer)
{
return comparer.Compare(o1, o2);
}
return ((IComparable<T>)o1).CompareTo(o2);
}
private void SiftUp(int childIndex)
{
T target = elements[childIndex];
int parentIndex;
while (childIndex > 0)
{
parentIndex = (childIndex - 1) / 2;
T parent = elements[parentIndex];
if (Compare(parent, target) <= 0)
{
break;
}
elements[childIndex] = parent;
childIndex = parentIndex;
}
elements[childIndex] = target;
}
private void SiftDown(int rootIndex)
{
T target = elements[rootIndex];
int childIndex;
while ((childIndex = rootIndex * 2 + 1) < count)
{
if (childIndex + 1 < count
&& Compare(elements[childIndex + 1], elements[childIndex]) < 0)
{
childIndex++;
}
if (Compare(target, elements[childIndex]) <= 0)
{
break;
}
elements[rootIndex] = elements[childIndex];
rootIndex = childIndex;
}
elements[rootIndex] = target;
}
private void InitSize(ICollection<T> c)
{
if (null == c)
{
throw new ArgumentNullException("c");
}
if (c.Count == 0)
{
elements = NewElementArray(1);
}
else
{
int capacity = (int)Math.Ceiling(c.Count
* DEFAULT_INIT_CAPACITY_RATIO);
elements = NewElementArray(capacity);
}
}
private void GrowToSize(int size)
{
if (size > elements.Length)
{
T[] newElements = NewElementArray(size * DEFAULT_CAPACITY_RATIO);
System.Array.Copy(elements, 0, newElements, 0, elements.Length);
elements = newElements;
}
}
#endregion Priority queue operations
#region AbstractQueue operations
public virtual bool AddAll(ICollection<T> collection)
{
if (null == collection)
{
throw new ArgumentNullException("collection");
}
if (this == collection)
{
throw new ArgumentException("collection must not be the same instance as this.", "collection");
}
bool result = false;
foreach (var value in collection)
{
if (value == null)
{
throw new ArgumentNullException("PriorityQueue values must not be null.");
}
if (Offer(value))
{
result = true;
}
}
return result;
}
/// <summary>
/// Retrieves and removes the head of this queue. This method differs
/// from <see cref="Poll()"/> only in that it throws an exception if this
/// queue is empty.
/// <para/>
/// This implementation returns the result of <see cref="Poll()"/>
/// unless the queue is empty.
/// </summary>
/// <returns>the head of this queue</returns>
/// <exception cref="KeyNotFoundException">if this queue is empty</exception>
public virtual T Remove()
{
T x = Poll();
if (x == null)
{
throw new KeyNotFoundException();
}
return x;
}
/// <summary>
/// Retrieves, but does not remove, the head of this queue. This method
/// differs from <see cref="Peek()"/> only in that it throws an exception if
/// this queue is empty.
/// <para/>
/// This implementation returns the result of <see cref="Peek()"/>
/// unless the queue is empty.
/// </summary>
/// <returns>the head of this queue</returns>
/// <exception cref="KeyNotFoundException">if this queue is empty</exception>
public virtual T Element()
{
T x = Peek();
if (x == null)
{
throw new KeyNotFoundException();
}
return x;
}
#endregion
#region ICollection<T> implementation (LUCENENET specific)
private int IndexOf(T o)
{
if (o != null)
{
for (int i = 0; i < count; i++)
if (o.Equals(elements[i]))
return i;
}
return -1;
}
/// <summary>
/// Returns <c>true</c> if this queue contains the specified element.
/// More formally, returns <c>true</c> if and only if this queue contains
/// at least one element <paramref name="item"/> such that <c>o.Equals(item)</c>.
/// </summary>
/// <param name="item">The object to locate in the priority queue</param>
/// <returns><c>true</c> if item is found in the priority queue; otherwise, <c>false.</c> </returns>
public virtual bool Contains(T item)
{
return IndexOf(item) != -1;
}
/// <summary>
/// Copies the elements of the priority queue to an Array, starting at a particular Array index.
/// </summary>
/// <param name="array">The one-dimensional Array that is the destination of the elements copied from the priority queue. The Array must have zero-based indexing. </param>
/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
/// <remarks>
/// It is not guaranteed that items will be copied in the sorted order.
/// </remarks>
public virtual void CopyTo(T[] array, int arrayIndex)
{
int size = this.count;
if (array.Length < size)
{
elements.CopyTo(array, arrayIndex);
}
else
{
System.Array.Copy(elements, 0, array, arrayIndex, size);
if (array.Length > size)
{
array[size] = null;
}
}
}
/// <summary>
/// Gets a value indicating whether the collection is read-only.
/// </summary>
/// <remarks>
/// For priority queue this property returns <c>false</c>.
/// </remarks>
public virtual bool IsReadOnly
{
get { return false; }
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>Enumerator</returns>
/// <remarks>
/// Returned enumerator does not iterate elements in sorted order.</remarks>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion ICollection<T> implementation (LUCENENET specific)
}
}