.RangeTester rangeTester, bool entireTree, bool reversed)
{
this.myBag = myBag;
this.rangeTester = rangeTester;
this.entireTree = entireTree;
this.reversed = reversed;
}
public sealed override int Count
{
get
{
if (entireTree)
return myBag.Count;
else {
// Note: we can't cache the result of this call because the underlying
// set can change, which would make the cached value incorrect.
return myBag.tree.CountRange(rangeTester);
}
}
}
public sealed override T this[int index]
{
get
{
if (entireTree) {
if (reversed)
return myBag[myBag.Count - 1 - index];
else
return myBag[index];
}
else {
T dummy;
int firstIndex = myBag.tree.FirstItemInRange(rangeTester, out dummy);
int lastIndex = myBag.tree.LastItemInRange(rangeTester, out dummy);
if (firstIndex < 0 || lastIndex < 0 || index < 0 || index >= (lastIndex - firstIndex + 1))
throw new ArgumentOutOfRangeException("index");
if (reversed)
return myBag[lastIndex - index];
else
return myBag[firstIndex + index];
}
}
}
public sealed override int IndexOf(T item)
{
if (entireTree) {
if (reversed)
return myBag.Count - 1 - myBag.LastIndexOf(item);
else
return myBag.IndexOf(item);
}
else {
T dummy;
if (rangeTester(item) != 0)
return -1;
if (reversed) {
int indexInSet = myBag.tree.FindIndex(item, false);
if (indexInSet < 0)
return -1;
int indexOfEnd = myBag.tree.LastItemInRange(rangeTester, out dummy);
return indexOfEnd - indexInSet;
}
else {
int indexInSet = myBag.tree.FindIndex(item, true);
if (indexInSet < 0)
return -1;
int indexOfStart = myBag.tree.FirstItemInRange(rangeTester, out dummy);
return indexInSet - indexOfStart;
}
}
}
}
#endregion Read-only list view
#region Sub-views
///
/// Returns a View collection that can be used for enumerating the items in the bag in
/// reversed order.
///
///
///Typically, this method is used in conjunction with a foreach statement. For example:
///
/// foreach(T item in bag.Reversed()) {
/// // process item
/// }
///
/// If an item is added to or deleted from the bag while the View is being enumerated, then
/// the enumeration will end with an InvalidOperationException.
///Calling Reverse does not copy the data in the tree, and the operation takes constant time.
///
/// An OrderedBag.View of items in reverse order.
public View Reversed() // A reversed view that can be enumerated
{
return new View(this, tree.EntireRangeTester, true, true);
}
///
/// Returns a View collection that can be used for enumerating a range of the items in the bag.
/// Only items that are greater than and
/// less than are included. The items are enumerated in sorted order.
/// Items equal to the end points of the range can be included or excluded depending on the
/// and parameters.
///
///
///If is greater than or equal to , the returned collection is empty.
///Typically, this method is used in conjunction with a foreach statement. For example:
///
/// foreach(T item in bag.Range(from, true, to, false)) {
/// // process item
/// }
///
/// If an item is added to or deleted from the bag while the View is being enumerated, then
/// the enumeration will end with an InvalidOperationException.
///Calling Range does not copy the data in the tree, and the operation takes constant time.
///
/// The lower bound of the range.
/// If true, the lower bound is inclusive--items equal to the lower bound will
/// be included in the range. If false, the lower bound is exclusive--items equal to the lower bound will not
/// be included in the range.
/// The upper bound of the range.
/// If true, the upper bound is inclusive--items equal to the upper bound will
/// be included in the range. If false, the upper bound is exclusive--items equal to the upper bound will not
/// be included in the range.
/// An OrderedBag.View of items in the given range.
public View Range(T from, bool fromInclusive, T to, bool toInclusive) // A partial view that can be enumerated
{
return new View(this, tree.DoubleBoundedRangeTester(from, fromInclusive, to, toInclusive), false, false);
}
///
/// Returns a View collection that can be used for enumerating a range of the items in the bag.
/// Only items that are greater than (and optionally, equal to) are included.
/// The items are enumerated in sorted order. Items equal to can be included
/// or excluded depending on the parameter.
///
///
///Typically, this method is used in conjunction with a foreach statement. For example:
///
/// foreach(T item in bag.RangeFrom(from, true)) {
/// // process item
/// }
///
/// If an item is added to or deleted from the bag while the View is being enumerated, then
/// the enumeration will end with an InvalidOperationException.
///Calling RangeFrom does not copy the data in the tree, and the operation takes constant time.
///
/// The lower bound of the range.
/// If true, the lower bound is inclusive--items equal to the lower bound will
/// be included in the range. If false, the lower bound is exclusive--items equal to the lower bound will not
/// be included in the range.
/// An OrderedBag.View of items in the given range.
public View RangeFrom(T from, bool fromInclusive) // A partial view that can be enumerated
{
return new View(this, tree.LowerBoundedRangeTester(from, fromInclusive), false, false);
}
///
/// Returns a View collection that can be used for enumerating a range of the items in the bag.
/// Only items that are less than (and optionally, equal to) are included.
/// The items are enumerated in sorted order. Items equal to can be included
/// or excluded depending on the parameter.
///
///
///Typically, this method is used in conjunction with a foreach statement. For example:
///
/// foreach(T item in bag.RangeTo(to, false)) {
/// // process item
/// }
///
/// If an item is added to or deleted from the bag while the View is being enumerated, then
/// the enumeration will end with an InvalidOperationException.
///Calling RangeTo does not copy the data in the tree, and the operation takes constant time.
///
/// The upper bound of the range.
/// If true, the upper bound is inclusive--items equal to the upper bound will
/// be included in the range. If false, the upper bound is exclusive--items equal to the upper bound will not
/// be included in the range.
/// An OrderedBag.View of items in the given range.
public View RangeTo(T to, bool toInclusive) // A partial view that can be enumerated
{
return new View(this, tree.UpperBoundedRangeTester(to, toInclusive), false, false);
}
#endregion
#region View nested class
///
/// The OrderedBag<T>.View class is used to look at a subset of the items
/// inside an ordered bag. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods.
///
///
/// Views are dynamic. If the underlying bag changes, the view changes in sync. If a change is made
/// to the view, the underlying bag changes accordingly.
///Typically, this class is used in conjunction with a foreach statement to enumerate the items
/// in a subset of the OrderedBag. For example:
///
/// foreach(T item in bag.Range(from, to)) {
/// // process item
/// }
///
///
[Serializable]
public class View : CollectionBase
{
private OrderedBag myBag;
private RedBlackTree.RangeTester rangeTester; // range tester for the range being used.
private bool entireTree; // is the view the whole tree?
private bool reversed; // is the view reversed?
///
/// Initialize the view.
///
/// OrderedBag being viewed
/// Range tester that defines the range being used.
/// If true, then rangeTester defines the entire tree.
/// Is the view enuemerated in reverse order?
internal View(OrderedBag myBag, RedBlackTree.RangeTester rangeTester, bool entireTree, bool reversed)
{
this.myBag = myBag;
this.rangeTester = rangeTester;
this.entireTree = entireTree;
this.reversed = reversed;
}
///
/// Determine if the given item lies within the bounds of this view.
///
/// Item to test.
/// True if the item is within the bounds of this view.
private bool ItemInView(T item)
{
return rangeTester(item) == 0;
}
///
/// Enumerate all the items in this view.
///
/// An IEnumerator<T> with the items in this view.
public sealed override IEnumerator GetEnumerator()
{
if (reversed)
return myBag.tree.EnumerateRangeReversed(rangeTester).GetEnumerator();
else
return myBag.tree.EnumerateRange(rangeTester).GetEnumerator();
}
///
/// Number of items in this view.
///
/// Number of items that lie within the bounds the view.
public sealed override int Count
{
get
{
if (entireTree)
return myBag.Count;
else {
// Note: we can't cache the result of this call because the underlying
// set can change, which would make the cached value incorrect.
return myBag.tree.CountRange(rangeTester);
}
}
}
///
/// Removes all the items within this view from the underlying bag.
///
/// The following removes all the items that start with "A" from an OrderedBag.
///
/// bag.Range("A", "B").Clear();
///
///
public sealed override void Clear()
{
if (entireTree) {
myBag.Clear();
}
else {
myBag.tree.DeleteRange(rangeTester);
}
}
///
/// Adds a new item to the bag underlying this View. If the bag already contains an item equal to
/// , that item is replaces with . If
/// is outside the range of this view, an InvalidOperationException
/// is thrown.
///
///
/// Equality between items is determined by the comparison instance or delegate used
/// to create the bag.
/// Adding an item takes time O(log N), where N is the number of items in the bag.
/// The item to add.
/// True if the bag already contained an item equal to (which was replaced), false
/// otherwise.
public sealed override void Add(T item)
{
if (!ItemInView(item))
throw new ArgumentException(Strings.OutOfViewRange, "item");
else
myBag.Add(item);
}
///
/// Searches the underlying bag for an item equal to , and if found,
/// removes it from the bag. If not found, the bag is unchanged. If the item is outside
/// the range of this view, the bag is unchanged.
///
///
/// Equality between items is determined by the comparison instance or delegate used
/// to create the bag.
/// Removing an item from the bag takes time O(log N), where N is the number of items in the bag.
/// The item to remove.
/// True if was found and removed. False if was not in the bag, or
/// was outside the range of this view.
public sealed override bool Remove(T item)
{
if (!ItemInView(item))
return false;
else
return myBag.Remove(item);
}
///
/// Determines if this view of the bag contains an item equal to . The bag
/// is not changed. If
///
/// Searching the bag for an item takes time O(log N), where N is the number of items in the bag.
/// The item to search for.
/// True if the bag contains , and is within
/// the range of this view. False otherwise.
public sealed override bool Contains(T item)
{
if (!ItemInView(item))
return false;
else
return myBag.Contains(item);
}
///
/// Get the first index of the given item in the view. The smallest item in the view has index 0,
/// the next smallest item has index 1, and the largest item has index Count-1.
///
/// Finding the index takes time O(log N), which N is the number of items in
/// the set.
/// The item to get the index of.
/// The index of the first item in the view equal to , or -1 if the item is not present
/// in the view.
public int IndexOf(T item)
{
if (entireTree) {
if (reversed) {
int indexInSet = myBag.tree.FindIndex(item, false);
if (indexInSet < 0)
return -1;
return myBag.Count - 1 - indexInSet;
}
else {
return myBag.tree.FindIndex(item, true);
}
}
else {
T dummy;
if (!ItemInView(item))
return -1;
if (reversed) {
int indexInSet = myBag.tree.FindIndex(item, false);
if (indexInSet < 0)
return -1;
int indexOfEnd = myBag.tree.LastItemInRange(rangeTester, out dummy);
return indexOfEnd - indexInSet;
}
else {
int indexInSet = myBag.tree.FindIndex(item, true);
if (indexInSet < 0)
return -1;
int indexOfStart = myBag.tree.FirstItemInRange(rangeTester, out dummy);
return indexInSet - indexOfStart;
}
}
}
///
/// Get the last index of the given item in the view. The smallest item in the view has index 0,
/// the next smallest item has index 1, and the largest item has index Count-1.
///
/// Finding the index takes time O(log N), which N is the number of items in
/// the set.
/// The item to get the index of.
/// The index of the last item in the view equal to , or -1 if the item is not present
/// in the view.
public int LastIndexOf(T item)
{
if (entireTree) {
if (reversed) {
int indexInSet = myBag.tree.FindIndex(item, true);
if (indexInSet < 0)
return -1;
return myBag.Count - 1 - indexInSet;
}
else {
return myBag.tree.FindIndex(item, false);
}
}
else {
T dummy;
if (!ItemInView(item))
return -1;
if (reversed) {
int indexInSet = myBag.tree.FindIndex(item, true);
if (indexInSet < 0)
return -1;
int indexOfEnd = myBag.tree.LastItemInRange(rangeTester, out dummy);
return indexOfEnd - indexInSet;
}
else {
int indexInSet = myBag.tree.FindIndex(item, false);
if (indexInSet < 0)
return -1;
int indexOfStart = myBag.tree.FirstItemInRange(rangeTester, out dummy);
return indexInSet - indexOfStart;
}
}
}
///
/// Get the item by its index in the sorted order. The smallest item in the view has index 0,
/// the next smallest item has index 1, and the largest item has index Count-1.
///
/// The indexer takes time O(log N), which N is the number of items in
/// the set.
/// The index to get the item by.
/// The item at the given index.
/// is
/// less than zero or greater than or equal to Count.
public T this[int index]
{
get
{
if (entireTree) {
if (reversed) {
return myBag[myBag.Count - 1 - index];
}
else {
return myBag[index];
}
}
else {
T dummy;
int firstIndex = myBag.tree.FirstItemInRange(rangeTester, out dummy);
int lastIndex = myBag.tree.LastItemInRange(rangeTester, out dummy);
if (firstIndex < 0 || lastIndex < 0 || index < 0 || index >= (lastIndex - firstIndex + 1))
throw new ArgumentOutOfRangeException("index");
if (reversed)
return myBag[lastIndex - index];
else
return myBag[firstIndex + index];
}
}
}
///
/// Get a read-only list view of the items in this view. The
/// items in the list are in sorted order, with the smallest item
/// at index 0. This view does not copy any data, and reflects any
/// changes to the underlying OrderedSet.
///
/// A read-only IList<T> view onto this view.
public IList AsList()
{
return new ListView(myBag, rangeTester, entireTree, reversed);
}
///
/// Creates a new View that has the same items as this view, in the reversed order.
///
/// A new View that has the reversed order of this view, with the same upper
/// and lower bounds.
public View Reversed()
{
return new View(myBag, rangeTester, entireTree, !reversed);
}
///
/// Returns the first item in this view: the item
/// that would appear first if the view was enumerated.
///
/// GetFirst() takes time O(log N), where N is the number of items in the bag.
/// The first item in the view.
/// The view has no items in it.
public T GetFirst()
{
T item;
int found;
if (reversed)
found = myBag.tree.LastItemInRange(rangeTester, out item);
else
found = myBag.tree.FirstItemInRange(rangeTester, out item);
if (found < 0)
throw new InvalidOperationException(Strings.CollectionIsEmpty);
return item;
}
///
/// Returns the last item in the view: the item
/// that would appear last if the view was enumerated.
///
/// GetLast() takes time O(log N), where N is the number of items in the bag.
/// The last item in the view.
/// The view has no items in it.
public T GetLast()
{
T item;
int found;
if (reversed)
found = myBag.tree.FirstItemInRange(rangeTester, out item);
else
found = myBag.tree.LastItemInRange(rangeTester, out item);
if (found < 0)
throw new InvalidOperationException(Strings.CollectionIsEmpty);
return item;
}
}
#endregion
}
}