Module: Calc

Defined in:
ext/calc/calc.c,
lib/calc.rb,
lib/calc/c.rb,
lib/calc/q.rb,
lib/calc/numeric.rb,
lib/calc/version.rb

Defined Under Namespace

Classes: C, MathError, Numeric, Q

Constant Summary collapse

BUILTINS1 =

builtins implemented as instance methods on Calc::Q or Calc::C

%i(
  abs acos acosh acot acoth acsc acsch agd appr arg asec asech asin asinh
  atan atan2 atanh bernoulli bit bround btrunc catalan ceil cfappr cfsim char
  cmp comb conj cos cosh cot coth csc csch den digit digits estr euler exp
  fact factor fcnt fib floor frac frem gcd gcdrem gd highbit hypot ilog
  ilog10 ilog2 im int inverse iroot iseven isimag isint ismult isodd isprime
  isqrt isreal isrel issq jacobi lcm lcmfact lfactor ln log lowbit ltol meq
  minv mmin mne mod near nextcand nextprime norm num perm pfact pix places
  pmod popcnt power prevcand prevprime ptest quo quomod re root round scale
  sec sech sgn sin sinh sqrt tan tanh trunc xor
).freeze
BUILTINS2 =

builtins implemented as module methods on Calc

%i(
  avg config freebernoulli freeeuler hean hnrmod max min pi polar ssq sum
  version
).freeze
ALL_BUILTINS =
BUILTINS1 + BUILTINS2
VERSION =
"0.2.0".freeze

Class Method Summary collapse

Class Method Details

.avg(*args) ⇒ Calc::Q, Calc::C

Average (arithmetic mean)

Any number of numeric arguments can be provided. Returns the sum of all values divided by the number of values. If no values are provided, returns nil.

Examples:

Calc.avg(1, 2, 3)          #=> Calc::Q(2)
Calc.avg(4, Calc::C(2, 2)) #=> Calc::C(3+1i)

Returns:



64
65
66
67
68
# File 'lib/calc.rb', line 64

def self.avg(*args)
  args.flatten!
  return nil if args.none?
  args.map { |n| to_calc_x(n) }.inject(:+) / args.size
end

.C(*args) ⇒ Object

rubocop:disable Style/MethodName



50
51
52
# File 'lib/calc.rb', line 50

def self.C(*args) # rubocop:disable Style/MethodName
  C.new(*args)
end

.configObject

.freebernoullinil

Frees memory used to store calculated bernoulli numbers.

Examples:

Calc.freebernoulli  #=> nil

Returns:

  • (nil)


9
10
11
12
13
14
15
# File 'ext/calc/calc.c', line 9

static VALUE
calc_freebernoulli(VALUE self)
{
    setup_math_error();
    qfreebern();
    return Qnil;
}

.freeeulernil

Frees memory used to store calculated euler numbers.

Examples:

Calc.freeeuler  #=> nil

Returns:

  • (nil)


23
24
25
26
27
28
29
# File 'ext/calc/calc.c', line 23

static VALUE
calc_freeeuler(VALUE self)
{
    setup_math_error();
    qfreeeuler();
    return Qnil;
}

.hmean(*args) ⇒ Calc::Q, Calc::C

Harmonic mean

Returns zero if any of the provded values is zero. Returns nil if no values are provided. Otherwise returns the harmonic mean of the given values.

Returns:

  • (Calc::Q, Calc::C)

    Calc.hmean(1, 2, 4) #=> Calc::Q(12/7) Calc.hmean(2, Complex(0, 2)) #=> Calc::C(2+2i)



79
80
81
82
83
84
# File 'lib/calc.rb', line 79

def self.hmean(*args)
  args.flatten!
  return nil if args.none?
  return Q::ZERO if args.detect(&:zero?)
  args.size / args.map { |n| to_calc_x(n) }.map(&:inverse).inject(:+)
end

.hnrmod(v, h, n, r) ⇒ Calc::Q

Computer mod h * 2^n + r

hnrmod(v, h, n, r) computes the value:

v % (h * 2^n + r)

where all parameters are integers and:

h > 0
n > 0
r == -1, 0 or 1

