Round 3 of the seed shrink. To the overlay: identity, constantly, neg?,
even?, odd? (20-coll, ahead of their first in-tier uses), not= and unreduced
(00-syntax — the kernel and seq tiers use them), ==, ensure-reduced,
halt-when, parse-boolean, parse-uuid, newline, seque, array-seq, to-array-2d,
and the masking unchecked-byte/short/char/float/double coercions. parse-uuid
validates via re-matches over a new __make-uuid host binding (overlay source
can't write :jolt/type map literals). memfn moves to 30-macros as a working
macro over the .method call sugar instead of a fn that throws.
Behavior fixes toward Clojure, each with spec rows: == now throws on
non-numbers instead of comparing them, and halt-when is the canonical
::halt-map version (the halt value replaces the whole reduction result, no
double completion). list? and map-entry? stay in the seed — both are
representation-coupled (plist/tuple checks).
clojure-test-suite goes 4701 -> 4700: update.cljc expects
(update {:k 1} :k identity 1 2 3 4) to throw an arity error, and jolt fns
don't enforce fixed arity anywhere (pre-existing, language-wide — the seed's
Janet identity threw natively). Filed as jolt-6xn; fixing it should flip
several suite rows at once.
206 lines
10 KiB
Text
206 lines
10 KiB
Text
# Specification: numbers & arithmetic.
|
|
(use ../support/harness)
|
|
|
|
(defspec "numbers / arithmetic"
|
|
["add" "6" "(+ 1 2 3)"]
|
|
["add zero args" "0" "(+)"]
|
|
["subtract" "5" "(- 10 3 2)"]
|
|
["negate" "-5" "(- 5)"]
|
|
["multiply" "24" "(* 2 3 4)"]
|
|
["multiply zero args" "1" "(*)"]
|
|
["divide" "2" "(/ 10 5)"]
|
|
["divide to fraction" "0.5" "(/ 1 2)"]
|
|
["inc" "6" "(inc 5)"]
|
|
["dec" "4" "(dec 5)"]
|
|
["quot" "3" "(quot 10 3)"]
|
|
["rem" "1" "(rem 10 3)"]
|
|
["mod" "2" "(mod -1 3)"]
|
|
["rem negative" "-1" "(rem -1 3)"]
|
|
["max" "9" "(max 3 9 1)"]
|
|
["min" "1" "(min 3 9 1)"]
|
|
["abs" "5" "(abs -5)"]
|
|
["promoting + alias" "3" "(+' 1 2)"]
|
|
["inc' alias" "6" "(inc' 5)"])
|
|
|
|
(defspec "numbers / comparison"
|
|
["less than" "true" "(< 1 2 3)"]
|
|
["less than false" "false" "(< 1 3 2)"]
|
|
["greater than" "true" "(> 3 2 1)"]
|
|
["<=" "true" "(<= 1 1 2)"]
|
|
[">=" "true" "(>= 3 3 2)"]
|
|
["= numbers" "true" "(= 2 2)"]
|
|
["= different" "false" "(= 2 3)"]
|
|
["== numeric" "true" "(== 2 2)"]
|
|
["not=" "true" "(not= 1 2)"]
|
|
["compare less" "-1" "(compare 1 2)"]
|
|
["compare equal" "0" "(compare 1 1)"]
|
|
["compare greater" "1" "(compare 2 1)"])
|
|
|
|
(defspec "numbers / predicates"
|
|
["zero?" "true" "(zero? 0)"]
|
|
["pos?" "true" "(pos? 5)"]
|
|
["neg?" "true" "(neg? -5)"]
|
|
["even?" "true" "(even? 4)"]
|
|
["odd?" "true" "(odd? 3)"]
|
|
["number?" "true" "(number? 5)"]
|
|
["number? false" "false" "(number? :a)"]
|
|
["int?" "true" "(int? 5)"]
|
|
["pos-int?" "true" "(pos-int? 5)"]
|
|
["neg-int?" "true" "(neg-int? -5)"]
|
|
["nat-int? zero" "true" "(nat-int? 0)"]
|
|
["nat-int? neg" "false" "(nat-int? -1)"]
|
|
["ratio? false" "false" "(ratio? 5)"])
|
|
|
|
# Symbolic float values and float/double predicates. NOTE: Janet represents
|
|
# integers and integer-valued doubles identically, so (float? 1.0) is false
|
|
# (1.0 is indistinguishable from 1) — a documented divergence. Fractional and
|
|
# non-finite values ARE recognized as floats.
|
|
(defspec "numbers / floats & symbolic values"
|
|
["read ##Inf" "true" "(= ##Inf (/ 1.0 0.0))"]
|
|
["read ##-Inf" "true" "(< ##-Inf 0)"]
|
|
["##NaN not= itself" "true" "(not (== ##NaN ##NaN))"]
|
|
["float? fractional" "true" "(float? 1.5)"]
|
|
["double? fractional" "true" "(double? 0.25)"]
|
|
["float? integer" "false" "(float? 3)"]
|
|
["float? ##Inf" "true" "(float? ##Inf)"]
|
|
["double? ##NaN" "true" "(double? ##NaN)"]
|
|
["infinite? ##Inf" "true" "(infinite? ##Inf)"]
|
|
["infinite? ##-Inf" "true" "(infinite? ##-Inf)"]
|
|
["infinite? finite" "false" "(infinite? 1.5)"]
|
|
["NaN? ##NaN" "true" "(NaN? ##NaN)"]
|
|
["NaN? number" "false" "(NaN? 1.0)"]
|
|
["int? ##Inf false" "false" "(int? ##Inf)"]
|
|
["pos-int? ##Inf" "false" "(pos-int? ##Inf)"])
|
|
|
|
# Numeric literal syntaxes. Jolt has no true bignum/ratio/bigdec types, so the
|
|
# N (bigint) and M (bigdec) suffixes read as the plain number, ratios as the
|
|
# double quotient; radix integers (NrDDD) are parsed by base.
|
|
(defspec "numbers / literal syntax"
|
|
["bigint suffix N" "42" "42N"]
|
|
["bigint zero" "0" "0N"]
|
|
["bigdec suffix M" "1.5" "1.5M"]
|
|
["bigdec int M" "0" "0.0M"]
|
|
["ratio -> double" "0.5" "1/2"]
|
|
["ratio 3/4" "0.75" "3/4"]
|
|
["neg ratio" "-0.5" "-1/2"]
|
|
["radix binary" "10" "2r1010"]
|
|
["radix hex-ish" "255" "16rFF"]
|
|
["radix base36" "35" "36rZ"]
|
|
["hex" "255" "0xFF"]
|
|
["exponent" "1000.0" "1e3"]
|
|
["exponent neg" "0.015" "1.5e-2"])
|
|
|
|
# Strictness: numeric ops reject non-numbers like Clojure; the integer
|
|
# predicates reject non-integers; quot/rem/mod reject zero/non-finite.
|
|
(defspec "numbers / strictness (throws like Clojure)"
|
|
["odd? nil" :throws "(odd? nil)"]
|
|
["odd? fractional" :throws "(odd? 1.5)"]
|
|
["even? inf" :throws "(even? ##Inf)"]
|
|
["zero? nil" :throws "(zero? nil)"]
|
|
["pos? false" :throws "(pos? false)"]
|
|
["neg? keyword" :throws "(neg? :a)"]
|
|
["< nil" :throws "(< nil 1)"]
|
|
["> with nil" :throws "(> 1 nil)"]
|
|
["max non-number" :throws "(max 1 nil)"]
|
|
["quot by zero" :throws "(quot 10 0)"]
|
|
["quot inf" :throws "(quot ##Inf 1)"]
|
|
["< arity-1 any" "true" "(< :anything)"]
|
|
["odd? ok" "true" "(odd? 3)"]
|
|
["< ok" "true" "(< 1 2 3)"]
|
|
["quot ok" "3" "(quot 10 3)"])
|
|
|
|
(defspec "numbers / printing of inf & nan"
|
|
["str Infinity" "\"Infinity\"" "(str ##Inf)"]
|
|
["str -Infinity" "\"-Infinity\"" "(str ##-Inf)"]
|
|
["str NaN" "\"NaN\"" "(str ##NaN)"]
|
|
["pr-str Infinity" "\"Infinity\"" "(pr-str ##Inf)"]
|
|
["inf inside coll" "\"[Infinity]\"" "(str [##Inf])"])
|
|
|
|
(defspec "numbers / bit-ops & math"
|
|
["bit-and" "4" "(bit-and 12 6)"]
|
|
["bit-or" "14" "(bit-or 12 6)"]
|
|
["bit-xor" "10" "(bit-xor 12 6)"]
|
|
["bit-shift-left" "8" "(bit-shift-left 1 3)"]
|
|
["bit-shift-right" "2" "(bit-shift-right 8 2)"]
|
|
["bit-set" "8" "(bit-set 0 3)"]
|
|
["bit-clear" "13" "(bit-clear 15 1)"]
|
|
["bit-test true" "true" "(bit-test 4 2)"]
|
|
["bigint 64-bit" "\"9000000000\"" "(str (bigint 9000000000))"])
|
|
|
|
(defspec "numbers / random (invariants — non-deterministic)"
|
|
["rand-int in range" "true" "(let [r (rand-int 5)] (and (integer? r) (>= r 0) (< r 5)))"]
|
|
["rand-int zero" "0" "(rand-int 1)"]
|
|
["rand in [0,1)" "true" "(let [r (rand)] (and (>= r 0) (< r 1)))"]
|
|
["rand n in [0,n)" "true" "(let [r (rand 10)] (and (>= r 0) (< r 10)))"]
|
|
["rand-nth member" "true" "(contains? #{:a :b :c} (rand-nth [:a :b :c]))"]
|
|
["rand-nth single" ":x" "(rand-nth [:x])"])
|
|
|
|
# Clojure 1.11 string->scalar parsers: nil on malformed, throw on non-string.
|
|
(defspec "numbers / parse fns (1.11)"
|
|
["parse-long" "42" "(parse-long \"42\")"]
|
|
["parse-long negative" "-7" "(parse-long \"-7\")"]
|
|
["parse-long plus" "7" "(parse-long \"+7\")"]
|
|
["parse-long float nil" "nil" "(parse-long \"1.5\")"]
|
|
["parse-long hex nil" "nil" "(parse-long \"0x10\")"]
|
|
["parse-long empty nil" "nil" "(parse-long \"\")"]
|
|
["parse-long junk nil" "nil" "(parse-long \"12ab\")"]
|
|
["parse-long throws" :throws "(parse-long 42)"]
|
|
["parse-double" "1.5" "(parse-double \"1.5\")"]
|
|
["parse-double int" "4.0" "(parse-double \"4\")"]
|
|
["parse-double sci" "1500.0" "(parse-double \"1.5e3\")"]
|
|
["parse-double neg" "-0.5" "(parse-double \"-0.5\")"]
|
|
["parse-double junk" "nil" "(parse-double \"abc\")"]
|
|
["parse-double trail" "nil" "(parse-double \"1.5x\")"]
|
|
["parse-double throws" :throws "(parse-double :k)"]
|
|
["parse-boolean true" "true" "(parse-boolean \"true\")"]
|
|
["parse-boolean false" "false" "(parse-boolean \"false\")"]
|
|
["parse-boolean case" "nil" "(parse-boolean \"True\")"]
|
|
["parse-boolean junk" "nil" "(parse-boolean \"yes\")"]
|
|
["parse-boolean throws" :throws "(parse-boolean true)"])
|
|
|
|
# Jolt numbers don't overflow, so the auto-promoting (') and unchecked
|
|
# variants are aliases of the checked ops (overlay defs, core/20-coll.clj).
|
|
(defspec "numbers / promoting & unchecked aliases"
|
|
["+'" "3" "(+' 1 2)"]
|
|
["-'" "3" "(-' 5 2)"]
|
|
["*'" "12" "(*' 3 4)"]
|
|
["inc'" "6" "(inc' 5)"]
|
|
["dec'" "4" "(dec' 5)"]
|
|
["unchecked-add" "5" "(unchecked-add 2 3)"]
|
|
["unchecked-add-int" "5" "(unchecked-add-int 2 3)"]
|
|
["unchecked-subtract" "3" "(unchecked-subtract 5 2)"]
|
|
["unchecked-subtract-int" "3" "(unchecked-subtract-int 5 2)"]
|
|
["unchecked-multiply" "12" "(unchecked-multiply 3 4)"]
|
|
["unchecked-multiply-int" "12" "(unchecked-multiply-int 3 4)"]
|
|
["unchecked-negate" "-5" "(unchecked-negate 5)"]
|
|
["unchecked-negate-int" "-5" "(unchecked-negate-int 5)"]
|
|
["unchecked-inc" "2" "(unchecked-inc 1)"]
|
|
["unchecked-inc-int" "2" "(unchecked-inc-int 1)"]
|
|
["unchecked-dec" "0" "(unchecked-dec 1)"]
|
|
["unchecked-dec-int" "0" "(unchecked-dec-int 1)"]
|
|
["unchecked-divide-int" "3" "(unchecked-divide-int 7 2)"]
|
|
["unchecked-divide-int negative truncates toward zero" "-3" "(unchecked-divide-int -7 2)"]
|
|
["unchecked-divide-int by zero throws" :throws "(unchecked-divide-int 1 0)"]
|
|
["unchecked-remainder-int" "1" "(unchecked-remainder-int 7 2)"]
|
|
["unchecked-remainder-int negative" "-1" "(unchecked-remainder-int -7 2)"]
|
|
["unchecked-int truncates" "3" "(unchecked-int 3.7)"]
|
|
["unchecked-int negative" "-3" "(unchecked-int -3.7)"]
|
|
["unchecked-long" "3" "(unchecked-long 3.7)"]
|
|
["int? on integer" "true" "(int? 5)"]
|
|
["int? on double" "false" "(int? 5.5)"]
|
|
["int? on non-number" "false" "(int? \"5\")"]
|
|
["num passes a number through" "5" "(num 5)"]
|
|
["num on a double" "5.5" "(num 5.5)"]
|
|
["num throws on non-number" :throws "(num \"x\")"])
|
|
|
|
# == is numeric equality: single arg is trivially true (Clojure's 1-arity
|
|
# never inspects the value); 2+ args must be numbers.
|
|
(defspec "numbers / == numeric equality"
|
|
["== single arg" "true" "(== :a)"]
|
|
["== equal" "true" "(== 2 2)"]
|
|
["== unequal" "false" "(== 2 3)"]
|
|
["== chained" "true" "(== 2 2 2)"]
|
|
["== chained unequal" "false" "(== 2 2 3)"]
|
|
["== int and double" "true" "(== 1 1.0)"]
|
|
["== throws on non-number" :throws "(== 1 :a)"]
|
|
["== throws on two keywords" :throws "(== :a :a)"])
|