aoc-2021 day 18; experimented with "trait interfaces"
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 30 Sep 2022 02:20:08 +0000 (21:20 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 30 Sep 2022 02:20:08 +0000 (21:20 -0500)
compiler/src/checker.c
compiler/src/symres.c
core/conv.onyx
core/hash.onyx
tests/aoc-2020/day17.onyx
tests/aoc-2021/day18 [new file with mode: 0644]
tests/aoc-2021/day18.onyx [new file with mode: 0644]
tests/aoc-2021/input/day18.txt [new file with mode: 0644]

index ba04efe94bdbcbaeb453177e8cb48e9d1c8a8828..67b88746468ae45bdd7d23522a1db7d515ecf4a8 100644 (file)
@@ -781,10 +781,12 @@ static void report_bad_binaryop(AstBinaryOp* binop) {
 static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) {
     if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
 
-    if (binop->overload_args == NULL) {
-        binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
-        bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
-        bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
+    if (binop->overload_args == NULL || binop->overload_args->values[1] == NULL) {
+        if (binop->overload_args == NULL) {
+            binop->overload_args = bh_alloc_item(context.ast_alloc, Arguments);
+            bh_arr_new(context.ast_alloc, binop->overload_args->values, 3);
+            bh_arr_set_length(binop->overload_args->values, third_argument ? 3 : 2);
+        }
 
         if (binop_is_assignment(binop->operation)) {
             binop->overload_args->values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
index 78ba5cc218750b7e2af42b21a0826e72c7a0f809..54a14cf4bd5ce8f89798c2e6073e38fee0c7a1e3 100644 (file)
@@ -357,7 +357,7 @@ static SymresStatus symres_field_access(AstFieldAccess** fa) {
         }
     }
     else if (force_a_lookup) {
-        if (context.cycle_detected) {
+        if (context.cycle_detected || context.cycle_almost_detected) {
             onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' does not exist here. This is a bad error message.",
                 (*fa)->token->text,
                 (*fa)->token->length);
@@ -1565,14 +1565,46 @@ static SymresStatus symres_constraint(AstConstraint* constraint) {
         }
 
         case Constraint_Phase_Checking_Expressions: {
+            SymresStatus ss;
+            onyx_errors_disable();
+
             fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
-                SYMRES(expression, &constraint->exprs[i].expr);
+                InterfaceConstraint* ic = &constraint->exprs[i];
+
+                // Most of this logic was directly copied from the
+                // check_constraint code. There might be a better
+                // way to factor this?
+                ss = symres_expression(&ic->expr);
+                if (ss == Symres_Yield_Macro) {
+                    onyx_errors_enable();
+                    return ss;
+                }
+
+                if (ss == Symres_Error && !ic->invert_condition) {
+                    goto constraint_error;
+                }
+
+                if (ss == Symres_Success && ic->invert_condition) {
+                    goto constraint_error;
+                }
 
-                if (constraint->exprs[i].expected_type_expr) {
-                    SYMRES(type, &constraint->exprs[i].expected_type_expr);
+                if (ic->expected_type_expr) {
+                    ss = symres_type(&ic->expected_type_expr);
+                    if (ss == Symres_Yield_Macro) {
+                        onyx_errors_enable();
+                        return ss;
+                    }
                 }
+
+                continue;
+
+              constraint_error:
+                onyx_errors_enable();
+                *constraint->report_status = Constraint_Check_Status_Failed;
+                return Symres_Error;
             }
 
+            onyx_errors_enable();
             return Symres_Success;
         }
     }
@@ -1762,6 +1794,7 @@ void symres_entity(Entity* ent) {
     if (ss == Symres_Yield_Micro) ent->micro_attempts++;
     if (ss == Symres_Complete)    ent->state = Entity_State_Finalized;
     if (ss == Symres_Goto_Parse)  ent->state = Entity_State_Parse;
+    if (ss == Symres_Error)       ent->state = Entity_State_Failed;
     if (ss == Symres_Success) {
         ent->macro_attempts = 0;
         ent->micro_attempts = 0;
index 8a8d0c0182ff5a126869ff058b6f3ef7c1b06b8e..4470c7e6a394dd62f9354216eb5b8e02d065faa8 100644 (file)
@@ -54,61 +54,82 @@ Custom_Parse :: struct {
     parse: (rawptr, str, Allocator) -> bool;
 }
 
-str_to_i64 :: (s: str, base: u32 = 10) -> i64 {
+str_to_i64 :: #match #local {}
+
+#overload
+str_to_i64 :: macro (s: str, base: u32 = 10) -> i64 {
+    str_to_i64 :: str_to_i64;
+    s_ := s;
+    return str_to_i64(^s_, base);
+}
+
+#overload
+str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 {
     use package core
 
     value: i64 = 0;
     mul := 1;
 
-    if s[0] == #char "-" {
+    if s.data[0] == #char "-" {
         mul = -1;
-        s = string.advance(s, 1);
+        string.advance(s, 1);
     }
 
-    if s[0] == #char "+" {
-        s = string.advance(s, 1);
+    if s.data[0] == #char "+" {
+         string.advance(s, 1);
     }
 
-    for c: s do switch c {
-        case #char "0" .. #char "9" {
-            value *= ~~base;
-            value += ~~(c - #char "0");
-        }
+    while !string.empty(*s) {
+        switch c := s.data[0]; c {
+            case #char "0" .. #char "9" {
+                value *= ~~base;
+                value += ~~(c - #char "0");
+            }
 
-        case #char "A" .. #char "Z" {
-            if base <= 10 do fallthrough;
+            case #char "A" .. #char "Z" {
+                if base <= 10 do fallthrough;
 
-            value *= ~~base;
-            value += ~~((c - #char "A") + 10);
-        }
-        
-        case #char "a" .. #char "z" {
-            if base <= 10 do fallthrough;
+                value *= ~~base;
+                value += ~~((c - #char "A") + 10);
+            }
+            
+            case #char "a" .. #char "z" {
+                if base <= 10 do fallthrough;
+
+                value *= ~~base;
+                value += ~~((c - #char "a") + 10);
+            }
 
-            value *= ~~base;
-            value += ~~((c - #char "a") + 10);
+            case #default do break break;
         }
 
-        case #default do break break;
+        string.advance(s);
     }
 
     return value * ~~mul;
 }
 
-str_to_f64 :: (s_: str) -> f64 {
+str_to_f64 :: #match #local {}
+
+#overload
+str_to_f64 :: macro (s: str) -> f64 {
+    str_to_f64 :: str_to_f64;
+    s_ := s;
+    return str_to_f64(^s_);
+}
+
+#overload
+str_to_f64 :: (s: ^str) -> f64 {
     use package core
 
-    // 's' needs to live on the stack to take its address. Stupid optimization
-    // that simple structs turn into registers for parameters.
-    s := s_;
-    string.strip_leading_whitespace(^s);
+    string.strip_leading_whitespace(s);
 
-    sign := parse_sign(^s);
-    value, _ := parse_digits(^s);    
+    sign := parse_sign(s);
+    value, _ := parse_digits(s);    
 
-    if s[0] == #char "." {
-        string.advance(^s, 1);
-        fraction, fraction_digits := parse_digits(^s);
+    if s.data[0] == #char "." {
+        string.advance(s, 1);
+        fraction, fraction_digits := parse_digits(s);
         while fraction_digits > 0 {
             fraction_digits -= 1;
             fraction /= 10;
@@ -118,11 +139,11 @@ str_to_f64 :: (s_: str) -> f64 {
 
     value *= sign;
 
-    if s[0] != #char "e" && s[0] != #char "E" do return value;
-    string.advance(^s, 1);
+    if s.data[0] != #char "e" && s.data[0] != #char "E" do return value;
+    string.advance(s, 1);
 
-    exponent_sign := parse_sign(^s);
-    exponent, _   := parse_digits(^s);
+    exponent_sign := parse_sign(s);
+    exponent, _   := parse_digits(s);
     if exponent_sign > 0 {
         while exponent > 0 {
             value *= 10;
index baa671251e4da2fb85e7aa73669e3191ea844054..fa4a30dca20c5cbf6e6cf4495aa78821fbf6634a 100644 (file)
@@ -11,9 +11,16 @@ to_u32 :: #match {
         for ch: key do hash += (hash << 5) + ~~ch;
         return hash;
     },
-    (key: type_expr) -> u32 do return to_u32(cast(u32) key);
+    (key: type_expr) -> u32 { return to_u32(cast(u32) key); },
+
+    #precedence 10000 macro (key: $T/HasHashMethod) => key->hash()
 }
 
 Hashable :: interface (t: $T) {
     { to_u32(t) } -> u32;
 }
+
+HasHashMethod :: interface (t: $T) {
+    { t->hash() } -> u32;
+}
+
index af534ca83459a40c14c32a9d4f597d446a479368..916bb576b916810a50b2e3cdb142abfefdcb3494 100644 (file)
@@ -1,26 +1,41 @@
 #load "core/std"
 
-use package core
-reader :: package core.string.reader
+use core
+use core.string {reader}
+
+OverloadsEqual :: interface (t: $T) {
+    { T.equals(t, t) } -> bool;
+}
+
+#operator == macro (t: $T/OverloadsEqual, s: T) => t->equals(s);
+
 
 CubePos :: struct {
     x, y, z, w : i32;
 }
 
-CubeState :: struct {
-    alive := false;
-    next  := false;
-}
+#inject CubePos {
+    hash :: (c: CubePos) => {
+        hash: u32 = 7;
+        hash += hash << 5 + core.hash.to_u32(c.x);
+        hash += hash << 5 + core.hash.to_u32(c.y);
+        hash += hash << 5 + core.hash.to_u32(c.z);
+        hash += hash << 5 + core.hash.to_u32(c.w);
+        return hash;
+    }
 
-#match hash.to_u32 (c: CubePos) -> u32 {
-    return 17 * c.x + 13 * c.y + 11 * c.z + 21 * c.w;
+    equals :: (a, b: CubePos) => {
+        return (a.x == b.x)
+            && (a.y == b.y)
+            && (a.z == b.z)
+            && (a.w == b.w);
+    }
 }
 
-#operator == (a: CubePos, b: CubePos) -> bool {
-    return (a.x == b.x)
-        && (a.y == b.y)
-        && (a.z == b.z)
-        && (a.w == b.w);
+
+CubeState :: struct {
+    alive := false;
+    next  := false;
 }
 
 get_neighbor_count :: (cubes: ^Map(CubePos, CubeState), pos: CubePos) -> u32 {
diff --git a/tests/aoc-2021/day18 b/tests/aoc-2021/day18
new file mode 100644 (file)
index 0000000..48106ee
--- /dev/null
@@ -0,0 +1,3 @@
+4864
+i: [[[[2,9],6],[[2,8],9]],[[[0,5],6],[[6,1],7]]]
+j: [[[[9,6],[5,8]],[6,2]],[[[8,0],[7,0]],[[5,6],4]]]
diff --git a/tests/aoc-2021/day18.onyx b/tests/aoc-2021/day18.onyx
new file mode 100644 (file)
index 0000000..09f08a0
--- /dev/null
@@ -0,0 +1,273 @@
+#load "core/std"
+
+PART :: 2
+
+use core
+use core.alloc {arena}
+
+num_allocator: Allocator;
+
+SnailNum :: struct {
+    parent: ^SnailNum;
+    left, right: ^SnailNum;
+
+    left_val, right_val: u32;
+}
+
+
+#inject SnailNum {
+    allocator: Allocator;
+
+    make :: () => {
+        return make(SnailNum, SnailNum.allocator);
+    }
+
+    make_pair :: (left, right: u32) => {
+        n := SnailNum.make();
+        n.left_val = left;
+        n.right_val = right;
+        return n;
+    }
+
+    clone :: (n: ^SnailNum) -> ^SnailNum {
+        if n == null do return null;
+
+        new_num := SnailNum.make();
+        new_num->set_left(SnailNum.clone(n.left));
+        new_num->set_right(SnailNum.clone(n.right));
+        new_num.left_val = n.left_val;
+        new_num.right_val = n.right_val;
+
+        return new_num;
+    }
+
+    add :: (a, b: ^SnailNum) => {
+        if a == null do return b;
+        if b == null do return a;
+
+        new_root := SnailNum.make();
+        new_root->set_left(a);
+        new_root->set_right(b);
+
+        while new_root->reduce() ---
+
+        return new_root;
+    }
+
+    reduce :: (use n: ^SnailNum) -> (reduced_something: bool) {
+        if r, _ := n->reduce_explodes(); r do return true;
+        if r    := n->reduce_splits();   r do return true;
+        return false;
+    }
+
+    reduce_explodes :: (use n: ^SnailNum, depth := 0) -> (reduced_something: bool, zero_node: bool) {
+        if depth <= 3 {
+            did_reduce, zero_node := false, false;
+
+            if left != null {
+                if did_reduce, zero_node = left->reduce_explodes(depth + 1); zero_node {
+                    left = null;
+                }
+            }
+
+            if right != null && !did_reduce {
+                if did_reduce, zero_node = right->reduce_explodes(depth + 1); zero_node {
+                    right = null;
+                }
+            }
+
+            return did_reduce, false;
+        }
+
+        pleft  := n->pair_to_left();
+        pright := n->pair_to_right();
+        if pleft  != null do *pleft += left_val;
+        if pright != null do *pright += right_val;
+
+        left_val = 0;
+        right_val = 0;
+
+        return true, true;
+    }
+
+    reduce_splits :: (use n: ^SnailNum) -> (reduced_something: bool) {
+        if left != null {
+            if left->reduce_splits() {
+                return true;
+            }
+
+        } elseif left_val >= 10 {
+            l1, l2 := split_number(left_val);
+            n->set_left(SnailNum.make_pair(l1, l2));
+            left_val = 0;
+            return true;
+        }
+
+        if right != null {
+            if right->reduce_splits() {
+                return true;
+            }
+
+        } elseif right_val >= 10 {
+            r1, r2 := split_number(right_val);
+            n->set_right(SnailNum.make_pair(r1, r2));
+            right_val = 0;
+            return true;
+        }
+        
+        return false;
+
+        split_number :: (n: u32) -> (u32, u32) {
+            h := n / 2;
+            return h, h + (0 if n % 2 == 0 else 1);
+        }
+    }
+
+    set_left :: (parent, new_left: ^SnailNum) {
+        parent.left_val = 0;
+        parent.left = new_left;
+        if new_left != null do new_left.parent = parent;
+    }
+
+    set_right :: (parent, new_right: ^SnailNum) {
+        parent.right_val = 0;
+        parent.right = new_right;
+        if new_right != null do new_right.parent = parent;
+    }
+
+    pair_to_left :: (n: ^SnailNum) -> ^u32 {
+        while n.parent != null && n.parent.left == n {
+            n = n.parent;
+        }
+
+        if n.parent == null do return null;
+
+        if n.parent.left == null do return ^n.parent.left_val;
+
+        n = n.parent.left;
+
+        while n.right != null {
+            n = n.right;
+        }
+
+        return ^n.right_val;
+    }
+
+    pair_to_right :: (n: ^SnailNum) -> ^u32 {
+        while n.parent != null && n.parent.right == n {
+            n = n.parent;
+        }
+
+        if n.parent == null do return null;
+
+        if n.parent.right == null do return ^n.parent.right_val;
+
+        n = n.parent.right;
+
+        while n.left != null {
+            n = n.left;
+        }
+
+        return ^n.left_val;
+    }
+
+    magnitude :: (use n: ^SnailNum) => {
+        if n == null {
+            return 0;
+        }
+
+        return 3 * (left_val + left->magnitude())
+            +  2 * (right_val + right->magnitude());
+    }
+
+    parse :: (line: ^str) -> ^SnailNum {
+        string.advance(line);  // [
+
+        root := SnailNum.make();
+        if line.data[0] == #char "[" {
+            root->set_left(SnailNum.parse(line));
+        } else {
+            root.left_val = ~~ conv.str_to_i64(line);
+        }
+
+        string.advance(line);  // ,
+
+        if line.data[0] == #char "[" {
+            root->set_right(SnailNum.parse(line));
+        } else {
+            root.right_val = ~~ conv.str_to_i64(line);
+        }
+
+        string.advance(line); // ]
+
+        return root;
+    }
+
+    format :: (output: ^conv.Format_Output, s: ^conv.Format, use n: ^SnailNum) {
+        if left == null && right == null {
+            conv.format(output, "[{},{}]", left_val, right_val);
+        }
+        elseif left == null && right != null {
+            conv.format(output, "[{},{*}]", left_val, right);
+        }
+        elseif left != null && right == null {
+            conv.format(output, "[{*},{}]", left, right_val);
+        }
+        elseif left != null && right != null {
+            conv.format(output, "[{*},{*}]", left, right);
+        }
+    }
+}
+
+
+
+main :: () {
+    num_arena := arena.make(context.allocator, 64 * 1024);
+    SnailNum.allocator = alloc.as_allocator(^num_arena);
+    
+    conv.register_custom_formatter(SnailNum.format);
+
+    for file: os.with_file("./tests/aoc-2021/input/day18.txt") {
+        r := io.reader_make(file);
+
+        #if PART == 1 {
+            s: ^SnailNum = null;
+            
+            for line: r->lines() {
+                n := SnailNum.parse(^line);
+                s = s->add(n);
+            }
+
+            printf("{*}\n", s->magnitude());
+        }
+
+        #if PART == 2 {
+            nums := make([..] ^SnailNum);
+            for line: r->lines() {
+                nums << SnailNum.parse(^line);
+            }
+
+            maximum := 0;
+            max_i, max_j : i32;
+
+            for i: nums.count {
+                for j: nums.count {
+                    if i == j do continue;
+
+                    n1 := nums[i]->clone();
+                    n2 := nums[j]->clone();
+
+                    mag := n1->add(n2)->magnitude();
+                    if mag >= maximum {
+                        maximum = mag;
+                        max_i, max_j = i, j;
+                    }
+                }
+            }
+
+            println(maximum);
+            printf("i: {*}\n", nums[max_i]);
+            printf("j: {*}\n", nums[max_j]);
+        }
+    }
+}
diff --git a/tests/aoc-2021/input/day18.txt b/tests/aoc-2021/input/day18.txt
new file mode 100644 (file)
index 0000000..9b65fac
--- /dev/null
@@ -0,0 +1,100 @@
+[[[[7,1],[0,0]],[6,[8,2]]],[8,[3,8]]]
+[[[3,6],[9,4]],[[[5,9],5],[8,0]]]
+[[[2,2],2],[1,[[1,6],7]]]
+[[[[0,9],7],[[3,2],8]],[6,[7,9]]]
+[[[[4,1],6],[[7,6],[2,2]]],[[[1,1],9],4]]
+[[[8,[3,7]],3],[[4,4],[[9,1],[3,5]]]]
+[[4,[8,2]],[1,[0,5]]]
+[8,[8,7]]
+[[[[2,2],7],[3,[4,5]]],[[4,6],[[2,5],4]]]
+[[[5,5],[[5,1],3]],[[2,[8,2]],[[6,9],[1,5]]]]
+[0,7]
+[[[[5,1],3],[8,[5,3]]],7]
+[[5,[2,[0,6]]],[[[5,5],2],[9,[8,0]]]]
+[[[[3,4],2],0],4]
+[[[[5,3],[2,7]],6],[[4,0],[9,[7,2]]]]
+[[[3,[2,5]],[3,3]],7]
+[[[[5,1],1],[4,8]],[[5,[8,3]],2]]
+[[4,[[8,1],[8,5]]],[[[4,1],0],6]]
+[[[5,5],[5,9]],[0,[[6,8],[0,1]]]]
+[4,[[[7,9],4],0]]
+[[[[0,1],7],[[3,6],5]],[8,[5,[6,1]]]]
+[[[7,7],[8,0]],[6,[8,[7,9]]]]
+[[[9,2],1],6]
+[[[4,4],[2,[5,0]]],[[[2,6],6],[5,[4,3]]]]
+[[2,[[4,7],5]],1]
+[[8,7],[[[2,0],7],[1,[0,3]]]]
+[[9,[[9,3],[9,5]]],[[8,7],[[4,1],[6,5]]]]
+[[3,4],[[9,4],5]]
+[[5,[[8,3],5]],1]
+[[0,[[9,0],[3,2]]],[2,[7,[5,1]]]]
+[[9,[[9,5],[8,6]]],[[4,4],[[3,8],[1,6]]]]
+[[[1,[5,2]],9],[[4,6],[3,[8,0]]]]
+[[1,7],[[1,7],9]]
+[[[[3,4],3],[[7,5],[9,1]]],[[[5,0],[3,0]],[[7,9],6]]]
+[[[7,2],[[1,0],[5,6]]],[[[3,7],[8,9]],6]]
+[[[[1,1],1],[[8,6],[9,8]]],[[[1,8],4],[8,9]]]
+[[[8,9],0],3]
+[[[1,7],[1,[3,9]]],[6,[0,[8,5]]]]
+[[0,5],[6,5]]
+[[[[6,8],[4,5]],[[7,4],6]],[[3,6],5]]
+[[8,[[0,9],8]],[9,[7,[7,9]]]]
+[0,[[[7,1],2],[[0,4],4]]]
+[[0,[[9,1],5]],[1,4]]
+[3,4]
+[[[9,3],[1,3]],[[[4,8],3],[[1,3],[9,0]]]]
+[[[[5,1],7],[[9,2],8]],[[[6,8],[5,4]],[0,1]]]
+[8,[[1,[3,0]],[[7,9],4]]]
+[[[6,4],[[2,9],[9,0]]],[7,[[0,0],3]]]
+[[3,[[9,6],6]],2]
+[[5,[[3,1],[7,5]]],[[[6,7],9],[[4,6],[5,2]]]]
+[[[4,[6,5]],8],[[6,[8,0]],[[9,3],3]]]
+[[[[4,9],[2,8]],9],[[[5,0],0],[[3,4],[2,8]]]]
+[[3,[7,1]],[9,[[1,8],7]]]
+[[9,1],[0,[[0,7],[7,1]]]]
+[[7,[0,[7,6]]],[[[5,3],1],[6,[4,5]]]]
+[8,[[[2,1],[6,9]],[[3,3],[4,6]]]]
+[0,[7,[3,0]]]
+[[[[1,6],3],[5,[8,0]]],[[[6,6],7],1]]
+[[[7,[8,3]],3],[[[2,8],5],[0,[9,5]]]]
+[[[[5,1],4],[[1,2],1]],7]
+[[[3,[7,5]],7],3]
+[[9,[6,[1,1]]],[[[4,1],[2,2]],[[9,5],[7,7]]]]
+[2,7]
+[[[9,[8,6]],[[9,0],[6,5]]],[[[6,7],5],[[7,7],[2,3]]]]
+[[[0,[6,4]],2],[4,[7,[7,5]]]]
+[[[[6,1],[9,1]],[[6,1],9]],[[2,6],0]]
+[[0,[[1,8],[3,5]]],[4,[[8,2],[4,2]]]]
+[[[[9,3],[4,2]],2],[[[2,1],[7,1]],[4,8]]]
+[[[3,[0,2]],3],8]
+[[[4,[4,9]],9],[[[4,4],5],9]]
+[[[[8,2],7],9],[[[1,0],[3,8]],[[7,7],0]]]
+[[[3,2],[9,7]],[[9,[8,2]],[[5,5],3]]]
+[[[7,[3,1]],[[8,3],1]],[[[8,6],[7,0]],4]]
+[[9,[[9,1],5]],[[4,[1,1]],2]]
+[[[[7,4],[0,3]],7],[8,[6,[3,3]]]]
+[5,5]
+[[6,7],[1,[7,[8,1]]]]
+[[1,[0,4]],7]
+[[[4,0],[[0,1],[2,2]]],[9,[[9,9],[3,0]]]]
+[[[6,0],[[8,6],3]],[[5,1],[[8,1],[2,7]]]]
+[[[[8,3],7],5],[9,[[5,1],8]]]
+[[[[4,0],[5,2]],[[0,0],7]],2]
+[[[[0,1],6],2],[[8,2],6]]
+[[[[2,4],1],[[6,7],9]],[[[1,6],9],3]]
+[[5,5],[[8,[7,7]],[5,8]]]
+[[6,[[9,2],[9,7]]],[[[8,5],[4,4]],7]]
+[[[9,[7,7]],[6,0]],[7,[[8,7],[1,2]]]]
+[[7,[6,2]],[[9,[5,2]],[1,4]]]
+[[[7,[5,9]],[[3,9],[4,5]]],[0,6]]
+[[9,[8,[2,2]]],[[9,7],[1,1]]]
+[[[[2,3],4],[[4,8],9]],[[9,[8,6]],[[0,9],0]]]
+[[0,[[9,3],0]],[8,8]]
+[[[[2,9],6],[[2,8],9]],[[[0,5],6],[[6,1],7]]]
+[[9,[[8,3],[5,8]]],[[7,[3,0]],3]]
+[[[4,[4,2]],0],1]
+[[[[9,6],[5,8]],[6,2]],[[[8,0],[7,0]],[[5,6],4]]]
+[[[8,0],[[4,3],[7,4]]],[[3,[7,9]],[[7,3],6]]]
+[[3,[5,[0,3]]],[5,4]]
+[[[[1,2],[6,3]],1],[[7,[5,2]],[[8,8],7]]]
+[[4,[[8,0],[7,1]]],[[8,[8,0]],[[1,5],3]]]