intervals 0.4.76 → 0.5.83

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- 0.4.76
1
+ 0.5.83
@@ -27,10 +27,6 @@ end
27
27
  class Interval
28
28
  include Enumerable
29
29
 
30
- # Exception class for failed preconditions on construction arguments.
31
- class ConstructionError < ArgumentError
32
- end
33
-
34
30
  # Create a closed (multi-)interval with the given numerical extrema.
35
31
  #
36
32
  # Interval[3] # In mathematical notation: [3,3]
@@ -55,9 +51,7 @@ class Interval
55
51
  unless
56
52
  array.all?{|x| Numeric === x} && array.size <= 2 ||
57
53
  array.all?{|x| Array === x && x.size <= 2 && x.all?{|c| Numeric === c}}
58
- raise ConstructionError,
59
- "An interval can only be constructed either from at most two numbers or from a " \
60
- "sequence of arrays of at most two numbers: #{array.inspect}"
54
+ raise Exception::Construction, array
61
55
  end
62
56
  raise
63
57
  end
@@ -99,6 +93,17 @@ class Interval
99
93
  }.transpose.first
100
94
  end
101
95
 
96
+ # Construct an interval by evaluating the given block using rounding
97
+ # down and rounding up modes.
98
+ #
99
+ # Interval.eval{1/3.0} # => Interval[0.333333333333333, 0.333333333333333]
100
+ #
101
+ def Interval.eval(&block)
102
+ Simple.new(
103
+ FPU.down {block.call},
104
+ FPU.up {block.call})
105
+ end
106
+
102
107
  # Returns a list of the parameters that can be used to reconstruct the
103
108
  # interval.
104
109
  #
@@ -349,6 +354,14 @@ class Interval
349
354
  all? {|x| x.sharp?}
350
355
  end
351
356
 
357
+ # An interval is degenerate if each component includes exactly one element.
358
+ #
359
+ # Interval
360
+ #
361
+ def degenerate?
362
+ all? {|x| x.degenerate?}
363
+ end
364
+
352
365
  # The hull is the smallest simple interval containing the interval itself.
353
366
  #
354
367
  # Interval[[1,2],[3,4]] # => Interval[1, 4]
@@ -516,6 +529,21 @@ class Interval::Simple < Interval
516
529
  (inf + sup) * 2 **-1
517
530
  end
518
531
 
532
+ # If the interval is degenerate, return its only element, else raise an exception.
533
+ #
534
+ # Interval.eval{1/2.0}.number
535
+ # # => 0.5
536
+ # Interval.eval{1/3.0}.number
537
+ # # fails
538
+ #
539
+ def number
540
+ if inf == sup
541
+ inf
542
+ else
543
+ raise Exception::Nondegenerate, self
544
+ end
545
+ end
546
+
519
547
  # Used to implement Interval's plus operator (\+).
520
548
  def add (other)
521
549
  self.class.new(
@@ -528,7 +556,7 @@ class Interval::Simple < Interval
528
556
  self.class.new(-sup,-inf)
529
557
  end
530
558
 
531
- # Dependent subtraction, i.e., the inverse of the addition
559
+ # Dependent subtraction, i.e., the inverse of the addition.
532
560
  #
533
561
  # Interval[3,4].dsub Interval[1,2]
534
562
  # # => Interval[2]
@@ -604,12 +632,17 @@ class Interval::Simple < Interval
604
632
  true
605
633
  elsif w.kind_of?(Float) && (inf >= 0 || sup <= 0)
606
634
  s1, s2 = extrema.map{|x| x.abs}.sort
607
- s1 + s1.ulp == s2
635
+ s1 + s1.to_f.ulp == s2
608
636
  else
609
637
  false
610
638
  end
611
639
  end
612
640
 
641
+ # Implements Interval#degenerate?
642
+ def degenerate? # :nodoc:
643
+ inf == sup
644
+ end
645
+
613
646
  # Implements Interval#exp
614
647
  def exp #:nodoc:
615
648
  self.class.new(FPU.exp_down(inf), FPU.exp_up(sup))
@@ -714,6 +747,14 @@ class Interval::Multiple < Interval
714
747
  self
715
748
  end
716
749
 
750
+ # Raise more expressive exceptions than NoMethodError when invoking
751
+ # methods specific to simple intervals
752
+ [:number,:midpoint,:width].each{|f|
753
+ define_method(f) {
754
+ raise Exception::Nonsimple, self
755
+ }
756
+ }
757
+
717
758
  end
718
759
 
719
760
  class Range
@@ -733,8 +774,7 @@ class Range
733
774
  end]
