@@ -0,0 +1,488 @@
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+ // 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
14+ // leaves.
15+
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.
19+ var M = 32;
20+ var E = 2;
21+
22+ // An empty array.
23+ var empty = { ctor:"_Array", _0:0, _1:new Array() };
24+
25+ // Gets the value at index i recursively.
26+ function get(i, a) {
27+ if (a._0 == 0) {
28+ if (i < a._1.length) {
29+ return a._1[i];
30+ } else {
31+ throw new Error("Index "+ i +" on the array is out of range. Check the length first or you safeGet.");
32+ }
33+ }
34+
35+ var slot = getSlot(i, a);
36+ var sub = slot > 0 ? a._2[slot-1] : 0;
37+ return get(i - sub, a._1[slot]);
38+ }
39+
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) {
44+ return a;
45+ }
46+ var newA = nodeCopy(a);
47+ newA._1 = a._1.slice();
48+
49+ if (a._0 == 0) {
50+ newA._1[i] = item;
51+ } else {
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]);
55+ }
56+ return newA;
57+ }
58+
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) {
63+ return pushed;
64+ }
65+
66+ newTree = create(item, a._0);
67+ return siblise(a, newTree);
68+ }
69+
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.
75+ if (a._0 == 0) {
76+ if (a._1.length < M) {
77+ var newA = { ctor:"_Array", _0:0, _1:a._1.slice() };
78+ newA._1.push(item);
79+ return newA;
80+ } else {
81+ return null;
82+ }
83+ }
84+
85+ // Recursively push
86+ var pushed = push_(item, botRight(a));
87+
88+ // There was space in the bottom right tree, so the slot will
89+ // be updated.
90+ if (pushed != null) {
91+ var newA = nodeCopy(a);
92+ newA._1[newA._1.length - 1] = pushed;
93+ newA._2[newA._2.length - 1]++;
94+ return newA
95+ }
96+
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
99+ // at the bottom.
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));
105+ return newA
106+ } else {
107+ return null;
108+ }
109+ }
110+
111+ // Converts an array into a list of elements.
112+ function elements(a) {
113+ return elements_(List.Nil, a);
114+ }
115+
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]);
119+ }
120+ return list;
121+ }
122+
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]);
129+ }
130+ return newA;
131+ }
132+
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]);
138+ }
139+ return newA;
140+ }
141+
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);
145+ }
146+ return b;
147+ }
148+
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);
152+ }
153+ return b;
154+ }
155+
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
160+ // optimized.
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));
165+ }
166+
167+ function sliceRight(to, a) {
168+ if (to == length(a)) {
169+ return a;
170+ }
171+
172+ // Handle leaf level.
173+ if (a._0 == 0) {
174+ var newA = { ctor:"_Array", _0:0 };
175+ newA._1 = a._1.slice(0, to + 1);
176+ return newA;
177+ }
178+
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]);
182+
183+ // Maybe the a node is not even needed, as sliced contains the whole slice.
184+ if (right == 0) {
185+ return sliced;
186+ }
187+
188+ // Create new node.
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);
194+ return newA;
195+ }
196+
197+ function sliceLeft(from, a) {
198+ if (from == 0) {
199+ return a;
200+ }
201+
202+ // Handle leaf level.
203+ if (a._0 == 0) {
204+ var newA = { ctor:"_Array", _0:0 };
205+ newA._1 = a._1.slice(from, a._1.length + 1);
206+ return newA;
207+ }
208+
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]);
212+
213+ // Maybe the a node is not even needed, as sliced contains the whole slice.
214+ if (left == a._1.length - 1) {
215+ return sliced;
216+ }
217+
218+ // Create new node.
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;
223+ var len = 0;
224+ for (var i = 0; i < newA._1.length; i++) {
225+ len += length(newA._1[i]);
226+ newA._2[i] = len;
227+ }
228+
229+ return newA;
230+ }
231+
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)); }
239+
240+ var c = concat_(a, b);
241+ if (c[1]._1.length > 0) {
242+ return siblise(c[0], c[1]);
243+ } else {
244+ return c[0];
245+ }
246+ }
247+
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) {
252+ if (a._0 == 1) {
253+ // Check if balancing is needed and return based on that.
254+ var toRemove = calcToRemove(a, b);
255+ if (toRemove <= E) {
256+ return [a,b];
257+ }
258+
259+ return shuffle(a, b, toRemove);
260+ }
261+
262+ var concated = concat_(botRight(a), botLeft(b));
263+ a = nodeCopy(a), b = nodeCopy(b);
264+
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;
269+
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]);
276+ b._2[i] = len;
277+ }
278+ } else {
279+ b._1.shift();
280+ for (var i = 1; i < b._2.length; i++) {
281+ b._2[i] = b._2[i] - b._2[0];
282+ }
283+ b._2.shift();
284+ }
285+
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) {
289+ return [a,b];
290+ }
291+
292+ return shuffle(a, b, toRemove);
293+ }
294+
295+ // Returns the extra search steps for E. Refer to the paper.
296+ function calcToRemove(a, b) {
297+ var subLengths = 0;
298+ for (var i = 0; i < a._1.length; i++) {
299+ subLengths += a._1[i]._1.length;
300+ }
301+ for (var i = 0; i < b._1.length; i++) {
302+ subLengths += b._1[i]._1.length;
303+ }
304+
305+ var toRemove = a._1.length + b._1.length
306+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
307+ }
308+
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];
312+ }
313+
314+ function set2(a, b, index, value) {
315+ if (index < a.length) {
316+ a[index] = value;
317+ } else {
318+ b[index - a.length] = value;
319+ }
320+ }
321+
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) };
327+ if (height > 0) {
328+ a._2 = new Array(length);
329+ }
330+ return a;
331+ }
332+
333+ function saveSlot(a, b, index, slot) {
334+ set2(a._1, b._1, index, slot);
335+
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));
339+ }
340+
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));
345+
346+ // Skip the slots with size M. More precise: copy the slot references
347+ // to the new node
348+ var read = 0;
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));
352+ read++;
353+ }
354+
355+ // Pulling items from left to right, caching in a slot before writing
356+ // it into the new nodes.
357+ var write = read;
358+ var slot = new createNode(a._0 - 1, 0);
359+ var from = 0;
360+
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)
367+
368+ // Copy and adjust size table.
369+ slot._1 = slot._1.concat(source._1.slice(from, to));
370+ if (slot._0 > 0) {
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);
375+ }
376+ }
377+
378+ from += to;
379+
380+ // Only proceed to next slots[i] if the current one was
381+ // fully copied.
382+ if (source._1.length <= to) {
383+ read++; from = 0;
384+ }
385+
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);
390+ write++;
391+ }
392+ }
393+
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);
397+ write++;
398+ }
399+
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));
403+ read++; write++;
404+ }
405+
406+ return [newA, newB];
407+ }
408+
409+ // Helper functions
410+ function botRight(a) { return a._1[a._1.length - 1]; }
411+ function botLeft(a) { return a._1[0]; }
412+
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(); }
419+ return newA;
420+ }
421+
422+ // Returns how many items are in the tree.
423+ function length(a) {
424+ if (a._0 == 0) {
425+ return a._1.length;
426+ } else {
427+ return a._2[a._2.length - 1];
428+ }
429+ }
430+
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) {
436+ slot++
437+ }
438+ return slot;
439+ }
440+
441+ // Recursively creates a tree with a given height containing
442+ // only the given item.
443+ function create(item, height) {
444+ if (height == 0) {
445+ return { ctor:"_Array", _0:0
446+ , _1:[item] };
447+ } else {
448+ return { ctor:"_Array", _0:height
449+ , _1:[create(item, height - 1)]
450+ , _2:[1] };
451+ }
452+ }
453+
454+ // Recursively creates a tree that contains the given tree.
455+ function parentise(tree, height) {
456+ if (height == tree._0) {
457+ return tree;
458+ } else {
459+ return { ctor:"_Array", _0:height
460+ , _1:[parentise(tree, height - 1)]
461+ , _2:[length(tree)] };
462+ }
463+ }
464+
465+ // Emphasizes blood brotherhood beneath two trees.
466+ function siblise(a, b) {
467+ return { ctor:"_Array", _0:a._0 + 1
468+ , _1:[a, b]
469+ , _2:[length(a), length(a) + length(b)] };
470+ }
471+
472+ Elm.Native.Array.values = {
473+ empty:empty,
474+ elements:elements,
475+ concat:F2(concat),
476+ push:F2(push),
477+ slice:F3(slice),
478+ get:F2(get),
479+ set:F3(set),
480+ map:F2(map),
481+ assocMap:F2(assocMap),
482+ foldl:F3(foldl),
483+ foldr:F3(foldr),
484+ length:length
485+ };
486+
487+ return elm.Native.Array.values = Elm.Native.Array.values;
488+}