(mod (SINGLETON_STRUCT INNER_PUZZLE lineage_proof my_amount inner_solution)

  ;; SINGLETON_STRUCT = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))

  ; SINGLETON_STRUCT, INNER_PUZZLE are curried in by the wallet

  ; EXAMPLE SOLUTION '(0xfadeddab 0xdeadbeef 1 (0xdeadbeef 200) 50 ((51 0xfadeddab 100) (60 "trash") (51 deadbeef 0)))'


  ; This puzzle is a wrapper around an inner smart puzzle which guarantees uniqueness.
  ; It takes its singleton identity from a coin with a launcher puzzle which guarantees that it is unique.

  (include condition_codes.clib)
  (include curry-and-treehash.clib)
  (include singleton_truths.clib)

  ; takes a lisp tree and returns the hash of it
  (defun sha256tree1 (TREE)
    (if (l TREE)
        (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
        (sha256 1 TREE)
    )
  )

  ; "assert" is a macro that wraps repeated instances of "if"
  ; usage: (assert A0 A1 ... An R)
  ; all of A0, A1, ... An must evaluate to non-null, or an exception is raised
  ; return the value of R (if we get that far)

  (defmacro assert items
    (if (r items)
        (list if (f items) (c assert (r items)) (q . (x)))
        (f items)
    )
  )

  (defun-inline mod_hash_for_singleton_struct (SINGLETON_STRUCT) (f SINGLETON_STRUCT))
  (defun-inline launcher_id_for_singleton_struct (SINGLETON_STRUCT) (f (r SINGLETON_STRUCT)))
  (defun-inline launcher_puzzle_hash_for_singleton_struct (SINGLETON_STRUCT) (r (r SINGLETON_STRUCT)))

  ;; return the full puzzlehash for a singleton with the innerpuzzle curried in
  ; puzzle-hash-of-curried-function is imported from curry-and-treehash.clib
  (defun-inline calculate_full_puzzle_hash (SINGLETON_STRUCT inner_puzzle_hash)
    (puzzle-hash-of-curried-function (mod_hash_for_singleton_struct SINGLETON_STRUCT)
      inner_puzzle_hash
      (sha256tree1 SINGLETON_STRUCT)
    )
  )

  ; assembles information from the solution to create our own full ID including asserting our parent is a singleton
  (defun create_my_ID (SINGLETON_STRUCT full_puzzle_hash parent_parent parent_inner_puzzle_hash parent_amount my_amount)
    (sha256 (sha256 parent_parent (calculate_full_puzzle_hash SINGLETON_STRUCT parent_inner_puzzle_hash) parent_amount)
      full_puzzle_hash
    my_amount)
  )

  ;; take a boolean and a non-empty list of conditions
  ;; strip off the first condition if a boolean is set
  ;; this is used to remove `(CREATE_COIN xxx -113)`
  ;; pretty sneaky, eh?
  (defun strip_first_condition_if (boolean condition_list)
    (if boolean
        (r condition_list)
        condition_list
    )
  )

  (defun-inline morph_condition (condition SINGLETON_STRUCT)
    (list (f condition) (calculate_full_puzzle_hash SINGLETON_STRUCT (f (r condition))) (f (r (r condition))))
  )

  ;; return the value of the coin created if this is a `CREATE_COIN` condition, or 0 otherwise
  (defun-inline created_coin_value_or_0 (condition)
    (if (= (f condition) CREATE_COIN)
        (f (r (r condition)))
        0
    )
  )

  ;; Returns a (bool . bool)
  (defun odd_cons_m113 (output_amount)
    (c
      (= (logand output_amount 1) 1)  ;; is it odd?
      (= output_amount -113)  ;; is it the escape value?
    )
  )

  ; Assert exactly one output with odd value exists - ignore it if value is -113

  ;; this function iterates over the output conditions from the inner puzzle & solution
  ;; and both checks that exactly one unique singleton child is created (with odd valued output),
  ;; and wraps the inner puzzle with this same singleton wrapper puzzle
  ;;
  ;; The special case where the output value is -113 means a child singleton is intentionally
  ;; *NOT* being created, thus forever ending this singleton's existence

  (defun check_and_morph_conditions_for_singleton (SINGLETON_STRUCT conditions has_odd_output_been_found)
    (if conditions
        (morph_next_condition SINGLETON_STRUCT conditions has_odd_output_been_found (odd_cons_m113 (created_coin_value_or_0 (f conditions))))
        (if has_odd_output_been_found
            0
            (x)  ;; no odd output found
        )
    )
  )

  ;; a continuation of `check_and_morph_conditions_for_singleton` with booleans `is_output_odd` and `is_output_m113`
  ;; precalculated
  (defun morph_next_condition (SINGLETON_STRUCT conditions has_odd_output_been_found (is_output_odd . is_output_m113))
    (assert
      (not (all is_output_odd has_odd_output_been_found))
      (strip_first_condition_if
        is_output_m113
        (c (if is_output_odd
               (morph_condition (f conditions) SINGLETON_STRUCT)
               (f conditions)
          )
          (check_and_morph_conditions_for_singleton SINGLETON_STRUCT (r conditions) (any is_output_odd has_odd_output_been_found))
        )
      )
    )
  )

  ; this final stager asserts our ID
  ; it also runs the innerpuz with the innersolution with the "truths" added
  ; it then passes that output conditions from the innerpuz to the morph conditions function
  (defun stager_three (SINGLETON_STRUCT lineage_proof my_id full_puzhash innerpuzhash my_amount INNER_PUZZLE inner_solution)
    (c (list ASSERT_MY_COIN_ID my_id) (check_and_morph_conditions_for_singleton SINGLETON_STRUCT (a INNER_PUZZLE (c (truth_data_to_truth_struct my_id full_puzhash innerpuzhash my_amount lineage_proof SINGLETON_STRUCT) inner_solution)) 0))
  )

  ; this checks whether we are an eve spend or not and calculates our full coin ID appropriately and passes it on to the final stager
  ; if we are the eve spend it also adds the additional checks that our parent's puzzle is the standard launcher format and that out parent ID is the same as our singleton ID

  (defun stager_two (SINGLETON_STRUCT lineage_proof full_puzhash innerpuzhash my_amount INNER_PUZZLE inner_solution)
    (stager_three
      SINGLETON_STRUCT
      lineage_proof
      (if (is_not_eve_proof lineage_proof)
          (create_my_ID
            SINGLETON_STRUCT
            full_puzhash
            (parent_info_for_lineage_proof lineage_proof)
            (puzzle_hash_for_lineage_proof lineage_proof)
            (amount_for_lineage_proof lineage_proof)
            my_amount
          )
          (if (=
                (launcher_id_for_singleton_struct SINGLETON_STRUCT)
                (sha256 (parent_info_for_eve_proof lineage_proof) (launcher_puzzle_hash_for_singleton_struct SINGLETON_STRUCT) (amount_for_eve_proof lineage_proof))
              )
              (sha256 (launcher_id_for_singleton_struct SINGLETON_STRUCT) full_puzhash my_amount)
              (x)
          )
      )
      full_puzhash
      innerpuzhash
      my_amount
      INNER_PUZZLE
      inner_solution
    )
  )

  ; this calculates our current full puzzle hash and passes it to stager two
  (defun stager_one (SINGLETON_STRUCT lineage_proof my_innerpuzhash my_amount INNER_PUZZLE inner_solution)
    (stager_two SINGLETON_STRUCT lineage_proof (calculate_full_puzzle_hash SINGLETON_STRUCT my_innerpuzhash) my_innerpuzhash my_amount INNER_PUZZLE inner_solution)
  )


  ; main

  ; if our value is not an odd amount then we are invalid
  ; this calculates my_innerpuzhash and passes all values to stager_one
  (if (logand my_amount 1)
      (stager_one SINGLETON_STRUCT lineage_proof (sha256tree1 INNER_PUZZLE) my_amount INNER_PUZZLE inner_solution)
      (x)
  )
)