734
775
  rescue NoMethodError
735
776
  if exclude_end? && !last.respond_to?(:succ)
736
- raise Interval::ConstructionError,
737
- "A three-dot range with non-discrete upper bound cannot be made into a closed interval"
777
+ raise Interval::Exception::OpenRight, self
738
778
  end
739
779
  raise
740
780
  end
@@ -749,6 +789,44 @@ class Interval
749
789
  # The sharp interval that includes Pi.
750
790
  PI = Math::PI + Interval[0, 2 ** -51]
751
791
 
792
+ # Module for the possible interval exceptions.
793
+ module Exception
794
+
795
+ # Exception class for failed preconditions on construction arguments.
796
+ class Construction < ArgumentError
797
+ def initialize(array)
798
+ super(
799
+ "An interval can only be constructed either from at most two " \
800
+ "numbers or from a sequence of arrays of at most two numbers: " +
801
+ array.inspect)
802
+ end
803
+ end
804
+
805
+ # Exception class for a failed precondition on an interval being degenerate.
806
+ class Nondegenerate < ArgumentError
807
+ def initialize(i)
808
+ super("#{i.inspect} is not degenerate.")
809
+ end
810
+ end
811
+
812
+ # Exception class for a failed precondition on an interval being simple.
813
+ class Nonsimple < ArgumentError
814
+ def initialize(i)
815
+ super("#{i.inspect} is not simple.")
816
+ end
817
+ end
818
+
819
+ # Exception class for failed preconditions on range type.
820
+ class OpenRight < ArgumentError
821
+ def initialize(range)
822
+ super(
823
+ "Cannot construct an interval from a three-dot range " \
824
+ "with end-value of type #{range.last.class}.")
825
+ end
826
+ end
827
+
828
+ end
829
+
752
830
  end
753
831
 
754
832
  require File.dirname(__FILE__) + '/../test/test_interval.rb' if __FILE__ == $0
@@ -0,0 +1,23 @@
1
+ require 'test/unit/assertions'
2
+
3
+ module Test
4
+ module Unit
5
+ module Assertions
6
+
7
+ def assert_predicate(x, predicate, message = nil)
8
+ template = "<?> should be ?, but it is not."
9
+ assert_block(
10
+ build_message(message, template, x, predicate.to_s.gsub(/\?$/){})
11
+ ) { x.send(predicate) }
12
+ end
13
+
14
+ def assert_not_predicate(x, predicate, message = nil)
15
+ template = "<?> is ?."
16
+ assert_block(
17
+ build_message(message, template, x, predicate.to_s.gsub(/\?$/){})
18
+ ) { !x.send(predicate) }
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -22,26 +22,26 @@ class FPU::TestRounding < Test::Unit::TestCase
22
22
 
23
23
  # Nearest rounding of 1/3 is downwards.
24
24
  def test_third
25
- assert(FPU.down {1/3.0} == 1/3.0, "1/3 to -");
26
- assert(FPU.up {1/3.0} != 1/3.0, "1/3 to +");
27
- assert(FPU.down {-1/3.0} != -1/3.0, "-1/3 to -");
28
- assert(FPU.up {-1/3.0} == -1/3.0, "-1/3 to +");
25
+ assert_equal 1/3.0, FPU.down {1/3.0}
26
+ assert_not_equal 1/3.0, FPU.up {1/3.0}
27
+ assert_not_equal -1/3.0, FPU.down {-1/3.0}
28
+ assert_equal -1/3.0, FPU.up {-1/3.0}
29
29
  end
