;; recursion.clj       Dave Reed         4/1/26
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defn sum-up-to [N]
  (if (< N 1)
    0
    (+ N (sum-up-to (dec N)))))

(defn my-count [arblist]
  (if (empty? arblist)
    0
    (inc (my-count (rest arblist)))))

(defn my-contains? [arblist item]
  (cond (empty? arblist) false
        (= item (first arblist)) true
        :else (my-contains? (rest arblist) item)))

(defn factorial [N]
  (if (zero? N)
    1
    (* N (factorial (dec N)))))

(defn factorial [N]
  (defn factorial-help [num val-so-far]
    (if (zero? num)
      val-so-far
      (recur (dec num) (* num val-so-far))))
  (factorial-help N 1))

(defn sum-up-to [N]
  (defn sum-help [num sum-so-far]
    (if (< num 1)
      sum-so-far
      (recur (dec num) (+ num sum-so-far))))
  (sum-help N 0))

(defn hailstone [N]
  (cond (dec N) 1
        (even? N) (inc (hailstone (/ N 2)))
        :else (inc (hailstone (inc (* 3 N))))))

(defn hailstone-tail [N]
  (defn hailstone-help [num so-far]
    (cond (= num 1) so-far
          (even? num) (recur (/ num 2) (inc so-far))
          :else (recur (inc (* 3 num)) (inc so-far))))
  (hailstone-help N 1))

(defn hailstone-list [N]
  (defn hailstone-help [list-so-far]
    (cond (= (first list-so-far) 1) (reverse list-so-far)
          (even? (first list-so-far))
          (recur (cons (/ (first list-so-far) 2) list-so-far))
          :else
          (recur (cons (inc (* 3 (first list-so-far))) list-so-far))))
  (hailstone-help (list N)))

(defn longest [low high]
  (if (> low high)
    0
    (max (hailstone-tail low) (longest (inc low) high))))

(defn longest-tail [low high]
  (defn longest-help [num longest-so-far]
    (if (> num high)
      longest-so-far
      (recur (inc num) (max (hailstone-tail num) longest-so-far))))
  (longest-help low 0))

(defn sum-nums [numlist]
  (if (empty? numlist)
    0
    (+ (first numlist) (sum-nums (rest numlist)))))

(defn prod-nums [numlist]
  (if (empty? numlist)
    1
    (* (first numlist) (prod-nums (rest numlist)))))

(defn count-zeros [numlist]
  (cond (empty? numlist) 0
        (zero? (first numlist)) (inc (count-zeros (rest numlist)))
        :else (count-zeros (rest numlist))))

(defn remove-zeros [numlist]
  (cond (empty? numlist) numlist
        (zero? (first numlist)) (remove-zeros (rest numlist))
        :else (cons (first numlist) (remove-zeros (rest numlist)))))

(defn remove-nonzeros [numlist]
  (cond (empty? numlist) numlist
        (zero? (first numlist)) (cons (first numlist) (remove-nonzeros (rest numlist)))
        :else (remove-nonzeros (rest numlist))))

(defn inc-all [numlist]
  (if (empty? numlist)
    numlist
    (cons (inc (first numlist)) (inc-all (rest numlist)))))

(defn coin-flip []
  (if (zero? (rand-int 2))
    :HEADS
    :TAILS))

(defn count-heads [numflips]
  (cond (zero? numflips) 0
        (= (coin-flip) :HEADS) (inc (count-heads (dec numflips)))
        :else (count-heads (dec numflips))))

(defn count-heads-tail [numflips]
  (defn count-help [numflips heads-so-far]
    (cond (zero? numflips) heads-so-far
          (= (coin-flip) :HEADS) (recur (dec numflips) (inc heads-so-far))
          :else (recur (dec numflips) heads-so-far)))
  (count-help numflips 0))

(defn longest [low high]
  (apply max (map hailstone-tail (range low (inc high)))))

(defn sum-nums [numlist]
  (apply + numlist))

(defn prod-nums [numlist]
  (apply * numlist))

(defn inc-all [numlist]
  (map inc numlist))

(defn count-zeros [numlist]
  (count (filter zero? numlist)))

(defn remove-nonzeros [numlist]
  (filter zero? numlist))

(defn remove-zeros [numlist]
  (filter (comp not zero?) numlist))
