intervals 0.3.56
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/VERSION.txt +1 -0
- data/ext/crlibm/AUTHORS +2 -0
- data/ext/crlibm/COPYING +504 -0
- data/ext/crlibm/ChangeLog +80 -0
- data/ext/crlibm/INSTALL +182 -0
- data/ext/crlibm/Makefile.am +84 -0
- data/ext/crlibm/Makefile.in +530 -0
- data/ext/crlibm/NEWS +0 -0
- data/ext/crlibm/README +31 -0
- data/ext/crlibm/TODO +47 -0
- data/ext/crlibm/VERSION +1 -0
- data/ext/crlibm/aclocal.m4 +989 -0
- data/ext/crlibm/atan-itanium.c +846 -0
- data/ext/crlibm/atan-pentium.c +261 -0
- data/ext/crlibm/atan_accurate.c +244 -0
- data/ext/crlibm/atan_accurate.h +191 -0
- data/ext/crlibm/atan_fast.c +324 -0
- data/ext/crlibm/atan_fast.h +678 -0
- data/ext/crlibm/config.guess +1461 -0
- data/ext/crlibm/config.sub +1566 -0
- data/ext/crlibm/configure +7517 -0
- data/ext/crlibm/configure.ac +364 -0
- data/ext/crlibm/crlibm.h +125 -0
- data/ext/crlibm/crlibm_config.h +149 -0
- data/ext/crlibm/crlibm_config.h.in +148 -0
- data/ext/crlibm/crlibm_private.c +293 -0
- data/ext/crlibm/crlibm_private.h +658 -0
- data/ext/crlibm/csh_fast.c +631 -0
- data/ext/crlibm/csh_fast.h +771 -0
- data/ext/crlibm/double-extended.h +496 -0
- data/ext/crlibm/exp-td.c +962 -0
- data/ext/crlibm/exp-td.h +685 -0
- data/ext/crlibm/exp_accurate.c +197 -0
- data/ext/crlibm/exp_accurate.h +85 -0
- data/ext/crlibm/gappa/log-de-E0-logir0.gappa +106 -0
- data/ext/crlibm/gappa/log-de-E0.gappa +79 -0
- data/ext/crlibm/gappa/log-de.gappa +81 -0
- data/ext/crlibm/gappa/log-td-E0-logir0.gappa +126 -0
- data/ext/crlibm/gappa/log-td-E0.gappa +143 -0
- data/ext/crlibm/gappa/log-td-accurate-E0-logir0.gappa +230 -0
- data/ext/crlibm/gappa/log-td-accurate-E0.gappa +213 -0
- data/ext/crlibm/gappa/log-td-accurate.gappa +217 -0
- data/ext/crlibm/gappa/log-td.gappa +156 -0
- data/ext/crlibm/gappa/trigoSinCosCase3.gappa +204 -0
- data/ext/crlibm/gappa/trigoTanCase2.gappa +73 -0
- data/ext/crlibm/install-sh +269 -0
- data/ext/crlibm/log-de.c +431 -0
- data/ext/crlibm/log-de.h +732 -0
- data/ext/crlibm/log-td.c +852 -0
- data/ext/crlibm/log-td.h +819 -0
- data/ext/crlibm/log10-td.c +906 -0
- data/ext/crlibm/log10-td.h +823 -0
- data/ext/crlibm/log2-td.c +935 -0
- data/ext/crlibm/log2-td.h +821 -0
- data/ext/crlibm/maple/atan.mpl +359 -0
- data/ext/crlibm/maple/common-procedures.mpl +997 -0
- data/ext/crlibm/maple/csh.mpl +446 -0
- data/ext/crlibm/maple/double-extended.mpl +151 -0
- data/ext/crlibm/maple/exp-td.mpl +195 -0
- data/ext/crlibm/maple/log-de.mpl +243 -0
- data/ext/crlibm/maple/log-td.mpl +316 -0
- data/ext/crlibm/maple/log10-td.mpl +345 -0
- data/ext/crlibm/maple/log2-td.mpl +334 -0
- data/ext/crlibm/maple/trigo.mpl +728 -0
- data/ext/crlibm/maple/triple-double.mpl +58 -0
- data/ext/crlibm/missing +198 -0
- data/ext/crlibm/mkinstalldirs +40 -0
- data/ext/crlibm/rem_pio2_accurate.c +219 -0
- data/ext/crlibm/rem_pio2_accurate.h +53 -0
- data/ext/crlibm/scs_lib/AUTHORS +3 -0
- data/ext/crlibm/scs_lib/COPYING +504 -0
- data/ext/crlibm/scs_lib/ChangeLog +16 -0
- data/ext/crlibm/scs_lib/INSTALL +215 -0
- data/ext/crlibm/scs_lib/Makefile.am +18 -0
- data/ext/crlibm/scs_lib/Makefile.in +328 -0
- data/ext/crlibm/scs_lib/NEWS +0 -0
- data/ext/crlibm/scs_lib/README +9 -0
- data/ext/crlibm/scs_lib/TODO +4 -0
- data/ext/crlibm/scs_lib/addition_scs.c +623 -0
- data/ext/crlibm/scs_lib/config.guess +1461 -0
- data/ext/crlibm/scs_lib/config.sub +1566 -0
- data/ext/crlibm/scs_lib/configure +6226 -0
- data/ext/crlibm/scs_lib/division_scs.c +110 -0
- data/ext/crlibm/scs_lib/double2scs.c +174 -0
- data/ext/crlibm/scs_lib/install-sh +269 -0
- data/ext/crlibm/scs_lib/missing +198 -0
- data/ext/crlibm/scs_lib/mkinstalldirs +40 -0
- data/ext/crlibm/scs_lib/multiplication_scs.c +456 -0
- data/ext/crlibm/scs_lib/poly_fct.c +112 -0
- data/ext/crlibm/scs_lib/print_scs.c +73 -0
- data/ext/crlibm/scs_lib/rand_scs.c +63 -0
- data/ext/crlibm/scs_lib/scs.h +353 -0
- data/ext/crlibm/scs_lib/scs2double.c +391 -0
- data/ext/crlibm/scs_lib/scs2mpf.c +58 -0
- data/ext/crlibm/scs_lib/scs2mpfr.c +61 -0
- data/ext/crlibm/scs_lib/scs_private.c +23 -0
- data/ext/crlibm/scs_lib/scs_private.h +133 -0
- data/ext/crlibm/scs_lib/tests/tbx_timing.h +102 -0
- data/ext/crlibm/scs_lib/wrapper_scs.h +486 -0
- data/ext/crlibm/scs_lib/zero_scs.c +52 -0
- data/ext/crlibm/stamp-h.in +1 -0
- data/ext/crlibm/tests/Makefile.am +43 -0
- data/ext/crlibm/tests/Makefile.in +396 -0
- data/ext/crlibm/tests/blind_test.c +148 -0
- data/ext/crlibm/tests/generate_test_vectors.c +258 -0
- data/ext/crlibm/tests/soak_test.c +334 -0
- data/ext/crlibm/tests/test_common.c +627 -0
- data/ext/crlibm/tests/test_common.h +28 -0
- data/ext/crlibm/tests/test_perf.c +570 -0
- data/ext/crlibm/tests/test_val.c +249 -0
- data/ext/crlibm/trigo_accurate.c +500 -0
- data/ext/crlibm/trigo_accurate.h +331 -0
- data/ext/crlibm/trigo_fast.c +1219 -0
- data/ext/crlibm/trigo_fast.h +639 -0
- data/ext/crlibm/triple-double.h +878 -0
- data/ext/extconf.rb +31 -0
- data/ext/fpu.c +107 -0
- data/ext/jamis-mod.rb +591 -0
- data/lib/fpu.rb +287 -0
- data/lib/interval.rb +1170 -0
- data/lib/intervals.rb +212 -0
- data/lib/struct_float.rb +133 -0
- data/test/data_atan.txt +360 -0
- data/test/data_cos.txt +346 -0
- data/test/data_cosh.txt +3322 -0
- data/test/data_exp.txt +3322 -0
- data/test/data_log.txt +141 -0
- data/test/data_sin.txt +140 -0
- data/test/data_sinh.txt +3322 -0
- data/test/data_tan.txt +342 -0
- metadata +186 -0
data/lib/interval.rb
ADDED
@@ -0,0 +1,1170 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Definitions 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 "fpu"
|
10
|
+
|
11
|
+
class Numeric
|
12
|
+
#
|
13
|
+
# Define a degenerate interval from a single value.
|
14
|
+
#
|
15
|
+
# 3.to_interval # => Interval[3]
|
16
|
+
#
|
17
|
+
def to_interval
|
18
|
+
Interval[self]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# A class representing the finite union of closed mathematical intervals
|
23
|
+
# in the extended real set.
|
24
|
+
#
|
25
|
+
# The real set is extended with +Infinity and -Infinity, and the intervals
|
26
|
+
# are closed, i.e. they include their extrema.
|
27
|
+
class Interval
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
# Create a closed (multi-)interval with the given numerical extrema.
|
31
|
+
#
|
32
|
+
# Interval[3] # In mathematical notation: [3,3]
|
33
|
+
# Interval[1,2] # In mathematical notation: [1,2]
|
34
|
+
# Interval[[1,2],[3,4]] # Union of [1,2] and [3,4]
|
35
|
+
#
|
36
|
+
# Note that the created interval is always in canonical form:
|
37
|
+
#
|
38
|
+
# Interval[[1,3],[2,4]] # => Interval[1, 4]
|
39
|
+
#
|
40
|
+
def Interval.[](*array)
|
41
|
+
make(
|
42
|
+
if array.empty?
|
43
|
+
[]
|
44
|
+
elsif array.first.kind_of?(Numeric)
|
45
|
+
[Simple[*array]]
|
46
|
+
else
|
47
|
+
array.select{|x| !x.empty?}.map { |x| Simple[*x] }
|
48
|
+
end
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a closed (multi-)interval from a set of simple intervals.
|
53
|
+
#
|
54
|
+
# Interval.make(Interval[1,2], Interval[3,4])
|
55
|
+
# # => Interval[[1, 2], [3, 4]]
|
56
|
+
#
|
57
|
+
def Interval.make(comps)
|
58
|
+
l = []
|
59
|
+
comps.sort_by{|x| x.inf}.each{|x|
|
60
|
+
if x.sup < x.inf
|
61
|
+
# skip it
|
62
|
+
elsif l.empty? || x.inf > l.last.sup
|
63
|
+
l <<= x
|
64
|
+
elsif x.sup > l.last.sup
|
65
|
+
l[-1] = Simple.new(l.last.inf, x.sup)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
if l.size == 1
|
69
|
+
l.first
|
70
|
+
else
|
71
|
+
Interval::Complex.new(l)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a list of the parameters that can be used to reconstruct the
|
76
|
+
# interval.
|
77
|
+
#
|
78
|
+
# a = Interval[1,2] # => Interval[1, 2]
|
79
|
+
# b = a.construction # => [1, 2]
|
80
|
+
# Interval[*b] == a # => true
|
81
|
+
#
|
82
|
+
def construction
|
83
|
+
map{|x| x.extrema.uniq}
|
84
|
+
end
|
85
|
+
|
86
|
+
# True if the interval is _simple_, i.e., if it has only one
|
87
|
+
# connected component.
|
88
|
+
#
|
89
|
+
# Interval[1,2].simple? # => true
|
90
|
+
# Interval[[1,2],[3,4]].simple?
|
91
|
+
# # => false
|
92
|
+
# If simple? is true, then the object is in fact of class
|
93
|
+
# Interval::Simple.
|
94
|
+
#
|
95
|
+
# Interval[1,2].class # => Interval::Simple
|
96
|
+
# Interval[1,2].midpoint # => 1.5
|
97
|
+
# Interval[[1,2],[3,4]].midpoint
|
98
|
+
# # raises a NoMethodError exception
|
99
|
+
#
|
100
|
+
def simple?
|
101
|
+
false
|
102
|
+
end
|
103
|
+
|
104
|
+
# Overrides Object#inspect, returning a string representation of the interval.
|
105
|
+
#
|
106
|
+
# Interval[1,2].inspect #= > "Interval[1, 2]"
|
107
|
+
|
108
|
+
def inspect
|
109
|
+
"Interval" + construction.inspect
|
110
|
+
end
|
111
|
+
|
112
|
+
# Overrides Object#to_s and uses #inspect to create a string representation of the interval.
|
113
|
+
def to_s
|
114
|
+
inspect
|
115
|
+
end
|
116
|
+
|
117
|
+
# Two intervals are equal if and only if they have equal components.
|
118
|
+
#
|
119
|
+
# Interval[[1,3],[2,4]] == Interval[1,4]
|
120
|
+
# # => true
|
121
|
+
def == (other)
|
122
|
+
self.class === other && self.components == other.components
|
123
|
+
end
|
124
|
+
|
125
|
+
# True if a point _x_ belongs to the interval.
|
126
|
+
#
|
127
|
+
# Interval[[1,2],[3,4]].include?(1.5)
|
128
|
+
# # => true
|
129
|
+
# Interval[[1,2],[3,4]].include?(2.5)
|
130
|
+
# # => false
|
131
|
+
def include?(x)
|
132
|
+
any?{|i| i.include?(x)}
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return itself.
|
136
|
+
def to_interval
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
# Make _other_ into an interval.
|
141
|
+
def coerce (other)
|
142
|
+
[other.to_interval, self]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Unitary minus: applied to _I_ returns -_I_.
|
146
|
+
#
|
147
|
+
# -Interval[1,2] # => Interval[-2, -1]
|
148
|
+
#
|
149
|
+
def -@
|
150
|
+
self.class.new map{|x| -x}.reverse
|
151
|
+
end
|
152
|
+
|
153
|
+
# The inverse of the interval, _I_.+inverse+ is the same as 1/_I_.
|
154
|
+
#
|
155
|
+
# Interval[1,2].inverse # => Interval[0.5, 1.0]
|
156
|
+
# Interval[-2,4].inverse # => Interval[[-Infinity, -0.5], [0.25, Infinity]]
|
157
|
+
#
|
158
|
+
def inverse
|
159
|
+
# implemented below
|
160
|
+
end
|
161
|
+
|
162
|
+
# The exponential function.
|
163
|
+
#
|
164
|
+
# Interval[[-Infinity,0],[1,Infinity]].exp
|
165
|
+
# # => Interval[[0,1],[Math::E,Infinity]]
|
166
|
+
#
|
167
|
+
def exp
|
168
|
+
# implemented below
|
169
|
+
end
|
170
|
+
|
171
|
+
# The logarithm function.
|
172
|
+
#
|
173
|
+
# Interval[1,Math::E].log # Interval[0,1]
|
174
|
+
# Interval[-1,+1].log # Interval[-Infinity,0]
|
175
|
+
#
|
176
|
+
def log
|
177
|
+
# implemented below
|
178
|
+
end
|
179
|
+
|
180
|
+
# The arc tangent function.
|
181
|
+
#
|
182
|
+
# 4 * Interval[1].atan # => Interval::PI
|
183
|
+
#
|
184
|
+
def atan
|
185
|
+
# implemented below
|
186
|
+
end
|
187
|
+
|
188
|
+
# The cosine function.
|
189
|
+
#
|
190
|
+
# (Interval::PI/3).cos # => Interval[0.5, 0.5]
|
191
|
+
#
|
192
|
+
def cos
|
193
|
+
# implemented below
|
194
|
+
end
|
195
|
+
|
196
|
+
# The sine function.
|
197
|
+
#
|
198
|
+
# (Interval::PI/6).sin # => Interval[0.5, 0.5]
|
199
|
+
#
|
200
|
+
def sin
|
201
|
+
# implemented below
|
202
|
+
end
|
203
|
+
|
204
|
+
# The tangent function.
|
205
|
+
#
|
206
|
+
# The cosine function. # => Interval[1.0, 1.0]
|
207
|
+
#
|
208
|
+
def tan
|
209
|
+
# implemented below
|
210
|
+
end
|
211
|
+
|
212
|
+
# Hyperbolic cosine.
|
213
|
+
def cosh
|
214
|
+
# implemented below
|
215
|
+
end
|
216
|
+
|
217
|
+
# Hyperbolic sine.
|
218
|
+
def sinh
|
219
|
+
# implemented below
|
220
|
+
end
|
221
|
+
|
222
|
+
## Implement unary operations
|
223
|
+
[:inverse, :exp, :log, :atan, :cos, :sin, :tan, :cosh, :sinh].each {|op|
|
224
|
+
define_method(op) {
|
225
|
+
Interval.make(map{|x| x.send(op).components}.flatten)
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
# Integer power.
|
230
|
+
#
|
231
|
+
# Interval[-2,2]**2 # => Interval[0, 4]
|
232
|
+
#
|
233
|
+
def ** (n)
|
234
|
+
Interval.make(map{|x| (x**n).components}.flatten)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Sum of intervals.
|
238
|
+
#
|
239
|
+
# Interval[1,2] + Interval[3,4]
|
240
|
+
# # => Interval[4, 6]
|
241
|
+
# Interval[2] + Interval[2]
|
242
|
+
# # => Interval[4]
|
243
|
+
#
|
244
|
+
# Note that
|
245
|
+
#
|
246
|
+
# Interval[-Infinity] + Interval[Infinity]
|
247
|
+
# # => Interval[-Infinity, Infinity]
|
248
|
+
#
|
249
|
+
def + (other)
|
250
|
+
# implemented below
|
251
|
+
end
|
252
|
+
|
253
|
+
# Multiplication of intervals.
|
254
|
+
#
|
255
|
+
# Interval[1,2] * Intervals[3,4]
|
256
|
+
# # => Interval[3, 8]
|
257
|
+
# Interval[2] * Interval[2]
|
258
|
+
# # => Interval[4]
|
259
|
+
#
|
260
|
+
# Note that
|
261
|
+
#
|
262
|
+
# Interval[0,1] * Interval[2,Infinity]
|
263
|
+
# # => Interval[-Infinity, Infinity]
|
264
|
+
#
|
265
|
+
def * (other)
|
266
|
+
# implemented below
|
267
|
+
end
|
268
|
+
|
269
|
+
# Set intersection.
|
270
|
+
#
|
271
|
+
# Interval[1,3] & Interval[2,4]
|
272
|
+
# # => Interval[2,3]
|
273
|
+
# Interval[1,2] & Interval[3,4]
|
274
|
+
# # => Interval[]
|
275
|
+
# (Interval[1,2] & Interval[3,4]).empty?
|
276
|
+
# # => true
|
277
|
+
#
|
278
|
+
def & (other)
|
279
|
+
# implemented below
|
280
|
+
end
|
281
|
+
|
282
|
+
## Implement binary operations: Sum (+), Multiplication (*) and Intersection (&)
|
283
|
+
[[:+, :add], [:*, :multiply], [:&, :intersect]].each {|op, meth|
|
284
|
+
define_method(op) {|other|
|
285
|
+
Interval.make(other.to_interval.map {|y| self.map {|x| x.send(meth,y) } }.flatten)
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
# Subtraction: _A_ - _B_ is equivalent to _A_ + (-_B_).
|
290
|
+
def - (other)
|
291
|
+
self + (-other)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Division: _A_ / _B_ is equivalent to _A_ * (_B_ ** -1).
|
295
|
+
def / (other)
|
296
|
+
self * other.to_interval.inverse
|
297
|
+
end
|
298
|
+
|
299
|
+
# Set union.
|
300
|
+
#
|
301
|
+
# Interval[1,3] | Interval[2,4]
|
302
|
+
# # => Interval[1, 4]
|
303
|
+
#
|
304
|
+
def | (other)
|
305
|
+
Interval.make(other.to_interval.components + self.components)
|
306
|
+
end
|
307
|
+
|
308
|
+
# True if the interval has no elements.
|
309
|
+
def empty?
|
310
|
+
components.empty?
|
311
|
+
end
|
312
|
+
|
313
|
+
# An interval is sharp if each pair of extrema differ by at most a bit in the
|
314
|
+
# least significant position.
|
315
|
+
#
|
316
|
+
# Interval[3].sharp? # => true
|
317
|
+
# Interval[1,2].sharp? # => false
|
318
|
+
# (1/Interval[3.0]).sharp? # => true
|
319
|
+
# Interval[[1],[2]].sharp? # => true
|
320
|
+
#
|
321
|
+
def sharp?
|
322
|
+
all? {|x| x.sharp?}
|
323
|
+
end
|
324
|
+
|
325
|
+
# The hull is the smallest simple interval containing the interval itself.
|
326
|
+
#
|
327
|
+
# Interval[[1,2],[3,4]] # => Interval[1, 4]
|
328
|
+
#
|
329
|
+
def hull
|
330
|
+
if empty?
|
331
|
+
Interval[]
|
332
|
+
else
|
333
|
+
Interval[components.first.inf, components.last.sup]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
@@newtonOpts = {
|
338
|
+
:maxIterations => 100,
|
339
|
+
:verbose => false
|
340
|
+
}
|
341
|
+
|
342
|
+
# Solve a non-linear equation using the Newton-Raphson method,
|
343
|
+
# finding all solutions in a in interval.
|
344
|
+
#
|
345
|
+
# For instance, to solve
|
346
|
+
# x**3 == x
|
347
|
+
# we rewrite it first in the form
|
348
|
+
# x**3 - x == 0
|
349
|
+
# The left-hand side of this equation has derivative
|
350
|
+
# 3* x**2 - 1
|
351
|
+
# To find all solutions in [-100,100] we call
|
352
|
+
# Interval[-100,100].newton(proc {|x| x**3 - x}, proc {|x| 3* x**2 - 1})
|
353
|
+
# # => Interval[[-1.0], [-0.0], [1.0]]
|
354
|
+
# a sharp result showing the three roots -1, 0 and +1.
|
355
|
+
#
|
356
|
+
def newton(f, fp, opts = {})
|
357
|
+
effectiveOpts = @@newtonOpts.dup.update(opts)
|
358
|
+
verbose = effectiveOpts[:verbose]
|
359
|
+
puts "Starting on #{self}" if verbose
|
360
|
+
step = proc {|w,ww| (w - f.call(Interval[w]) / fp.call(ww) ) & ww}
|
361
|
+
self.map{|xx|
|
362
|
+
effectiveOpts[:maxIterations].times{
|
363
|
+
previous = xx;
|
364
|
+
xx = step.call(xx.midpoint,xx)
|
365
|
+
if previous == xx
|
366
|
+
if xx.sharp?
|
367
|
+
puts "Sharp fixed point #{xx}" if verbose
|
368
|
+
xx = Interval[xx.extrema.select {|x| f.call(Interval[x]).include?(0)}]
|
369
|
+
break
|
370
|
+
end
|
371
|
+
nonminimal_extrema = xx.extrema.reject {|x| f.call(Interval[x]).include?(0)}
|
372
|
+
if nonminimal_extrema == [] then
|
373
|
+
puts "Unsharp fixed point #{xx}" if verbose
|
374
|
+
break
|
375
|
+
end
|
376
|
+
if nonminimal_extrema.each {|x|
|
377
|
+
yy = step.call(x,xx)
|
378
|
+
if yy != xx
|
379
|
+
xx = yy;
|
380
|
+
break
|
381
|
+
end
|
382
|
+
}
|
383
|
+
xx = Interval[
|
384
|
+
if nonminimal_extrema.include?(xx.inf)
|
385
|
+
xx.inf + xx.inf.ulp
|
386
|
+
else
|
387
|
+
xx.inf
|
388
|
+
end,
|
389
|
+
if nonminimal_extrema.include?(xx.sup)
|
390
|
+
xx.sup - xx.sup.ulp
|
391
|
+
else
|
392
|
+
xx.sup
|
393
|
+
end]
|
394
|
+
end
|
395
|
+
end
|
396
|
+
if xx.empty?
|
397
|
+
puts "No solution" if verbose
|
398
|
+
break
|
399
|
+
elsif ! xx.simple?
|
400
|
+
puts "Branch splitting" if verbose
|
401
|
+
xx = xx.newton(f,fp,opts)
|
402
|
+
break
|
403
|
+
end
|
404
|
+
} && verbose && puts("Failed convergence #{effectiveOpts[:maxIterations]}")
|
405
|
+
xx
|
406
|
+
}. inject{|a,x| a |=x}
|
407
|
+
end
|
408
|
+
|
409
|
+
end
|
410
|
+
|
411
|
+
# Implements simple intervals. A simple interval is an interval with a single
|
412
|
+
# component, i.e., a connected set of real numbers (extended with plus and minus
|
413
|
+
# infinity.)
|
414
|
+
class Interval::Simple < Interval
|
415
|
+
attr :inf
|
416
|
+
attr :sup
|
417
|
+
|
418
|
+
include Enumerable
|
419
|
+
|
420
|
+
# Implements Enumerable#each
|
421
|
+
def each
|
422
|
+
yield(self)
|
423
|
+
self
|
424
|
+
end
|
425
|
+
|
426
|
+
# Return an array with one element, itself.
|
427
|
+
def components
|
428
|
+
[self]
|
429
|
+
end
|
430
|
+
|
431
|
+
# Constructs a simple interval with given extrema.
|
432
|
+
#
|
433
|
+
# Interval::Simple[1] # => Interval[1]
|
434
|
+
# Interval::Simple[1,2] # => Interval[1,2]
|
435
|
+
#
|
436
|
+
def Simple.[](a, b = a)
|
437
|
+
new(a, b)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Constructs a simple interval, with given lower and upper bounds.
|
441
|
+
#
|
442
|
+
# Interval::Simple.new(1,1) # => Interval[1]
|
443
|
+
# Interval::Simple.new(1,2) # => Interval[1,2]
|
444
|
+
#
|
445
|
+
def initialize (a, b)
|
446
|
+
if (a.respond_to?(:nan?) && a.nan? ) || (b.respond_to?(:nan?) && b.nan?)
|
447
|
+
@inf, @sup = -Infinity, Infinity
|
448
|
+
else
|
449
|
+
@inf, @sup = a, b
|
450
|
+
end
|
451
|
+
freeze
|
452
|
+
end
|
453
|
+
|
454
|
+
# Overrides Interval#construction
|
455
|
+
def construction # :nodoc:
|
456
|
+
extrema.uniq
|
457
|
+
end
|
458
|
+
|
459
|
+
# Implements Interval#simple?, returning true.
|
460
|
+
def simple? # :nodoc:
|
461
|
+
true
|
462
|
+
end
|
463
|
+
|
464
|
+
# Two simple intervals are equal if they have same inf and sup.
|
465
|
+
def == (other)
|
466
|
+
self.class == other.class && inf == other.inf && sup == other.sup
|
467
|
+
end
|
468
|
+
|
469
|
+
# Implements Interval#include?.
|
470
|
+
def include?(x) # :nodoc:
|
471
|
+
inf <= x && x <= sup
|
472
|
+
end
|
473
|
+
|
474
|
+
# Returns the array with inf and sup.
|
475
|
+
#
|
476
|
+
# Interval[1,2].extrema # => [1, 2]
|
477
|
+
#
|
478
|
+
def extrema
|
479
|
+
[inf,sup]
|
480
|
+
end
|
481
|
+
|
482
|
+
# The interval's width is sup - inf.
|
483
|
+
#
|
484
|
+
# Interval[2].width # => 0
|
485
|
+
# Interval[2,3].width # => 1
|
486
|
+
#
|
487
|
+
def width
|
488
|
+
sup - inf
|
489
|
+
end
|
490
|
+
|
491
|
+
# The point in the middle of the interval.
|
492
|
+
#
|
493
|
+
# Interval[1,3].midpoint # => 2.0
|
494
|
+
#
|
495
|
+
def midpoint
|
496
|
+
(inf + sup) * 2 **-1
|
497
|
+
end
|
498
|
+
|
499
|
+
# Used to implement Interval's plus operator (\+).
|
500
|
+
def add (other)
|
501
|
+
self.class.new(
|
502
|
+
FPU.down {inf + other.inf},
|
503
|
+
FPU.up {sup + other.sup})
|
504
|
+
end
|
505
|
+
|
506
|
+
# Unitary minus.
|
507
|
+
def -@ # :nodoc:
|
508
|
+
self.class.new(-sup,-inf)
|
509
|
+
end
|
510
|
+
|
511
|
+
# Used to implement Interval's multiplicative operator (\*).
|
512
|
+
def multiply (other)
|
513
|
+
self.class.new(
|
514
|
+
FPU.down {
|
515
|
+
[inf * other.inf, inf * other.sup,
|
516
|
+
sup * other.inf, sup * other.sup]}.min,
|
517
|
+
FPU.up {
|
518
|
+
[inf * other.inf, inf * other.sup,
|
519
|
+
sup * other.inf, sup * other.sup]}.max)
|
520
|
+
rescue ArgumentError
|
521
|
+
self.class.new(-Infinity, Infinity)
|
522
|
+
end
|
523
|
+
|
524
|
+
# Implements Interval#inverse.
|
525
|
+
def inverse # :nodoc:
|
526
|
+
if include?(0) then
|
527
|
+
Interval[
|
528
|
+
[-Infinity, FPU::up { inf == 0 ? -Infinity : inf**-1 }],
|
529
|
+
[FPU::down { sup == 0 ? Infinity : sup**-1 }, Infinity]]
|
530
|
+
else
|
531
|
+
self.class.new(
|
532
|
+
FPU::down { sup ** -1 },
|
533
|
+
FPU::up { inf ** -1 })
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
# Implements Interval's power operator.
|
538
|
+
def ** (n) # :nodoc:
|
539
|
+
if n < 0
|
540
|
+
(self ** -n).inverse
|
541
|
+
elsif inf > 0
|
542
|
+
self.class.new(
|
543
|
+
FPU::down { FPU.power(inf, n) },
|
544
|
+
FPU::up { FPU.power(sup, n) })
|
545
|
+
elsif sup < 0
|
546
|
+
if (-1) ** n > 0
|
547
|
+
(-self)**n
|
548
|
+
else
|
549
|
+
- (-self)**n
|
550
|
+
end
|
551
|
+
elsif (-1) ** n > 0
|
552
|
+
self.class.new(0, FPU::up {[FPU.power(inf, n), FPU.power(sup, n)]}.max )
|
553
|
+
else
|
554
|
+
self.class.new(
|
555
|
+
FPU::down { FPU.power(inf, n) },
|
556
|
+
FPU::up { FPU.power(sup, n) })
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
# Implements Interval's & operator.
|
561
|
+
def intersect (other)
|
562
|
+
self.class.new([inf,other.inf].max, [sup,other.sup].min)
|
563
|
+
end
|
564
|
+
|
565
|
+
# Implements Interval#sharp?
|
566
|
+
def sharp? # :nodoc:
|
567
|
+
w = width
|
568
|
+
if w == 0
|
569
|
+
true
|
570
|
+
elsif w.kind_of?(Float) && (inf >= 0 || sup <= 0)
|
571
|
+
s1, s2 = extrema.map{|x| x.abs}.sort
|
572
|
+
s1 + s1.ulp == s2
|
573
|
+
else
|
574
|
+
false
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# Implements Interval#exp
|
579
|
+
def exp #:nodoc:
|
580
|
+
self.class.new(FPU.exp_down(inf), FPU.exp_up(sup))
|
581
|
+
end
|
582
|
+
|
583
|
+
# Implements Interval#atan
|
584
|
+
def atan #:nodoc:
|
585
|
+
self.class.new(FPU.atan_down(inf), FPU.atan_up(sup))
|
586
|
+
end
|
587
|
+
|
588
|
+
# Implements Interval#sinh
|
589
|
+
def sinh #:nodoc:
|
590
|
+
self.class.new(FPU.sinh_down(inf), FPU.sinh_up(sup))
|
591
|
+
end
|
592
|
+
|
593
|
+
# Implements Interval#log
|
594
|
+
def log #:nodoc:
|
595
|
+
if inf >= 0
|
596
|
+
self.class.new(FPU.log_down(inf), FPU.log_up(sup))
|
597
|
+
elsif sup >= 0
|
598
|
+
self.class.new(-Infinity, FPU.log_up(sup))
|
599
|
+
else
|
600
|
+
Interval[]
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# Implements Interval#cosh
|
605
|
+
def cosh #:nodoc:
|
606
|
+
if inf >=0
|
607
|
+
self.class.new(FPU.cosh_down(inf), FPU.cosh_up(sup))
|
608
|
+
elsif sup <= 0
|
609
|
+
self.class.new(FPU.cosh_down(sup), FPU.cosh_up(inf))
|
610
|
+
else
|
611
|
+
self.class.new(0,FPU.cosh_up([-inf,sup].max))
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
# Implements Interval#cos
|
616
|
+
def cos #:nodoc:
|
617
|
+
return Interval[-1,1] if inf.abs == Infinity || sup.abs == Infinity
|
618
|
+
p = inf.divmod(Math::PI).first.to_i
|
619
|
+
self.class.new(
|
620
|
+
if include?((2*((p-1).div(2))+3) * Math::PI)
|
621
|
+
-1
|
622
|
+
else
|
623
|
+
[FPU.cos_down(inf), FPU.cos_down(sup)].min
|
624
|
+
end,
|
625
|
+
if include?((2*(p.div(2))+2) * Math::PI)
|
626
|
+
+1
|
627
|
+
else
|
628
|
+
[FPU.cos_up(inf), FPU.cos_up(sup)].max
|
629
|
+
end)
|
630
|
+
end
|
631
|
+
|
632
|
+
# Implements Interval#sin
|
633
|
+
def sin #:nodoc:
|
634
|
+
return Interval[-1,1] if inf.abs == Infinity || sup.abs == Infinity
|
635
|
+
q = inf.divmod(Math::PI/2).first.to_i
|
636
|
+
p = (q-1).div(2)
|
637
|
+
self.class.new(
|
638
|
+
if include?((2*((p-1).div(2))+3.5) * Math::PI)
|
639
|
+
-1
|
640
|
+
else
|
641
|
+
[FPU.sin_down(inf), FPU.sin_down(sup)].min
|
642
|
+
end,
|
643
|
+
if include?((2*(p.div(2))+2.5) * Math::PI)
|
644
|
+
+1
|
645
|
+
else
|
646
|
+
[FPU.sin_up(inf), FPU.sin_up(sup)].max
|
647
|
+
end)
|
648
|
+
end
|
649
|
+
|
650
|
+
# Implement Interval#tan
|
651
|
+
def tan #:nodoc:
|
652
|
+
q = inf.divmod(Math::PI/2).first.to_i
|
653
|
+
p = (q-1).div(2)
|
654
|
+
if include?(Math::PI * (p+2.5))
|
655
|
+
self.class.new(-Infinity,Infinity)
|
656
|
+
elsif include?(Math::PI * (p+1.5))
|
657
|
+
Interval[[-Infinity,FPU.tan_up(sup)],[FPU.tan_down(inf),Infinity]]
|
658
|
+
else
|
659
|
+
self.class.new(FPU.tan_down(inf),FPU.tan_up(sup))
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
end
|
664
|
+
|
665
|
+
# Implements an interval with multiple components.
|
666
|
+
class Interval::Complex < Interval
|
667
|
+
|
668
|
+
# The array with the interval's components.
|
669
|
+
attr :components
|
670
|
+
|
671
|
+
def initialize (array) # :nodoc:
|
672
|
+
@components = array
|
673
|
+
freeze
|
674
|
+
end
|
675
|
+
|
676
|
+
# Implements Enumerable#each.
|
677
|
+
def each
|
678
|
+
components.each { |o| yield(o) }
|
679
|
+
self
|
680
|
+
end
|
681
|
+
|
682
|
+
end
|
683
|
+
|
684
|
+
class Range
|
685
|
+
|
686
|
+
# Convert to Interval.
|
687
|
+
#
|
688
|
+
# (1.2 .. 5.3).to_interval # => Interval[1.2, 5.3]
|
689
|
+
# (1 ... 4).to_interval # => Interval[1, 3]
|
690
|
+
# (1 ... 3.1).to_interval # Fails
|
691
|
+
#
|
692
|
+
def to_interval
|
693
|
+
Interval[first,
|
694
|
+
if exclude_end?
|
695
|
+
(last - 1).succ - 1
|
696
|
+
else
|
697
|
+
last
|
698
|
+
end]
|
699
|
+
end
|
700
|
+
|
701
|
+
end
|
702
|
+
|
703
|
+
class Interval
|
704
|
+
|
705
|
+
# The sharp interval that includes which Euler's constant.
|
706
|
+
E = Math::E + Interval[0, 2 ** -51]
|
707
|
+
|
708
|
+
# The sharp interval that includes Pi.
|
709
|
+
PI = Math::PI + Interval[0, 2 ** -51]
|
710
|
+
|
711
|
+
end
|
712
|
+
|
713
|
+
require 'test/unit'
|
714
|
+
|
715
|
+
# Assertions to be used when testing intervals.
|
716
|
+
module Interval::Assertions
|
717
|
+
|
718
|
+
def assert_sharp(x)
|
719
|
+
assert(x.sharp?, x.inspect + " should be sharp.")
|
720
|
+
assert((-x).sharp?, (-x).inspect + " should be sharp.")
|
721
|
+
end
|
722
|
+
|
723
|
+
# Assert that an interval is not sharp.
|
724
|
+
def assert_fuzzy(x)
|
725
|
+
assert(!x.sharp?, x.inspect + " should not be sharp.")
|
726
|
+
assert(!(-x).sharp?, (-x).inspect + " should not be sharp.")
|
727
|
+
end
|
728
|
+
|
729
|
+
# Assert that +s+ is a solution to +f+ == 0. Unless the +weak+ flag is set,
|
730
|
+
# assert also that +s+ == +expected+.
|
731
|
+
# If the +weak+ flag is set, assert that +s+ includes +expected+.
|
732
|
+
def assert_solution(s,f,expected, weak = nil)
|
733
|
+
res = s.map{|c| f.call(c)}.inject{|a,c| a|c}
|
734
|
+
# Did something spectacarly unexpected happen?
|
735
|
+
assert(res.simple?, res)
|
736
|
+
# Is it a solution?
|
737
|
+
assert(res.include?(0), res)
|
738
|
+
# Is it minimal?
|
739
|
+
assert(s.all? {|x| x.extrema.all? {|y| f.call(Interval[y]).include?(0)}}, s.inspect +
|
740
|
+
" is non minmal")
|
741
|
+
# Is it the expected solution?
|
742
|
+
if weak
|
743
|
+
# check the granularity
|
744
|
+
assert_equal(expected.components.size, s.components.size, [expected,s].inspect)
|
745
|
+
# check for inclusion
|
746
|
+
assert_equal(expected, s & expected, s)
|
747
|
+
else
|
748
|
+
# check for equality
|
749
|
+
assert_equal(expected,s)
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
end
|
754
|
+
|
755
|
+
# Tests Interval fundamentals: construction, equality and inspection.
|
756
|
+
class Interval::TestFundamentals < Test::Unit::TestCase
|
757
|
+
|
758
|
+
def test_constructor_and_equality
|
759
|
+
tester = proc {|i,ex,klass|
|
760
|
+
assert_equal(klass, i.class)
|
761
|
+
assert_equal(ex, i.construction)
|
762
|
+
assert_equal(i, Interval[*i.construction])
|
763
|
+
}
|
764
|
+
|
765
|
+
tester.call Interval[1], [1], Interval::Simple
|
766
|
+
tester.call Interval[1,2], [1,2], Interval::Simple
|
767
|
+
tester.call Interval[[1,2],[3,4]], [[1,2],[3,4]], Interval::Complex
|
768
|
+
tester.call Interval[[1],[2]], [[1],[2]], Interval::Complex
|
769
|
+
tester.call Interval[[1]], [1], Interval::Simple
|
770
|
+
tester.call Interval[2,0/0.0], [-Infinity,+Infinity], Interval::Simple
|
771
|
+
tester.call Interval[0/0.0,9], [-Infinity,+Infinity], Interval::Simple
|
772
|
+
tester.call Interval[[1,3],[4,6],[2,5],[9,9]], [[1,6],[9]], Interval::Complex
|
773
|
+
end
|
774
|
+
|
775
|
+
def test_construction_failure
|
776
|
+
assert_raise(NoMethodError) { Interval[1,[2,3]] }
|
777
|
+
assert_raise(ArgumentError) { Interval[1,2,3] }
|
778
|
+
assert_raise(NoMethodError) { Interval[1,[2,3]] }
|
779
|
+
assert_raise(NoMethodError) { Interval[[1],2] }
|
780
|
+
end
|
781
|
+
|
782
|
+
def test_inequality
|
783
|
+
assert(Interval[1,2] != Interval[1,3])
|
784
|
+
assert(Interval[1,2] != Interval[0,2])
|
785
|
+
assert(Interval[1,2] != Interval[0,3])
|
786
|
+
assert(Interval[1,2] != 3)
|
787
|
+
assert(Interval[[1,2],[3,4]] != Interval[[1,0],[3,4]])
|
788
|
+
end
|
789
|
+
|
790
|
+
def test_inspect_and_to_s
|
791
|
+
tester = proc {|s|
|
792
|
+
i = eval(s)
|
793
|
+
assert_equal(s,i.inspect)
|
794
|
+
assert_equal(s,i.to_s)
|
795
|
+
}
|
796
|
+
tester.call "Interval[1]"
|
797
|
+
tester.call "Interval[1, 2]"
|
798
|
+
tester.call "Interval[[1, 2], [3, 4]]"
|
799
|
+
tester.call "Interval[[1, 2], [4], [5, 6], [Infinity]]"
|
800
|
+
tester.call "Interval[1, 2]"
|
801
|
+
end
|
802
|
+
|
803
|
+
end
|
804
|
+
|
805
|
+
# Tests the arithmetic of Interval::Simple.
|
806
|
+
class Interval::TestSimpleArithmetic < Test::Unit::TestCase
|
807
|
+
|
808
|
+
def test_plus
|
809
|
+
assert_equal(Interval[-Infinity,+Infinity], Interval[-Infinity] + Interval[Infinity])
|
810
|
+
assert_equal(Interval[4,6], Interval[1,2] + Interval[3,4])
|
811
|
+
assert_equal(Interval[3,Infinity], Interval[1,Infinity]+Interval[2])
|
812
|
+
assert_equal(Interval[-Infinity,+Infinity],Interval[-Infinity,-1] + Interval[2,+Infinity])
|
813
|
+
assert_equal(Interval[-Infinity,+Infinity],Interval[-Infinity] + Interval[8,+Infinity])
|
814
|
+
end
|
815
|
+
|
816
|
+
def test_minus
|
817
|
+
assert_equal(Interval[-2,-1], -Interval[1,2])
|
818
|
+
end
|
819
|
+
|
820
|
+
def test_times
|
821
|
+
assert_equal(Interval[-Infinity,+Infinity],Interval[Infinity] * Interval[0])
|
822
|
+
assert_equal(Interval[+Infinity],Interval[Infinity] * Interval[3])
|
823
|
+
assert_equal(Interval[-8,+10], Interval[1,2] * Interval[-4,5])
|
824
|
+
assert_equal(Interval[3,8], Interval[1,2] * Interval[3,4])
|
825
|
+
assert_equal(Interval[-Infinity,+Infinity],Interval[0,1] * Interval[2,Infinity])
|
826
|
+
assert_equal(Interval[2, Infinity], Interval[-Infinity,-2] * Interval[-Infinity,-1])
|
827
|
+
end
|
828
|
+
|
829
|
+
def test_inverse
|
830
|
+
assert_equal(Interval[0.5,1],Interval[1,2].inverse)
|
831
|
+
assert_equal(Interval[-1,-0.5],(-Interval[1,2]).inverse)
|
832
|
+
assert_equal(Interval[[-Infinity,-1],[0.5,+Infinity]],Interval[-1,2].inverse)
|
833
|
+
assert_equal(Interval[[-Infinity],[1,+Infinity]],Interval[0,1].inverse)
|
834
|
+
end
|
835
|
+
|
836
|
+
def test_power
|
837
|
+
assert_equal((-Interval[1,2]).inverse, (-Interval[1,2]) ** -1)
|
838
|
+
assert_equal(Interval[0,4], Interval[-1,2]**2)
|
839
|
+
assert_equal(Interval[-27,8], Interval[-3,2]**3)
|
840
|
+
|
841
|
+
assert_equal(
|
842
|
+
Interval[FPU.down{(1/3.0)*(1/3.0)},FPU.up{(1/3.0)*(1/3.0)}],
|
843
|
+
(Interval[1]/3.0) ** 2)
|
844
|
+
|
845
|
+
assert_equal(
|
846
|
+
Interval[
|
847
|
+
FPU.down{(1/3.0)*(1/3.0)*(1/3.0)},
|
848
|
+
FPU.up{(1/3.0)*(1/3.0)*(1/3.0)}],
|
849
|
+
(Interval[1]/3.0) ** 3)
|
850
|
+
end
|
851
|
+
|
852
|
+
end
|
853
|
+
|
854
|
+
# Tests other methods of Interval::Simple.
|
855
|
+
class Interval::TestSimpleMethods < Test::Unit::TestCase
|
856
|
+
|
857
|
+
def test_midpoint
|
858
|
+
assert_equal(1.5,Interval[1,2].midpoint)
|
859
|
+
assert_raise(NoMethodError){ Interval[[1,2],[3,4]].midpoint }
|
860
|
+
end
|
861
|
+
|
862
|
+
def test_width
|
863
|
+
assert_equal(1, Interval[1,2].width)
|
864
|
+
assert_equal(1, Interval[1,2].width)
|
865
|
+
tester = proc{|x|
|
866
|
+
assert_equal(
|
867
|
+
2.0 ** (-(Math.log(x)/Math.log(2)).ceil - 52),
|
868
|
+
(1/Interval[x]).width)
|
869
|
+
}
|
870
|
+
tester.call(3.0)
|
871
|
+
tester.call(5.0)
|
872
|
+
tester.call(119.0)
|
873
|
+
tester.call(34e-4)
|
874
|
+
end
|
875
|
+
|
876
|
+
end
|
877
|
+
|
878
|
+
# Tests Interval algebra.
|
879
|
+
class Interval::TestAlgebra < Test::Unit::TestCase
|
880
|
+
|
881
|
+
def test_union
|
882
|
+
assert_equal(Interval[[1, 6], [9, 9]], Interval[[1, 3],[4, 6]] | Interval[[2, 5], [9,9]])
|
883
|
+
end
|
884
|
+
|
885
|
+
def test_plus
|
886
|
+
tester = proc{|x,y,sum|
|
887
|
+
assert_equal(sum,x+y)
|
888
|
+
assert_equal(sum,y+x)
|
889
|
+
}
|
890
|
+
tester.call(Interval[[1,2],[10,Infinity]], Interval[[1,9],[-2,-1]], Interval[[-1,1],[2,Infinity]])
|
891
|
+
tester.call(Interval[1,9], Interval[[1,2],[10,Infinity]], Interval[2,Infinity])
|
892
|
+
tester.call(Interval[1,2], 2, Interval[3,4])
|
893
|
+
end
|
894
|
+
|
895
|
+
def test_intersection
|
896
|
+
tester = proc{|x,y,z|
|
897
|
+
assert_equal(z, x & y)
|
898
|
+
assert_equal(z, y & x)
|
899
|
+
}
|
900
|
+
tester.call(Interval[1,2], Interval[0,3], Interval[1,2])
|
901
|
+
tester.call(Interval[1.1,1.9], Interval[1.3,2.5], Interval[1.3, 1.9])
|
902
|
+
tester.call(Interval[1.1,1.9], Interval[0.3,0.7], Interval[])
|
903
|
+
end
|
904
|
+
|
905
|
+
def test_inverse
|
906
|
+
assert_equal(Interval[[-Infinity, -2.0],[0.0, Infinity]],Interval[[-0.5,0.5],[0.2,Infinity]].inverse)
|
907
|
+
end
|
908
|
+
|
909
|
+
def test_minus
|
910
|
+
assert_equal(Interval[[-4,-3],[-2,-1]], -Interval[[1,2],[3,4]])
|
911
|
+
end
|
912
|
+
|
913
|
+
def test_times
|
914
|
+
tester = proc{|x,y,z|
|
915
|
+
assert_equal(z,x*y)
|
916
|
+
assert_equal(z,y*x)
|
917
|
+
}
|
918
|
+
tester.call(Interval[[1, 2], [3, 4]], Interval[0.5, 2], Interval[0.5,8])
|
919
|
+
tester.call(Interval[1,2], 2, Interval[2,4])
|
920
|
+
end
|
921
|
+
|
922
|
+
def test_division
|
923
|
+
assert_equal(Interval[-Infinity, Infinity], Interval[0,1]/Interval[0,1])
|
924
|
+
assert_equal(Interval[0.5], Interval[1]/2)
|
925
|
+
end
|
926
|
+
|
927
|
+
def test_power
|
928
|
+
assert_equal(Interval[-1,2], (Interval[-1,2]**-1)**-1)
|
929
|
+
end
|
930
|
+
|
931
|
+
end
|
932
|
+
|
933
|
+
#Tests other Interval methods.
|
934
|
+
class Interval::TestMethods < Test::Unit::TestCase
|
935
|
+
include Interval::Assertions
|
936
|
+
|
937
|
+
def test_include?
|
938
|
+
assert(Interval[1,2].include?(1.5),"1")
|
939
|
+
assert(Interval[1,2].include?(1),"2")
|
940
|
+
assert(Interval[1,2].include?(2),"3")
|
941
|
+
assert(!Interval[1,2].include?(0),"4")
|
942
|
+
assert(!Interval[1,2].include?(4),"5")
|
943
|
+
end
|
944
|
+
|
945
|
+
def test_simple?
|
946
|
+
assert(Interval[1,2].simple?)
|
947
|
+
assert(!Interval[[1,2],[3,4]].simple?)
|
948
|
+
end
|
949
|
+
|
950
|
+
def test_empty?
|
951
|
+
assert(!Interval[1,2].empty?)
|
952
|
+
assert(!Interval[[1,2],[4,3]].empty?)
|
953
|
+
assert(Interval[].empty?)
|
954
|
+
end
|
955
|
+
|
956
|
+
def test_sharp?
|
957
|
+
assert_sharp(Interval[5,5])
|
958
|
+
assert_sharp(Interval[[1],[2]])
|
959
|
+
assert_fuzzy(Interval[[1],[2,3]])
|
960
|
+
assert_fuzzy(Interval[5,6])
|
961
|
+
assert_fuzzy(Interval[1.1,1.2])
|
962
|
+
assert_fuzzy(Interval[Struct::Float[-1,0,1].to_f,Struct::Float[+1,0,1].to_f])
|
963
|
+
assert_sharp(Interval[Struct::Float[1,1022,2**52-1].to_f,1])
|
964
|
+
assert_sharp(Interval[Struct::Float[1,1029,2**52-1].to_f,128])
|
965
|
+
assert_sharp(Interval[-0.0,0.0])
|
966
|
+
assert_sharp(Interval[0.0,Struct::Float[1,0,1].to_f])
|
967
|
+
assert_sharp(Interval[6369051672525772, 6369051672525773] * 2.0 ** -52)
|
968
|
+
assert_sharp(Interval[6369051672525772, 6369051672525772] * 2.0 ** -52)
|
969
|
+
assert_fuzzy(Interval[6369051672525771, 6369051672525773] * 2.0 ** -52)
|
970
|
+
assert_fuzzy(Interval[6369051672525771, 6369051672525785] * 2.0 ** -52)
|
971
|
+
end
|
972
|
+
|
973
|
+
def test_hull
|
974
|
+
assert_equal(Interval[1,4], Interval[[1,2],[3,4]].hull)
|
975
|
+
assert_equal(Interval[1,4], Interval[1,4].hull)
|
976
|
+
assert_equal(Interval[], Interval[].hull)
|
977
|
+
end
|
978
|
+
|
979
|
+
def test_conversion_from_range
|
980
|
+
assert_equal(Interval[1.2, 5.3], (1.2 .. 5.3).to_interval)
|
981
|
+
assert_equal(Interval[1, 4], (1 .. 4).to_interval)
|
982
|
+
assert_equal(Interval[1, 3], (1 ... 4).to_interval)
|
983
|
+
assert_raise(NoMethodError){ (1 ... 3.1).to_interval }
|
984
|
+
end
|
985
|
+
|
986
|
+
end
|
987
|
+
|
988
|
+
# Tests the Interval-based non-linear solver.
|
989
|
+
class Interval::TestNewton < Test::Unit::TestCase
|
990
|
+
include Interval::Assertions
|
991
|
+
|
992
|
+
def test_sqrt2
|
993
|
+
f, fp = proc {|x| x**2 - 2}, proc {|x| 2*x}
|
994
|
+
s = Math::sqrt(2)
|
995
|
+
assert_equal(6369051672525773 * 2.0 **-52, s)
|
996
|
+
assert_equal(Interval[s], Interval[0.1,5.0].newton(f, fp))
|
997
|
+
assert_equal(Interval[], Interval[2.0,5.0].newton(f,fp))
|
998
|
+
assert_equal(Interval[s], Interval[-1.0,10.0].newton(f,fp))
|
999
|
+
assert_equal(Interval[-s], Interval[-5.0,0].newton(f,fp))
|
1000
|
+
assert_equal(Interval[[-s],[s]], Interval[-5.0,5.0].newton(f,fp))
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
def test_cubic_1
|
1004
|
+
f, fp = proc {|x| x**3 + x - 10}, proc {|x| 3*x**2 + 1}
|
1005
|
+
s = Interval[-1,5].newton(f,fp)
|
1006
|
+
assert_solution(s, f, Interval[2])
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def test_cubic_2
|
1010
|
+
f, fp = proc {|x| x**3 + x - 15}, proc {|x| 3*x**2 + 1}
|
1011
|
+
s = Interval[-1.0,5.0].newton(f,fp)
|
1012
|
+
assert_solution(s, f, Interval[5249383869325654 * 2.0 ** -51])
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
def test_cubic_3
|
1016
|
+
f, fp = proc {|x| x * (x ** 2 - 1)}, proc {|x| 3*x**2 - 1}
|
1017
|
+
s = Interval[-5.0,5.0].newton(f,fp)
|
1018
|
+
assert_solution(s, f, Interval[[-1],[0],[1]])
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def test_dennis_schnabel
|
1022
|
+
f1 = proc {|x| (((x-12) * x + 47 ) * x - 60 )* x}
|
1023
|
+
f2 = proc {|x| f1.call(x) + 24 }
|
1024
|
+
f3 = proc {|x| f1.call(x) + 24.1 }
|
1025
|
+
fp = proc {|x| ((4 * x - 12*3) * x + 47*2) * x - 60}
|
1026
|
+
|
1027
|
+
assert_solution(Interval[-1e2,1e2].newton(f1,fp), f1, Interval[[0],[3],[4],[5]],:weak)
|
1028
|
+
assert_solution(Interval[-1e2,1e2].newton(f2,fp), f2, Interval[[0.888305779071752],[1]], :weak)
|
1029
|
+
assert_equal(Interval[], Interval[-1e2,1e2].newton(f3,fp))
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
if FPU.respond_to?(:exp_up)
|
1033
|
+
def test_exp
|
1034
|
+
f, fp = proc{|x| x.exp + x}, proc {|x| x.exp + 1}
|
1035
|
+
z0 = Interval[-1e2,1e2].newton(f,fp)
|
1036
|
+
assert_solution(z0, f, Interval[-0.56714329040978384])
|
1037
|
+
|
1038
|
+
f, fp = proc{|x| x*(-x).exp + 1}, proc {|x| (1-x)*(-x).exp}
|
1039
|
+
z1 = Interval[-1e2,1e2].newton(f,fp)
|
1040
|
+
assert_solution(z0, f, z0, :weak)
|
1041
|
+
assert_sharp(z1)
|
1042
|
+
end end
|
1043
|
+
|
1044
|
+
if FPU.respond_to?(:exp_up)
|
1045
|
+
def test_trig
|
1046
|
+
f = proc{|x| (x*Interval::PI/3.0).cos() -0.5}
|
1047
|
+
fp = proc{|x| -(x*Interval::PI/3).sin() * Interval::PI/3}
|
1048
|
+
s = Interval[-10.0,10.0].newton(f, fp)
|
1049
|
+
assert_solution(s, f, Interval[[-7], [-5], [-1], [1], [5], [7]], :weak)
|
1050
|
+
end end
|
1051
|
+
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
if FPU.respond_to?(:exp_up)
|
1055
|
+
# Tests Interval transcendental functions.
|
1056
|
+
class Interval::TestTranscendental < Test::Unit::TestCase
|
1057
|
+
include Interval::Assertions
|
1058
|
+
|
1059
|
+
def test_exp
|
1060
|
+
assert_equal(Interval::E, Interval[1].exp)
|
1061
|
+
assert_equal(Interval[1,Interval::E.sup], Interval[0,1].exp)
|
1062
|
+
assert_equal(Interval[Interval::E.inf, Infinity], Interval[1,Infinity].exp)
|
1063
|
+
assert_equal(Interval[0], Interval[-Infinity].exp)
|
1064
|
+
assert_equal(Interval[[0, 1], [Math::E, Infinity]], Interval[[-Infinity,0],[1,Infinity]].exp)
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
def test_log
|
1068
|
+
assert_equal(Interval[-Infinity, 0], Interval[-1,+1].log)
|
1069
|
+
assert_equal(Interval[0], Interval[1].log)
|
1070
|
+
assert_equal(Interval[], Interval[-2,-1].log)
|
1071
|
+
assert_equal(Interval[-Infinity], Interval[-2,0].log)
|
1072
|
+
assert_equal(Interval[-Infinity,0], Interval[0,1].log)
|
1073
|
+
assert(Interval::E.log.include?(1))
|
1074
|
+
assert_equal(3 * 2 ** -53, Interval[1].exp.log.width)
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def test_atan
|
1078
|
+
assert_equal(Interval::PI, 4 * Interval[1].atan)
|
1079
|
+
assert_equal(Interval[-Interval::PI.sup, Interval::PI.sup], 2 * Interval[-Infinity,Infinity].atan)
|
1080
|
+
assert_equal(Interval[0], Interval[0].atan)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
def test_cosh
|
1084
|
+
assert(Interval[1].cosh.sharp?)
|
1085
|
+
assert_equal(Interval[1], Interval[0].cosh)
|
1086
|
+
assert_equal((Interval[1].cosh | Interval[3].cosh).hull, Interval[1,3].cosh)
|
1087
|
+
assert_equal(Interval[1,3].cosh, Interval[-3,-1].cosh)
|
1088
|
+
assert_equal((Interval[0] | Interval[3].cosh).hull, Interval[-1,3].cosh)
|
1089
|
+
assert_equal(Interval[-1,3].cosh, Interval[-3,1].cosh)
|
1090
|
+
assert_equal(Interval[0, Infinity], Interval[-Infinity,Infinity].cosh)
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def test_sinh
|
1094
|
+
assert(Interval[1].sinh.sharp?)
|
1095
|
+
assert_equal(Interval[0], Interval[0].sinh)
|
1096
|
+
assert_equal((Interval[-1].sinh | Interval[3].sinh).hull, Interval[-1,3].sinh)
|
1097
|
+
assert_equal(Interval[-Infinity, Infinity], Interval[-Infinity,Infinity].sinh)
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def test_cos
|
1101
|
+
assert_equal(Interval[-1,-1 + 2 ** -53], Interval::PI.cos)
|
1102
|
+
assert_equal(Interval[1 - 2 ** -53, 1], (2*Interval::PI).cos)
|
1103
|
+
assert_equal(Interval[-1,1], Interval[Infinity].cos)
|
1104
|
+
assert_equal(Interval[-1,1], Interval[-Infinity].cos)
|
1105
|
+
onehalf = (Interval::PI/3).cos
|
1106
|
+
assert( onehalf.include?(0.5))
|
1107
|
+
assert_equal(Interval[onehalf.inf,1], (-Interval::PI/4 | Interval::PI/3).hull.cos)
|
1108
|
+
minusonehalf = (2*Interval::PI/3).cos
|
1109
|
+
assert(minusonehalf.include?(-0.5))
|
1110
|
+
assert_equal(Interval[-1,minusonehalf.sup], (5*Interval::PI/4 | 2*Interval::PI/3).hull.cos)
|
1111
|
+
assert_equal((minusonehalf | onehalf).hull, ((Interval::PI | 2*Interval::PI).hull/3).cos)
|
1112
|
+
full = (5 * Interval::PI/3 | 3.5 * Interval::PI).hull
|
1113
|
+
assert_equal(Interval[-1,1], full.cos)
|
1114
|
+
assert_equal(Interval[-1,1], (full + 2 * Interval::PI).cos)
|
1115
|
+
assert_equal(Interval[-1,1], (full + 4 * Interval::PI).cos)
|
1116
|
+
assert_equal(Interval[-1,1], (full + 6 * Interval::PI).cos)
|
1117
|
+
assert_equal(Interval[-1,1], (full + 8 * Interval::PI).cos)
|
1118
|
+
assert_equal(Interval[-1,1], (full - 2 * Interval::PI).cos)
|
1119
|
+
assert_equal(Interval[-1,1], (full - 4 * Interval::PI).cos)
|
1120
|
+
assert_equal(Interval[-1,1], (full - 6 * Interval::PI).cos)
|
1121
|
+
assert_equal(Interval[-1,1], (full - 8 * Interval::PI).cos)
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
def test_sin
|
1125
|
+
assert_equal(Interval[-1,-1 + 2 ** -53], (3*Interval::PI/2).sin)
|
1126
|
+
assert_equal(Interval[1 - 2 ** -53, 1], (Interval::PI/2).sin)
|
1127
|
+
assert_equal(Interval[-1,1], Interval[Infinity].sin)
|
1128
|
+
assert_equal(Interval[-1,1], Interval[-Infinity].sin)
|
1129
|
+
onehalf = (Interval::PI/6).sin
|
1130
|
+
assert( onehalf.include?(0.5))
|
1131
|
+
assert_equal(Interval[onehalf.inf,1], (3 * Interval::PI/4 | Interval::PI/6).hull.sin)
|
1132
|
+
minusonehalf = (7*Interval::PI/6).sin
|
1133
|
+
assert(minusonehalf.include?(-0.5))
|
1134
|
+
assert_equal(Interval[-1,minusonehalf.sup], (7*Interval::PI/4 | 7*Interval::PI/6).hull.sin)
|
1135
|
+
assert_equal((-onehalf | onehalf).hull, ((Interval::PI | -Interval::PI).hull/6.0).sin)
|
1136
|
+
full = ((5 * Interval::PI/3 | 3.5 * Interval::PI) + Interval::PI/2).hull
|
1137
|
+
assert_equal(Interval[-1,1], full.sin)
|
1138
|
+
assert_equal(Interval[-1,1], (full + 2 * Interval::PI).cos)
|
1139
|
+
assert_equal(Interval[-1,1], (full + 4 * Interval::PI).cos)
|
1140
|
+
assert_equal(Interval[-1,1], (full + 6 * Interval::PI).cos)
|
1141
|
+
assert_equal(Interval[-1,1], (full + 8 * Interval::PI).cos)
|
1142
|
+
assert_equal(Interval[-1,1], (full - 2 * Interval::PI).cos)
|
1143
|
+
assert_equal(Interval[-1,1], (full - 4 * Interval::PI).cos)
|
1144
|
+
assert_equal(Interval[-1,1], (full - 6 * Interval::PI).cos)
|
1145
|
+
assert_equal(Interval[-1,1], (full - 8 * Interval::PI).cos)
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def test_tan
|
1149
|
+
assert_equal(Interval[0], Interval[0].tan)
|
1150
|
+
assert_equal(Interval[], Interval[].tan)
|
1151
|
+
assert_equal(Interval[-Infinity, Infinity], Interval[0,6].tan)
|
1152
|
+
assert_equal(Interval[-Infinity, Infinity], (-Interval::PI/2 | Interval::PI/2).hull.tan)
|
1153
|
+
assert_equal(Interval[-Infinity, Infinity], (- Interval::PI/4 | Interval::PI * 1.25).hull.tan)
|
1154
|
+
assert_equal(Interval[-Infinity, Infinity], (4.75 *Interval::PI | Interval::PI * 6.25).hull.tan)
|
1155
|
+
|
1156
|
+
assert_equal(2 ** -51, Interval::PI.tan.width)
|
1157
|
+
assert(Interval::PI.tan.include?(0))
|
1158
|
+
assert_equal(3 * 2 ** -53, (Interval::PI/4).tan.width)
|
1159
|
+
assert((Interval::PI/4).tan.include?(1) )
|
1160
|
+
|
1161
|
+
x = (Interval::PI/4 | Interval::PI * 0.75).hull
|
1162
|
+
assert_equal((Interval[x.sup].tan | -Infinity).hull | (Interval[x.inf].tan | Infinity).hull, x.tan)
|
1163
|
+
|
1164
|
+
x = (Interval::PI | -Interval::PI).hull/4
|
1165
|
+
assert_equal((Interval[x.inf].tan | Interval[x.sup].tan).hull, x.tan)
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
end end
|
1169
|
+
|
1170
|
+
Test::Unit.run = (__FILE__ != $0)
|