[m-rev.] for review: Add random.init/3

Paul Bone paul at bone.id.au
Wed May 25 15:55:50 AEST 2016


For review by anyone

----

Add random.init/3

random.init/3 makes it easier to seed the random number generator.  It will
calculate a seed based on the current time and, if known, the process ID.
This is sufficient for simple applications but shouldn't be used for
security-sensitive applications.

library/random.m:
    As above.

NEWS:
    Announce the new addition.
---
 NEWS             |  3 +++
 library/random.m | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/NEWS b/NEWS
index 5628bb5..7388ba0 100644
--- a/NEWS
+++ b/NEWS
@@ -336,6 +336,9 @@ Changes to the Mercury standard library:
 
    - is_empty/1
 
+* We have added a new init/3 predicate to the random module.  This predicate
+  seeds the PRNG using the current time and process ID (if known).
+
 Changes to the Mercury compiler:
 
 * We have added a new option --warn-dead-preds. While the existing option
diff --git a/library/random.m b/library/random.m
index 2ce0150..5aacf1f 100644
--- a/library/random.m
+++ b/library/random.m
@@ -51,6 +51,7 @@
 :- module random.
 :- interface.
 
+:- import_module io.
 :- import_module list.
 
 %---------------------------------------------------------------------------%
@@ -65,6 +66,16 @@
     %
 :- pred init(int::in, supply::uo) is det.
 
+    % init(RS, !IO).
+    %
+    % Creates a supply of random numbers seeded based on the current time of
+    % day and the process ID (if known).
+    %
+    % NOTE: This is not suitable for security but is still suitable for some
+    % applications.
+    %
+:- pred init(supply::uo, io::di, io::uo) is det.
+
     % random(Num, !RS).
     %
     % Extracts a number Num in the range 0 .. RandMax from the random number
@@ -130,6 +141,7 @@
 
 :- import_module array.
 :- import_module int.
+:- import_module time.
 
 :- type supply
     --->    rs(int). % I(j)
@@ -141,6 +153,42 @@ params(9301, 49297, 233280).
 init(I0, rs(RS)) :-
     copy(I0, RS).
 
+init(RS, !IO) :-
+    get_pid(Pid, !IO),
+    time(Time, !IO),
+    localtime(Time) = TM,
+    TM = tm(Year, Month, Day, Hour, Min, Sec, _, _, _),
+    % For seeding the RNG 31 days in a month is fine.
+    Now = Sec * 60*60*24*31*12 +
+          Min * 60*24*31*12 +
+          Hour * 24*31*12 +
+          Day * 31*12 +
+          Month * 12 +
+          Year,
+    ( if Pid = 0 then
+        Seed = Now
+    else
+        Seed = (Now << 16) \/ Pid
+    ),
+    init(Seed, RS).
+
+:- pred get_pid(int::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+    get_pid(PID::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+#if defined (WIN32) && !defined (__CYGWIN32__)
+    PID = GetCurrentProcessId();
+#elif MR_HAVE_GETPID
+    PID = getpid();
+#else
+    PID = 0;
+#endif
+").
+
+get_pid(0, !IO).
+
 random(I, rs(RS0), rs(RS)) :-
     RS0 = I0,
     random.params(A, C, M),
-- 
2.8.1



More information about the reviews mailing list