(ns propeller.push.instructions.numeric
  "FLOAT and INTEGER Instructions (polymorphic).
  Additional instructions can be found at [Additional Instructions](Additional_Instructions.md)."
  (:require [propeller.tools.math :as math]
            [propeller.push.instructions :refer [def-instruction
                                                 generate-instructions
                                                 make-instruction]]))

;; =============================================================================
;; FLOAT and INTEGER Instructions (polymorphic)
;; =============================================================================

;; Pushes TRUE onto the BOOLEAN stack if the first item is greater than the second
;; item, and FALSE otherwise
(def _gt
  "Pushes TRUE onto the BOOLEAN stack if the first item is greater
  than the second item, and FALSE otherwise"
  ^{:stacks #{:boolean}
    :name "_gt"}
  (fn [stack state]
    (make-instruction state > [stack stack] :boolean)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_buy
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gt_buy"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :buy nil) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_sell
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gt_sell"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :sell nil) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_hold
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gt_hold"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :hold nil) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _gt_buy_sell
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_buy_sell"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :buy :sell) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _gt_buy_hold
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_buy_hold"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :buy :hold) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _gt_sell_buy
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :sell :buy) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _gt_sell_hold
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :sell :hold) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _gt_hold_buy
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :hold :buy) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _gt_hold_sell
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gt_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (> %1 %2) :hold :sell) [stack stack] :signal)))

;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than or
;; equal to the top item, and FALSE otherwise
(def _gte
  "Pushes TRUE onto the BOOLEAN stack if the second item is greater than or
  equal to the top item, and FALSE otherwise"
  ^{:stacks #{:boolean}
    :name "_gte"}
  (fn [stack state]
    (make-instruction state >= [stack stack] :boolean)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_buy
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gte_buy"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :buy nil) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_sell
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gte_sell"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :sell nil) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_hold
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_gte_hold"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :hold nil) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _gte_buy_sell
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_buy_sell"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :buy :sell) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _gte_buy_hold
  "Pushes :buy onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_buy_hold"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :buy :hold) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _gte_sell_buy
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :sell :buy) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _gte_sell_hold
  "Pushes :sell onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :sell :hold) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _gte_hold_buy
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :hold :buy) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _gte_hold_sell
  "Pushes :hold onto the SIGNAL stack if the first item is greater
  than or equal to the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_gte_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (>= %1 %2) :hold :sell) [stack stack] :signal)))

;; Pushes TRUE onto the BOOLEAN stack if the second item is less than the top
;; item, and FALSE otherwise
(def _lt
  "Pushes TRUE onto the BOOLEAN stack if the second item is less than the top
  item, and FALSE otherwise"
  ^{:stacks #{:boolean}
    :name "_lt"}
  (fn [stack state]
    (make-instruction state < [stack stack] :boolean)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_buy
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lt_buy"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :buy nil) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_sell
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lt_sell"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :sell nil) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_hold
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lt_hold"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :hold nil) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _lt_buy_sell
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_buy_sell"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :buy :sell) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _lt_buy_hold
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_buy_hold"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :buy :hold) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _lt_sell_buy
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :sell :buy) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _lt_sell_hold
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :sell :hold) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _lt_hold_buy
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :hold :buy) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _lt_hold_sell
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lt_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (< %1 %2) :hold :sell) [stack stack] :signal)))

;; Pushes TRUE onto the BOOLEAN stack if the second item is less than or equal
;; to the top item, and FALSE otherwise
(def _lte
  "Pushes TRUE onto the BOOLEAN stack if the second item is less than or equal
  to the top item, and FALSE otherwise"
  ^{:stacks #{:boolean}
    :name "_lte"}
  (fn [stack state]
    (make-instruction state <= [stack stack] :boolean)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_buy
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lte_buy"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :buy nil) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_sell
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lte_sell"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :sell nil) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_hold
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than or equal to the second item, and acts as a no-op otherwise"
  ^{:stacks #{}
    :name "_lte_hold"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :hold nil) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _lte_buy_sell
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_buy_sell"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :buy :sell) [stack stack] :signal)))

;; Pushes :buy onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _lte_buy_hold
  "Pushes :buy onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_buy_hold"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :buy :hold) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _lte_sell_buy
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :sell :buy) [stack stack] :signal)))

