1if not modules then modules = { } end modules ['supp-ran'] = {
2 version = 1.001,
3 comment = "companion to supp-ran.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11local report_system = logs.reporter("system","randomizer")
12
13local trace_random = false trackers.register("system.randomizer", function(v) trace_random = v end)
14local trace_details = false trackers.register("system.randomizer.details", function(v) trace_random = v trace_details = v end)
15
16local insert, remove = table.insert, table.remove
17
18local tonumber = tonumber
19local sub = string.sub
20local math = math
21local context = context
22local implement = interfaces.implement
23
24local random = math.random
25local randomseed = math.randomseed
26local round = math.round
27local stack = { }
28local last = 1
29local maxcount = 0x3FFFFFFF
30
31math.random = function(...)
32 local n = random(...)
33 if trace_details then
34 report_system("math %s",n)
35 end
36 return n
37end
38
39local function setrandomseedi(n)
40 if n <= 1 then
41 n = n * maxcount
42 elseif n < 1000 then
43 n = n * 1000
44 end
45 n = round(n)
46 randomseed(n)
47 last = random(0,maxcount)
48 if trace_details then
49 report_system("seed %s from %s",last,n)
50 elseif trace_random then
51 report_system("setting seed %s",n)
52 end
53end
54
55math.setrandomseedi = setrandomseedi
56
57local function getrandomnumber(min,max)
58 if min > max then
59 min, max = max, min
60 end
61 last = random(min,max)
62 if trace_details then
63 report_system("number %s",last)
64 end
65 return last
66end
67
68local function setrandomseed(n)
69 last = n
70 setrandomseedi(n)
71end
72
73local function getrandomseed()
74 return last
75end
76
77
78
79
80
81
82
83
84
85
86
87local function pushrandomseed()
88
89 insert(stack,randomseed(last) or last)
90 if trace_random or trace_details then
91 report_system("pushing seed %s",last)
92 end
93end
94
95local function reuserandomseed(n)
96 local seed = stack[#stack]
97 if seed then
98 if trace_random or trace_details then
99 report_system("reusing seed %s",last)
100 end
101 randomseed(seed)
102 end
103end
104
105local function poprandomseed()
106 local seed = remove(stack)
107 if seed then
108 if trace_random or trace_details then
109 report_system("popping seed %s",seed)
110 end
111 randomseed(seed)
112 end
113end
114
115local function getrandom(where,...)
116 if type(where) == "string" then
117 local n = random(...)
118 if trace_details then
119 report_system("%s %s",where,n)
120 end
121 return n
122 else
123 local n = random(where,...)
124 if trace_details then
125 report_system("utilities %s",n)
126 end
127 return n
128 end
129end
130
131
132
133implement { name = "getrandomnumber", actions = { getrandomnumber, context }, arguments = { "integer", "integer" } }
134implement { name = "getrandomdimen", actions = { getrandomnumber, context }, arguments = { "dimen", "dimen" } }
135implement { name = "getrandomfloat", actions = { getrandomnumber, context }, arguments = { "number", "number" } }
136implement { name = "getrandomseed", actions = { getrandomseed, context } }
137implement { name = "setrandomseed", actions = setrandomseed, arguments = "integer" }
138implement { name = "pushrandomseed", actions = pushrandomseed, public = true, }
139implement { name = "poprandomseed", actions = poprandomseed, public = true, }
140implement { name = "reuserandomseed", actions = reuserandomseed, public = true, }
141
142
143
144local newrepeatable, getrepeatable, getrepeatableseed, repeatable
145
146do
147
148 local default = environment.version or "context lmtx"
149 local hashed = md5.HEX
150
151 local list = { }
152 local saved = false
153
154 newrepeatable = function(name,seed)
155 if not name or name == "" then
156 name = "default"
157 seed = default
158 elseif not seed then
159 seed = default
160 end
161 if not saved then
162 saved = { }
163 job.variables.collected.repeatable = saved
164 end
165 saved[name] = seed
166 local hash = hashed(seed)
167 if trace_random then
168 report_system("repeatable %a with seed %a starts out as %a",name,seed,hash)
169 end
170 local func = function()
171 local n = tonumber(sub(hash,1,8),16)
172
173 local r = n / 0xFFFFFFFF
174
175 hash = hashed(hash)
176 if trace_details then
177 report_system("repeatable %a moves on to %a giving %i and %0.9f",name,hash,n,r)
178 end
179 return r
180 end
181 list[name] = func
182
183
184 return func
185 end
186
187 table.setmetatableindex(list,function(t,k)
188 local v = rawget(t,"default")
189 if not v then
190 v = newrepeatable("default",default)
191 end
192 t[k] = v
193 return v
194 end)
195
196 getrepeatable = function(name)
197 return list[name or "default"]
198 end
199
200 repeatable = function(name)
201 return list[name or "default"]()
202 end
203
204 getrepeatableseed = function(name)
205 local r = job.variables.collected.repeatable
206 return r and r[name or "default"] or default
207 end
208
209 implement {
210 name = "newrepeatablerandom",
211 public = true,
212 protected = true,
213 arguments = { "csnameunchecked", "argument" },
214 actions = function(c,s)
215
216
217 implement {
218 name = c,
219 public = true,
220 actions = { newrepeatable(c,s), context },
221 }
222 end
223 }
224
225end
226
227
228
229utilities.randomizer = {
230 setseedi = setrandomseedi,
231 getnumber = getrandomnumber,
232 setseed = setrandomseed,
233 getseed = getrandomseed,
234
235 pushseed = pushrandomseed,
236 reuseseed = reuserandomseed,
237 popseed = poprandomseed,
238 get = getrandom,
239
240
241 newrepeatable = newrepeatable,
242 getrepeatable = getrepeatable,
243 getrepeatableseed = getrepeatableseed,
244 repeatable = repeatable,
245}
246 |