redshift 1.3.15 → 1.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/RELEASE-NOTES +4 -0
- data/bench/diff-bench +2 -2
- data/bench/run +1 -1
- data/bench/strictness.rb +2 -2
- data/examples/ball.rb +1 -1
- data/examples/collide.rb +1 -1
- data/examples/delay.rb +1 -1
- data/examples/derivative.rb +1 -1
- data/examples/euler.rb +1 -1
- data/examples/lotka-volterra.rb +1 -1
- data/examples/pid.rb +1 -1
- data/examples/subsystem.rb +1 -1
- data/examples/thermostat.rb +1 -1
- data/lib/redshift/component.rb +2 -2
- data/lib/redshift/redshift.rb +2 -2
- data/lib/{accessible-index.rb → redshift/util/accessible-index.rb} +0 -0
- data/lib/redshift/util/argos.rb +214 -0
- data/lib/redshift/util/histogram.rb +155 -0
- data/lib/redshift/util/object-diff.rb +83 -0
- data/lib/redshift/util/plot.rb +386 -0
- data/lib/redshift/util/random.rb +261 -0
- data/lib/redshift/util/superhash.rb +454 -0
- data/lib/redshift/util/tracer.rb +113 -0
- data/lib/redshift/util/tracer/trace.rb +145 -0
- data/lib/redshift/util/tracer/var.rb +112 -0
- data/rakefile +2 -2
- data/test/test_flow_trans.rb +28 -25
- metadata +45 -7
- data/examples/persist-ball.rb +0 -68
@@ -0,0 +1,83 @@
|
|
1
|
+
class Object
|
2
|
+
def diff other, &block
|
3
|
+
d = other - self
|
4
|
+
if block
|
5
|
+
block.call d
|
6
|
+
end
|
7
|
+
d
|
8
|
+
end
|
9
|
+
|
10
|
+
def filter
|
11
|
+
(yield self) && self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NilClass
|
16
|
+
def diff other, &block
|
17
|
+
0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Array
|
22
|
+
def diff other, &block
|
23
|
+
a = (0...[self.size, (other ? other.size : 0)].min).map do |i|
|
24
|
+
self[i].diff(other[i], &block)
|
25
|
+
end
|
26
|
+
if block
|
27
|
+
block.call a, other.size - self.size
|
28
|
+
end
|
29
|
+
a
|
30
|
+
end
|
31
|
+
|
32
|
+
def filter(&block)
|
33
|
+
aa = inject([]) do |a, val|
|
34
|
+
v = val.filter(&block)
|
35
|
+
a << v if v
|
36
|
+
a
|
37
|
+
end
|
38
|
+
aa.empty? ? nil : aa
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Hash
|
43
|
+
def diff other, &block
|
44
|
+
ha = (self.keys & other.keys).inject({}) do |h,k|
|
45
|
+
h[k] = self[k].diff(other[k], &block); h
|
46
|
+
end
|
47
|
+
if block
|
48
|
+
block.call ha, self.keys - other.keys, other.keys - self.keys
|
49
|
+
end
|
50
|
+
ha
|
51
|
+
end
|
52
|
+
|
53
|
+
def filter(&block)
|
54
|
+
ha = inject({}) do |h, (key, val)|
|
55
|
+
v = val.filter(&block)
|
56
|
+
h[key] = v if v
|
57
|
+
h
|
58
|
+
end
|
59
|
+
ha.empty? ? nil : ha
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if __FILE__ == $0
|
64
|
+
|
65
|
+
#r = [{0=>1, 2=>3, 1=>[-1=>-2, 7=>8]}].filter {|x| x>1}
|
66
|
+
#p r
|
67
|
+
#exit
|
68
|
+
|
69
|
+
a = {
|
70
|
+
"foo" => [ {1=>2}, {3=>4.2} ],
|
71
|
+
"bar" => [ 4, 5, [6, 8] ]
|
72
|
+
}
|
73
|
+
|
74
|
+
b = {
|
75
|
+
"foo" => [ {1=>2}, {3=>4} ],
|
76
|
+
"bar" => [ 4, 7, [6, 9] ]
|
77
|
+
}
|
78
|
+
|
79
|
+
require 'yaml'
|
80
|
+
|
81
|
+
y a.diff(b).filter {|x| x > -0.1}
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
autoload :Tempfile, "tempfile"
|
2
|
+
|
3
|
+
# Interface to gnuplot and, eventually, other plotting apps.
|
4
|
+
module Plot
|
5
|
+
# +app+ is the name of the plot program. Yields and returns the plot instance.
|
6
|
+
# Use the #add, #command, and #show methods on this object.
|
7
|
+
def Plot.new(app = ENV['PLOTTER'] || 'gnuplot', &block)
|
8
|
+
plot =
|
9
|
+
case app
|
10
|
+
when /gnuplot/i
|
11
|
+
Gnuplot.new app
|
12
|
+
|
13
|
+
when /^matlab/i
|
14
|
+
raise "matlab not supported yet.\n Try 'gnuplot'."
|
15
|
+
|
16
|
+
else
|
17
|
+
raise "Plot doesn't recognize '#{app}'.\n Try 'gnuplot'."
|
18
|
+
end
|
19
|
+
yield plot if block_given?
|
20
|
+
plot
|
21
|
+
end
|
22
|
+
|
23
|
+
class GenericPlot
|
24
|
+
# Array of strings that have been sent to the plot program.
|
25
|
+
attr_reader :command_history
|
26
|
+
|
27
|
+
def initialize app
|
28
|
+
@app = app
|
29
|
+
@files = []
|
30
|
+
@command_queue = []
|
31
|
+
@command_history = []
|
32
|
+
@data_stuff = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear_data
|
36
|
+
@data_stuff = []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Closes the plot window and cleans up tempfiles, if you want
|
40
|
+
# to do that before the user closes the window explicitly.
|
41
|
+
# Doesn't work with the -persist option typically used on windows.
|
42
|
+
def close
|
43
|
+
if @pipe
|
44
|
+
@pipe.close
|
45
|
+
@pipe = nil
|
46
|
+
elsif @pid # only when parent has forked the plot handler
|
47
|
+
Process.kill("TERM", @pid)
|
48
|
+
Process.wait(@pid)
|
49
|
+
@pid = nil
|
50
|
+
else
|
51
|
+
raise "can't close plot"
|
52
|
+
end
|
53
|
+
@files = nil # let tempfiles be GC-ed and deleted
|
54
|
+
end
|
55
|
+
|
56
|
+
# Send +str+ to the plot program, or, if #show has not been called yet,
|
57
|
+
# queue the string to be sent later when #show is called.
|
58
|
+
def command str
|
59
|
+
command_history << str
|
60
|
+
if @pipe
|
61
|
+
@pipe.puts str
|
62
|
+
else
|
63
|
+
@command_queue << str
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Start the plotting program by opening a pipe to it. Send all queued
|
68
|
+
# commands. You can continue to send commands by calling #command.
|
69
|
+
def show
|
70
|
+
unless @pipe
|
71
|
+
@pipe = IO.popen @app, "w"
|
72
|
+
@pipe.sync = true
|
73
|
+
while (cmd=@command_queue.shift)
|
74
|
+
@pipe.puts cmd
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add a data element to the plot with specified +data+ array and options
|
80
|
+
# and, optionally, a path. If +data+ is a string, it is assumed to be a
|
81
|
+
# expression defining a function, such as "sin(x)+1".
|
82
|
+
def add data = [], options = nil, path = nil
|
83
|
+
case data
|
84
|
+
when String
|
85
|
+
@data_stuff << [nil, [data, options].join(" "), nil]
|
86
|
+
else
|
87
|
+
@data_stuff << [data, options, path]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def dump data, path = nil
|
92
|
+
file =
|
93
|
+
if path
|
94
|
+
File.new(path, "w")
|
95
|
+
else
|
96
|
+
Tempfile.new("ruby_plot")
|
97
|
+
## might be better (at least on windows) to send the data
|
98
|
+
## inline, to avoid the "no file" errors when you try to
|
99
|
+
## zoom or replot
|
100
|
+
end
|
101
|
+
|
102
|
+
@files << file
|
103
|
+
# This is here to prevent GC from collecting Tempfile object
|
104
|
+
# and thereby deleting the temp file from disk.
|
105
|
+
|
106
|
+
path = file.path
|
107
|
+
|
108
|
+
if (data.first.first.respond_to? :join rescue false)
|
109
|
+
data.each do |group|
|
110
|
+
group.each do |point|
|
111
|
+
file.puts point.join("\t")
|
112
|
+
end
|
113
|
+
file.puts
|
114
|
+
end
|
115
|
+
|
116
|
+
elsif (data.first.respond_to? :join rescue false) # assume one group
|
117
|
+
for d in data
|
118
|
+
if d.respond_to? :join
|
119
|
+
file.puts d.join("\t")
|
120
|
+
elsif d == nil
|
121
|
+
file.puts
|
122
|
+
else
|
123
|
+
file.puts d.inspect
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
elsif (data.first.respond_to? :each rescue false)
|
128
|
+
data.each do |group|
|
129
|
+
group.each do |point|
|
130
|
+
file.puts point.to_a.join("\t") # to_a in case of narray
|
131
|
+
end
|
132
|
+
file.puts
|
133
|
+
end
|
134
|
+
|
135
|
+
else # assume one group
|
136
|
+
for d in data
|
137
|
+
if d.respond_to? :join
|
138
|
+
file.puts d.join("\t")
|
139
|
+
elsif d == nil
|
140
|
+
file.puts
|
141
|
+
else
|
142
|
+
file.puts d.inspect
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
path
|
148
|
+
|
149
|
+
ensure
|
150
|
+
file.close unless file.closed?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class Gnuplot < GenericPlot
|
155
|
+
@@gnuplot_counter = 0
|
156
|
+
|
157
|
+
# Returns an array of the form [major, minor].
|
158
|
+
def Gnuplot.version(app)
|
159
|
+
@version ||= {}
|
160
|
+
@version[app] ||=
|
161
|
+
begin
|
162
|
+
v = `#{app} --version`.match(/gnuplot\s+(\d+)\.(\d+)/)
|
163
|
+
v && v[1..2].map {|s| s.to_i}
|
164
|
+
rescue
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def Gnuplot.version_at_least?(app, major_minor)
|
170
|
+
major, minor = Gnuplot.version(app)
|
171
|
+
major_needed, minor_needed = major_minor
|
172
|
+
(major == major_needed and minor >= minor_needed) or
|
173
|
+
(major > major_needed)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Does the specified gnuplot executable support the 'Close' event?
|
177
|
+
def has_working_close_event
|
178
|
+
Gnuplot.version_at_least?(@app, [4, 3])
|
179
|
+
end
|
180
|
+
|
181
|
+
# Select the best term choice based on platform and gnuplot version.
|
182
|
+
def best_term
|
183
|
+
case RUBY_PLATFORM
|
184
|
+
when /mswin32|mingw32/
|
185
|
+
"win"
|
186
|
+
else
|
187
|
+
if Gnuplot.version_at_least?(@app, [4, 2])
|
188
|
+
"wxt"
|
189
|
+
else
|
190
|
+
"x11"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
attr_reader :uniqname
|
196
|
+
|
197
|
+
def initialize(*)
|
198
|
+
super
|
199
|
+
@uniqname = next_uniqname
|
200
|
+
end
|
201
|
+
|
202
|
+
def next_uniqname
|
203
|
+
"Gnuplot_#{Process.pid}_#{@@gnuplot_counter+=1}"
|
204
|
+
end
|
205
|
+
|
206
|
+
def use3d
|
207
|
+
@plot_cmd = "splot"
|
208
|
+
end
|
209
|
+
|
210
|
+
def use2d
|
211
|
+
@plot_cmd = "plot"
|
212
|
+
end
|
213
|
+
|
214
|
+
def plot_cmd
|
215
|
+
@plot_cmd ||= "plot"
|
216
|
+
end
|
217
|
+
|
218
|
+
def term
|
219
|
+
@term ||= ENV["GNUTERM"] || best_term
|
220
|
+
end
|
221
|
+
attr_writer :term
|
222
|
+
|
223
|
+
attr_accessor :term_options
|
224
|
+
|
225
|
+
def set_window_title title
|
226
|
+
command "set term #{term} title '#{title}' #{term_options}"
|
227
|
+
end
|
228
|
+
alias window_title= set_window_title
|
229
|
+
|
230
|
+
attr_reader :is_to_file
|
231
|
+
|
232
|
+
def command str
|
233
|
+
case str
|
234
|
+
when /^\s*set\s+term\s+(\S+)/
|
235
|
+
self.term = $1
|
236
|
+
if /title\s+['"]?([^'"]*)/ =~ str
|
237
|
+
@uniqname = $1
|
238
|
+
end
|
239
|
+
when /^\s*set\s+output\s/
|
240
|
+
@is_to_file = true
|
241
|
+
end
|
242
|
+
super
|
243
|
+
end
|
244
|
+
|
245
|
+
def commit
|
246
|
+
args = @data_stuff.map do |data, options, path|
|
247
|
+
if data
|
248
|
+
"'#{dump data, path}' #{options}"
|
249
|
+
else
|
250
|
+
options
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
cmd_line = [plot_cmd, args.join(", ")].join(" ")
|
255
|
+
|
256
|
+
command "set mouse"
|
257
|
+
command cmd_line
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Module for creating "fire and forget" plot windows. Instead of a plot window
|
262
|
+
# that your main program keeps interacting with, these windows are created
|
263
|
+
# with one specific plot and left to be closed by the user. No cleanup is
|
264
|
+
# required.
|
265
|
+
#
|
266
|
+
# The functions in this module cam be accessed in two ways
|
267
|
+
#
|
268
|
+
# include Plot::PlotUtils
|
269
|
+
# gnuplot do ... end
|
270
|
+
#
|
271
|
+
# or
|
272
|
+
#
|
273
|
+
# Plot::PlotUtils.gnuplot do ... end
|
274
|
+
#
|
275
|
+
module PlotUtils
|
276
|
+
module_function
|
277
|
+
|
278
|
+
# Yields and returns the Plot instance. On unix/linux, the returned
|
279
|
+
# plot instance can no longer be used to send more plot commands, since
|
280
|
+
# it is just a copy of the real plot instance which is in a child process.
|
281
|
+
# On windows, be sure not to exit immediately after calling this method,
|
282
|
+
# or else tempfiles will be deleted before gnuplot has had a chance to read
|
283
|
+
# them.
|
284
|
+
def gnuplot(app=nil, &bl)
|
285
|
+
begin
|
286
|
+
gnuplot_fork(app, &bl)
|
287
|
+
rescue NotImplementedError => ex
|
288
|
+
raise unless ex.message =~ /fork/
|
289
|
+
gnuplot_no_fork(app, &bl)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def fork_returning_result # :nodoc:
|
294
|
+
read_result, write_result = IO.pipe
|
295
|
+
fork do
|
296
|
+
read_result.close
|
297
|
+
result_setter = proc do |r|
|
298
|
+
write_result.write Marshal.dump(r)
|
299
|
+
write_result.close
|
300
|
+
end
|
301
|
+
yield result_setter
|
302
|
+
end
|
303
|
+
write_result.close
|
304
|
+
|
305
|
+
begin
|
306
|
+
Marshal.load(read_result.read)
|
307
|
+
rescue => ex
|
308
|
+
ex ## ?
|
309
|
+
end
|
310
|
+
ensure
|
311
|
+
[write_result, read_result].each do |io|
|
312
|
+
io.close unless io.closed?
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def gnuplot_fork(app=nil, &bl) # :nodoc:
|
317
|
+
Plot.new(app || 'gnuplot') do |plot|
|
318
|
+
unless bl
|
319
|
+
raise ArgumentError, "no block given"
|
320
|
+
end
|
321
|
+
bl[plot]
|
322
|
+
|
323
|
+
if plot.is_to_file
|
324
|
+
plot.commit
|
325
|
+
plot.show
|
326
|
+
plot.close
|
327
|
+
result = plot
|
328
|
+
|
329
|
+
else
|
330
|
+
result = fork_returning_result do |result_setter|
|
331
|
+
trap "INT" do exit end
|
332
|
+
trap "TERM" do exit end # clean up tempfiles
|
333
|
+
|
334
|
+
plot.command "set mouse"
|
335
|
+
# redundant, but in some gp versions must do this
|
336
|
+
# before the set term in the line below:
|
337
|
+
plot.set_window_title plot.uniqname
|
338
|
+
if plot.has_working_close_event
|
339
|
+
plot.command "bind allwindows Close 'exit gnuplot'"
|
340
|
+
end
|
341
|
+
|
342
|
+
plot.commit
|
343
|
+
result = plot.dup
|
344
|
+
result.clear_data
|
345
|
+
result.instance_eval {@files = nil; @pid = Process.pid}
|
346
|
+
result_setter.call result
|
347
|
+
plot.show
|
348
|
+
|
349
|
+
if plot.has_working_close_event
|
350
|
+
Process.wait # wait for gnuplot to exit
|
351
|
+
else
|
352
|
+
loop do
|
353
|
+
sleep 5
|
354
|
+
wmstate = `xprop -name #{plot.uniqname} WM_STATE 2>&1`
|
355
|
+
break if not $?.success?
|
356
|
+
break if wmstate[/window state:\s*withdrawn/i]
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
return result
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def gnuplot_no_fork(app=nil, &bl) # :nodoc:
|
367
|
+
# assume windows
|
368
|
+
Plot.new("#{app || "pgnuplot.exe"} -persist") do |plot|
|
369
|
+
# -persist is nicer on windows: supports mouse/key interaction
|
370
|
+
bl[plot]
|
371
|
+
if plot.is_to_file
|
372
|
+
plot.commit
|
373
|
+
plot.show
|
374
|
+
plot.close
|
375
|
+
|
376
|
+
else
|
377
|
+
plot.command "bind allwindows Close 'exit gnuplot'"
|
378
|
+
plot.command "bind allwindows 'q' 'exit gnuplot'"
|
379
|
+
plot.commit
|
380
|
+
plot.show
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|