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,192 @@
1
+ #include "calc.h"
2
+
3
+ /* Frees memory used to store calculated bernoulli numbers.
4
+ *
5
+ * @return [nil]
6
+ * @example
7
+ * Calc.freebernoulli #=> nil
8
+ */
9
+ static VALUE
10
+ calc_freebernoulli(VALUE self)
11
+ {
12
+ setup_math_error();
13
+ qfreebern();
14
+ return Qnil;
15
+ }
16
+
17
+ /* Frees memory used to store calculated euler numbers.
18
+ *
19
+ * @return [nil]
20
+ * @example
21
+ * Calc.freeeuler #=> nil
22
+ */
23
+ static VALUE
24
+ calc_freeeuler(VALUE self)
25
+ {
26
+ setup_math_error();
27
+ qfreeeuler();
28
+ return Qnil;
29
+ }
30
+
31
+ /* Computer mod h * 2^n + r
32
+ *
33
+ * hnrmod(v, h, n, r) computes the value:
34
+ * v % (h * 2^n + r)
35
+ * where all parameters are integers and:
36
+ * h > 0
37
+ * n > 0
38
+ * r == -1, 0 or 1
39
+ *
40
+ * This is faster than standard mod.
41
+ *
42
+ * @param v [Integer]
43
+ * @param h [Integer]
44
+ * @param n [Integer]
45
+ * @param r [Integer]
46
+ * @return [Calc::Q]
47
+ * @example
48
+ * Calc.hnrmod(2**177 - 1, 1, 177, -1) #=> Calc::Q(0)
49
+ * Calc.hnrmod(10**40, 17, 51, 1) #=> Calc::Q(33827019788296445)
50
+ */
51
+ static VALUE
52
+ calc_hnrmod(VALUE self, VALUE v, VALUE h, VALUE n, VALUE r)
53
+ {
54
+ NUMBER *qv, *qh, *qn, *qr, *qresult;
55
+ ZVALUE zresult;
56
+ setup_math_error();
57
+
58
+ qv = value_to_number(v, 0);
59
+ if (qisfrac(qv)) {
60
+ qfree(qv);
61
+ rb_raise(e_MathError, "1st arg of hnrmod (v) must be an integer");
62
+ }
63
+ qh = value_to_number(h, 0);
64
+ if (qisfrac(qh) || qisneg(qh) || qiszero(qh)) {
65
+ qfree(qv);
66
+ qfree(qh);
67
+ rb_raise(e_MathError, "2nd arg of hnrmod (h) must be an integer > 0");
68
+ }
69
+ qn = value_to_number(n, 0);
70
+ if (qisfrac(qn) || qisneg(qn) || qiszero(qn)) {
71
+ qfree(qv);
72
+ qfree(qh);
73
+ qfree(qn);
74
+ rb_raise(e_MathError, "3rd arg of hnrmod (n) must be an integer > 0");
75
+ }
76
+ qr = value_to_number(r, 0);
77
+ if (qisfrac(qr) || !zisabsleone(qr->num)) {
78
+ qfree(qv);
79
+ qfree(qh);
80
+ qfree(qn);
81
+ qfree(qr);
82
+ rb_raise(e_MathError, "4th arg of hnrmod (r) must be -1, 0 or 1");
83
+ }
84
+ zhnrmod(qv->num, qh->num, qn->num, qr->num, &zresult);
85
+ qresult = qalloc();
86
+ qresult->num = zresult;
87
+ return wrap_number(qresult);
88
+ }
89
+
90
+ /* Evaluates п (pi) to a specified accuracy
91
+ *
92
+ * @param eps [Numeric,Calc::Q] (optional) calculation accuracy
93
+ * @return [Calc::Q]
94
+ * @raise [Calc::MathError] if v, h, n or r are non-integer or h or v < 1
95
+ * @raise [Calc::MathError] if r is not -1, 0 or 1
96
+ * @example
97
+ * Calc.pi #=> Calc::Q(3.14159265358979323846)
98
+ * Calc.pi("1e-40") #=> Calc::Q(3.1415926535897932384626433832795028841972)
99
+ */
100
+ static VALUE
101
+ calc_pi(int argc, VALUE * argv, VALUE self)
102
+ {
103
+ NUMBER *qepsilon, *qresult;
104
+ VALUE epsilon;
105
+ setup_math_error();
106
+
107
+ if (rb_scan_args(argc, argv, "01", &epsilon) == 0) {
108
+ qresult = qpi(conf->epsilon);
109
+ }
110
+ else {
111
+ qepsilon = value_to_number(epsilon, 1);
112
+ qresult = qpi(qepsilon);
113
+ qfree(qepsilon);
114
+ }
115
+ return wrap_number(qresult);
116
+ }
117
+
118
+ /* Returns a new complex (or real) number specified by modulus (radius) and
119
+ * argument (angle, in radians).
120
+ *
121
+ * @param radius [Numeric,Calc::Numeric]
122
+ * @param angle [Numeric,Calc::Numeric]
123
+ * @param eps [Numeric] (optional) calculation accuracy
124
+ * @return [Calc::Numeric]
125
+ * @example
126
+ * Calc.polar(1,2) #=> Calc::C(-0.416146836547142387+0.9092974268256816954i)
127
+ * Calc.polar(1,2,"0.01") #=> Calc::C(-0.42+0.91i)
128
+ * Calc.polar(2,0) #=> Calc::Q(2)
129
+ */
130
+ static VALUE
131
+ calc_polar(int argc, VALUE * argv, VALUE self)
132
+ {
133
+ VALUE radius, angle, epsilon, result;
134
+ NUMBER *qradius, *qangle, *qepsilon;
135
+ setup_math_error();
136
+
137
+ if (rb_scan_args(argc, argv, "21", &radius, &angle, &epsilon) == 3) {
138
+ qepsilon = value_to_number(epsilon, 1);
139
+ if (qisneg(qepsilon) || qiszero(qepsilon)) {
140
+ qfree(qepsilon);
141
+ rb_raise(e_MathError, "Negative or zero epsilon for polar");
142
+ }
143
+ }
144
+ else {
145
+ qepsilon = NULL;
146
+ }
147
+ qradius = value_to_number(radius, 0);
148
+ qangle = value_to_number(angle, 0);
149
+ result = wrap_complex(c_polar(qradius, qangle, qepsilon ? qepsilon : conf->epsilon));
150
+ if (qepsilon) {
151
+ qfree(qepsilon);
152
+ }
153
+ qfree(qradius);
154
+ qfree(qangle);
155
+ return result;
156
+ }
157
+
158
+ /* Returns the calc version string
159
+ *
160
+ * This will return a string specifying the version of calc/libcalc which
161
+ * ruby-calc was compiled against.
162
+ *
163
+ * For the version of ruby-calc itself, use `Calc::VERSION`.
164
+ *
165
+ * @example
166
+ * Calc.version #=> "2.12.5.0"
167
+ */
168
+ static VALUE
169
+ calc_version(VALUE self)
170
+ {
171
+ return rb_str_new_cstr(version());
172
+ }
173
+
174
+ void
175
+ Init_calc(void)
176
+ {
177
+ VALUE m;
178
+ libcalc_call_me_first();
179
+
180
+ m = rb_define_module("Calc");
181
+ rb_define_module_function(m, "config", calc_config, -1);
182
+ rb_define_module_function(m, "freebernoulli", calc_freebernoulli, 0);
183
+ rb_define_module_function(m, "freeeuler", calc_freeeuler, 0);
184
+ rb_define_module_function(m, "hnrmod", calc_hnrmod, 4);
185
+ rb_define_module_function(m, "pi", calc_pi, -1);
186
+ rb_define_module_function(m, "polar", calc_polar, -1);
187
+ rb_define_module_function(m, "version", calc_version, 0);
188
+ define_calc_math_error(m);
189
+ define_calc_numeric(m);
190
+ define_calc_q(m);
191
+ define_calc_c(m);
192
+ }
@@ -0,0 +1,71 @@
1
+ #ifndef CALC_H
2
+ #define CALC_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ /* cannot include calc/calc.h, which contains some things we need, because it
7
+ * includes calc/value.h which defines VALUE, a name already used by ruby.
8
+ * copying things we need here for now. */
9
+ extern void libcalc_call_me_first(void);
10
+ extern void reinitialize(void);
11
+ extern char *version(void);
12
+
13
+ #include <calc/cmath.h>
14
+ #include <calc/config.h>
15
+ #include <calc/lib_calc.h>
16
+
17
+ /* config.c */
18
+ extern VALUE calc_config(int argc, VALUE * argv, VALUE klass);
19
+ extern long value_to_mode(VALUE v);
20
+
21
+ /* convert.c */
22
+ extern NUMBER *value_to_number(VALUE arg, int string_allowed);
23
+ extern COMPLEX *value_to_complex(VALUE arg);
24
+ extern long value_to_long(VALUE n);
25
+ extern VALUE wrap_complex(COMPLEX * c);
26
+ extern VALUE wrap_number(NUMBER * n);
27
+
28
+ /* math_error.c */
29
+ extern VALUE e_MathError; /* Calc::MathError class (exception) */
30
+ extern void define_calc_math_error();
31
+
32
+ #ifdef JUMP_ON_MATH_ERROR
33
+ extern void setup_math_error();
34
+ #else
35
+ #define setup_math_error() ((void)0)
36
+ #endif
37
+
38
+ /* numeric.c */
39
+ extern VALUE cNumeric; /* Calc::Numeric module */
40
+ extern void define_calc_numeric(VALUE m);
41
+
42
+ /* q.c (rational numbers) */
43
+ extern const rb_data_type_t calc_q_type;
44
+ extern VALUE cQ; /* Calc::Q class */
45
+
46
+ extern VALUE cq_alloc(VALUE klass);
47
+ extern void define_calc_q(VALUE m);
48
+
49
+ /* c.c (complex numbers) */
50
+ extern const rb_data_type_t calc_c_type;
51
+ extern VALUE cC; /* Calc::C class */
52
+
53
+ extern VALUE cc_alloc(VALUE klass);
54
+ extern void define_calc_c(VALUE m);
55
+
56
+ /*** macros ***/
57
+
58
+ /* initialize new ruby values */
59
+ #define cq_new() cq_alloc(cQ)
60
+ #define cc_new() cc_alloc(cC)
61
+
62
+ /* test ruby values match our TypedData classes */
63
+ #define CALC_Q_P(v) (rb_typeddata_is_kind_of((v), &calc_q_type))
64
+ #define CALC_C_P(v) (rb_typeddata_is_kind_of((v), &calc_c_type))
65
+
66
+ /* ruby before 2.1 doesn't have RARRAY_AREF */
67
+ #ifndef RARRAY_AREF
68
+ #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
69
+ #endif
70
+
71
+ #endif /* CALC_H */
@@ -0,0 +1,239 @@
1
+ #include "calc.h"
2
+
3
+ /* "mode" conversions. this is based on code in config.c */
4
+
5
+ typedef struct {
6
+ const char *name;
7
+ long type;
8
+ } nametype2;
9
+
10
+ static nametype2 modes[] = {
11
+ {"fraction", MODE_FRAC},
12
+ {"frac", MODE_FRAC},
13
+ {"integer", MODE_INT},
14
+ {"int", MODE_INT},
15
+ {"real", MODE_REAL},
16
+ {"float", MODE_REAL},
17
+ {"default", MODE_INITIAL}, /* MODE_REAL */
18
+ {"scientific", MODE_EXP},
19
+ {"sci", MODE_EXP},
20
+ {"exp", MODE_EXP},
21
+ {"hexadecimal", MODE_HEX},
22
+ {"hex", MODE_HEX},
23
+ {"octal", MODE_OCTAL},
24
+ {"oct", MODE_OCTAL},
25
+ {"binary", MODE_BINARY},
26
+ {"bin", MODE_BINARY},
27
+ {"off", MODE2_OFF},
28
+ {NULL, 0}
29
+ };
30
+
31
+ /* config types we support - a subset of "configs[]" in calc's config.c */
32
+
33
+ static nametype2 configs[] = {
34
+ {"mode", CONFIG_MODE},
35
+ {"display", CONFIG_DISPLAY},
36
+ {"epsilon", CONFIG_EPSILON},
37
+ {"quomod", CONFIG_QUOMOD},
38
+ {"quo", CONFIG_QUO},
39
+ {"mod", CONFIG_MOD},
40
+ {"sqrt", CONFIG_SQRT},
41
+ {"appr", CONFIG_APPR},
42
+ {"cfappr", CONFIG_CFAPPR},
43
+ {"cfsim", CONFIG_CFSIM},
44
+ {"round", CONFIG_ROUND},
45
+ {NULL, 0}
46
+ };
47
+
48
+ static long
49
+ lookup_long(nametype2 * set, const char *name)
50
+ {
51
+ nametype2 *cp;
52
+
53
+ for (cp = set; cp->name; cp++) {
54
+ if (strcmp(cp->name, name) == 0)
55
+ return cp->type;
56
+ }
57
+ return -1;
58
+ }
59
+
60
+ static const char *
61
+ lookup_name(nametype2 * set, long val)
62
+ {
63
+ nametype2 *cp;
64
+
65
+ for (cp = set; cp->name; cp++) {
66
+ if (val == cp->type)
67
+ return cp->name;
68
+ }
69
+ return NULL;
70
+ }
71
+
72
+ /* given a String or Symbol, returns the index into a nameset
73
+ * or -1 if not found */
74
+ static long
75
+ value_to_nametype_long(VALUE v, nametype2 * set)
76
+ {
77
+ VALUE tmp;
78
+ char *str;
79
+
80
+ if (TYPE(v) == T_STRING) {
81
+ str = StringValueCStr(v);
82
+ }
83
+ else if (TYPE(v) == T_SYMBOL) {
84
+ tmp = rb_funcall(v, rb_intern("to_s"), 0);
85
+ str = StringValueCStr(tmp);
86
+ }
87
+ else {
88
+ rb_raise(rb_eArgError, "expected String or Symbol");
89
+ }
90
+ return lookup_long(set, str);
91
+ }
92
+
93
+ /* convert value to a libcalc mode flag. value may be a string or a symbol.
94
+ * raises an exception if the mode in invalid. */
95
+ long
96
+ value_to_mode(VALUE v)
97
+ {
98
+ long n;
99
+
100
+ n = value_to_nametype_long(v, modes);
101
+ if (n < 0) {
102
+ rb_raise(rb_eArgError, "invalid output mode");
103
+ }
104
+ return n;
105
+ }
106
+
107
+ static VALUE
108
+ mode_to_string(long n)
109
+ {
110
+ const char *p;
111
+
112
+ p = lookup_name(modes, n);
113
+ if (p == NULL) {
114
+ rb_raise(e_MathError, "invalid output mode: %ld", n);
115
+ }
116
+ return rb_str_new2(p);
117
+ }
118
+
119
+ /* convert a string or symbol to the libcalc CALC_* enum
120
+ * returns -1 if the name is invalid/unsupported in ruby-calc */
121
+ static long
122
+ value_to_config(VALUE v)
123
+ {
124
+ return value_to_nametype_long(v, configs);
125
+ }
126
+
127
+ static LEN
128
+ value_to_len(VALUE v, const char *name)
129
+ {
130
+ NUMBER *q;
131
+ LEN len = 0;
132
+
133
+ q = value_to_number(v, 1);
134
+ if (!qisint(q)) {
135
+ qfree(q);
136
+ rb_raise(e_MathError, "Non-integer value for %s", name);
137
+ }
138
+ if (zge31b(q->num)) {
139
+ qfree(q);
140
+ rb_raise(e_MathError, "Integer too big for %s", name);
141
+ }
142
+ len = (LEN) ztoi(q->num);
143
+ qfree(q);
144
+ if (len < 0)
145
+ rb_raise(e_MathError, "Negative value for %s", name);
146
+ return len;
147
+ }
148
+
149
+ /* Unfortunately in this function we can't use the libcalc method setconfig()
150
+ * or config_value() - because it is defined in value.h which is
151
+ * incompatible with the ruby C API (they both define a type called VALUE).
152
+ *
153
+ * some of its code is duplicated here.
154
+ */
155
+
156
+ /* Gets or sets a libcalc configuration type.
157
+ */
158
+ VALUE
159
+ calc_config(int argc, VALUE * argv, VALUE klass)
160
+ {
161
+ VALUE name, new_value, old_value;
162
+ int args;
163
+ setup_math_error();
164
+
165
+ args = rb_scan_args(argc, argv, "11", &name, &new_value);
166
+
167
+ switch (value_to_config(name)) {
168
+
169
+ case CONFIG_DISPLAY:
170
+ old_value = INT2FIX(conf->outdigits);
171
+ if (args == 2)
172
+ math_setdigits(FIX2INT(new_value));
173
+ break;
174
+
175
+ case CONFIG_MODE:
176
+ old_value = mode_to_string(conf->outmode);
177
+ if (args == 2)
178
+ math_setmode((int) value_to_mode(new_value));
179
+ break;
180
+
181
+ case CONFIG_EPSILON:
182
+ old_value = wrap_number(qlink(conf->epsilon));
183
+ if (args == 2)
184
+ setepsilon(value_to_number(new_value, 1));
185
+ break;
186
+
187
+ case CONFIG_QUOMOD:
188
+ old_value = INT2FIX(conf->quomod);
189
+ if (args == 2)
190
+ conf->quomod = value_to_len(new_value, "quomod");
191
+ break;
192
+
193
+ case CONFIG_QUO:
194
+ old_value = INT2FIX(conf->quo);
195
+ if (args == 2)
196
+ conf->quo = value_to_len(new_value, "quo");
197
+ break;
198
+
199
+ case CONFIG_MOD:
200
+ old_value = INT2FIX(conf->mod);
201
+ if (args == 2)
202
+ conf->mod = value_to_len(new_value, "mod");
203
+ break;
204
+
205
+ case CONFIG_SQRT:
206
+ old_value = INT2FIX(conf->sqrt);
207
+ if (args == 2)
208
+ conf->sqrt = value_to_len(new_value, "sqrt");
209
+ break;
210
+
211
+ case CONFIG_APPR:
212
+ old_value = INT2FIX(conf->appr);
213
+ if (args == 2)
214
+ conf->appr = value_to_len(new_value, "appr");
215
+ break;
216
+
217
+ case CONFIG_CFAPPR:
218
+ old_value = INT2FIX(conf->cfappr);
219
+ if (args == 2)
220
+ conf->cfappr = value_to_len(new_value, "cfappr");
221
+ break;
222
+
223
+ case CONFIG_CFSIM:
224
+ old_value = INT2FIX(conf->cfsim);
225
+ if (args == 2)
226
+ conf->cfsim = value_to_len(new_value, "cfsim");
227
+ break;
228
+
229
+ case CONFIG_ROUND:
230
+ old_value = INT2FIX(conf->round);
231
+ if (args == 2)
232
+ conf->round = value_to_len(new_value, "round");
233
+ break;
234
+
235
+ default:
236
+ rb_raise(rb_eArgError, "Invalid or unsupported config parameter");
237
+ }
238
+ return old_value;
239
+ }