This is faster than standard mod.

Examples:

Calc.hnrmod(2**177 - 1, 1, 177, -1) #=> Calc::Q(0)
Calc.hnrmod(10**40, 17, 51, 1)      #=> Calc::Q(33827019788296445)

Parameters:

  • v (Integer)
  • h (Integer)
  • n (Integer)
  • r (Integer)

Returns:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'ext/calc/calc.c', line 51

static VALUE
calc_hnrmod(VALUE self, VALUE v, VALUE h, VALUE n, VALUE r)
{
    NUMBER *qv, *qh, *qn, *qr, *qresult;
    ZVALUE zresult;
    setup_math_error();

    qv = value_to_number(v, 0);
    if (qisfrac(qv)) {
        qfree(qv);
        rb_raise(e_MathError, "1st arg of hnrmod (v) must be an integer");
    }
    qh = value_to_number(h, 0);
    if (qisfrac(qh) || qisneg(qh) || qiszero(qh)) {
        qfree(qv);
        qfree(qh);
        rb_raise(e_MathError, "2nd arg of hnrmod (h) must be an integer > 0");
    }
    qn = value_to_number(n, 0);
    if (qisfrac(qn) || qisneg(qn) || qiszero(qn)) {
        qfree(qv);
        qfree(qh);
        qfree(qn);
        rb_raise(e_MathError, "3rd arg of hnrmod (n) must be an integer > 0");
    }
    qr = value_to_number(r, 0);
    if (qisfrac(qr) || !zisabsleone(qr->num)) {
        qfree(qv);
        qfree(qh);
        qfree(qn);
        qfree(qr);
        rb_raise(e_MathError, "4th arg of hnrmod (r) must be -1, 0 or 1");
    }
    zhnrmod(qv->num, qh->num, qn->num, qr->num, &zresult);
    qresult = qalloc();
    qresult->num = zresult;
    return wrap_number(qresult);
}

.max(*args) ⇒ Calc::Q

Maximum from provided values.

Each argument must be convertable to Calc::Q. If no values, returns nil.

Examples:

Calc.max(5, 3, 7, 2, 9) #=> Calc::Q(9)

Returns:



93
94
95
# File 'lib/calc.rb', line 93

def self.max(*args)
  args.compact.map { |n| Calc::Q(n) }.max
end

.min(*args) ⇒ Calc::Q

Minimum from provided values

Each argument must be convertable to Calc::Q. If no values, returns nil.

Examples:

Calc.min(5, 3, 7, 2, 9) #=> Calc::Q(2)

Returns:



104
105
106
# File 'lib/calc.rb', line 104

def self.min(*args)
  args.compact.map { |n| Calc::Q(n) }.min
end

.pi(*args) ⇒ Calc::Q

Evaluates п (pi) to a specified accuracy

Examples:

Calc.pi          #=> Calc::Q(3.14159265358979323846)
Calc.pi("1e-40") #=> Calc::Q(3.1415926535897932384626433832795028841972)

Parameters:

Returns:

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'ext/calc/calc.c', line 100

static VALUE
calc_pi(int argc, VALUE * argv, VALUE self)
{
    NUMBER *qepsilon, *qresult;
    VALUE epsilon;
    setup_math_error();

    if (rb_scan_args(argc, argv, "01", &epsilon) == 0) {
        qresult = qpi(conf->epsilon);
    }
    else {
        qepsilon = value_to_number(epsilon, 1);
        qresult = qpi(qepsilon);
        qfree(qepsilon);
    }
    return wrap_number(qresult);
}

.polar(*args) ⇒ Calc::Numeric

Returns a new complex (or real) number specified by modulus (radius) and argument (angle, in radians).

Examples:

Calc.polar(1,2)        #=> Calc::C(-0.416146836547142387+0.9092974268256816954i)
Calc.polar(1,2,"0.01") #=> Calc::C(-0.42+0.91i)
Calc.polar(2,0)        #=> Calc::Q(2)

Parameters:

Returns:



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'ext/calc/calc.c', line 130

