       # assert RPNForthThing.initialize(~w[ 3 5 < ? 33 44 ]) == 33
       assert RPNForthThing.initialize(~w[ 3 5 > if 33 else 44 then ]) == 44
       # assert RPNForthThing.initialize(~w[ 3 5 > ? 33 44 ]) == 44
+    end
+
+    test "nested if else then" do
+      assert capture_io(fn ->
+        RPNForthThing.initialize(~w[ 5 dup 3 < if ." greater " else 7 < if ." lesser " else ." equal " then then ])
+      end) == "lesser"
     end
 
     test "if then, NO else" do

  # anything else is true... although typically a 1 is returned
  # for truths, boolean tests should compare with 0 (falsity) instead of 1

  def compute([ "=" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if y == x, do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ "<>" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if y != x, do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ "<" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if x < y, do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ ">" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if x > y, do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ "and" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if ((y != 0) and (x != 0)), do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ "or" | remaining_input], [y, x | data_stack], loop_stack, dict) do
    out = if ((y != 0) or (x != 0)), do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  def compute([ "not" | remaining_input], [x | data_stack], loop_stack, dict) do
    out = if x == 0, do: 1, else: 0
    compute(remaining_input, [out | data_stack], loop_stack, dict)
  end

  # def compute([ "?" | remaining_input], data_stack, loop_stack, dict) do
  #   compute([ "if" | remaining_input], data_stack, loop_stack, dict)
  # end

  # If-Else-Then
  # Note that this is a prefix function!
  # Usage: test_val is on top of data_stack. "if <then-clause> else <else-clause> then"
  # puts then-clause on top of instruction data_stack if test_val != 0, else-clause if test_val == 0.
  def compute([ "if" | remaining_input], [x | data_stack], loop_stack, dict) do
    {if_and_possibly_else_clause, ["then" | remaining_input]} = Enum.split_while(remaining_input, fn(ins) -> ins != "then" end)
    {if_clause, else_clause} =
      if Enum.any?(if_and_possibly_else_clause, fn ins -> ins == "else" end) do

    end
  end

  # when no instructions left and one item on data stack, just return that
  def compute([], [last_val], _, _) do
    last_val
  end

  # when no items left on either instruction or data stack, just bail
  def compute([], [], _, _) do
    # just bail
  end

  # when no instructions left, just return data stack
  def compute([], data_stack, _, _) when is_list(data_stack) do
    data_stack
  end

  def compute([ "end" | _ ], [last_elem_in_data_stack], [], _) do
    last_elem_in_data_stack
  end

  def compute([ "end" | _ ], data_stack, [], _) when is_list(data_stack) do
    data_stack
  end

  def compute([ "do" | remainder ], [x, y | data_stack], return_stack, dict) do
    # The way this works is:
    # DO moves the index (0) and the control (5) over to the return stack.
    # I copies the top of the return stack to the data stack.
    # LOOP increments the index (top of return stack).
    # If the index is less than the control (one below the top of return stack),
    # then it reruns the commands from DO back to LOOP.
    # If the index is >=, then it pops the index and control from the
    # return stack, and control resumes as normal.
    compute(remainder, data_stack, [x, y, %{do: remainder} | return_stack], dict)
  end

  def compute([ "i" | remainder ], data_stack, [x, _y, %{do: _do_block} | _rest_of_return_stack] = return_stack, dict) do
    compute(remainder, [x | data_stack], return_stack, dict)
  end

  # note implementation difference/similarity to "i". "i" assumes a loop already on the return stack
  # (and pattern-matches on that), r@ does not
  def compute([ "r@" | remainder ], data_stack, [x | _rest_of_return_stack] = return_stack, dict) do
    compute(remainder, [x | data_stack], return_stack, dict)
  end

  def compute([ ">r" | remainder ], [n | data_stack], return_stack, dict) do
    compute(remainder, data_stack, [n | return_stack], dict)
  end

  def compute([ "r>" | remainder ], data_stack, [n | return_stack], dict) do
    compute(remainder, [n | data_stack], return_stack, dict)
  end

  def compute([ "j" | remainder ], data_stack, [_x, _y, %{do: _do_block}, z | _rest_of_return_stack] = return_stack, dict) do
    compute(remainder, [z | data_stack], return_stack, dict)
  end

  # loops. holy crap NESTED LOOPS WORK.
  def compute([ "loop" | remainder ], data_stack, [x, y, %{do: do_block} | return_stack], dict) do
    x = x + 1
    if x < y do
      compute(do_block, data_stack, [x, y, %{do: do_block} | return_stack], dict)
    else
      compute(remainder, data_stack, return_stack, dict)
    end
  end

  # quoted string output
  def compute([".\"" | remaining_input ], data_stack, return_stack, dict) do
    {to_print, [_closequote | rest]} = Enum.split_while(remaining_input, fn(ins) -> ins != "\"" end)
    IO.write(Enum.join(to_print, " "))
    # IO.write(" ") # is this necessary to match the spec?
    compute(rest, data_stack, return_stack, dict)
  end

  def compute([ "+" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [x + y | data_stack], return_stack, dict)
  end

  def compute([ "2+" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [x + 2 | data_stack], return_stack, dict)
  end

  def compute([ "-" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [x - y | data_stack], return_stack, dict)
  end

  def compute([ "2-" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [x - 2 | data_stack], return_stack, dict)
  end

  def compute([ "*/" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(["*", x, "/" | remaining_input], data_stack, return_stack, dict)
  end

  def compute([ "/mod" | remaining_input], [x, y | data_stack], return_stack, dict) do
    compute(remaining_input, [div(y, x), rem(y, x) | data_stack], return_stack, dict)
  end

  def compute([ "*/mod" | remaining_input], [x, y, z | data_stack], return_stack, dict) do
    compute(remaining_input, [rem(x*y, z), div(x*y, z) | data_stack], return_stack, dict)
  end

  def compute([ "*" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [x * y | data_stack], return_stack, dict)
  end

  def compute([ "2*" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [x * 2 | data_stack], return_stack, dict)
  end

  def compute([ "/" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [trunc(x / y) | data_stack], return_stack, dict)
  end

  def compute([ "2/" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [trunc(x / 2) | data_stack], return_stack, dict)
  end

  def compute([ "**" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [trunc(:math.pow(x, y)) | data_stack], return_stack, dict)
  end

  # note: allowing these conflicts with integer-only arithmetic
  def compute([ "pi" | remaining_input], data_stack, return_stack, dict) do
    compute(remaining_input, [:math.pi | data_stack], return_stack, dict)
  end

  def compute([ "sin" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [:math.sin(x) | data_stack], return_stack, dict)
  end

  def compute([ "cos" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [:math.cos(x) | data_stack], return_stack, dict)
  end

  def compute([ "tan" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [:math.tan(x) | data_stack], return_stack, dict)
  end

  def compute([ "sqrt" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [:math.sqrt(x) | data_stack], return_stack, dict)
  end

  def compute([ "drop" | remaining_input], [_ | data_stack], return_stack, dict) do
    compute(remaining_input, data_stack, return_stack, dict)
  end

  def compute([ "dup" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [x, x | data_stack], return_stack, dict)
  end

  def compute([ "swap" | remaining_input], [y, x | data_stack], return_stack, dict) do
    compute(remaining_input, [x, y | data_stack], return_stack, dict)
  end

  def compute([ "over" | remaining_input], [x, y | data_stack], return_stack, dict) do
    compute(remaining_input, [y, x, y | data_stack], return_stack, dict)
  end

  # In-place decrement
  def compute([ "1-" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [(x - 1) | data_stack], return_stack, dict)
  end

  # In-place increment
  def compute([ "1+" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [(x + 1) | data_stack], return_stack, dict)
  end

  # In-place negation
  def compute([ "@-" | remaining_input], [x | data_stack], return_stack, dict) do
    compute(remaining_input, [-x | data_stack], return_stack, dict)
  end

  # negate (similar)
  def compute([ "negate" | remaining_input], [n | data_stack], return_stack, dict) do
    compute(remaining_input, [-n | data_stack], return_stack, dict)
  end

  # random numbers
  def compute([ "rand" | remaining_input], data_stack, return_stack, dict) do
    compute(remaining_input, [:rand.uniform | data_stack], return_stack, dict)
  end

  # max
  def compute([ "max" | remaining_input], [a, b | data_stack], return_stack, dict) do
    max = case a < b do
      true -> b
      false -> a
    end
    compute(remaining_input, [max | data_stack], return_stack, dict)
  end

  # min
  def compute([ "min" | remaining_input], [a, b | data_stack], return_stack, dict) do
    min = case a < b do
      true -> a
      false -> b
    end
    compute(remaining_input, [min | data_stack], return_stack, dict)
  end

  # abs
  def compute([ "abs" | remaining_input], [n | data_stack], return_stack, dict) do
    compute(remaining_input, [abs(n) | data_stack], return_stack, dict)
  end

  # inspection. just spits out the remaining instructions, and the data_stack and dict states
  # Sort of, shockingly simple?
  def compute([ "inspect" | remaining_input], data_stack, return_stack, dict) do
    IO.puts "Remaining instructions:"
    IO.inspect remaining_input
    IO.puts "Data Stack:"
    IO.inspect data_stack
    IO.puts "Return Stack:"
    IO.inspect return_stack
    IO.puts "Dictionary:"
    IO.inspect dict
    compute(remaining_input, data_stack, return_stack, dict)
  end

  # Logic/Booleans
