stochastic-rs-core 2.5.0

Core traits, SIMD RNG, and macros for stochastic-rs.
Documentation
//! Bulk-fill helpers — `fill_uniform_f64` / `fill_uniform_f32` write SIMD
//! stores straight into a caller-supplied slice, with no intermediate
//! `[f; 8]` return-by-value round-trip.

use wide::f32x8;
use wide::f64x4;
use wide::u32x8;
use wide::u64x4;

use super::simd_rng::SimdRng;
use super::xoshiro::F32_MAGIC;
use super::xoshiro::F64_MAGIC;

impl SimdRng {
  /// Fills `out` with uniform `f64` values in `[0, 1)` using direct SIMD
  /// stores — every 4-lane chunk is written via an unaligned `f64x4` store
  /// (`vmovupd`), and any tail shorter than 4 falls back to a scalar copy.
  ///
  /// 52-bit precision via the magic-number trick (1 ULP shy of the 53-bit
  /// scalar variant) in exchange for a fully vectorised pipeline.
  #[inline]
  pub fn fill_uniform_f64(&mut self, out: &mut [f64]) {
    let magic = u64x4::splat(F64_MAGIC);
    let one = f64x4::splat(1.0);
    let len = out.len();
    let full_chunks = len / 4;
    let ptr = out.as_mut_ptr();

    for i in 0..full_chunks {
      let u = self.f64_engine.next();
      let bits = (u >> 12u32) | magic;
      let f: f64x4 = unsafe { core::mem::transmute::<u64x4, f64x4>(bits) };
      let result = f - one;
      unsafe {
        core::ptr::write_unaligned(ptr.add(i * 4) as *mut f64x4, result);
      }
    }

    let tail = len - full_chunks * 4;
    if tail > 0 {
      let u = self.f64_engine.next();
      let bits = (u >> 12u32) | magic;
      let f: f64x4 = unsafe { core::mem::transmute::<u64x4, f64x4>(bits) };
      let arr: [f64; 4] = (f - one).to_array();
      let dst = unsafe { core::slice::from_raw_parts_mut(ptr.add(full_chunks * 4), tail) };
      dst.copy_from_slice(&arr[..tail]);
    }
  }

  /// Fills `out` with uniform `f32` values in `[0, 1)`. Each 8-lane chunk
  /// is written via an unaligned `f32x8` store; tails shorter than 8 fall
  /// back to a scalar copy. 23-bit precision via the magic-number trick —
  /// zero integer-to-float conversion cost.
  #[inline]
  pub fn fill_uniform_f32(&mut self, out: &mut [f32]) {
    let magic = u32x8::splat(F32_MAGIC);
    let one = f32x8::splat(1.0);
    let len = out.len();
    let full_chunks = len / 8;
    let ptr = out.as_mut_ptr();

    for i in 0..full_chunks {
      let u = self.f32_engine.next();
      let bits = (u >> 9u32) | magic;
      let f: f32x8 = unsafe { core::mem::transmute::<u32x8, f32x8>(bits) };
      let result = f - one;
      unsafe {
        core::ptr::write_unaligned(ptr.add(i * 8) as *mut f32x8, result);
      }
    }

    let tail = len - full_chunks * 8;
    if tail > 0 {
      let u = self.f32_engine.next();
      let bits = (u >> 9u32) | magic;
      let f: f32x8 = unsafe { core::mem::transmute::<u32x8, f32x8>(bits) };
      let arr: [f32; 8] = (f - one).to_array();
      let dst = unsafe { core::slice::from_raw_parts_mut(ptr.add(full_chunks * 8), tail) };
      dst.copy_from_slice(&arr[..tail]);
    }
  }
}