static VALUE
calc_polar(int argc, VALUE * argv, VALUE self)
{
    VALUE radius, angle, epsilon, result;
    NUMBER *qradius, *qangle, *qepsilon;
    setup_math_error();

    if (rb_scan_args(argc, argv, "21", &radius, &angle, &epsilon) == 3) {
        qepsilon = value_to_number(epsilon, 1);
        if (qisneg(qepsilon) || qiszero(qepsilon)) {
            qfree(qepsilon);
            rb_raise(e_MathError, "Negative or zero epsilon for polar");
        }
    }
    else {
        qepsilon = NULL;
    }
    qradius = value_to_number(radius, 0);
    qangle = value_to_number(angle, 0);
    result = wrap_complex(c_polar(qradius, qangle, qepsilon ? qepsilon : conf->epsilon));
    if (qepsilon) {
        qfree(qepsilon);
    }
    qfree(qradius);
    qfree(qangle);
    return result;
}

.poly(*args) ⇒ Calc::Numeric

Evaluate a polynomial

First case:

poly(a_0, a_1, ..., a_n, x)

returns:

a_n + (a_n-1 + ... + (a_1 + a_0 * x) * x ..) * x

In particular:

poly(a, x) -> a
poly(a, b, x) -> b + a * x
poly(a, b, c, x) -> c + (b + a * x) * x
                 or a*x**2 + b*x + c

In the second case, the first parameter is an array of coefficients, ie:

poly([a_0, a_1, ... a_n], x)

returns:

a_0 + (a_n-1 + (a_2 + ... a_n * x) * x)

Note that the order of coeffecients is reverse of the first case.

If one or more elements of clist is another array, and there is more than one argument (x, y, …) the coefficient corresponding to such an element is the value of the poly for that list and the next argument in x, y, … For example:

poly([[a, b, c], [d, e], f], x, y)

Returns:

(a + b * y + c * y^2) + (d + e * y) * x + f * x^2

For more explanation and examples on how the nested arrays works, see “help poly” bearning in mind that a calc list is equivament to a ruby array.

Examples:

# 2 * 7**2 + 3 * 7 + 5
Calc.poly(2, 3, 5, 7) #=> Calc::Q(124)

Returns:

Raises:

  • (ArgumentError)


142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/calc.rb', line 142

def self.poly(*args)
  raise ArgumentError, "Need at least one argument for poly" if args.none?
  if args.first.respond_to?(:each)
    # second case
    clist = args.shift
    evalpoly(clist, args.flatten, 0)
  else
    # first case
    x = to_calc_x(args.pop)
    return x if args.none?
    args.reverse.each_with_index.map { |coeff, i| to_calc_x(coeff) * x**i }.reduce(:+)
  end
end

.Q(*args) ⇒ Object

rubocop:disable Style/MethodName



46
47
48
# File 'lib/calc.rb', line 46

def self.Q(*args) # rubocop:disable Style/MethodName
  Q.new(*args)
end

.ssq(*args) ⇒ Calc::C, Calc::Q

Returns the sum of squares.

Nil values are ignored. If any argument is am array, it contributes the sum of squares of its contents recursively.

Examples:

Calc.ssq(1, 2, 3)       #=> Calc::Q(14)
Calc.ssq(1+2i, 3-4i, 5) #=> Calc::C(15-20i)

Returns:

Raises:

  • (ArgumentError)

    if any argument can’t be converted to a Calc class



195
196
197
# File 'lib/calc.rb', line 195

def self.ssq(*args)
  args.flatten.map { |term| to_calc_x(term)**2 }.inject(:+)
end

.sum(*args) ⇒ Object



199
200
201
# File 'lib/calc.rb', line 199

def self.sum(*args)
  args.flatten.map { |t| to_calc_x(t) }.compact.inject(:+)
end

.versionObject

Returns the calc version string

This will return a string specifying the version of calc/libcalc which ruby-calc was compiled against.

For the version of ruby-calc itself, use ‘Calc::VERSION`.

Examples:

Calc.version #=> "2.12.5.0"


168
169
170
171
172
# File 'ext/calc/calc.c', line 168

static VALUE
calc_version(VALUE self)
{
    return rb_str_new_cstr(version());
}