rcas 0.2.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.
- checksums.yaml +7 -0
- data/CITATION.cff +17 -0
- data/DESIGN.md +783 -0
- data/LICENSE +21 -0
- data/MANUAL.md +6265 -0
- data/README.md +267 -0
- data/bin/rcas +9 -0
- data/bin/rcas-app +9 -0
- data/bin/rcas-chat +9 -0
- data/lib/rcas/algebraic.rb +481 -0
- data/lib/rcas/analysis.rb +966 -0
- data/lib/rcas/app/launcher.rb +203 -0
- data/lib/rcas/app/public/app.css +402 -0
- data/lib/rcas/app/public/app.js +449 -0
- data/lib/rcas/app/public/index.html +46 -0
- data/lib/rcas/app/server.rb +220 -0
- data/lib/rcas/app/window.rb +94 -0
- data/lib/rcas/app/worksheet.rb +290 -0
- data/lib/rcas/app.rb +168 -0
- data/lib/rcas/background.rb +758 -0
- data/lib/rcas/chat/assistant.rb +199 -0
- data/lib/rcas/chat/picker.rb +164 -0
- data/lib/rcas/chat/repl.rb +583 -0
- data/lib/rcas/chat/session.rb +137 -0
- data/lib/rcas/chat/settings.rb +71 -0
- data/lib/rcas/chat/style.rb +30 -0
- data/lib/rcas/chat/tool.rb +53 -0
- data/lib/rcas/chat/ui.rb +316 -0
- data/lib/rcas/chat/usage.rb +62 -0
- data/lib/rcas/chat/workspace.rb +132 -0
- data/lib/rcas/chat.rb +54 -0
- data/lib/rcas/coefficients.rb +170 -0
- data/lib/rcas/combinatorics.rb +274 -0
- data/lib/rcas/complex_parts.rb +160 -0
- data/lib/rcas/constants.rb +129 -0
- data/lib/rcas/core_ext.rb +35 -0
- data/lib/rcas/decide.rb +501 -0
- data/lib/rcas/decompositions.rb +241 -0
- data/lib/rcas/differentiate.rb +144 -0
- data/lib/rcas/discussion.rb +558 -0
- data/lib/rcas/distributions.rb +980 -0
- data/lib/rcas/dixon.rb +95 -0
- data/lib/rcas/docs.rb +321 -0
- data/lib/rcas/domains.rb +728 -0
- data/lib/rcas/expand.rb +174 -0
- data/lib/rcas/expression.rb +613 -0
- data/lib/rcas/factor.rb +605 -0
- data/lib/rcas/finite_field.rb +577 -0
- data/lib/rcas/fourier.rb +118 -0
- data/lib/rcas/fps.rb +678 -0
- data/lib/rcas/fraction.rb +126 -0
- data/lib/rcas/functions.rb +1136 -0
- data/lib/rcas/gcd.rb +112 -0
- data/lib/rcas/geometry.rb +266 -0
- data/lib/rcas/groebner.rb +162 -0
- data/lib/rcas/hold.rb +277 -0
- data/lib/rcas/hypothesis.rb +364 -0
- data/lib/rcas/inequalities.rb +689 -0
- data/lib/rcas/integral_functions.rb +260 -0
- data/lib/rcas/integrate.rb +1589 -0
- data/lib/rcas/integrate_substitutions.rb +434 -0
- data/lib/rcas/interpolate.rb +40 -0
- data/lib/rcas/irb.rb +146 -0
- data/lib/rcas/laplace.rb +159 -0
- data/lib/rcas/latex.rb +556 -0
- data/lib/rcas/lattice.rb +172 -0
- data/lib/rcas/linear_algebra.rb +117 -0
- data/lib/rcas/linear_program.rb +416 -0
- data/lib/rcas/lint.rb +79 -0
- data/lib/rcas/matrix.rb +531 -0
- data/lib/rcas/matrix_multiply.rb +202 -0
- data/lib/rcas/multimodular.rb +286 -0
- data/lib/rcas/named_polynomials.rb +274 -0
- data/lib/rcas/number_theory.rb +443 -0
- data/lib/rcas/numerics.rb +825 -0
- data/lib/rcas/ode.rb +488 -0
- data/lib/rcas/openmath/objects.rb +364 -0
- data/lib/rcas/openmath/phrasebook.rb +551 -0
- data/lib/rcas/openmath/popcorn.rb +518 -0
- data/lib/rcas/openmath/xml.rb +309 -0
- data/lib/rcas/openmath.rb +49 -0
- data/lib/rcas/petkovsek.rb +165 -0
- data/lib/rcas/piecewise.rb +488 -0
- data/lib/rcas/plot.rb +763 -0
- data/lib/rcas/plot3d.rb +419 -0
- data/lib/rcas/poly_matrix.rb +318 -0
- data/lib/rcas/poly_recurrence.rb +117 -0
- data/lib/rcas/polynomial.rb +466 -0
- data/lib/rcas/precision.rb +925 -0
- data/lib/rcas/printer.rb +150 -0
- data/lib/rcas/product.rb +155 -0
- data/lib/rcas/q_difference.rb +296 -0
- data/lib/rcas/q_functions.rb +158 -0
- data/lib/rcas/q_summation.rb +308 -0
- data/lib/rcas/q_zeilberger.rb +199 -0
- data/lib/rcas/random.rb +506 -0
- data/lib/rcas/rational_function.rb +186 -0
- data/lib/rcas/recurrence.rb +323 -0
- data/lib/rcas/render.rb +431 -0
- data/lib/rcas/results.rb +192 -0
- data/lib/rcas/scalar.rb +219 -0
- data/lib/rcas/series.rb +726 -0
- data/lib/rcas/simplify.rb +649 -0
- data/lib/rcas/solve.rb +2002 -0
- data/lib/rcas/special.rb +163 -0
- data/lib/rcas/statistics.rb +175 -0
- data/lib/rcas/steps.rb +835 -0
- data/lib/rcas/summation.rb +532 -0
- data/lib/rcas/trig.rb +264 -0
- data/lib/rcas/van_hoeij.rb +241 -0
- data/lib/rcas/vector.rb +175 -0
- data/lib/rcas/vector_calculus.rb +411 -0
- data/lib/rcas/version.rb +5 -0
- data/lib/rcas/zeilberger.rb +358 -0
- data/lib/rcas.rb +91 -0
- data/package.json +8 -0
- metadata +206 -0
data/lib/rcas/expand.rb
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCAS
|
|
4
|
+
# Multiplies out products and integer powers of sums.
|
|
5
|
+
#
|
|
6
|
+
# ((:x + 1) * (1 - :x)).expand # => 1 - x**2
|
|
7
|
+
# ((:x + :y)**2).expand # => x**2 + 2*x*y + y**2
|
|
8
|
+
#
|
|
9
|
+
# Works on "tables": a sum is [constant, { factors => coefficient }] where
|
|
10
|
+
# factors is a { base => exponent } hash (the same shape Simplify uses).
|
|
11
|
+
# Like terms are merged after every multiplication, so a product of many
|
|
12
|
+
# sums never grows beyond the number of distinct monomials in the result.
|
|
13
|
+
module Expand
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def expand(expr)
|
|
17
|
+
rebuild(table(expr))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# => [constant, { factors => coefficient }]
|
|
21
|
+
# Sums and products are flattened with an explicit stack, so arbitrarily
|
|
22
|
+
# long chains (a + b + c + ...) don't recurse once per term.
|
|
23
|
+
def table(expr)
|
|
24
|
+
constant = 0
|
|
25
|
+
terms = {}
|
|
26
|
+
stack = [[expr, 1]]
|
|
27
|
+
until stack.empty?
|
|
28
|
+
e, sign = stack.pop
|
|
29
|
+
case e
|
|
30
|
+
when Add then stack.push([e.right, sign], [e.left, sign])
|
|
31
|
+
when Sub then stack.push([e.right, -sign], [e.left, sign])
|
|
32
|
+
when Neg then stack.push([e.arg, -sign])
|
|
33
|
+
when Num then constant += sign * e.value
|
|
34
|
+
else
|
|
35
|
+
c, t = term_table(e)
|
|
36
|
+
constant += sign * c
|
|
37
|
+
t.each { |f, coeff| add_term(terms, f, sign * coeff) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
[constant, terms]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Table of a non-additive node.
|
|
44
|
+
def term_table(expr)
|
|
45
|
+
case expr
|
|
46
|
+
when Var then [0, { { expr => 1 } => 1 }]
|
|
47
|
+
when Mul, Div then product_table(expr)
|
|
48
|
+
when Pow then power(expr)
|
|
49
|
+
when Fn then single(Fn.new(expr.name, expr.args.map { |a| expand(a) }))
|
|
50
|
+
else [0, { { expr => 1 } => 1 }] # constants, integrals, derivatives: atoms
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def product_table(expr)
|
|
55
|
+
factors = []
|
|
56
|
+
stack = [[expr, 1]]
|
|
57
|
+
until stack.empty?
|
|
58
|
+
e, power = stack.pop
|
|
59
|
+
case e
|
|
60
|
+
when Mul then stack.push([e.right, power], [e.left, power])
|
|
61
|
+
when Div then stack.push([e.right, -power], [e.left, power])
|
|
62
|
+
else factors << [e, power]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
factors.reduce([1, {}]) do |acc, (e, power)|
|
|
66
|
+
t = table(e)
|
|
67
|
+
multiply(acc, power == 1 ? t : invert(t))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def rebuild((constant, terms)) = Simplify.rebuild_sum(constant, terms)
|
|
72
|
+
|
|
73
|
+
# ---- table arithmetic -------------------------------------------------
|
|
74
|
+
|
|
75
|
+
def add((ca, ta), (cb, tb), sign)
|
|
76
|
+
terms = ta.dup
|
|
77
|
+
tb.each { |f, c| add_term(terms, f, sign * c) }
|
|
78
|
+
[ca + sign * cb, terms]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def scale((constant, terms), k)
|
|
82
|
+
return [0, {}] if k.zero?
|
|
83
|
+
[constant * k, terms.transform_values { |c| c * k }]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def multiply((ca, ta), (cb, tb))
|
|
87
|
+
constant = ca * cb
|
|
88
|
+
terms = {}
|
|
89
|
+
ta.each { |f, c| add_term(terms, f, c * cb) } unless cb.zero?
|
|
90
|
+
tb.each { |f, c| add_term(terms, f, c * ca) } unless ca.zero?
|
|
91
|
+
ta.each do |fa, xa|
|
|
92
|
+
tb.each do |fb, xb|
|
|
93
|
+
f, c = fold_numeric(multiply_factors(fa, fb), xa * xb)
|
|
94
|
+
if f.empty?
|
|
95
|
+
constant += c
|
|
96
|
+
else
|
|
97
|
+
add_term(terms, f, c)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
[constant, terms]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# A numeric base with an integer exponent (2**(1/2) * 2**(1/2) => 2**1)
|
|
105
|
+
# belongs in the coefficient.
|
|
106
|
+
def fold_numeric(factors, coeff)
|
|
107
|
+
numeric = factors.select { |b, e| b.is_a?(Num) && !b.value.is_a?(Complex) && e.is_a?(Integer) }
|
|
108
|
+
return [factors, coeff] if numeric.empty?
|
|
109
|
+
numeric.each { |b, e| coeff *= Simplify.pow_number(b.value, e) }
|
|
110
|
+
[factors.reject { |b, _| numeric.key?(b) }, Simplify.normalize_number(coeff)]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def invert(den)
|
|
114
|
+
constant, terms = den
|
|
115
|
+
if terms.empty?
|
|
116
|
+
raise ZeroDivisionError, "division by zero" if constant.zero?
|
|
117
|
+
[Simplify.normalize_number(Rational(1) / constant), {}]
|
|
118
|
+
elsif terms.size == 1 && constant.zero?
|
|
119
|
+
factors, coeff = terms.first
|
|
120
|
+
inverse = factors.to_h { |base, exp| [base, Simplify.multiply_exponents(exp, -1)] }
|
|
121
|
+
[0, { inverse => Simplify.normalize_number(Rational(1) / coeff) }]
|
|
122
|
+
else
|
|
123
|
+
[0, { { rebuild(den) => -1 } => 1 }]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def power(expr)
|
|
128
|
+
exp = expr.exponent
|
|
129
|
+
base = table(expr.base)
|
|
130
|
+
n = exp.value if exp.is_a?(Num) && exp.integer?
|
|
131
|
+
return [1, {}] if n == 0
|
|
132
|
+
return base if n == 1
|
|
133
|
+
if n && n > 1 && term_count(base) > 1
|
|
134
|
+
result = [1, {}]
|
|
135
|
+
while n.positive?
|
|
136
|
+
result = multiply(result, base) if n.odd?
|
|
137
|
+
base = multiply(base, base) if n > 1
|
|
138
|
+
n >>= 1
|
|
139
|
+
end
|
|
140
|
+
result
|
|
141
|
+
elsif n && n < -1 && term_count(base) > 1
|
|
142
|
+
# (x + y)**-2 is 1/(x + y)**2, and expand treats that quotient by
|
|
143
|
+
# expanding the denominator: the same here, or the two spellings of
|
|
144
|
+
# one expression had two expansions (third review, C10)
|
|
145
|
+
single(Pow.new(rebuild(power(Pow.new(rebuild(base), Num.new(-n)))), Num.new(-1)))
|
|
146
|
+
else
|
|
147
|
+
single(Pow.new(rebuild(base), expand(exp)))
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# A non-sum expression as a table, letting Simplify fold and factorize it.
|
|
152
|
+
def single(expr) = Simplify.termize(Simplify.simplify(expr))
|
|
153
|
+
|
|
154
|
+
def term_count((constant, terms)) = terms.size + (constant.zero? ? 0 : 1)
|
|
155
|
+
|
|
156
|
+
# A term that cancels is dropped - unless it carries infinity, which
|
|
157
|
+
# cancels to nothing at all: that one is kept so that Simplify's rebuild
|
|
158
|
+
# answers `undefined` rather than 0.
|
|
159
|
+
def add_term(terms, factors, coeff)
|
|
160
|
+
c = (terms[factors] || 0) + coeff
|
|
161
|
+
c.zero? && !factors.key?(OO) ? terms.delete(factors) : terms[factors] = c
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def multiply_factors(fa, fb)
|
|
165
|
+
merged = fa.dup
|
|
166
|
+
fb.each do |base, exp|
|
|
167
|
+
e = Simplify.add_exponents(merged[base] || 0, exp)
|
|
168
|
+
gone = e.is_a?(Numeric) && e.zero? && base != OO # oo/oo is undefined, not 1
|
|
169
|
+
gone ? merged.delete(base) : merged[base] = e
|
|
170
|
+
end
|
|
171
|
+
merged
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|