intervals 0.3.63 → 0.4.75

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/lib/struct_float.rb CHANGED
@@ -65,69 +65,5 @@ class Float
65
65
 
66
66
  end
67
67
 
68
- require 'test/unit'
68
+ require File.dirname(__FILE__) + '/../test/test_struct_float.rb' if __FILE__ == $0
69
69
 
70
- # Tests Struct::Float.
71
- class Struct::Float::Test < Test::Unit::TestCase
72
-
73
- def assert_consistent(f,s)
74
- assert_equal(f,s.to_f)
75
- assert_equal(s,f.to_struct)
76
- assert_equal(s, eval(s.inspect))
77
- assert(f.ulp.infinite? || (f/f.ulp).to_i == f/f.ulp, [f,f.ulp].inspect)
78
- end
79
-
80
- def assert_nan(s)
81
- # NaN does not have a unique representation, and therefore
82
- # it does not make sense to have it as a constant as for
83
- # Infinity. In particular a NaN has 2047 as biased exponent and
84
- # non-zero fraction
85
- assert(2047, s.biased_exp)
86
- assert(!s.fraction.zero?, s.fraction)
87
- # You cannot test for NaN with equality. You MUST use nan?
88
- assert(s.to_f.nan?, s.to_f)
89
- assert_equal(s, eval(s.inspect))
90
- end
91
-
92
- def test_consistency
93
- assert_consistent Infinity, Struct::Float[1,2047,0]
94
- assert_consistent -Infinity, Struct::Float[-1,2047,0]
95
- assert_consistent 2.0 ** -1074, Struct::Float[1,0,1]
96
- assert_consistent 0.0, Struct::Float[1,0,0]
97
- assert_consistent -0.0, Struct::Float[-1,0,0]
98
- assert_consistent 1.0, Struct::Float[1,1023, 0]
99
- assert_consistent 1/3.0, Struct::Float[1,1021,0x5555555555555]
100
- assert_consistent 1/5.0, Struct::Float[1,1020,0x999999999999a]
101
- end
102
-
103
- def test_nan
104
- assert_nan( (0.0/0.0).to_struct)
105
- assert_nan( Struct::Float[-1,2047,34])
106
- assert_nan( Struct::Float[+1,2047,34])
107
- end
108
-
109
- def test_denormalized
110
- assert_consistent 34 * 2.0 ** -(1023+51), Struct::Float[1,0,34]
111
- end
112
-
113
- def test_normalized
114
- assert_consistent -(2**52+34) * 2.0**(1043-1023-52), Struct::Float[-1,1043,34]
115
- end
116
-
117
- def test_samples
118
- n = 1000
119
- srand 12345
120
- n.times{
121
- s = Struct::Float[2*rand(2) -1, rand(2048), rand(2**52)]
122
- f = s.to_f
123
- if f.nan?
124
- assert_nan(s)
125
- else
126
- assert_consistent(f,s)
127
- end
128
- }
129
- end
130
-
131
- end
132
-
133
- Test::Unit.run = (__FILE__ != $0)
data/test/runall.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Tests for the whole library.
4
+ #
5
+ # Copyright (c) 2006 Stefano Taschini <taschini@ieee.org>.
6
+ # Licensed under the terms of LGPL: http://www.gnu.org/copyleft/lesser.html
7
+
8
+ require File.dirname(__FILE__) + '/../lib/interval'
9
+
10
+ Dir.glob(File.dirname(__FILE__) + '/test*.rb').each{|f|
11
+ require f
12
+ }
13
+
14
+ Test::Unit.run = (__FILE__ != $0)
data/test/test_fpu.rb ADDED
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Tests for the module FPU.
4
+ #
5
+ # Copyright (c) 2006 Stefano Taschini <taschini@ieee.org>.
6
+ # Licensed under the terms of LGPL: http://www.gnu.org/copyleft/lesser.html
7
+
8
+ require File.dirname(__FILE__) + '/../lib/fpu' if __FILE__ == $0
9
+ require 'test/unit'
10
+
11
+ # Tests the FPU rounding modes.
12
+ class FPU::TestRounding < Test::Unit::TestCase
13
+ include FPU
14
+
15
+ def setup
16
+ @saved = rounding
17
+ end
18
+
19
+ def teardown
20
+ self.rounding = @saved
21
+ end
22
+
23
+ # Nearest rounding of 1/3 is downwards.
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 +");
29
+ end
30
+
31
+ # 1/4 is exact.
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 +");
37
+ end
38
+
39
+ # Nearest rounding of 1/5 is upwards.
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 +");
45
+ end
46
+
47
+ def test_platform
48
+ modes = case (RUBY_PLATFORM)
49
+ when /powerpc/ then [0,2,3]
50
+ when /i[0-9]86/ then [0,0x0800,0x0400]
51
+ when /sparc/ then [0,0x80000000, 0xC0000000]
52
+ else flunk("Cannot be sure of the processor flags for platform "+RUBY_PLATFORM)
53
+ end
54
+ assert_equal(modes,
55
+ [rounding, FPU.up { rounding }, FPU.down { rounding }])
56
+ end
57
+
58
+ def test_rounding_consts
59
+ assert_equal(PlusRounding , FPU.up { FPU.rounding })
60
+ assert_equal(MinusRounding , FPU.down { FPU.rounding })
61
+ assert_equal(StandardRounding, rounding)
62
+
63
+ tester = proc { |x| assert_equal(x, (self.rounding = x; FPU.rounding))}
64
+ tester.call(PlusRounding);
65
+ tester.call(MinusRounding);
66
+ tester.call(StandardRounding);
67
+ end
68
+
69
+ def test_exception_safety
70
+ assert_raise(ArgumentError){FPU.up {[2.0, 0/0.0].sort}}
71
+ assert_equal(StandardRounding, rounding)
72
+ assert_raise(ArgumentError){FPU.down {[2.0, 0/0.0].sort}}
73
+ assert_equal(StandardRounding, rounding)
74
+ end
75
+
76
+ def test_power
77
+ x = 1/3.0;
78
+ # The cube of one third should depend on the rounding mode
79
+ assert_operator(FPU.down{x*x*x}, :<, FPU.up{x*x*x})
80
+ # But using the built-in power operator, usually it doesn't
81
+ # assert_equal(FPU.down{x**3}, FPU.up{x**3})
82
+ # So we define an integer power methods that does
83
+ assert_operator(FPU.down{FPU.power(x, 3)}, :<, FPU.up{FPU.power(x, 3)})
84
+
85
+ assert_equal(32, FPU.power(2, 5))
86
+ assert_equal([FPU.down{x*x*x}, FPU.up{x*x*x}],[FPU.down{FPU.power(x, 3)},FPU.up{FPU.power(x, 3)}])
87
+ assert_equal(1.25 ** 13, FPU.power(1.25, 13))
88
+ assert_equal((-1.25) ** 17, FPU.power(-1.25, 17))
89
+ assert_equal((-1.25) ** 18, FPU.power(-1.25, 18))
90
+ (1..10).each {|i|
91
+ assert_equal(2.0 ** i, FPU.power(2.0, i))
92
+ }
93
+
94
+ end
95
+
96
+ end
97
+
98
+ if !FPU.respond_to?(:exp_up)
99
+ warn "***\n*** Module fpu is compiled without transcendetal functions\n***"
100
+ else
101
+ # Tests the correct-rounding transcendental functions, if they exist.
102
+ class FPU::TestTranscendental < Test::Unit::TestCase
103
+ require 'enumerator'
104
+
105
+ def locate(name)
106
+ ([File.dirname(__FILE__)] + $LOAD_PATH).
107
+ map{|p| File.expand_path("../test/#{name}", File.expand_path(p))}.
108
+ find(proc {raise LoadError, "Cannot locate #{name}"}) {|p|
109
+ File.exist?(p)
110
+ }
111
+ end
112
+
113
+ @@test_vectors_cache = {}
114
+
115
+ def get_vectors (name)
116
+ @@test_vectors_cache[name] ||= IO.readlines(locate(name)).map{|x|
117
+ e1,m1,e2,m2 = x.chomp.split(' ').map{|y| y.to_i}
118
+ [e1,m1,e2,m2,e2,m2 - 1].enum_slice(2).map{|e,m| Math::ldexp(m,e)}}
119
+ end
120
+
121
+ def test_easy
122
+ a, b, c= [
123
+ [-1, 1008, 2553927345288884],
124
+ [1, 1022, 4503168880409012],
125
+ [1, 1022, 4503168880409011]].map{|x| Struct::Float[*x].to_f}
126
+ assert_equal(b, FPU.exp_up(a))
127
+ assert_equal(c, FPU.exp_down(a))
128
+
129
+ assert_equal(Infinity, FPU.exp_up(Infinity))
130
+ assert_equal(Infinity, FPU.exp_down(Infinity))
131
+ assert_equal(0, FPU.exp_up(-Infinity))
132
+ assert_equal(0, FPU.exp_down(-Infinity))
133
+
134
+ assert_equal(Infinity, FPU.log_up(Infinity))
135
+ assert_equal(Infinity, FPU.log_down(Infinity))
136
+ assert_equal(-Infinity, FPU.log_up(0))
137
+ assert_equal(-Infinity, FPU.log_down(0))
138
+
139
+ assert_equal(Math::PI, 2*FPU.atan_down(Infinity))
140
+ assert_equal(Math::PI+2 ** -51, 2*FPU.atan_up(Infinity))
141
+ assert_equal(-Math::PI, 2*FPU.atan_up(-Infinity))
142
+ assert_equal(-Math::PI-2 ** -51, 2*FPU.atan_down(-Infinity))
143
+ end
144
+
145
+ def test_exp
146
+ get_vectors("data_exp.txt").map{|x,y,z|
147
+ assert_equal(y, FPU.exp_up(x))
148
+ assert_equal(z, FPU.exp_down(x))
149
+ }
150
+ end
151
+
152
+ def test_log
153
+ get_vectors("data_log.txt").map{|x,y,z|
154
+ assert_equal(y, FPU.log_up(x))
155
+ assert_equal(z, FPU.log_down(x))
156
+ }
157
+ end
158
+
159
+ def test_atan
160
+ get_vectors("data_atan.txt").map{|x,y,z|
161
+ assert_equal(y, FPU.atan_up(x))
162
+ assert_equal(z, FPU.atan_down(x))
163
+ }
164
+ end
165
+
166
+ def test_cos
167
+ get_vectors("data_cos.txt").map{|x,y,z|
168
+ assert_equal(y, FPU.cos_up(x))
169
+ assert_equal(z, FPU.cos_down(x))
170
+ }
171
+ end
172
+
173
+ def test_sin
174
+ get_vectors("data_sin.txt").map{|x,y,z|
175
+ assert_equal(y, FPU.sin_up(x))
176
+ assert_equal(z, FPU.sin_down(x))
177
+ }
178
+ end
179
+
180
+ def test_tan
181
+ get_vectors("data_tan.txt").map{|x,y,z|
182
+ assert_equal(y, FPU.tan_up(x))
183
+ assert_equal(z, FPU.tan_down(x))
184
+ }
185
+ end
186
+
187
+ def test_cosh
188
+ get_vectors("data_cosh.txt").map{|x,y,z|
189
+ assert_equal(y, FPU.cosh_up(x))
190
+ assert_equal(z, FPU.cosh_down(x))
191
+ }
192
+ end
193
+
194
+ def test_sinh
195
+ get_vectors("data_sinh.txt").map{|x,y,z|
196
+ assert_equal(y, FPU.sinh_up(x))
197
+ assert_equal(z, FPU.sinh_down(x))
198
+ }
199
+ end
200
+
201
+ # It can happen, depending on the implementation, that Math::sinh is broken
202
+ def broken_math #:nodoc:
203
+ x = -8218111829489689 * 2.0 **(-67)
204
+ y = -8218111833737307 * 2.0 **(-67)
205
+ assert_equal(y, FPU.sinh_up(x))
206
+ z = Math::sinh(x);
207
+ assert(z <= y, [FPU.split(x), FPU.split(z), FPU.split(y)].inspect)
208
+ end
209
+
210
+ end end
@@ -0,0 +1,645 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Tests for the class Interval.
4
+ #
5
+ # Copyright (c) 2006 Stefano Taschini <taschini@ieee.org>.
6
+ # Licensed under the terms of LGPL: http://www.gnu.org/copyleft/lesser.html
7
+
8
+ # To control the FPU's rounding mode
9
+ require File.dirname(__FILE__) + '/../lib/interval' if __FILE__ == $0
10
+ require 'test/unit'
11
+
12
+ # Assertions to be used when testing intervals.
13
+ module Interval::Assertions
14
+
15
+ def assert_sharp(x)
16
+ assert(x.sharp?, x.inspect + " should be sharp.")
17
+ assert((-x).sharp?, (-x).inspect + " should be sharp.")
18
+ end
19
+
20
+ # 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.")
24
+ end
25
+
26
+ # Assert that +s+ is a solution to +f+ == 0. Unless the +weak+ flag is set,
27
+ # assert also that +s+ == +expected+.
28
+ # If the +weak+ flag is set, assert that +s+ includes +expected+.
29
+ def assert_solution(s,f,expected, weak = nil)
30
+ res = s.map{|c| f.call(c)}.inject{|a,c| a|c}
31
+ # Did something spectacarly unexpected happen?
32
+ assert(res.simple?, res)
33
+ # Is it a solution?
34
+ assert_include(res, 0)
35
+ # Is it minimal?
36
+ assert(s.all? {|x| x.extrema.all? {|y| f.call(Interval[y]).include?(0)}}, s.inspect +
37
+ " is non minmal")
38
+ # Is it the expected solution?
39
+ if weak
40
+ # check the granularity
41
+ assert_equal(expected.components.size, s.components.size, [expected,s].inspect)
42
+ # check for inclusion
43
+ assert_subset(expected, s)
44
+ else
45
+ # check for equality
46
+ assert_equal(expected,s)
47
+ end
48
+ end
49
+
50
+ # Assert that the interval +i+ includes +x+.
51
+ def assert_include(i, x)
52
+ assert(i.include?(x), i.inspect + " should include " + x.inspect)
53
+ end
54
+
55
+ # 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
+ end
59
+
60
+ # 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)
63
+ end
64
+
65
+ end
66
+
67
+ # Tests Interval fundamentals: construction, equality and inspection.
68
+ class Interval::TestFundamentals < Test::Unit::TestCase
69
+
70
+ def test_constructor_and_equality
71
+ tester = proc {|i,ex,klass|
72
+ assert_equal(klass, i.class)
73
+ assert_equal(ex, i.construction)
74
+ assert_equal(i, Interval[*i.construction])
75
+ }
76
+
77
+ tester.call Interval[1], [1], Interval::Simple
78
+ tester.call Interval[1,2], [1,2], Interval::Simple
79
+ tester.call Interval[[1,2],[3,4]], [[1,2],[3,4]], Interval::Multiple
80
+ tester.call Interval[[1],[2]], [[1],[2]], Interval::Multiple
81
+ tester.call Interval[[1]], [1], Interval::Simple
82
+ tester.call Interval[2,0/0.0], [-Infinity,+Infinity], Interval::Simple
83
+ tester.call Interval[0/0.0,9], [-Infinity,+Infinity], Interval::Simple
84
+ tester.call Interval[[1,3],[4,6],[2,5],[9,9]], [[1,6],[9]], Interval::Multiple
85
+ end
86
+
87
+ 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'] }
93
+ end
94
+
95
+ def test_inequality
96
+ assert_not_equal(Interval[1,2], Interval[1,3])
97
+ assert_not_equal(Interval[1,2], Interval[0,2])
98
+ assert_not_equal(Interval[1,2], Interval[0,3])
99
+ assert_not_equal(Interval[1,2], 3)
100
+ assert_not_equal(Interval[[1,2],[3,4]], Interval[[1,0],[3,4]])
101
+ end
102
+
103
+ def test_inspect_and_to_s
104
+ tester = proc {|s|
105
+ i = eval(s)
106
+ assert_equal(s,i.inspect)
107
+ assert_equal(s,i.to_s)
108
+ }
109
+ tester.call "Interval[1]"
110
+ tester.call "Interval[1, 2]"
111
+ tester.call "Interval[[1, 2], [3, 4]]"
112
+ tester.call "Interval[[1, 2], [4], [5, 6], [Infinity]]"
113
+ tester.call "Interval[1, 2]"
114
+ end
115
+
116
+ end
117
+
118
+ # Tests the arithmetic of Interval::Simple.
119
+ class Interval::TestSimpleArithmetic < Test::Unit::TestCase
120
+
121
+ def test_plus
122
+ assert_equal(Interval[-Infinity,+Infinity], Interval[-Infinity] + Interval[Infinity])
123
+ assert_equal(Interval[4,6], Interval[1,2] + Interval[3,4])
124
+ assert_equal(Interval[3,Infinity], Interval[1,Infinity]+Interval[2])
125
+ assert_equal(Interval[-Infinity,+Infinity],Interval[-Infinity,-1] + Interval[2,+Infinity])
126
+ assert_equal(Interval[-Infinity,+Infinity],Interval[-Infinity] + Interval[8,+Infinity])
127
+ end
128
+
129
+ def test_minus
130
+ assert_equal(Interval[-2,-1], -Interval[1,2])
131
+ end
132
+
133
+ def test_dsub
134
+ assert_equal(Interval[2], Interval[3,4].dsub(Interval[1,2]))
135
+ assert_equal(Interval[3,4], Interval[4,6].dsub(Interval[1,2]))
136
+ assert_equal(
137
+ Interval[],
138
+ Interval.union(Interval[3,4].dsub(Interval[0,2])))
139
+ end
140
+
141
+ def test_times
142
+ assert_equal(Interval[-Infinity,+Infinity],Interval[Infinity] * Interval[0])
143
+ assert_equal(Interval[+Infinity],Interval[Infinity] * Interval[3])
144
+ assert_equal(Interval[-8,+10], Interval[1,2] * Interval[-4,5])
145
+ assert_equal(Interval[3,8], Interval[1,2] * Interval[3,4])
146
+ assert_equal(Interval[-Infinity,+Infinity],Interval[0,1] * Interval[2,Infinity])
147
+ assert_equal(Interval[2, Infinity], Interval[-Infinity,-2] * Interval[-Infinity,-1])
148
+ end
149
+
150
+ def test_inverse
151
+ assert_equal(Interval[0.5,1],Interval[1,2].inverse)
152
+ assert_equal(Interval[-1,-0.5],(-Interval[1,2]).inverse)
153
+ assert_equal(Interval[[-Infinity,-1],[0.5,+Infinity]],Interval[-1,2].inverse)
154
+ assert_equal(Interval[[-Infinity],[1,+Infinity]],Interval[0,1].inverse)
155
+ end
156
+
157
+ def test_power
158
+ assert_equal((-Interval[1,2]).inverse, (-Interval[1,2]) ** -1)
159
+ assert_equal(Interval[0,4], Interval[-1,2]**2)
160
+ assert_equal(Interval[-27,8], Interval[-3,2]**3)
161
+
162
+ assert_equal(
163
+ Interval[FPU.down{(1/3.0)*(1/3.0)},FPU.up{(1/3.0)*(1/3.0)}],
164
+ (Interval[1]/3.0) ** 2)
165
+
166
+ assert_equal(
167
+ Interval[
168
+ FPU.down{(1/3.0)*(1/3.0)*(1/3.0)},
169
+ FPU.up{(1/3.0)*(1/3.0)*(1/3.0)}],
170
+ (Interval[1]/3.0) ** 3)
171
+ end
172
+
173
+ end
174
+
175
+ # Tests other methods of Interval::Simple.
176
+ class Interval::TestSimpleMethods < Test::Unit::TestCase
177
+
178
+ def test_midpoint
179
+ assert_equal(1.5,Interval[1,2].midpoint)
180
+ assert_raise(NoMethodError){ Interval[[1,2],[3,4]].midpoint }
181
+ end
182
+
183
+ def test_width
184
+ assert_equal(1, Interval[1,2].width)
185
+ assert_equal(1, Interval[1,2].width)
186
+ tester = proc{|x|
187
+ assert_equal(
188
+ 2.0 ** (-(Math.log(x)/Math.log(2)).ceil - 52),
189
+ (1/Interval[x]).width)
190
+ }
191
+ tester.call(3.0)
192
+ tester.call(5.0)
193
+ tester.call(119.0)
194
+ tester.call(34e-4)
195
+ end
196
+
197
+ end
198
+
199
+ # Tests Interval algebra.
200
+ class Interval::TestAlgebra < Test::Unit::TestCase
201
+
202
+ def test_union
203
+ assert_equal(Interval[[1, 6], [9, 9]], Interval[[1, 3],[4, 6]] | Interval[[2, 5], [9,9]])
204
+ end
205
+
206
+ def test_plus
207
+ tester = proc{|x,y,sum|
208
+ assert_equal(sum,x+y)
209
+ assert_equal(sum,y+x)
210
+ }
211
+ tester.call(Interval[[1,2],[10,Infinity]], Interval[[1,9],[-2,-1]], Interval[[-1,1],[2,Infinity]])
212
+ tester.call(Interval[1,9], Interval[[1,2],[10,Infinity]], Interval[2,Infinity])
213
+ tester.call(Interval[1,2], 2, Interval[3,4])
214
+ end
215
+
216
+ def test_intersection
217
+ tester = proc{|x,y,z|
218
+ assert_equal(z, x & y)
219
+ assert_equal(z, y & x)
220
+ }
221
+ tester.call(Interval[1,2], Interval[0,3], Interval[1,2])
222
+ tester.call(Interval[1.1,1.9], Interval[1.3,2.5], Interval[1.3, 1.9])
223
+ tester.call(Interval[1.1,1.9], Interval[0.3,0.7], Interval[])
224
+ end
225
+
226
+ def test_inverse
227
+ assert_equal(Interval[[-Infinity, -2.0],[0.0, Infinity]],Interval[[-0.5,0.5],[0.2,Infinity]].inverse)
228
+ end
229
+
230
+ def test_minus
231
+ assert_equal(Interval[[-4,-3],[-2,-1]], -Interval[[1,2],[3,4]])
232
+ end
233
+
234
+ def test_times
235
+ tester = proc{|x,y,z|
236
+ assert_equal(z,x*y)
237
+ assert_equal(z,y*x)
238
+ }
239
+ tester.call(Interval[[1, 2], [3, 4]], Interval[0.5, 2], Interval[0.5,8])
240
+ tester.call(Interval[1,2], 2, Interval[2,4])
241
+ end
242
+
243
+ def test_division
244
+ assert_equal(Interval[-Infinity, Infinity], Interval[0,1]/Interval[0,1])
245
+ assert_equal(Interval[0.5], Interval[1]/2)
246
+ end
247
+
248
+ def test_power
249
+ assert_equal(Interval[-1,2], (Interval[-1,2]**-1)**-1)
250
+ end
251
+
252
+ end
253
+
254
+ #Tests other Interval methods.
255
+ class Interval::TestMethods < Test::Unit::TestCase
256
+ include Interval::Assertions
257
+
258
+ def test_include?
259
+ assert_include(Interval[1,2], 1.5)
260
+ assert_include(Interval[1,2], 1)
261
+ assert_include(Interval[1,2], 2)
262
+ assert_outside(Interval[1,2], 0)
263
+ assert_outside(Interval[1,2], 4)
264
+ end
265
+
266
+ def test_simple?
267
+ assert(Interval[1,2].simple?)
268
+ assert(!Interval[[1,2],[3,4]].simple?)
269
+ end
270
+
271
+ def test_empty?
272
+ assert(!Interval[1,2].empty?)
273
+ assert(!Interval[[1,2],[4,3]].empty?)
274
+ assert(Interval[].empty?)
275
+ end
276
+
277
+ def test_sharp?
278
+ assert_sharp(Interval[5,5])
279
+ assert_sharp(Interval[[1],[2]])
280
+ assert_fuzzy(Interval[[1],[2,3]])
281
+ assert_fuzzy(Interval[5,6])
282
+ assert_fuzzy(Interval[1.1,1.2])
283
+ assert_fuzzy(Interval[Struct::Float[-1,0,1].to_f,Struct::Float[+1,0,1].to_f])
284
+ assert_sharp(Interval[Struct::Float[1,1022,2**52-1].to_f,1])
285
+ assert_sharp(Interval[Struct::Float[1,1029,2**52-1].to_f,128])
286
+ assert_sharp(Interval[-0.0,0.0])
287
+ assert_sharp(Interval[0.0,Struct::Float[1,0,1].to_f])
288
+ assert_sharp(Interval[6369051672525772, 6369051672525773] * 2.0 ** -52)
289
+ assert_sharp(Interval[6369051672525772, 6369051672525772] * 2.0 ** -52)
290
+ assert_fuzzy(Interval[6369051672525771, 6369051672525773] * 2.0 ** -52)
291
+ assert_fuzzy(Interval[6369051672525771, 6369051672525785] * 2.0 ** -52)
292
+ end
293
+
294
+ def test_hull
295
+ assert_equal(Interval[1,4], Interval[[1,2],[3,4]].hull)
296
+ assert_equal(Interval[1,4], Interval[1,4].hull)
297
+ assert_equal(Interval[], Interval[].hull)
298
+ end
299
+
300
+ def test_conversion_from_range
301
+ assert_equal(Interval[1.2, 5.3], (1.2 .. 5.3).to_interval)
302
+ assert_equal(Interval[1, 4], (1 .. 4).to_interval)
303
+ assert_equal(Interval[1, 3], (1 ... 4).to_interval)
304
+ assert_raise(Interval::ConstructionError){ (1 ... 3.1).to_interval }
305
+ if defined?(Rational)
306
+ x = Rational(1,3)
307
+ assert_equal(Interval[1, x], (1 .. x).to_interval)
308
+ assert_raise(Interval::ConstructionError){ (1 ... x).to_interval }
309
+ end
310
+ end
311
+
312
+ end
313
+
314
+ # Tests the Interval-based non-linear solver.
315
+ class Interval::TestNewton < Test::Unit::TestCase
316
+ include Interval::Assertions
317
+
318
+ def test_sqrt2
319
+ f, fp = proc {|x| x**2 - 2}, proc {|x| 2*x}
320
+ s = Math::sqrt(2)
321
+ assert_equal(6369051672525773 * 2.0 **-52, s)
322
+ assert_equal(Interval[s], Interval[0.1,5.0].newton(f, fp))
323
+ assert_equal(Interval[], Interval[2.0,5.0].newton(f,fp))
324
+ assert_equal(Interval[s], Interval[-1.0,10.0].newton(f,fp))
325
+ assert_equal(Interval[-s], Interval[-5.0,0].newton(f,fp))
326
+ assert_equal(Interval[[-s],[s]], Interval[-5.0,5.0].newton(f,fp))
327
+ end
328
+
329
+ def test_cubic_1
330
+ f, fp = proc {|x| x**3 + x - 10}, proc {|x| 3*x**2 + 1}
331
+ s = Interval[-1,5].newton(f,fp)
332
+ assert_solution(s, f, Interval[2])
333
+ end
334
+
335
+ def test_cubic_2
336
+ f, fp = proc {|x| x**3 + x - 15}, proc {|x| 3*x**2 + 1}
337
+ s = Interval[-1.0,5.0].newton(f,fp)
338
+ assert_solution(s, f, Interval[5249383869325654 * 2.0 ** -51])
339
+ end
340
+
341
+ def test_cubic_3
342
+ f, fp = proc {|x| x * (x ** 2 - 1)}, proc {|x| 3*x**2 - 1}
343
+ s = Interval[-5.0,5.0].newton(f,fp)
344
+ assert_solution(s, f, Interval[[-1],[0],[1]])
345
+ end
346
+
347
+ def test_dennis_schnabel
348
+ f1 = proc {|x| (((x-12) * x + 47 ) * x - 60 )* x}
349
+ f2 = proc {|x| f1.call(x) + 24 }
350
+ f3 = proc {|x| f1.call(x) + 24.1 }
351
+ fp = proc {|x| ((4 * x - 12*3) * x + 47*2) * x - 60}
352
+
353
+ assert_solution(Interval[-1e2,1e2].newton(f1,fp), f1, Interval[[0],[3],[4],[5]],:weak)
354
+ assert_solution(Interval[-1e2,1e2].newton(f2,fp), f2, Interval[[0.888305779071752],[1]], :weak)
355
+ assert_equal(Interval[], Interval[-1e2,1e2].newton(f3,fp))
356
+ end
357
+
358
+ if FPU.respond_to?(:exp_up)
359
+ def test_exp
360
+ f, fp = proc{|x| x.exp + x}, proc {|x| x.exp + 1}
361
+ z0 = Interval[-1e2,1e2].newton(f,fp)
362
+ assert_solution(z0, f, Interval[-0.56714329040978384])
363
+
364
+ f, fp = proc{|x| x*(-x).exp + 1}, proc {|x| (1-x)*(-x).exp}
365
+ z1 = Interval[-1e2,1e2].newton(f,fp)
366
+ assert_solution(z0, f, z0, :weak)
367
+ assert_sharp(z1)
368
+ end end
369
+
370
+ if FPU.respond_to?(:exp_up)
371
+ def test_trig
372
+ f = proc{|x| (x*Interval::PI/3.0).cos() -0.5}
373
+ fp = proc{|x| -(x*Interval::PI/3).sin() * Interval::PI/3}
374
+ s = Interval[-10.0,10.0].newton(f, fp)
375
+ assert_solution(s, f, Interval[[-7], [-5], [-1], [1], [5], [7]], :weak)
376
+ end end
377
+
378
+ end
379
+
380
+ if FPU.respond_to?(:exp_up)
381
+ # Tests Interval transcendental functions.
382
+ class Interval::TestTranscendental < Test::Unit::TestCase
383
+ include Interval::Assertions
384
+
385
+ def test_exp
386
+ assert_equal(Interval::E, Interval[1].exp)
387
+ assert_equal(Interval[1,Interval::E.sup], Interval[0,1].exp)
388
+ assert_equal(Interval[Interval::E.inf, Infinity], Interval[1,Infinity].exp)
389
+ assert_equal(Interval[0], Interval[-Infinity].exp)
390
+ assert_equal(Interval[[0, 1], [Math::E, Infinity]], Interval[[-Infinity,0],[1,Infinity]].exp)
391
+ end
392
+
393
+ def test_log
394
+ assert_equal(Interval[-Infinity, 0], Interval[-1,+1].log)
395
+ assert_equal(Interval[0], Interval[1].log)
396
+ assert_equal(Interval[], Interval[-2,-1].log)
397
+ assert_equal(Interval[-Infinity], Interval[-2,0].log)
398
+ assert_equal(Interval[-Infinity,0], Interval[0,1].log)
399
+ assert_include(Interval::E.log, 1)
400
+ assert_equal(3 * 2 ** -53, Interval[1].exp.log.width)
401
+ end
402
+
403
+ def test_atan
404
+ assert_equal(Interval::PI, 4 * Interval[1].atan)
405
+ assert_equal(Interval[-Interval::PI.sup, Interval::PI.sup], 2 * Interval[-Infinity,Infinity].atan)
406
+ assert_equal(Interval[0], Interval[0].atan)
407
+ end
408
+
409
+ def test_cosh
410
+ assert_sharp(Interval[1].cosh)
411
+ assert_equal(Interval[1], Interval[0].cosh)
412
+ assert_equal((Interval[1].cosh | Interval[3].cosh).hull, Interval[1,3].cosh)
413
+ assert_equal(Interval[1,3].cosh, Interval[-3,-1].cosh)
414
+ assert_equal((Interval[0] | Interval[3].cosh).hull, Interval[-1,3].cosh)
415
+ assert_equal(Interval[-1,3].cosh, Interval[-3,1].cosh)
416
+ assert_equal(Interval[0, Infinity], Interval[-Infinity,Infinity].cosh)
417
+ end
418
+
419
+ def test_sinh
420
+ assert_sharp(Interval[1].sinh)
421
+ assert_equal(Interval[0], Interval[0].sinh)
422
+ assert_equal((Interval[-1].sinh | Interval[3].sinh).hull, Interval[-1,3].sinh)
423
+ assert_equal(Interval[-Infinity, Infinity], Interval[-Infinity,Infinity].sinh)
424
+ end
425
+
426
+ def test_cos
427
+ assert_equal(Interval[-1,-1 + 2 ** -53], Interval::PI.cos)
428
+ assert_equal(Interval[1 - 2 ** -53, 1], (2*Interval::PI).cos)
429
+ assert_equal(Interval[-1,1], Interval[Infinity].cos)
430
+ assert_equal(Interval[-1,1], Interval[-Infinity].cos)
431
+ onehalf = (Interval::PI/3).cos
432
+ assert_include(onehalf, 0.5)
433
+ assert_equal(Interval[onehalf.inf,1], (-Interval::PI/4 | Interval::PI/3).hull.cos)
434
+ minusonehalf = (2*Interval::PI/3).cos
435
+ assert_include(minusonehalf, -0.5)
436
+ assert_equal(Interval[-1,minusonehalf.sup], (5*Interval::PI/4 | 2*Interval::PI/3).hull.cos)
437
+ assert_equal((minusonehalf | onehalf).hull, ((Interval::PI | 2*Interval::PI).hull/3).cos)
438
+ full = (5 * Interval::PI/3 | 3.5 * Interval::PI).hull
439
+ assert_equal(Interval[-1,1], full.cos)
440
+ assert_equal(Interval[-1,1], (full + 2 * Interval::PI).cos)
441
+ assert_equal(Interval[-1,1], (full + 4 * Interval::PI).cos)
442
+ assert_equal(Interval[-1,1], (full + 6 * Interval::PI).cos)
443
+ assert_equal(Interval[-1,1], (full + 8 * Interval::PI).cos)
444
+ assert_equal(Interval[-1,1], (full - 2 * Interval::PI).cos)
445
+ assert_equal(Interval[-1,1], (full - 4 * Interval::PI).cos)
446
+ assert_equal(Interval[-1,1], (full - 6 * Interval::PI).cos)
447
+ assert_equal(Interval[-1,1], (full - 8 * Interval::PI).cos)
448
+ end
449
+
450
+ def test_sin
451
+ assert_equal(Interval[-1,-1 + 2 ** -53], (3*Interval::PI/2).sin)
452
+ assert_equal(Interval[1 - 2 ** -53, 1], (Interval::PI/2).sin)
453
+ assert_equal(Interval[-1,1], Interval[Infinity].sin)
454
+ assert_equal(Interval[-1,1], Interval[-Infinity].sin)
455
+ onehalf = (Interval::PI/6).sin
456
+ assert_include(onehalf, 0.5)
457
+ assert_equal(Interval[onehalf.inf,1], (3 * Interval::PI/4 | Interval::PI/6).hull.sin)
458
+ minusonehalf = (7*Interval::PI/6).sin
459
+ assert_include(minusonehalf, -0.5)
460
+ assert_equal(Interval[-1,minusonehalf.sup], (7*Interval::PI/4 | 7*Interval::PI/6).hull.sin)
461
+ assert_equal((-onehalf | onehalf).hull, ((Interval::PI | -Interval::PI).hull/6.0).sin)
462
+ full = ((5 * Interval::PI/3 | 3.5 * Interval::PI) + Interval::PI/2).hull
463
+ assert_equal(Interval[-1,1], full.sin)
464
+ assert_equal(Interval[-1,1], (full + 2 * Interval::PI).cos)
465
+ assert_equal(Interval[-1,1], (full + 4 * Interval::PI).cos)
466
+ assert_equal(Interval[-1,1], (full + 6 * Interval::PI).cos)
467
+ assert_equal(Interval[-1,1], (full + 8 * Interval::PI).cos)
468
+ assert_equal(Interval[-1,1], (full - 2 * Interval::PI).cos)
469
+ assert_equal(Interval[-1,1], (full - 4 * Interval::PI).cos)
470
+ assert_equal(Interval[-1,1], (full - 6 * Interval::PI).cos)
471
+ assert_equal(Interval[-1,1], (full - 8 * Interval::PI).cos)
472
+ end
473
+
474
+ def test_tan
475
+ assert_equal(Interval[0], Interval[0].tan)
476
+ assert_equal(Interval[], Interval[].tan)
477
+ assert_equal(Interval[-Infinity, Infinity], Interval[0,6].tan)
478
+ assert_equal(Interval[-Infinity, Infinity], (-Interval::PI/2 | Interval::PI/2).hull.tan)
479
+ assert_equal(Interval[-Infinity, Infinity], (- Interval::PI/4 | Interval::PI * 1.25).hull.tan)
480
+ assert_equal(Interval[-Infinity, Infinity], (4.75 *Interval::PI | Interval::PI * 6.25).hull.tan)
481
+
482
+ assert_equal(2 ** -51, Interval::PI.tan.width)
483
+ assert_include(Interval::PI.tan, 0)
484
+ assert_equal(3 * 2 ** -53, (Interval::PI/4).tan.width)
485
+ assert_include((Interval::PI/4).tan, 1)
486
+
487
+ x = (Interval::PI/4 | Interval::PI * 0.75).hull
488
+ assert_equal((Interval[x.sup].tan | -Infinity).hull | (Interval[x.inf].tan | Infinity).hull, x.tan)
489
+
490
+ x = (Interval::PI | -Interval::PI).hull/4
491
+ assert_equal((Interval[x.inf].tan | Interval[x.sup].tan).hull, x.tan)
492
+ end
493
+
494
+ end end
495
+
496
+ # Tests Kahan's summation formula
497
+ class Interval::KahanTest < Test::Unit::TestCase
498
+ include Interval::Assertions
499
+
500
+ # Generate a random data vector
501
+ #
502
+ # data = Kahan_Test.gen_data(100)
503
+ #
504
+ def gen_data(n)
505
+ (0...n).map {rand - 0.5}
506
+ end
507
+
508
+ class PoorMansRational < Numeric
509
+ attr :num
510
+ attr :den
511
+
512
+ def initialize(n, d)
513
+ if d < 0
514
+ n, d = -n, -d
515
+ end
516
+ b, a = [n.abs,d].sort
517
+ while b > 0
518
+ a, b = b, a % b
519
+ end
520
+ @num = n.div(a)
521
+ @den = d.div(a)
522
+ end
523
+
524
+ def + (other)
525
+ case other
526
+ when self.class
527
+ self.class.new(num * other.den + den * other.num, den * other.den)
528
+ when Integer
529
+ self.class.new(num + den * other, den)
530
+ else
531
+ x, y = other.coerce(self)
532
+ x + y
533
+ end
534
+ end
535
+
536
+ def coerce(other)
537
+ case other
538
+ when self.class
539
+ return other, self
540
+ when Integer
541
+ return self.class.new(other, 1), self
542
+ else
543
+ super
544
+ end
545
+ end
546
+
547
+ def to_f
548
+ @num.to_f/@den.to_f
549
+ end
550
+
551
+ end
552
+
553
+ unless defined?(Rational)
554
+ def Rational(n, d = 1)
555
+ PoorMansRational.new(n,d)
556
+ end
557
+ end
558
+
559
+ # Convert to a rational number with no precision loss
560
+ #
561
+ # to_r(1.1).to_f == 1.1 # => true
562
+ #
563
+ def to_r(x)
564
+ if !x.finite?
565
+ self
566
+ elsif x.zero?
567
+ 0
568
+ else
569
+ exp = [Math.frexp(x).last - 53, -1074].max
570
+ sig = (x / (2.0 ** exp)).to_i
571
+ if exp >= 0
572
+ Rational(sig * 2 ** exp)
573
+ else
574
+ Rational(sig, 2 ** (-exp))
575
+ end
576
+ end
577
+ end
578
+
579
+ def assert_consistent_sum(raw, strict = nil)
580
+ # The exact sum, with rationals
581
+ exact = raw.inject(0){|a,x| a+to_r(x)}.to_f
582
+
583
+ # Summing them as floats
584
+ rawsum = raw.inject{|a,x| a+x}
585
+
586
+ # Data as a collection of intervals
587
+ data = raw.map{|x| Interval[x]}
588
+
589
+ # Kahan's sum
590
+ kahan = Interval.sum(*data)
591
+
592
+ # Naive sum
593
+ naive = data.inject{|a,x| a+x}
594
+
595
+ # Kahan must include the exact sum
596
+ assert_include(kahan, exact)
597
+
598
+ # In some cases the raw sum is outside Kahan's result
599
+ assert_outside(kahan, rawsum) if strict
600
+
601
+ # Kahan's result must be sharper than the naive sum
602
+ assert_subset(kahan, naive)
603
+
604
+ #puts [rawsum, exact, kahan.width, naive.width, naive.width/kahan.width].
605
+ # inspect if @@long && ! kahan.include?(rawsum)
606
+ end
607
+
608
+ def test_basic_consistency
609
+ assert_equal(Interval[3], Interval.sum(Interval[1], Interval[2]))
610
+ assert_equal(Interval[[3],[6]], Interval.sum(Interval[[1],[4]], Interval[2]))
611
+ assert_equal(Interval[-2,14],Interval.sum(Interval[[1,9],[-2,-1]], Interval[[0,3],[1,5]]))
612
+ assert_equal(Interval[[-22, -20], [-19, -10], [-2, 0], [1, 10]],
613
+ Interval.sum(Interval[[1,9],[-2,-1]], Interval[[0,1],[-20,-19]]))
614
+ end
615
+
616
+ @@long = ARGV.include?("long")
617
+
618
+ def test_large_data
619
+ assert_consistent_sum [0.694758884739873,
620
+ 0.604772180759511,
621
+ 0.796647997261382,
622
+ 0.102320271311194,
623
+ 0.87286348648478,
624
+ 0.175213643903808,
625
+ 0.82310391214613,
626
+ 0.198147944349148,
627
+ 0.24650422376045,
628
+ 0.471233184991935].map{|x| x-0.5}, :strict
629
+ srand 12345
630
+ assert_consistent_sum gen_data(200), :strict
631
+ if @@long
632
+ assert_consistent_sum gen_data(2000), :strict
633
+ assert_consistent_sum gen_data(100)
634
+ assert_consistent_sum gen_data(100)
635
+ assert_consistent_sum gen_data(100)
636
+ assert_consistent_sum gen_data(100)
637
+ assert_consistent_sum gen_data(100)
638
+ assert_consistent_sum gen_data(100)
639
+ assert_consistent_sum gen_data(100)
640
+ assert_consistent_sum gen_data(1000)
641
+ assert_consistent_sum gen_data(10000)
642
+ end
643
+ end
644
+
645
+ end