@@ -1,4 +1,4 @@
1−Elm.Native.Array = {};
1+Elm.Native.Array = {};
2Elm.Native.Array.make = function(elm) {
2Elm.Native.Array.make = function(elm) {
3 elm.Native = elm.Native || {};
3 elm.Native = elm.Native || {};
4 elm.Native.Array = elm.Native.Array || {};
4 elm.Native.Array = elm.Native.Array || {};
@@ -24,38 +24,144 @@ Elm.Native.Array.make = function(elm) {
24 // An empty array.
24 // An empty array.
25 var empty = { ctor:"_Array", height:0, table:new Array() };
25 var empty = { ctor:"_Array", height:0, table:new Array() };
27− // Gets the value at index i recursively.
28− function get(i, a) {
27+ function get(i, array) {
30− if (i < a.table.length) {
28+ if (i < 0 || i >= length(array)) {
33− throw new Error("Index "+ i +" is out of range. Check the length of your array first or use getSafe or getMaybe.");
29+ throw new Error("Index " + i + " is out of range. Check the length of " +
30+ "your array first or use getMaybe or getWithDefault.");
32+ return unsafeGet(i, array);
37− var slot = getSlot(i, a);
35+ function unsafeGet(i, array) {
36+ for (var x = array.height; x > 0; x--) {
37+ var slot = i >> (x * 5);
38− var sub = slot > 0 ? a.lengths[slot-1] : 0;
39+ while (array.lengths[slot - 1] > i) { slot--; }
40+ i -= array.lengths[slot - 1];
42+ array = array.table[slot];
39− return get(i - sub, a.table[slot]);
44+ return array.table[i];
42 // Sets the value at the index i. Only the nodes leading to i will get
47 // Sets the value at the index i. Only the nodes leading to i will get
43 // copied and updated.
48 // copied and updated.
44− function set(i, item, a) {
49+ function set(i, item, array) {
45− if (length(a) <= i) {
50+ if (i < 0 || length(array) <= i) {
46− return a;
51+ return array;
48− var newA = nodeCopy(a);
53+ return unsafeSet(i, item, array);
49− newA.table = a.table.slice();
54+ }
56+ function unsafeSet(i, item, array) {
57+ array = nodeCopy(array);
51− if (a.height == 0) {
59+ if (array.height == 0) {
52− newA.table[i] = item;
60+ array.table[i] = item;
54− var slot = getSlot(i, a);
62+ var slot = getSlot(i, array);
55− var sub = slot > 0 ? a.lengths[slot-1] : 0;
63+ if (slot > 0) {
56− newA.table[slot] = set(i - sub, item, a.table[slot]);
64+ i -= array.lengths[slot - 1];
66+ array.table[slot] = unsafeSet(i, item, array.table[slot]);
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);
77+ function initialize_(f, h, from, to) {
79+ var table = new Array((to - from) % (M + 1));
80+ for (var i = 0; i < table.length; i++) {
81+ table[i] = f(from + i);
83+ return { ctor:"_Array", height:0, table:table };
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);
94+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
97+ function fromList(list) {
98+ if (list == List.Nil) { return empty; }
100+ // Allocate M sized blocks (table) and write list elements to it.
101+ var table = new Array(M);
102+ var nodes = new Array();
105+ while (list.ctor !== '[]') {
110+ // table is full, so we can push a leaf containing it into the
113+ fromListPush({ ctor:"_Array", height:0, table:table }
115+ table = new Array(M);
120+ // Maybe there is something left on the table.
122+ fromListPush({ ctor:"_Array", height:0, table:table.splice(0,i) }
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);
133+ var head = nodes[nodes.length - 1];
134+ if (head.height > 0 && head.table.length == 1) {
135+ return head.table[0];
141+ // Push a node into a higher node as a child.
142+ function fromListPush(toPush, nodes) {
143+ var h = toPush.height;
145+ // Maybe the node on this height does not exist.
146+ if (nodes.length == h) {
147+ nodes.push({ ctor:"_Array", height:h + 1
149+ , lengths:new Array() });
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];
157+ nodes[h].lengths.push(len);
159+ if (nodes[h].table.length == M) {
160+ fromListPush(nodes[h], nodes);
161+ nodes[h] = { ctor:"_Array", height:h + 1
163+ , lengths:new Array() };
61 // Pushes an item via push_ to the bottom right of a tree.
167 // Pushes an item via push_ to the bottom right of a tree.
@@ -65,7 +171,7 @@ Elm.Native.Array.make = function(elm) {
65 return pushed;
171 return pushed;
68− newTree = create(item, a.height);
174+ var newTree = create(item, a.height);
69 return siblise(a, newTree);
175 return siblise(a, newTree);
@@ -143,22 +249,19 @@ Elm.Native.Array.make = function(elm) {
145 function foldl(f, b, a) {
251 function foldl(f, b, a) {
146− for (var i = a.table.length - 1; i >= 0; i--) {
252+ for (var i = 0; i < a.table.length; i++) {
147 b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
253 b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
149 return b;
255 return b;
152 function foldr(f, b, a) {
258 function foldr(f, b, a) {
153− for (var i = 0; i < a.table.length; i++) {
259+ for (var i = a.table.length; i--; ) {
154 b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
260 b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
156 return b;
262 return 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
265 // TODO: currently, it slices the right, then the left. This can be
163 // optimized.
266 // optimized.
164 function slice(from, to, a) {
267 function slice(from, to, a) {
@@ -175,7 +278,7 @@ Elm.Native.Array.make = function(elm) {
175 // Handle leaf level.
278 // Handle leaf level.
176 if (a.height == 0) {
279 if (a.height == 0) {
177 var newA = { ctor:"_Array", height:0 };
280 var newA = { ctor:"_Array", height:0 };
178− newA.table = a.table.slice(0, to + 1);
281+ newA.table = a.table.slice(0, to);
179 return newA;
282 return newA;
@@ -232,15 +335,15 @@ Elm.Native.Array.make = function(elm) {
232 return newA;
335 return newA;
235− // Concats two trees.
338+ // Appends two trees.
236− // TODO: Add support for concatting trees of different sizes. Current
339+ // TODO: Add support for appending trees of different sizes. Current
237− // behavior will just rise the lower tree and then concat them.
340+ // behavior will just rise the lower tree and then append them.
238− function concat(a, b) {
341+ function append(a,b) {
239− if (b.height > a.height) { return concat(parentise(a, b.height), b); }
342+ if (b.height > a.height) { return append(parentise(a, b.height), b); }
240− if (a.height > b.height) { return concat(a, parentise(b, a.height)); }
343+ if (a.height > b.height) { return append(a, parentise(b, a.height)); }
241− if (a.height == 0) { return concat(parentise(a, 1), parentise(b, 1)); }
344+ if (a.height == 0) { return append(parentise(a, 1), parentise(b, 1)); }
243− var c = concat_(a, b);
346+ var c = append_(a, b);
244 if (c[1].table.length > 0) {
347 if (c[1].table.length > 0) {
245 return siblise(c[0], c[1]);
348 return siblise(c[0], c[1]);
@@ -249,9 +352,9 @@ Elm.Native.Array.make = function(elm) {
251 // Returns an array of two nodes. The second node _may_ be empty. This case
354 // 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
355+ // needs to be handled by the function, that called append_. May be only
253 // called for trees with an minimal height of 1.
356 // called for trees with an minimal height of 1.
254− function concat_(a, b) {
357+ function append_(a, b) {
255 if (a.height == 1) {
358 if (a.height == 1) {
256 // Check if balancing is needed and return based on that.
359 // Check if balancing is needed and return based on that.
257 var toRemove = calcToRemove(a, b);
360 var toRemove = calcToRemove(a, b);
@@ -262,18 +365,18 @@ Elm.Native.Array.make = function(elm) {
262 return shuffle(a, b, toRemove);
365 return shuffle(a, b, toRemove);
265− var concated = concat_(botRight(a), botLeft(b));
368+ var appended = append_(botRight(a), botLeft(b));
266 a = nodeCopy(a), b = nodeCopy(b);
369 a = nodeCopy(a), b = nodeCopy(b);
268 // Adjust the bottom right side of the new tree.
371 // Adjust the bottom right side of the new tree.
269− a.table[a.table.length - 1] = concated[0];
372+ a.table[a.table.length - 1] = appended[0];
270− a.lengths[a.lengths.length - 1] = length(concated[0])
373+ a.lengths[a.lengths.length - 1] = length(appended[0])
271 a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
374 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.
376 // Adjust the bottom left side of the new tree.
274− if (concated[1].table.length > 0) {
377+ if (appended[1].table.length > 0) {
275− b.table[0] = concated[1];
378+ b.table[0] = appended[1];
276− b.lengths[0] = length(concated[1]);
379+ b.lengths[0] = length(appended[1]);
277 for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
380 for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
278 len += length(b.table[i]);
381 len += length(b.table[i]);
279 b.lengths[i] = len;
382 b.lengths[i] = len;
@@ -423,21 +526,19 @@ Elm.Native.Array.make = function(elm) {
425 // Returns how many items are in the tree.
528 // Returns how many items are in the tree.
426− function length(a) {
529+ function length(array) {
427− if (a.height == 0) {
530+ if (array.height == 0) {
428− return a.table.length;
531+ return array.table.length;
430− return a.lengths[a.lengths.length - 1];
533+ return array.lengths[array.lengths.length - 1];
434 // Calculates in which slot of "table" the item probably is, then
537 // Calculates in which slot of "table" the item probably is, then
435 // find the exact slot via forward searching in "lengths". Returns the index.
538 // find the exact slot via forward searching in "lengths". Returns the index.
436 function getSlot(i, a) {
539 function getSlot(i, a) {
437− var slot = Math.floor(i / (Math.pow(M, a.height)));
540+ var slot = i >> (5 * a.height);
438− while (a.lengths[slot] <= i) {
541+ while (a.lengths[slot - 1] > i) { slot--; }
441 return slot;
542 return slot;
@@ -472,10 +573,52 @@ Elm.Native.Array.make = function(elm) {
472 , lengths:[length(a), length(a) + length(b)] };
573 , lengths:[length(a), length(a) + length(b)] };
576+ function toJSArray(a) {
577+ var jsArray = new Array(length(a));
578+ toJSArray_(jsArray, 0, a);
582+ function toJSArray_(jsArray, i, a) {
583+ for (var t = 0; t < a.table.length; t++) {
584+ if (a.height == 0) {
585+ jsArray[i + t] = a.table[t];
587+ var inc = t == 0 ? 0 : a.lengths[t - 1];
588+ toJSArray_(jsArray, i + inc, a.table[t]);
593+ function fromJSArray(jsArray) {
594+ if (jsArray.length == 0) { return empty; }
595+ var h = Math.floor(Math.log(jsArray.length) / Math.log(M));
596+ return fromJSArray_(jsArray, h, 0, jsArray.length);
599+ function fromJSArray_(jsArray, h, from, to) {
601+ return { ctor:"_Array", height:0
602+ , table:jsArray.slice(from, to) };
605+ var step = Math.pow(M, h);
606+ var table = new Array(Math.ceil((to - from) / step));
607+ var lengths = new Array(table.length);
608+ for (var i = 0; i < table.length; i++) {
609+ table[i] = fromJSArray_( jsArray, h - 1, from + (i * step)
610+ , Math.min(from + ((i + 1) * step), to));
611+ lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
613+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
475 Elm.Native.Array.values = {
616 Elm.Native.Array.values = {
476 empty:empty,
617 empty:empty,
477 toList:toList,
619 toList:toList,
478− concat:F2(concat),
620+ initialize:F2(initialize),
479 push:F2(push),
622 push:F2(push),
480 slice:F3(slice),
623 slice:F3(slice),
481 get:F2(get),
624 get:F2(get),
@@ -484,7 +627,10 @@ Elm.Native.Array.make = function(elm) {
484 indexedMap:F2(indexedMap),
627 indexedMap:F2(indexedMap),
485 foldl:F3(foldl),
628 foldl:F3(foldl),
486 foldr:F3(foldr),
629 foldr:F3(foldr),
487− length:length
630+ length:length,
632+ toJSArray:toJSArray,
633+ fromJSArray:fromJSArray
490 return elm.Native.Array.values = Elm.Native.Array.values;
636 return elm.Native.Array.values = Elm.Native.Array.values;