30
30
 
31
31
  # 1/4 is exact.
32
32
  def test_fourth
33
- assert(FPU.down {1/4.0} == 1/4.0, "1/4 to -");
34
- assert(FPU.up {1/4.0} == 1/4.0, "1/4 to +");
35
- assert(FPU.down {-1/4.0} == -1/4.0, "-1/4 to -");
36
- assert(FPU.up {-1/4.0} == -1/4.0, "-1/4 to +");
33
+ assert_equal 1/4.0, FPU.down {1/4.0}
34
+ assert_equal 1/4.0, FPU.up {1/4.0}
35
+ assert_equal -1/4.0, FPU.down {-1/4.0}
36
+ assert_equal -1/4.0, FPU.up {-1/4.0}
37
37
  end
38
38
 
39
39
  # Nearest rounding of 1/5 is upwards.
40
40
  def test_fifth
41
- assert(FPU.down {1/5.0} != 1/5.0, "1/5 to -");
42
- assert(FPU.up {1/5.0} == 1/5.0, "1/5 to +");
43
- assert(FPU.down {-1/5.0} == -1/5.0, "-1/5 to -");
44
- assert(FPU.up {-1/5.0} != -1/5.0, "-1/5 to +");
41
+ assert_not_equal 1/5.0, FPU.down {1/5.0}
42
+ assert_equal 1/5.0, FPU.up {1/5.0}
43
+ assert_equal -1/5.0, FPU.down {-1/5.0}
44
+ assert_not_equal -1/5.0, FPU.up {-1/5.0}
45
45
  end
46
46
 
47
47
  def test_platform
@@ -8,19 +8,20 @@
8
8
  # To control the FPU's rounding mode
9
9
  require File.dirname(__FILE__) + '/../lib/interval' if __FILE__ == $0
10
10
  require 'test/unit'
11
+ require File.dirname(__FILE__) + '/assertions.rb'
11
12
 
12
13
  # Assertions to be used when testing intervals.
13
- module Interval::Assertions
14
+ module Interval::Assertions
14
15
 
15
- def assert_sharp(x)
16
- assert(x.sharp?, x.inspect + " should be sharp.")
17
- assert((-x).sharp?, (-x).inspect + " should be sharp.")
16
+ def assert_sharp(x, message = nil)
17
+ assert_predicate(x, :sharp?)
18
+ assert_predicate(-x, :sharp?)
18
19
  end
19
20
 
20
21
  # Assert that an interval is not sharp.
21
- def assert_fuzzy(x)
22
- assert(!x.sharp?, x.inspect + " should not be sharp.")
23
- assert(!(-x).sharp?, (-x).inspect + " should not be sharp.")
22
+ def assert_fuzzy(x, message = nil)
23
+ assert_not_predicate(x, :sharp?)
24
+ assert_not_predicate(-x, :sharp?)
24
25
  end
25
26
 
26
27
  # Assert that +s+ is a solution to +f+ == 0. Unless the +weak+ flag is set,
@@ -29,7 +30,7 @@ module Interval::Assertions
29
30
  def assert_solution(s,f,expected, weak = nil)
30
31
  res = s.map{|c| f.call(c)}.inject{|a,c| a|c}
31
32
  # Did something spectacarly unexpected happen?
32
- assert(res.simple?, res)
33
+ assert_predicate(res, :simple?)
33
34
  # Is it a solution?
34
35
  assert_include(res, 0)
35
36
  # Is it minimal?
@@ -48,18 +49,21 @@ module Interval::Assertions
48
49
  end
49
50
 
50
51
  # Assert that the interval +i+ includes +x+.
51
- def assert_include(i, x)
52
- assert(i.include?(x), i.inspect + " should include " + x.inspect)
52
+ def assert_include(i, x, message = nil)
53
+ template = "<?> should include <?>, but it does not."
54
+ assert_block(build_message(message, template, i, x)) { i.include?(x) }
53
55
  end
54
56
 
55
57
  # Assert that the interval +i+ does not include +x+.
