use core::ops::{Range, RangeInclusive};
use rand::distributions::uniform::{
SampleBorrow, SampleRange, SampleUniform, UniformFloat, UniformSampler,
};
use rand::distributions::{Distribution, Standard};
use rand::Rng;
use crate::float::Float;
use crate::units::{Degrees, Gradians, Radians, Turns};
use crate::{Angle, AngleUnbounded};
impl<F: Float> Distribution<Angle<F>> for Standard
where
Self: Distribution<F>,
{
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Angle<F> {
let x = rng.gen::<F>();
let x = x * F::TAU; let x = x - F::PI; let x = -x;
debug_assert!(-F::PI < x && x <= F::PI);
Angle::from_radians_unchecked(x)
}
}
impl<F: Float> Distribution<AngleUnbounded<F>> for Standard
where
Self: Distribution<Angle<F>>,
{
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AngleUnbounded<F> {
rng.gen::<Angle<F>>().to_unbounded()
}
}
macro_rules! impl_distribution_for_unit {
(
$($Unit:ident),+
) => {
$(
impl<T> Distribution<$Unit<T>> for Standard
where
Self: Distribution<T>,
{
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $Unit<T> {
$Unit(rng.gen())
}
}
)+
};
}
impl_distribution_for_unit!(Radians, Degrees, Turns, Gradians);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UniformAngle<F>(UniformFloat<F>);
impl<F: Float> SampleUniform for Angle<F>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
type Sampler = UniformAngle<F>;
}
impl<F: Float> UniformSampler for UniformAngle<F>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
type X = Angle<F>;
#[inline]
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low.borrow().to_radians();
let mut high = high.borrow().to_radians();
if low > high {
high += F::TAU;
}
Self(UniformFloat::new(low, high))
}
#[inline]
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low.borrow().to_radians();
let mut high = high.borrow().to_radians();
if low > high {
high += F::TAU;
}
Self(UniformFloat::new_inclusive(low, high))
}
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let x = self.0.sample(rng);
Angle::from_radians(x)
}
}
impl<F: Float> SampleRange<Angle<F>> for Range<Angle<F>>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
#[inline]
fn sample_single<R: rand::RngCore + ?Sized>(self, rng: &mut R) -> Angle<F> {
UniformAngle::sample_single(self.start, self.end, rng)
}
#[inline]
fn is_empty(&self) -> bool {
self.start == self.end
}
}
impl<F: Float> SampleRange<Angle<F>> for RangeInclusive<Angle<F>>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
#[inline]
fn sample_single<R: rand::RngCore + ?Sized>(self, rng: &mut R) -> Angle<F> {
UniformAngle::sample_single_inclusive(self.start(), self.end(), rng)
}
#[inline]
fn is_empty(&self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UniformAngleUnbounded<F>(UniformFloat<F>);
impl<F: Copy> SampleUniform for AngleUnbounded<F>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
type Sampler = UniformAngleUnbounded<F>;
}
impl<F: Copy> UniformSampler for UniformAngleUnbounded<F>
where
UniformFloat<F>: UniformSampler<X = F>,
F: SampleBorrow<F>,
{
type X = AngleUnbounded<F>;
fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low.borrow().to_radians();
let high = high.borrow().to_radians();
Self(UniformFloat::new(low, high))
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low.borrow().to_radians();
let high = high.borrow().to_radians();
Self(UniformFloat::new_inclusive(low, high))
}
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let x = self.0.sample(rng);
AngleUnbounded::from_radians(x)
}
}
#[cfg(test)]
mod tests {
use rand::Rng;
use crate::{units::*, Angle32, Angle64, AngleUnbounded32, AngleUnbounded64};
#[test]
fn check_rand_impl() {
let mut rng = rand::thread_rng();
macro_rules! check {
(
$($angle:ident),+
) => {
$(
let _: $angle = rand::random();
let _: Radians<$angle> = rand::random();
let _: Degrees<$angle> = rand::random();
let _: Turns<$angle> = rand::random();
let _: Gradians<$angle> = rand::random();
let _: $angle = rng.gen_range($angle::ZERO..$angle::RAD_PI);
let _: $angle = rng.gen_range($angle::ZERO..=$angle::RAD_PI);
)+
};
}
check!(Angle32, Angle64, AngleUnbounded32, AngleUnbounded64);
}
}