;; binary-trees (Computer Language Benchmarks Game) — an ALLOCATION/GC stress
;; test. Builds and discards millions of short-lived `Node` records; the nodes
;; ESCAPE (stored in the tree, walked later), so this is the regime escape analysis
;; targets and the ray tracer never exercises (~7% alloc).
;;
;; Portable Clojure: runs on jolt and JVM Clojure for cross-impl comparison.
;;   bench/run.sh binary-trees 14
(ns binary-trees)

(defrecord Node [left right])

(defn make-tree [depth]
  (if (zero? depth)
    (->Node nil nil)
    (->Node (make-tree (dec depth)) (make-tree (dec depth)))))

(defn check-tree [node]
  (let [l (:left node)]
    (if (nil? l)
      1
      (+ (+ 1 (check-tree l)) (check-tree (:right node))))))

(defn run [max-depth]
  (let [min-depth 4
        stretch-depth (inc max-depth)
        _ (check-tree (make-tree stretch-depth))
        long-lived (make-tree max-depth)]
    (loop [d min-depth acc 0]
      (if (<= d max-depth)
        (let [iterations (bit-shift-left 1 (+ (- max-depth d) min-depth))
              sum (loop [i 0 s 0]
                    (if (< i iterations)
                      (recur (inc i) (+ s (check-tree (make-tree d))))
                      s))]
          (recur (+ d 2) (+ acc sum)))
        ;; touch the long-lived tree so it isn't dead-code-eliminated
        (+ acc (check-tree long-lived))))))

(defn -main [& args]
  (let [max-depth (if (seq args) (Integer/parseInt (first args)) 14)]
    (dotimes [_ 2] (run (min max-depth 10)))            ; warmup
    (let [runs 3
          times (mapv (fn [_]
                        (let [t0 (System/nanoTime)
                              r (run max-depth)
                              ms (/ (- (System/nanoTime) t0) 1000000.0)]
                          [ms r]))
                      (range runs))
          mss (mapv first times)
          mean (/ (reduce + mss) runs)]
      (println "binary-trees depth" max-depth "checksum" (second (first times)))
      (println "runs:" (mapv (fn [t] (/ (Math/round (* t 10.0)) 10.0)) mss))
      (println "mean:" (/ (Math/round (* mean 10.0)) 10.0) "ms"))))
