redshift 1.3.15 → 1.3.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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