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.
@@ -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
+ }