Skip to main content
added 13 characters in body
Source Link
Shepmaster
  • 8.8k
  • 28
  • 28

http://school.anhb.uwa.edu.au/personalpages/kwessen/shared/Marsaglia03.html https://stackoverflow.com/questions/12884351/complement-multiply-with-carry-cmwc-rng-c-winmain16-compile-issue

  • In fn new()fn new(), having q_init "mut"q_init be mut seems like a hack, is there a way more functional way to do this using slices?
  • Is this an appropriate struct to use the DefaultDefault trait? I read that it's not recommended to use that.
  • There's a lot of "as usize"as usize around, I don't quite have the feel for casting types as I would do in C++.
  • fn reset()fn reset() seems ridiculous, is there a better way to reassign all values at once?

http://school.anhb.uwa.edu.au/personalpages/kwessen/shared/Marsaglia03.html https://stackoverflow.com/questions/12884351/complement-multiply-with-carry-cmwc-rng-c-winmain16-compile-issue

  • In fn new(), having q_init "mut" seems like a hack, is there a way more functional way to do this using slices?
  • Is this an appropriate struct to use the Default trait? I read that it's not recommended to use that.
  • There's a lot of "as usize" around, I don't quite have the feel for casting types as I would do in C++.
  • fn reset() seems ridiculous, is there a better way to reassign all values at once?
  • In fn new(), having q_init be mut seems like a hack, is there a more functional way to do this using slices?
  • Is this an appropriate struct to use the Default trait? I read that it's not recommended to use that.
  • There's a lot of as usize around, I don't quite have the feel for casting types as I would do in C++.
  • fn reset() seems ridiculous, is there a better way to reassign all values at once?
Source Link

Complementary multiply with carry in Rust

Background and motivation

I'm a C++ developer with a passion for modern, multi-threaded and functional code. I came across Rust not so long ago and it sounded like everything I wanted my C++ code to be. Naturally, I decided to try it out.

I was working on my own C++ project that had a need for some random numbers. I thought it would be a good opportunity to use some Rust and call into it from my C++ code. After some messing about, I thought I would convert some C/C++ code to Rust from the following pages:

http://school.anhb.uwa.edu.au/personalpages/kwessen/shared/Marsaglia03.html https://stackoverflow.com/questions/12884351/complement-multiply-with-carry-cmwc-rng-c-winmain16-compile-issue

Long story short, I ended up with the following code:

Code

#[macro_use]
extern crate lazy_static;

mod pointgen_random
{    
    const CMWC_CYCLE: u32 = 4096;
    const PHI: u32 = 0x9e3779b9;

    struct ComplementaryMultiplyWithCarryGen {
        q: [u32; CMWC_CYCLE as usize],
        c: u32,
        i: usize
    }

    impl ComplementaryMultiplyWithCarryGen {

        fn new(seed: u32) -> ComplementaryMultiplyWithCarryGen {
            let mut q_init = [0; CMWC_CYCLE as usize];

            q_init[0] = seed;
            q_init[1] = seed + PHI;
            q_init[2] = seed + PHI + PHI;

            for i in 3..CMWC_CYCLE as usize{
                q_init[i] = q_init[i - 3] ^ q_init[i - 2] ^ PHI ^ seed;
            }

            return ComplementaryMultiplyWithCarryGen{q: q_init, c: 362436, i: 4095}; 
        }

        fn reset(&mut self, seed: u32){
            let cmwc = ComplementaryMultiplyWithCarryGen::new(seed);
            self.q = cmwc.q;
            self.i = cmwc.i;
            self.c = cmwc.c;
    }

        fn random(&mut self) -> u32{
            const A: u64 = 18782;
            const R: u32 = 0xfffffffe;

            self.i = (self.i + 1) & (CMWC_CYCLE as usize - 1);
            let t = A * self.q[self.i] as u64 + self.c as u64;
        
            self.c = (t >> 32) as u32;
            let mut x = (t + self.c as u64) as u32;
            if x < self.c {
                x+=1;
                self.c+=1;
            }

            self.q[self.i] = R - x;
            return self.q[self.i];
        }
    }

    use std::sync::Mutex;

    // Lazy singleton random number generator
    lazy_static!{
            static ref GLOBAL_CMWC_GEN: Mutex<ComplementaryMultiplyWithCarryGen> = Mutex::new(ComplementaryMultiplyWithCarryGen::new(0));
    }

    pub fn get_random_cmwc() -> u32
    {
        GLOBAL_CMWC_GEN.lock().unwrap().random()
    }

    pub fn set_cmwc_seed(seed: u32)
    {
        GLOBAL_CMWC_GEN.lock().unwrap().reset(seed);
    }
}

// Public exports for PointGenLib
// All prefixed with pointgen_ to avoid name conflicts
#[no_mangle]
pub extern "C" fn pointgen_set_cmwc_seed(seed: u32)
{
    pointgen_random::set_cmwc_seed(seed);
}

#[no_mangle]
pub extern "C" fn pointgen_random_cmwc() -> u32
{
    return pointgen_random::get_random_cmwc();
}

It compiles on Windows without warning and appears to give some sensible pseudo-random numbers from 0 to 2^32, but there's just a lot of things in it that I don't like or don't feel right:

  • In fn new(), having q_init "mut" seems like a hack, is there a way more functional way to do this using slices?
  • Is this an appropriate struct to use the Default trait? I read that it's not recommended to use that.
  • There's a lot of "as usize" around, I don't quite have the feel for casting types as I would do in C++.
  • fn reset() seems ridiculous, is there a better way to reassign all values at once?

Any other help/suggestions/advice you can give would be very much appreciated.