-
Notifications
You must be signed in to change notification settings - Fork 0
/
nDRandom.tcl
294 lines (294 loc) · 9.77 KB
/
nDRandom.tcl
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#nDRandom
#nDRandom.tcl
#
##===================================================================
# Copyright (c) 2021 Yuji SODE <yuji.sode@gmail.com>
#
# This software is released under the MIT License.
# See LICENSE or http://opensource.org/licenses/mit-license.php
##===================================================================
#Tool that outputs a random coordinates generator in n-th dimensions
#
#=== Synopsis ===
#*** [shell] ***
# - `tclsh nDRandom.tcl name option min1 max1 division1 ?min2 max2 division2 ?... minN maxN divisionN??;`
# - $name: a keyword to determine output file name and its functions
# - $option: a keyword to determine output type
# - $min1, $max1 and $division1: data range
# - $min2, $max2 and $division2 (, ..., $minN, $maxN and $divisionN): optional data ranges
# - $division: number of divided ranges that are not less than 2
#
#*** [Tcl] ***
# - `::nDRandom::setRange min max division;`: loads a variable range
# - `::nDRandom::output name ?option?;`: outputs a random coordinates generater in the current directory
#
# - $min and $max: minimum and maximum values
# - $division: number of divided ranges that are not less than 2
# - $name: a keyword to determine output file and its functions
# - $option: an optional keyword to determine output type, and default value is "tcl"
#
# #################################
# #--- available keyword for $option ---
# #"tcl": Tcl, "js": JavaScript
# #################################
#
#=== generated script ===
#the generated script is output in the current directory
#generated script format: "${name}_info()" and "${name}_random(double)" in math functions
#
# - ${name}_info(): it returns preset data
# - ${name}_random(double): it returns a random coordinates in n-th dimensions
# - $double: a real number in range (0.0, 1.0)
# - output coordinates: {x1 dx1 x2 dx2 ... xn dxn} where Xi = xi+c*dxi and c is a constant
#--------------------------------------------------------------------
#
#*** <namespace ::nDRandom> ***
#
# - `::nDRandom::setRange min max division;`
# procedure that loads a variable range: minimum and maximum values, and number of divided ranges
# number of preset data is returned
# - $min and $max: minimum and maximum values
# - $division: number of divided ranges that are not less than 2
#
# - `::nDRandom::reset;`
# procedure that resets preset data, and returns number of preset data
#
# - `::nDRandom::info;`
# procedure that returns a list of preset data
#
# - `::nDRandom::vars;`
# procedure that returns parameters: {min1 d1 d1 dx1 min2 d2 d1*d2 dx2 ... minj dj d1*d2*...*dj dxj} where di is division
# => use `foreach {e1 e2 e3 e4} [returned list] { ... };`
#
#--- outputs ---
# - `::nDRandom::output name ?option?;`
# procedure that outputs a random coordinates generater in the current directory
# - $name: a keyword to determine output file and its functions
# - $option: an optional keyword, and default value is "tcl"
# #################################
# #--- available keyword for $option ---
# #"tcl": Tcl, "js": JavaScript
# #################################
#--------------------------------------------------------------------
#
#*** <namespace ::tcl::mathfunc> ***
#additional math function
#
#--- lSum_min.tcl (Yuji SODE, 2018): https://gist.github.com/YujiSODE/1f9a4e2729212691972b196a76ba9bd0 ---
# - `lSum(list)`: function that returns sum of given list
# - `$list`: a numerical list
##===================================================================
#
set auto_noexec 1;
package require Tcl 8.6;
#--------------------------------------------------------------------
#
#additional math function
#*** <namespace ::tcl::mathfunc> ***
#=== lSum_min.tcl (Yuji SODE, 2018): https://gist.github.com/YujiSODE/1f9a4e2729212691972b196a76ba9bd0 ===
#Additional mathematical function for Tcl expressions
# [Reference]
# - Iri, M., and Fujino., Y. 1985. Suchi keisan no joshiki (in Japanese). Kyoritsu Shuppan Co., Ltd.; ISBN 978-4-320-01343-8
proc ::tcl::mathfunc::lSum {list} {namespace path {::tcl::mathop};set S 0.0;set R 0.0;set T 0.0;foreach e $list {set R [+ $R [expr double($e)]];set T $S;set S [+ $S $R];set T [+ $S [expr {-$T}]];set R [+ $R [expr {-$T}]];};return $S;};
#--------------------------------------------------------------------
#
#*** <namespace ::nDRandom> ***
namespace eval ::nDRandom {
#=== variables ===
#
#N is number of preset data
variable N 0;
#
#ID_LIST is a list of preset data id
# - id format: `xi` where i is positive integer
variable ID_LIST {};
#
#DATA is an array of preset data
# - data format: `{id min max division dx}`
variable DATA;
array set DATA {};
};
#
#procedure that loads a variable range: minimum and maximum values, and number of divided ranges
#number of preset data is returned
proc ::nDRandom::setRange {min max division} {
# - $min and $max: minimum and maximum values
# - $division: number of divided ranges that are not less than 2
#
variable ::nDRandom::N;variable ::nDRandom::ID_LIST;variable ::nDRandom::DATA;
###
set min_ [expr {$min<$max?$min:$max}];
set max_ [expr {$min<$max?$max:$min}];
#
set division [expr {$division<2?2:int($division)}];
#
# (max-min)/division
set dx [expr {lSum([list $max_ -$min_])/double($division)}];
#
#id
incr ::nDRandom::N 1;
set id "x$::nDRandom::N";
#
lappend ::nDRandom::ID_LIST $id;
#
set ::nDRandom::DATA($id) "$id $min_ $max_ $division $dx";
#
unset min_ max_ division dx id;
#
#number of preset data is returned
return $::nDRandom::N;
};
#
#procedure that resets preset data, and returns number of preset data
proc ::nDRandom::reset {} {
variable ::nDRandom::N;variable ::nDRandom::ID_LIST;variable ::nDRandom::DATA;
###
set ::nDRandom::N 0;
#
set ::nDRandom::ID_LIST {};
#
array unset ::nDRandom::DATA;
array set ::nDRandom::DATA {};
#
#number of preset data is returned
return $::nDRandom::N;
};
#
#procedure that returns a list of preset data
proc ::nDRandom::info {} {
variable ::nDRandom::ID_LIST;variable ::nDRandom::DATA;
###
set vList {};
lappend vList {ID MIN MAX divisions dx};
#
if {[llength $::nDRandom::ID_LIST]} {
foreach e $::nDRandom::ID_LIST {
lappend vList $::nDRandom::DATA($e);
};
};
#
return $vList;
};
#
#procedure that returns parameters: {min1 d1 d1 dx1 min2 d2 d1*d2 dx2 ... minj dj d1*d2*...*dj dxj} where di is division
# => use `foreach {e1 e2 e3 e4} [returned list] { ... };`
proc ::nDRandom::vars {} {
variable ::nDRandom::ID_LIST;variable ::nDRandom::DATA;
###
set l {};
set d {};
set min 0.0;
set div 0;
set dPi 1.0;
set dx 0.0;
#
if {![llength $::nDRandom::ID_LIST]} {return "NO_DATA";};
#
foreach e $::nDRandom::ID_LIST {
#
#$d: `{id min max division dx}`
set d $::nDRandom::DATA($e);
#
set min [expr {double([lindex $d 1])}];
set div [expr {int([lindex $d 3])}];
set dPi [expr {$dPi*double($div)}];
set dx [expr {double([lindex $d 4])}];
#
lappend l $min;
lappend l $div;
lappend l $dPi;
lappend l $dx;
};
#
unset d div dPi dx;
#
return $l;
};
#
#procedure that returns number of divided cells
proc ::nDRandom::cells {} {
variable ::nDRandom::ID_LIST;variable ::nDRandom::DATA;
###
set div 0;
set n 1;
#
if {![llength $::nDRandom::ID_LIST]} {return "NO_DATA";};
#
foreach e $::nDRandom::ID_LIST {
#
#$d: `{id min max division dx}`
set d $::nDRandom::DATA($e);
#
set div [expr {int([lindex $d 3])}];
set n [expr {$n*$div}];
};
#
unset d div;
#
return $n;
};
#--------------------------------------------------------------------
#
#--- outputs ---
#procedure that outputs a random coordinates generator in the current directory
#it returns output file name
#
#=== generated script ===
#the generated script is output in the current directory
#generated script format: "${name}_info()" and "${name}_random(double)" in math functions
#
# - ${name}_info(): it returns preset data
# - ${name}_random(double): it returns a random coordinates in n-th dimensions
# - $double: a real number in range (0.0, 1.0)
# - output coordinates: {x1 dx1 x2 dx2 ... xn dxn} where Xi = xi+c*dxi and c is a constant
proc ::nDRandom::output {name {option tcl}} {
# - $name: a keyword to determine output file name and its functions
# - $option: an optional keyword to determine output type, and default value is "tcl"
#################################
#--- available keyword for $option ---
#"tcl": Tcl, "js": JavaScript
#################################
###
#
source -encoding utf-8 "ndrandomOutput_${option}.tcl";
#=> `::nDRandom::output\$ $name;`
#
return [::nDRandom::output\$ $name];
};
#
#=======================================================================================
#
#=== shell ===
#`tclsh nDRandom.tcl name option min1 max1 division1 ?min2 max2 division2 ?... minN maxN divisionN??;`
# - $name: a keyword to determine output file name and its functions
# - $option: a keyword to determine output type
# - $min1, $max1 and $division1: data range
# - $min2, $max2 and $division2 (, ..., $minN, $maxN and $divisionN): optional data ranges
# - $division: number of divided ranges that are not less than 2
###
#
#--- arguments ---
set name_shell [lindex $argv 0];
set option_shell [lindex $argv 1];
set ranges_shell [lrange $argv 2 end];
#
#when arguments are invalid
if {$argc&&(!([llength $name_shell])||!([llength $option_shell]))} {
unset name_shell option_shell ranges_shell;
return -code error "arguments are invalid";
};
#
#when data range is invalid
if {[llength $ranges_shell]%3} {
unset name_shell option_shell ranges_shell;
return -code error "data range requires form of \"min1 max1 division1 ?min2 max2 division2 ?... minN maxN divisionN??\"";
};
#
if {$argc} {
foreach {e1 e2 e3} $ranges_shell {
::nDRandom::setRange $e1 $e2 $e3;
};
puts stdout [join [::nDRandom::info] \n];
puts stdout "output file:\t[pwd]/[::nDRandom::output $name_shell $option_shell]";
unset name_shell option_shell ranges_shell;
};