FFI introduction : Mersenne Twister
If your random number generator is not the Mersenne Twister, may I suggest to replace it with the Mersenne Twister of Makoto Matsumoto and Takuji Nishimura. This would significantly and instantly enhance the quality of your ML code that uses random numbers.
In SML#, this is done by the following trivial 15 minute process; no stub or data conversion is necessary.
$ tar xvfz mt19937ar.tgz ./ ./mt19937ar.c ./mt19937ar.out ./readme-mt.txt
(Comments) A faster and enhanced version SFMT has also been released. Using this should equally be simple (if your ML compiler is SML#). So I recommend to try using this version by yourself.
In Linux OS, the following one line will do:
$ gcc -shared -o mt19937ar.so mt19937ar.c
The resulting file mt19937ar.so contains the following 8 functions.
void init_genrand(unsigned long s) void init_by_array(unsigned long init_key, int key_length) unsigned long genrand_int32(void) long genrand_int31(void) double genrand_real1(void) double genrand_real2(void) double genrand_real3(void) double genrand_res53(void) int main(void)
To open the library mt19937ar.so, use the following FFI library functions:
# DynamicLink.dlopen; val it = fn : string -> unit ptr # DynamicLink.dlsym; val it = fn : unit ptr * string -> unit ptr
dlopen(path) opens the file path path of a dynamically linked library. dlsym(ptr, s) searches the function name s in the library pointed by the handle ptr, and returns a pointer to the function.
The returned C function pointer is imported to SML# name space by the following SML# special construct:
exp _import : (t1,...,tn) -> t
It assumes exp to be a C pointer to the function that takes n arguments of types t1,...,tn and returns a value of type t, and converts it to an SML# function of the corresponding type
t1 * ... * tn -> t
So, you can simply write the following declaration that corresponds to the headers of 8 C functions in mt19937ar.
structure MT = struct local val mtLib = DynamicLink.dlopen "./mt19937ar.so" fun find s = DynamicLink.dlsym (mtLib,s) in val init_genrand = find "init_genrand" : _import (int) -> void val init_by_array = find "init_by_array" : _import (int array,int) -> void val genrand_int32 = find "genrand_int32" : _import () -> int val genrand_int31 = find "genrand_int31" : _import () -> int val genrand_real1 = find "genrand_real1" : _import () -> real val genrand_real2 = find "genrand_real2" : _import () -> real val genrand_real3 = find "genrand_real3" : _import () -> real val genrand_res53 = find "genrand_res53" : _import () -> real val main = find "main" : _import () -> int end end
When this is evaluated, SML# print the following.
# use "mt.sml"; structure MT : sig val genrand_int31 : unit -> int val genrand_int32 : unit -> int val genrand_real1 : unit -> real val genrand_real2 : unit -> real val genrand_real3 : unit -> real val genrand_res53 : unit -> real val init_by_array : int Array.array * int -> unit val init_genrand : int -> unit val main : unit -> int end #
That's all. You are ready to use the Mersenne Twister by using these 8 functions with your favorite higher-order functions.
To test, try invoking the main function, which is the test program provided in the original package.
# MT.main(); 1000 outputs of genrand_int32() 1067595299 955945823 477289528 4107218783 4228976476 ...