/-! This does some basic unit tests for the pushProj pass in LCNF  -/


/--
trace: [Compiler.pushProj] size: 5
    def test1 a : tobj :=
      cases a : tobj
      | Option.none =>
        let _x.1 : tagged := 0;
        return _x.1
      | Option.some =>
        let val.2 : tobj := oproj[0] a;
        return val.2
[Compiler.pushProj] size: 6
    def test1 @&a : tobj :=
      cases a : tobj
      | Option.none =>
        let _x.1 : tagged := 0;
        return _x.1
      | Option.some =>
        let val.2 : tobj := oproj[0] a;
        inc val.2;
        return val.2
[Compiler.pushProj] size: 2
    def test1._boxed a : tobj :=
      let res : tobj := test1 a;
      dec a;
      return res
-/
#guard_msgs in
set_option pp.letVarTypes true in
set_option trace.Compiler.pushProj true in
def test1 (a : Option Nat) : Nat :=
  match a with
  | some a => a
  | none => 0


/--
trace: [Compiler.pushProj] size: 10
    def test2 a b : tobj :=
      cases a : tobj
      | Option.none =>
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          return a
        | Option.some =>
          let val.1 : tobj := oproj[0] a;
          let val.2 : tobj := oproj[0] b;
          let _x.3 : tobj := Nat.add val.1 val.2;
          let _x.4 : obj := ctor_1[Option.some] _x.3;
          return _x.4
[Compiler.pushProj] size: 27
    def test2 @&a b : tobj :=
      cases a : tobj
      | Option.none =>
        dec b;
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          inc[ref] a;
          return a
        | Option.some =>
          let val.1 : tobj := oproj[0] a;
          let val.2 : tobj := oproj[0] b;
          jp resetjp.3 _x.4 isShared.5 : tobj :=
            let _x.6 : tobj := Nat.add val.1 val.2;
            dec val.2;
            jp reusejp.7 _x.8 : tobj :=
              return _x.8;
            cases isShared.5 : tobj
            | Bool.false =>
              oset _x.4 [0] := _x.6;
              goto reusejp.7 _x.4
            | Bool.true =>
              let reuseFailAlloc.9 : obj := ctor_1[Option.some] _x.6;
              goto reusejp.7 reuseFailAlloc.9;
          let isSharedCheck.10 : UInt8 := isShared b;
          cases isSharedCheck.10 : tobj
          | Bool.false =>
            goto resetjp.3 b isSharedCheck.10
          | Bool.true =>
            inc val.2;
            dec b;
            goto resetjp.3 ◾ isSharedCheck.10
[Compiler.pushProj] size: 2
    def test2._boxed a b : tobj :=
      let res : tobj := test2 a b;
      dec a;
      return res
-/
#guard_msgs in
set_option pp.letVarTypes true in
set_option trace.Compiler.pushProj true in
def test2 (a b : Option Nat) : Option Nat :=
  match a with
  | some a =>
    match b with
    | some b => some (a + b)
    | none => some a
  | none => none

/--
trace: [Compiler.pushProj] size: 14
    def test3 a b : tobj :=
      cases a : tobj
      | Option.none =>
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          let val.1 : tobj := oproj[0] a;
          let _x.2 : tagged := 1;
          let _x.3 : tobj := Nat.add val.1 _x.2;
          let _x.4 : obj := ctor_1[Option.some] _x.3;
          return _x.4
        | Option.some =>
          let val.5 : tobj := oproj[0] a;
          let val.6 : tobj := oproj[0] b;
          let _x.7 : tobj := Nat.add val.5 val.6;
          let _x.8 : obj := ctor_1[Option.some] _x.7;
          return _x.8
[Compiler.pushProj] size: 48
    def test3 a b : tobj :=
      cases a : tobj
      | Option.none =>
        dec b;
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          let val.1 : tobj := oproj[0] a;
          jp resetjp.2 _x.3 isShared.4 : tobj :=
            let _x.5 : tagged := 1;
            let _x.6 : tobj := Nat.add val.1 _x.5;
            dec val.1;
            jp reusejp.7 _x.8 : tobj :=
              return _x.8;
            cases isShared.4 : tobj
            | Bool.false =>
              oset _x.3 [0] := _x.6;
              goto reusejp.7 _x.3
            | Bool.true =>
              let reuseFailAlloc.9 : obj := ctor_1[Option.some] _x.6;
              goto reusejp.7 reuseFailAlloc.9;
          let isSharedCheck.10 : UInt8 := isShared a;
          cases isSharedCheck.10 : tobj
          | Bool.false =>
            goto resetjp.2 a isSharedCheck.10
          | Bool.true =>
            inc val.1;
            dec a;
            goto resetjp.2 ◾ isSharedCheck.10
        | Option.some =>
          let val.11 : tobj := oproj[0] a;
          inc val.11;
          dec[ref][1 objs] a;
          let val.12 : tobj := oproj[0] b;
          jp resetjp.13 _x.14 isShared.15 : tobj :=
            let _x.16 : tobj := Nat.add val.11 val.12;
            dec val.12;
            dec val.11;
            jp reusejp.17 _x.18 : tobj :=
              return _x.18;
            cases isShared.15 : tobj
            | Bool.false =>
              oset _x.14 [0] := _x.16;
              goto reusejp.17 _x.14
            | Bool.true =>
              let reuseFailAlloc.19 : obj := ctor_1[Option.some] _x.16;
              goto reusejp.17 reuseFailAlloc.19;
          let isSharedCheck.20 : UInt8 := isShared b;
          cases isSharedCheck.20 : tobj
          | Bool.false =>
            goto resetjp.13 b isSharedCheck.20
          | Bool.true =>
            inc val.12;
            dec b;
            goto resetjp.13 ◾ isSharedCheck.20
