ruby-calc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/ext/calc/convert.c
ADDED
@@ -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
|
+
}
|
data/ext/calc/extconf.rb
ADDED
@@ -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
|
+
}
|
data/ext/calc/numeric.c
ADDED
@@ -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
|
+
}
|