Deque.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Runtime.CompilerServices;
  6. namespace MonoGame.Extended.Collections
  7. {
  8. internal static class Deque
  9. {
  10. internal static readonly Func<int, int> DefaultResizeFunction = x => x * 2;
  11. }
  12. /// <summary>
  13. /// Represents a collection of objects which elements can added to or removed either from the front or back; a
  14. /// <a href="https://en.wikipedia.org/wiki/Double-ended_queue">double ended queue</a> (deque).
  15. /// </summary>
  16. /// <remarks>
  17. /// <a href="https://en.wikipedia.org/wiki/Circular_buffer">circular array</a> is used as the internal data
  18. /// structure for the <see cref="Deque{T}" />.
  19. /// </remarks>
  20. /// <typeparam name="T">The type of the elements in the deque.</typeparam>
  21. public class Deque<T> : IList<T>
  22. {
  23. private const int _defaultCapacity = 4;
  24. private static readonly T[] _emptyArray = new T[0];
  25. private int _frontArrayIndex;
  26. private T[] _items;
  27. private Func<int, int> _resizeFunction = Deque.DefaultResizeFunction;
  28. /// <summary>
  29. /// Initializes a new instance of the <see cref="Deque{T}" /> class that is empty and has the default initial capacity.
  30. /// </summary>
  31. /// <remarks>
  32. /// <para>
  33. /// The capacity of a <see cref="Deque{T}" /> is the number of elements that the <see cref="Deque{T}" /> can
  34. /// hold. As elements are added to a <see cref="Deque{T}" />, <see cref="Capacity" /> is automatically increased by
  35. /// <see cref="ResizeFunction" /> as required by reallocating the internal array.
  36. /// </para>
  37. /// <para>
  38. /// If the size of the collection can be estimated, using the <see cref="Deque{T}(int)" /> constructor and
  39. /// specifying the initial capacity eliminates the need to perform a number of resizing operations while adding
  40. /// elements to the <see cref="Deque{T}" />.
  41. /// </para>
  42. /// <para>
  43. /// The capacity can be decreased by calling the <see cref="TrimExcess" /> method or by setting the
  44. /// <see cref="Capacity" /> property explicitly. Decreasing, or increasing, the capacity reallocates memory and
  45. /// copies all the
  46. /// elements in the <see cref="Deque{T}" />.
  47. /// </para>
  48. /// <para>This constructor is an O(1) operation.</para>
  49. /// </remarks>
  50. public Deque()
  51. {
  52. _items = _emptyArray;
  53. }
  54. /// <summary>
  55. /// Initializes a new instance of the <see cref="Deque{T}" /> class that contains elements copied from the specified
  56. /// collection and has sufficient capacity to accommodate the number of elements copied.
  57. /// </summary>
  58. /// <param name="collection">The collection whose elements are copied to the new deque.</param>
  59. /// <exception cref="ArgumentNullException"><paramref name="collection" /> is null.</exception>
  60. /// <remarks>
  61. /// <para>
  62. /// The elements are copied onto the <see cref="Deque{T}" /> in the same order they are read by the enumerator of
  63. /// <paramref name="collection" />.
  64. /// </para>
  65. /// <para>This constructor is an O(n) operation, where n is the number of elements in <paramref name="collection" />.</para>
  66. /// </remarks>
  67. public Deque(IEnumerable<T> collection)
  68. {
  69. if (collection == null)
  70. throw new ArgumentNullException(nameof(collection));
  71. var array = collection as T[] ?? collection.ToArray();
  72. var count = array.Length;
  73. if (count == 0)
  74. _items = _emptyArray;
  75. else
  76. {
  77. _items = new T[count];
  78. array.CopyTo(_items, 0);
  79. Count = count;
  80. }
  81. }
  82. /// <summary>
  83. /// Initializes a new instance of the <see cref="Deque{T}" /> class that is empty and has the specified initial
  84. /// capacity.
  85. /// </summary>
  86. /// <param name="capacity">The number of elements that the new <see cref="Deque{T}" /> can initially store.</param>
  87. /// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity" /> is less than 0.</exception>
  88. /// <remarks>
  89. /// <para>
  90. /// The capacity of a <see cref="Deque{T}" /> is the number of elements that the <see cref="Deque{T}" /> can
  91. /// hold. As elements are added to a <see cref="Deque{T}" />, <see cref="Capacity" /> is automatically increased by
  92. /// <see cref="ResizeFunction" /> as required by reallocating the internal array.
  93. /// </para>
  94. /// <para>
  95. /// If the size of the collection can be estimated, specifying the initial capacity eliminates the need to
  96. /// perform a number of resizing operations while adding elements to the <see cref="Deque{T}" />.
  97. /// </para>
  98. /// <para>
  99. /// The capacity can be decreased by calling the <see cref="TrimExcess" /> method or by setting the
  100. /// <see cref="Capacity" /> property explicitly. Decreasing, or increasing, the capacity reallocates memory and
  101. /// copies all the elements in the <see cref="Deque{T}" />.
  102. /// </para>
  103. /// <para>This constructor is an O(n) operation, where n is <paramref name="capacity" />.</para>
  104. /// </remarks>
  105. public Deque(int capacity)
  106. {
  107. if (capacity < 0)
  108. throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity was less than zero.");
  109. _items = capacity == 0 ? _emptyArray : new T[capacity];
  110. }
  111. /// <summary>
  112. /// Gets or sets the resize function used to calculate and set <see cref="Capacity" /> when a greater capacity is
  113. /// required.
  114. /// </summary>
  115. /// <returns>
  116. /// The <see cref="Func{T, TResult}" /> used to calculate and set <see cref="Capacity" /> when a greater capacity
  117. /// is required.
  118. /// </returns>
  119. /// <remarks>
  120. /// The default resize function is twice the <see cref="Capacity" />. Setting
  121. /// <see cref="ResizeFunction" /> to <c>null</c> will set it back to the default.
  122. /// </remarks>
  123. public Func<int, int> ResizeFunction
  124. {
  125. get => _resizeFunction;
  126. set => _resizeFunction = value ?? Deque.DefaultResizeFunction;
  127. }
  128. /// <summary>
  129. /// Gets or sets the total number of elements the internal data structure can hold without resizing.
  130. /// </summary>
  131. /// <returns>The number of elements that the <see cref="Deque{T}" /> can contain before resizing is required.</returns>
  132. /// <exception cref="ArgumentOutOfRangeException">
  133. /// <see cref="Capacity" /> cannot be set to a value less than <see cref="Count" />.
  134. /// </exception>
  135. /// <remarks>
  136. /// Changing <see cref="Capacity" /> reallocates memory and copies all the
  137. /// elements in the <see cref="Deque{T}" />.
  138. /// </remarks>
  139. public int Capacity
  140. {
  141. get => _items.Length;
  142. set
  143. {
  144. if (value < Count)
  145. throw new ArgumentOutOfRangeException(nameof(value), "capacity was less than the current size.");
  146. if (value == Capacity)
  147. return;
  148. if (value == 0)
  149. {
  150. _items = _emptyArray;
  151. return;
  152. }
  153. var newItems = new T[value];
  154. CopyTo(newItems);
  155. _frontArrayIndex = 0;
  156. _items = null;
  157. _items = newItems;
  158. }
  159. }
  160. /// <summary>
  161. /// Gets or sets the element at the specified index.
  162. /// </summary>
  163. /// <param name="index">The zero-based index of the element to get or set.</param>
  164. /// <returns>The element at the specified index.</returns>
  165. /// <exception cref="ArgumentOutOfRangeException">
  166. /// Index was out of range. Must be non-negative and less than <see cref="Count" />.
  167. /// </exception>
  168. /// <remarks>
  169. /// <para></para>
  170. /// <para>
  171. /// Use <c>0</c> for the <paramref name="index" /> to get or set the element at the beginning of the
  172. /// <see cref="Deque{T}" />, and use <c><see cref="Count" /> - 1</c> for the <paramref name="index" /> to get the
  173. /// element at the end of the <see cref="Deque{T}" />.
  174. /// </para>
  175. /// </remarks>
  176. public T this[int index]
  177. {
  178. get
  179. {
  180. var arrayIndex = GetArrayIndex(index);
  181. if (arrayIndex == -1)
  182. {
  183. throw new ArgumentOutOfRangeException(nameof(index),
  184. "Index was out of range. Must be non-negative and less than the size of the collection.");
  185. }
  186. return _items[arrayIndex];
  187. }
  188. set
  189. {
  190. var arrayIndex = GetArrayIndex(index);
  191. if (arrayIndex == -1)
  192. {
  193. throw new ArgumentOutOfRangeException(nameof(index),
  194. "Index was out of range. Must be non-negative and less than the size of the collection.");
  195. }
  196. _items[arrayIndex] = value;
  197. }
  198. }
  199. /// <summary>
  200. /// Gets the number of elements contained in the <see cref="Deque{T}" />.
  201. /// </summary>
  202. /// <returns>The number of elements contained in the <see cref="Deque{T}" />.</returns>
  203. public int Count { get; private set; }
  204. bool ICollection<T>.IsReadOnly => false;
  205. /// <summary>
  206. /// Returns an enumerator that iterates through the <see cref="Deque{T}" />.
  207. /// </summary>
  208. /// <returns>An <see cref="IEnumerator{T}" /> that can be used to iterate through the <see cref="Deque{T}" />.</returns>
  209. public IEnumerator<T> GetEnumerator()
  210. {
  211. if (Count == 0)
  212. yield break;
  213. if (Count <= _items.Length - _frontArrayIndex)
  214. {
  215. for (var i = _frontArrayIndex; i < _frontArrayIndex + Count; i++)
  216. yield return _items[i];
  217. }
  218. else
  219. {
  220. for (var i = _frontArrayIndex; i < Capacity; i++)
  221. yield return _items[i];
  222. for (var i = 0; i < (_frontArrayIndex + Count) % Capacity; i++)
  223. yield return _items[i];
  224. }
  225. }
  226. IEnumerator IEnumerable.GetEnumerator()
  227. {
  228. return GetEnumerator();
  229. }
  230. void ICollection<T>.Add(T item)
  231. {
  232. AddToBack(item);
  233. }
  234. /// <summary>
  235. /// Searches for the specified element and returns the zero-based index of the first occurrence within the entire
  236. /// <see cref="Deque{T}" />.
  237. /// </summary>
  238. /// <param name="item">
  239. /// The element to locate in the <see cref="Deque{T}" />. The value can be <c>null</c> for reference
  240. /// types.
  241. /// </param>
  242. /// <returns>
  243. /// The zero-based index of the first occurrence of <paramref name="item" /> within the entire
  244. /// <see cref="Deque{T}" />, if found; otherwise, <c>-1</c>.
  245. /// </returns>
  246. /// <remarks>
  247. /// <para>
  248. /// This method is an O(1) operation if <paramref name="item" /> is at the front or back of the
  249. /// <see cref="Deque{T}" />; otherwise, this method is an O(n) operation where n is <see cref="Count" />.
  250. /// </para>
  251. /// </remarks>
  252. public int IndexOf(T item)
  253. {
  254. var comparer = EqualityComparer<T>.Default;
  255. if (Get(0, out var checkFrontBackItem) && comparer.Equals(checkFrontBackItem, item))
  256. return 0;
  257. var backIndex = Count - 1;
  258. if (Get(backIndex, out checkFrontBackItem) && comparer.Equals(checkFrontBackItem, item))
  259. return backIndex;
  260. int index;
  261. if (Count <= _items.Length - _frontArrayIndex)
  262. index = Array.IndexOf(_items, item, _frontArrayIndex, Count);
  263. else
  264. {
  265. index = Array.IndexOf(_items, item, _frontArrayIndex, _items.Length - _frontArrayIndex);
  266. if (index < 0)
  267. index = Array.IndexOf(_items, item, 0, _frontArrayIndex + Count - _items.Length);
  268. }
  269. var circularIndex = (index - _frontArrayIndex + _items.Length) % _items.Length;
  270. return circularIndex;
  271. }
  272. void IList<T>.Insert(int index, T item)
  273. {
  274. throw new NotImplementedException();
  275. }
  276. /// <summary>
  277. /// Removes the first occurrence of a specific element from the <see cref="Deque{T}" />.
  278. /// </summary>
  279. /// <param name="item">
  280. /// The element to remove from the <see cref="Deque{T}" />. The value can be <c>null</c> for reference
  281. /// types.
  282. /// </param>
  283. /// <returns>
  284. /// <c>true</c> if <paramref name="item" /> was successfully removed; otherwise, false. This method also returns false
  285. /// if <paramref name="item" /> is not found in the <see cref="Deque{T}" />.
  286. /// </returns>
  287. /// <remarks>
  288. /// <para>
  289. /// This method is an O(1) operation if <paramref name="item" /> is at the front or back of the
  290. /// <see cref="Deque{T}" />; otherwise, this method is an O(n) operation where n is <see cref="Count" />.
  291. /// </para>
  292. /// </remarks>
  293. public bool Remove(T item)
  294. {
  295. var index = IndexOf(item);
  296. if (index == -1)
  297. return false;
  298. RemoveAt(index);
  299. return true;
  300. }
  301. /// <summary>
  302. /// Removes the element at the specified index of the <see cref="Deque{T}" />.
  303. /// </summary>
  304. /// <param name="index">The zero-based index of the element to remove.</param>
  305. /// <exception cref="ArgumentOutOfRangeException">
  306. /// <para><paramref name="index" /> is less than 0.</para>
  307. /// <para>-or-</para>
  308. /// <para><paramref name="index" /> is equal to or greater than <see cref="Count" />.</para>
  309. /// </exception>
  310. public void RemoveAt(int index)
  311. {
  312. if (index < 0)
  313. throw new ArgumentOutOfRangeException(nameof(index), index, "Index was less than zero.");
  314. if (index >= Count)
  315. throw new ArgumentOutOfRangeException(nameof(index), index, "Index was equal or greater than TotalCount.");
  316. if (index == 0)
  317. {
  318. RemoveFromFront();
  319. }
  320. else
  321. {
  322. if (index == Count - 1)
  323. {
  324. RemoveFromBack();
  325. }
  326. else
  327. {
  328. if (index < Count / 2)
  329. {
  330. var arrayIndex = GetArrayIndex(index);
  331. // shift the array from 0 to before the index to remove by 1 to the right
  332. // the element to remove is replaced by the copy
  333. Array.Copy(_items, 0, _items, 1, arrayIndex);
  334. // the first element in the arrya is now either a duplicate or it's default value
  335. // to be safe set it to it's default value regardless of circumstance
  336. _items[0] = default(T);
  337. // if we shifted the front element, adjust the front index
  338. if (_frontArrayIndex < arrayIndex)
  339. _frontArrayIndex = (_frontArrayIndex + 1) % _items.Length;
  340. // decrement the count so the back index is calculated correctly
  341. Count--;
  342. }
  343. else
  344. {
  345. var arrayIndex = GetArrayIndex(index);
  346. // shift the array from the center of the array to before the index to remove by 1 to the right
  347. // the element to remove is replaced by the copy
  348. var arrayCenterIndex = _items.Length / 2;
  349. Array.Copy(_items, arrayCenterIndex, _items, arrayCenterIndex + 1, _items.Length - 1 - arrayIndex);
  350. // the last element in the array is now either a duplicate or it's default value
  351. // to be safe set it to it's default value regardless of circumstance
  352. _items[_items.Length - 1] = default(T);
  353. // if we shifted the front element, adjust the front index
  354. if (_frontArrayIndex < arrayIndex)
  355. _frontArrayIndex = (_frontArrayIndex + 1) % _items.Length;
  356. // decrement the count so the back index is calculated correctly
  357. Count--;
  358. }
  359. }
  360. }
  361. }
  362. /// <summary>
  363. /// Removes all elements from the <see cref="Deque{T}" />.
  364. /// </summary>
  365. /// <remarks>
  366. /// <para>
  367. /// <see cref="Count" /> is set to <c>0</c>, and references to other objects from elements of the collection are
  368. /// also released.
  369. /// </para>
  370. /// <para>
  371. /// <see cref="Capacity" /> remains unchanged. To reset the capacity of the <see cref="Deque{T}" />, call the
  372. /// <see cref="TrimExcess" /> method or set the <see cref="Capacity" /> property explictly. Decreasing, or
  373. /// increasing, the capacity reallocates memory and copies all the elements in the <see cref="Deque{T}" />.
  374. /// Trimming an empty <see cref="Deque{T}" /> sets <see cref="Capacity" /> to the default capacity.
  375. /// </para>
  376. /// <para>This method is an O(n) operation, where n is <see cref="Count" />.</para>
  377. /// </remarks>
  378. public void Clear()
  379. {
  380. // allow the garbage collector to reclaim the references
  381. if (Count == 0)
  382. return;
  383. if (Count > _items.Length - _frontArrayIndex)
  384. {
  385. Array.Clear(_items, _frontArrayIndex, _items.Length - _frontArrayIndex);
  386. Array.Clear(_items, 0, _frontArrayIndex + Count - _items.Length);
  387. }
  388. else
  389. Array.Clear(_items, _frontArrayIndex, Count);
  390. Count = 0;
  391. _frontArrayIndex = 0;
  392. }
  393. /// <summary>
  394. /// Determines whether an element is in the <see cref="Deque{T}" />.
  395. /// </summary>
  396. /// <param name="item">
  397. /// The element to locate in the <see cref="Deque{T}" />. The value can be <c>null</c> for reference
  398. /// types.
  399. /// </param>
  400. /// <returns><c>true</c> if <paramref name="item" /> is found in the <see cref="Deque{T}" />; otherwise, false.</returns>
  401. /// <remarks>
  402. /// <para>
  403. /// This method determines equality by using the default equality comparer, as defined by the object's
  404. /// implementation
  405. /// of the <see cref="IEquatable{T}.Equals(T)" /> method for the type of values in the list.
  406. /// </para>
  407. /// <para>
  408. /// This method performs a linear search; therefore, this method is an O(n) operation, where n is
  409. /// <see cref="Count" />.
  410. /// </para>
  411. /// </remarks>
  412. public bool Contains(T item)
  413. {
  414. return this.Contains(item, EqualityComparer<T>.Default);
  415. }
  416. /// <summary>
  417. /// Copies the entire <see cref="Deque{T}" /> to a compatible one-dimensional array, starting at the specified index of
  418. /// the target array.
  419. /// </summary>
  420. /// <param name="array">
  421. /// The one-dimensional <see cref="Array" /> that is the destination of the elements copied from
  422. /// <see cref="Deque{T}" />. The <see cref="Array" /> must have zero-based indexing.
  423. /// </param>
  424. /// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>
  425. /// <exception cref="ArgumentNullException"><paramref name="array" /> is null.</exception>
  426. /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex" /> is less than 0.</exception>
  427. /// <exception cref="ArgumentException">
  428. /// The number of elements in the source <see cref="Deque{T}" /> is greater than the
  429. /// available space from <paramref name="arrayIndex" /> to the end of the destination <paramref name="array" />.
  430. /// </exception>
  431. /// <remarks>
  432. /// This method uses <see cref="Array.Copy(Array, int, Array, int, int)" /> to copy the elements. The elements are
  433. /// copied to the <see cref="Array" /> in the same order in which the enumerator iterates
  434. /// through the <see cref="Deque{T}" />. This method is an O(n) operation, where n is <see cref="Count" />.
  435. /// </remarks>
  436. public void CopyTo(T[] array, int arrayIndex = 0)
  437. {
  438. if (array == null)
  439. throw new ArgumentNullException(nameof(array));
  440. if (array.Rank != 1)
  441. throw new ArgumentException("Only single dimensional arrays are supported for the requested action.");
  442. if (arrayIndex < 0)
  443. throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Index was less than the array's lower bound.");
  444. if (arrayIndex >= array.Length)
  445. {
  446. throw new ArgumentOutOfRangeException(nameof(arrayIndex),
  447. "Index was greater than the array's upper bound.");
  448. }
  449. if (array.Length - arrayIndex < Count)
  450. throw new ArgumentException("Destination array was not long enough.");
  451. if (Count == 0)
  452. return;
  453. try
  454. {
  455. var loopsAround = Count > _items.Length - _frontArrayIndex;
  456. if (!loopsAround)
  457. Array.Copy(_items, _frontArrayIndex, array, arrayIndex, Count);
  458. else
  459. {
  460. Array.Copy(_items, _frontArrayIndex, array, arrayIndex, Capacity - _frontArrayIndex);
  461. Array.Copy(_items, 0, array, arrayIndex + Capacity - _frontArrayIndex,
  462. _frontArrayIndex + (Count - Capacity));
  463. }
  464. }
  465. catch (ArrayTypeMismatchException)
  466. {
  467. throw new ArgumentException(
  468. "Target array type is not compatible with the type of items in the collection.");
  469. }
  470. }
  471. /// <summary>
  472. /// Sets the capacity to the actual number of elements in the <see cref="Deque{T}" />, if that number is less than a
  473. /// threshold value.
  474. /// </summary>
  475. /// <remarks>
  476. /// <para>
  477. /// This method can be used to minimize the <see cref="Deque{T}" />'s memory overhead if no new elements will be
  478. /// added. The cost of reallocating and copying the elements of a <see cref="Deque{T}" /> can be considerable.
  479. /// However, the <see cref="TrimExcess" /> method does nothing if the <see cref="Count" /> is more than 90% of
  480. /// <see cref="Capacity" />. This avoids incurring a large reallocation cost for a relatively small gain.
  481. /// </para>
  482. /// <para>
  483. /// If <see cref="Count" /> is more than 90% of <see cref="Capacity" />, this method is an O(1) operation; O(n)
  484. /// otherwise, where n is <see cref="Count" />.
  485. /// </para>
  486. /// <para>
  487. /// To reset a <see cref="Deque{T}" /> to its initial state, call the <see cref="Clear" /> method before calling
  488. /// the <see cref="TrimExcess" /> method. Trimming an empty <see cref="Deque{T}" /> sets <see cref="Capacity" /> to
  489. /// the default capacity.
  490. /// </para>
  491. /// <para>The capacity can also be set using the <see cref="Capacity" /> property.</para>
  492. /// </remarks>
  493. public void TrimExcess()
  494. {
  495. if (Count > (int)(_items.Length * 0.9))
  496. return;
  497. Capacity = Count;
  498. }
  499. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  500. private int GetArrayIndex(int index)
  501. {
  502. if ((index < 0) || (index >= Count))
  503. return -1;
  504. return _items.Length != 0 ? (_frontArrayIndex + index) % _items.Length : 0;
  505. }
  506. private void EnsureCapacity(int minimumCapacity)
  507. {
  508. if (_items.Length >= minimumCapacity)
  509. return;
  510. var newCapacity = _defaultCapacity;
  511. if (_items.Length > 0)
  512. newCapacity = _resizeFunction(_items.Length);
  513. newCapacity = Math.Max(newCapacity, minimumCapacity);
  514. Capacity = newCapacity;
  515. }
  516. /// <summary>
  517. /// Adds an element to the beginning of the <see cref="Deque{T}" />.
  518. /// </summary>
  519. /// <param name="item">The element to add to the <see cref="Deque{T}" />. The value can be <c>null</c>.</param>
  520. /// <remarks>
  521. /// <para>
  522. /// As elements are added to a <see cref="Deque{T}" />, <see cref="Capacity" /> is automatically increased by
  523. /// <see cref="ResizeFunction" /> as required by reallocating the internal circular array.
  524. /// </para>
  525. /// <para>
  526. /// If <see cref="Count" /> is less than <see cref="Capacity" />, this method is an O(1) operation. Otherwise the
  527. /// internal circular array needs to be resized to accommodate the new element and this method becomes an O(n)
  528. /// operation, where n is <see cref="Count" />.
  529. /// </para>
  530. /// </remarks>
  531. public void AddToFront(T item)
  532. {
  533. EnsureCapacity(Count + 1);
  534. _frontArrayIndex = (_frontArrayIndex - 1 + _items.Length) % _items.Length;
  535. _items[_frontArrayIndex] = item;
  536. Count++;
  537. }
  538. /// <summary>
  539. /// Adds an element to the end of the <see cref="Deque{T}" />.
  540. /// </summary>
  541. /// <param name="item">The element to add to the <see cref="Deque{T}" />. The value can be <c>null</c>.</param>
  542. /// <remarks>
  543. /// <para>
  544. /// As elements are added to a <see cref="Deque{T}" />, <see cref="Capacity" /> is automatically increased by
  545. /// <see cref="ResizeFunction" /> as required by reallocating the internal circular array.
  546. /// </para>
  547. /// <para>
  548. /// If <see cref="Count" /> is less than <see cref="Capacity" />, this method is an O(1) operation. Otherwise the
  549. /// internal circular array needs to be resized to accommodate the new element and this method becomes an O(n)
  550. /// operation, where n is <see cref="Count" />.
  551. /// </para>
  552. /// </remarks>
  553. public void AddToBack(T item)
  554. {
  555. EnsureCapacity(Count + 1);
  556. var index = (_frontArrayIndex + Count++) % _items.Length;
  557. _items[index] = item;
  558. }
  559. /// <summary>
  560. /// Returns the element at the specified index of the <see cref="Deque{T}" />.
  561. /// </summary>
  562. /// <param name="index">The zero-based index of the element to get.</param>
  563. /// <param name="item">
  564. /// When this method returns, contains the element at the specified index of the
  565. /// <see cref="Deque{T}" />, if <paramref name="index" /> was non-negative and less than <see cref="Count" />;
  566. /// otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.
  567. /// </param>
  568. /// <returns>
  569. /// <c>true</c> if <paramref name="item" /> was successfully retrieved at <paramref name="index" /> from the of the
  570. /// <see cref="Deque{T}" />;
  571. /// otherwise, <c>false</c> if <paramref name="index" /> was non-negative and less than <see cref="Count" />.
  572. /// </returns>
  573. public bool Get(int index, out T item)
  574. {
  575. var arrayIndex = GetArrayIndex(index);
  576. if (arrayIndex == -1)
  577. {
  578. item = default(T);
  579. return false;
  580. }
  581. item = _items[arrayIndex];
  582. return true;
  583. }
  584. /// <summary>
  585. /// Returns the element at the beginning of the <see cref="Deque{T}" />.
  586. /// </summary>
  587. /// <param name="item">
  588. /// When this method returns, contains the element at the beginning of the <see cref="Deque{T}" />, if
  589. /// <see cref="Deque{T}" /> was not empty; otherwise, the default value for the type of the value parameter. This
  590. /// parameter is passed uninitialized.
  591. /// </param>
  592. /// <returns>
  593. /// <c>true</c> if <paramref name="item" /> was successfully from the beginning of the <see cref="Deque{T}" />;
  594. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  595. /// </returns>
  596. public bool GetFront(out T item)
  597. {
  598. return Get(0, out item);
  599. }
  600. /// <summary>
  601. /// Returns the element at the end of the <see cref="Deque{T}" />.
  602. /// </summary>
  603. /// <param name="item">
  604. /// When this method returns, contains the element at the end of the <see cref="Deque{T}" />, if
  605. /// <see cref="Deque{T}" /> was not empty; otherwise, the default value for the type of the value parameter. This
  606. /// parameter is passed uninitialized.
  607. /// </param>
  608. /// <returns>
  609. /// <c>true</c> if <paramref name="item" /> was successfully from the end of the <see cref="Deque{T}" />;
  610. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  611. /// </returns>
  612. public bool GetBack(out T item)
  613. {
  614. return Get(Count - 1, out item);
  615. }
  616. /// <summary>
  617. /// Removes the element at the beginning of the <see cref="Deque{T}" />.
  618. /// </summary>
  619. /// <param name="item">
  620. /// When this method returns, contains the element removed from the beginning of the
  621. /// <see cref="Deque{T}" />, if the <see cref="Deque{T}" /> was not empty; otherwise, the default value for the type of
  622. /// the value parameter. This parameter is passed uninitialized.
  623. /// </param>
  624. /// <returns>
  625. /// <c>true</c> if <paramref name="item" /> was successfully removed from the beginning of the <see cref="Deque{T}" />;
  626. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  627. /// </returns>
  628. /// <remarks>
  629. /// <para>
  630. /// This method is similar to the <see cref="GetFront" /> method, but <see cref="GetFront" /> does not
  631. /// modify the <see cref="Deque{T}" />.
  632. /// </para>
  633. /// <para>
  634. /// <c>null</c> can be added to the <see cref="Deque{T}" /> as a value. To distinguish between a null value and
  635. /// the end of the <see cref="Deque{T}" />, check whether the return value of <see cref="RemoveFromFront(out T)" />
  636. /// is
  637. /// <c>false</c> or
  638. /// <see cref="Count" /> is <c>0</c>.
  639. /// </para>
  640. /// <para>
  641. /// This method is an O(1) operation.
  642. /// </para>
  643. /// </remarks>
  644. public bool RemoveFromFront(out T item)
  645. {
  646. if (Count == 0)
  647. {
  648. item = default(T);
  649. return false;
  650. }
  651. var index = _frontArrayIndex % _items.Length;
  652. item = _items[index];
  653. _items[index] = default(T);
  654. _frontArrayIndex = (_frontArrayIndex + 1) % _items.Length;
  655. Count--;
  656. return true;
  657. }
  658. /// <summary>
  659. /// Removes the element at the beginning of the <see cref="Deque{T}" />.
  660. /// </summary>
  661. /// <returns>
  662. /// <c>true</c> if the element was successfully removed from the beginning of the <see cref="Deque{T}" />;
  663. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  664. /// </returns>
  665. /// <remarks>
  666. /// <para>
  667. /// This method is similar to the <see cref="GetFront" /> method, but <see cref="GetFront" /> does not
  668. /// modify the <see cref="Deque{T}" />.
  669. /// </para>
  670. /// <para>
  671. /// <c>null</c> can be added to the <see cref="Deque{T}" /> as a value. To distinguish between a null value and
  672. /// the end of the <see cref="Deque{T}" />, check whether the return value of <see cref="RemoveFromFront()" /> is
  673. /// <c>false</c> or
  674. /// <see cref="Count" /> is <c>0</c>.
  675. /// </para>
  676. /// <para>
  677. /// This method is an O(1) operation.
  678. /// </para>
  679. /// </remarks>
  680. public bool RemoveFromFront()
  681. {
  682. if (Count == 0)
  683. return false;
  684. var index = _frontArrayIndex % _items.Length;
  685. _items[index] = default(T);
  686. _frontArrayIndex = (_frontArrayIndex + 1) % _items.Length;
  687. Count--;
  688. return true;
  689. }
  690. /// <summary>
  691. /// Removes the element at the end of the <see cref="Deque{T}" />.
  692. /// </summary>
  693. /// <param name="item">
  694. /// When this method returns, contains the element removed from the end of the
  695. /// <see cref="Deque{T}" />, if the <see cref="Deque{T}" /> was not empty; otherwise, the default value for the type of
  696. /// the value parameter. This parameter is passed uninitialized.
  697. /// </param>
  698. /// <returns>
  699. /// <c>true</c> if <paramref name="item" /> was successfully removed from the end of the <see cref="Deque{T}" />;
  700. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  701. /// </returns>
  702. /// <remarks>
  703. /// <para>
  704. /// This method is similar to the <see cref="GetBack" /> method, but <see cref="GetBack" /> does not
  705. /// modify the <see cref="Deque{T}" />.
  706. /// </para>
  707. /// <para>
  708. /// <c>null</c> can be added to the <see cref="Deque{T}" /> as a value. To distinguish between a null value and
  709. /// the end of the <see cref="Deque{T}" />, check whether the return value of <see cref="RemoveFromBack(out T)" />
  710. /// is
  711. /// <c>false</c> or
  712. /// <see cref="Count" /> is <c>0</c>.
  713. /// </para>
  714. /// <para>
  715. /// This method is an O(1) operation.
  716. /// </para>
  717. /// </remarks>
  718. public bool RemoveFromBack(out T item)
  719. {
  720. if (Count == 0)
  721. {
  722. item = default(T);
  723. return false;
  724. }
  725. var circularBackIndex = (_frontArrayIndex + (Count - 1)) % _items.Length;
  726. item = _items[circularBackIndex];
  727. _items[circularBackIndex] = default(T);
  728. Count--;
  729. return true;
  730. }
  731. /// <summary>
  732. /// Removes the element at the end of the <see cref="Deque{T}" />.
  733. /// </summary>
  734. /// <returns>
  735. /// <c>true</c> if the element was successfully removed from the end of the <see cref="Deque{T}" />;
  736. /// otherwise, <c>false</c> if the <see cref="Deque{T}" /> is empty.
  737. /// </returns>
  738. /// <remarks>
  739. /// <para>
  740. /// This method is similar to the <see cref="GetBack" /> method, but <see cref="GetBack" /> does not
  741. /// modify the <see cref="Deque{T}" />.
  742. /// </para>
  743. /// <para>
  744. /// <c>null</c> can be added to the <see cref="Deque{T}" /> as a value. To distinguish between a null value and
  745. /// the end of the <see cref="Deque{T}" />, check whether the return value of <see cref="RemoveFromBack()" /> is
  746. /// <c>false</c> or
  747. /// <see cref="Count" /> is <c>0</c>.
  748. /// </para>
  749. /// <para>
  750. /// This method is an O(1) operation.
  751. /// </para>
  752. /// </remarks>
  753. public bool RemoveFromBack()
  754. {
  755. if (Count == 0)
  756. return false;
  757. var circularBackIndex = (_frontArrayIndex + (Count - 1)) % _items.Length;
  758. _items[circularBackIndex] = default(T);
  759. Count--;
  760. return true;
  761. }
  762. /// <summary>
  763. /// Removes and returns the last item.
  764. /// </summary>
  765. /// <returns>The item that was removed</returns>
  766. public T Pop()
  767. {
  768. if (RemoveFromBack(out var item))
  769. return item;
  770. throw new InvalidOperationException();
  771. }
  772. }
  773. }