56
- def assert_outside(i, x)
57
- assert(!i.include?(x), i.inspect + " should not include " + x.inspect)
58
+ def assert_outside(i, x, message = nil)
59
+ template = "<?> should not include <?>, but it does."
60
+ assert_block(build_message(message, template, i, x)) { !i.include?(x) }
58
61
  end
59
62
 
60
63
  # Assert that +x+ is a subset of +y+.
61
- def assert_subset(x,y)
62
- assert(x == x & y, x.inspect + " should be a subset of " + y.inspect)
64
+ def assert_subset(x, y, message = nil)
65
+ template = "<?> should be a subset of <?>, but is not."
66
+ assert_block(build_message(message, template, x, y)) { x == x & y }
63
67
  end
64
68
 
65
69
  end
@@ -85,11 +89,11 @@ class Interval::TestFundamentals < Test::Unit::TestCase
85
89
  end
86
90
 
87
91
  def test_construction_failure
88
- assert_raise(Interval::ConstructionError) { Interval[1,[2,3]] }
89
- assert_raise(Interval::ConstructionError) { Interval[1,2,3] }
90
- assert_raise(Interval::ConstructionError) { Interval[[1],2] }
91
- assert_raise(Interval::ConstructionError) { Interval['a', 1] }
92
- assert_raise(Interval::ConstructionError) { Interval['a', 'b'] }
92
+ assert_raise(Interval::Exception::Construction) { Interval[1,[2,3]] }
93
+ assert_raise(Interval::Exception::Construction) { Interval[1,2,3] }
94
+ assert_raise(Interval::Exception::Construction) { Interval[[1],2] }
95
+ assert_raise(Interval::Exception::Construction) { Interval['a', 1] }
96
+ assert_raise(Interval::Exception::Construction) { Interval['a', 'b'] }
93
97
  end
94
98
 
95
99
  def test_inequality
@@ -177,7 +181,13 @@ class Interval::TestSimpleMethods < Test::Unit::TestCase
177
181
 
178
182
  def test_midpoint
179
183
  assert_equal(1.5,Interval[1,2].midpoint)
180
- assert_raise(NoMethodError){ Interval[[1,2],[3,4]].midpoint }
184
+ ex = assert_raise(Interval::Exception::Nonsimple){ Interval[[1,2],[3,4]].midpoint }
185
+ end
186
+
187
+ def test_number
188
+ assert_equal(0.5, Interval.eval{1/2.0}.number)
189
+ assert_raise(Interval::Exception::Nondegenerate) { Interval.eval{1/3.0}.number }
190
+ assert_raise(Interval::Exception::Nonsimple) { Interval[[1,2],[3,4]].number }
181
191
  end
182
192
 
183
193
  def test_width
@@ -192,6 +202,7 @@ class Interval::TestSimpleMethods < Test::Unit::TestCase
192
202
  tester.call(5.0)
193
203
  tester.call(119.0)
194
204
  tester.call(34e-4)
205
+ assert_raise(Interval::Exception::Nonsimple) { Interval[[1,2],[3,4]].width }
195
206
  end
196
207
 
197
208
  end
@@ -264,19 +275,22 @@ class Interval::TestMethods < Test::Unit::TestCase
264
275
  end
265
276
 
266
277
  def test_simple?
267
- assert(Interval[1,2].simple?)
268
- assert(!Interval[[1,2],[3,4]].simple?)
278
+ assert_predicate(Interval[1,2], :simple?)
279
+ assert_not_predicate(Interval[[1,2],[3,4]], :simple?)
269
280
  end
270
281
 
271
282
  def test_empty?
272
- assert(!Interval[1,2].empty?)
273
- assert(!Interval[[1,2],[4,3]].empty?)
274
- assert(Interval[].empty?)
283
+ assert_not_predicate(Interval[1,2], :empty?)
284
+ assert_not_predicate(Interval[[1,2],[4,3]], :empty?)
285
+ assert_predicate(Interval[], :empty?)
275
286
  end
276
287
 
277
288
  def test_sharp?
278
289
  assert_sharp(Interval[5,5])
