Skip to content

Instantly share code, notes, and snippets.

@dexterous
Created June 4, 2025 09:44
Show Gist options
  • Save dexterous/431797996049cf5a76d48685d20f4da4 to your computer and use it in GitHub Desktop.
Save dexterous/431797996049cf5a76d48685d20f4da4 to your computer and use it in GitHub Desktop.
Prime Factorization in Clojure
(defn factors! [n]
(when-not (integer? n)
(throw (IllegalArgumentException.
(format "`%s` is not a natural number." (pr-str n)))))
(loop [n n, c (biginteger 2), fs (transient [])]
(cond
(neg? n) (recur (- n) c (conj! fs -1))
(> c n) (seq (persistent! fs)) ; XXX: only #{0, 1}
(> c (Math/sqrt n)) (seq (persistent! (conj! fs n)))
(zero? (rem n c)) (recur (/ n c) c (conj! fs c))
:else (recur n (.nextProbablePrime c) fs))))
(defn factors [n & {:keys [debug] :or {debug false}}]
(when-not (integer? n)
(throw (IllegalArgumentException.
(format "`%s` is not a natural number." (pr-str n)))))
(letfn [(factors* [n c]
(when debug
(println "Trying" n c))
(cond
(neg? n) (lazy-seq (cons -1 (lazy-seq (factors* (- n) c))))
(> c n) '() ; XXX: only #{0, 1}
(> c (Math/sqrt n)) (cons n nil)
(zero? (rem n c)) (lazy-seq
(cons c (lazy-seq (factors* (/ n c) c))))
:else (lazy-seq (factors* n (.nextProbablePrime c)))))]
(factors* n (biginteger 2))))
(defn prime? [n]
(= n (first (factors n))))
;; utils
(require '[clojure.pprint :as pp]
'[clojure.string :as s]
'[clojure.set :as x])
(defn check [b]
(if b "\uD83D\uDDF8" "\uD83D\uDDF4"))
(defn percent [tot val & {p :precision :or {p 3}}]
(let [w (int (Math/log10 tot))]
(format (str "%" w "s (%" (+ 4 p) "." p "f %%)")
val (bigdec (* (/ val tot) 100)))))
(defn digits [n]
(reverse (map #(mod % 10)
(take-while pos? (iterate #(Math/floorDiv % 10) n)))))
(defn exponent [n]
(let [sup-chars [\u2070 "" \u00B2 \u00B3 \u2074
\u2075 \u2076 \u2077 \u2078 \u2079]]
(s/join (map sup-chars (digits n)))))
(defn math-repr [fs]
(->> fs
frequencies
(map (fn [[n f]] (str n (exponent f))))
(s/join \u2219)))
(defn features [keys-to-fns coll]
(map (comp (partial zipmap (keys keys-to-fns))
(apply juxt (vals keys-to-fns)))
coll))
;; tests
(pp/print-table
(features {'number identity
'prime? (comp check prime?)
'eager (comp math-repr factors!)
'lazy (comp math-repr factors)}
(concat (range -5 31)
[129 4523202 10000000000 8976842742 Long/MAX_VALUE])))
(do
(pp/print-table
(reduce #(x/join %1 %2 {'prime? 'prime?})
(map #(features {'prime? key
(percent % %) (comp (partial percent %) val)}
(->> (range 1 (inc %))
(pmap prime?)
frequencies))
(map #(int (Math/pow 10 %)) (range 1 6)))))
(shutdown-agents))
;; (do
;; (doseq [n (map #(int (Math/pow 10 %)) (range 1 6))]
;; (pp/print-table
;; (features {'prime? key
;; n (comp (partial percent n) val)}
;; (->> (range 1 (inc n))
;; (pmap prime?)
;; frequencies))))
;; (shutdown-agents))
(pp/print-table
(features {'message #(try (factors %)
(catch IllegalArgumentException x
(ex-message x)))}
[1.0 3.2 '() [] #{} {} "foo" map (Object.)]))
(letfn [(blank-line [] (println))
(seperator [n] (println "+-------------+" n))]
(blank-line)
(let [f (atom (factors 275 :debug true))
n (atom 0)]
(seperator @n)
(while (seq @f)
(println "Got" (first @f))
(swap! f rest)
(swap! n inc)
(seperator @n))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment