Runs the Philox block function directly, on raw counter and key words. Test-only.
This exists so the LIBRARY'S OWN kernel can be known-answer-checked against all three
published Random123 vectors, rather than only the one the public API can reach. The mapping
puts the 0-based block index in counter words 0 and 1, and the block index comes from an
integer(int64) draw — so it cannot exceed roughly 2**62, while KAT 2 needs 0xffffffff
there and KAT 3 about 9.6e18. Both are unreachable through pf_random_at and friends by
construction, for any seed, stream or draw. Note the key is not restricted in the same way:
it is derived from the seed by a bijection, so every 64-bit key is reachable; it is only the
counter that is bounded.
Without this, KAT 2 and KAT 3 could only be asserted against test_random_reference.f90, and
the library kernel was tied to them transitively — directly known-answer-checked once and
indirectly twice, through a reference that is itself checked three times. That chain is
sound but it is a chain, and it leaves the one arithmetic that actually ships less directly
evidenced than the reference written to check it. This hook makes all three direct.
Public only because it has to be, exactly as parquet_debug_random_uses_int128 is: this
module reaches no bind(C) surface, so the C++-side debug-hook convention is unavailable to
it. It is excluded from README's API overview, no library code calls it, and it is a pure
observation with no setter — it cannot change what any draw returns.
Arguments are the module's own coordinates, not Philox's four counter words: key splits
into key words 0 and 1 (low half first), stream into counter words 2 and 3, and index
into counter words 0 and 1. So KAT 2 — every counter and key word 0xffffffff — is the call
parquet_debug_random_block(-1_int64, -1_int64, -1_int64, ...).
Reports which branch a coordinate-addressed normal draw took, and what it cost. Test-only.
Public only because it has to be: this module reaches no bind(C) surface, so the C++-side
debug-hook convention the rest of the library uses is unavailable to it (see CLAUDE.md, "A
Fortran-side debug hook has to be PUBLIC, so prefer a C++ one"). It is excluded from
README.md's API overview and no library code calls it.
It is an OBSERVATION hook, not an override, which is why it needs no process-global
state at all: it re-runs the same construction the ordinary call runs and reports what
happened. (It is not pure only because it can be asked for the polar form, which routes
through exp_key.) A test asserts that all three Ziggurat branches occur over a fixture and
the reported value equals the ordinary call's -- without which a rejection branch could be
compiled and never entered while every test passed.
path is 1 for the immediate rectangle acceptance, 2 for the wedge test and 3 for the tail;
the polar form has one acceptance branch and always reports 1, its rejections showing up as
pairs > 2. pairs counts 32-bit word PAIRS, so the word cost is twice it.
Reports which branch a %gamma draw took. Test-only; see parquet_debug_normal_path
for why a Fortran-side hook has to be public here.
It advances rng exactly as %gamma does and returns the same value, so a test can walk a
stream through it and census the branches. path is 1 for a squeeze acceptance and 2 for
the full logarithmic test, plus 2 more when the shape < 1 boost was applied -- so all four
combinations are distinguishable.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(pf_random_stream), | intent(inout) | :: | rng |
the stream to advance, as |
||
| real(kind=real64), | intent(in) | :: | shape |
the shape parameter; must be > 0 |
||
| real(kind=real64), | intent(out) | :: | r |
the draw, equal to |
||
| integer(kind=int32), | intent(out) | :: | path |
1/2 squeeze/log; +2 when boosted |
||
| logical, | intent(out), | optional | :: | squeeze_ok |
|
pure subroutine parquet_debug_gamma_path(rng, shape, r, path, squeeze_ok) type(pf_random_stream), intent(inout) :: rng !! the stream to advance, as `%gamma` would real(real64), intent(in) :: shape !! the shape parameter; must be > 0 real(real64), intent(out) :: r !! the draw, equal to `%gamma`'s integer(int32), intent(out) :: path !! 1/2 squeeze/log; +2 when boosted logical, intent(out), optional :: squeeze_ok !! `.false.` iff the squeeze accepted a !! candidate the full test would reject call gamma_draw(rng, shape, r, path, squeeze_ok) end subroutine parquet_debug_gamma_path