290
+ assert_sharp(Interval[5,5.0])
291
+ assert_sharp(Interval[5.0,5])
279
292
  assert_sharp(Interval[[1],[2]])
293
+ assert_fuzzy(Interval[5,5.1])
280
294
  assert_fuzzy(Interval[[1],[2,3]])
281
295
  assert_fuzzy(Interval[5,6])
282
296
  assert_fuzzy(Interval[1.1,1.2])
@@ -291,6 +305,25 @@ class Interval::TestMethods < Test::Unit::TestCase
291
305
  assert_fuzzy(Interval[6369051672525771, 6369051672525785] * 2.0 ** -52)
292
306
  end
293
307
 
308
+ def test_degenerate?
309
+ assert_predicate(Interval[5], :degenerate?)
310
+ assert_predicate(Interval[5.43], :degenerate?)
311
+ assert_predicate(Interval.eval{1/2.0}, :degenerate?)
312
+
313
+ assert_not_predicate(Interval.eval{1/3.0}, :degenerate?)
314
+ assert_not_predicate(Interval.eval{1/5.0}, :degenerate?)
315
+ assert_not_predicate(Interval[1,1.1], :degenerate?)
316
+
317
+ assert_predicate(Interval[[1],[2]], :degenerate?)
318
+ assert_predicate(Interval[[1.23],[2]], :degenerate?)
319
+ assert_predicate(Interval[[1],[2.3]], :degenerate?)
320
+ assert_predicate(Interval[[1.0123],[2.3]], :degenerate?)
321
+
322
+ assert_not_predicate(Interval[[1],[1.1,1.2]], :degenerate?)
323
+ assert_not_predicate(Interval[[1,1.1],[2]], :degenerate?)
324
+ assert_not_predicate(Interval[[1,1.1],[2,3]], :degenerate?)
325
+ end
326
+
294
327
  def test_hull
295
328
  assert_equal(Interval[1,4], Interval[[1,2],[3,4]].hull)
296
329
  assert_equal(Interval[1,4], Interval[1,4].hull)
@@ -301,14 +334,36 @@ class Interval::TestMethods < Test::Unit::TestCase
301
334
  assert_equal(Interval[1.2, 5.3], (1.2 .. 5.3).to_interval)
302
335
  assert_equal(Interval[1, 4], (1 .. 4).to_interval)
303
336
  assert_equal(Interval[1, 3], (1 ... 4).to_interval)
304
- assert_raise(Interval::ConstructionError){ (1 ... 3.1).to_interval }
337
+ assert_raise(Interval::Exception::OpenRight){ (1 ... 3.1).to_interval }
305
338
  if defined?(Rational)
306
339
  x = Rational(1,3)
307
340
  assert_equal(Interval[1, x], (1 .. x).to_interval)
308
- assert_raise(Interval::ConstructionError){ (1 ... x).to_interval }
341
+ assert_raise(Interval::Exception::OpenRight){ (1 ... x).to_interval }
309
342
  end
310
343
  end
311
344
 
345
+ def test_eval
346
+ assert_equal(1/Interval[3.0], Interval.eval{1/3.0})
347
+ assert_equal(1/Interval[5.0], Interval.eval{1/5.0})
348
+ assert_equal(Interval[0.5], Interval.eval{1/2.0})
349
+ end
350
+
351
+ def test_exceptions
352
+ ex = assert_raise(Interval::Exception::Nonsimple){ Interval[[1,2],[3,4]].midpoint }
353
+ assert_equal("Interval[[1, 2], [3, 4]] is not simple.", ex.message)
354
+ ex = assert_raise(Interval::Exception::Nondegenerate) { Interval[1,2].number }
355
+ assert_equal("Interval[1, 2] is not degenerate.", ex.message)
356
+ ex = assert_raise(Interval::Exception::Construction) { Interval[1,2,3] }
357
+ assert_equal(
358
+ "An interval can only be constructed either from at most two " \
359
+ "numbers or from a sequence of arrays of at most two numbers: [1, 2, 3]",
360
+ ex.message)
361
+ ex = assert_raise(Interval::Exception::OpenRight){ (1 ... 3.1).to_interval }
362
+ assert_equal(
363
+ "Cannot construct an interval from a three-dot range " \
364
+ "with end-value of type Float.", ex.message)
365
+ end
366
+
312
367
  end
