@@ -0,0 +1,592 @@
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.
28+ function get(i, array) {
29+ if (i < 0 || i >= length(array)) {
30+ throw new Error("Index " + i + " is out of range. Check the length of " +
31+ "your array first or use getMaybe or getWithDefault.");
33+ return unsafeGet(i, array);
36+ function unsafeGet(i, array) {
37+ if (array.height == 0) {
38+ return array.table[i];
40+ var slot = getSlot(i, array);
41+ var offset = slot > 0 ? array.lengths[slot-1] : 0;
42+ return unsafeGet(i - offset, array.table[slot]);
45+ // Sets the value at the index i. Only the nodes leading to i will get
46+ // copied and updated.
47+ function set(i, item, a) {
48+ if (length(a) <= i) {
51+ var newA = nodeCopy(a);
52+ newA.table = a.table.slice();
55+ newA.table[i] = item;
57+ var slot = getSlot(i, a);
58+ var sub = slot > 0 ? a.lengths[slot-1] : 0;
59+ newA.table[slot] = set(i - sub, item, a.table[slot]);
64+ function initialize(len, f) {
65+ if (len == 0) { return empty; }
66+ var h = Math.floor(Math.log(len)/Math.log(M));
67+ return initialize_(f, h, 0, len);
70+ function initialize_(f, h, from, to) {
72+ var table = new Array((to - from) % (M + 1));
73+ for (var i = 0; i < table.length; i++) {
74+ table[i] = f(from + i);
76+ return { ctor:"_Array", height:0, table:table };
79+ var step = Math.pow(M, h);
80+ var table = new Array(Math.ceil((to - from) / step));
81+ var lengths = new Array(table.length);
82+ for (var i = 0; i < table.length; i++) {
83+ table[i] = initialize_( f, h - 1, from + (i * step)
84+ , Math.min(from + ((i + 1) * step), to));
85+ lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
87+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
90+ function fromList(list) {
91+ if (list == List.Nil) { return empty; }
93+ // Allocate M sized blocks (table) and write list elements to it.
94+ var table = new Array(M);
95+ var nodes = new Array();
98+ while (list.ctor !== '[]') {
103+ // table is full, so we can push a leaf containing it into the
106+ fromListPush({ ctor:"_Array", height:0, table:table }
108+ table = new Array(M);
113+ // Maybe there is something left on the table.
115+ fromListPush({ ctor:"_Array", height:0, table:table.splice(0,i) }
119+ // Go through all of the nodes and eventually push them into higher nodes.
120+ for (var h = 0; h < nodes.length - 1; h++) {
121+ if (nodes[h].table.length > 0) {
122+ fromListPush(nodes[h], nodes);
126+ var head = nodes[nodes.length - 1];
127+ if (head.height > 0 && head.table.length == 1) {
128+ return head.table[0];
134+ // Push a node into a higher node as a child.
135+ function fromListPush(toPush, nodes) {
136+ var h = toPush.height;
138+ // Maybe the node on this height does not exist.
139+ if (nodes.length == h) {
140+ nodes.push({ ctor:"_Array", height:h + 1
142+ , lengths:new Array() });
145+ nodes[h].table.push(toPush);
146+ var len = length(toPush);
147+ if (nodes[h].lengths.length > 0) {
148+ len += nodes[h].lengths[nodes[h].lengths.length - 1];
150+ nodes[h].lengths.push(len);
152+ if (nodes[h].table.length == M) {
153+ fromListPush(nodes[h], nodes);
154+ nodes[h] = { ctor:"_Array", height:h + 1
156+ , lengths:new Array() };
160+ // Pushes an item via push_ to the bottom right of a tree.
161+ function push(item, a) {
162+ var pushed = push_(item, a);
163+ if (pushed !== null) {
167+ var newTree = create(item, a.height);
168+ return siblise(a, newTree);
171+ // Recursively tries to push an item to the bottom-right most
172+ // tree possible. If there is no space left for the item,
173+ // null will be returned.
174+ function push_(item, a) {
175+ // Handle resursion stop at leaf level.
176+ if (a.height == 0) {
177+ if (a.table.length < M) {
178+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
179+ newA.table.push(item);
187+ var pushed = push_(item, botRight(a));
189+ // There was space in the bottom right tree, so the slot will
191+ if (pushed != null) {
192+ var newA = nodeCopy(a);
193+ newA.table[newA.table.length - 1] = pushed;
194+ newA.lengths[newA.lengths.length - 1]++;
198+ // When there was no space left, check if there is space left
199+ // for a new slot with a tree which contains only the item
201+ if (a.table.length < M) {
202+ var newSlot = create(item, a.height - 1);
203+ var newA = nodeCopy(a);
204+ newA.table.push(newSlot);
205+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
212+ // Converts an array into a list of elements.
213+ function toList(a) {
214+ return toList_(List.Nil, a);
217+ function toList_(list, a) {
218+ for (var i = a.table.length - 1; i >= 0; i--) {
219+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
224+ // Maps a function over the elements of an array.
225+ function map(f, a) {
226+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
227+ if (a.height > 0) { newA.lengths = a.lengths; }
228+ for (var i = 0; i < a.table.length; i++) {
229+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
234+ // Maps a function over the elements with their index as first argument.
235+ function indexedMap(f, a) {
236+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
237+ if (a.height > 0) { newA.lengths = a.lengths; }
238+ for (var i = 0; i < a.table.length; i++) {
239+ newA.table[i] = a.height == 0 ? A2(f, i, a.table[i]) : indexedMap(f, a.table[i]);
244+ function foldl(f, b, a) {
245+ for (var i = 0; i < a.table.length; i++) {
246+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
251+ function foldr(f, b, a) {
252+ for (var i = a.table.length; i--; ) {
253+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
258+ // Returns a sliced tree. "to" is inclusive, but this may change,
259+ // when I understand, why e.g. JS does not handle it this way. :-)
260+ // If "from" or "to" is negative, they will select from the end on.
261+ // TODO: currently, it slices the right, then the left. This can be
263+ function slice(from, to, a) {
264+ if (from < 0) { from += length(a); }
265+ if (to < 0) { to += length(a); }
266+ return sliceLeft(from, sliceRight(to, a));
269+ function sliceRight(to, a) {
270+ if (to == length(a)) {
274+ // Handle leaf level.
275+ if (a.height == 0) {
276+ var newA = { ctor:"_Array", height:0 };
277+ newA.table = a.table.slice(0, to + 1);
281+ // Slice the right recursively.
282+ var right = getSlot(to, a);
283+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
285+ // Maybe the a node is not even needed, as sliced contains the whole slice.
291+ var newA = { ctor:"_Array", height:a.height
292+ , table:a.table.slice(0, right + 1)
293+ , lengths:a.lengths.slice(0, right + 1) };
294+ newA.table[right] = sliced;
295+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
299+ function sliceLeft(from, a) {
304+ // Handle leaf level.
305+ if (a.height == 0) {
306+ var newA = { ctor:"_Array", height:0 };
307+ newA.table = a.table.slice(from, a.table.length + 1);
311+ // Slice the left recursively.
312+ var left = getSlot(from, a);
313+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
315+ // Maybe the a node is not even needed, as sliced contains the whole slice.
316+ if (left == a.table.length - 1) {
321+ var newA = { ctor:"_Array", height:a.height
322+ , table:a.table.slice(left, a.table.length + 1)
323+ , lengths:new Array(a.table.length - left) };
324+ newA.table[left] = sliced;
326+ for (var i = 0; i < newA.table.length; i++) {
327+ len += length(newA.table[i]);
328+ newA.lengths[i] = len;
334+ // Appends two trees.
335+ // TODO: Add support for appending trees of different sizes. Current
336+ // behavior will just rise the lower tree and then append them.
337+ function append(a,b) {
338+ if (b.height > a.height) { return append(parentise(a, b.height), b); }
339+ if (a.height > b.height) { return append(a, parentise(b, a.height)); }
340+ if (a.height == 0) { return append(parentise(a, 1), parentise(b, 1)); }
342+ var c = append_(a, b);
343+ if (c[1].table.length > 0) {
344+ return siblise(c[0], c[1]);
350+ // Returns an array of two nodes. The second node _may_ be empty. This case
351+ // needs to be handled by the function, that called append_. May be only
352+ // called for trees with an minimal height of 1.
353+ function append_(a, b) {
354+ if (a.height == 1) {
355+ // Check if balancing is needed and return based on that.
356+ var toRemove = calcToRemove(a, b);
357+ if (toRemove <= E) {
361+ return shuffle(a, b, toRemove);
364+ var appended = append_(botRight(a), botLeft(b));
365+ a = nodeCopy(a), b = nodeCopy(b);
367+ // Adjust the bottom right side of the new tree.
368+ a.table[a.table.length - 1] = appended[0];
369+ a.lengths[a.lengths.length - 1] = length(appended[0])
370+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
372+ // Adjust the bottom left side of the new tree.
373+ if (appended[1].table.length > 0) {
374+ b.table[0] = appended[1];
375+ b.lengths[0] = length(appended[1]);
376+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
377+ len += length(b.table[i]);
382+ for (var i = 1; i < b.lengths.length; i++) {
383+ b.lengths[i] = b.lengths[i] - b.lengths[0];
388+ // Check if balancing is needed and return based on that.
389+ var toRemove = calcToRemove(a, b);
390+ if (toRemove <= E || b.table.length == 0) {
394+ return shuffle(a, b, toRemove);
397+ // Returns the extra search steps for E. Refer to the paper.
398+ function calcToRemove(a, b) {
400+ for (var i = 0; i < a.table.length; i++) {
401+ subLengths += a.table[i].table.length;
403+ for (var i = 0; i < b.table.length; i++) {
404+ subLengths += b.table[i].table.length;
407+ var toRemove = a.table.length + b.table.length
408+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
411+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
412+ function get2(a, b, index) {
413+ return index < a.length ? a[index] : b[index - a.length];
416+ function set2(a, b, index, value) {
417+ if (index < a.length) {
420+ b[index - a.length] = value;
424+ function saveSlot(a, b, index, slot) {
425+ set2(a.table, b.table, index, slot);
427+ var l = (index == 0 || index == a.lengths.length) ?
428+ 0 : get2(a.lengths, a.lengths, index - 1);
429+ set2(a.lengths, b.lengths, index, l + length(slot));
432+ // Creates a node or leaf with a given length at their arrays for perfomance.
433+ // Is only used by shuffle.
434+ function createNode(h, length) {
435+ if (length < 0) { length = 0; }
436+ var a = { ctor:"_Array", height:h, table:new Array(length) };
438+ a.lengths = new Array(length);
443+ // Returns an array of two balanced nodes.
444+ function shuffle(a, b, toRemove) {
445+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
446+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
448+ // Skip the slots with size M. More precise: copy the slot references
451+ while (get2(a.table, b.table, read).table.length % M == 0) {
452+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
453+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
457+ // Pulling items from left to right, caching in a slot before writing
458+ // it into the new nodes.
460+ var slot = new createNode(a.height - 1, 0);
463+ // If the current slot is still containing data, then there will be at
464+ // least one more write, so we do not break this loop yet.
465+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
466+ // Find out the max possible items for copying.
467+ var source = get2(a.table, b.table, read);
468+ var to = Math.min(M - slot.table.length, source.table.length)
470+ // Copy and adjust size table.
471+ slot.table = slot.table.concat(source.table.slice(from, to));
472+ if (slot.height > 0) {
473+ var len = slot.lengths.length;
474+ for (var i = len; i < len + to - from; i++) {
475+ slot.lengths[i] = length(slot.table[i]);
476+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
482+ // Only proceed to next slots[i] if the current one was
484+ if (source.table.length <= to) {
488+ // Only create a new slot if the current one is filled up.
489+ if (slot.table.length == M) {
490+ saveSlot(newA, newB, write, slot);
491+ slot = createNode(a.height - 1,0);
496+ // Cleanup after the loop. Copy the last slot into the new nodes.
497+ if (slot.table.length > 0) {
498+ saveSlot(newA, newB, write, slot);
502+ // Shift the untouched slots to the left
503+ while (read < a.table.length + b.table.length ) {
504+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
508+ return [newA, newB];
511+ // Navigation functions
512+ function botRight(a) { return a.table[a.table.length - 1]; }
513+ function botLeft(a) { return a.table[0]; }
515+ // Copies a node for updating. Note that you should not use this if
516+ // only updating only one of "table" or "lengths" for performance reasons.
517+ function nodeCopy(a) {
518+ var newA = { ctor:"_Array", height:a.height
519+ , table:a.table.slice() };
520+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
524+ // Returns how many items are in the tree.
525+ function length(array) {
526+ if (array.height == 0) {
527+ return array.table.length;
529+ return array.lengths[array.lengths.length - 1];
533+ // Calculates in which slot of "table" the item probably is, then
534+ // find the exact slot via forward searching in "lengths". Returns the index.
535+ function getSlot(i, a) {
536+ var slot = Math.floor(i / (Math.pow(M, a.height)));
537+ while (a.lengths[slot] <= i) {
543+ // Recursively creates a tree with a given height containing
544+ // only the given item.
545+ function create(item, h) {
547+ return { ctor:"_Array", height:0
550+ return { ctor:"_Array", height:h
551+ , table:[create(item, h - 1)]
556+ // Recursively creates a tree that contains the given tree.
557+ function parentise(tree, h) {
558+ if (h == tree.height) {
561+ return { ctor:"_Array", height:h
562+ , table:[parentise(tree, h - 1)]
563+ , lengths:[length(tree)] };
567+ // Emphasizes blood brotherhood beneath two trees.
568+ function siblise(a, b) {
569+ return { ctor:"_Array", height:a.height + 1
571+ , lengths:[length(a), length(a) + length(b)] };
574+ Elm.Native.Array.values = {
578+ initialize:F2(initialize),
585+ indexedMap:F2(indexedMap),
591+ return elm.Native.Array.values = Elm.Native.Array.values;