Rename src/jolt -> stdlib (the runtime-loaded layer; jolt-core stays the seed-baked layer) and update the loader / emit-image / doc paths. Drop dead code: the spike/ experiments, the duplicate clojuredocs-export.edn (json moves to tools/), the Janet-era jolt.http binding, and the orphaned persistent_vector.clj whose ns/path didn't even match. Strip porting residue from comments and docstrings across host/chez, jolt-core, stdlib, tests, and docs: internal issue ids, "Phase N" markers, and the "vs Janet" historical exposition, leaving present-tense descriptions and the real JVM-Clojure semantic contrasts. Same pass over the corpus suite labels. The seed is unchanged (docstrings/comments aren't emitted), so the self-host fixpoint and corpus are untouched. Port tools/spec_coverage.py off the dead janet probe to bin/joltc and regenerate coverage.md; drop the dead :host/janet rule from certify.clj and regenerate the conformance profile. Add docs/host-interop.md (the JVM shims and how to register your own host class from a library) and a writing-style note in CLAUDE.md. Stabilize the four racy concurrency corpus cases (future-cancel and agent send/send-off): give the future a sleeping body and the agent a slow action, so cancel reliably catches an in-flight future and deref reliably reads the pre-update snapshot. They certify deterministically now, so drop their :flaky allowlist entries and the orphaned legend.
84 lines
3.2 KiB
Clojure
84 lines
3.2 KiB
Clojure
;; mandelbrot — pure floating-point compute: for each point of an NxN grid,
|
||
;; iterate z = z^2 + c up to a cap and count iterations. No allocation, no
|
||
;; dispatch, no collections in the hot loop — just double arithmetic and tight
|
||
;; recur loops. This isolates the irreducible-math axis the ray tracer is bound
|
||
;; on (where devirt/alloc passes measured flat), so it tracks native-arith codegen
|
||
;; and loop quality directly.
|
||
;;
|
||
;; Portable Clojure (jolt + JVM Clojure).
|
||
;; jolt -m mandelbrot 1000 (JOLT_DIRECT_LINK=1 JOLT_WHOLE_PROGRAM=1)
|
||
(ns mandelbrot
|
||
(:require [jolt.png :as png]))
|
||
|
||
(defn count-point [cr ci cap]
|
||
(loop [i 0 zr 0.0 zi 0.0]
|
||
(if (or (>= i cap) (> (+ (* zr zr) (* zi zi)) 4.0))
|
||
i
|
||
(recur (inc i)
|
||
(+ (- (* zr zr) (* zi zi)) cr)
|
||
(+ (* 2.0 (* zr zi)) ci)))))
|
||
|
||
(defn run [n]
|
||
(let [cap 200
|
||
nd (* 1.0 n)]
|
||
(loop [y 0 acc 0]
|
||
(if (< y n)
|
||
(let [ci (- (/ (* 2.0 y) nd) 1.0)
|
||
row (loop [x 0 a 0]
|
||
(if (< x n)
|
||
(let [cr (- (/ (* 2.0 x) nd) 1.5)]
|
||
(recur (inc x) (+ a (count-point cr ci cap))))
|
||
a))]
|
||
(recur (inc y) (+ acc row)))
|
||
acc))))
|
||
|
||
;; --- PNG demo (jolt.png) --------------------------------------------------
|
||
;; Render a real picture of the set, reusing count-point as the kernel. `render`
|
||
;; is a separate -main subcommand so the numeric-arg bench path is untouched.
|
||
|
||
(defn- color
|
||
"Escape-iteration count -> RGB. In-set points (n>=cap) are black; faster
|
||
escapes run through a warm gradient."
|
||
[n cap]
|
||
(if (>= n cap)
|
||
[0 0 0]
|
||
(let [t (/ (double n) cap)]
|
||
[(int (* 255 (min 1.0 (* 3.0 t))))
|
||
(int (* 255 (min 1.0 (max 0.0 (* 3.0 (- t 0.33))))))
|
||
(int (* 255 (min 1.0 (max 0.0 (* 3.0 (- t 0.66))))))])))
|
||
|
||
(defn render!
|
||
"Render a size×size view of the Mandelbrot set to a PNG at path."
|
||
[path size]
|
||
(let [w size h size cap 1000
|
||
img (png/image w h)]
|
||
(doseq [py (range h)]
|
||
(doseq [px (range w)]
|
||
(let [cr (- (* 3.5 (/ (double px) w)) 2.5) ; real ∈ [-2.5, 1.0]
|
||
ci (- (* 2.8 (/ (double py) h)) 1.4) ; imag ∈ [-1.4, 1.4]
|
||
[r g b] (color (count-point cr ci cap) cap)]
|
||
(png/put! img r g b))))
|
||
(png/write img w h path)
|
||
(println "wrote" path (str w "×" h ", cap " cap))))
|
||
|
||
(defn- run-bench [args]
|
||
(let [n (if (seq args) (Integer/parseInt (first args)) 1000)]
|
||
(dotimes [_ 2] (run (quot n 2))) ; warmup
|
||
(let [runs 3
|
||
times (mapv (fn [_]
|
||
(let [t0 (System/nanoTime)
|
||
r (run n)
|
||
ms (/ (- (System/nanoTime) t0) 1000000.0)]
|
||
[ms r]))
|
||
(range runs))
|
||
mss (mapv first times)
|
||
mean (/ (reduce + mss) runs)]
|
||
(println "mandelbrot n" n "result" (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"))))
|
||
|
||
(defn -main [& args]
|
||
(if (= (first args) "render")
|
||
(render! (or (second args) "mandelbrot.png")
|
||
(if (nth args 2 nil) (Integer/parseInt (nth args 2)) 600))
|
||
(run-bench args)))
|