;; powers.hs — integer exponentiation and powers-of-2 checks.

(define
  hk-prog-val
  (fn
    (src name)
    (hk-deep-force (get (hk-eval-program (hk-core src)) name))))

(define
  hk-as-list
  (fn
    (xs)
    (cond
      ((and (list? xs) (= (first xs) "[]")) (list))
      ((and (list? xs) (= (first xs) ":"))
        (cons (nth xs 1) (hk-as-list (nth xs 2))))
      (:else xs))))

(define
  hk-pow-src
  "pow _ 0 = 1\npow base n = base * pow base (n - 1)\n\npowers base k = map (pow base) [0..k]\n\nisPowerOf2 n\n  | n <= 0    = False\n  | n == 1    = True\n  | otherwise = n `mod` 2 == 0 && isPowerOf2 (n `div` 2)\n\nlog2 1 = 0\nlog2 n = 1 + log2 (n `div` 2)\n")

(hk-test "pow 2 0 = 1" (hk-prog-val (str hk-pow-src "r = pow 2 0\n") "r") 1)

(hk-test "pow 2 1 = 2" (hk-prog-val (str hk-pow-src "r = pow 2 1\n") "r") 2)

(hk-test
  "pow 2 8 = 256"
  (hk-prog-val (str hk-pow-src "r = pow 2 8\n") "r")
  256)

(hk-test "pow 3 4 = 81" (hk-prog-val (str hk-pow-src "r = pow 3 4\n") "r") 81)

(hk-test
  "pow 10 3 = 1000"
  (hk-prog-val (str hk-pow-src "r = pow 10 3\n") "r")
  1000)

(hk-test
  "powers 2 4 = [1,2,4,8,16]"
  (hk-as-list (hk-prog-val (str hk-pow-src "r = powers 2 4\n") "r"))
  (list 1 2 4 8 16))

(hk-test
  "powers 3 3 = [1,3,9,27]"
  (hk-as-list (hk-prog-val (str hk-pow-src "r = powers 3 3\n") "r"))
  (list 1 3 9 27))

(hk-test
  "isPowerOf2 1 = True"
  (hk-prog-val (str hk-pow-src "r = isPowerOf2 1\n") "r")
  (list "True"))

(hk-test
  "isPowerOf2 8 = True"
  (hk-prog-val (str hk-pow-src "r = isPowerOf2 8\n") "r")
  (list "True"))

(hk-test
  "isPowerOf2 6 = False"
  (hk-prog-val (str hk-pow-src "r = isPowerOf2 6\n") "r")
  (list "False"))

(hk-test
  "isPowerOf2 0 = False"
  (hk-prog-val (str hk-pow-src "r = isPowerOf2 0\n") "r")
  (list "False"))

(hk-test "log2 1 = 0" (hk-prog-val (str hk-pow-src "r = log2 1\n") "r") 0)

(hk-test "log2 8 = 3" (hk-prog-val (str hk-pow-src "r = log2 8\n") "r") 3)

(hk-test
  "log2 1024 = 10"
  (hk-prog-val (str hk-pow-src "r = log2 1024\n") "r")
  10)

{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}
