PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/_heapq.cs

http://github.com/IronLanguages/main
C# | 300 lines | 235 code | 33 blank | 32 comment | 42 complexity | e98e3248ef9940a48f1b6b376e1a32e1 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. using Microsoft.Scripting.Runtime;
  20. using IronPython.Runtime;
  21. using IronPython.Runtime.Binding;
  22. using IronPython.Runtime.Operations;
  23. using IronPython.Runtime.Types;
  24. [assembly: PythonModule("_heapq", typeof(IronPython.Modules.PythonHeapq))]
  25. namespace IronPython.Modules {
  26. public static class PythonHeapq {
  27. public const string __doc__ = "implements a heapq or priority queue.";
  28. public const string __about__ = "Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for all k.";
  29. #region public API
  30. [Documentation("Transform list into a heap, in-place, in O(len(heap)) time.")]
  31. public static void heapify(CodeContext/*!*/ context, List list) {
  32. lock (list) {
  33. DoHeapify(context, list);
  34. }
  35. }
  36. [Documentation("Pop the smallest item off the heap, maintaining the heap invariant.")]
  37. public static object heappop(CodeContext/*!*/ context, List list) {
  38. lock (list) {
  39. int last = list._size - 1;
  40. if (last < 0) {
  41. throw PythonOps.IndexError("index out of range");
  42. }
  43. list.FastSwap(0, last);
  44. list._size--;
  45. SiftDown(context, list, 0, last - 1);
  46. return list._data[list._size];
  47. }
  48. }
  49. [Documentation("Push item onto heap, maintaining the heap invariant.")]
  50. public static void heappush(CodeContext/*!*/ context, List list, object item) {
  51. lock (list) {
  52. list.AddNoLock(item);
  53. SiftUp(context, list, list._size - 1);
  54. }
  55. }
  56. [Documentation("Push item on the heap, then pop and return the smallest item\n"
  57. + "from the heap. The combined action runs more efficiently than\n"
  58. + "heappush() followed by a separate call to heappop()."
  59. )]
  60. public static object heappushpop(CodeContext/*!*/ context, List list, object item) {
  61. lock (list) {
  62. return DoPushPop(context, list, item);
  63. }
  64. }
  65. [Documentation("Pop and return the current smallest value, and add the new item.\n\n"
  66. + "This is more efficient than heappop() followed by heappush(), and can be\n"
  67. + "more appropriate when using a fixed-size heap. Note that the value\n"
  68. + "returned may be larger than item! That constrains reasonable uses of\n"
  69. + "this routine unless written as part of a conditional replacement:\n\n"
  70. + " if item > heap[0]:\n"
  71. + " item = heapreplace(heap, item)\n"
  72. )]
  73. public static object heapreplace(CodeContext/*!*/ context, List list, object item) {
  74. lock (list) {
  75. object ret = list._data[0];
  76. list._data[0] = item;
  77. SiftDown(context, list, 0, list._size - 1);
  78. return ret;
  79. }
  80. }
  81. [Documentation("Find the n largest elements in a dataset.\n\n"
  82. + "Equivalent to: sorted(iterable, reverse=True)[:n]\n"
  83. )]
  84. public static List nlargest(CodeContext/*!*/ context, int n, object iterable) {
  85. if (n <= 0) {
  86. return new List();
  87. }
  88. List ret = new List(Math.Min(n, 4000)); // don't allocate anything too huge
  89. IEnumerator en = PythonOps.GetEnumerator(iterable);
  90. // populate list with first n items
  91. for (int i = 0; i < n; i++) {
  92. if (!en.MoveNext()) {
  93. // fewer than n items; finish up here
  94. HeapSort(context, ret, true);
  95. return ret;
  96. }
  97. ret.append(en.Current);
  98. }
  99. // go through the remainder of the iterator, maintaining a min-heap of the n largest values
  100. DoHeapify(context, ret);
  101. while (en.MoveNext()) {
  102. DoPushPop(context, ret, en.Current);
  103. }
  104. // return the largest items, in descending order
  105. HeapSort(context, ret, true);
  106. return ret;
  107. }
  108. [Documentation("Find the n smallest elements in a dataset.\n\n"
  109. + "Equivalent to: sorted(iterable)[:n]\n"
  110. )]
  111. public static List nsmallest(CodeContext/*!*/ context, int n, object iterable) {
  112. if (n <= 0) {
  113. return new List();
  114. }
  115. List ret = new List(Math.Min(n, 4000)); // don't allocate anything too huge
  116. IEnumerator en = PythonOps.GetEnumerator(iterable);
  117. // populate list with first n items
  118. for (int i = 0; i < n; i++) {
  119. if (!en.MoveNext()) {
  120. // fewer than n items; finish up here
  121. HeapSort(context, ret);
  122. return ret;
  123. }
  124. ret.append(en.Current);
  125. }
  126. // go through the remainder of the iterator, maintaining a max-heap of the n smallest values
  127. DoHeapifyMax(context, ret);
  128. while (en.MoveNext()) {
  129. DoPushPopMax(context, ret, en.Current);
  130. }
  131. // return the smallest items, in ascending order
  132. HeapSort(context, ret);
  133. return ret;
  134. }
  135. #endregion
  136. #region private implementation details (NOTE: thread-unsafe)
  137. private static bool IsLessThan(CodeContext/*!*/ context, object x, object y) {
  138. object ret;
  139. if (PythonTypeOps.TryInvokeBinaryOperator(context, x, y, "__lt__", out ret) &&
  140. !Object.ReferenceEquals(ret, NotImplementedType.Value)) {
  141. return Converter.ConvertToBoolean(ret);
  142. } else if (PythonTypeOps.TryInvokeBinaryOperator(context, y, x, "__le__", out ret) &&
  143. !Object.ReferenceEquals(ret, NotImplementedType.Value)) {
  144. return !Converter.ConvertToBoolean(ret);
  145. } else {
  146. return PythonContext.GetContext(context).LessThan(x, y);
  147. }
  148. }
  149. private static void HeapSort(CodeContext/*!*/ context, List list) {
  150. HeapSort(context, list, false);
  151. }
  152. private static void HeapSort(CodeContext/*!*/ context, List list, bool reverse) {
  153. // for an ascending sort (reverse = false), use a max-heap, and vice-versa
  154. if (reverse) {
  155. DoHeapify(context, list);
  156. } else {
  157. DoHeapifyMax(context, list);
  158. }
  159. int last = list._size - 1;
  160. while (last > 0) {
  161. // put the root node (max if ascending, min if descending) at the end
  162. list.FastSwap(0, last);
  163. // shrink heap by 1
  164. last--;
  165. // maintain heap invariant
  166. if (reverse) {
  167. SiftDown(context, list, 0, last);
  168. } else {
  169. SiftDownMax(context, list, 0, last);
  170. }
  171. }
  172. }
  173. private static void DoHeapify(CodeContext/*!*/ context, List list) {
  174. int last = list._size - 1;
  175. int start = (last - 1) / 2; // index of last parent node
  176. // Sift down each parent node from right to left.
  177. while (start >= 0) {
  178. SiftDown(context, list, start, last);
  179. start--;
  180. }
  181. }
  182. private static void DoHeapifyMax(CodeContext/*!*/ context, List list) {
  183. int last = list._size - 1;
  184. int start = (last - 1) / 2; // index of last parent node
  185. // Sift down each parent node from right to left.
  186. while (start >= 0) {
  187. SiftDownMax(context, list, start, last);
  188. start--;
  189. }
  190. }
  191. private static object DoPushPop(CodeContext/*!*/ context, List heap, object item) {
  192. object first;
  193. if (heap._size == 0 || !IsLessThan(context, first = heap._data[0], item)) {
  194. return item;
  195. }
  196. heap._data[0] = item;
  197. SiftDown(context, heap, 0, heap._size - 1);
  198. return first;
  199. }
  200. private static object DoPushPopMax(CodeContext/*!*/ context, List heap, object item) {
  201. object first;
  202. if (heap._size == 0 || !IsLessThan(context, item, first = heap._data[0])) {
  203. return item;
  204. }
  205. heap._data[0] = item;
  206. SiftDownMax(context, heap, 0, heap._size - 1);
  207. return first;
  208. }
  209. private static void SiftDown(CodeContext/*!*/ context, List heap, int start, int stop) {
  210. int parent = start;
  211. int child;
  212. while ((child = parent * 2 + 1) <= stop) {
  213. // find the smaller sibling
  214. if (child + 1 <= stop && IsLessThan(context, heap._data[child + 1], heap._data[child])) {
  215. child++;
  216. }
  217. // check if min-heap property is violated
  218. if (IsLessThan(context, heap._data[child], heap._data[parent])) {
  219. heap.FastSwap(parent, child);
  220. parent = child;
  221. } else {
  222. return;
  223. }
  224. }
  225. }
  226. private static void SiftDownMax(CodeContext/*!*/ context, List heap, int start, int stop) {
  227. int parent = start;
  228. int child;
  229. while ((child = parent * 2 + 1) <= stop) {
  230. // find the larger sibling
  231. if (child + 1 <= stop && IsLessThan(context, heap._data[child], heap._data[child + 1])) {
  232. child++;
  233. }
  234. // check if max-heap property is violated
  235. if (IsLessThan(context, heap._data[parent], heap._data[child])) {
  236. heap.FastSwap(parent, child);
  237. parent = child;
  238. } else {
  239. return;
  240. }
  241. }
  242. }
  243. private static void SiftUp(CodeContext/*!*/ context, List heap, int index) {
  244. while (index > 0) {
  245. int parent = (index - 1) / 2;
  246. if (IsLessThan(context, heap._data[index], heap._data[parent])) {
  247. heap.FastSwap(parent, index);
  248. index = parent;
  249. } else {
  250. return;
  251. }
  252. }
  253. }
  254. private static void SiftUpMax(CodeContext/*!*/ context, List heap, int index) {
  255. while (index > 0) {
  256. int parent = (index - 1) / 2;
  257. if (IsLessThan(context, heap._data[parent], heap._data[index])) {
  258. heap.FastSwap(parent, index);
  259. index = parent;
  260. } else {
  261. return;
  262. }
  263. }
  264. }
  265. #endregion
  266. }
  267. }