feldtruby 0.3.16 → 0.3.18

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +9 -2
  3. data/Rakefile +8 -0
  4. data/feldtruby.gemspec +6 -0
  5. data/lib/feldtruby/annotations.rb +10 -0
  6. data/lib/feldtruby/array/basic_stats.rb +3 -1
  7. data/lib/feldtruby/array/permutations_and_subsets.rb +17 -0
  8. data/lib/feldtruby/float.rb +23 -0
  9. data/lib/feldtruby/logger.rb +216 -30
  10. data/lib/feldtruby/minitest_extensions.rb +0 -1
  11. data/lib/feldtruby/mongodb.rb +16 -0
  12. data/lib/feldtruby/mongodb_logger.rb +245 -0
  13. data/lib/feldtruby/optimize/differential_evolution.rb +29 -5
  14. data/lib/feldtruby/optimize/elite_archive.rb +91 -0
  15. data/lib/feldtruby/optimize/max_steps_termination_criterion.rb +1 -1
  16. data/lib/feldtruby/optimize/objective.rb +343 -222
  17. data/lib/feldtruby/optimize/optimizer.rb +138 -60
  18. data/lib/feldtruby/optimize/search_space.rb +10 -0
  19. data/lib/feldtruby/optimize.rb +1 -26
  20. data/lib/feldtruby/statistics.rb +74 -3
  21. data/lib/feldtruby/time.rb +19 -0
  22. data/lib/feldtruby/version.rb +1 -1
  23. data/old/event_logger.rb +682 -0
  24. data/spikes/comparing_samplers_on_classic_optimization_functions/analyze_sampler_comparison_results.R +78 -0
  25. data/spikes/comparing_samplers_on_classic_optimization_functions/compare_samplers.rb +264 -0
  26. data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_130405_175934.csv +561 -0
  27. data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder.csv +11201 -0
  28. data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder_all_radii_4_to_30.csv +44801 -0
  29. data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_omnitest.csv +1401 -0
  30. data/spikes/mongodb_logger.rb +47 -0
  31. data/spikes/simple_de_run.rb +32 -0
  32. data/test/helper.rb +17 -1
  33. data/test/test_array_basic_stats.rb +5 -1
  34. data/test/test_array_permutations_and_subsets.rb +23 -0
  35. data/test/test_float.rb +15 -0
  36. data/test/test_html_doc_getter.rb +1 -1
  37. data/test/test_logger.rb +86 -48
  38. data/test/test_mongodb_logger.rb +116 -0
  39. data/test/test_object_annotations.rb +14 -0
  40. data/test/test_optimize.rb +7 -6
  41. data/test/test_optimize_differential_evolution.rb +21 -19
  42. data/test/test_optimize_elite_archive.rb +85 -0
  43. data/test/test_optimize_objective.rb +237 -74
  44. data/test/test_optimize_populationbasedoptimizer.rb +72 -6
  45. data/test/test_optimize_random_search.rb +0 -17
  46. data/test/test_optimize_search_space.rb +15 -0
  47. data/test/test_statistics.rb +30 -4
  48. data/test/test_time.rb +22 -0
  49. data/test/tmp_shorter.csv +200 -0
  50. metadata +62 -21
