ruby-calc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,193 @@
1
+ #include "calc.h"
2
+
3
+ /* attempt to convert a bignum a long then to Calc::Q using itoq().
4
+ * NUM2LONG will raise a RangeError if arg doesn't fit in a long */
5
+ static VALUE
6
+ bignum_to_calc_q_via_long(VALUE arg)
7
+ {
8
+ return wrap_number(itoq(NUM2LONG(arg)));
9
+ }
10
+
11
+ /* handles exceptions during via_long. convert the arg to a string, then
12
+ * Calc::Q using str2q() */
13
+ static VALUE
14
+ bignum_to_calc_q_via_string(VALUE arg, VALUE e)
15
+ {
16
+ VALUE string;
17
+
18
+ if (!rb_obj_is_kind_of(e, rb_eRangeError)) {
19
+ /* something other than RangeError; re-raise it */
20
+ rb_exc_raise(e);
21
+ }
22
+ string = rb_funcall(arg, rb_intern("to_s"), 0);
23
+ return wrap_number(str2q(StringValueCStr(string)));
24
+ }
25
+
26
+ /* convert a Bignum to Calc::Q
27
+ * first tries to convert via a long; if that raises an exception, convert via
28
+ * a string */
29
+ static VALUE
30
+ bignum_to_calc_q(VALUE arg)
31
+ {
32
+ return rb_rescue(&bignum_to_calc_q_via_long, arg, &bignum_to_calc_q_via_string, arg);
33
+ }
34
+
35
+ /* convert a ruby Rational to a NUMBER*. Since the denominator/numerator of
36
+ * the rational number could be too big for long, they are converted to NUMBER*
37
+ * first.
38
+ */
39
+ static NUMBER *
40
+ rational_to_number(VALUE arg)
41
+ {
42
+ NUMBER *qresult, *qnum, *qden;
43
+
44
+ qnum = value_to_number(rb_funcall(arg, rb_intern("numerator"), 0), 0);
45
+ qden = value_to_number(rb_funcall(arg, rb_intern("denominator"), 0), 0);
46
+ qresult = qqdiv(qnum, qden);
47
+ qfree(qnum);
48
+ qfree(qden);
49
+ return qresult;
50
+ }
51
+
52
+ /* converts a ruby value into a NUMBER*. Allowed types:
53
+ * - Fixnum
54
+ * - Bignum
55
+ * - Calc::Q
56
+ * - Rational
57
+ * - String (using libcalc str2q)
58
+ * - Float (will be converted to a Rational first)
59
+ *
60
+ * the caller is responsible for freeing the returned number. storing it in
61
+ * a Calc::Q is sufficient for the ruby GC to get it.
62
+ */
63
+ NUMBER *
64
+ value_to_number(VALUE arg, int string_allowed)
65
+ {
66
+ NUMBER *qresult;
67
+ VALUE num, tmp;
68
+
69
+ if (TYPE(arg) == T_FIXNUM) {
70
+ qresult = itoq(NUM2LONG(arg));
71
+ }
72
+ else if (TYPE(arg) == T_BIGNUM) {
73
+ num = bignum_to_calc_q(arg);
74
+ qresult = qlink((NUMBER *) DATA_PTR(num));
75
+ }
76
+ else if (CALC_Q_P(arg)) {
77
+ qresult = qlink((NUMBER *) DATA_PTR(arg));
78
+ }
79
+ else if (TYPE(arg) == T_RATIONAL) {
80
+ qresult = rational_to_number(arg);
81
+ }
82
+ else if (TYPE(arg) == T_FLOAT) {
83
+ tmp = rb_funcall(arg, rb_intern("to_r"), 0);
84
+ qresult = rational_to_number(tmp);
85
+ }
86
+ else if (string_allowed && TYPE(arg) == T_STRING) {
87
+ qresult = str2q(StringValueCStr(arg));
88
+ /* libcalc str2q allows a 0 denominator */
89
+ if (ziszero(qresult->den)) {
90
+ qfree(qresult);
91
+ rb_raise(rb_eZeroDivError, "division by zero");
92
+ }
93
+ }
94
+ else {
95
+ rb_raise(rb_eArgError, "%" PRIsVALUE " (%" PRIsVALUE ") can't be converted to Calc::Q",
96
+ arg, rb_obj_class(arg));
97
+ }
98
+ return qresult;
99
+ }
100
+
101
+ /* convert a ruby value into a COMPLEX*. Allowed types:
102
+ * - Complex
103
+ * - Any type allowed by value_to_number (except string).
104
+ *
105
+ * libcalc doesn't provide any way to convert a string to a complex number.
106
+ *
107
+ * caller is responseible for freeing the returned complex. storing it in
108
+ * a Calc::C is sufficient for the ruby GC to get it.
109
+ */
110
+ COMPLEX *
111
+ value_to_complex(VALUE arg)
112
+ {
113
+ COMPLEX *cresult;
114
+ VALUE real, imag;
115
+
116
+ if (CALC_C_P(arg)) {
117
+ cresult = clink((COMPLEX *) DATA_PTR(arg));
118
+ }
119
+ else if (TYPE(arg) == T_COMPLEX) {
120
+ real = rb_funcall(arg, rb_intern("real"), 0);
121
+ imag = rb_funcall(arg, rb_intern("imag"), 0);
122
+ cresult = qqtoc(value_to_number(real, 0), value_to_number(imag, 0));
123
+ }
124
+ else if (CALC_Q_P(arg)) {
125
+ cresult = qqtoc(DATA_PTR(arg), &_qzero_);
126
+ }
127
+ else if (FIXNUM_P(arg) || TYPE(arg) == T_BIGNUM || TYPE(arg) == T_RATIONAL
128
+ || TYPE(arg) == T_FLOAT) {
129
+ cresult = qqtoc(value_to_number(arg, 0), &_qzero_);
130
+ }
131
+ else {
132
+ rb_raise(rb_eArgError, "%" PRIsVALUE " (%" PRIsVALUE ") can't be converted to Calc::C",
133
+ arg, rb_obj_class(arg));
134
+ }
135
+
136
+ return cresult;
137
+ }
138
+
139
+ /* get a long out of a ruby VALUE. if it is fractional, or too big to be
140
+ * represented, raises an error */
141
+ long
142
+ value_to_long(VALUE v)
143
+ {
144
+ long l;
145
+ NUMBER *q;
146
+
147
+ if (FIXNUM_P(v)) {
148
+ return FIX2LONG(v);
149
+ }
150
+ q = value_to_number(v, 0);
151
+ if (qisfrac(q)) {
152
+ qfree(q);
153
+ rb_raise(rb_eTypeError, "Fraction (%" PRIsVALUE ") can't be converted to long", v);
154
+ }
155
+ if (zgtmaxlong(q->num)) {
156
+ qfree(q);
157
+ rb_raise(rb_eTypeError, "%" PRIsVALUE " is too large to convert to long", v);
158
+ }
159
+ l = qtoi(q);
160
+ qfree(q);
161
+ return l;
162
+ }
163
+
164
+ /* wrap a COMPLEX* into a ruby VALUE of class Calc::C (if there is a non-zero
165
+ * imaginary part) or Calc::Q (otherwise).
166
+ */
167
+ VALUE
168
+ wrap_complex(COMPLEX * c)
169
+ {
170
+ VALUE result;
171
+
172
+ if (cisreal(c)) {
173
+ result = cq_new();
174
+ DATA_PTR(result) = qlink(c->real);
175
+ comfree(c);
176
+ }
177
+ else {
178
+ result = cc_new();
179
+ DATA_PTR(result) = c;
180
+ }
181
+ return result;
182
+ }
183
+
184
+ /* wrap a NUMBER* into a ruby VALUE of class Calc::Q. */
185
+ VALUE
186
+ wrap_number(NUMBER * n)
187
+ {
188
+ VALUE result;
189
+
190
+ result = cq_new();
191
+ DATA_PTR(result) = n;
192
+ return result;
193
+ }
@@ -0,0 +1,29 @@
1
+ require "mkmf"
2
+
3
+ unless find_header("calc/cmath.h")
4
+ abort "calc/cmath.h is missing, please install calc development headers"
5
+ end
6
+
7
+ if RUBY_PLATFORM =~ /darwin/
8
+ # on macosx, detection of libcustcalc doesn't work. just assume it is
9
+ # required.
10
+ $libs = append_library($libs, "custcalc")
11
+ unless have_library("calc", "libcalc_call_me_first")
12
+ abort "can't find libcalc, please install calc"
13
+ end
14
+ # on macosx, i can't get the linker to use our version of math_error().
15
+ # this macro uses an alternative error hander based on setjmp/longjmp.
16
+ $defs << "-DJUMP_ON_MATH_ERROR"
17
+ else
18
+ # try libcalc by itself first - this will work if libcustcalc wasn't made,
19
+ # but will fail if it was
20
+ unless have_library("calc", "libcalc_call_me_first")
21
+ puts "trying again with -lcustcalc"
22
+ $libs = append_library($libs, "custcalc")
23
+ unless have_library("calc", "libcalc_call_me_first")
24
+ abort "can't find libcalc, please install calc"
25
+ end
26
+ end
27
+ end
28
+
29
+ create_makefile("calc/calc")
@@ -0,0 +1,72 @@
1
+ #include <stdarg.h>
2
+ #include "calc.h"
3
+
4
+ /* Document-class: Calc::MathError
5
+ *
6
+ * Exceptions of this class are raised whenever a libcalc function encounters
7
+ * an error.
8
+ */
9
+ VALUE e_MathError;
10
+
11
+ #ifdef JUMP_ON_MATH_ERROR
12
+
13
+ /* this is an alternative error handler used on systems when we can't tell
14
+ * the linker to use our version of math_error. uses setjmp to tell libcalc's
15
+ * default math_error to return here instead of exiting. a ruby exception
16
+ * is raised if the longjmp is made.
17
+ *
18
+ * the downside of this is that each c function which calls libcalc methods
19
+ * that /could/ call math_error needs to call this first. apart from the small
20
+ * performance hit, this is extra unwanted code in many functions. if I
21
+ * could find a way to tell the macosx linker to replace math_error() like
22
+ * it works on Linux, this method would not be necessary.
23
+ *
24
+ * unfortunately it is not possible to call this once (say, during definition
25
+ * of Calc::MathError) and except later C functions to be able to jump into it.
26
+ * crossing the ruby/C boundary like that causes crashes or hangs.
27
+ *
28
+ * on other systems, this function name is #defined to nothing.
29
+ */
30
+
31
+ void
32
+ setup_math_error(void)
33
+ {
34
+ int error;
35
+ VALUE mesg;
36
+
37
+ if ((error = setjmp(calc_matherr_jmpbuf)) != 0) {
38
+ mesg = rb_str_new2(calc_err_msg);
39
+ reinitialize();
40
+ rb_exc_raise(rb_exc_new3(e_MathError, mesg));
41
+ }
42
+ calc_use_matherr_jmpbuf = 1;
43
+ }
44
+
45
+ #else
46
+
47
+ /* provide our own version of math_error which raises a ruby exception
48
+ * instead of exiting.
49
+ *
50
+ * ideally this would call rb_raise, but you can't just pass a varargs list to
51
+ * another function (see http://c-faq.com/varargs/handoff.html).
52
+ * so this just does what rb_raise does internally.
53
+ */
54
+ void
55
+ math_error(char *fmt, ...)
56
+ {
57
+ va_list args;
58
+ VALUE mesg;
59
+
60
+ va_start(args, fmt);
61
+ mesg = rb_vsprintf(fmt, args);
62
+ va_end(args);
63
+ rb_exc_raise(rb_exc_new3(e_MathError, mesg));
64
+ }
65
+
66
+ #endif /* JUMP_ON_MATH_ERROR */
67
+
68
+ void
69
+ define_calc_math_error(VALUE m)
70
+ {
71
+ e_MathError = rb_define_class_under(m, "MathError", rb_eStandardError);
72
+ }
@@ -0,0 +1,623 @@
1
+ #include <calc.h>
2
+
3
+ /* Document-class: Calc::Numeric
4
+ *
5
+ * Parent class to the libraries numeric classes (Calc::Q and Calc::C)
6
+ */
7
+ VALUE cNumeric;
8
+
9
+ /* similar to trans_function, but for ln and log; the rational versions (qln,
10
+ * qlog) return wrong results for self < 0, so call the complex version in that
11
+ * case.
12
+ * ref: f_ln() and f_log() in calc's func.c
13
+ */
14
+ static VALUE
15
+ log_function(int argc, VALUE * argv, VALUE self, NUMBER * (fq) (NUMBER *, NUMBER *),
16
+ COMPLEX * (*fc) (COMPLEX *, NUMBER *))
17
+ {
18
+ VALUE epsilon, result;
19
+ NUMBER *qepsilon, *qself;
20
+ COMPLEX *cself;
21
+ setup_math_error();
22
+
23
+ if (rb_scan_args(argc, argv, "01", &epsilon) == 0) {
24
+ qepsilon = NULL;
25
+ }
26
+ else {
27
+ qepsilon = value_to_number(epsilon, 1);
28
+ }
29
+ if (CALC_Q_P(self)) {
30
+ qself = DATA_PTR(self);
31
+ if (!qisneg(qself) && !qiszero(qself)) {
32
+ result = cq_new();
33
+ DATA_PTR(result) = (*fq) (qself, qepsilon ? qepsilon : conf->epsilon);
34
+ }
35
+ else {
36
+ cself = comalloc();
37
+ qfree(cself->real);
38
+ cself->real = qlink(qself);
39
+ result = wrap_complex((*fc) (cself, qepsilon ? qepsilon : conf->epsilon));
40
+ comfree(cself);
41
+ }
42
+ }
43
+ else if (CALC_C_P(self)) {
44
+ cself = DATA_PTR(self);
45
+ result = wrap_complex((*fc) (cself, qepsilon ? qepsilon : conf->epsilon));
46
+ }
47
+ else {
48
+ rb_raise(e_MathError, "log_function called with invalid receiver");
49
+ }
50
+ if (qepsilon) {
51
+ qfree(qepsilon);
52
+ }
53
+ return result;
54
+ }
55
+
56
+ static VALUE
57
+ shift(VALUE self, VALUE other, BOOL rightshift)
58
+ {
59
+ NUMBER *qother;
60
+ long n;
61
+ setup_math_error();
62
+
63
+ qother = value_to_number(other, 0);
64
+ if (qisfrac(qother)) {
65
+ qfree(qother);
66
+ rb_raise(rb_eArgError, "shift by non-integer");
67
+ }
68
+ if (zge31b(qother->num)) {
69
+ qfree(qother);
70
+ rb_raise(rb_eArgError, "shift by too many bits");
71
+ }
72
+ n = qtoi(qother);
73
+ qfree(qother);
74
+ if (rightshift) {
75
+ n = -n;
76
+ }
77
+ if (CALC_Q_P(self)) {
78
+ return wrap_number(qshift(DATA_PTR(self), n));
79
+ }
80
+ return wrap_complex(c_shift(DATA_PTR(self), n));
81
+ }
82
+
83
+
84
+ NUMBER *
85
+ sign_of_int(int r)
86
+ {
87
+ return (r > 0) ? qlink(&_qone_) : (r < 0) ? qlink(&_qnegone_) : qlink(&_qzero_);
88
+ }
89
+
90
+ /* Left shift an integer by a given number of bits. This multiplies the number
91
+ * by the appropriate power of 2.
92
+ *
93
+ * @param n [Integer] number of bits to shift
94
+ * @return [Calc::C,Calc::Q]
95
+ * @raise [Calc::MathError] if self is a non-integer
96
+ * @raise [ArgumentError] if abs(n) is >= 2^31
97
+ * @example:
98
+ * Calc::Q(2) << 3 #=> Calc::Q(16)
99
+ * Calc::C(2, 3) << 3 #=> Calc::C(16+24i)
100
+ */
101
+ static VALUE
102
+ cn_shift_left(VALUE self, VALUE other)
103
+ {
104
+ return shift(self, other, FALSE);
105
+ }
106
+
107
+ /* Right shift an integer by a given number of bits. This multiplies the
108
+ * number by the appropriate power of 2. Low bits are truncated.
109
+ *
110
+ * @param n [Integer] number of bits to shift
111
+ * @return [Calc::C,Calc::Q]
112
+ * @raise [Calc::MathError] if self is a non-integer
113
+ * @raise [ArgumentError] if abs(n) is >= 2^31
114
+ * @example:
115
+ * Calc::Q(8) >> 2 #=> Calc::Q(2)
116
+ * Calc::C(8, 12) >> 2 #=> Calc::C(2+3i)
117
+ */
118
+ static VALUE
119
+ cn_shift_right(VALUE self, VALUE other)
120
+ {
121
+ return shift(self, other, TRUE);
122
+ }
123
+
124
+ /* Compare 2 values.
125
+ *
126
+ * If x and y are both real, returns -1, 0 or 1 according as x < y, x == y or
127
+ * x > y.
128
+ *
129
+ * If one or both of x and y are complex, returns a complex number composed
130
+ * of the real and imaginary parts being compared individually as above.
131
+ *
132
+ * @param y [Numeric] value to compare self to
133
+ * @example
134
+ * Calc::Q(3).cmp(4) #=> Calc::Q(-1)
135
+ * Calc::Q(3).cmp(4+4i) #=> Calc::C(-1-1i)
136
+ * Calc::C(3i).cmp(3+3i) #=> Calc::Q(-1)
137
+ */
138
+ static VALUE
139
+ cn_cmp(VALUE self, VALUE other)
140
+ {
141
+ VALUE result;
142
+ NUMBER *qself, *qother;
143
+ COMPLEX *cself, *cother, *cresult;
144
+ int r, i;
145
+ setup_math_error();
146
+
147
+ if (CALC_Q_P(self)) {
148
+ qself = DATA_PTR(self);
149
+ if (CALC_C_P(other) || TYPE(other) == T_COMPLEX) {
150
+ cother = value_to_complex(other);
151
+ r = qrel(qself, cother->real);
152
+ i = qrel(&_qzero_, cother->imag);
153
+ comfree(cother);
154
+ }
155
+ else {
156
+ qother = value_to_number(other, 0);
157
+ r = qrel(qself, qother);
158
+ i = 0;
159
+ qfree(qother);
160
+ }
161
+ }
162
+ else if (CALC_C_P(self)) {
163
+ cself = DATA_PTR(self);
164
+ if (CALC_C_P(other) || TYPE(other) == T_COMPLEX) {
165
+ cother = value_to_complex(other);
166
+ r = qrel(cself->real, cother->real);
167
+ i = qrel(cself->imag, cother->imag);
168
+ }
169
+ else {
170
+ qother = value_to_number(other, 0);
171
+ r = qrel(cself->real, qother);
172
+ i = qrel(cself->imag, &_qzero_);
173
+ qfree(qother);
174
+ }
175
+ }
176
+ else {
177
+ rb_raise(rb_eTypeError, "receiver must be Calc::Q or Calc::C");
178
+ }
179
+ if (i == 0) {
180
+ result = cq_new();
181
+ DATA_PTR(result) = sign_of_int(r);
182
+ }
183
+ else {
184
+ result = cc_new();
185
+ cresult = comalloc();
186
+ qfree(cresult->real);
187
+ cresult->real = sign_of_int(r);
188
+ qfree(cresult->imag);
189
+ cresult->imag = sign_of_int(i);
190
+ DATA_PTR(result) = cresult;
191
+ }
192
+ return result;
193
+ }
194
+
195
+ /* combinatorial number
196
+ *
197
+ * Returns the number of combinations in which `other` things may be chosen
198
+ * from `self` items ignoring order.
199
+ *
200
+ * @param other [Integer]
201
+ * @return [Calc::Q,Calc::C]
202
+ * @raise [MathError] if `other` is too large or not a positive integer
203
+ * @example
204
+ * Calc::Q(5).comb(3) #=> Calc::Q(10)
205
+ * Calc::Q(60).comb(30) #=> Calc::Q(118264581564861424)
206
+ * Calc::Q("5.1").comb(5) #=> Calc::Q(1.24780425)
207
+ * Calc::C(8,9).comb(3) #=> Calc::C(-227.5+97.5i)
208
+ */
209
+ static VALUE
210
+ cn_comb(VALUE self, VALUE other)
211
+ {
212
+ VALUE result;
213
+ NUMBER *qother, *qresult, *qdiv, *qtmp;
214
+ COMPLEX *cresult, *ctmp1, *ctmp2;
215
+ long n;
216
+ setup_math_error();
217
+
218
+ qother = value_to_number(other, 0);
219
+ if (qisfrac(qother)) {
220
+ qfree(qother);
221
+ rb_raise(e_MathError, "non-integer argument to comb");
222
+ }
223
+ if (qisneg(qother)) {
224
+ qfree(qother);
225
+ result = cq_new();
226
+ DATA_PTR(result) = qlink(&_qzero_);
227
+ return result;
228
+ }
229
+ else if (qiszero(qother)) {
230
+ qfree(qother);
231
+ result = cq_new();
232
+ DATA_PTR(result) = qlink(&_qone_);
233
+ return result;
234
+ }
235
+ else if (qisone(qother)) {
236
+ qfree(qother);
237
+ return self;
238
+ }
239
+ else if (CALC_Q_P(self)) {
240
+ qresult = qcomb(DATA_PTR(self), qother);
241
+ qfree(qother);
242
+ if (qresult == NULL) {
243
+ rb_raise(e_MathError, "argument too large for comb");
244
+ }
245
+ result = cq_new();
246
+ DATA_PTR(result) = qresult;
247
+ return result;
248
+ }
249
+ /* if here, self is a Calc::C and qother is integer > 1. algorithm based
250
+ * on calc's func.c, but only for COMPLEX*. */
251
+ if (zge24b(qother->num)) {
252
+ qfree(qother);
253
+ rb_raise(e_MathError, "argument too large for comb");
254
+ }
255
+ n = qtoi(qother);
256
+ cresult = clink((COMPLEX *) DATA_PTR(self));
257
+ ctmp1 = c_addq((COMPLEX *) DATA_PTR(self), &_qnegone_);
258
+ qdiv = qlink(&_qtwo_);
259
+ n--;
260
+ for (;;) {
261
+ ctmp2 = c_mul(cresult, ctmp1);
262
+ comfree(cresult);
263
+ cresult = c_divq(ctmp2, qdiv);
264
+ comfree(ctmp2);
265
+ if (--n == 0 || ciszero(cresult)) {
266
+ comfree(ctmp1);
267
+ qfree(qdiv);
268
+ result = cc_new();
269
+ DATA_PTR(result) = cresult;
270
+ return result;
271
+ }
272
+ ctmp2 = c_addq(ctmp1, &_qnegone_);
273
+ comfree(ctmp1);
274
+ ctmp1 = ctmp2;
275
+ qtmp = qinc(qdiv);
276
+ qfree(qdiv);
277
+ qdiv = qtmp;
278
+ }
279
+ }
280
+
281
+ /* floor of logarithm to specified integer base
282
+ *
283
+ * x.ilog(b) returns the greatest integer for which b^n <= abs(x)
284
+ *
285
+ * @param base [Integer]
286
+ * @return [Calc::Q]
287
+ * @raise [Calc::MathError] if x is zero, b is non-integer or b is <= 1
288
+ * @example
289
+ * Calc::Q(2).ilog(3) #=> Calc::Q(0)
290
+ * Calc::Q(8).ilog(3) #=> Calc::Q(1)
291
+ * Calc::Q(9).ilog(3) #=> Calc::Q(2)
292
+ */
293
+ static VALUE
294
+ cn_ilog(VALUE self, VALUE base)
295
+ {
296
+ VALUE result;
297
+ NUMBER *qbase, *qresult;
298
+ setup_math_error();
299
+
300
+ qbase = value_to_number(base, 0);
301
+ if (qisfrac(qbase) || qiszero(qbase) || qisunit(qbase) || qisneg(qbase)) {
302
+ qfree(qbase);
303
+ rb_raise(e_MathError, "base must be an integer > 1");
304
+ }
305
+ if (CALC_Q_P(self)) {
306
+ qresult = qilog(DATA_PTR(self), qbase->num);
307
+ }
308
+ else if (CALC_C_P(self)) {
309
+ qresult = c_ilog(DATA_PTR(self), qbase->num);
310
+ }
311
+ else {
312
+ rb_raise(rb_eTypeError, "cn_ilog called with invalid receiver");
313
+ }
314
+ qfree(qbase);
315
+ if (!qresult) {
316
+ rb_raise(e_MathError, "invalid argument for ilog");
317
+ }
318
+ result = cq_new();
319
+ DATA_PTR(result) = qresult;
320
+ return result;
321
+ }
322
+
323
+ /* Natural logarithm
324
+ *
325
+ * Note that this is like using ruby's Math.log.
326
+ *
327
+ * @param eps [Numeric,Calc::Q] (optional) calculation accuracy
328
+ * @return [Calc::Q,Calc::C]
329
+ * @raise [Calc::MathError] if self is zero
330
+ * @example
331
+ * Calc::Q(10).ln #=> Calc::Q(2.30258509299404568402)
332
+ * Calc::Q(-10).ln #=> Calc::C(2.30258509299404568402+3.14159265358979323846i)
333
+ * Calc::C(0, 10).ln #=> Calc::C(2.30258509299404568402+1.57079632679489661923i)
334
+ */
335
+ static VALUE
336
+ cn_ln(int argc, VALUE * argv, VALUE self)
337
+ {
338
+ return log_function(argc, argv, self, &qln, &c_ln);
339
+ }
340
+
341
+ /* Base 10 logarithm
342
+ *
343
+ * Note that this is like using ruby's Math.log10.
344
+ *
345
+ * @param eps [Numeric,Calc::Q] (optional) calculation accuracy
346
+ * @return [Calc::Q,Calc::C]
347
+ * @raise [Calc::MathError] if self is zero
348
+ * @example
349
+ * Calc::Q(-1).log #=> Calc::C(~1.36437635384184134748i)
350
+ * Calc::Q(10).log #=> Calc::Q(1)
351
+ * Calc::Q(100).log #=> Calc::Q(2)
352
+ * Calc::Q("1e10").log #=> Calc::Q(10)
353
+ * Calc::C(0, 10).log #=> Calc::C(1+~0.68218817692092067374i)
354
+ */
355
+ static VALUE
356
+ cn_log(int argc, VALUE * argv, VALUE self)
357
+ {
358
+ return log_function(argc, argv, self, &qlog, &c_log);
359
+ }
360
+
361
+ /* Compute integer quotient of a value by a real number (integer division)
362
+ *
363
+ * If y is zero, returns zero.
364
+ *
365
+ * @param y [Numeric]
366
+ * @param rnd [Integer] rounding flags, default Calc.config(:quo)
367
+ * @return [Calc::Q,Calc::C]
368
+ * @example
369
+ * Calc::Q(11,5) #=> Calc::Q(2)
370
+ */
371
+ static VALUE
372
+ cn_quo(int argc, VALUE * argv, VALUE self)
373
+ {
374
+ VALUE y, rnd;
375
+ NUMBER *qy, *qresult;
376
+ COMPLEX *cself, *cresult;
377
+ long r;
378
+ setup_math_error();
379
+
380
+ if (rb_scan_args(argc, argv, "11", &y, &rnd) == 2) {
381
+ r = value_to_long(rnd);
382
+ }
383
+ else {
384
+ r = conf->quo;
385
+ }
386
+ qy = value_to_number(y, 1);
387
+ if (qiszero(qy)) {
388
+ qfree(qy);
389
+ rb_raise(rb_eZeroDivError, "division by zero in quo");
390
+ }
391
+ if (CALC_Q_P(self)) {
392
+ qresult = qquo(DATA_PTR(self), qy, r);
393
+ qfree(qy);
394
+ return wrap_number(qresult);
395
+ }
396
+ cself = DATA_PTR(self);
397
+ cresult = comalloc();
398
+ qfree(cresult->real);
399
+ qfree(cresult->imag);
400
+ cresult->real = qquo(cself->real, qy, r);
401
+ cresult->imag = qquo(cself->imag, qy, r);
402
+ qfree(qy);
403
+ return wrap_complex(cresult);
404
+ }
405
+
406
+ /* Root of a number
407
+ *
408
+ * x.root(n) returns the nth root of x. x can be real or complex, n must be
409
+ * a positive integer.
410
+ *
411
+ * If the nth root of x is a multiple of eps, it will be returned exactly.
412
+ * Otherwise the returned value will be a multiple of eps close to the real
413
+ * nth root of x.
414
+ *
415
+ * @param n [Integer]
416
+ * @param eps [Numeric] optional epsilon, default Calc.config(:epsilon)
417
+ * @return [Calc::Q,Calc::C]
418
+ * @raise [Calc::MathError] if n is not a positive integer
419
+ * @example
420
+ * Calc::Q(7).root(4) #=> Calc::Q(1.62657656169778574321)
421
+ * Calc::Q(-7).root(4) #=> Calc::C(1.15016331689560300254+1.15016331689560300254i)
422
+ * Calc::C(7, 7).root(4) #=> Calc::C(1.73971135785495811228+0.34605010474820910752i)
423
+ */
424
+ static VALUE
425
+ cn_root(int argc, VALUE * argv, VALUE self)
426
+ {
427
+ VALUE n, epsilon, result;
428
+ NUMBER *qn, *qepsilon, *qself;
429
+ COMPLEX ctmp;
430
+ setup_math_error();
431
+
432
+ if (rb_scan_args(argc, argv, "11", &n, &epsilon) > 1) {
433
+ qepsilon = value_to_number(epsilon, 1);
434
+ if (qiszero(qepsilon)) {
435
+ qfree(qepsilon);
436
+ rb_raise(e_MathError, "zero epsilon for root");
437
+ }
438
+ }
439
+ else {
440
+ qepsilon = qlink(conf->epsilon);
441
+ }
442
+ qn = value_to_number(n, 0);
443
+ if (qisneg(qn) || qiszero(qn) || qisfrac(qn)) {
444
+ qfree(qepsilon);
445
+ qfree(qn);
446
+ rb_raise(e_MathError, "non-positive integer root");
447
+ }
448
+ if (CALC_Q_P(self)) {
449
+ qself = DATA_PTR(self);
450
+ if (!qisneg(qself)) {
451
+ result = wrap_number(qroot(qself, qn, qepsilon));
452
+ }
453
+ else {
454
+ ctmp.real = qself;
455
+ ctmp.imag = &_qzero_;
456
+ ctmp.links = 1;
457
+ result = wrap_complex(c_root(&ctmp, qn, qepsilon));
458
+ }
459
+ }
460
+ else {
461
+ result = wrap_complex(c_root(DATA_PTR(self), qn, qepsilon));
462
+ }
463
+ qfree(qepsilon);
464
+ qfree(qn);
465
+ return result;
466
+ }
467
+
468
+ /* Scale a number by a power of 2
469
+ *
470
+ * x.scale(n) returns the value of 2**n * x.
471
+ *
472
+ * Unlike the << and >> operators, this function works on fractional x.
473
+ *
474
+ * @param n [Integer]
475
+ * @return [Calc::C,Calc::Q]
476
+ * @raise [ArgumentError] if n isn't an integer < 2^31
477
+ * @example
478
+ * Calc::Q(3).scale(2) #=> Calc::Q(12)
479
+ * Calc::Q(3).scale(-2) #=> Calc::Q(0.75)
480
+ * Calc::C(3, 3).scale(2) #=> Calc::C(12+12i)
481
+ */
482
+ static VALUE
483
+ cn_scale(VALUE self, VALUE other)
484
+ {
485
+ NUMBER *qother;
486
+ long n;
487
+ setup_math_error();
488
+
489
+ qother = value_to_number(other, 0);
490
+ if (qisfrac(qother)) {
491
+ qfree(qother);
492
+ rb_raise(rb_eArgError, "scale by non-integer");
493
+ }
494
+ if (zge31b(qother->num)) {
495
+ qfree(qother);
496
+ rb_raise(rb_eArgError, "scale factor must be < 2^31");
497
+ }
498
+ n = qtoi(qother);
499
+ qfree(qother);
500
+ if (CALC_Q_P(self)) {
501
+ return wrap_number(qscale(DATA_PTR(self), n));
502
+ }
503
+ else {
504
+ return wrap_complex(c_scale(DATA_PTR(self), n));
505
+ }
506
+ }
507
+
508
+ /* Indicates sign of a real or complex number
509
+ *
510
+ * For real x, x.sgn returns
511
+ * -1 if x < 0
512
+ * 0 if x == 0
513
+ * 1 if x > 0
514
+ *
515
+ * For complex x, x.sgn returns Calc::C(x.re.sgn, x.im.sgn)
516
+ *
517
+ * @return [Calc::C,Calc::Q]
518
+ * @example
519
+ * Calc::Q(9).sgn #=> Calc::Q(1)
520
+ * Calc::Q(0).sgn #=> Calc::Q(0)
521
+ * Calc::Q(-9).sgn #=> Calc::Q(-1)
522
+ * Calc::C(1, -1).sgn #=> Calc::C(1-1i)
523
+ */
524
+ static VALUE
525
+ cn_sgn(VALUE self)
526
+ {
527
+ COMPLEX *cself, *cresult;
528
+ setup_math_error();
529
+
530
+ if (CALC_Q_P(self)) {
531
+ return wrap_number(qsign(DATA_PTR(self)));
532
+ }
533
+ cself = DATA_PTR(self);
534
+ cresult = comalloc();
535
+ qfree(cresult->real);
536
+ qfree(cresult->imag);
537
+ cresult->real = qsign(cself->real);
538
+ cresult->imag = qsign(cself->imag);
539
+ return wrap_complex(cresult);
540
+ }
541
+
542
+ /* Square root
543
+ *
544
+ * Calculates the square root of self (rational or complex). If eps
545
+ * is provided, it specifies the accuracy/error of the calculation, otherwise
546
+ * config("epsilon") is used.
547
+ * If z is provided, it controls the sign and rounding if required, otherwise
548
+ * config("sqrt") is used.
549
+ * Type "help sqrt" in calc for a full explanation of z.
550
+ *
551
+ * @param eps [Numeric,Calc::Q] (optional) calculation accuracy
552
+ * @param z [Integer] (optional) sign and rounding flags
553
+ * @example
554
+ * Calc::Q(4).sqrt #=> Calc::Q(2)
555
+ * Calc::Q(5).sqrt #=> Calc::Q(2.23606797749978969641)
556
+ * Calc::C(0,8).sqrt #=> Calc::C(2+2i)
557
+ */
558
+ static VALUE
559
+ cn_sqrt(int argc, VALUE * argv, VALUE self)
560
+ {
561
+ VALUE result, epsilon, z;
562
+ NUMBER *qtmp, *qepsilon;
563
+ COMPLEX *cresult;
564
+ long R;
565
+ int n;
566
+ setup_math_error();
567
+
568
+ n = rb_scan_args(argc, argv, "02", &epsilon, &z);
569
+ if (n >= 1) {
570
+ qepsilon = value_to_number(epsilon, 1);
571
+ }
572
+ else {
573
+ qepsilon = conf->epsilon;
574
+ }
575
+ if (n == 2) {
576
+ R = FIX2LONG(z);
577
+ }
578
+ else {
579
+ R = conf->sqrt;
580
+ }
581
+ if (CALC_Q_P(self) && !qisneg((NUMBER *) DATA_PTR(self))) {
582
+ /* non-negative rational */
583
+ result = cq_new();
584
+ DATA_PTR(result) = qsqrt(DATA_PTR(self), qepsilon, R);
585
+ }
586
+ else {
587
+ if (CALC_Q_P(self)) {
588
+ /* negative rational */
589
+ qtmp = qneg(DATA_PTR(self));
590
+ cresult = comalloc();
591
+ qfree(cresult->imag);
592
+ cresult->imag = qsqrt(qtmp, qepsilon, R);
593
+ qfree(qtmp);
594
+ }
595
+ else {
596
+ /* complex */
597
+ cresult = c_sqrt(DATA_PTR(self), qepsilon, R);
598
+ }
599
+ result = wrap_complex(cresult);
600
+ }
601
+ if (n >= 1) {
602
+ qfree(qepsilon);
603
+ }
604
+ return result;
605
+ }
606
+
607
+ void
608
+ define_calc_numeric(VALUE m)
609
+ {
610
+ cNumeric = rb_define_class_under(m, "Numeric", rb_cData);
611
+ rb_define_method(cNumeric, "<<", cn_shift_left, 1);
612
+ rb_define_method(cNumeric, ">>", cn_shift_right, 1);
613
+ rb_define_method(cNumeric, "cmp", cn_cmp, 1);
614
+ rb_define_method(cNumeric, "comb", cn_comb, 1);
615
+ rb_define_method(cNumeric, "ilog", cn_ilog, 1);
616
+ rb_define_method(cNumeric, "ln", cn_ln, -1);
617
+ rb_define_method(cNumeric, "log", cn_log, -1);
618
+ rb_define_method(cNumeric, "quo", cn_quo, -1);
619
+ rb_define_method(cNumeric, "root", cn_root, -1);
620
+ rb_define_method(cNumeric, "scale", cn_scale, 1);
621
+ rb_define_method(cNumeric, "sgn", cn_sgn, 0);
622
+ rb_define_method(cNumeric, "sqrt", cn_sqrt, -1);
623
+ }