;; Pushes :sell onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :hold onto the SIGNAL stack otherwise.
(def _lte_sell_hold
  "Pushes :sell onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :hold onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_sell_buy"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :sell :hold) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :buy onto the SIGNAL stack otherwise.
(def _lte_hold_buy
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :buy onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :hold :buy) [stack stack] :signal)))

;; Pushes :hold onto the SIGNAL stack if the first item is less than or equal to the second
;; item, pushes :sell onto the SIGNAL stack otherwise.
(def _lte_hold_sell
  "Pushes :hold onto the SIGNAL stack if the first item is less
  than or equal to the second item, pushes :sell onto the SIGNAL stack otherwise"
  ^{:stacks #{}
    :name "_lte_hold_buy"}
  (fn [stack state]
    (make-instruction state #(if (<= %1 %2) :hold :sell) [stack stack] :signal)))

;; Pushes the sum of the top two items onto the same stack
(def _add
  "Pushes the sum of the top two items onto the same stack"
  ^{:stacks #{}
    :name "_add"}
  (fn [stack state]
    #?(:clj (make-instruction state +' [stack stack] stack)
       :cljs (make-instruction state + [stack stack] stack))))

;; Pushes the difference of the top two items (i.e. the second item minus the
;; top item) onto the same stack
(def _subtract
  "Pushes the difference of the top two items (i.e. the second item minus the
  top item) onto the same stack"
  ^{:stacks #{}
    :name "_subtract"}
  (fn [stack state]
    #?(:clj (make-instruction state -' [stack stack] stack)
       :cljs (make-instruction state - [stack stack] stack))))

;; Pushes the product of the top two items onto the same stack
(def _mult
  "Pushes the product of the top two items onto the same stack"
  ^{:stacks #{}
    :name "_mult"}
  (fn [stack state]
    #?(:clj (make-instruction state *' [stack stack] stack)
       :cljs (make-instruction state * [stack stack] stack))))

;; Pushes the quotient of the top two items (i.e. the first item divided by the
;; second item) onto the same stack. If the second item is zero, pushes 1
(def _quot
  "Pushes the quotient of the top two items (i.e. the first item divided by the
  second item) onto the same stack. If the second item is zero, pushes 1"
  ^{:stacks #{}
    :name "_quot"}
  (fn [stack state]
    (make-instruction state #(if (zero? %2) 1 (quot %1 %2)) [stack stack] stack)))

;; Pushes the top item modulo the second item onto the same stack. If the second
;; item is zero, pushes 1. The modulus is computed as the remainder of the
;; quotient, where the quotient has first been truncated towards negative
;; infinity.
(def _mod
  "Pushes the top item modulo the second item onto the same stack. If the second
  item is zero, pushes 1. The modulus is computed as the remainder of the
   quotient, where the quotient has first been truncated towards negative
   infinity."
  ^{:stacks #{}
    :name "_mod"}
  (fn [stack state]
    (make-instruction state #(if (zero? %2) 1 (mod %1 %2)) [stack stack] stack)))

;; Pushes the maximum of the top two items
(def _max
  "Pushes the maximum of the top two items"
  ^{:stacks #{}
    :name "_max"}
  (fn [stack state]
    (make-instruction state max [stack stack] stack)))

;; Pushes the minimum of the top two items
(def _min
  "Pushes the minimum of the top two items"
  ^{:stacks #{}
    :name "_min"}
  (fn [stack state]
    (make-instruction state min [stack stack] stack)))

;; Pushes 1 / 1.0 if the top BOOLEAN is TRUE, or 0 / 0.0 if FALSE
(def _from_boolean
  "Pushes 1 / 1.0 if the top BOOLEAN is TRUE, or 0 / 0.0 if FALSE"
  ^{:stacks #{:boolean}
    :name "_from_boolean"}
  (fn [stack state]
    (make-instruction state
                      #((if (= stack :integer) int float) (if % 1 0))
                      [:boolean]
                      stack)))

;; Pushes the ASCII value of the top CHAR
(def _from_char
  "Pushes the ASCII value of the top CHAR"
  ^{:stacks #{:char}
    :name "_from_char"}
  (fn [stack state]
    (make-instruction state (if (= stack :integer) int float) [:char] stack)))

;; Pushes the value of the top STRING, if it can be parsed as a number.
;; Otherwise, acts as a NOOP
(def _from_string
  "Pushes the value of the top STRING, if it can be parsed as a number.
Otherwise, acts as a NOOP"
  ^{:stacks #{:string}
    :name "_from_string"}
  (fn [stack state]
    (make-instruction state
                      #(try (if (= stack :integer)
                             #?(:clj (Integer/parseInt %)
                                :cljs (js/parseInt %)) 
                             #?(:clj (Float/parseFloat %) 
                                :cljs (js/parseFloat %)))
                            #?(:clj (catch Exception e :ignore-instruction)
                               :cljs (catch js/Error e :ignore-instruction)))
                      [:string]
                      stack)))

