@@ -0,0 +1,643 @@
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;
8+
9+ var List = Elm.Native.List.make(elm);
10+
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
17+
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.
21+ var M = 32;
22+ var E = 2;
23+
24+ // An empty array.
25+ var empty = { ctor:"_Array", height:0, table:new Array() };
26+
27+ function get(i, array) {
28+ if (i < 0 || i >= length(array)) {
29+ throw new Error("Index " + i + " is out of range. Check the length of " +
30+ "your array first or use getMaybe or getWithDefault.");
31+ }
32+ return unsafeGet(i, array);
33+ }
34+
35+ function unsafeGet(i, array) {
36+ for (var x = array.height; x > 0; x--) {
37+ var slot = i >> (x * 5);
38+ if (slot > 0) {
39+ while (array.lengths[slot - 1] > i) { slot--; }
40+ i -= array.lengths[slot - 1];
41+ }
42+ array = array.table[slot];
43+ }
44+ return array.table[i];
45+ }
46+
47+ // Sets the value at the index i. Only the nodes leading to i will get
48+ // copied and updated.
49+ function set(i, item, array) {
50+ if (i < 0 || length(array) <= i) {
51+ return array;
52+ }
53+ return unsafeSet(i, item, array);
54+ }
55+
56+ function unsafeSet(i, item, array) {
57+ array = nodeCopy(array);
58+
59+ if (array.height == 0) {
60+ array.table[i] = item;
61+ } else {
62+ var slot = getSlot(i, array);
63+ if (slot > 0) {
64+ i -= array.lengths[slot - 1];
65+ }
66+ array.table[slot] = unsafeSet(i, item, array.table[slot]);
67+ }
68+ return array;
69+ }
70+
71+ function initialize(len, f) {
72+ if (len == 0) { return empty; }
73+ var h = Math.floor(Math.log(len)/Math.log(M));
74+ return initialize_(f, h, 0, len);
75+ }
76+
77+ function initialize_(f, h, from, to) {
78+ if (h == 0) {
79+ var table = new Array((to - from) % (M + 1));
80+ for (var i = 0; i < table.length; i++) {
81+ table[i] = f(from + i);
82+ }
83+ return { ctor:"_Array", height:0, table:table };
84+ }
85+
86+ var step = Math.pow(M, h);
87+ var table = new Array(Math.ceil((to - from) / step));
88+ var lengths = new Array(table.length);
89+ for (var i = 0; i < table.length; i++) {
90+ table[i] = initialize_( f, h - 1, from + (i * step)
91+ , Math.min(from + ((i + 1) * step), to));
92+ lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
93+ }
94+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
95+ }
96+
97+ function fromList(list) {
98+ if (list == List.Nil) { return empty; }
99+
100+ // Allocate M sized blocks (table) and write list elements to it.
101+ var table = new Array(M);
102+ var nodes = new Array();
103+ var i = 0;
104+
105+ while (list.ctor !== '[]') {
106+ table[i] = list._0;
107+ list = list._1;
108+ i++;
109+
110+ // table is full, so we can push a leaf containing it into the
111+ // next node.
112+ if (i == M) {
113+ fromListPush({ ctor:"_Array", height:0, table:table }
114+ , nodes);
115+ table = new Array(M);
116+ i = 0;
117+ }
118+ }
119+
120+ // Maybe there is something left on the table.
121+ if (i > 0) {
122+ fromListPush({ ctor:"_Array", height:0, table:table.splice(0,i) }
123+ , nodes);
124+ }
125+
126+ // Go through all of the nodes and eventually push them into higher nodes.
127+ for (var h = 0; h < nodes.length - 1; h++) {
128+ if (nodes[h].table.length > 0) {
129+ fromListPush(nodes[h], nodes);
130+ }
131+ }
132+
133+ var head = nodes[nodes.length - 1];
134+ if (head.height > 0 && head.table.length == 1) {
135+ return head.table[0];
136+ } else {
137+ return head;
138+ }
139+ }
140+
141+ // Push a node into a higher node as a child.
142+ function fromListPush(toPush, nodes) {
143+ var h = toPush.height;
144+
145+ // Maybe the node on this height does not exist.
146+ if (nodes.length == h) {
147+ nodes.push({ ctor:"_Array", height:h + 1
148+ , table:new Array()
149+ , lengths:new Array() });
150+ }
151+
152+ nodes[h].table.push(toPush);
153+ var len = length(toPush);
154+ if (nodes[h].lengths.length > 0) {
155+ len += nodes[h].lengths[nodes[h].lengths.length - 1];
156+ }
157+ nodes[h].lengths.push(len);
158+
159+ if (nodes[h].table.length == M) {
160+ fromListPush(nodes[h], nodes);
161+ nodes[h] = { ctor:"_Array", height:h + 1
162+ , table:new Array()
163+ , lengths:new Array() };
164+ }
165+ }
166+
167+ // Pushes an item via push_ to the bottom right of a tree.
168+ function push(item, a) {
169+ var pushed = push_(item, a);
170+ if (pushed !== null) {
171+ return pushed;
172+ }
173+
174+ var newTree = create(item, a.height);
175+ return siblise(a, newTree);
176+ }
177+
178+ // Recursively tries to push an item to the bottom-right most
179+ // tree possible. If there is no space left for the item,
180+ // null will be returned.
181+ function push_(item, a) {
182+ // Handle resursion stop at leaf level.
183+ if (a.height == 0) {
184+ if (a.table.length < M) {
185+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
186+ newA.table.push(item);
187+ return newA;
188+ } else {
189+ return null;
190+ }
191+ }
192+
193+ // Recursively push
194+ var pushed = push_(item, botRight(a));
195+
196+ // There was space in the bottom right tree, so the slot will
197+ // be updated.
198+ if (pushed != null) {
199+ var newA = nodeCopy(a);
200+ newA.table[newA.table.length - 1] = pushed;
201+ newA.lengths[newA.lengths.length - 1]++;
202+ return newA
203+ }
204+
205+ // When there was no space left, check if there is space left
206+ // for a new slot with a tree which contains only the item
207+ // at the bottom.
208+ if (a.table.length < M) {
209+ var newSlot = create(item, a.height - 1);
210+ var newA = nodeCopy(a);
211+ newA.table.push(newSlot);
212+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
213+ return newA
214+ } else {
215+ return null;
216+ }
217+ }
218+
219+ // Converts an array into a list of elements.
220+ function toList(a) {
221+ return toList_(List.Nil, a);
222+ }
223+
224+ function toList_(list, a) {
225+ for (var i = a.table.length - 1; i >= 0; i--) {
226+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
227+ }
228+ return list;
229+ }
230+
231+ // Maps a function over the elements of an array.
232+ function map(f, a) {
233+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
234+ if (a.height > 0) { newA.lengths = a.lengths; }
235+ for (var i = 0; i < a.table.length; i++) {
236+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
237+ }
238+ return newA;
239+ }
240+
241+ // Maps a function over the elements with their index as first argument.
242+ function indexedMap(f, a) {
243+ return indexedMap_(f, a, 0);
244+ }
245+
246+ function indexedMap_(f, a, from) {
247+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
248+ if (a.height > 0) { newA.lengths = a.lengths; }
249+ for (var i = 0; i < a.table.length; i++) {
250+ newA.table[i] = a.height == 0 ? A2(f, from + i, a.table[i])
251+ : indexedMap_( f, a.table[i]
252+ , i == 0 ? 0 : a.lengths[i - 1]);
253+ }
254+ return newA;
255+ }
256+
257+ function foldl(f, b, a) {
258+ for (var i = 0; i < a.table.length; i++) {
259+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
260+ }
261+ return b;
262+ }
263+
264+ function foldr(f, b, a) {
265+ for (var i = a.table.length; i--; ) {
266+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
267+ }
268+ return b;
269+ }
270+
271+ // TODO: currently, it slices the right, then the left. This can be
272+ // optimized.
273+ function slice(from, to, a) {
274+ if (from < 0) { from += length(a); }
275+ if (to < 0) { to += length(a); }
276+ return sliceLeft(from, sliceRight(to, a));
277+ }
278+
279+ function sliceRight(to, a) {
280+ if (to == length(a)) {
281+ return a;
282+ }
283+
284+ // Handle leaf level.
285+ if (a.height == 0) {
286+ var newA = { ctor:"_Array", height:0 };
287+ newA.table = a.table.slice(0, to);
288+ return newA;
289+ }
290+
291+ // Slice the right recursively.
292+ var right = getSlot(to, a);
293+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
294+
295+ // Maybe the a node is not even needed, as sliced contains the whole slice.
296+ if (right == 0) {
297+ return sliced;
298+ }
299+
300+ // Create new node.
301+ var newA = { ctor:"_Array", height:a.height
302+ , table:a.table.slice(0, right + 1)
303+ , lengths:a.lengths.slice(0, right + 1) };
304+ newA.table[right] = sliced;
305+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
306+ return newA;
307+ }
308+
309+ function sliceLeft(from, a) {
310+ if (from == 0) {
311+ return a;
312+ }
313+
314+ // Handle leaf level.
315+ if (a.height == 0) {
316+ var newA = { ctor:"_Array", height:0 };
317+ newA.table = a.table.slice(from, a.table.length + 1);
318+ return newA;
319+ }
320+
321+ // Slice the left recursively.
322+ var left = getSlot(from, a);
323+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
324+
325+ // Maybe the a node is not even needed, as sliced contains the whole slice.
326+ if (left == a.table.length - 1) {
327+ return sliced;
328+ }
329+
330+ // Create new node.
331+ var newA = { ctor:"_Array", height:a.height
332+ , table:a.table.slice(left, a.table.length + 1)
333+ , lengths:new Array(a.table.length - left) };
334+ newA.table[left] = sliced;
335+ var len = 0;
336+ for (var i = 0; i < newA.table.length; i++) {
337+ len += length(newA.table[i]);
338+ newA.lengths[i] = len;
339+ }
340+
341+ return newA;
342+ }
343+
344+ // Appends two trees.
345+ // TODO: Add support for appending trees of different sizes. Current
346+ // behavior will just rise the lower tree and then append them.
347+ function append(a,b) {
348+ if (b.height > a.height) { return append(parentise(a, b.height), b); }
349+ if (a.height > b.height) { return append(a, parentise(b, a.height)); }
350+ if (a.height == 0) { return append(parentise(a, 1), parentise(b, 1)); }
351+
352+ var c = append_(a, b);
353+ if (c[1].table.length > 0) {
354+ return siblise(c[0], c[1]);
355+ } else {
356+ return c[0];
357+ }
358+ }
359+
360+ // Returns an array of two nodes. The second node _may_ be empty. This case
361+ // needs to be handled by the function, that called append_. May be only
362+ // called for trees with an minimal height of 1.
363+ function append_(a, b) {
364+ if (a.height == 1) {
365+ // Check if balancing is needed and return based on that.
366+ var toRemove = calcToRemove(a, b);
367+ if (toRemove <= E) {
368+ return [a,b];
369+ }
370+
371+ return shuffle(a, b, toRemove);
372+ }
373+
374+ var appended = append_(botRight(a), botLeft(b));
375+ a = nodeCopy(a), b = nodeCopy(b);
376+
377+ // Adjust the bottom right side of the new tree.
378+ a.table[a.table.length - 1] = appended[0];
379+ a.lengths[a.lengths.length - 1] = length(appended[0])
380+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
381+
382+ // Adjust the bottom left side of the new tree.
383+ if (appended[1].table.length > 0) {
384+ b.table[0] = appended[1];
385+ b.lengths[0] = length(appended[1]);
386+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
387+ len += length(b.table[i]);
388+ b.lengths[i] = len;
389+ }
390+ } else {
391+ b.table.shift();
392+ for (var i = 1; i < b.lengths.length; i++) {
393+ b.lengths[i] = b.lengths[i] - b.lengths[0];
394+ }
395+ b.lengths.shift();
396+ }
397+
398+ // Check if balancing is needed and return based on that.
399+ var toRemove = calcToRemove(a, b);
400+ if (toRemove <= E || b.table.length == 0) {
401+ return [a,b];
402+ }
403+
404+ return shuffle(a, b, toRemove);
405+ }
406+
407+ // Returns the extra search steps for E. Refer to the paper.
408+ function calcToRemove(a, b) {
409+ var subLengths = 0;
410+ for (var i = 0; i < a.table.length; i++) {
411+ subLengths += a.table[i].table.length;
412+ }
413+ for (var i = 0; i < b.table.length; i++) {
414+ subLengths += b.table[i].table.length;
415+ }
416+
417+ var toRemove = a.table.length + b.table.length
418+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
419+ }
420+
421+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
422+ function get2(a, b, index) {
423+ return index < a.length ? a[index] : b[index - a.length];
424+ }
425+
426+ function set2(a, b, index, value) {
427+ if (index < a.length) {
428+ a[index] = value;
429+ } else {
430+ b[index - a.length] = value;
431+ }
432+ }
433+
434+ function saveSlot(a, b, index, slot) {
435+ set2(a.table, b.table, index, slot);
436+
437+ var l = (index == 0 || index == a.lengths.length) ?
438+ 0 : get2(a.lengths, a.lengths, index - 1);
439+ set2(a.lengths, b.lengths, index, l + length(slot));
440+ }
441+
442+ // Creates a node or leaf with a given length at their arrays for perfomance.
443+ // Is only used by shuffle.
444+ function createNode(h, length) {
445+ if (length < 0) { length = 0; }
446+ var a = { ctor:"_Array", height:h, table:new Array(length) };
447+ if (h > 0) {
448+ a.lengths = new Array(length);
449+ }
450+ return a;
451+ }
452+
453+ // Returns an array of two balanced nodes.
454+ function shuffle(a, b, toRemove) {
455+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
456+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
457+
458+ // Skip the slots with size M. More precise: copy the slot references
459+ // to the new node
460+ var read = 0;
461+ while (get2(a.table, b.table, read).table.length % M == 0) {
462+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
463+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
464+ read++;
465+ }
466+
467+ // Pulling items from left to right, caching in a slot before writing
468+ // it into the new nodes.
469+ var write = read;
470+ var slot = new createNode(a.height - 1, 0);
471+ var from = 0;
472+
473+ // If the current slot is still containing data, then there will be at
474+ // least one more write, so we do not break this loop yet.
475+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
476+ // Find out the max possible items for copying.
477+ var source = get2(a.table, b.table, read);
478+ var to = Math.min(M - slot.table.length, source.table.length)
479+
480+ // Copy and adjust size table.
481+ slot.table = slot.table.concat(source.table.slice(from, to));
482+ if (slot.height > 0) {
483+ var len = slot.lengths.length;
484+ for (var i = len; i < len + to - from; i++) {
485+ slot.lengths[i] = length(slot.table[i]);
486+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
487+ }
488+ }
489+
490+ from += to;
491+
492+ // Only proceed to next slots[i] if the current one was
493+ // fully copied.
494+ if (source.table.length <= to) {
495+ read++; from = 0;
496+ }
497+
498+ // Only create a new slot if the current one is filled up.
499+ if (slot.table.length == M) {
500+ saveSlot(newA, newB, write, slot);
501+ slot = createNode(a.height - 1,0);
502+ write++;
503+ }
504+ }
505+
506+ // Cleanup after the loop. Copy the last slot into the new nodes.
507+ if (slot.table.length > 0) {
508+ saveSlot(newA, newB, write, slot);
509+ write++;
510+ }
511+
512+ // Shift the untouched slots to the left
513+ while (read < a.table.length + b.table.length ) {
514+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
515+ read++; write++;
516+ }
517+
518+ return [newA, newB];
519+ }
520+
521+ // Navigation functions
522+ function botRight(a) { return a.table[a.table.length - 1]; }
523+ function botLeft(a) { return a.table[0]; }
524+
525+ // Copies a node for updating. Note that you should not use this if
526+ // only updating only one of "table" or "lengths" for performance reasons.
527+ function nodeCopy(a) {
528+ var newA = { ctor:"_Array", height:a.height
529+ , table:a.table.slice() };
530+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
531+ return newA;
532+ }
533+
534+ // Returns how many items are in the tree.
535+ function length(array) {
536+ if (array.height == 0) {
537+ return array.table.length;
538+ } else {
539+ return array.lengths[array.lengths.length - 1];
540+ }
541+ }
542+
543+ // Calculates in which slot of "table" the item probably is, then
544+ // find the exact slot via forward searching in "lengths". Returns the index.
545+ function getSlot(i, a) {
546+ var slot = i >> (5 * a.height);
547+ while (a.lengths[slot - 1] > i) { slot--; }
548+ return slot;
549+ }
550+
551+ // Recursively creates a tree with a given height containing
552+ // only the given item.
553+ function create(item, h) {
554+ if (h == 0) {
555+ return { ctor:"_Array", height:0
556+ , table:[item] };
557+ } else {
558+ return { ctor:"_Array", height:h
559+ , table:[create(item, h - 1)]
560+ , lengths:[1] };
561+ }
562+ }
563+
564+ // Recursively creates a tree that contains the given tree.
565+ function parentise(tree, h) {
566+ if (h == tree.height) {
567+ return tree;
568+ } else {
569+ return { ctor:"_Array", height:h
570+ , table:[parentise(tree, h - 1)]
571+ , lengths:[length(tree)] };
572+ }
573+ }
574+
575+ // Emphasizes blood brotherhood beneath two trees.
576+ function siblise(a, b) {
577+ return { ctor:"_Array", height:a.height + 1
578+ , table:[a, b]
579+ , lengths:[length(a), length(a) + length(b)] };
580+ }
581+
582+ function toJSArray(a) {
583+ var jsArray = new Array(length(a));
584+ toJSArray_(jsArray, 0, a);
585+ return jsArray;
586+ }
587+
588+ function toJSArray_(jsArray, i, a) {
589+ for (var t = 0; t < a.table.length; t++) {
590+ if (a.height == 0) {
591+ jsArray[i + t] = a.table[t];
592+ } else {
593+ var inc = t == 0 ? 0 : a.lengths[t - 1];
594+ toJSArray_(jsArray, i + inc, a.table[t]);
595+ }
596+ }
597+ }
598+
599+ function fromJSArray(jsArray) {
600+ if (jsArray.length == 0) { return empty; }
601+ var h = Math.floor(Math.log(jsArray.length) / Math.log(M));
602+ return fromJSArray_(jsArray, h, 0, jsArray.length);
603+ }
604+
605+ function fromJSArray_(jsArray, h, from, to) {
606+ if (h == 0) {
607+ return { ctor:"_Array", height:0
608+ , table:jsArray.slice(from, to) };
609+ }
610+
611+ var step = Math.pow(M, h);
612+ var table = new Array(Math.ceil((to - from) / step));
613+ var lengths = new Array(table.length);
614+ for (var i = 0; i < table.length; i++) {
615+ table[i] = fromJSArray_( jsArray, h - 1, from + (i * step)
616+ , Math.min(from + ((i + 1) * step), to));
617+ lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
618+ }
619+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
620+ }
621+
622+ Elm.Native.Array.values = {
623+ empty:empty,
624+ fromList:fromList,
625+ toList:toList,
626+ initialize:F2(initialize),
627+ append:F2(append),
628+ push:F2(push),
629+ slice:F3(slice),
630+ get:F2(get),
631+ set:F3(set),
632+ map:F2(map),
633+ indexedMap:F2(indexedMap),
634+ foldl:F3(foldl),
635+ foldr:F3(foldr),
636+ length:length,
637+
638+ toJSArray:toJSArray,
639+ fromJSArray:fromJSArray
640+ };
641+
642+ return elm.Native.Array.values = Elm.Native.Array.values;
643+}