intervals 0.4.76 → 0.5.83

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.
@@ -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