=begin pod

=head1 NAME

Noise::Simplex::Native — Fast NativeCall port of L<Noise::Simplex>
(2-D & 3-D Simplex-noise generator for Raku)

=head1 SYNOPSIS

=begin code :lang<raku>
use Noise::Simplex::Native;

# 2-D field
my $s   = Noise::Simplex::Native::Simplex.new(seed => 42);
my &n2d = $s.create-noise2d;
say n2d(12.3, 9.8);          # → value ≈ [-1, 1]

# 3-D slice
my &n3d = $s.create-noise3d;
say n3d(1.0, 2.0, 0.5);

# RAII: native ctx is released when $s and any closures go out of scope.
=end code

=head1 DESCRIPTION

Drop-in replacement for L<Noise::Simplex> with the per-sample hot loop
implemented in C and called via NativeCall. Public API is byte-for-byte
the same as the original, plus an additive C<dispose> method for
deterministic teardown.

The permutation table is built in Raku using the same
L<Math::Random::MT> seeding as the original module, then handed to
the C side as a 256-byte array. Same seed → identical permutation
table on both modules (verified by test).

=head1 CLASS

=head2 C<Simplex>

=item C<.new(:$seed!)>

Create a generator initialised with an integer seed. Different seeds
produce independent noise fields.

=item C<.create-noise2d>  → Callable

Returns a two-argument callable C<sub ($x, $y --> Num)>. Inputs may
be Int, Rat, or Num — they are coerced to Num at the boundary.

=item C<.create-noise3d>  → Callable

Returns a three-argument callable C<sub ($x, $y, $z --> Num)>.

=item C<.build-permutation-table>  → Array[Int]

Returns the 512-element permutation table. Useful for verification;
not normally called directly by users.

=item C<.fill-noise2d-grid(:$x0!, :$dx!, :$w!, :$y0!, :$dy!, :$h!)>  → CArray[num64]

Bulk-fill a 2D regular grid in a single NativeCall. Returns a
C<CArray[num64]> of length C<$w * $h> where index C<$j * $w + $i>
holds C<noise2d($x0 + $i * $dx, $y0 + $j * $dy)>. Bit-identical to
the per-sample closure.

=item C<.fill-noise3d-grid(:$x0!, :$dx!, :$w!, :$y0!, :$dy!, :$h!, :$z0!, :$dz!, :$d!)>  → CArray[num64]

Bulk-fill a 3D regular volume. Returns a C<CArray[num64]> of length
C<$w * $h * $d> with z-major iteration: index
C<($k * $h + $j) * $w + $i> holds the sample at
C<(x0 + i*dx, y0 + j*dy, z0 + k*dz)>. Bit-identical to the
per-sample closure (modulo the same simplex-diagonal caveat that
applies to all 3D output, see DETERMINISM below).

=item C<.dispose>  → Nil

Idempotent. Releases the native ctx eagerly. After C<.dispose>, any
sample call throws "used after dispose".

=item C<submethod DESTROY>

Runs C<.dispose> at GC time. Users should not call this directly —
it exists to make RAII work.

=head1 RANGE

Outputs are centred on zero and scaled to roughly C<[-1, 1]> using
factors 70 (2-D) and 32 (3-D), same as the original.

=head1 PERFORMANCE

Pure-Raku Noise::Simplex spends nearly all its time in the per-sample
closure: array indexing, six squarings, several multiplies, and a
couple of permutation lookups. NativeCall removes the closure
overhead and runs the math in IEEE 754 double directly. On a 65k-
sample 2D fill the per-sample speedup over pure Raku is around
15-20× on Apple Silicon; YMMV across hardware.

For dense regular-grid sampling, prefer the C<.fill-noise{2,3}d-grid>
methods. They sample the whole grid in a single NativeCall — boundary
cost paid once instead of N times, and the C compiler inlines the
inner noise function into the loop at C<-O3>. Typical extra speedup
over the per-sample closures is 5-10× on a 256x256 grid, so the
end-to-end win over pure Raku approaches two orders of magnitude
when the bulk path is used.

=head1 MEMORY MANAGEMENT

The native ctx (~20 KB) is held inside the C<Simplex> instance and
released by C<submethod DESTROY> at garbage-collection time. Users
do not need to call C<.dispose>.

The closures returned by C<create-noise2d> / C<create-noise3d>
capture C<self> strongly, so the C<Simplex> instance (and its native
ctx) stays alive as long as any closure holds a reference. This is
the key safety property: closures returned to a caller remain valid
even if the original C<Simplex> binding goes out of scope.

C<.dispose> exists for callers that want deterministic release — for
example, freeing the ctx eagerly in a long-running batch job between
many seeds. It is idempotent. Calling any sample closure after
C<.dispose> throws.

=head1 DETERMINISM

Output relative to the pure-Raku L<Noise::Simplex>, for the same
seed:

=item B<2-D> — bit-identical when inputs are exactly representable
in IEEE 754 double (integers, powers-of-2 fractions). For inputs
the original keeps in Rat for any intermediate step (e.g. C<-3.3>),
the result may diverge by ≤1 ULP because the native module coerces
C<.Num> at the boundary while the original computes Rat-precision
sums where it can.

=item B<3-D> — typically within ULPs, but may diverge by O(0.001) at
simplex-diagonal coordinates. The original 3D pipeline computes in
arbitrary-precision Rat (its C<$f3 = 1/3> and C<$g3 = 1/6> are Rat
literals, and Rat propagates through the whole I<floor → X0 → x0>
derivation). At coordinates where C<x0>, C<y0>, or C<z0> happen to
be exactly equal in Rat (e.g. on the C<x = y> plane), Raku's exact
comparison and C's accumulated-rounding comparison can pick
I<different> simplex corners. The two outputs are then both valid
simplex noise but not the same sample. This is fundamental — no
compiler flag can close the gap.

In practice, on a 1024-sample 3D grid the median absolute difference
is ULP-small (<1e-15) and the worst-case is bounded under 0.05.

The native code is compiled with C<-fno-fast-math> and
C<-ffp-contract=off> so the compiler may not reorder FP operations
or fuse multiply-adds — across-build determinism within this module
is preserved.

=head1 ENVIRONMENT

=item C<NOISE_SIMPLEX_NATIVE_LIB> — full path to a hand-built
shared library, overriding the bundled one. For custom builds,
system copies, or air-gapped installs.

=head1 SEE ALSO

L<Noise::Simplex> — the original pure-Raku implementation.

=head1 AUTHOR

Matt Doughty

=head1 LICENSE

Artistic 2.0

=end pod