313
368
 
314
369
  # Tests the Interval-based non-linear solver.
@@ -7,6 +7,7 @@
7
7
 
8
8
  require 'test/unit'
9
9
  require File.dirname(__FILE__) + '/../lib/struct_float.rb' if __FILE__ == $0
10
+ require File.dirname(__FILE__) + '/assertions.rb'
10
11
 
11
12
  # Tests Struct::Float.
12
13
  class Struct::Float::Test < Test::Unit::TestCase
@@ -23,10 +24,10 @@ class Struct::Float::Test < Test::Unit::TestCase
23
24
  # it does not make sense to have it as a constant as for
24
25
  # Infinity. In particular a NaN has 2047 as biased exponent and
25
26
  # non-zero fraction
26
- assert(2047, s.biased_exp)
27
- assert(!s.fraction.zero?, s.fraction)
27
+ assert_equal(2047, s.biased_exp)
28
+ assert_not_predicate(s.fraction, :zero?)
28
29
  # You cannot test for NaN with equality. You MUST use nan?
29
- assert(s.to_f.nan?, s.to_f)
30
+ assert_predicate(s.to_f, :nan?)
30
31
  assert_equal(s, eval(s.inspect))
31
32
  end
32
33
 
@@ -13,13 +13,17 @@ module Enumerable
13
13
  # Enumerable#min fails if the collection includes a NaN. Instead,
14
14
  # this methods returns a NaN.
15
15
  def ieee_min
16
- find( proc { min } ){|x| x.respond_to?(:nan?) && x.nan?}
16
+ min
17
+ rescue ArgumentError
18
+ find{|x| x.respond_to?(:nan?) && x.nan?} or raise
17
19
  end
18
20
 
19
21
  # Enumerable#max fails if the collection includes a NaN. Instead,
20
22
  # this methods returns a NaN.
21
23
  def ieee_max
22
- find( proc { max } ){|x| x.respond_to?(:nan?) && x.nan?}
24
+ max
25
+ rescue ArgumentError
26
+ find{|x| x.respond_to?(:nan?) && x.nan?} or raise
23
27
  end
24
28
 
25
29
  end
@@ -39,18 +43,17 @@ class Interval::Simple
39
43
 
40
44
  end
41
45
 
46
+ require 'test/unit'
47
+ require File.dirname(__FILE__) + '/../test/assertions.rb'
48
+
42
49
  # Tests exception-free multiplication.
43
50
  class CleanNanTest < Test::Unit::TestCase
44
51
 
45
- def assert_nan(s)
46
- assert(s.nan?, "#{s} is not nan")
47
- end
48
-
49
52
  def test_ieee_min_and_max
50
53
  assert_equal(1,[1,2,3].ieee_min)
51
54
  assert_equal(3,[1,2,3].ieee_max)
52
- assert_nan([1,0.0/0.0,3].ieee_min)
53
- assert_nan([1,0.0/0.0,3].ieee_max)
55
+ assert_predicate([1,0.0/0.0,3].ieee_min, :nan?)
56
+ assert_predicate([1,0.0/0.0,3].ieee_max, :nan?)
54
57
  end
55
58
 
56
59
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: intervals
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.76
7
- date: 2006-04-09 00:00:00 +02:00
6
+ version: 0.5.83
7
+ date: 2006-04-13 00:00:00 +02:00
8
8
  summary: Interval arithmetic in Ruby.
9
9
  require_paths:
10
10
  - lib
@@ -40,6 +40,7 @@ files:
40
40
  - test/test_struct_float.rb
41
41
  - test/data_cos.txt
42
42
  - test/test_fpu.rb
43
+ - test/assertions.rb
43
44
  - ext/jamis-mod.rb
44
45
  - ext/extconf.rb
45
46
  - ext/fpu.c