multirand 0.2.0

A threaded pseudo-random number generator
Documentation
//! # Cmn
//!
//! Defines `CreateFrom`, an internal trait to implement the conversions for primitive types

/// Conversion from u64 into other types, for extracting the random value from `ThreadRandom`
///
/// Create a Some(T), or return None if conversion fails
///
/// # Examples
/// Creating values of different types:
/// ```
/// use multirand::ThreadRandom;
/// use std::vec::Vec;
///
/// let rand = ThreadRandom::os().unwrap();
///
/// let mut v: Vec<u8> = Vec::new();
/// for _ in 0 .. 1000 { v.push(rand.random::<u8>().unwrap()); }
///
/// let i = rand.random::<u128>().unwrap();
/// ```
pub trait CreateFrom {
    /// Creates the random value
    fn create(v: [u32; 4]) -> Self;
}

// Manually implement every conversion for the primitive types
// Is this good code? No. Is it pretty? God no. Is it practical? Eh.

/// Handle the way Unicode Scalar Values work
/// Basically, pick a one of the ranges 0 - 0xD7FF or 0xE000 - 0x10FFFF at random:
/// ```
/// fn create(v: [u32; 4]) -> char {
///     if (v[1] & 1) != 0 {
///         char::from_u32(v[0] % 0xD800).expect("Oops")
///     } else {
///         char::from_u32(0xE000 + v[0] % (0x110000 - 0xE000)).expect("Oops")
///     }
/// }
/// ```
/// Only, thatd be dumb because the ranges arent equal. Assigning a 50/50 chance to picking from each range leads to an
/// uneven distribution across all possible Unicode Scalar Values, clustering around the lower range 0 - 0xD7FF.
///
/// So, instead, modulus by the number of possible USVs, and if its equal to 0xD800 or greater, add 0xE000
/// This will produce a lot of un-assigned or otherwise unused characters, but thats dependent on font. If you want ASCII
/// only then take a u8 and mod 128 :P
impl CreateFrom for char {
    fn create(v: [u32; 4]) -> char {
        let mut result = v[0] % (0xD7FF + (0xE000..=0x10FFFF).count() as u32);

        if result > 0xD7FF {
            result += 0xE000
        }

        char::from_u32(result).expect("Oops")
    }
}

impl CreateFrom for bool {
    fn create(v: [u32; 4]) -> bool {
        (v[0] & 1) != 0
    }
}

impl CreateFrom for u8 {
    fn create(v: [u32; 4]) -> u8 {
        (v[0] & 0xFF) as u8
    }
}

impl CreateFrom for i8 {
    fn create(v: [u32; 4]) -> i8 {
        (v[0] & 0xFF) as i8
    }
}

impl CreateFrom for u16 {
    fn create(v: [u32; 4]) -> u16 {
        (v[0] & 0xFFFF) as u16
    }
}

impl CreateFrom for i16 {
    fn create(v: [u32; 4]) -> i16 {
        (v[0] & 0xFFFF) as i16
    }
}

impl CreateFrom for u32 {
    fn create(v: [u32; 4]) -> u32 {
        v[0]
    }
}

impl CreateFrom for i32 {
    fn create(v: [u32; 4]) -> i32 {
        v[0] as i32
    }
}

impl CreateFrom for u64 {
    fn create(v: [u32; 4]) -> u64 {
        ((v[0] as u64) << 32) | (v[1] as u64)
    }
}

impl CreateFrom for i64 {
    fn create(v: [u32; 4]) -> i64 {
        ((v[0] as i64) << 32) | (v[1] as i64)
    }
}

impl CreateFrom for u128 {
    fn create(v: [u32; 4]) -> u128 {
        let mut i = (v[0] as u128) << 96;
        i |= (v[1] as u128) << 64;
        i |= (v[2] as u128) << 32;
        i |= v[3] as u128;
        i
    }
}

impl CreateFrom for i128 {
    fn create(v: [u32; 4]) -> i128 {
        let mut i = (v[0] as i128) << 96;
        i |= (v[1] as i128) << 64;
        i |= (v[2] as i128) << 32;
        i |= v[3] as i128;
        i
    }
}

/// To handle the platform-dependent bit width of `size`, just convert to the widest available
/// integer size and cast using `as`. Maybe there is a more elegent way to do this?
impl CreateFrom for usize {
    fn create(v: [u32; 4]) -> usize {
        let mut i = (v[0] as u128) << 96;
        i |= (v[1] as u128) << 64;
        i |= (v[2] as u128) << 32;
        i |= v[3] as u128;
        i as usize
    }
}

impl CreateFrom for isize {
    fn create(v: [u32; 4]) -> isize {
        let mut i = (v[0] as u128) << 96;
        i |= (v[1] as u128) << 64;
        i |= (v[2] as u128) << 32;
        i |= v[3] as u128;
        i as isize
    }
}

/// Mask potential NaN and -/+inf values
/// This check retains subnormal values, which are supported on IEEE 754-2008 compliant systems
/// Read [the wiki for floating point](https://en.wikipedia.org/wiki/Floating-point_arithmetic) or [the standard itself](https://ieeexplore.ieee.org/document/4610935) for more information
impl CreateFrom for f32 {
    fn create(v: [u32; 4]) -> f32 {
        let mut i = v[0];
        let mask = 0b0_11111111_00000000000000000000000;
        if (i & mask) >> 23 == 0xFF {
            i ^= (v[1] & 0xFF) << 23
        }

        f32::from_bits(i)
    }
}

impl CreateFrom for f64 {
    fn create(v: [u32; 4]) -> f64 {
        let mut i = ((v[0] as u64) << 32) | (v[1] as u64);
        let mask: u64 = 0b0_11111111111_00000000000000000000000000000000000000000000000000000;
        if (i & mask) >> 52 == 0x07FF {
            i ^= ((v[2] & 0x07FF) as u64) << 52;
        }

        f64::from_bits(i)
    }
}

// Thfs is where my negative trait bounds would go,, IF THERE WERE ANY @W@
// trait Dummy {}
// impl Dummy for u128 {}
// impl Dummy for i128 {}

// impl CreateFrom for u128 {
//    fn create(v: [u64; 4]) -> Option<u128> {
//        Some((v[0] as u128) << 64 & (v[3] as u128))
//    }
// }

// impl CreateFrom for i128 {
//     fn create(v: [u64; 4]) -> Option<i128> {
//         Some((v[0] as i128) << 64 & (v[3] as i128))
//     }
// }

// impl<T: !Dummy> CreateFrom for T
// where
//     T: TryFrom<u64> + Sized,
// {
//     fn create(v: [u64; 4]) -> Option<T> {
//         T::try_from(v[0]).ok()
//     }
// }