001 (ns walkmap.utils
002 "Miscellaneous utility functions."
003 (:require [clojure.math.numeric-tower :as m]))
004
005 (defn deep-merge
006 "Recursively merges maps. If vals are not maps, the last value wins."
007 ;; TODO: not my implementation, not sure I entirely trust it.
008 ;; TODO TODO: if we are to successfully merge walkmap objects, we must
009 ;; return, on each object, the union of its tags if any.
010 [& vals]
011 (if (every? map? vals)
012 (apply merge-with deep-merge vals)
013 (last vals)))
014
015 (defn truncate
016 "If string `s` is more than `n` characters long, return the first `n`
017 characters; otherwise, return `s`."
018 [s n]
019 (if (and (string? s) (number? n) (> (count s) n))
020 (subs s 0 n)
021 s))
022
023 (defn kind-type
024 "Identify the type of an `object`, e.g. for logging. If it has a `:kind` key,
025 it's one of ours, and that's what we want. Otherwise, we want its type; but
026 the type of `nil` is `nil`, which doesn't get printed when assembling error
027 ,essages, so return \"nil\"."
028 [object]
029 (or (:kind object) (type object) "nil"))
030
031 (defn =ish
032 "True if numbers `n1`, `n2` are roughly equal; that is to say, equal to
033 within `tolerance` (defaults to one part in a million)."
034 ([n1 n2]
035 (if (and (number? n1) (number? n2))
036 (let [m (m/abs (min n1 n2))
037 t (if (zero? m) 0.000001 (* 0.000001 m))]
038 (=ish n1 n2 t))
039 (= n1 n2)))
040 ([n1 n2 tolerance]
041 (if (and (number? n1) (number? n2))
042 (< (m/abs (- n1 n2)) tolerance)
043 (= n1 n2))))