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))))