(.module: [lux #* [control [monad (#+ do)]] [data ["." maybe] [number ["." i64]] [text ("text/." Equivalence) format]] ["." math ["r" random]] ["." macro ["s" syntax (#+ syntax:)]] test]) (context: "Value identity." (<| (times 100) (do @ [size (|> r.nat (:: @ map (|>> (n/% 100) (n/max 10)))) x (r.unicode size) y (r.unicode size)] ($_ seq (test "Every value is identical to itself, and the 'id' function doesn't change values in any way." (and (is? x x) (is? x (id x)))) (test "Values created separately can't be identical." (not (is? x y))) )))) (do-template [category rand-gen even? odd? = < >] [(context: (format "[" category "] " "Simple operations.") (<| (times 100) (do @ [value rand-gen] ($_ seq (test (format "[" category "] " "Moving up-down or down-up should result in same value.") (and (|> value inc dec (= value)) (|> value dec inc (= value)))) (test (format "[" category "] " "(x1) > x && (x-1) < x") (and (|> value inc (> value)) (|> value dec (< value)))) (test (format "[" category "] " "Every odd/even number is surrounded by two of the other kind.") (if (even? value) (and (|> value inc odd?) (|> value dec odd?)) (and (|> value inc even?) (|> value dec even?))))))))] ["Nat" r.nat n/even? n/odd? n/= n/< n/>] ["Int" r.int i/even? i/odd? i/= i/< i/>] ) (do-template [category rand-gen = < > <= >= min max] [(context: (format "[" category "] " "(More) simple operations.") (<| (times 100) (do @ [x rand-gen y rand-gen] (seq (test (format "[" category "] " "The symmetry of numerical comparisons.") (or (= x y) (if (< y x) (> x y) (< x y)))) (test (format "[" category "] " "Minimums and maximums.") (and (and (<= x (min x y)) (<= y (min x y))) (and (>= x (max x y)) (>= y (max x y)))))))))] ["Int" r.int i/= i/< i/> i/<= i/>= i/min i/max] ["Nat" r.nat n/= n/< n/> n/<= n/>= n/min n/max] ["Frac" r.frac f/= f/< f/> f/<= f/>= f/min f/max] ["Rev" r.rev r/= r/< r/> r/<= r/>= r/min r/max] ) (do-template [category rand-gen = + - * / <%> > <0> <1> ] [(context: (format "[" category "] " "Additive identity") (<| (times 100) (do @ [x rand-gen] (test "" (and (|> x (+ <0>) (= x)) (|> x (- <0>) (= x))))))) (context: (format "[" category "] " "Addition & Substraction") (<| (times 100) (do @ [x (:: @ map rand-gen) y (:: @ map rand-gen) #let [x (* x) y (* y)]] (test "" (and (|> x (- y) (+ y) (= x)) (|> x (+ y) (- y) (= x))))))) (context: (format "[" category "] " "Multiplicative identity") (<| (times 100) (do @ [x rand-gen] (test "" ## Skip this test for Rev ## because Rev division loses the last ## 32 bits of precision. (or (text/= "Rev" category) (and (|> x (* <1>) (= x)) (|> x (/ <1>) (= x)))))))) (context: (format "[" category "] " "Multiplication & Division") (<| (times 100) (do @ [x (:: @ map rand-gen) y (|> rand-gen (:: @ map ) (r.filter (|>> (= <0>) not))) #let [r (<%> y x) x' (- r x)]] (test "" ## Skip this test for Rev ## because Rev division loses the last ## 32 bits of precision. (or (text/= "Rev" category) (or (> x' y) (|> x' (/ y) (* y) (= x')))) ))))] ["Nat" r.nat n/= n/+ n/- n/* n// n/% n/> 0 1 1_000_000 (n/% 1_000) id] ["Int" r.int i/= i/+ i/- i/* i// i/% i/> +0 +1 +1_000_000 (i/% +1_000) id] ["Frac" r.frac f/= f/+ f/- f/* f// f/% f/> +0.0 +1.0 +1_000_000.0 id math.floor] ["Rev" r.rev r/= r/+ r/- r/* r// r/% r/> .0 (.rev -1) (.rev -1) id id] ) (def: frac-rev (r.Random Rev) (|> r.rev (:: r.Functor map (|>> (i64.left-shift 11) (i64.logical-right-shift 11))))) (do-template [category rand-gen -> <- = ] [(context: (format "[" category "] " "Numeric conversions") (<| (times 100) (do @ [value rand-gen #let [value ( value)]] (test "" (|> value -> <- (= value))))))] ["Int->Nat" r.int .nat .int i/= (i/% +1_000_000)] ["Nat->Int" r.nat .int .nat n/= (n/% 1_000_000)] ["Int->Frac" r.int int-to-frac frac-to-int i/= (i/% +1_000_000)] ["Frac->Int" r.frac frac-to-int int-to-frac f/= math.floor] ["Rev->Frac" frac-rev rev-to-frac frac-to-rev r/= id] ) (context: "Simple macros and constructs" ($_ seq (test "Can write easy loops for iterative programming." (i/= +1000 (loop [counter +0 value +1] (if (i/< +3 counter) (recur (inc counter) (i/* +10 value)) value)))) (test "Can create lists easily through macros." (and (case (list +1 +2 +3) (#.Cons +1 (#.Cons +2 (#.Cons +3 #.Nil))) #1 _ #0) (case (list& +1 +2 +3 (list +4 +5 +6)) (#.Cons +1 (#.Cons +2 (#.Cons +3 (#.Cons +4 (#.Cons +5 (#.Cons +6 #.Nil)))))) #1 _ #0))) (test "Can have defaults for Maybe values." (and (is? "yolo" (maybe.default "yolo" #.None)) (is? "lol" (maybe.default "yolo" (#.Some "lol"))))) )) (template: (hypotenuse x y) (i/+ (i/* x x) (i/* y y))) (context: "Templates." (<| (times 100) (do @ [x r.int y r.int] (test "Template application is a stand-in for the templated code." (i/= (i/+ (i/* x x) (i/* y y)) (hypotenuse x y)))))) (context: "Cross-platform support." ($_ seq (test "Can provide default in case there is no particular platform support." (for {"" #0} #1)) (test "Can pick code depending on the platform being targeted." (for {"JVM" #1 "JS" #1} #0))))