(ns emmy.util
"Shared utilities between clojure and clojurescript."
(:refer-clojure :exclude [bigint biginteger double long int uuid])
(:require #?(:clj [clojure.core :as core])
#?(:clj [clojure.math.numeric-tower :as nt])
#?(:cljs goog.math.Integer)
#?(:cljs goog.math.Long)
[clojure.walk :as w])
#?(:clj
(:import (clojure.lang BigInt)
(java.util UUID)
(java.util.concurrent TimeoutException)))
#?(:cljs
(:require-macros [emmy.util])))
(defn counted
"Takes a function and returns a pair of:
- an atom that keeps track of fn invocation counts,
- the instrumented fn"
([f] (counted f 0))
([f initial-count]
(let [count (atom initial-count)]
[count (fn [x]
(swap! count inc)
(f x))])))
#object[emmy.util$counted 0x289767d5 "
emmy.util$counted@289767d5"
]
(def compute-sqrt #?(:clj nt/sqrt :cljs Math/sqrt))
#object[clojure.math.numeric_tower$eval34323$fn__34368$G__34314__34373 0x71456d8b "
clojure.math.numeric_tower$eval34323$fn__34368$G__34314__34373@71456d8b"
]
(def compute-expt #?(:clj nt/expt :cljs Math/pow))
#object[clojure.math.numeric_tower$expt 0x1b13dbd3 "
clojure.math.numeric_tower$expt@1b13dbd3"
]
(def compute-abs #?(:clj nt/abs :cljs Math/abs))
#object[clojure.math.numeric_tower$abs 0x2fb7f472 "
clojure.math.numeric_tower$abs@2fb7f472"
]
(def biginttype #?(:clj BigInt :cljs js/BigInt))
clojure.lang.BigInt
(def inttype #?(:clj Integer :cljs goog.math.Integer))
java.lang.Integer
(def longtype #?(:clj Long :cljs goog.math.Long))
java.lang.Long
(defn keyset [m]
(into #{} (keys m)))
#object[emmy.util$keyset 0x3c8d979e "
emmy.util$keyset@3c8d979e"
]
(defn map-vals
"Returns a map of identical type and key set to `m`, with each value `v`
transformed by the supplied function`f` into `(f v)`."
[f m]
(reduce-kv (fn [acc k v]
(assoc acc k (f v)))
(empty m)
m))
#object[emmy.util$map_vals 0x667e9cb1 "
emmy.util$map_vals@667e9cb1"
]
(defn re-matches?
"Returns true if s matches the regex pattern re, false otherwise."
[re s]
#?(:clj (.matches (re-matcher re s))
:cljs (.test re s)))
#object[emmy.util$re_matches_QMARK_ 0x63b61eee "
emmy.util$re_matches_QMARK_@63b61eee"
]
(defn bigint [x]
#?(:clj (core/bigint x)
:cljs (js/BigInt x)))
#object[emmy.util$bigint 0x7332628e "
emmy.util$bigint@7332628e"
]
(defn bigint?
"Returns true if the supplied `x` is a `BigInt`, false otherwise."
[x]
#?(:clj (instance? BigInt x)
:cljs (= "bigint" (goog/typeOf x))))
#object[emmy.util$bigint_QMARK_ 0x23d32a11 "
emmy.util$bigint_QMARK_@23d32a11"
]
(defn parse-bigint [x]
`(bigint ~x))
#object[emmy.util$parse_bigint 0x31daa1ce "
emmy.util$parse_bigint@31daa1ce"
]
(defn biginteger [x]
#?(:clj (core/biginteger x)
:cljs (js/BigInt x)))
#object[emmy.util$biginteger 0x62489875 "
emmy.util$biginteger@62489875"
]
(defn int [x]
#?(:clj (core/int x)
:cljs (goog.math.Integer/fromNumber x)))
#object[emmy.util$int 0x20ea0a0 "
emmy.util$int@20ea0a0"
]
(defn long [x]
#?(:clj (core/long x)
:cljs (goog.math.Long/fromNumber x)))
#object[emmy.util$long 0x38378152 "
emmy.util$long@38378152"
]
(defn double [x]
#?(:clj (core/double x)
:cljs (if (number? x) x (js/Number x))))
#object[emmy.util$double 0x5c87934b "
emmy.util$double@5c87934b"
]
(defn unsupported [s]
(throw
#?(:clj (UnsupportedOperationException. ^String s)
:cljs (js/Error s))))
#object[emmy.util$unsupported 0x7a2a339e "
emmy.util$unsupported@7a2a339e"
]
(defn exception [s]
(throw
#?(:clj (Exception. ^String s)
:cljs (js/Error s))))
#object[emmy.util$exception 0xd5687db "
emmy.util$exception@d5687db"
]
(defn uuid
"Returns a string containing a randomly generated unique identifier."
[]
(str
#?(:clj (UUID/randomUUID)
:cljs (random-uuid))))
#object[emmy.util$uuid 0x96fba32 "
emmy.util$uuid@96fba32"
]
(defn illegal [s]
(throw
#?(:clj (IllegalArgumentException. ^String s)
:cljs (js/Error s))))
#object[emmy.util$illegal 0x69927d9f "
emmy.util$illegal@69927d9f"
]
(defn interrupted [s]
(throw
#?(:clj (InterruptedException. ^String s)
:cljs (js/Error s))))
#object[emmy.util$interrupted 0x428f05f0 "
emmy.util$interrupted@428f05f0"
]
(defn illegal-state [s]
(throw
#?(:clj (IllegalStateException. ^String s)
:cljs (js/Error s))))
#object[emmy.util$illegal_state 0x7bde2a25 "
emmy.util$illegal_state@7bde2a25"
]
(defn arithmetic-ex [s]
(throw
#?(:clj (ArithmeticException. s)
:cljs (js/Error s))))
#object[emmy.util$arithmetic_ex 0xf94a146 "
emmy.util$arithmetic_ex@f94a146"
]
(defn timeout-ex [s]
(throw
#?(:clj (TimeoutException. s)
:cljs (js/Error s))))
#object[emmy.util$timeout_ex 0x75105bae "
emmy.util$timeout_ex@75105bae"
]
(defn failure-to-converge [s]
(throw
#?(:clj (Exception. ^String s)
:cljs (js/Error s))))
#object[emmy.util$failure_to_converge 0x4f0a6079 "
emmy.util$failure_to_converge@4f0a6079"
]
(defn throwable? [t]
(instance? #?(:clj Throwable :cljs js/Error) t))
#object[emmy.util$throwable_QMARK_ 0x458fe4bf "
emmy.util$throwable_QMARK_@458fe4bf"
]
(defn without-symbol-namespaces
"Walks x, removing namespaces from any symbols that are found.
Convenient in unit tests, where the distinction between symbols
in `clojure.core` vs. `cljs.core` is unimportant"
[x]
(w/postwalk (fn [s] (if (qualified-symbol? s) (symbol (name s)) s)) x))
#object[emmy.util$without_symbol_namespaces 0x6fbbe3d9 "
emmy.util$without_symbol_namespaces@6fbbe3d9"
]
(defmacro sci-macro
"Like `defmacro` but when emitting cljs, emits a function
with &env and &form prepended to arglists and :sci/macro metadata,
so that the macro can be imported into sci using copy-var."
{:clj-kondo/lint-as 'clojure.core/defn}
[name & body]
(if (:ns &env)
(let [[doc body] (if (string? (first body))
[(first body) (rest body)]
[nil body])
[options body] (if (map? (first body))
[(first body) (rest body)]
[nil body])
arities (if (vector? (first body)) (list body) body)
arities (map (fn [[argv & body]]
(list (into '[&form &env] argv)
`(let [~'&env (assoc ~'&env :sci? true)]
~@body))) arities)]
`(defn ~(vary-meta name assoc :sci/macro true)
~@(when doc [doc])
~@(when options [options])
~@arities))
`(~'clojure.core/defmacro ~name ~@body)))
#object[emmy.util$sci_macro 0x180118ab "
emmy.util$sci_macro@180118ab"
]
(defmacro copy-ns
([ns-sym sci-ns] `(copy-ns ~ns-sym ~sci-ns nil))
([ns-sym sci-ns opts]
(list 'sci.core/copy-ns
ns-sym
sci-ns
(merge {:copy-meta [:doc :arglists :macro :sci/macro :imported-from]} opts))))
#object[emmy.util$copy_ns 0x414aa7a "
emmy.util$copy_ns@414aa7a"
]
(def machine-epsilon
(loop [e 1.0]
(if (= 1.0 (+ e 1.0))
(* e 2.0)
(recur (/ e 2.0)))))
2.220446049250313e-16
(def sqrt-machine-epsilon
(Math/sqrt machine-epsilon))
1.4901161193847656e-8