@@ -0,0 +1,491 @@
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+ // A RRB-Tree has two distinct data types.
12+ // Leaf -> "height" is always 0
13+ // "table" is an array of elements
14+ // Node -> "height" is always greater than 0
15+ // "table" is an array of child nodes
16+ // "lengths" is an array of accumulated lengths of the child nodes
18+ // M is the maximal table size. 32 seems fast. E is the allowed increase
19+ // of search steps when concatting to find an index. Lower values will
20+ // decrease balancing, but will increase search steps.
25+ var empty = { ctor:"_Array", height:0, table:new Array() };
27+ // Gets the value at index i recursively.
30+ if (i < a.table.length) {
33+ throw new Error("Index "+ i +" is out of range. Check the length of your array first or use getSafe or getMaybe.");
37+ var slot = getSlot(i, a);
38+ var sub = slot > 0 ? a.lengths[slot-1] : 0;
39+ return get(i - sub, a.table[slot]);
42+ // Sets the value at the index i. Only the nodes leading to i will get
43+ // copied and updated.
44+ function set(i, item, a) {
45+ if (length(a) <= i) {
48+ var newA = nodeCopy(a);
49+ newA.table = a.table.slice();
52+ newA.table[i] = item;
54+ var slot = getSlot(i, a);
55+ var sub = slot > 0 ? a.lengths[slot-1] : 0;
56+ newA.table[slot] = set(i - sub, item, a.table[slot]);
61+ // Pushes an item via push_ to the bottom right of a tree.
62+ function push(item, a) {
63+ var pushed = push_(item, a);
64+ if (pushed !== null) {
68+ newTree = create(item, a.height);
69+ return siblise(a, newTree);
72+ // Recursively tries to push an item to the bottom-right most
73+ // tree possible. If there is no space left for the item,
74+ // null will be returned.
75+ function push_(item, a) {
76+ // Handle resursion stop at leaf level.
78+ if (a.table.length < M) {
79+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
80+ newA.table.push(item);
88+ var pushed = push_(item, botRight(a));
90+ // There was space in the bottom right tree, so the slot will
92+ if (pushed != null) {
93+ var newA = nodeCopy(a);
94+ newA.table[newA.table.length - 1] = pushed;
95+ newA.lengths[newA.lengths.length - 1]++;
99+ // When there was no space left, check if there is space left
100+ // for a new slot with a tree which contains only the item
102+ if (a.table.length < M) {
103+ var newSlot = create(item, a.height - 1);
104+ var newA = nodeCopy(a);
105+ newA.table.push(newSlot);
106+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
113+ // Converts an array into a list of elements.
114+ function toList(a) {
115+ return toList_(List.Nil, a);
118+ function toList_(list, a) {
119+ for (var i = a.table.length - 1; i >= 0; i--) {
120+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
125+ // Maps a function over the elements of an array.
126+ function map(f, a) {
127+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
128+ if (a.height > 0) { newA.lengths = a.lengths; }
129+ for (var i = 0; i < a.table.length; i++) {
130+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
135+ // Maps a function over the elements with their index as first argument.
136+ function indexedMap(f, a) {
137+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
138+ if (a.height > 0) { newA.lengths = a.lengths; }
139+ for (var i = 0; i < a.table.length; i++) {
140+ newA.table[i] = a.height == 0 ? A2(f, i, a.table[i]) : indexedMap(f, a.table[i]);
145+ function foldl(f, b, a) {
146+ for (var i = a.table.length - 1; i >= 0; i--) {
147+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
152+ function foldr(f, b, a) {
153+ for (var i = 0; i < a.table.length; i++) {
154+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
159+ // Returns a sliced tree. "to" is inclusive, but this may change,
160+ // when I understand, why e.g. JS does not handle it this way. :-)
161+ // If "from" or "to" is negative, they will select from the end on.
162+ // TODO: currently, it slices the right, then the left. This can be
164+ function slice(from, to, a) {
165+ if (from < 0) { from += length(a); }
166+ if (to < 0) { to += length(a); }
167+ return sliceLeft(from, sliceRight(to, a));
170+ function sliceRight(to, a) {
171+ if (to == length(a)) {
175+ // Handle leaf level.
176+ if (a.height == 0) {
177+ var newA = { ctor:"_Array", height:0 };
178+ newA.table = a.table.slice(0, to + 1);
182+ // Slice the right recursively.
183+ var right = getSlot(to, a);
184+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
186+ // Maybe the a node is not even needed, as sliced contains the whole slice.
192+ var newA = { ctor:"_Array", height:a.height
193+ , table:a.table.slice(0, right + 1)
194+ , lengths:a.lengths.slice(0, right + 1) };
195+ newA.table[right] = sliced;
196+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
200+ function sliceLeft(from, a) {
205+ // Handle leaf level.
206+ if (a.height == 0) {
207+ var newA = { ctor:"_Array", height:0 };
208+ newA.table = a.table.slice(from, a.table.length + 1);
212+ // Slice the left recursively.
213+ var left = getSlot(from, a);
214+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
216+ // Maybe the a node is not even needed, as sliced contains the whole slice.
217+ if (left == a.table.length - 1) {
222+ var newA = { ctor:"_Array", height:a.height
223+ , table:a.table.slice(left, a.table.length + 1)
224+ , lengths:new Array(a.table.length - left) };
225+ newA.table[left] = sliced;
227+ for (var i = 0; i < newA.table.length; i++) {
228+ len += length(newA.table[i]);
229+ newA.lengths[i] = len;
235+ // Concats two trees.
236+ // TODO: Add support for concatting trees of different sizes. Current
237+ // behavior will just rise the lower tree and then concat them.
238+ function concat(a, b) {
239+ if (b.height > a.height) { return concat(parentise(a, b.height), b); }
240+ if (a.height > b.height) { return concat(a, parentise(b, a.height)); }
241+ if (a.height == 0) { return concat(parentise(a, 1), parentise(b, 1)); }
243+ var c = concat_(a, b);
244+ if (c[1].table.length > 0) {
245+ return siblise(c[0], c[1]);
251+ // Returns an array of two nodes. The second node _may_ be empty. This case
252+ // needs to be handled by the function, that called concat_. May be only
253+ // called for trees with an minimal height of 1.
254+ function concat_(a, b) {
255+ if (a.height == 1) {
256+ // Check if balancing is needed and return based on that.
257+ var toRemove = calcToRemove(a, b);
258+ if (toRemove <= E) {
262+ return shuffle(a, b, toRemove);
265+ var concated = concat_(botRight(a), botLeft(b));
266+ a = nodeCopy(a), b = nodeCopy(b);
268+ // Adjust the bottom right side of the new tree.
269+ a.table[a.table.length - 1] = concated[0];
270+ a.lengths[a.lengths.length - 1] = length(concated[0])
271+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
273+ // Adjust the bottom left side of the new tree.
274+ if (concated[1].table.length > 0) {
275+ b.table[0] = concated[1];
276+ b.lengths[0] = length(concated[1]);
277+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
278+ len += length(b.table[i]);
283+ for (var i = 1; i < b.lengths.length; i++) {
284+ b.lengths[i] = b.lengths[i] - b.lengths[0];
289+ // Check if balancing is needed and return based on that.
290+ var toRemove = calcToRemove(a, b);
291+ if (toRemove <= E || b.table.length == 0) {
295+ return shuffle(a, b, toRemove);
298+ // Returns the extra search steps for E. Refer to the paper.
299+ function calcToRemove(a, b) {
301+ for (var i = 0; i < a.table.length; i++) {
302+ subLengths += a.table[i].table.length;
304+ for (var i = 0; i < b.table.length; i++) {
305+ subLengths += b.table[i].table.length;
308+ var toRemove = a.table.length + b.table.length
309+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
312+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
313+ function get2(a, b, index) {
314+ return index < a.length ? a[index] : b[index - a.length];
317+ function set2(a, b, index, value) {
318+ if (index < a.length) {
321+ b[index - a.length] = value;
325+ function saveSlot(a, b, index, slot) {
326+ set2(a.table, b.table, index, slot);
328+ var l = (index == 0 || index == a.lengths.length) ?
329+ 0 : get2(a.lengths, a.lengths, index - 1);
330+ set2(a.lengths, b.lengths, index, l + length(slot));
333+ // Creates a node or leaf with a given length at their arrays for perfomance.
334+ // Is only used by shuffle.
335+ function createNode(h, length) {
336+ if (length < 0) { length = 0; }
337+ var a = { ctor:"_Array", height:h, table:new Array(length) };
339+ a.lengths = new Array(length);
344+ // Returns an array of two balanced nodes.
345+ function shuffle(a, b, toRemove) {
346+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
347+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
349+ // Skip the slots with size M. More precise: copy the slot references
352+ while (get2(a.table, b.table, read).table.length % M == 0) {
353+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
354+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
358+ // Pulling items from left to right, caching in a slot before writing
359+ // it into the new nodes.
361+ var slot = new createNode(a.height - 1, 0);
364+ // If the current slot is still containing data, then there will be at
365+ // least one more write, so we do not break this loop yet.
366+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
367+ // Find out the max possible items for copying.
368+ var source = get2(a.table, b.table, read);
369+ var to = Math.min(M - slot.table.length, source.table.length)
371+ // Copy and adjust size table.
372+ slot.table = slot.table.concat(source.table.slice(from, to));
373+ if (slot.height > 0) {
374+ var len = slot.lengths.length;
375+ for (var i = len; i < len + to - from; i++) {
376+ slot.lengths[i] = length(slot.table[i]);
377+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
383+ // Only proceed to next slots[i] if the current one was
385+ if (source.table.length <= to) {
389+ // Only create a new slot if the current one is filled up.
390+ if (slot.table.length == M) {
391+ saveSlot(newA, newB, write, slot);
392+ slot = createNode(a.height - 1,0);
397+ // Cleanup after the loop. Copy the last slot into the new nodes.
398+ if (slot.table.length > 0) {
399+ saveSlot(newA, newB, write, slot);
403+ // Shift the untouched slots to the left
404+ while (read < a.table.length + b.table.length ) {
405+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
409+ return [newA, newB];
412+ // Navigation functions
413+ function botRight(a) { return a.table[a.table.length - 1]; }
414+ function botLeft(a) { return a.table[0]; }
416+ // Copies a node for updating. Note that you should not use this if
417+ // only updating only one of "table" or "lengths" for performance reasons.
418+ function nodeCopy(a) {
419+ var newA = { ctor:"_Array", height:a.height
420+ , table:a.table.slice() };
421+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
425+ // Returns how many items are in the tree.
426+ function length(a) {
427+ if (a.height == 0) {
428+ return a.table.length;
430+ return a.lengths[a.lengths.length - 1];
434+ // Calculates in which slot of "table" the item probably is, then
435+ // find the exact slot via forward searching in "lengths". Returns the index.
436+ function getSlot(i, a) {
437+ var slot = Math.floor(i / (Math.pow(M, a.height)));
438+ while (a.lengths[slot] <= i) {
444+ // Recursively creates a tree with a given height containing
445+ // only the given item.
446+ function create(item, h) {
448+ return { ctor:"_Array", height:0
451+ return { ctor:"_Array", height:h
452+ , table:[create(item, h - 1)]
457+ // Recursively creates a tree that contains the given tree.
458+ function parentise(tree, h) {
459+ if (h == tree.height) {
462+ return { ctor:"_Array", height:h
463+ , table:[parentise(tree, h - 1)]
464+ , lengths:[length(tree)] };
468+ // Emphasizes blood brotherhood beneath two trees.
469+ function siblise(a, b) {
470+ return { ctor:"_Array", height:a.height + 1
472+ , lengths:[length(a), length(a) + length(b)] };
475+ Elm.Native.Array.values = {
484+ indexedMap:F2(indexedMap),
490+ return elm.Native.Array.values = Elm.Native.Array.values;