Change soft-fail to use the config, rather than env
[rbx.git] / kernel / core / numeric.rb
blob4d642cc91d7ea581d673bb8d569b24a98db924d9
1 # depends on: class.rb comparable.rb
3 class Numeric
4   include Comparable
6   # Numeric and sub-classes do not have ivars
7   def __ivars__ ; nil  ; end
9   def +@
10     self
11   end
13   def -@
14     0 - self
15   end
17   def +(other)
18     b, a = math_coerce other
19     a + b
20   end
21   
22   def -(other)
23     b, a = math_coerce other
24     a - b
25   end
26   
27   def *(other)
28     b, a = math_coerce other
29     a * b
30   end
31   
32   def %(other)
33     b, a = math_coerce other
34     raise ZeroDivisionError, "divided by 0" unless b.__kind_of__(Float) or b != 0
35     a % b
36   end
38   #--
39   # see README-DEVELOPERS regarding safe math compiler plugin
40   #++
42   def divide(other)
43     b, a = math_coerce other
44     raise ZeroDivisionError, "divided by 0" unless b.__kind_of__(Float) or b != 0
45     a / b
46   end
47   alias_method :/, :divide
48   
49   def **(other)
50     b, a = math_coerce other
51     a ** b
52   end
53   
54   def divmod(other)
55     b, a = math_coerce other
56     
57     if other == 0
58       raise FloatDomainError, "NaN" if other.__kind_of__ Float
59       raise ZeroDivisionError, "divided by 0"
60     end
61     
62     a.divmod b
63   end
64   
65   def div(other)
66     raise FloatDomainError, "NaN" if self == 0 && other.__kind_of__(Float) && other == 0
67     b, a = math_coerce other
68     (a / b).floor
69   end
71   def quo(other)
72     if other.__kind_of__ Integer
73       self / Float(other)
74     else
75       b, a = math_coerce other
76       a / b
77     end
78   end
80   def <(other)
81     b, a = math_coerce other, :compare_error
82     a < b
83   end
84   
85   def <=(other)
86     b, a = math_coerce other, :compare_error
87     a <= b
88   end
89   
90   def >(other)
91     b, a = math_coerce other, :compare_error
92     a > b
93   end
94   
95   def >=(other)
96     b, a = math_coerce other, :compare_error
97     a >= b
98   end
100   def ==(other)
101     return true if other.equal?(self)
102     !!(other == self)
103   end
104   
105   def <=>(other)
106     begin
107       b, a = math_coerce other, :compare_error
108       return a <=> b
109     rescue ArgumentError
110       return nil
111     end
112   end
113   
114   def truncate
115     Float(self).truncate
116   end
118   # Delegate #to_int to #to_i in subclasses
119   def to_int
120     self.to_i
121   end
122   
123   # Delegate #modulp to #% in subclasses
124   def modulo(other)
125     self % other
126   end
128   def integer?
129     false
130   end
132   def zero?
133     self == 0
134   end
136   def nonzero?
137     zero? ? nil : self
138   end
140   def round
141     self.to_f.round
142   end
143   
144   def abs
145     self < 0 ? -self : self
146   end
148   def floor
149     int = self.to_i
150     if self == int or self > 0
151       int
152     else
153       int - 1
154     end
155   end
157   def ceil
158     int = self.to_i
159     if self == int or self < 0
160       int
161     else
162       int + 1
163     end
164   end
166   def remainder(other)
167     b, a = math_coerce other
168     mod = a % b
170     if mod != 0 && (a < 0 && b > 0 || a > 0 && b < 0)
171       mod - b
172     else
173       mod
174     end
175   end
177   #--
178   # We deviate from MRI behavior here because we ensure that Fixnum op Bignum
179   # => Bignum (possibly normalized to Fixnum)
180   #
181   # Note these differences on MRI, where a is a Fixnum, b is a Bignum
182   #
183   #   a.coerce b => [Float, Float]
184   #   b.coerce a => [Bignum, Bignum]
185   #++
187   def coerce(other)
188     Ruby.primitive :numeric_coerce
189     [Float(other), Float(self)]
190   end
192   ##
193   # This method mimics the semantics of MRI's do_coerce function
194   # in numeric.c. Note these differences between it and #coerce:
195   #
196   #   1.2.coerce("2") => [2.0, 1.2]
197   #   1.2 + "2" => TypeError: String can't be coerced into Float
198   #
199   # See also Integer#coerce
201   def math_coerce(other, error=:coerce_error)
202     begin
203       values = other.coerce(self)
204     rescue
205       send error, other
206     end
207     
208     unless values.__kind_of__(Array) && values.length == 2
209       raise TypeError, "coerce must return [x, y]"
210     end
212     return values[1], values[0]
213   end
214   private :math_coerce
215   
216   def coerce_error(other)
217     raise TypeError, "#{other.class} can't be coerced into #{self.class}"
218   end
219   private :coerce_error
220   
221   def compare_error(other)
222     raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
223   end
224   private :compare_error
226   def step(limit, step=1, &block)
227     raise ArgumentError, "step cannot be 0" if step == 0
228     limit,step = step.coerce(limit)
229     # FIXME: why is this not covered by the block parameter above?
230     raise LocalJumpError, "no block given" unless block_given?
231     idx,step = step.coerce(self)
232     cmp = step > 0 ? :<= : :>=
233     while (idx.send(cmp,limit))
234       yield(idx)
235       idx += step
236     end
237     return self
238   rescue TypeError => e
239     raise ArgumentError, e.message
240   end