@@ -0,0 +1,490 @@
1+Elm.Native.Array = {};
2+Elm.Native.Array.make = function(elm) {
3+ elm.Native = elm.Native || {};
4+ elm.Native.Array = elm.Native.Array || {};
5+ if (elm.Native.Array.values) return elm.Native.Array.values;
6+ if ('values' in Elm.Native.Array)
7+ return elm.Native.Array.values = Elm.Native.Array.values;
9+ var List = Elm.Native.List.make(elm);
11+ // An RRB-Tree has two distinct data types. A leaf which contains data as
12+ // an array in _1, and a height in _0, that is always 0. A node has in
13+ // addition its size table in _2, while _1 contains an array of nodes or
16+ // M is the maximal node size. 32 seems fast. E is the allowed increase
17+ // of search steps when concatting to find an index. Lower values will
18+ // decrease balancing, but will increase search steps.
23+ var empty = { ctor:"_Array", _0:0, _1:new Array() };
25+ // Gets the value at index i recursively.
28+ if (i < a._1.length) {
31+ throw new Error("Index "+ i +" on the array is out of range. Check the length first or you safeGet.");
35+ var slot = getSlot(i, a);
36+ var sub = slot > 0 ? a._2[slot-1] : 0;
37+ return get(i - sub, a._1[slot]);
40+ // Sets the value at the index i. Only the nodes leading to i will get
41+ // copied and updated.
42+ function set(i, item, a) {
43+ if (length(a) <= i) {
46+ var newA = nodeCopy(a);
47+ newA._1 = a._1.slice();
52+ var slot = getSlot(i, a);
53+ var sub = slot > 0 ? a._2[slot-1] : 0;
54+ newA._1[slot] = set(i - sub, item, a._1[slot]);
59+ // Pushes an item via push_ to the bottom right of a tree.
60+ function push(item, a) {
61+ var pushed = push_(item, a);
62+ if (pushed !== null) {
66+ newTree = create(item, a._0);
67+ return siblise(a, newTree);
70+ // Recursively tries to push an item to the bottom-right most
71+ // tree possible. If there is no space left for the item,
72+ // null will be returned.
73+ function push_(item, a) {
74+ // Handle resursion stop at leaf level.
76+ if (a._1.length < M) {
77+ var newA = { ctor:"_Array", _0:0, _1:a._1.slice() };
86+ var pushed = push_(item, botRight(a));
88+ // There was space in the bottom right tree, so the slot will
90+ if (pushed != null) {
91+ var newA = nodeCopy(a);
92+ newA._1[newA._1.length - 1] = pushed;
93+ newA._2[newA._2.length - 1]++;
97+ // When there was no space left, check if there is space left
98+ // for a new slot with a tree which contains only the item
100+ if (a._1.length < M) {
101+ var newSlot = create(item, a._0 - 1);
102+ var newA = nodeCopy(a);
103+ newA._1.push(newSlot);
104+ newA._2.push(newA._2[newA._2.length - 1] + length(newSlot));
111+ // Converts an array into a list of elements.
112+ function elements(a) {
113+ return elements_(List.Nil, a);
116+ function elements_(list, a) {
117+ for (var i = a._1.length - 1; i >= 0; i--) {
118+ list = a._0 == 0 ? List.Cons(a._1[i], list) : elements_(list, a._1[i]);
123+ // Maps a function over an array.
124+ function map(f, a) {
125+ var newA = { ctor:"_Array", _0:a._0, _1:new Array(a._1) };
126+ if (a._0 > 0) { newA._2 = a._2; }
127+ for (var i = 0; i < a._1.length; i++) {
128+ newA._1[i] = a._0 == 0 ? f(a._1[i]) : map(f, a._1[i]);
133+ function assocMap(f, a) {
134+ var newA = { ctor:"_Array", _0:a._0, _1:new Array(a._1) };
135+ if (a._0 > 0) { newA._2 = a._2; }
136+ for (var i = 0; i < a._1.length; i++) {
137+ newA._1[i] = a._0 == 0 ? A2(f, i, a._1[i]) : assocMap(f, a._1[i]);
142+ function foldl(f, b, a) {
143+ for (var i = a._1.length - 1; i >= 0; i--) {
144+ b = A2(f, a._0 == 0 ? a._1[i] : foldl(f, b, a._1[i]), b);
149+ function foldr(f, b, a) {
150+ for (var i = 0; i < a._1.length; i++) {
151+ b = A2(f, a._0 == 0 ? a._1[i] : foldr(f, b, a._1[i]), b);
156+ // Returns a sliced tree. The to is inclusive, but this may change,
157+ // when I understand, why e.g. JS does not handle it this way. :-)
158+ // If from or to is negative, they will select from the end on.
159+ // TODO: currently, it slices the right, then the left. This can be
161+ function slice(from, to, a) {
162+ if (from < 0) { from += length(a); }
163+ if (to < 0) { to += length(a); }
164+ return sliceLeft(from, sliceRight(to, a));
167+ function sliceRight(to, a) {
168+ if (to == length(a)) {
172+ // Handle leaf level.
174+ var newA = { ctor:"_Array", _0:0 };
175+ newA._1 = a._1.slice(0, to + 1);
179+ // Slice the right recursively.
180+ var right = getSlot(to, a);
181+ var sliced = sliceRight(to - (right > 0 ? a._2[right - 1] : 0), a._1[right]);
183+ // Maybe the a node is not even needed, as sliced contains the whole slice.
189+ var newA = { ctor:"_Array", _0:a._0
190+ , _1:a._1.slice(0, right + 1)
191+ , _2:a._2.slice(0, right + 1) };
192+ newA._1[right] = sliced;
193+ newA._2[right] = length(sliced) + (right > 0 ? newA._2[right - 1] : 0);
197+ function sliceLeft(from, a) {
202+ // Handle leaf level.
204+ var newA = { ctor:"_Array", _0:0 };
205+ newA._1 = a._1.slice(from, a._1.length + 1);
209+ // Slice the left recursively.
210+ var left = getSlot(from, a);
211+ var sliced = sliceLeft(from - (left > 0 ? a._2[left - 1] : 0), a._1[left]);
213+ // Maybe the a node is not even needed, as sliced contains the whole slice.
214+ if (left == a._1.length - 1) {
219+ var newA = { ctor:"_Array", _0:a._0
220+ , _1:a._1.slice(left, a._1.length + 1)
221+ , _2:new Array(a._1.length - left) };
222+ newA._1[left] = sliced;
224+ for (var i = 0; i < newA._1.length; i++) {
225+ len += length(newA._1[i]);
232+ // Concats two trees.
233+ // TODO: Add support for concatting trees of different sizes. Current
234+ // behavior will just rise the lower tree and then concat them.
235+ function concat(a, b) {
236+ if (b._0 > a._0) { return concat(parentise(a, b._0), b); }
237+ if (a._0 > b._0) { return concat(a, parentise(b, a._0)); }
238+ if (a._0 == 0) { return concat(parentise(a, 1), parentise(b, 1)); }
240+ var c = concat_(a, b);
241+ if (c[1]._1.length > 0) {
242+ return siblise(c[0], c[1]);
248+ // Returns an array of two nodes. The second node _may_ be empty. This case
249+ // needs to be handled by the function, that called concat_. May be only
250+ // called for trees with an minimal height of 1.
251+ function concat_(a, b) {
253+ // Check if balancing is needed and return based on that.
254+ var toRemove = calcToRemove(a, b);
255+ if (toRemove <= E) {
259+ return shuffle(a, b, toRemove);
262+ var concated = concat_(botRight(a), botLeft(b));
263+ a = nodeCopy(a), b = nodeCopy(b);
265+ // Adjust the bottom right side of the new tree.
266+ a._1[a._1.length - 1] = concated[0];
267+ a._2[a._2.length - 1] = length(concated[0])
268+ a._2[a._2.length - 1] += a._2.length > 1 ? a._2[a._2.length - 2] : 0;
270+ // Adjust the bottom left side of the new tree.
271+ if (concated[1]._1.length > 0) {
272+ b._1[0] = concated[1];
273+ b._2[0] = length(concated[1]);
274+ for (var i = 1, len = length(b._1[0]); i < b._2.length; i++) {
275+ len += length(b._1[i]);
280+ for (var i = 1; i < b._2.length; i++) {
281+ b._2[i] = b._2[i] - b._2[0];
286+ // Check if balancing is needed and return based on that.
287+ var toRemove = calcToRemove(a, b);
288+ if (toRemove <= E || b._1.length == 0) {
292+ return shuffle(a, b, toRemove);
295+ // Returns the extra search steps for E. Refer to the paper.
296+ function calcToRemove(a, b) {
298+ for (var i = 0; i < a._1.length; i++) {
299+ subLengths += a._1[i]._1.length;
301+ for (var i = 0; i < b._1.length; i++) {
302+ subLengths += b._1[i]._1.length;
305+ var toRemove = a._1.length + b._1.length
306+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
309+ // get2 and set2 are helpers for accessing over two arrays.
310+ function get2(a, b, index) {
311+ return index < a.length ? a[index] : b[index - a.length];
314+ function set2(a, b, index, value) {
315+ if (index < a.length) {
318+ b[index - a.length] = value;
322+ // Creates a node or leaf with a given length at their arrays for perfomance.
323+ // Is only used by shuffle.
324+ function createNode(height, length) {
325+ if (length < 0) { length = 0; }
326+ var a = { ctor:"_Array", _0:height, _1:new Array(length) };
328+ a._2 = new Array(length);
333+ function saveSlot(a, b, index, slot) {
334+ set2(a._1, b._1, index, slot);
336+ var l = (index == 0 || index == a._2.length) ?
337+ 0 : get2(a._2, a._2, index - 1);
338+ set2(a._2, b._2, index, l + length(slot));
341+ // Returns an array of two balanced nodes.
342+ function shuffle(a, b, toRemove) {
343+ var newA = createNode(a._0, Math.min(M, a._1.length + b._1.length - toRemove));
344+ var newB = createNode(a._0, newA._1.length - (a._1.length + b._1.length - toRemove));
346+ // Skip the slots with size M. More precise: copy the slot references
349+ while (get2(a._1, b._1, read)._1.length % M == 0) {
350+ set2(newA._1, newB._1, read, get2(a._1, b._1, read));
351+ set2(newA._2, newB._2, read, get2(a._2, b._2, read));
355+ // Pulling items from left to right, caching in a slot before writing
356+ // it into the new nodes.
358+ var slot = new createNode(a._0 - 1, 0);
361+ // If the current slot is still containing data, then there will be at
362+ // least one more write, so we do not break this loop yet.
363+ while (read - write - (slot._1.length > 0 ? 1 : 0) < toRemove) {
364+ // Find out the max possible items for copying.
365+ var source = get2(a._1, b._1, read);
366+ var to = Math.min(M - slot._1.length, source._1.length)
368+ // Copy and adjust size table.
369+ slot._1 = slot._1.concat(source._1.slice(from, to));
371+ var len = slot._2.length;
372+ for (var i = len; i < len + to - from; i++) {
373+ slot._2[i] = length(slot._1[i]);
374+ slot._2[i] += (i > 0 ? slot._2[i - 1] : 0);
380+ // Only proceed to next slots[i] if the current one was
382+ if (source._1.length <= to) {
386+ // Only create a new slot if the current one is filled up.
387+ if (slot._1.length == M) {
388+ saveSlot(newA, newB, write, slot);
389+ slot = createNode(a._0 - 1,0);
394+ // Cleanup after the loop. Copy the last slot into the new nodes.
395+ if (slot._1.length > 0) {
396+ saveSlot(newA, newB, write, slot);
400+ // Shift the untouched slots to the left
401+ while (read < a._1.length + b._1.length ) {
402+ saveSlot(newA, newB, write, get2(a._1, b._1, read));
406+ return [newA, newB];
410+ function botRight(a) { return a._1[a._1.length - 1]; }
411+ function botLeft(a) { return a._1[0]; }
413+ // Copies a node for updating. Note that you should not use
414+ // this if only updating one of _1 and _2 for performance reasons.
415+ function nodeCopy(a) {
416+ var newA = { ctor:"_Array", _0:a._0
417+ , _1:a._1.slice() };
418+ if (a._0 > 0) { newA._2 = a._2.slice(); }
422+ // Returns how many items are in the tree.
423+ function length(a) {
427+ return a._2[a._2.length - 1];
431+ // Calculates in which slot the item probably is, then
432+ // find the exact slot in the size table ._2. Returns the index.
433+ function getSlot(i, a) {
434+ var slot = Math.floor(i / (Math.pow(M, a._0)));
435+ while (a._2[slot] <= i) {
441+ // Recursively creates a tree with a given height containing
442+ // only the given item.
443+ function create(item, height) {
445+ return { ctor:"_Array", _0:0
448+ return { ctor:"_Array", _0:height
449+ , _1:[create(item, height - 1)]
454+ // Recursively creates a tree that contains the given tree.
455+ function parentise(tree, height) {
456+ if (height == tree._0) {
459+ return { ctor:"_Array", _0:height
460+ , _1:[parentise(tree, height - 1)]
461+ , _2:[length(tree)] };
465+ // Emphasizes blood brotherhood beneath two trees.
466+ function siblise(a, b) {
467+ return { ctor:"_Array", _0:a._0 + 1
469+ , _2:[length(a), length(a) + length(b)] };
472+ Elm.Native.Array.values = {
481+ assocMap:F2(assocMap),
487+ return elm.Native.Array.values = Elm.Native.Array.values;