;; Pushes the increment (i.e. +1) of the top item of the stack
(def _inc
  "Pushes the increment (i.e. +1) of the top item of the stack"
  ^{:stacks #{}
    :name "_inc"}
  (fn [stack state]
    (make-instruction state inc [stack] stack)))

;; Pushes the decrement (i.e. -1) of the top item of the stack
(def _dec
  "Pushes the decrement (i.e. -1) of the top item of the stack"
  ^{:stacks #{}
    :name "_dec"}
  (fn [stack state]
    (make-instruction state dec [stack] stack)))

;; Pushes the square of the top item of the stack
(def _square
  "Pushes the square of the top item of the stack"
  ^{:stacks #{}
    :name "_square"}
  (fn [stack state]
    (make-instruction state #(* % %) [stack] stack)))

(generate-instructions
  [:float :integer]
  [_gt _gte _lt _lte _add _subtract _mult _quot _mod _max _min _inc _dec
   _from_boolean _from_char _from_string _gt_buy _gt_sell _gt_hold _gte_buy
   _gte_sell _gte_hold _lt_buy _lt_sell _lt_hold _lte_buy _lte_sell _lte_hold
   _gt_buy_sell _gt_buy_hold _gt_sell_buy _gt_sell_hold _gt_hold_buy _gt_hold_sell
   _gte_buy_sell _gte_buy_hold _gte_sell_buy _gte_sell_hold _gte_hold_buy _gte_hold_sell
   _lt_buy_sell _lt_buy_hold _lt_sell_buy _lt_sell_hold _lt_hold_buy _lt_hold_sell
   _lte_buy_sell _lte_buy_hold _lte_sell_buy _lte_sell_hold _lte_hold_buy _lte_hold_sell
   _square])

;; =============================================================================
;; FLOAT Instructions only
;; =============================================================================

;; Divides the top two items on the float stack
;; If denominator is 0, returns 1.0
(def-instruction
  :float_div
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state #(float (if (zero? %2) 1 (/ %1 %2))) [:float :float] :float)))

;; Pushes the cosine of the top FLOAT
(def-instruction
  :float_cos
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state math/cos [:float] :float)))

;; Pushes the sine of the top FLOAT
(def-instruction
  :float_sin
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state math/sin [:float] :float)))

;; Pushes the tangent of the top FLOAT
(def-instruction
  :float_tan
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state math/tan [:float] :float)))

;; Pushes the square root of the top FLOAT. Makes the value
;; absolute first.
(def-instruction
  :float_sqrt
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state #(math/sqrt (math/abs %)) [:float] :float)))

;; Pushes the natural log of the top FLOAT
(def-instruction
  :float_log
  ^{:stacks #{:float}}
  (fn [state]
    (make-instruction state #(if (zero? %) 1 (math/log (math/abs %))) [:float] :float)))

;; Pushes the floating point version of the top INTEGER
(def-instruction
  :float_from_integer
  ^{:stacks #{:float :integer}}
  (fn [state]
    (make-instruction state float [:integer] :float)))

;; =============================================================================
;; INTEGER Instructions only
;; =============================================================================

;; Pushes the result of truncating the top FLOAT towards negative infinity
(def-instruction
  :integer_from_float
  ^{:stacks #{:float :integer}}
  (fn [state]
    (make-instruction state int [:float] :integer)))
