intervals 0.3.63 → 0.4.75

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