flan/test/programs/math2.flan
Joseph Ferano a4c6b996ff def re-runs its initialiser, and defvar is renamed defonce
The trio the author decided on 2026-09-20 is now all built: def is CL's
defparameter — its initialiser runs on every daemon re-run, unguarded, so
an edited initialiser repaints the same storage on C-c C-c plus re-run —
defonce (Clojure's name for CL's defvar, per the author) initialises once
behind the .init~once. flag, and defconst stays the image.

One parse arm reads both forms; the difference is Ast.reinit, carried to
Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and
Check.check_global lifts every def initialiser — zero and literal
included — into global/<n>, so the host's startup reaches it through the
function cell and a re-evaluated def swaps it (Session's def_inits;
Emit.redefinition declares the cell for a non-sibling target). The old
defvar spelling is refused with the rename and both compiling spellings,
and every program, test, doc and editor list is swept — except sand.flan,
the author's live WIP, whose seven defvar lines are flagged in FIX.org
and keep its three dependent tests red on this branch.
2026-09-21 07:12:04 +07:00

79 lines
3.2 KiB
Plaintext

;;;; atan2, pow, and clamp — the three gaps NEXT.md's second-tier list names
;;;; under "maths gaps".
;;;;
;;;; Two of them are declares over libm and the third is a macro, and that
;;;; split is the whole content of this file. atan2f and powf are not correctly
;;;; rounded under IEEE-754, exactly as sinf and cosf are not, so every case
;;;; below is a value whose answer is exact in binary — a quadrant boundary, a
;;;; power of two, a perfect square — rather than one that would pin a
;;;; particular libm's last bit and then fail on wasi-libc.
;;;;
;;;; clamp is a macro because a function could not be: min and max are builtins
;;;; at every numeric type and there are no generics, so a clamp function is
;;;; one copy per type. The proof that the macro is not one copy per type is
;;;; that the same three-word call below is made at i32, i64, u8 and f32.
(defn show [x f32] ()
(print x)
(print " "))
;;; clamp must evaluate each of its three arguments exactly once. A macro that
;;; repeated one — say (if (< x lo) lo (if (> x hi) hi x)), which names x twice
;;; — would read identically and call this twice.
(defonce calls i32 0)
(defn tick [x i32] i32
(set calls (+ calls 1))
x)
(defn main [] i32
;; atan2 across all four quadrants and on both axes, which is the whole
;; reason for it over a division: the quadrant is recovered from two signs,
;; and (/ y x) has thrown it away before atan sees it. The two on the x = 0
;; axis are where the division does not exist at all.
(show (atan2-f32 1.0 1.0)) ; pi/4
(show (atan2-f32 1.0 -1.0)) ; 3pi/4
(show (atan2-f32 -1.0 -1.0)) ; -3pi/4
(show (atan2-f32 -1.0 1.0)) ; -pi/4
(show (atan2-f32 0.0 1.0)) ; 0
(show (atan2-f32 1.0 0.0)) ; pi/2
(show (atan2-f32 -1.0 0.0)) ; -pi/2
(println "")
;; pow. A power of two, a half-power that is a perfect square, a negative
;; exponent, and the two edge exponents every implementation special-cases.
(show (pow-f32 2.0 10.0)) ; 1024
(show (pow-f32 9.0 0.5)) ; 3
(show (pow-f32 2.0 -2.0)) ; 0.25
(show (pow-f32 5.0 0.0)) ; 1
(show (pow-f32 5.0 1.0)) ; 5
(println "")
;; clamp: below the range, above it, and inside it untouched.
(print (clamp 0 1 3)) (print " ") ; 1
(print (clamp 5 1 3)) (print " ") ; 3
(print (clamp 2 1 3)) (print " ") ; 2
;; Both ends are inclusive, which is the off-by-one a hand-written clamp
;; gets wrong with a < where it wanted <=.
(print (clamp 1 1 3)) (print " ") ; 1
(print (clamp 3 1 3)) ; 3
(println "")
;; The same three words at three more types, none of which a function could
;; have served without a copy of its own.
(print (clamp (i64 900) (i64 0) (i64 255))) (print " ") ; 255
(print (clamp (u8 200) (u8 0) (u8 255))) (print " ") ; 200
(show (clamp 2.5 0.0 1.0)) ; 1
(println "")
;; lo above hi is not an error and answers hi: the outer min wins. Written
;; down here rather than left for someone to discover.
(print (clamp 7 5 1))
(println "")
;; Three arguments, three evaluations.
(print (clamp (tick 5) (tick 1) (tick 3))) (print " ")
(print calls)
(println "")
0)