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.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/CITATION.cff +17 -0
  3. data/DESIGN.md +783 -0
  4. data/LICENSE +21 -0
  5. data/MANUAL.md +6265 -0
  6. data/README.md +267 -0
  7. data/bin/rcas +9 -0
  8. data/bin/rcas-app +9 -0
  9. data/bin/rcas-chat +9 -0
  10. data/lib/rcas/algebraic.rb +481 -0
  11. data/lib/rcas/analysis.rb +966 -0
  12. data/lib/rcas/app/launcher.rb +203 -0
  13. data/lib/rcas/app/public/app.css +402 -0
  14. data/lib/rcas/app/public/app.js +449 -0
  15. data/lib/rcas/app/public/index.html +46 -0
  16. data/lib/rcas/app/server.rb +220 -0
  17. data/lib/rcas/app/window.rb +94 -0
  18. data/lib/rcas/app/worksheet.rb +290 -0
  19. data/lib/rcas/app.rb +168 -0
  20. data/lib/rcas/background.rb +758 -0
  21. data/lib/rcas/chat/assistant.rb +199 -0
  22. data/lib/rcas/chat/picker.rb +164 -0
  23. data/lib/rcas/chat/repl.rb +583 -0
  24. data/lib/rcas/chat/session.rb +137 -0
  25. data/lib/rcas/chat/settings.rb +71 -0
  26. data/lib/rcas/chat/style.rb +30 -0
  27. data/lib/rcas/chat/tool.rb +53 -0
  28. data/lib/rcas/chat/ui.rb +316 -0
  29. data/lib/rcas/chat/usage.rb +62 -0
  30. data/lib/rcas/chat/workspace.rb +132 -0
  31. data/lib/rcas/chat.rb +54 -0
  32. data/lib/rcas/coefficients.rb +170 -0
  33. data/lib/rcas/combinatorics.rb +274 -0
  34. data/lib/rcas/complex_parts.rb +160 -0
  35. data/lib/rcas/constants.rb +129 -0
  36. data/lib/rcas/core_ext.rb +35 -0
  37. data/lib/rcas/decide.rb +501 -0
  38. data/lib/rcas/decompositions.rb +241 -0
  39. data/lib/rcas/differentiate.rb +144 -0
  40. data/lib/rcas/discussion.rb +558 -0
  41. data/lib/rcas/distributions.rb +980 -0
  42. data/lib/rcas/dixon.rb +95 -0
  43. data/lib/rcas/docs.rb +321 -0
  44. data/lib/rcas/domains.rb +728 -0
  45. data/lib/rcas/expand.rb +174 -0
  46. data/lib/rcas/expression.rb +613 -0
  47. data/lib/rcas/factor.rb +605 -0
  48. data/lib/rcas/finite_field.rb +577 -0
  49. data/lib/rcas/fourier.rb +118 -0
  50. data/lib/rcas/fps.rb +678 -0
  51. data/lib/rcas/fraction.rb +126 -0
  52. data/lib/rcas/functions.rb +1136 -0
  53. data/lib/rcas/gcd.rb +112 -0
  54. data/lib/rcas/geometry.rb +266 -0
  55. data/lib/rcas/groebner.rb +162 -0
  56. data/lib/rcas/hold.rb +277 -0
  57. data/lib/rcas/hypothesis.rb +364 -0
  58. data/lib/rcas/inequalities.rb +689 -0
  59. data/lib/rcas/integral_functions.rb +260 -0
  60. data/lib/rcas/integrate.rb +1589 -0
  61. data/lib/rcas/integrate_substitutions.rb +434 -0
  62. data/lib/rcas/interpolate.rb +40 -0
  63. data/lib/rcas/irb.rb +146 -0
  64. data/lib/rcas/laplace.rb +159 -0
  65. data/lib/rcas/latex.rb +556 -0
  66. data/lib/rcas/lattice.rb +172 -0
  67. data/lib/rcas/linear_algebra.rb +117 -0
  68. data/lib/rcas/linear_program.rb +416 -0
  69. data/lib/rcas/lint.rb +79 -0
  70. data/lib/rcas/matrix.rb +531 -0
  71. data/lib/rcas/matrix_multiply.rb +202 -0
  72. data/lib/rcas/multimodular.rb +286 -0
  73. data/lib/rcas/named_polynomials.rb +274 -0
  74. data/lib/rcas/number_theory.rb +443 -0
  75. data/lib/rcas/numerics.rb +825 -0
  76. data/lib/rcas/ode.rb +488 -0
  77. data/lib/rcas/openmath/objects.rb +364 -0
  78. data/lib/rcas/openmath/phrasebook.rb +551 -0
  79. data/lib/rcas/openmath/popcorn.rb +518 -0
  80. data/lib/rcas/openmath/xml.rb +309 -0
  81. data/lib/rcas/openmath.rb +49 -0
  82. data/lib/rcas/petkovsek.rb +165 -0
  83. data/lib/rcas/piecewise.rb +488 -0
  84. data/lib/rcas/plot.rb +763 -0
  85. data/lib/rcas/plot3d.rb +419 -0
  86. data/lib/rcas/poly_matrix.rb +318 -0
  87. data/lib/rcas/poly_recurrence.rb +117 -0
  88. data/lib/rcas/polynomial.rb +466 -0
  89. data/lib/rcas/precision.rb +925 -0
  90. data/lib/rcas/printer.rb +150 -0
  91. data/lib/rcas/product.rb +155 -0
  92. data/lib/rcas/q_difference.rb +296 -0
  93. data/lib/rcas/q_functions.rb +158 -0
  94. data/lib/rcas/q_summation.rb +308 -0
  95. data/lib/rcas/q_zeilberger.rb +199 -0
  96. data/lib/rcas/random.rb +506 -0
  97. data/lib/rcas/rational_function.rb +186 -0
  98. data/lib/rcas/recurrence.rb +323 -0
  99. data/lib/rcas/render.rb +431 -0
  100. data/lib/rcas/results.rb +192 -0
  101. data/lib/rcas/scalar.rb +219 -0
  102. data/lib/rcas/series.rb +726 -0
  103. data/lib/rcas/simplify.rb +649 -0
  104. data/lib/rcas/solve.rb +2002 -0
  105. data/lib/rcas/special.rb +163 -0
  106. data/lib/rcas/statistics.rb +175 -0
  107. data/lib/rcas/steps.rb +835 -0
  108. data/lib/rcas/summation.rb +532 -0
  109. data/lib/rcas/trig.rb +264 -0
  110. data/lib/rcas/van_hoeij.rb +241 -0
  111. data/lib/rcas/vector.rb +175 -0
  112. data/lib/rcas/vector_calculus.rb +411 -0
  113. data/lib/rcas/version.rb +5 -0
  114. data/lib/rcas/zeilberger.rb +358 -0
  115. data/lib/rcas.rb +91 -0
  116. data/package.json +8 -0
  117. metadata +206 -0
@@ -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