// // ImmutableQueue.cs // // Author: // Mike Krüger // // Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) // // 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; using System.Collections.Generic; namespace Quobject.Collections.Immutable { public class ImmutableQueue : IImmutableQueue { readonly ImmutableStack frontStack; readonly ImmutableStack backStack; internal ImmutableQueue() { frontStack = backStack = ImmutableStack.Empty; } ImmutableQueue(ImmutableStack frontStack, ImmutableStack backStack) { if (frontStack == null) throw new ArgumentNullException("frontStack"); if (backStack == null) throw new ArgumentNullException("backStack"); this.frontStack = frontStack; this.backStack = backStack; } #region IImmutableQueue implementation internal static readonly ImmutableQueue Empty = new ImmutableQueue(ImmutableStack.Empty, ImmutableStack.Empty); public bool IsEmpty { get { return frontStack.IsEmpty && backStack.IsEmpty; } } public ImmutableQueue Clear() { return Empty; } IImmutableQueue IImmutableQueue.Clear() { return Empty; } public ImmutableQueue Dequeue() { if (IsEmpty) throw new InvalidOperationException("Queue is empty."); var stack = frontStack.Pop(); if (!stack.IsEmpty) return new ImmutableQueue(stack, backStack); return new ImmutableQueue(Reverse(backStack), ImmutableStack.Empty); } public ImmutableQueue Dequeue(out T value) { value = Peek(); return Dequeue(); } IImmutableQueue IImmutableQueue.Dequeue() { return Dequeue(); } static ImmutableStack Reverse(IImmutableStack stack) { var result = ImmutableStack.Empty; var cur = stack; while (!cur.IsEmpty) { result = result.Push(cur.Peek()); cur = cur.Pop(); } return result; } public ImmutableQueue Enqueue(T value) { if (IsEmpty) return new ImmutableQueue(ImmutableStack.Empty.Push(value), ImmutableStack.Empty); return new ImmutableQueue(frontStack, backStack.Push(value)); } IImmutableQueue IImmutableQueue.Enqueue(T value) { return Enqueue(value); } public T Peek() { if (IsEmpty) throw new InvalidOperationException("Queue is empty."); return frontStack.Peek(); } #endregion #region IEnumerable implementation public IEnumerator GetEnumerator() { return new Enumerator(this); } struct Enumerator : IEnumerator { readonly ImmutableQueue start; IImmutableStack frontStack; IImmutableStack backStack; public Enumerator(ImmutableQueue stack) { this.start = stack; this.frontStack = null; this.backStack = null; } #region IEnumerator implementation bool IEnumerator.MoveNext() { if (frontStack == null) { frontStack = start.frontStack; backStack = Reverse (start.backStack); } else if (!frontStack.IsEmpty) { frontStack = frontStack.Pop(); } else if (!backStack.IsEmpty) { backStack = backStack.Pop(); } return !(frontStack.IsEmpty && backStack.IsEmpty); } void IEnumerator.Reset() { frontStack = null; backStack = null; } object IEnumerator.Current { get { return Current; } } #endregion #region IDisposable implementation void IDisposable.Dispose() { } #endregion #region IEnumerator implementation public T Current { get { if (frontStack == null) return default(T); if (frontStack.IsEmpty && backStack.IsEmpty) throw new InvalidOperationException(); return !frontStack.IsEmpty ? frontStack.Peek() : backStack.Peek(); } } #endregion } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } public static class ImmutableQueue { public static ImmutableQueue Create() { return ImmutableQueue.Empty; } public static ImmutableQueue Create(T item) { return Create().Enqueue(item); } public static ImmutableQueue Create(IEnumerable items) { var result = ImmutableQueue.Empty; foreach (var item in items) result = result.Enqueue(item); return result; } public static ImmutableQueue Create(params T[] items) { return Create((IEnumerable)items); } public static IImmutableQueue Dequeue(this IImmutableQueue queue, out T value) { if (queue == null) throw new ArgumentNullException("queue"); value = queue.Peek(); return queue.Dequeue(); } } }