summaryrefslogtreecommitdiff
path: root/ipl/procs/random.icn
blob: 8dc58f27a5184a66777c0c2a99960917f0da3560 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
############################################################################
#
#	File:     random.icn
#
#	Subject:  Procedures related to random numbers
#
#	Authors:  Ralph E. Griswold and Gregg M. Townsend
#
#	Date:     June 24, 2002
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This file contains procedures related to pseudo-random numbers.
#
#	rand_num()	is a linear congruential pseudo-random number
#			generator.  Each time it is called, it produces
#			another number in the sequence and also assigns it
#			to the global variable random.  With no arguments,
#			rand_num() produces the same sequence(s) as Icon's
#			built-in random-number generator.  Arguments can be
#			used to get different sequences.
#
#			The global variable random serves the same role that
#			&random does for Icon's built-in random number
#			generator.
#
#	rand_int(i)	produces a randomly selected integer in the range 1
#			to i.  It models ?i for positive i.
#
#	randomize()	sets &random to a "random" value, using /dev/urandom
#			if available, otherwise based on the date and time.
#
#	randrange(min, max)
#			produces random number in the range min <= i <= max.
#
#	randrangeseq(i, j)
#			generates the integers from i to j in random order.
#
#
#	randseq(seed)	generates the values of &random, starting at seed,
#			that occur as the result of using ?x.
#
#	rng(a, c, m, x)	generates a sequence of numbers using the linear
#			congruence method.  With appropriate parameters, the
#		 	result is a pseudo-random sequence.  The default
#			values produce the sequence used in Icon.
#
#	shuffle(x)	shuffles the elements of x
#
############################################################################
#
#  Links:  factors
#
############################################################################

link factors

global random

procedure rand_num(a_, c_, m_)		#: random number generator
   static random_last, a, c, m

   initial {
      /random := 0
      a := \a_ | 1103515245
      c := \c_ | 453816694
      m := (\m_ | 2 ^ 31)
      }

    return random := (a * random + c) % m
     
end

procedure rand_int(i)			#: model ?i
   static scale

   initial scale := 1.0 / (2 ^ 31 - 1)

   (i := (0 < integer(i))) | runerr(205, i)

   return integer(i * rand_num() * scale) + 1

end

procedure randomize()			#: randomize
   local f, s
   static ncalls
   initial ncalls := 0

   ncalls +:= 1

   if f := open("/dev/urandom", "ru") then {
      s := reads(f, 3)
      close(f)
      if *\s > 0 then {
         &random := ncalls % 113
         every &random := 256 * &random + ord(!s)
         return
         }
      }

   &random := map("sSmMhH", "Hh:Mm:Ss", &clock) +
      map("YyXxMmDd", "YyXx/Mm/Dd", &date) + &time + 1009 * ncalls

   return

end

procedure randrange(min, max)		#: random number in range

   return min - 1 + ?(max - min + 1)

end

procedure randrangeseq(i, j)		#: random sequence in range
   local x, m, a, c, n

   n := j - i + 1

   if n < 0 then fail

   x := 1
   m := nxtprime(n)
   a := m + 1
   c := nxtprime(m)

   every 1 to m do {
      x := (a * x + c) % m
      if x < n then {		# discard out-of-range values
         suspend x + i
         }
      }

end

procedure randseq(seed)			#: generate &random

   suspend &random := seed
   suspend |?1 & &random

end

procedure rng(a, c, m, x)	#: random number generator

   /a := 1103515245			# multiplicative constant
   /c := 453816694			# additive constant
   /m := 2 ^ 31 - 1			# modulus
   /x := 0				# initial value

   suspend x
   suspend x := iand(a * |x + c, m)

end

#  The procedure shuffle(x) shuffles a string, list, or record.
#  In the case that x is a string, a corresponding string with the
#  characters randomly rearranged is produced. In the case that x is
#  list or records the elements are randomly rearranged.
   
procedure shuffle(x)			#: shuffle
   local i

   x := string(x)		# may fail
   every i := *x to 2 by -1 do
      x[?i] :=: x[i]
   return x
end

#  Note:  the following procedure is simpler, but does not produce
#  as good a shuffle:
#
#procedure shuffle(x)
#   x := string(x)
#   every !x :=: ?x
#   return x
#end