@@ -0,0 +1,682 @@
1
+ class Logger
2
+ private
3
+
4
+ def description_for_metric_change newValue, oldValue, eventType, metric
5
+
6
+ if newValue.is_a?(Numeric)
7
+
8
+ pc = percent_change(oldValue, newValue)
9
+ pcs = (pc ? " (#{pc})" : "")
10
+
11
+ summary = summary_stats eventType, metric
12
+
13
+ sum_str = summary ? ", mean = #{summary}" : ""
14
+
15
+ ovstr = oldValue ? oldValue.to_significant_digits(3).to_s : ""
16
+
17
+ "#{ovstr} -> #{newValue.to_significant_digits(3)}#{pcs}#{sum_str}"
18
+
19
+ else
20
+
21
+ " was = #{oldValue.inspect}\n now = #{newValue.inspect}"
22
+
23
+ end
24
+ end
25
+
26
+ def summary_stats eventType, metric
27
+ nil # We cannot calc summary stats since this logger does not save history
28
+ end
29
+
30
+ def percent_change oldValue, newValue
31
+ return nil if oldValue.nil?
32
+ sign = (oldValue < newValue) ? "+" : ""
33
+ "#{sign}%.3g%%" % ((newValue - oldValue) / oldValue.to_f * 100.0)
34
+ end
35
+ end
36
+
37
+ # A structured logging object that logs events. Events are time-stamped
38
+ # objects that have type and can contain arbitrary data. A logger can have
39
+ # multiple IO streams to which it logs events. With each event data can be
40
+ # saved.
41
+ #
42
+ # This default logger saves events locally in a hash.
43
+ class EventLogger < Logger
44
+ # Return the number of events of type _eventType_.
45
+ def num_events eventType = nil
46
+ events(eventType).length
47
+ end
48
+
49
+ # Events are time-stamped hashes of ruby values with a named type (given
50
+ # as a string). The time stamps are always saved as UTC.
51
+ class Event
52
+ attr_reader :type, :data, :time
53
+ def initialize(type, data, time = Time.now)
54
+ @type, @data, @time = type, data, time
55
+ end
56
+
57
+ # Convert to hash that will be saved in db. Since each event type is saved in its
58
+ # own db/collection we do NOT include the event type.
59
+ def to_db_hash
60
+ # We save with minimal json tag names since we need to conserve space
61
+ # if saving this to disc or db.
62
+ {
63
+ # _id will be added automatically by MongoDB.
64
+ "t" => time.utc.iso8601, # We follow the Cube convention of saving UTC
65
+ "d" => data
66
+ }
67
+ end
68
+ end
69
+
70
+ # Return all the events for a given _eventType_.
71
+ def events(eventType = nil)
72
+ @events[eventType]
73
+ end
74
+
75
+ # Return all events, for a given _eventType_, between the _start_ and _stop_
76
+ # times. If _includePreEvent_ is true we include the event that comes directly
77
+ # before the start time.
78
+ def events_between start, stop, eventType = nil, includePreEvent = false
79
+
80
+ all_events = events(eventType)
81
+
82
+ es = all_events.select do |e|
83
+
84
+ t = e.time
85
+
86
+ t >= start && t <= stop
87
+
88
+ end
89
+
90
+ if includePreEvent
91
+
92
+ index_to_first_selected_event = all_events.index(es.first)
93
+
94
+ if index_to_first_selected_event && index_to_first_selected_event > 0
95
+ # There is a pre-event so add it
96
+ es.unshift all_events[index_to_first_selected_event - 1]
97
+ end
98
+
99
+ end
100
+
101
+ es
102
+
103
+ end
104
+
105
+ # Log an event described by a _message_ optionally with an event type
106
+ # _eventType_ and data in a hash.
107
+ #
108
+ # The _printFrequency_ gives the minimum time that must elapse between
109
+ # messages for _eventType_ is printed on the IO stream(s).
110
+ def log message, eventType = nil, data = {}, saveMessageInData = true, printFrequency = 0.0
111
+
112
+ # Get current time now, before we waste time in processing this event
113
+ time, elapsed = time_and_elapsed_time_since_last_event eventType
114
+
115
+ event = nil # So we return nil if no event was created
116
+
117
+ # We only save the event if an eventType or data was given.
118
+ if eventType || data.length > 0
119
+
120
+ event = Event.new(eventType, data, time)
121
+
122
+ save_event event
123
+
124
+ end
125
+
126
+ print_message_if_needed message, eventType, printFrequency, time
127
+
128
+ event
129
+
130
+ end
131
+
132
+ # Log a _newValue_ for the _eventType_. Optionally a message and a name
133
+ # for a metric can be given but if not we use sensible defaults.
134
+ def log_value eventType, newValue, message = nil, metric = :_v, printFrequency = 0.0
135
+
136
+ log message, eventType, {metric => newValue}, false, printFrequency
137
+
138
+ end
139
+
140
+ # Get an array of values for the metric named _metric_ in events
141
+ # of type _eventType_.
142
+ def values_for_event_and_metric eventType, metric = :_v
143
+ events(eventType).map {|e| e.data[metric]}
144
+ end
145
+
146
+ # Return the current (latest) value for a given eventType and metric.
147
+ # Return nil if no value has been set.
148
+ def current_value eventType, metric = :_v
149
+
150
+ event = events(eventType).last
151
+
152
+ return nil unless event
153
+
154
+ event.data[metric]
155
+
156
+ end
157
+
158
+ # Shortcut method to get the value saved for a certain _eventType_.
159
+ def values_for eventType
160
+ values_for_event_and_metric eventType
161
+ end
162
+
163
+ # Return the current (latest) value for a given eventType and metric.
164
+ # Return nil if no value has been set.
165
+ def previous_value eventType, metric = :_v
166
+
167
+ event = events(eventType)[-2]
168
+
169
+ return nil unless event
170
+
171
+ event.data[metric]
172
+
173
+ end
174
+
175
+ # Return the values at each _step_ between _start_ (inclusive) and _stop_
176
+ # (exclusive) for _eventType_ and _metric_. Both _start_, _stop_ can be
177
+ # either a Time object or a number of milli seconds (if they are integers).
178
+ # The _step_ should be given as milliseconds and defaults to one second,
179
+ # i.e. 1_000.
180
+ def values_in_steps_between eventType, start = UnixEpoch, stop = Time.now, step = 1_000, metric = :_v
181
+
182
+ # Get events in the given interval. Since we send true to this method
183
+ # the events will include the one event prior to _start_ time if
184
+ # it exists.
185
+ events = events_between(start, stop, eventType, true)
186
+
187
+ #puts events.inspect
188
+
189
+ current_msec = start.is_a?(Integer) ? start : start.milli_seconds
190
+
191
+ stop_msec = stop.is_a?(Integer) ? stop : stop.milli_seconds
192
+
193
+ if events.first != nil && events.first.time.milli_seconds <= current_msec
194
+
195
+ #puts "1"
196
+
197
+ prev_value = events.first.data[metric]
198
+
199
+ values_at_steps events.drop(1), current_msec, stop_msec, step, prev_value, metric
200
+
201
+ else
202
+
203
+ #puts "2"
204
+
205
+ values_at_steps events, current_msec, stop_msec, step, nil, metric
206
+
207
+ end
208
+
209
+ end
210
+
211
+ private
212
+
213
+ def values_at_steps events, currentMsec, stopMsec, step, prevValue, metric
214
+
215
+ #puts "curr = #{currentMsec}, stop = #{stopMsec}, step = #{step}, prevValue = #{prevValue}"
216
+
217
+ e, *rest = events
218
+
219
+ if e.nil?
220
+
221
+ #puts "a"
222
+
223
+ return [] if currentMsec >= stopMsec
224
+
225
+ # No more events so fill up with prevValue
226
+ [prevValue] * ((stopMsec - currentMsec) / step)
227
+
228
+ else
229
+
230
+ #puts "b"
231
+
232
+ next_event_msec = e.time.milli_seconds
233
+
234
+ q, m = (next_event_msec - currentMsec).divmod(step)
235
+
236
+ vs = [prevValue] * q
237
+
238
+ if m == 0
239
+ if next_event_msec < stopMsec
240
+ vs << e.data[metric]
241
+ else
242
+ return vs
243
+ end
244
+ else
245
+ vs << prevValue
246
+ end
247
+
248
+ next_msec = currentMsec + (q + 1) * step
249
+
250
+ vs + values_at_steps( rest, next_msec, stopMsec, step, e.data[metric], metric )
251
+
252
+ end
253
+
254
+ end
255
+
256
+ # Set up the internal data store.
257
+ def setup_data_store
258
+ # For this default logger class we just use an array of the events for each
259
+ # event type.
260
+ @events = Hash.new {|h,k| h[k] = Array.new}
261
+ end
262
+
263
+ # Return the current time and elapsed time since we last logged an event of
264
+ # this type.
265
+ # If a filter is given it is used to filter the events (of this type)
266
+ # before selecting the one to compare current time to.
267
+ def time_and_elapsed_time_since_last_event(eventType = nil, &filter)
268
+
269
+ if filter
270
+ latest = events(eventType).reverse.find(&filter)
271
+ else
272
+ latest = events(eventType).last
273
+ end
274
+
275
+ t = Time.now
276
+
277
+ return [t, 0.0] unless latest
278
+
279
+ return t, (t - latest.time)
280
+
281
+ end
282
+
283
+ # Save the event in the data store.
284
+ def save_event event
285
+ @events[event.type] << event
286
+ event
287
+ end
288
+
289
+ def summary_stats eventType, metric
290
+ values_for_event_and_metric(eventType, metric).summary_stats
291
+ end
292
+ end
293
+
294
+ #describe 'EventLogger' do
295
+ #
296
+ # before do
297
+ # @sio = StringIO.new
298
+ # @l = FeldtRuby::EventLogger.new @sio, {:verbose => true}
299
+ # end
300
+ #
301
+ #
302
+ # it 'returns an event from the log method if an event was logged' do
303
+ #
304
+ # e = @l.log "1", :a
305
+ # e.must_be_instance_of FeldtRuby::EventLogger::Event
306
+ #
307
+ # end
308
+ #
309
+ # it 'does not print messages to io stream(s) if verbose flag is false' do
310
+ #
311
+ # sio = StringIO.new
312
+ # l = FeldtRuby::EventLogger.new(sio, {:verbose => false})
313
+ #
314
+ # l.log "event 1", :a
315
+ # sio.string.must_equal ""
316
+ #
317
+ # l.verbose = true
318
+ # l.log "event 2", :a
319
+ # sio.string.split("\n").last[13..-1].must_equal "{a}: event 2"
320
+ #
321
+ # l.verbose = false
322
+ # l.log "event 3", :a
323
+ # sio.string.split("\n").last[13..-1].must_equal "{a}: event 2" # Still 2 since 3 was not printed
324
+ #
325
+ # end
326
+ #
327
+ # it 'can log events of a given type' do
328
+ #
329
+ # @l.log "1", :increase
330
+ # @sio.string[13..-1].must_equal "{increase}: 1\n"
331
+ # @l.num_events.must_equal 0
332
+ # @l.num_events(:increase).must_equal 1
333
+ #
334
+ # @l.log "2", :increase
335
+ # @sio.string[40..-1].must_equal "{increase}: 2\n"
336
+ # @l.num_events.must_equal 0
337
+ # @l.num_events(:increase).must_equal 2
338
+ #
339
+ # end
340
+ #
341
+ # it 'can return old events of given type' do
342
+ #
343
+ # @l.log "1", :increase
344
+ # @l.log "2", :increase
345
+ # @l.log "0.4", :alpha
346
+ # @l.log "1", :increase
347
+ #
348
+ # ei = @l.events(:increase)
349
+ # ei.length.must_equal 3
350
+ #
351
+ # ea = @l.events(:alpha)
352
+ # ea.length.must_equal 1
353
+ #
354
+ # eb = @l.events(:beta)
355
+ # eb.length.must_equal 0
356
+ #
357
+ # end
358
+ #
359
+ # it 'time stamps each log entry' do
360
+ #
361
+ # @l.log "1", :a
362
+ # @l.log "2", :b
363
+ # @l.log "2", :a
364
+ #
365
+ # time_stamps_a = @l.events(:a).map {|e| e.time}
366
+ # time_stamps_b = @l.events(:b).map {|e| e.time}
367
+ #
368
+ # time_stamps_a[0].must_be_instance_of Time
369
+ # time_stamps_a[1].must_be_instance_of Time
370
+ # time_stamps_b[0].must_be_instance_of Time
371
+ #
372
+ # time_stamps_a[0].must_be :<, time_stamps_a[1]
373
+ # time_stamps_a[0].must_be :<, time_stamps_b[0]
374
+ # time_stamps_b[0].must_be :<, time_stamps_a[1]
375
+ #
376
+ # end
377
+ #
378
+ # it 'can log to multiple io streams' do
379
+ #
380
+ # sio2 = StringIO.new
381
+ # @l.add_io sio2
382
+ #
383
+ # @l.log "a"
384
+ #
385
+ # @sio.string[13..-1].must_equal "a\n"
386
+ # sio2.string[13..-1].must_equal "a\n"
387
+ #
388
+ # end
389
+ #
390
+ # it 'has no value for a metric without events' do
391
+ #
392
+ # @l.current_value(:fitness).must_equal nil
393
+ #
394
+ # end
395
+ #
396
+ # it 'can return the values logged so far' do
397
+ #
398
+ # @l.log_value :a, 1.0
399
+ # @l.previous_value(:a).must_equal nil
400
+ # @l.current_value(:a).must_equal 1.0
401
+ # @l.values_for_event_and_metric(:a, :_v).must_equal [1.0]
402
+ # @l.values_for(:a).must_equal [1.0]
403
+ #
404
+ # @l.log_value :a, 2.0
405
+ # @l.previous_value(:a).must_equal 1.0
406
+ # @l.current_value(:a).must_equal 2.0
407
+ # @l.values_for_event_and_metric(:a, :_v).must_equal [1.0, 2.0]
408
+ # @l.values_for(:a).must_equal [1.0, 2.0]
409
+ #
410
+ # @l.log_value :a, 3.0
411
+ # @l.previous_value(:a).must_equal 2.0
412
+ # @l.current_value(:a).must_equal 3.0
413
+ # @l.values_for_event_and_metric(:a, :_v).must_equal [1.0, 2.0, 3.0]
414
+ # @l.values_for(:a).must_equal [1.0, 2.0, 3.0]
415
+ #
416
+ # end
417
+ #
418
+ # it 'updates the value when new values are logged' do
419
+ #
420
+ # @l.current_value(:Fitness).must_equal nil
421
+ #
422
+ # @l.log_value(:Fitness, 1.0)
423
+ #
424
+ # expected1 = "{Fitness}: -> 1.0, mean = 1 (min = 1, max = 1, median = 1, stdev = 0)\n"
425
+ # @sio.string[13..-1].must_equal expected1
426
+ #
427
+ # @l.current_value(:Fitness).must_equal 1.0
428
+ #
429
+ # @l.log_value(:Fitness, 1.2)
430
+ #
431
+ # @l.current_value(:Fitness).must_equal 1.2
432
+ #
433
+ # expected2 = "{Fitness}: 1.0 -> 1.2 (+20%), mean = 1.1 (min = 1, max = 1.2, median = 1.1, stdev = 0.1)"
434
+ # @sio.string.split("\n").last[13..-1].must_equal expected2
435
+ #
436
+ # @l.log_value(:Fitness, 0.9)
437
+ #
438
+ # @l.current_value(:Fitness).must_equal 0.9
439
+ #
440
+ # expected3 = "{Fitness}: 1.2 -> 0.9 (-25%), mean = 1.03 (min = 0.9, max = 1.2, median = 1, stdev = 0.125)"
441
+ # @sio.string.split("\n").last[13..-1].must_equal expected3
442
+ #
443
+ # @l.current_value(:F).must_equal nil
444
+ #
445
+ # @l.log_value(:F, 42.0)
446
+ #
447
+ # expected4 = "{F}: -> 42.0, mean = 42 (min = 42, max = 42, median = 42, stdev = 0)"
448
+ # @sio.string.split("\n").last[13..-1].must_equal expected4
449
+ #
450
+ # end
451
+ #
452
+ # describe "an timed scenario of adding multiple events" do
453
+ #
454
+ # before do
455
+ # @t0 = Time.now
456
+ # sleep 0.01
457
+ #
458
+ # @t1 = Time.now
459
+ # sleep 0.01
460
+ #
461
+ # @l.log "1", :a
462
+ #
463
+ # e = @l.log_value :c, 10
464
+ # @tc10 = e.time
465
+ #
466
+ # @t2 = Time.now
467
+ # sleep 0.01
468
+ # e = @l.log "2", :a
469
+ # @ta2 = e.time
470
+ #
471
+ # @t3 = Time.now
472
+ # sleep 0.01
473
+ # @l.log "3", :a
474
+ #
475
+ # @t4 = Time.now
476
+ # sleep 0.01
477
+ # @l.log "1", :b
478
+ # e = @l.log_value :c, 20
479
+ # @tc20 = e.time
480
+ #
481
+ # @t5 = Time.now
482
+ # sleep 0.01
483
+ # @l.log "4", :a
484
+ #
485
+ # sleep 0.01
486
+ # @t6 = Time.now
487
+ #
488
+ # sleep 0.01
489
+ # @t7 = Time.now
490
+ # end
491
+ #
492
+ # it 'can return events between certain times' do
493
+ #
494
+ # @l.events_between(@t0, @t1, :a).length.must_equal 0
495
+ #
496
+ # @l.events_between(@t1, @t6, :a).length.must_equal 4
497
+ # @l.events_between(@t1, @t6, :a).map {|e| e.data[:_m]}.must_equal ["1", "2", "3", "4"]
498
+ #
499
+ # @l.events_between(@t2, @t6, :a).length.must_equal 3
500
+ # @l.events_between(@t2, @t6, :a).map {|e| e.data[:_m]}.must_equal ["2", "3", "4"]
501
+ #
502
+ # @l.events_between(@t3, @t6, :a).length.must_equal 2
503
+ # @l.events_between(@t3, @t6, :a).map {|e| e.data[:_m]}.must_equal ["3", "4"]
504
+ #
505
+ # @l.events_between(@t4, @t6, :a).length.must_equal 1
506
+ # @l.events_between(@t4, @t6, :a).map {|e| e.data[:_m]}.must_equal ["4"]
507
+ #
508
+ # @l.events_between(@t5, @t6, :a).length.must_equal 1
509
+ # @l.events_between(@t5, @t6, :a).map {|e| e.data[:_m]}.must_equal ["4"]
510
+ #
511
+ # @l.events_between(@t6, @t7, :a).length.must_equal 0
512
+ # @l.events_between(@t6, @t7, :a).map {|e| e.data[:_m]}.must_equal []
513
+ #
514
+ # @l.events_between(@t6, @t7, :b).length.must_equal 0
515
+ # @l.events_between(@t6, @t7, :b).map {|e| e.data[:_m]}.must_equal []
516
+ #
517
+ # @l.events_between(@t4, @t5, :b).length.must_equal 1
518
+ # @l.events_between(@t4, @t5, :b).map {|e| e.data[:_m]}.must_equal ["1"]
519
+ #
520
+ # @l.events_between(@t1, @t7, :b).map {|e| e.data[:_m]}.must_equal ["1"]
521
+ # @l.events_between(@t2, @t7, :b).map {|e| e.data[:_m]}.must_equal ["1"]
522
+ # @l.events_between(@t3, @t7, :b).map {|e| e.data[:_m]}.must_equal ["1"]
523
+ #
524
+ # @l.events_between(@t1, @t6, :c).map {|e| e.data[:_v]}.must_equal [10, 20]
525
+ # @l.events_between(@t1, @t2, :c).map {|e| e.data[:_v]}.must_equal [10]
526
+ # @l.events_between(@t2, @t5, :c).map {|e| e.data[:_v]}.must_equal [20]
527
+ # @l.events_between(@t6, @t7, :c).map {|e| e.data[:_v]}.must_equal []
528
+ #
529
+ # end
530
+ #
531
+ # it 'can return a pre-event to a time interval if asked to' do
532
+ #
533
+ # @l.events_between(@tc10, @t3, :c).length.must_equal 1
534
+ # @l.events_between(@tc10, @t3, :a).length.must_equal 1
535
+ #
536
+ # @l.events_between(@tc10, @t3, :c, true).length.must_equal 1
537
+ # @l.events_between(@tc10, @t3, :a, true).length.must_equal 2
538
+ #
539
+ # end
540
+ #
541
+ # # Sleep time above was 0.01 secs => 10 msecs. So what happened is:
542
+ # # @t0, 10ms, t1, 10ms, a="1", c=10, t2, 10ms,
543
+ # # a="2", t3, 10ms, a="3", t4, 10ms, b="1", c=20, t5, 10ms, a="4",
544
+ # # 10ms, t6, 10ms, t7
545
+ #
546
+ # it "can return values in steps in intervals when there are no events" do
547
+ #
548
+ # @l.values_in_steps_between(:c, @t0, @t1, 5).must_equal [nil, nil]
549
+ # @l.values_in_steps_between(:c, @t0, @t1, 10).must_equal [nil]
550
+ #
551
+ # @l.values_in_steps_between(:c, @t6, @t7, 5).must_equal [nil, nil]
552
+ # @l.values_in_steps_between(:c, @t6, @t7, 10).must_equal [nil]
553
+ #
554
+ # end
555
+ #
556
+ # it "can return values in steps in intervals where there are events" do
557
+ #
558
+ # sio = StringIO.new
559
+ # l = FeldtRuby::EventLogger.new sio
560
+ #
561
+ # t0 = Time.now
562
+ # sleep 0.01
563
+ # t1 = Time.now
564
+ # sleep 0.01
565
+ # td1 = l.log_value(:d, 1).time
566
+ # sleep 0.01
567
+ # td2 = l.log_value(:d, 2).time
568
+ # sleep 0.001
569
+ # t2 = Time.now # t2 should be at least one msec later than td2
570
+ # sleep 0.011
571
+ # td3 = l.log_value(:d, 3).time
572
+ # sleep 0.01
573
+ # t3 = Time.now
574
+ #
575
+ # l.values_in_steps_between(:d, t0, t1, 5).must_equal [nil, nil]
576
+ #
577
+ # diff = td2.milli_seconds - td1.milli_seconds
578
+ #
579
+ # l.values_in_steps_between(:d, td1, td2, diff-1).must_equal [1, 1]
580
+ #
581
+ # # The stop time is exclusive so should NOT be included
582
+ # l.values_in_steps_between(:d, td1, td2, diff).must_equal [1]
583
+ # l.values_in_steps_between(:d, td1, td2, diff+1).must_equal [1]
584
+ #
585
+ # l.values_in_steps_between(:d, td1, t2, diff-1).must_equal [1, 1]
586
+ # l.values_in_steps_between(:d, td1, t2, diff).must_equal [1, 2]
587
+ # l.values_in_steps_between(:d, td1, t2, diff+1).must_equal [1]
588
+ #
589
+ # vs = l.values_in_steps_between(:d, t0, t3, 1)
590
+ #
591
+ # n = 0
592
+ # d = td1.milli_seconds - t0.milli_seconds
593
+ # vs[n, d].must_equal( [nil] * d )
594
+ #
595
+ # n += d
596
+ # d = td2.milli_seconds - td1.milli_seconds
597
+ # vs[n, d].must_equal( [1] * d )
598
+ #
599
+ # n += d
600
+ # d = td3.milli_seconds - td2.milli_seconds
601
+ # vs[n, d].must_equal( [2] * d )
602
+ #
603
+ # n += d
604
+ # d = t3.milli_seconds - td3.milli_seconds
605
+ # vs[n, d].must_equal( [3] * d )
606
+ #
607
+ # end
608
+ #
609
+ # end
610
+ #end
611
+ #
612
+ #describe "Adding logging to an object and its instance vars" do
613
+ # class A
614
+ # include FeldtRuby::Logging
615
+ #
616
+ # def initialize(b)
617
+ # @b = b
618
+ # setup_logger_and_distribute_to_instance_variables()
619
+ # end
620
+ #
621
+ # def calc
622
+ # log "entering calc"
623
+ # val = 1 + @b.calc
624
+ # log_value :res, val
625
+ # val
626
+ # end
627
+ # end
628
+ #
629
+ # class B
630
+ # include FeldtRuby::Logging
631
+ #
632
+ # def initialize(l = nil)
633
+ # setup_logger_and_distribute_to_instance_variables l
634
+ # end
635
+ #
636
+ # def calc
637
+ # log "entering B#calc"
638
+ # v = rand(100)
639
+ # log_value :b_res, v
640
+ # v
641
+ # end
642
+ # end
643
+ #
644
+ # before do
645
+ # @b = B.new
646
+ # @a = A.new @b
647
+ # end
648
+ #
649
+ # it 'sets up the sam logger in both A objects and their instance vars' do
650
+ #
651
+ # @a.logger.is_a?(FeldtRuby::Logger).must_equal true
652
+ #
653
+ # @b.logger.must_equal @a.logger
654
+ #
655
+ # end
656
+ #
657
+ # it 'uses an explicit logger supplied to a instance var also in the using object' do
658
+ #
659
+ # l = FeldtRuby::EventLogger.new
660
+ # b = B.new l
661
+ # a = A.new b
662
+ #
663
+ # b.logger.must_equal a.logger
664
+ # a.logger.must_equal l
665
+ #
666
+ # end
667
+ #
668
+ # it 'logs to the logger' do
669
+ #
670
+ # l = @a.logger
671
+ # l.verbose = false # So we don't print them while testing...
672
+ #
673
+ # res = @a.calc()
674
+ # l.current_value(:res).must_equal res
675
+ # l.current_value(:b_res).must_equal res-1
676
+ #
677
+ # res = @a.calc()
678
+ # l.current_value(:res).must_equal res
679
+ # l.current_value(:b_res).must_equal res-1
680
+ #
681
+ # end
682
+ #end