(ns warreq.kea.calc.math
  (:require [clojure.math.numeric-tower :refer [expt]]
            [neko.notify :refer [toast]]
            [warreq.kea.calc.util :as u])
  (:import java.math.BigDecimal))

(defn rpn
  "Evaluate an expression composed in Reverse Polish Notation and return the
  result. `rpn` may optionally take a stack-based representation of an expression
  as a secondary parameter."
  ([e]
   (rpn e '()))
  ([e s]
   (if (empty? e)
      s
     (if (number? (first e))
       (recur (rest e)
              (conj s (first e)))
       (recur (rest e)
              (conj (drop 2 s) (reduce (first e) (reverse (take 2 s)))))))))

(defn floating-division [x y]
  (if (not= ^BigDecimal y BigDecimal/ZERO)
    (.divide ^BigDecimal x ^BigDecimal y 2 java.math.RoundingMode/HALF_EVEN)
    (do (u/vibrate! 500)
        (toast "Cannot divide by 0."))))

(defn op-alias [op]
  (case op
    "^" expt
    "÷" floating-division
    "×" *
    "+" +
    "-" -))

(ns warreq.kea.calc.math
  (:require [clojure.math.numeric-tower :refer [expt]]
            [neko.notify :refer [toast]]
            [warreq.kea.calc.util :as u])
  (:import java.math.BigDecimal))

(defn rpn
  "Evaluate an expression composed in Reverse Polish Notation and return the
  result. `rpn` may optionally take a stack-based representation of an expression
  as a secondary parameter."
  ([e]
   (rpn e '()))
  ([e s]
   (if (empty? e)
     s
     (if (number? (first e))
       (recur (rest e)
              (conj s (first e)))
       (recur (rest e)
              (conj (drop 2 s) (reduce (first e) (reverse (take 2 s)))))))))

(defn floating-division [x y]
  (let [x (bigdec x)
        y (bigdec y)]
    (if (not= y BigDecimal/ZERO)
      (.divide x y 2 java.math.RoundingMode/HALF_EVEN)
      (do (u/vibrate! 500)
          (toast "Cannot divide by 0.")))))

(defn op-alias [op]
  (case op
    "^" expt
    "÷" floating-division
    "×" *
    "+" +
    "-" -))

Fix issue where expt followed by division could result in error.
Fix issue where expt followed by division could result in error.

We should not assume that the numbers in the stack at any given
time can be cast directly to a BigDecimal, because some operations,
(like expt) may return a double or something, and then suddenly
one of the operands is not castable.

Instead, in the division function, explicitly create BigDecimals
from the operands and work with those.

warreq/calc-clj