(ns cyclelengths
  (:gen-class))

(defn factorial [n]
  " n! "
  (apply *' (range 1 (inc n))))             

(defn pow [n i]
  " n^i"
  (apply *' (repeat i n)))

(defn analytical [n]
  " Analytical Computation "
  (->>(range 1 (inc n))
      (map #(/ (factorial n) (pow n %) (factorial (- n %)))) 
      (reduce + 0)))


(def TIMES 1000000)

(defn single-test-cycle-length [n]
  " Single random test of cycle length "
  (loop [count 0
         bits 0
         x 1]
    (if (zero? (bit-and x bits))
      (recur (inc count) (bit-or bits x) (bit-shift-left 1 (rand-int n)))
        count)))

(defn avg-cycle-length [n times]
  " Average results of single tests of cycle lengths "
  (/
   (reduce +
           (for [i (range times)]
             (single-test-cycle-length n)))
  times))


(println "\tAvg\t\tExp\t\tDiff")
(doseq [q (range 1 21)
        :let [anal (double (analytical q))
              avg (double (avg-cycle-length q TIMES))
              diff (Math/abs (* 100 (- 1 (/ avg anal))))]]
  (println (format "%3d\t%.4f\t%.4f\t%.2f%%" q avg anal diff)))