From bc0d5512a517ae54eecb83d1326e1e6a86f8cc8b Mon Sep 17 00:00:00 2001 From: MichiRecRoom <1008889+LikeLakers2@users.noreply.github.com> Date: Tue, 5 Nov 2024 05:12:54 -0500 Subject: [PATCH] Add support for `rand`'s `Uniform` distribution to all Vec types (#580) --- src/features/impl_rand.rs | 604 ++++++++++++++++++++++++++++++++++---- 1 file changed, 546 insertions(+), 58 deletions(-) diff --git a/src/features/impl_rand.rs b/src/features/impl_rand.rs index 88dceb38..8f77c5f5 100644 --- a/src/features/impl_rand.rs +++ b/src/features/impl_rand.rs @@ -1,5 +1,21 @@ macro_rules! impl_vec_types { - ($t:ty, $vec2:ident, $vec3:ident, $vec4:ident) => { + ( + $t:ty, + $vec2:ident, + $vec3:ident, + $vec4:ident, + $uniform:ident, + $upper_range_multiplier:expr + ) => { + use super::{UniformVec2, UniformVec3, UniformVec4}; + use rand::{ + distributions::{ + uniform::{SampleBorrow, SampleUniform, UniformSampler}, + Distribution, Standard, + }, + Rng, + }; + impl Distribution<$vec2> for Standard { #[inline] fn sample(&self, rng: &mut R) -> $vec2 { @@ -7,6 +23,100 @@ macro_rules! impl_vec_types { } } + impl SampleUniform for $vec2 { + type Sampler = UniformVec2<$uniform<$t>>; + } + + impl UniformSampler for UniformVec2<$uniform<$t>> { + type X = $vec2; + + fn new(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!(low.x < high.x, "Uniform::new called with `low.x >= high.x"); + assert!(low.y < high.y, "Uniform::new called with `low.y >= high.y"); + Self { + x_gen: $uniform::new(low.x, high.x), + y_gen: $uniform::new(low.y, high.y), + } + } + + fn new_inclusive(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::new_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::new_inclusive called with `low.y >= high.y" + ); + Self { + x_gen: $uniform::new_inclusive(low.x, high.x), + y_gen: $uniform::new_inclusive(low.y, high.y), + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + Self::X::from([self.x_gen.sample(rng), self.y_gen.sample(rng)]) + } + + fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single called with `low.y >= high.y" + ); + Self::X::from([ + $uniform::<$t>::sample_single(low.x, high.x, rng), + $uniform::<$t>::sample_single(low.y, high.y, rng), + ]) + } + + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single_inclusive called with `low.y >= high.y" + ); + Self::X::from([ + $uniform::<$t>::sample_single_inclusive(low.x, high.x, rng), + $uniform::<$t>::sample_single_inclusive(low.y, high.y, rng), + ]) + } + } + impl Distribution<$vec3> for Standard { #[inline] fn sample(&self, rng: &mut R) -> $vec3 { @@ -14,6 +124,121 @@ macro_rules! impl_vec_types { } } + impl SampleUniform for $vec3 { + type Sampler = UniformVec3<$uniform<$t>>; + } + + impl UniformSampler for UniformVec3<$uniform<$t>> { + type X = $vec3; + + fn new(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!(low.x < high.x, "Uniform::new called with `low.x >= high.x"); + assert!(low.y < high.y, "Uniform::new called with `low.y >= high.y"); + assert!(low.z < high.z, "Uniform::new called with `low.z >= high.z"); + Self { + x_gen: $uniform::new(low.x, high.x), + y_gen: $uniform::new(low.y, high.y), + z_gen: $uniform::new(low.z, high.z), + } + } + + fn new_inclusive(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::new_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::new_inclusive called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::new_inclusive called with `low.z >= high.z" + ); + Self { + x_gen: $uniform::new_inclusive(low.x, high.x), + y_gen: $uniform::new_inclusive(low.y, high.y), + z_gen: $uniform::new_inclusive(low.z, high.z), + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + Self::X::from([ + self.x_gen.sample(rng), + self.y_gen.sample(rng), + self.z_gen.sample(rng), + ]) + } + + fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::sample_single called with `low.z >= high.z" + ); + Self::X::from([ + $uniform::<$t>::sample_single(low.x, high.x, rng), + $uniform::<$t>::sample_single(low.y, high.y, rng), + $uniform::<$t>::sample_single(low.z, high.z, rng), + ]) + } + + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single_inclusive called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::sample_single_inclusive called with `low.z >= high.z" + ); + Self::X::from([ + $uniform::<$t>::sample_single_inclusive(low.x, high.x, rng), + $uniform::<$t>::sample_single_inclusive(low.y, high.y, rng), + $uniform::<$t>::sample_single_inclusive(low.z, high.z, rng), + ]) + } + } + impl Distribution<$vec4> for Standard { #[inline] fn sample(&self, rng: &mut R) -> $vec4 { @@ -21,8 +246,141 @@ macro_rules! impl_vec_types { } } + impl SampleUniform for $vec4 { + type Sampler = UniformVec4<$uniform<$t>>; + } + + impl UniformSampler for UniformVec4<$uniform<$t>> { + type X = $vec4; + + fn new(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!(low.x < high.x, "Uniform::new called with `low.x >= high.x"); + assert!(low.y < high.y, "Uniform::new called with `low.y >= high.y"); + assert!(low.z < high.z, "Uniform::new called with `low.z >= high.z"); + assert!(low.w < high.w, "Uniform::new called with `low.w >= high.w"); + Self { + x_gen: $uniform::new(low.x, high.x), + y_gen: $uniform::new(low.y, high.y), + z_gen: $uniform::new(low.z, high.z), + w_gen: $uniform::new(low.w, high.w), + } + } + + fn new_inclusive(low_b: B1, high_b: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::new_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::new_inclusive called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::new_inclusive called with `low.z >= high.z" + ); + assert!( + low.w < high.w, + "Uniform::new_inclusive called with `low.w >= high.w" + ); + Self { + x_gen: $uniform::new_inclusive(low.x, high.x), + y_gen: $uniform::new_inclusive(low.y, high.y), + z_gen: $uniform::new_inclusive(low.z, high.z), + w_gen: $uniform::new_inclusive(low.w, high.w), + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + Self::X::from([ + self.x_gen.sample(rng), + self.y_gen.sample(rng), + self.z_gen.sample(rng), + self.w_gen.sample(rng), + ]) + } + + fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::sample_single called with `low.z >= high.z" + ); + assert!( + low.w < high.w, + "Uniform::sample_single called with `low.w >= high.w" + ); + Self::X::from([ + $uniform::<$t>::sample_single(low.x, high.x, rng), + $uniform::<$t>::sample_single(low.y, high.y, rng), + $uniform::<$t>::sample_single(low.z, high.z, rng), + $uniform::<$t>::sample_single(low.w, high.w, rng), + ]) + } + + fn sample_single_inclusive( + low_b: B1, + high_b: B2, + rng: &mut R, + ) -> Self::X + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + assert!( + low.x < high.x, + "Uniform::sample_single_inclusive called with `low.x >= high.x" + ); + assert!( + low.y < high.y, + "Uniform::sample_single_inclusive called with `low.y >= high.y" + ); + assert!( + low.z < high.z, + "Uniform::sample_single_inclusive called with `low.z >= high.z" + ); + assert!( + low.w < high.w, + "Uniform::sample_single_inclusive called with `low.w >= high.w" + ); + Self::X::from([ + $uniform::<$t>::sample_single_inclusive(low.x, high.x, rng), + $uniform::<$t>::sample_single_inclusive(low.y, high.y, rng), + $uniform::<$t>::sample_single_inclusive(low.z, high.z, rng), + $uniform::<$t>::sample_single_inclusive(low.w, high.w, rng), + ]) + } + } + #[test] - fn test_vec2_rand() { + fn test_vec2_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -33,7 +391,7 @@ macro_rules! impl_vec_types { } #[test] - fn test_vec3_rand() { + fn test_vec3_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -44,7 +402,7 @@ macro_rules! impl_vec_types { } #[test] - fn test_vec4_rand() { + fn test_vec4_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -53,12 +411,161 @@ macro_rules! impl_vec_types { let b: $vec4 = rng2.gen(); assert_eq!(a, b.into()); } + + test_vec_type_uniform!( + test_vec2_rand_uniform_equality, + $vec2, + $t, + 2, + $upper_range_multiplier + ); + + test_vec_type_uniform!( + test_vec3_rand_uniform_equality, + $vec3, + $t, + 3, + $upper_range_multiplier + ); + + test_vec_type_uniform!( + test_vec4_rand_uniform_equality, + $vec4, + $t, + 4, + $upper_range_multiplier + ); + }; +} + +macro_rules! test_vec_type_uniform { + // NOTE: These were intended to be placed in a `macro_rules!` statement in + // the main rule below, but rustc wants to complain about unused macros if I + // try to do that... even if I do use the macros. + (__repeat_code 2, $code:expr) => { + ($code, $code) + }; + (__repeat_code 3, $code:expr) => { + ($code, $code, $code) + }; + (__repeat_code 4, $code:expr) => { + ($code, $code, $code, $code) + }; + ( + $equality_test_name:ident, + $vec:ident, + $t:ty, + $t_count:tt, + $upper_range_divisor:expr + ) => { + /// Tests that we reach the same result, whether we generate the vector + /// type directly, or generate its internal values $t_count times and + /// convert the result into the vector type. + #[test] + fn $equality_test_name() { + use rand::{distributions::Uniform, Rng, SeedableRng}; + use rand_xoshiro::Xoshiro256Plus; + + let mut int_rng = Xoshiro256Plus::seed_from_u64(0); + let mut vec_rng = Xoshiro256Plus::seed_from_u64(0); + + macro_rules! test_uniform { + ( + __single_test, + $uniform_function_name:ident, + $t_low:expr, + $t_high:expr, + $vec_low:expr, + $vec_high:expr + ) => { + let int_u = Uniform::$uniform_function_name($t_low, $t_high); + let vec_u = Uniform::$uniform_function_name($vec_low, $vec_high); + + let v_int = test_vec_type_uniform!( + __repeat_code $t_count, + int_rng.sample(int_u) + ); + let v_vec: $vec = vec_rng.sample(vec_u); + assert_eq!(v_int, v_vec.into()); + }; + ( + $uniform_function_name:ident, + $t_low:expr, + $t_high:expr, + $vec_low:expr, + $vec_high:expr + ) => { + test_uniform!( + __single_test, + $uniform_function_name, + $t_low, $t_high, + $vec_low, $vec_high + ); + + test_uniform!( + __single_test, + $uniform_function_name, + &$t_low, &$t_high, + &$vec_low, &$vec_high + ); + }; + } + + test_uniform!( + new, + <$t>::default(), + <$t>::MAX / $upper_range_divisor, + $vec::default(), + $vec::MAX / $upper_range_divisor + ); + + test_uniform!( + new_inclusive, + <$t>::default(), + <$t>::MAX / $upper_range_divisor, + $vec::default(), + $vec::MAX / $upper_range_divisor + ); + + macro_rules! test_sample_uniform_sampler { + ($sampler_function_name:ident) => { + let v_int = test_vec_type_uniform!( + __repeat_code $t_count, + <$t as SampleUniform>::Sampler::$sampler_function_name( + <$t>::default(), + <$t>::MAX / $upper_range_divisor, + &mut int_rng, + ) + ); + + let v_vec: $vec = <$vec as SampleUniform>::Sampler::$sampler_function_name( + $vec::default(), + $vec::MAX / $upper_range_divisor, + &mut vec_rng, + ); + assert_eq!(v_int, v_vec.into()); + }; + } + + test_sample_uniform_sampler!(sample_single); + test_sample_uniform_sampler!(sample_single_inclusive); + } + }; +} + +macro_rules! impl_int_types { + ($t:ty, $vec2:ident, $vec3:ident, $vec4:ident) => { + use rand::distributions::uniform::UniformInt; + + impl_vec_types!($t, $vec2, $vec3, $vec4, UniformInt, 1); }; } macro_rules! impl_float_types { ($t:ident, $mat2:ident, $mat3:ident, $mat4:ident, $quat:ident, $vec2:ident, $vec3:ident, $vec4:ident) => { - impl_vec_types!($t, $vec2, $vec3, $vec4); + use rand::distributions::uniform::UniformFloat; + + impl_vec_types!($t, $vec2, $vec3, $vec4, UniformFloat, 10.0); impl Distribution<$mat2> for Standard { #[inline] @@ -94,7 +601,7 @@ macro_rules! impl_float_types { } #[test] - fn test_mat2_rand() { + fn test_mat2_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -105,7 +612,7 @@ macro_rules! impl_float_types { } #[test] - fn test_mat3_rand() { + fn test_mat3_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -116,7 +623,7 @@ macro_rules! impl_float_types { } #[test] - fn test_mat4_rand() { + fn test_mat4_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -127,7 +634,7 @@ macro_rules! impl_float_types { } #[test] - fn test_quat_rand() { + fn test_quat_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -140,14 +647,31 @@ macro_rules! impl_float_types { }; } +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UniformVec2 { + x_gen: G, + y_gen: G, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UniformVec3 { + x_gen: G, + y_gen: G, + z_gen: G, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UniformVec4 { + x_gen: G, + y_gen: G, + z_gen: G, + w_gen: G, +} + mod f32 { use crate::f32::math; use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; use core::f32::consts::TAU; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; impl_float_types!(f32, Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4); @@ -159,7 +683,7 @@ mod f32 { } #[test] - fn test_vec3a_rand() { + fn test_vec3a_rand_standard() { use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256Plus; let mut rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -174,90 +698,54 @@ mod f64 { use crate::f64::math; use crate::{DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4}; use core::f64::consts::TAU; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; impl_float_types!(f64, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4); } mod i8 { use crate::{I8Vec2, I8Vec3, I8Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(i8, I8Vec2, I8Vec3, I8Vec4); + impl_int_types!(i8, I8Vec2, I8Vec3, I8Vec4); } mod i16 { use crate::{I16Vec2, I16Vec3, I16Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(i16, I16Vec2, I16Vec3, I16Vec4); + impl_int_types!(i16, I16Vec2, I16Vec3, I16Vec4); } mod i32 { use crate::{IVec2, IVec3, IVec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(i32, IVec2, IVec3, IVec4); + impl_int_types!(i32, IVec2, IVec3, IVec4); } mod i64 { use crate::{I64Vec2, I64Vec3, I64Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(i64, I64Vec2, I64Vec3, I64Vec4); + impl_int_types!(i64, I64Vec2, I64Vec3, I64Vec4); } mod u8 { use crate::{U8Vec2, U8Vec3, U8Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(u8, U8Vec2, U8Vec3, U8Vec4); + impl_int_types!(u8, U8Vec2, U8Vec3, U8Vec4); } mod u16 { use crate::{U16Vec2, U16Vec3, U16Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(u16, U16Vec2, U16Vec3, U16Vec4); + impl_int_types!(u16, U16Vec2, U16Vec3, U16Vec4); } mod u32 { use crate::{UVec2, UVec3, UVec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(u32, UVec2, UVec3, UVec4); + impl_int_types!(u32, UVec2, UVec3, UVec4); } mod u64 { use crate::{U64Vec2, U64Vec3, U64Vec4}; - use rand::{ - distributions::{Distribution, Standard}, - Rng, - }; - impl_vec_types!(u64, U64Vec2, U64Vec3, U64Vec4); + impl_int_types!(u64, U64Vec2, U64Vec3, U64Vec4); }