@@ -0,0 +1,494 @@
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+ // 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.");
32+ }
33+ return unsafeGet(i, array);
34+ }
35+
36+ function unsafeGet(i, array) {
37+ if (array.height == 0) {
38+ return array.table[i];
39+ }
40+ var slot = getSlot(i, array);
41+ var offset = slot > 0 ? array.lengths[slot-1] : 0;
42+ return unsafeGet(i - offset, array.table[slot]);
43+ }
44+
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) {
49+ return a;
50+ }
51+ var newA = nodeCopy(a);
52+ newA.table = a.table.slice();
53+
54+ if (a.height == 0) {
55+ newA.table[i] = item;
56+ } else {
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]);
60+ }
61+ return newA;
62+ }
63+
64+ // Pushes an item via push_ to the bottom right of a tree.
65+ function push(item, a) {
66+ var pushed = push_(item, a);
67+ if (pushed !== null) {
68+ return pushed;
69+ }
70+
71+ var newTree = create(item, a.height);
72+ return siblise(a, newTree);
73+ }
74+
75+ // Recursively tries to push an item to the bottom-right most
76+ // tree possible. If there is no space left for the item,
77+ // null will be returned.
78+ function push_(item, a) {
79+ // Handle resursion stop at leaf level.
80+ if (a.height == 0) {
81+ if (a.table.length < M) {
82+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
83+ newA.table.push(item);
84+ return newA;
85+ } else {
86+ return null;
87+ }
88+ }
89+
90+ // Recursively push
91+ var pushed = push_(item, botRight(a));
92+
93+ // There was space in the bottom right tree, so the slot will
94+ // be updated.
95+ if (pushed != null) {
96+ var newA = nodeCopy(a);
97+ newA.table[newA.table.length - 1] = pushed;
98+ newA.lengths[newA.lengths.length - 1]++;
99+ return newA
100+ }
101+
102+ // When there was no space left, check if there is space left
103+ // for a new slot with a tree which contains only the item
104+ // at the bottom.
105+ if (a.table.length < M) {
106+ var newSlot = create(item, a.height - 1);
107+ var newA = nodeCopy(a);
108+ newA.table.push(newSlot);
109+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
110+ return newA
111+ } else {
112+ return null;
113+ }
114+ }
115+
116+ // Converts an array into a list of elements.
117+ function toList(a) {
118+ return toList_(List.Nil, a);
119+ }
120+
121+ function toList_(list, a) {
122+ for (var i = a.table.length - 1; i >= 0; i--) {
123+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
124+ }
125+ return list;
126+ }
127+
128+ // Maps a function over the elements of an array.
129+ function map(f, a) {
130+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
131+ if (a.height > 0) { newA.lengths = a.lengths; }
132+ for (var i = 0; i < a.table.length; i++) {
133+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
134+ }
135+ return newA;
136+ }
137+
138+ // Maps a function over the elements with their index as first argument.
139+ function indexedMap(f, a) {
140+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
141+ if (a.height > 0) { newA.lengths = a.lengths; }
142+ for (var i = 0; i < a.table.length; i++) {
143+ newA.table[i] = a.height == 0 ? A2(f, i, a.table[i]) : indexedMap(f, a.table[i]);
144+ }
145+ return newA;
146+ }
147+
148+ function foldl(f, b, a) {
149+ for (var i = a.table.length - 1; i >= 0; i--) {
150+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
151+ }
152+ return b;
153+ }
154+
155+ function foldr(f, b, a) {
156+ for (var i = 0; i < a.table.length; i++) {
157+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
158+ }
159+ return b;
160+ }
161+
162+ // Returns a sliced tree. "to" is inclusive, but this may change,
163+ // when I understand, why e.g. JS does not handle it this way. :-)
164+ // If "from" or "to" is negative, they will select from the end on.
165+ // TODO: currently, it slices the right, then the left. This can be
166+ // optimized.
167+ function slice(from, to, a) {
168+ if (from < 0) { from += length(a); }
169+ if (to < 0) { to += length(a); }
170+ return sliceLeft(from, sliceRight(to, a));
171+ }
172+
173+ function sliceRight(to, a) {
174+ if (to == length(a)) {
175+ return a;
176+ }
177+
178+ // Handle leaf level.
179+ if (a.height == 0) {
180+ var newA = { ctor:"_Array", height:0 };
181+ newA.table = a.table.slice(0, to + 1);
182+ return newA;
183+ }
184+
185+ // Slice the right recursively.
186+ var right = getSlot(to, a);
187+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
188+
189+ // Maybe the a node is not even needed, as sliced contains the whole slice.
190+ if (right == 0) {
191+ return sliced;
192+ }
193+
194+ // Create new node.
195+ var newA = { ctor:"_Array", height:a.height
196+ , table:a.table.slice(0, right + 1)
197+ , lengths:a.lengths.slice(0, right + 1) };
198+ newA.table[right] = sliced;
199+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
200+ return newA;
201+ }
202+
203+ function sliceLeft(from, a) {
204+ if (from == 0) {
205+ return a;
206+ }
207+
208+ // Handle leaf level.
209+ if (a.height == 0) {
210+ var newA = { ctor:"_Array", height:0 };
211+ newA.table = a.table.slice(from, a.table.length + 1);
212+ return newA;
213+ }
214+
215+ // Slice the left recursively.
216+ var left = getSlot(from, a);
217+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
218+
219+ // Maybe the a node is not even needed, as sliced contains the whole slice.
220+ if (left == a.table.length - 1) {
221+ return sliced;
222+ }
223+
224+ // Create new node.
225+ var newA = { ctor:"_Array", height:a.height
226+ , table:a.table.slice(left, a.table.length + 1)
227+ , lengths:new Array(a.table.length - left) };
228+ newA.table[left] = sliced;
229+ var len = 0;
230+ for (var i = 0; i < newA.table.length; i++) {
231+ len += length(newA.table[i]);
232+ newA.lengths[i] = len;
233+ }
234+
235+ return newA;
236+ }
237+
238+ // Appends two trees.
239+ // TODO: Add support for appending trees of different sizes. Current
240+ // behavior will just rise the lower tree and then append them.
241+ function append(a,b) {
242+ if (b.height > a.height) { return append(parentise(a, b.height), b); }
243+ if (a.height > b.height) { return append(a, parentise(b, a.height)); }
244+ if (a.height == 0) { return append(parentise(a, 1), parentise(b, 1)); }
245+
246+ var c = append_(a, b);
247+ if (c[1].table.length > 0) {
248+ return siblise(c[0], c[1]);
249+ } else {
250+ return c[0];
251+ }
252+ }
253+
254+ // Returns an array of two nodes. The second node _may_ be empty. This case
255+ // needs to be handled by the function, that called append_. May be only
256+ // called for trees with an minimal height of 1.
257+ function append_(a, b) {
258+ if (a.height == 1) {
259+ // Check if balancing is needed and return based on that.
260+ var toRemove = calcToRemove(a, b);
261+ if (toRemove <= E) {
262+ return [a,b];
263+ }
264+
265+ return shuffle(a, b, toRemove);
266+ }
267+
268+ var appended = append_(botRight(a), botLeft(b));
269+ a = nodeCopy(a), b = nodeCopy(b);
270+
271+ // Adjust the bottom right side of the new tree.
272+ a.table[a.table.length - 1] = appended[0];
273+ a.lengths[a.lengths.length - 1] = length(appended[0])
274+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
275+
276+ // Adjust the bottom left side of the new tree.
277+ if (appended[1].table.length > 0) {
278+ b.table[0] = appended[1];
279+ b.lengths[0] = length(appended[1]);
280+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
281+ len += length(b.table[i]);
282+ b.lengths[i] = len;
283+ }
284+ } else {
285+ b.table.shift();
286+ for (var i = 1; i < b.lengths.length; i++) {
287+ b.lengths[i] = b.lengths[i] - b.lengths[0];
288+ }
289+ b.lengths.shift();
290+ }
291+
292+ // Check if balancing is needed and return based on that.
293+ var toRemove = calcToRemove(a, b);
294+ if (toRemove <= E || b.table.length == 0) {
295+ return [a,b];
296+ }
297+
298+ return shuffle(a, b, toRemove);
299+ }
300+
301+ // Returns the extra search steps for E. Refer to the paper.
302+ function calcToRemove(a, b) {
303+ var subLengths = 0;
304+ for (var i = 0; i < a.table.length; i++) {
305+ subLengths += a.table[i].table.length;
306+ }
307+ for (var i = 0; i < b.table.length; i++) {
308+ subLengths += b.table[i].table.length;
309+ }
310+
311+ var toRemove = a.table.length + b.table.length
312+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
313+ }
314+
315+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
316+ function get2(a, b, index) {
317+ return index < a.length ? a[index] : b[index - a.length];
318+ }
319+
320+ function set2(a, b, index, value) {
321+ if (index < a.length) {
322+ a[index] = value;
323+ } else {
324+ b[index - a.length] = value;
325+ }
326+ }
327+
328+ function saveSlot(a, b, index, slot) {
329+ set2(a.table, b.table, index, slot);
330+
331+ var l = (index == 0 || index == a.lengths.length) ?
332+ 0 : get2(a.lengths, a.lengths, index - 1);
333+ set2(a.lengths, b.lengths, index, l + length(slot));
334+ }
335+
336+ // Creates a node or leaf with a given length at their arrays for perfomance.
337+ // Is only used by shuffle.
338+ function createNode(h, length) {
339+ if (length < 0) { length = 0; }
340+ var a = { ctor:"_Array", height:h, table:new Array(length) };
341+ if (h > 0) {
342+ a.lengths = new Array(length);
343+ }
344+ return a;
345+ }
346+
347+ // Returns an array of two balanced nodes.
348+ function shuffle(a, b, toRemove) {
349+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
350+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
351+
352+ // Skip the slots with size M. More precise: copy the slot references
353+ // to the new node
354+ var read = 0;
355+ while (get2(a.table, b.table, read).table.length % M == 0) {
356+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
357+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
358+ read++;
359+ }
360+
361+ // Pulling items from left to right, caching in a slot before writing
362+ // it into the new nodes.
363+ var write = read;
364+ var slot = new createNode(a.height - 1, 0);
365+ var from = 0;
366+
367+ // If the current slot is still containing data, then there will be at
368+ // least one more write, so we do not break this loop yet.
369+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
370+ // Find out the max possible items for copying.
371+ var source = get2(a.table, b.table, read);
372+ var to = Math.min(M - slot.table.length, source.table.length)
373+
374+ // Copy and adjust size table.
375+ slot.table = slot.table.concat(source.table.slice(from, to));
376+ if (slot.height > 0) {
377+ var len = slot.lengths.length;
378+ for (var i = len; i < len + to - from; i++) {
379+ slot.lengths[i] = length(slot.table[i]);
380+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
381+ }
382+ }
383+
384+ from += to;
385+
386+ // Only proceed to next slots[i] if the current one was
387+ // fully copied.
388+ if (source.table.length <= to) {
389+ read++; from = 0;
390+ }
391+
392+ // Only create a new slot if the current one is filled up.
393+ if (slot.table.length == M) {
394+ saveSlot(newA, newB, write, slot);
395+ slot = createNode(a.height - 1,0);
396+ write++;
397+ }
398+ }
399+
400+ // Cleanup after the loop. Copy the last slot into the new nodes.
401+ if (slot.table.length > 0) {
402+ saveSlot(newA, newB, write, slot);
403+ write++;
404+ }
405+
406+ // Shift the untouched slots to the left
407+ while (read < a.table.length + b.table.length ) {
408+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
409+ read++; write++;
410+ }
411+
412+ return [newA, newB];
413+ }
414+
415+ // Navigation functions
416+ function botRight(a) { return a.table[a.table.length - 1]; }
417+ function botLeft(a) { return a.table[0]; }
418+
419+ // Copies a node for updating. Note that you should not use this if
420+ // only updating only one of "table" or "lengths" for performance reasons.
421+ function nodeCopy(a) {
422+ var newA = { ctor:"_Array", height:a.height
423+ , table:a.table.slice() };
424+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
425+ return newA;
426+ }
427+
428+ // Returns how many items are in the tree.
429+ function length(array) {
430+ if (array.height == 0) {
431+ return array.table.length;
432+ } else {
433+ return array.lengths[array.lengths.length - 1];
434+ }
435+ }
436+
437+ // Calculates in which slot of "table" the item probably is, then
438+ // find the exact slot via forward searching in "lengths". Returns the index.
439+ function getSlot(i, a) {
440+ var slot = Math.floor(i / (Math.pow(M, a.height)));
441+ while (a.lengths[slot] <= i) {
442+ slot++
443+ }
444+ return slot;
445+ }
446+
447+ // Recursively creates a tree with a given height containing
448+ // only the given item.
449+ function create(item, h) {
450+ if (h == 0) {
451+ return { ctor:"_Array", height:0
452+ , table:[item] };
453+ } else {
454+ return { ctor:"_Array", height:h
455+ , table:[create(item, h - 1)]
456+ , lengths:[1] };
457+ }
458+ }
459+
460+ // Recursively creates a tree that contains the given tree.
461+ function parentise(tree, h) {
462+ if (h == tree.height) {
463+ return tree;
464+ } else {
465+ return { ctor:"_Array", height:h
466+ , table:[parentise(tree, h - 1)]
467+ , lengths:[length(tree)] };
468+ }
469+ }
470+
471+ // Emphasizes blood brotherhood beneath two trees.
472+ function siblise(a, b) {
473+ return { ctor:"_Array", height:a.height + 1
474+ , table:[a, b]
475+ , lengths:[length(a), length(a) + length(b)] };
476+ }
477+
478+ Elm.Native.Array.values = {
479+ empty:empty,
480+ toList:toList,
481+ append:F2(append),
482+ push:F2(push),
483+ slice:F3(slice),
484+ get:F2(get),
485+ set:F3(set),
486+ map:F2(map),
487+ indexedMap:F2(indexedMap),
488+ foldl:F3(foldl),
489+ foldr:F3(foldr),
490+ length:length
491+ };
492+
493+ return elm.Native.Array.values = Elm.Native.Array.values;
494+}