(ns com.tailrecursion.blacktide.algorithms-test
"Partitioning and small parsers compared with independent collection/regex
models. These programs are also used in the public examples."
(:require [clojure.test :refer [deftest is]]
[clojure.test.check.clojure-test :refer [defspec]]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[com.tailrecursion.blacktide :refer [loophole label goto setq]]
[blacktide.algorithms :refer [tokens partition! decimal-prefix]]))
(defn- partition-reference [xs]
(when (seq xs)
(let [pivot (first xs)]
(loop [xs (vec xs) left -1 right (count xs)]
(let [left (first (drop-while #(< (xs %) pivot) (range (inc left) (count xs))))
right (first (drop-while #(> (xs %) pivot) (range (dec right) -1 -1)))]
(if (>= left right) [right xs]
(recur (assoc xs left (xs right) right (xs left)) left right)))))))
(defn- decimal-reference [s]
(when-let [[token sign digits] (re-find #"^([+-]?)([0-9]+)" s)]
[(* (if (= sign "-") -1 1) (bigint (java.math.BigInteger. digits))) (count token)]))
(defn- even-flips? [xs]
(loophole [i 0 even-state true valid true]
(label scan)
(when (= i (count xs)) (goto done))
(case (nth xs i)
:flip (setq even-state (not even-state))
:hold nil
(do (setq valid false) (goto done)))
(setq i (inc i)) (goto scan)
(label done) (and valid even-state)))
(defn token-reference [s]
(->> s (partition-by #(Character/isWhitespace ^char %))
(remove #(Character/isWhitespace ^char (first %))) (mapv #(apply str %))))
(deftest parsing-and-partition-boundaries
(doseq [s ["" "-" "+" "x" "0" "-0" "+0001" "-9223372036854775809!"
"9999999999999999999999999999999999999999999999999"]]
(is (= (decimal-reference s) (decimal-prefix s))))
(doseq [s ["" " " "a" " a " "one two\tthree\nfour" "a\u2003b\u00a0c"]]
(is (= (token-reference s) (tokens s))))
(doseq [xs [[] [1] [2 1] [1 1 1] [0 -1 2 -3] [Long/MAX_VALUE Long/MIN_VALUE 0]]]
(let [a (long-array xs)]
(is (= (partition-reference xs) (when (seq xs) [(partition! a) (vec a)]))))))
(defspec deterministic-parser-and-mutation-differentials {:num-tests 300 :seed 424242}
(prop/for-all [xs (gen/vector (gen/choose -15 15) 0 34)
number (gen/let [sign (gen/elements ["" "-" "+"])
digits (gen/vector (gen/elements (vec "0123456789")) 0 24)
suffix (gen/elements ["" "x" " 3"])]
(str sign (apply str digits) suffix))
text (gen/fmap #(apply str %) (gen/vector (gen/elements [\a \b \space \tab \newline \u2003 \u00a0]) 0 39))
states (gen/vector (gen/elements [:flip :hold :hold :bad]) 0 34)]
(let [a (long-array xs)]
(and (= (partition-reference xs) (when (seq xs) [(partition! a) (vec a)]))
(= (decimal-reference number) (decimal-prefix number))
(= (token-reference text) (tokens text))
(= (and (every? #{:flip :hold} states) (even? (count (filter #{:flip} states))))
(even-flips? states))))))