summaryrefslogtreecommitdiff
path: root/lib/mathn.rb
blob: 61de1ff9aca740e8aea24f58e379dd26eea2556a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
##
# = mathn
#
# mathn is a library for changing the way Ruby does math.
#
# == Usage
#
# To start using this library, simply:
#
#   require "mathn"
#
# This will change the way division works for Fixnums, specifically
#
#   3 / 2
#
# will return (3/2) instead of the usual 1.
#
# == Copyright
#
# Author: Keiju ISHITSUKA(SHL Japan Inc.)
#
# --
# $Release Version: 0.5 $
# $Revision: 1.1.1.1.4.1 $

require "cmath.rb"
require "matrix.rb"
require "prime.rb"

require "mathn/rational"
require "mathn/complex"

unless defined?(Math.exp!)
  Object.instance_eval{remove_const :Math}
  Math = CMath
end

class Fixnum
  remove_method :/
  alias / quo

  alias power! ** unless method_defined? :power!

  ##
  # exponentiate by +other+
  def ** (other)
    if self < 0 && other.round != other
      Complex(self, 0.0) ** other
    else
      power!(other)
    end
  end

end

class Bignum
  remove_method :/
  alias / quo

  alias power! ** unless method_defined? :power!

  ##
  # exponentiate by +other+
  def ** (other)
    if self < 0 && other.round != other
      Complex(self, 0.0) ** other
    else
      power!(other)
    end
  end

end

class Rational
  remove_method :**

  ##
  # exponentiate by +other+
  def ** (other)
    if other.kind_of?(Rational)
      other2 = other
      if self < 0
        return Complex(self, 0.0) ** other
      elsif other == 0
        return Rational(1,1)
      elsif self == 0
        return Rational(0,1)
      elsif self == 1
        return Rational(1,1)
      end

      npd = numerator.prime_division
      dpd = denominator.prime_division
      if other < 0
        other = -other
        npd, dpd = dpd, npd
      end

      for elm in npd
        elm[1] = elm[1] * other
        if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
          return Float(self) ** other2
        end
        elm[1] = elm[1].to_i
      end

      for elm in dpd
        elm[1] = elm[1] * other
        if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
          return Float(self) ** other2
        end
        elm[1] = elm[1].to_i
      end

      num = Integer.from_prime_division(npd)
      den = Integer.from_prime_division(dpd)

      Rational(num,den)

    elsif other.kind_of?(Integer)
      if other > 0
        num = numerator ** other
        den = denominator ** other
      elsif other < 0
        num = denominator ** -other
        den = numerator ** -other
      elsif other == 0
        num = 1
        den = 1
      end
      Rational(num, den)
    elsif other.kind_of?(Float)
      Float(self) ** other
    else
      x , y = other.coerce(self)
      x ** y
    end
  end
end

module Math
  remove_method(:sqrt)

  ##
  # compute the square root of +a+
  def sqrt(a)
    if a.kind_of?(Complex)
      abs = sqrt(a.real*a.real + a.imag*a.imag)
#      if not abs.kind_of?(Rational)
#        return a**Rational(1,2)
#      end
      x = sqrt((a.real + abs)/Rational(2))
      y = sqrt((-a.real + abs)/Rational(2))
#      if !(x.kind_of?(Rational) and y.kind_of?(Rational))
#        return a**Rational(1,2)
#      end
      if a.imag >= 0
        Complex(x, y)
      else
        Complex(x, -y)
      end
    elsif a.respond_to?(:nan?) and a.nan?
      a
    elsif a >= 0
      rsqrt(a)
    else
      Complex(0,rsqrt(-a))
    end
  end

  def rsqrt(a) # :nodoc:
    if a.kind_of?(Float)
      sqrt!(a)
    elsif a.kind_of?(Rational)
      rsqrt(a.numerator)/rsqrt(a.denominator)
    else
      src = a
      max = 2 ** 32
      byte_a = [src & 0xffffffff]
      # ruby's bug
      while (src >= max) and (src >>= 32)
        byte_a.unshift src & 0xffffffff
      end

      answer = 0
      main = 0
      side = 0
      for elm in byte_a
        main = (main << 32) + elm
        side <<= 16
        if answer != 0
          if main * 4  < side * side
            applo = main.div(side)
          else
            applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1
          end
        else
          applo = sqrt!(main).to_i + 1
        end

        while (x = (side + applo) * applo) > main
          applo -= 1
        end
        main -= x
        answer = (answer << 16) + applo
        side += applo * 2
      end
      if main == 0
        answer
      else
        sqrt!(a)
      end
    end
  end

  class << self
    remove_method(:sqrt)
  end
  module_function :sqrt
  module_function :rsqrt
end

class Float
  alias power! **

  ##
  # exponentiate by +other+
  def ** (other)
    if self < 0 && other.round != other
      Complex(self, 0.0) ** other
    else
      power!(other)
    end
  end

end