;; zipwith.hs — zip, zipWith, unzip operations.

(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-zip-src
  "addPair (x, y) = x + y\npairSum xs ys = map addPair (zip xs ys)\n\nscaleBy k xs = map (\\x -> x * k) xs\n\ndotProduct xs ys = sum (zipWith (*) xs ys)\n\nzipIndex xs = zip [0..length xs - 1] xs\n")

(hk-test
  "zip two lists"
  (hk-as-list (hk-prog-val (str hk-zip-src "r = zip [1,2,3] [4,5,6]\n") "r"))
  (list (list "Tuple" 1 4) (list "Tuple" 2 5) (list "Tuple" 3 6)))

(hk-test
  "zip unequal lengths — shorter wins"
  (hk-as-list (hk-prog-val (str hk-zip-src "r = zip [1,2] [10,20,30]\n") "r"))
  (list (list "Tuple" 1 10) (list "Tuple" 2 20)))

(hk-test
  "zipWith (+)"
  (hk-as-list
    (hk-prog-val (str hk-zip-src "r = zipWith (+) [1,2,3] [10,20,30]\n") "r"))
  (list 11 22 33))

(hk-test
  "zipWith (*)"
  (hk-as-list
    (hk-prog-val (str hk-zip-src "r = zipWith (*) [2,3,4] [10,10,10]\n") "r"))
  (list 20 30 40))

(hk-test
  "dotProduct [1,2,3] [4,5,6] = 32"
  (hk-prog-val (str hk-zip-src "r = dotProduct [1,2,3] [4,5,6]\n") "r")
  32)

(hk-test
  "dotProduct unit vectors = 0"
  (hk-prog-val (str hk-zip-src "r = dotProduct [1,0] [0,1]\n") "r")
  0)

(hk-test
  "pairSum adds element-wise"
  (hk-as-list
    (hk-prog-val (str hk-zip-src "r = pairSum [1,2,3] [4,5,6]\n") "r"))
  (list 5 7 9))

(hk-test
  "unzip separates pairs"
  (hk-prog-val (str hk-zip-src "r = unzip [(1,2),(3,4),(5,6)]\n") "r")
  (list
    "Tuple"
    (list ":" 1 (list ":" 3 (list ":" 5 (list "[]"))))
    (list ":" 2 (list ":" 4 (list ":" 6 (list "[]"))))))

(hk-test
  "zip empty = []"
  (hk-as-list (hk-prog-val (str hk-zip-src "r = zip [] [1,2,3]\n") "r"))
  (list))

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