-/
#guard_msgs in
set_option pp.letVarTypes true in
set_option trace.Compiler.pushProj true in
def test3 (a b : Option Nat) : Option Nat :=
  match a with
  | some a =>
    match b with
    | some b => some (a + b)
    | none => some (a + 1)
  | none => none

/--
trace: [Compiler.pushProj] size: 18
    def test4 a b c : tobj :=
      cases a : tobj
      | Option.none =>
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          let val.1 : tobj := oproj[0] a;
          let _x.2 : tagged := 1;
          let _x.3 : tobj := Nat.add val.1 _x.2;
          let _x.4 : obj := ctor_1[Option.some] _x.3;
          return _x.4
        | Option.some =>
          cases c : tobj
          | Bool.false =>
            let _x.5 : tagged := ctor_0[Option.none];
            return _x.5
          | Bool.true =>
            let val.6 : tobj := oproj[0] a;
            let val.7 : tobj := oproj[0] b;
            let _x.8 : tobj := Nat.add val.6 val.7;
            let _x.9 : obj := ctor_1[Option.some] _x.8;
            return _x.9
[Compiler.pushProj] size: 54
    def test4 a b c : tobj :=
      cases a : tobj
      | Option.none =>
        dec b;
        return a
      | Option.some =>
        cases b : tobj
        | Option.none =>
          let val.1 : tobj := oproj[0] a;
          jp resetjp.2 _x.3 isShared.4 : tobj :=
            let _x.5 : tagged := 1;
            let _x.6 : tobj := Nat.add val.1 _x.5;
            dec val.1;
            jp reusejp.7 _x.8 : tobj :=
              return _x.8;
            cases isShared.4 : tobj
            | Bool.false =>
              oset _x.3 [0] := _x.6;
              goto reusejp.7 _x.3
            | Bool.true =>
              let reuseFailAlloc.9 : obj := ctor_1[Option.some] _x.6;
              goto reusejp.7 reuseFailAlloc.9;
          let isSharedCheck.10 : UInt8 := isShared a;
          cases isSharedCheck.10 : tobj
          | Bool.false =>
            goto resetjp.2 a isSharedCheck.10
          | Bool.true =>
            inc val.1;
            dec a;
            goto resetjp.2 ◾ isSharedCheck.10
        | Option.some =>
          cases c : tobj
          | Bool.false =>
            dec[ref][1 objs] b;
            dec[ref][1 objs] a;
            let _x.11 : tagged := ctor_0[Option.none];
            return _x.11
          | Bool.true =>
            let val.12 : tobj := oproj[0] a;
            inc val.12;
            dec[ref][1 objs] a;
            let val.13 : tobj := oproj[0] b;
            jp resetjp.14 _x.15 isShared.16 : tobj :=
              let _x.17 : tobj := Nat.add val.12 val.13;
              dec val.13;
              dec val.12;
              jp reusejp.18 _x.19 : tobj :=
                return _x.19;
              cases isShared.16 : tobj
              | Bool.false =>
                oset _x.15 [0] := _x.17;
                goto reusejp.18 _x.15
              | Bool.true =>
                let reuseFailAlloc.20 : obj := ctor_1[Option.some] _x.17;
                goto reusejp.18 reuseFailAlloc.20;
            let isSharedCheck.21 : UInt8 := isShared b;
            cases isSharedCheck.21 : tobj
            | Bool.false =>
              goto resetjp.14 b isSharedCheck.21
            | Bool.true =>
              inc val.13;
              dec b;
              goto resetjp.14 ◾ isSharedCheck.21
[Compiler.pushProj] size: 2
    def test4._boxed a b c : tobj :=
      let c.boxed : UInt8 := unbox c;
      let res : tobj := test4 a b c.boxed;
      return res
-/
#guard_msgs in
set_option pp.letVarTypes true in
set_option trace.Compiler.pushProj true in
def test4 (a b : Option Nat) (c : Bool) : Option Nat :=
  match a with
  | some a =>
    match b with
    | some b =>
      match c with
      | true => some (a + b)
      | false => none
    | none => some (a + 1)
  | none => none

