1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- G N A T . M B B S _ F L O A T _ R A N D O M --
9 -- Copyright (C) 1992-2018, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
34 package body GNAT
.MBBS_Float_Random
is
36 -------------------------
37 -- Implementation Note --
38 -------------------------
40 -- The design of this spec is a bit awkward, as a result of Ada 95 not
41 -- permitting in-out parameters for function formals (most naturally
42 -- Generator values would be passed this way). In pure Ada 95, the only
43 -- solution would be to add a self-referential component to the generator
44 -- allowing access to the generator object from inside the function. This
45 -- would work because the generator is limited, which prevents any copy.
47 -- This is a bit heavy, so what we do is to use Unrestricted_Access to
48 -- get a pointer to the state in the passed Generator. This works because
49 -- Generator is a limited type and will thus always be passed by reference.
51 package Calendar
renames Ada
.Calendar
;
53 type Pointer
is access all State
;
55 -----------------------
56 -- Local Subprograms --
57 -----------------------
59 procedure Euclid
(P
, Q
: Int
; X
, Y
: out Int
; GCD
: out Int
);
61 function Euclid
(P
, Q
: Int
) return Int
;
63 function Square_Mod_N
(X
, N
: Int
) return Int
;
69 procedure Euclid
(P
, Q
: Int
; X
, Y
: out Int
; GCD
: out Int
) is
75 (P
, Q
: Int
; -- a (i-1), a (i)
76 X
, Y
: Int
; -- x (i), y (i)
77 XP
, YP
: in out Int
; -- x (i-1), y (i-1)
86 Quo
: Int
:= P
/ Q
; -- q <-- |_ a (i-1) / a (i) _|
87 XT
: Int
:= X
; -- x (i)
88 YT
: Int
:= Y
; -- y (i)
91 if P
rem Q
= 0 then -- while does not divide
96 Recur
(Q
, P
- Q
* Quo
, XP
- Quo
* X
, YP
- Quo
* Y
, XT
, YT
, Quo
);
99 -- a (i+1) <-- a (i-1) - q*a (i)
100 -- x (i+1) <-- x (i-1) - q*x (i)
101 -- y (i+1) <-- y (i-1) - q*y (i)
111 -- Start of processing for Euclid
114 Recur
(P
, Q
, 0, 1, XT
, YT
, GCD
);
119 function Euclid
(P
, Q
: Int
) return Int
is
121 pragma Unreferenced
(Y
, GCD
);
123 Euclid
(P
, Q
, X
, Y
, GCD
);
131 function Image
(Of_State
: State
) return String is
133 return Int
'Image (Of_State
.X1
) & ',' & Int
'Image (Of_State
.X2
)
135 Int
'Image (Of_State
.P
) & ',' & Int
'Image (Of_State
.Q
);
142 function Random
(Gen
: Generator
) return Uniformly_Distributed
is
143 Genp
: constant Pointer
:= Gen
.Gen_State
'Unrestricted_Access;
146 Genp
.X1
:= Square_Mod_N
(Genp
.X1
, Genp
.P
);
147 Genp
.X2
:= Square_Mod_N
(Genp
.X2
, Genp
.Q
);
149 Float ((Flt
(((Genp
.X2
- Genp
.X1
) * Genp
.X
)
150 mod Genp
.Q
) * Flt
(Genp
.P
)
151 + Flt
(Genp
.X1
)) * Genp
.Scl
);
158 -- Version that works from given initiator value
160 procedure Reset
(Gen
: Generator
; Initiator
: Integer) is
161 Genp
: constant Pointer
:= Gen
.Gen_State
'Unrestricted_Access;
165 X1
:= 2 + Int
(Initiator
) mod (K1
- 3);
166 X2
:= 2 + Int
(Initiator
) mod (K2
- 3);
168 -- Eliminate effects of small initiators
171 X1
:= Square_Mod_N
(X1
, K1
);
172 X2
:= Square_Mod_N
(X2
, K2
);
184 -- Version that works from specific saved state
186 procedure Reset
(Gen
: Generator
; From_State
: State
) is
187 Genp
: constant Pointer
:= Gen
.Gen_State
'Unrestricted_Access;
190 Genp
.all := From_State
;
193 -- Version that works from calendar
195 procedure Reset
(Gen
: Generator
) is
196 Genp
: constant Pointer
:= Gen
.Gen_State
'Unrestricted_Access;
197 Now
: constant Calendar
.Time
:= Calendar
.Clock
;
201 X1
:= Int
(Calendar
.Year
(Now
)) * 12 * 31 +
202 Int
(Calendar
.Month
(Now
)) * 31 +
203 Int
(Calendar
.Day
(Now
));
205 X2
:= Int
(Calendar
.Seconds
(Now
) * Duration (1000.0));
207 X1
:= 2 + X1
mod (K1
- 3);
208 X2
:= 2 + X2
mod (K2
- 3);
210 -- Eliminate visible effects of same day starts
213 X1
:= Square_Mod_N
(X1
, K1
);
214 X2
:= Square_Mod_N
(X2
, K2
);
231 procedure Save
(Gen
: Generator
; To_State
: out State
) is
233 To_State
:= Gen
.Gen_State
;
240 function Square_Mod_N
(X
, N
: Int
) return Int
is
241 Temp
: constant Flt
:= Flt
(X
) * Flt
(X
);
245 Div
:= Int
(Temp
/ Flt
(N
));
246 Div
:= Int
(Temp
- Flt
(Div
) * Flt
(N
));
259 function Value
(Coded_State
: String) return State
is
260 Last
: constant Natural := Coded_State
'Last;
261 Start
: Positive := Coded_State
'First;
262 Stop
: Positive := Coded_State
'First;
266 while Stop
<= Last
and then Coded_State
(Stop
) /= ',' loop
271 raise Constraint_Error
;
274 Outs
.X1
:= Int
'Value (Coded_State
(Start
.. Stop
- 1));
279 exit when Stop
> Last
or else Coded_State
(Stop
) = ',';
283 raise Constraint_Error
;
286 Outs
.X2
:= Int
'Value (Coded_State
(Start
.. Stop
- 1));
291 exit when Stop
> Last
or else Coded_State
(Stop
) = ',';
295 raise Constraint_Error
;
298 Outs
.P
:= Int
'Value (Coded_State
(Start
.. Stop
- 1));
299 Outs
.Q
:= Int
'Value (Coded_State
(Stop
+ 1 .. Last
));
300 Outs
.X
:= Euclid
(Outs
.P
, Outs
.Q
);
301 Outs
.Scl
:= 1.0 / (Flt
(Outs
.P
) * Flt
(Outs
.Q
));
303 -- Now do *some* sanity checks
305 if Outs
.Q
< 31 or else Outs
.P
< 31
306 or else Outs
.X1
not in 2 .. Outs
.P
- 1
307 or else Outs
.X2
not in 2 .. Outs
.Q
- 1
309 raise Constraint_Error
;
314 end GNAT
.MBBS_Float_Random
;