浏览代码

Implement erand48(), jrand48() and nrand48()

Peter Limkilde Svendsen 5 年之前
父节点
当前提交
13108776ae
共有 4 个文件被更改,包括 72 次插入15 次删除
  1. 23 3
      src/header/stdlib/lcg48.rs
  2. 21 12
      src/header/stdlib/mod.rs
  3. 3 0
      tests/expected/stdlib/lcg48.stdout
  4. 25 0
      tests/stdlib/lcg48.c

+ 23 - 3
src/header/stdlib/lcg48.rs

@@ -12,14 +12,14 @@ pub static mut XI: u64 = 0;
 pub static mut A: u64 = 0x5deece66d;
 pub static mut C: u16 = 0xb;
 
-/// Advances the linear congruential generator to the next element in its
+/// Gets the next element in the linear congruential generator's
 /// sequence.
-pub unsafe fn generator_step() {
+pub unsafe fn next_x(x: u64) -> u64 {
     /* The recurrence relation of the linear congruential generator,
      * X_(n+1) = (a * X_n + c) % m,
      * with m = 2**48. The multiplication and addition can overflow a u64, but
      * we just let it wrap since we take mod 2**48 anyway. */
-    XI = A.wrapping_mul(XI).wrapping_add(u64::from(C)) & 0xffff_ffff_ffff;
+    A.wrapping_mul(x).wrapping_add(u64::from(C)) & 0xffff_ffff_ffff
 }
 
 /// Get a C `double` from a 48-bit integer (for `drand48()` and `erand48()`).
@@ -41,3 +41,23 @@ pub fn x_to_int32(x: u64) -> c_long {
     // Cast via i32 to ensure we get the sign correct
     (x >> 16) as i32 as c_long
 }
+
+/// Build a 48-bit integer from a size-3 array of unsigned short.
+/// 
+/// Takes a pointer argument due to the inappropriate C function
+/// signatures generated from Rust's sized arrays, see
+/// https://github.com/eqrion/cbindgen/issues/171
+pub unsafe fn ushort_arr3_to_uint48(arr_ptr: *const c_ushort) -> u64 {
+    let arr = [*arr_ptr.offset(0), *arr_ptr.offset(1), *arr_ptr.offset(2)];
+    
+    /* Cast via u16 to ensure we get only the lower 16 bits of each
+     * element, as specified by POSIX. */
+    u64::from(arr[0] as u16) | (u64::from(arr[1] as u16) << 16) | (u64::from(arr[2] as u16) << 32)
+}
+
+/// Set a size-3 array of unsigned short from a 48-bit integer.
+pub unsafe fn set_ushort_arr3_from_uint48(arr_ptr: *mut c_ushort, value: u64) {
+    *arr_ptr.offset(0) = c_ushort::from(value as u16);
+    *arr_ptr.offset(1) = c_ushort::from((value >> 16) as u16);
+    *arr_ptr.offset(2) = c_ushort::from((value >> 32) as u16);
+}

+ 21 - 12
src/header/stdlib/mod.rs

@@ -232,7 +232,7 @@ pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t {
 
 #[no_mangle]
 pub unsafe extern "C" fn drand48() -> c_double {
-    lcg48::generator_step();
+    lcg48::XI = lcg48::next_x(lcg48::XI);
     lcg48::x_to_float64(lcg48::XI)
 }
 
@@ -246,9 +246,12 @@ pub extern "C" fn ecvt(
     unimplemented!();
 }
 
-// #[no_mangle]
-pub extern "C" fn erand(xsubi: [c_ushort; 3]) -> c_double {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double {
+    let old_xi = lcg48::ushort_arr3_to_uint48(xsubi);
+    let new_xi = lcg48::next_x(old_xi);
+    lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi);
+    lcg48::x_to_float64(new_xi)
 }
 
 #[no_mangle]
@@ -361,9 +364,12 @@ pub extern "C" fn initstate(seec: c_uint, state: *mut c_char, size: size_t) -> *
     unimplemented!();
 }
 
-// #[no_mangle]
-pub extern "C" fn jrand48(xsubi: [c_ushort; 3]) -> c_long {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long {
+    let old_xi = lcg48::ushort_arr3_to_uint48(xsubi);
+    let new_xi = lcg48::next_x(old_xi);
+    lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi);
+    lcg48::x_to_int32(new_xi)
 }
 
 // #[no_mangle]
@@ -416,7 +422,7 @@ pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t {
 
 #[no_mangle]
 pub unsafe extern "C" fn lrand48() -> c_long {
-    lcg48::generator_step();
+    lcg48::XI = lcg48::next_x(lcg48::XI);
     lcg48::x_to_uint31(lcg48::XI)
 }
 
@@ -573,13 +579,16 @@ pub extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int {
 
 #[no_mangle]
 pub unsafe extern "C" fn mrand48() -> c_long {
-    lcg48::generator_step();
+    lcg48::XI = lcg48::next_x(lcg48::XI);
     lcg48::x_to_int32(lcg48::XI)
 }
 
-// #[no_mangle]
-pub extern "C" fn nrand48(xsubi: [c_ushort; 3]) -> c_long {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long {
+    let old_xi = lcg48::ushort_arr3_to_uint48(xsubi);
+    let new_xi = lcg48::next_x(old_xi);
+    lcg48::set_ushort_arr3_from_uint48(xsubi, new_xi);
+    lcg48::x_to_uint31(new_xi)
 }
 
 // #[no_mangle]

+ 3 - 0
tests/expected/stdlib/lcg48.stdout

@@ -2,3 +2,6 @@ lrand48 (uninitialized): 0 2116118 89401895 379337186 782977366 196130996 198207
 drand48: 0.750266 0.607593 0.567593 0.799563 0.984984 0.205670 0.625922 0.794426 0.369416 0.854100
 lrand48: 1611183183 1304796356 1218897288 1717049088 2115236938 441672110 1344158015 1706017430 793314380 1834165927
 mrand48: -1072600929 -1685374584 -1857172720 -860869119 -64493420 883344220 -1606651266 -882932436 1586628760 -626635441
+erand48: 0.210555 0.014158 0.111353 0.658369 0.103767 0.180385 0.945033 0.745768 0.290272 0.111716
+nrand48: 514983590 590935818 1480794144 1496813112 2133865028 1816766485 2095074020 126058208 909762120 14734916
+jrand48: -1066398599 903693914 -1922375113 -2090140830 1218074962 1662411059 -722435322 764426686 -874142666 -1454656015

+ 25 - 0
tests/stdlib/lcg48.c

@@ -7,6 +7,7 @@ int main(void) {
     long x_l, x_m;
     double x_d;
     long seedval = 0xcafebeef;
+    unsigned short xsubi[3] = {0xabcd, 0xef42, 0x5678};
     
     printf("lrand48 (uninitialized):");
     for (int i = 0; i < 10; i++)
@@ -42,4 +43,28 @@ int main(void) {
         printf(" %ld", x_m);
     }
     printf("\n");
+    
+    printf("erand48:");
+    for (int i = 0; i < 10; i++)
+    {
+        x_d = erand48(xsubi);
+        printf(" %lf", x_d);
+    }
+    printf("\n");
+    
+    printf("nrand48:");
+    for (int i = 0; i < 10; i++)
+    {
+        x_l = nrand48(xsubi);
+        printf(" %ld", x_l);
+    }
+    printf("\n");
+    
+    printf("jrand48:");
+    for (int i = 0; i < 10; i++)
+    {
+        x_l = jrand48(xsubi);
+        printf(" %ld", x_l);
+    }
+    printf("\n");
 }