ruby-calc 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +52 -0
- data/.rubocop_todo.yml +11 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +497 -0
- data/Rakefile +23 -0
- data/bin/console +10 -0
- data/bin/install_calc.sh +18 -0
- data/bin/makefile.patch +48 -0
- data/bin/setup +7 -0
- data/bin/todo.rb +374 -0
- data/ext/calc/c.c +775 -0
- data/ext/calc/calc.c +192 -0
- data/ext/calc/calc.h +71 -0
- data/ext/calc/config.c +239 -0
- data/ext/calc/convert.c +193 -0
- data/ext/calc/extconf.rb +29 -0
- data/ext/calc/math_error.c +72 -0
- data/ext/calc/numeric.c +623 -0
- data/ext/calc/q.c +2755 -0
- data/lib/calc.rb +214 -0
- data/lib/calc/c.rb +371 -0
- data/lib/calc/import.rb +6 -0
- data/lib/calc/numeric.rb +208 -0
- data/lib/calc/q.rb +628 -0
- data/lib/calc/version.rb +3 -0
- data/ruby-calc.gemspec +29 -0
- metadata +167 -0
data/lib/calc.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require "calc/version"
|
2
|
+
require "calc/calc"
|
3
|
+
require "calc/numeric"
|
4
|
+
require "calc/q"
|
5
|
+
require "calc/c"
|
6
|
+
|
7
|
+
module Calc
|
8
|
+
# builtins implemented as instance methods on Calc::Q or Calc::C
|
9
|
+
BUILTINS1 = %i(
|
10
|
+
abs acos acosh acot acoth acsc acsch agd appr arg asec asech asin asinh
|
11
|
+
atan atan2 atanh bernoulli bit bround btrunc catalan ceil cfappr cfsim char
|
12
|
+
cmp comb conj cos cosh cot coth csc csch den digit digits estr euler exp
|
13
|
+
fact factor fcnt fib floor frac frem gcd gcdrem gd highbit hypot ilog
|
14
|
+
ilog10 ilog2 im int inverse iroot iseven isimag isint ismult isodd isprime
|
15
|
+
isqrt isreal isrel issq jacobi lcm lcmfact lfactor ln log lowbit ltol meq
|
16
|
+
minv mmin mne mod near nextcand nextprime norm num perm pfact pix places
|
17
|
+
pmod popcnt power prevcand prevprime ptest quo quomod re root round scale
|
18
|
+
sec sech sgn sin sinh sqrt tan tanh trunc xor
|
19
|
+
).freeze
|
20
|
+
|
21
|
+
# builtins implemented as module methods on Calc
|
22
|
+
BUILTINS2 = %i(
|
23
|
+
avg config freebernoulli freeeuler hean hnrmod max min pi polar ssq sum
|
24
|
+
version
|
25
|
+
).freeze
|
26
|
+
|
27
|
+
ALL_BUILTINS = BUILTINS1 + BUILTINS2
|
28
|
+
|
29
|
+
# module versions of instance builtins; implemented by turning the first
|
30
|
+
# argument into the right class and calling the instance method
|
31
|
+
class << self
|
32
|
+
BUILTINS1.each do |f|
|
33
|
+
define_method f do |*args|
|
34
|
+
x = args.shift
|
35
|
+
if x.is_a?(Calc::Q) || x.is_a?(Calc::C)
|
36
|
+
x.__send__(f, *args)
|
37
|
+
elsif x.is_a?(Complex)
|
38
|
+
Calc::C(x).__send__(f, *args)
|
39
|
+
else
|
40
|
+
Calc::Q(x).__send__(f, *args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.Q(*args) # rubocop:disable Style/MethodName
|
47
|
+
Q.new(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.C(*args) # rubocop:disable Style/MethodName
|
51
|
+
C.new(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Average (arithmetic mean)
|
55
|
+
#
|
56
|
+
# Any number of numeric arguments can be provided. Returns the sum of all
|
57
|
+
# values divided by the number of values. If no values are provided, returns
|
58
|
+
# nil.
|
59
|
+
#
|
60
|
+
# @return [Calc::Q,Calc::C]
|
61
|
+
# @example
|
62
|
+
# Calc.avg(1, 2, 3) #=> Calc::Q(2)
|
63
|
+
# Calc.avg(4, Calc::C(2, 2)) #=> Calc::C(3+1i)
|
64
|
+
def self.avg(*args)
|
65
|
+
args.flatten!
|
66
|
+
return nil if args.none?
|
67
|
+
args.map { |n| to_calc_x(n) }.inject(:+) / args.size
|
68
|
+
end
|
69
|
+
|
70
|
+
# Harmonic mean
|
71
|
+
#
|
72
|
+
# Returns zero if any of the provded values is zero. Returns nil if no
|
73
|
+
# values are provided. Otherwise returns the harmonic mean of the given
|
74
|
+
# values.
|
75
|
+
#
|
76
|
+
# @return [Calc::Q,Calc::C]
|
77
|
+
# Calc.hmean(1, 2, 4) #=> Calc::Q(12/7)
|
78
|
+
# Calc.hmean(2, Complex(0, 2)) #=> Calc::C(2+2i)
|
79
|
+
def self.hmean(*args)
|
80
|
+
args.flatten!
|
81
|
+
return nil if args.none?
|
82
|
+
return Q::ZERO if args.detect(&:zero?)
|
83
|
+
args.size / args.map { |n| to_calc_x(n) }.map(&:inverse).inject(:+)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Maximum from provided values.
|
87
|
+
#
|
88
|
+
# Each argument must be convertable to Calc::Q. If no values, returns nil.
|
89
|
+
#
|
90
|
+
# @return [Calc::Q]
|
91
|
+
# @example
|
92
|
+
# Calc.max(5, 3, 7, 2, 9) #=> Calc::Q(9)
|
93
|
+
def self.max(*args)
|
94
|
+
args.compact.map { |n| Calc::Q(n) }.max
|
95
|
+
end
|
96
|
+
|
97
|
+
# Minimum from provided values
|
98
|
+
#
|
99
|
+
# Each argument must be convertable to Calc::Q. If no values, returns nil.
|
100
|
+
#
|
101
|
+
# @return [Calc::Q]
|
102
|
+
# @example
|
103
|
+
# Calc.min(5, 3, 7, 2, 9) #=> Calc::Q(2)
|
104
|
+
def self.min(*args)
|
105
|
+
args.compact.map { |n| Calc::Q(n) }.min
|
106
|
+
end
|
107
|
+
|
108
|
+
# Evaluate a polynomial
|
109
|
+
#
|
110
|
+
# First case:
|
111
|
+
# poly(a_0, a_1, ..., a_n, x)
|
112
|
+
# returns:
|
113
|
+
# a_n + (a_n-1 + ... + (a_1 + a_0 * x) * x ..) * x
|
114
|
+
# In particular:
|
115
|
+
# poly(a, x) -> a
|
116
|
+
# poly(a, b, x) -> b + a * x
|
117
|
+
# poly(a, b, c, x) -> c + (b + a * x) * x
|
118
|
+
# or a*x**2 + b*x + c
|
119
|
+
#
|
120
|
+
# In the second case, the first parameter is an array of coefficients, ie:
|
121
|
+
# poly([a_0, a_1, ... a_n], x)
|
122
|
+
# returns:
|
123
|
+
# a_0 + (a_n-1 + (a_2 + ... a_n * x) * x)
|
124
|
+
# Note that the order of coeffecients is reverse of the first case.
|
125
|
+
#
|
126
|
+
# If one or more elements of clist is another array, and there is more than
|
127
|
+
# one argument (x, y, ...) the coefficient corresponding to such an element
|
128
|
+
# is the value of the poly for that list and the next argument in x, y, ...
|
129
|
+
# For example:
|
130
|
+
# poly([[a, b, c], [d, e], f], x, y)
|
131
|
+
# Returns:
|
132
|
+
# (a + b * y + c * y^2) + (d + e * y) * x + f * x^2
|
133
|
+
#
|
134
|
+
# For more explanation and examples on how the nested arrays works, see
|
135
|
+
# "help poly" bearning in mind that a calc list is equivament to a ruby
|
136
|
+
# array.
|
137
|
+
#
|
138
|
+
# @return [Calc::Numeric]
|
139
|
+
# @example
|
140
|
+
# # 2 * 7**2 + 3 * 7 + 5
|
141
|
+
# Calc.poly(2, 3, 5, 7) #=> Calc::Q(124)
|
142
|
+
def self.poly(*args)
|
143
|
+
raise ArgumentError, "Need at least one argument for poly" if args.none?
|
144
|
+
if args.first.respond_to?(:each)
|
145
|
+
# second case
|
146
|
+
clist = args.shift
|
147
|
+
evalpoly(clist, args.flatten, 0)
|
148
|
+
else
|
149
|
+
# first case
|
150
|
+
x = to_calc_x(args.pop)
|
151
|
+
return x if args.none?
|
152
|
+
args.reverse.each_with_index.map { |coeff, i| to_calc_x(coeff) * x**i }.reduce(:+)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# evalpoly and evp are modelled on functions of the same name in libcalc,
|
157
|
+
# which we can't use because they use VALUE and LIST types. because the
|
158
|
+
# libcalc versions use doubly linked lists, the ruby versions has to pass
|
159
|
+
# around an index to the coeffecients array instead.
|
160
|
+
def self.evalpoly(clist, lp, x)
|
161
|
+
return nil if clist.none?
|
162
|
+
if lp[x].nil?
|
163
|
+
if clist.first.respond_to?(:each)
|
164
|
+
evalpoly(clist.first, lp, x + 1)
|
165
|
+
else
|
166
|
+
to_calc_x(clist.first)
|
167
|
+
end
|
168
|
+
else
|
169
|
+
evp(clist, lp, x)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
private_class_method :evalpoly
|
173
|
+
|
174
|
+
def self.evp(clist, lp, x)
|
175
|
+
clist.reverse.reduce(Q::ZERO) do |vres, v|
|
176
|
+
(vres * lp[x]) + if v.respond_to?(:each)
|
177
|
+
evalpoly(v, lp, x + 1) || Q::ZERO
|
178
|
+
else
|
179
|
+
to_calc_x(v)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
private_class_method :evp
|
184
|
+
|
185
|
+
# Returns the sum of squares.
|
186
|
+
#
|
187
|
+
# Nil values are ignored. If any argument is am array, it contributes
|
188
|
+
# the sum of squares of its contents recursively.
|
189
|
+
#
|
190
|
+
# @return [Calc::C,Calc::Q]
|
191
|
+
# @raise [ArgumentError] if any argument can't be converted to a Calc class
|
192
|
+
# @example
|
193
|
+
# Calc.ssq(1, 2, 3) #=> Calc::Q(14)
|
194
|
+
# Calc.ssq(1+2i, 3-4i, 5) #=> Calc::C(15-20i)
|
195
|
+
def self.ssq(*args)
|
196
|
+
args.flatten.map { |term| to_calc_x(term)**2 }.inject(:+)
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.sum(*args)
|
200
|
+
args.flatten.map { |t| to_calc_x(t) }.compact.inject(:+)
|
201
|
+
end
|
202
|
+
|
203
|
+
# returns a Calc::Q or Calc::C object, converting if necessary
|
204
|
+
def self.to_calc_x(n)
|
205
|
+
if n.is_a?(Calc::Q) || n.is_a?(Calc::C)
|
206
|
+
n
|
207
|
+
elsif n.is_a?(Complex)
|
208
|
+
Calc::C(n)
|
209
|
+
else
|
210
|
+
Calc::Q(n)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
private_class_method :to_calc_x
|
214
|
+
end
|
data/lib/calc/c.rb
ADDED
@@ -0,0 +1,371 @@
|
|
1
|
+
module Calc
|
2
|
+
class C
|
3
|
+
# Returns the absolute value of a complex number. For purely real or
|
4
|
+
# purely imaginary values, returns the absolute value of the non-zero
|
5
|
+
# part. Otherwise returns the absolute part of its complex form within
|
6
|
+
# the specified accuracy.
|
7
|
+
#
|
8
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
9
|
+
# @return [Calc::Q]
|
10
|
+
# @example
|
11
|
+
# Calc::C(-1).abs #=> Calc::Q(1)
|
12
|
+
# Calc::C(3,-4).abs #=> Calc::Q(5)
|
13
|
+
# Calc::C(4,5).abs("1e-5") #=> Calc::Q(6.40312)
|
14
|
+
def abs(*args)
|
15
|
+
# see absvalue() in value.c
|
16
|
+
re.hypot(im, *args)
|
17
|
+
end
|
18
|
+
alias magnitude abs
|
19
|
+
|
20
|
+
# Approximate numbers of multiples of a specific number.
|
21
|
+
#
|
22
|
+
# c.appr(y,z) is equivalent to c.re.appr(y,z) + c.im.appr(y,z) * Calc::C(0,1)
|
23
|
+
def appr(*args)
|
24
|
+
q1 = re.appr(*args)
|
25
|
+
q2 = im.appr(*args)
|
26
|
+
if q2.zero?
|
27
|
+
q1
|
28
|
+
else
|
29
|
+
C.new(q1, q2)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the argument (the angle or phase) of a complex number in radians.
|
34
|
+
#
|
35
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
36
|
+
# @return [Calc::Q]
|
37
|
+
# @example
|
38
|
+
# Calc::C(1,0).arg #=> 0
|
39
|
+
# Calc::C(-1,0).arg #=> -pi
|
40
|
+
# Calc::C(1,1).arg #=> Calc::Q(0.78539816339744830962)
|
41
|
+
def arg(*args)
|
42
|
+
# see f_arg() in func.c
|
43
|
+
im.atan2(re, *args)
|
44
|
+
end
|
45
|
+
alias angle arg
|
46
|
+
alias phase arg
|
47
|
+
|
48
|
+
# Round real and imaginary parts to the specified number of binary digits
|
49
|
+
#
|
50
|
+
# @return [Calc::C,Calc::Q]
|
51
|
+
# @param places [Integer] number of binary places to round to (default 0)
|
52
|
+
# @param rns [Integer] rounding flags (default Calc.config(:round))
|
53
|
+
# @example
|
54
|
+
# Calc::C("7/32","-7/32").bround(3) #=> Calc::C(0.25-0.25i)
|
55
|
+
def bround(*args)
|
56
|
+
q1 = re.bround(*args)
|
57
|
+
q2 = im.bround(*args)
|
58
|
+
if q2.zero?
|
59
|
+
q1
|
60
|
+
else
|
61
|
+
C.new(q1, q2)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Complex conjugate
|
66
|
+
#
|
67
|
+
# Returns the complex conjugate of self (same real part and same imaginary
|
68
|
+
# part but with opposite sign)
|
69
|
+
#
|
70
|
+
# @return [Calc::C]
|
71
|
+
# @example
|
72
|
+
# Calc::C(3,3).conj #=> Calc::C(3-3i)
|
73
|
+
def conj
|
74
|
+
C.new(re, -im)
|
75
|
+
end
|
76
|
+
alias conjugate conj
|
77
|
+
|
78
|
+
# Trigonometric cotangent
|
79
|
+
#
|
80
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
81
|
+
# @return [Calc::C]
|
82
|
+
# @example
|
83
|
+
# Calc::C(2,3).cot #=> Calc::C(~-0.00373971037633695666-~0.99675779656935831046i)
|
84
|
+
def cot(*args)
|
85
|
+
# see f_cot() in func.c
|
86
|
+
cos(*args) / sin(*args)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Hyperbolic cotangent
|
90
|
+
#
|
91
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
92
|
+
# @return [Calc::C]
|
93
|
+
# @example
|
94
|
+
# Calc::C(2,3).coth #=> Calc::C(~1.03574663776499539611+~0.01060478347033710175i)
|
95
|
+
def coth(*args)
|
96
|
+
# see f_coth() in func.c
|
97
|
+
cosh(*args) / sinh(*args)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Trigonometric cosecant
|
101
|
+
#
|
102
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
103
|
+
# @return [Calc::C]
|
104
|
+
# @example
|
105
|
+
# Calc::C(2,3).csc #=> Calc::C(~0.09047320975320743981+~0.04120098628857412646i)
|
106
|
+
def csc(*args)
|
107
|
+
# see f_csc() in func.c
|
108
|
+
sin(*args).inverse
|
109
|
+
end
|
110
|
+
|
111
|
+
# Hyperbolic cosecant
|
112
|
+
#
|
113
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
114
|
+
# @return [Calc::C]
|
115
|
+
# @example
|
116
|
+
# Calc::C(2,3).csch #=> Calc::C(~-0.27254866146294019951-~0.04030057885689152187i)
|
117
|
+
def csch(*args)
|
118
|
+
# see f_csch() in func.c
|
119
|
+
sinh(*args).inverse
|
120
|
+
end
|
121
|
+
|
122
|
+
# Denominator of a complex number
|
123
|
+
#
|
124
|
+
# The denominator is the lowest common denominator of the real and
|
125
|
+
# imaginary parts
|
126
|
+
#
|
127
|
+
# @return [Calc::Q]
|
128
|
+
# @example
|
129
|
+
# Calc::C("1/2", "2/3").denominator #=> Calc::Q(6)
|
130
|
+
def denominator
|
131
|
+
re.den.lcm(im.den)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a string which if evaluated creates a new object with the original value
|
135
|
+
#
|
136
|
+
# @return [String]
|
137
|
+
# @example
|
138
|
+
# Calc::C(0.5,-2).estr #=> "Calc::C(Calc::Q(1,2),-2)"
|
139
|
+
def estr
|
140
|
+
s = self.class.name
|
141
|
+
s << "("
|
142
|
+
s << (re.int? ? re.to_s : re.estr)
|
143
|
+
s << "," + (im.int? ? im.to_s : im.estr) unless im.zero?
|
144
|
+
s << ")"
|
145
|
+
s
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns true if self has integer real part and zero imaginary part
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
# @example
|
152
|
+
# Calc::C(1,1).int? #=> false
|
153
|
+
# Calc::C(1,0).int? #=> true
|
154
|
+
def int?
|
155
|
+
im.zero? ? re.int? : false
|
156
|
+
end
|
157
|
+
|
158
|
+
alias imaginary im
|
159
|
+
|
160
|
+
def iseven
|
161
|
+
even? ? Q::ONE : Q::ZERO
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns 1 if the number is imaginary (zero real part and non-zero
|
165
|
+
# imaginary part) otherwise returns 0. See also [imag?].
|
166
|
+
#
|
167
|
+
# @return [Calc::Q]
|
168
|
+
# @example
|
169
|
+
# Calc::C(0,1).isimag #=> Calc::Q(1)
|
170
|
+
# Calc::C(1,1).isimag #=> Calc::Q(0)
|
171
|
+
def isimag
|
172
|
+
imag? ? Q::ONE : Q::ZERO
|
173
|
+
end
|
174
|
+
|
175
|
+
def isodd
|
176
|
+
odd? ? Q::ONE : Q::ZERO
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns 1 if the number has zero imaginary part, otherwise returns 0.
|
180
|
+
# See also [real?].
|
181
|
+
#
|
182
|
+
# @return [Calc::Q]
|
183
|
+
# @example
|
184
|
+
# Calc::C(1,1).isreal #=> Calc::Q(0)
|
185
|
+
# Calc::C(1,0).isreal #=> Calc::Q(1)
|
186
|
+
def isreal
|
187
|
+
real? ? Q::ONE : Q::ZERO
|
188
|
+
end
|
189
|
+
|
190
|
+
# Computes the remainder for an integer quotient
|
191
|
+
#
|
192
|
+
# Result is equivalent to applying the mod function separately to the real
|
193
|
+
# and imaginary parts.
|
194
|
+
#
|
195
|
+
# @param y [Integer]
|
196
|
+
# @param r [Integer] (optional) rounding mode (see "help mod")
|
197
|
+
# @return [Calc::C]
|
198
|
+
# @example
|
199
|
+
# Calc::C(0, 11).mod(5) #=> Calc::C(1i)
|
200
|
+
def mod(*args)
|
201
|
+
q1 = re.mod(*args)
|
202
|
+
q2 = im.mod(*args)
|
203
|
+
if q2.zero?
|
204
|
+
q1
|
205
|
+
else
|
206
|
+
Calc::C(q1, q2)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Numerator of a complex number
|
211
|
+
#
|
212
|
+
# @return [Calc::C]
|
213
|
+
# @example
|
214
|
+
# Calc::C("1/2", "2/3").numerator #=> Calc::C(3+4i)
|
215
|
+
def numerator
|
216
|
+
C.new(re.num * denominator / re.den, im.num * denominator / im.den)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Returns the real part of the value as a rational
|
220
|
+
#
|
221
|
+
# If this number is non-imaginary, equivalent to re.rationalize(eps).
|
222
|
+
#
|
223
|
+
# Note that this method exists for ruby Numeric compatibility. Libcalc has
|
224
|
+
# an alternative approximation method with different semantics, see `appr`.
|
225
|
+
#
|
226
|
+
# @param eps [Float,Rational]
|
227
|
+
# @return [Calc::Q]
|
228
|
+
# @example
|
229
|
+
# Calc::C("1/3", 0).rationalize #=> Calc::Q(1/3)
|
230
|
+
# Calc::C(1, 2).rationalize # RangeError
|
231
|
+
def rationalize(eps = nil)
|
232
|
+
raise RangeError, "Can't convert #{ self } into Rational" if im.nonzero?
|
233
|
+
re.rationalize(eps)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Round real and imaginary parts to the specified number of decimal digits
|
237
|
+
#
|
238
|
+
# @return [Calc::C,Calc::Q]
|
239
|
+
# @param places [Integer] number of decimal places to round to (default 0)
|
240
|
+
# @param rns [Integer] rounding flags (default Calc.config(:round))
|
241
|
+
# Calc::C("7/32","-7/32").round(3) #=> Calc::C(0.218-0.219i)
|
242
|
+
def round(*args)
|
243
|
+
q1 = re.round(*args)
|
244
|
+
q2 = im.round(*args)
|
245
|
+
if q2.zero?
|
246
|
+
q1
|
247
|
+
else
|
248
|
+
C.new(q1, q2)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Trigonometric secant
|
253
|
+
#
|
254
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
255
|
+
# @return [Calc::C]
|
256
|
+
# @example
|
257
|
+
# Calc::C(2,3).sec #=> Calc::C(~-0.04167496441114427005+~0.09061113719623759653i)
|
258
|
+
def sec(*args)
|
259
|
+
# see f_sec() in func.c
|
260
|
+
cos(*args).inverse
|
261
|
+
end
|
262
|
+
|
263
|
+
# Hyperbolic secant
|
264
|
+
#
|
265
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
266
|
+
# @return [Calc::C]
|
267
|
+
# @example
|
268
|
+
# Calc::C(2,3).sech #=> Calc::C(~-0.26351297515838930964-~0.03621163655876852087i)
|
269
|
+
def sech(*args)
|
270
|
+
# see f_sech() in func.c
|
271
|
+
cosh(*args).inverse
|
272
|
+
end
|
273
|
+
|
274
|
+
# Trigonometric tangent
|
275
|
+
#
|
276
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
277
|
+
# @return [Calc::C]
|
278
|
+
# @example
|
279
|
+
# Calc::C(1,2).tan #=> Calc::C(~-0.00376402564150424829+~1.00323862735360980145i)
|
280
|
+
def tan(*args)
|
281
|
+
# see f_tan() in func.c
|
282
|
+
sin(*args) / cos(*args)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Hyperbolic tangent
|
286
|
+
#
|
287
|
+
# @param eps [Calc::Q] (optional) calculation accuracy
|
288
|
+
# @return [Calc::C]
|
289
|
+
# @example
|
290
|
+
# Calc::C(1,2).tanh #=> Calc::C(~0.96538587902213312428-~0.00988437503832249372i)
|
291
|
+
def tanh(*args)
|
292
|
+
# see f_tanh() in func.c
|
293
|
+
sinh(*args) / cosh(*args)
|
294
|
+
end
|
295
|
+
|
296
|
+
# Converts a Calc::C object into a ruby Complex object
|
297
|
+
#
|
298
|
+
# @return [Complex]
|
299
|
+
# @example
|
300
|
+
# Calc::C(2, 3).to_c #=> ((2/1)+(3/1)*i)
|
301
|
+
def to_c
|
302
|
+
Complex(re.to_r, im.to_r)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Convert a complex number with zero imaginary part into a ruby Float
|
306
|
+
#
|
307
|
+
# @return [Float]
|
308
|
+
# @raise [RangeError] if imaginary part is non-zero
|
309
|
+
# @example
|
310
|
+
# Calc::C("2/3", 0).to_f #=> 0.6666666666666666
|
311
|
+
def to_f
|
312
|
+
raise RangeError, "can't convert #{ self } into Float" if im.nonzero?
|
313
|
+
re.to_f
|
314
|
+
end
|
315
|
+
|
316
|
+
# Convert a wholly real number to an integer.
|
317
|
+
#
|
318
|
+
# Note that the return value is a ruby Fixnum or Bignum. If you want to
|
319
|
+
# convert to an integer but have the result be a `Calc::Q` object, use
|
320
|
+
# `trunc` or `round`.
|
321
|
+
#
|
322
|
+
# @return [Fixnum,Bugnum]
|
323
|
+
# @raise [RangeError] if imaginary part is non-zero
|
324
|
+
# @example
|
325
|
+
# Calc::C(2, 0).to_i #=> 2
|
326
|
+
# Calc::C(2, 2).to_i # RangeError
|
327
|
+
def to_i
|
328
|
+
raise RangeError, "can't convert #{ self } into Integer" if im.nonzero?
|
329
|
+
re.to_i
|
330
|
+
end
|
331
|
+
|
332
|
+
# Convert a complex number with zero imaginary part into a ruby Rational
|
333
|
+
#
|
334
|
+
# @return [Rational]
|
335
|
+
# @raise [RangeError] if imaginary part is non-zero
|
336
|
+
# @example
|
337
|
+
# Calc::C("2/3", 0).to_r #=> (2/3)
|
338
|
+
def to_r
|
339
|
+
raise RangeError, "can't convert #{ self } into Rational" if im.nonzero?
|
340
|
+
re.to_r
|
341
|
+
end
|
342
|
+
|
343
|
+
def to_s(*args)
|
344
|
+
if im.zero?
|
345
|
+
re.to_s(*args)
|
346
|
+
elsif re.zero?
|
347
|
+
imag_part(im, *args)
|
348
|
+
elsif im > 0
|
349
|
+
re.to_s(*args) + "+" + imag_part(im, *args)
|
350
|
+
else
|
351
|
+
re.to_s(*args) + "-" + imag_part(im.abs, *args)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def inspect
|
356
|
+
"Calc::C(#{ self })"
|
357
|
+
end
|
358
|
+
|
359
|
+
# aliases for compatibility with ruby Complex
|
360
|
+
alias integer? int?
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
# for formatting imaginary parts; if a fraction, put the "i" after the
|
365
|
+
# denominator (eg 2i/3). otherwise it goes at the end (eg 0.5i).
|
366
|
+
def imag_part(number, *args)
|
367
|
+
string = number.to_s(*args)
|
368
|
+
string.insert(string.index("/") || -1, "i")
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|