on master help sign in
Merge pull request #737 from Xashili/fixAppend
84b8834Evan Czaplicki
libraries/Native/Array.js+88−42
@@ -358,70 +358,116 @@ Elm.Native.Array.make = function(elm) {
358 }
358 }
359
359
360 // Appends two trees.
360 // Appends two trees.
361− // TODO: Add support for appending trees of different sizes. Current
362− // behavior will just rise the lower tree and then append them.
363 function append(a,b) {
361 function append(a,b) {
364− if (a.table.length == 0) return b;
362+ if (a.table.length === 0) {
363+ return b;
364+ }
365− if (b.table.length == 0) return a;
365+ if (b.table.length === 0) {
366− if (b.height > a.height) return append(parentise(a, b.height), b);
366+ return a;
367− if (a.height > b.height) return append(a, parentise(b, a.height));
367+ }
368− if (a.height == 0) return append(parentise(a, 1), parentise(b, 1));
369
368
370 var c = append_(a, b);
369 var c = append_(a, b);
370+
371+ // Check if both nodes can be crunshed together.
372+ if (c[0].table.length + c[1].table.length <= M) {
373+ if (c[0].table.length === 0) {
374+ return c[1];
375+ }
371− if (c[1].table.length > 0) {
376+ if (c[1].table.length === 0) {
372− return siblise(c[0], c[1]);
377+ return c[0];
373− } else {
378+ }
379+
380+ // Adjust .table and .lengths
381+ c[0].table = c[0].table.concat(c[1].table);
382+ if (c[0].height > 0) {
383+ var len = length(c[0]);
384+ for (var i = 0; i < c[1].lengths.length; i++) {
385+ c[1].lengths[i] += len;
386+ }
387+ c[0].lengths = c[0].lengths.concat(c[1].lengths);
388+ }
389+
374 return c[0];
390 return c[0];
375 }
391 }
392+
393+ var toRemove = calcToRemove(a, b);
394+ if (toRemove > E) {
395+ c = shuffle(c[0], c[1], toRemove);
396+ }
397+
398+ return siblise(c[0], c[1]);
376 }
399 }
377
400
378− // Returns an array of two nodes. The second node _may_ be empty. This case
401+ // Returns an array of two nodes; right and left. One node _may_ be empty.
379− // needs to be handled by the function, that called append_. May be only
380− // called for trees with an minimal height of 1.
381 function append_(a, b) {
402 function append_(a, b) {
382− if (a.height == 1) {
403+ if (a.height === 0 && b.height === 0) {
383− // Check if balancing is needed and return based on that.
384− var toRemove = calcToRemove(a, b);
385− if (toRemove <= E) {
386− return [a,b];
404+ return [a, b];
387− }
388−
389− return shuffle(a, b, toRemove);
390 }
405 }
391
406
407+ if (a.height !== 1 || b.height !== 1) {
408+ if (a.height === b.height) {
409+ a = nodeCopy(a);
410+ b = nodeCopy(b);
392− var appended = append_(botRight(a), botLeft(b));
411+ var appended = append_(botRight(a), botLeft(b));
393− a = nodeCopy(a), b = nodeCopy(b);
394
412
395− // Adjust the bottom right side of the new tree.
413+ insertRight(a, appended[1]);
396− a.table[a.table.length - 1] = appended[0];
414+ insertLeft(b, appended[0]);
397− a.lengths[a.lengths.length - 1] = length(appended[0])
415+ } else if (a.height > b.height) {
398− a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
416+ a = nodeCopy(a);
417+ var appended = append_(botRight(a), b);
399
418
400− // Adjust the bottom left side of the new tree.
401− if (appended[1].table.length > 0) {
402− b.table[0] = appended[1];
419+ insertRight(a, appended[0]);
403− b.lengths[0] = length(appended[1]);
420+ b = parentise(appended[1], appended[1].height + 1);
404− for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
405− len += length(b.table[i]);
406− b.lengths[i] = len;
407− }
408− } else {
421+ } else {
409− b.table.shift();
422+ b = nodeCopy(b);
410− for (var i = 1; i < b.lengths.length; i++) {
423+ var appended = append_(a, botLeft(b));
411− b.lengths[i] = b.lengths[i] - b.lengths[0];
424+
425+ var left = appended[0].table.length === 0 ? 0 : 1;
426+ var right = left === 0 ? 1 : 0;
427+ insertLeft(b, appended[left]);
428+ a = parentise(appended[right], appended[right].height + 1);
412 }
429 }
413− b.lengths.shift();
414 }
430 }
415
431
416 // Check if balancing is needed and return based on that.
432 // Check if balancing is needed and return based on that.
417− var toRemove = calcToRemove(a, b);
418− if (toRemove <= E || b.table.length == 0) {
433+ if (a.table.length === 0 || b.table.length === 0) {
419 return [a,b];
434 return [a,b];
420 }
435 }
421
436
437+ var toRemove = calcToRemove(a, b);
438+ if (toRemove <= E) {
439+ return [a,b];
440+ }
422 return shuffle(a, b, toRemove);
441 return shuffle(a, b, toRemove);
423 }
442 }
424
443
444+ // Helperfunctions for append_. Replaces a child node at the side of the parent.
445+ function insertRight(parent, node) {
446+ var index = parent.table.length - 1;
447+ parent.table[index] = node;
448+ parent.lengths[index] = length(node)
449+ parent.lengths[index] += index > 0 ? parent.lengths[index - 1] : 0;
450+ }
451+
452+ function insertLeft(parent, node) {
453+ if (node.table.length > 0) {
454+ parent.table[0] = node;
455+ parent.lengths[0] = length(node);
456+
457+ var len = length(parent.table[0]);
458+ for (var i = 1; i < parent.lengths.length; i++) {
459+ len += length(parent.table[i]);
460+ parent.lengths[i] = len;
461+ }
462+ } else {
463+ parent.table.shift();
464+ for (var i = 1; i < parent.lengths.length; i++) {
465+ parent.lengths[i] = parent.lengths[i] - parent.lengths[0];
466+ }
467+ parent.lengths.shift();
468+ }
469+ }
470+
425 // Returns the extra search steps for E. Refer to the paper.
471 // Returns the extra search steps for E. Refer to the paper.
426 function calcToRemove(a, b) {
472 function calcToRemove(a, b) {
427 var subLengths = 0;
473 var subLengths = 0;
tests/libraries/Test/Array.elm+10
@@ -7,6 +7,15 @@ import Native.Array
7import ElmTest.Assertion (..)
7import ElmTest.Assertion (..)
8import ElmTest.Test (..)
8import ElmTest.Test (..)
9
9
10+mergeSplit : Int -> Array.Array a -> Array.Array a
11+mergeSplit n arr =
12+ let left = Array.slice 0 n arr
13+ right = Array.slice n (Array.length arr) arr
14+ in Array.append left right
15+
16+holeArray : Array.Array Int
17+holeArray = foldl mergeSplit (Array.fromList [0..100]) [0..100]
18+
10tests : Test
19tests : Test
11tests =
20tests =
12 let creationTests = suite "Creation"
21 let creationTests = suite "Creation"
@@ -28,6 +37,7 @@ tests =
28 , test "append" <| assertEqual [42,42,81,81,81] (Array.toList (Array.append (Array.repeat 2 42) (Array.repeat 3 81)))
37 , test "append" <| assertEqual [42,42,81,81,81] (Array.toList (Array.append (Array.repeat 2 42) (Array.repeat 3 81)))
29 , test "appendEmpty 1" <| assertEqual [1..33] (Array.toList (Array.append Array.empty (Array.fromList [1..33])))
38 , test "appendEmpty 1" <| assertEqual [1..33] (Array.toList (Array.append Array.empty (Array.fromList [1..33])))
30 , test "appendEmpty 2" <| assertEqual [1..33] (Array.toList (Array.append (Array.fromList [1..33]) Array.empty))
39 , test "appendEmpty 2" <| assertEqual [1..33] (Array.toList (Array.append (Array.fromList [1..33]) Array.empty))
40+ , test "appendAndSlice" <| assertEqual [0..100] (Array.toList holeArray)
31 ]
41 ]
32 getAndSetTests = suite "Get and Set"
42 getAndSetTests = suite "Get and Set"
33 [ test "get" <| assertEqual (Just 2) (Array.get 1 (Array.fromList [3,2,1]))
43 [ test "get" <| assertEqual (Just 2) (Array.get 1 (Array.fromList [3,2,1]))