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,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RCAS
4
+ # The named families of polynomials, in a namespace of their own: they are
5
+ # many, and bare names are precious (`legendre` is already the Legendre
6
+ # symbol of number theory, `bernoulli` the Bernoulli number).
7
+ #
8
+ # Poly.chebyshev_t(5, x) # => 5*x - 20*x**3 + 16*x**5
9
+ # Poly.legendre(4, x) # => 3/8 - 15*x**2/4 + 35*x**4/8
10
+ # Poly.cyclotomic(12, x) # => 1 - x**2 + x**4
11
+ #
12
+ # Each family is computed from its three-term recurrence (Jacobi: from its
13
+ # explicit sum) on lists of exact coefficients and handed back expanded, so
14
+ # the second argument may be any expression - `Poly.legendre(2, cos(t))` is
15
+ # the Legendre polynomial in cos(t), `Poly.hermite(3, 2)` a number. The
16
+ # parameters `alpha:` and `beta:` of the classical families may stay
17
+ # symbolic; they default to the indeterminates of the same name.
18
+ #
19
+ # Sources (keys: MANUAL.md, Sources): [AS64, ch. 22, ch. 23]; [Sze75];
20
+ # [GKP94, ch. 5, ch. 6]; [vzGG13, ch. 14].
21
+ module Poly
22
+ extend self
23
+
24
+ # Poly.chebyshev_t(5, x): the Chebyshev polynomial T_n of the first kind, T_n(cos(u)) = cos(n*u)
25
+ def chebyshev_t(n, x = :x)
26
+ build(three_term(degree(n, "chebyshev_t"), [num(1)], [num(0), num(1)]) { [num(2), num(0), num(-1)] }, x)
27
+ end
28
+
29
+ # Poly.chebyshev_u(4, x): the Chebyshev polynomial U_n of the second kind, U_n(cos(u)) = sin((n + 1)*u)/sin(u)
30
+ def chebyshev_u(n, x = :x)
31
+ build(three_term(degree(n, "chebyshev_u"), [num(1)], [num(0), num(2)]) { [num(2), num(0), num(-1)] }, x)
32
+ end
33
+
34
+ # Poly.legendre(4, x): the Legendre polynomial P_n, orthogonal on [-1, 1] with weight 1
35
+ def legendre(n, x = :x)
36
+ coeffs = three_term(degree(n, "legendre"), [num(1)], [num(0), num(1)]) do |k|
37
+ [num(Rational(2 * k - 1, k)), num(0), num(Rational(1 - k, k))]
38
+ end
39
+ build(coeffs, x)
40
+ end
41
+
42
+ # Poly.hermite(3, x): the physicists' Hermite polynomial H_n, weight exp(-x**2)
43
+ def hermite(n, x = :x)
44
+ coeffs = three_term(degree(n, "hermite"), [num(1)], [num(0), num(2)]) { |k| [num(2), num(0), num(2 - 2 * k)] }
45
+ build(coeffs, x)
46
+ end
47
+
48
+ # Poly.hermite_prob(3, x): the probabilists' Hermite polynomial He_n, weight exp(-x**2/2)
49
+ def hermite_prob(n, x = :x)
50
+ coeffs = three_term(degree(n, "hermite_prob"), [num(1)], [num(0), num(1)]) { |k| [num(1), num(0), num(1 - k)] }
51
+ build(coeffs, x)
52
+ end
53
+
54
+ # Poly.laguerre(3, x) or Poly.laguerre(3, x, alpha: 1): the (generalized) Laguerre polynomial L_n, weight x**alpha*exp(-x)
55
+ def laguerre(n, x = :x, alpha: 0)
56
+ a = Expression.lift(alpha)
57
+ coeffs = three_term(degree(n, "laguerre"), [num(1)], [Scalar.add(num(1), a), num(-1)]) do |k|
58
+ [num(Rational(-1, k)), # -x/k
59
+ scale(Scalar.add(a, num(2 * k - 1)), Rational(1, k)), # (2k - 1 + alpha)/k
60
+ scale(Scalar.add(a, num(k - 1)), Rational(-1, k))] # -(k - 1 + alpha)/k
61
+ end
62
+ build(coeffs, x)
63
+ end
64
+
65
+ # Poly.gegenbauer(3, x, alpha: 2): the Gegenbauer (ultraspherical) polynomial C_n, weight (1 - x**2)**(alpha - 1/2)
66
+ def gegenbauer(n, x = :x, alpha: :alpha)
67
+ a = Expression.lift(alpha)
68
+ coeffs = three_term(degree(n, "gegenbauer"), [num(1)], [num(0), Scalar.mul(num(2), a)]) do |k|
69
+ [scale(Scalar.add(a, num(k - 1)), Rational(2, k)), num(0), # 2(k - 1 + alpha)*x/k
70
+ scale(Scalar.add(Scalar.mul(num(2), a), num(k - 2)), Rational(-1, k))] # -(k - 2 + 2*alpha)/k
71
+ end
72
+ build(coeffs, x)
73
+ end
74
+
75
+ # Poly.jacobi(2, x, alpha: 1, beta: 2): the Jacobi polynomial P_n, weight (1 - x)**alpha*(1 + x)**beta
76
+ def jacobi(n, x = :x, alpha: :alpha, beta: :beta)
77
+ n = degree(n, "jacobi")
78
+ a = Expression.lift(alpha)
79
+ b = Expression.lift(beta)
80
+ left = [num(Rational(-1, 2)), num(Rational(1, 2))] # (x - 1)/2
81
+ right = [num(Rational(1, 2)), num(Rational(1, 2))] # (x + 1)/2
82
+ coeffs = (0..n).reduce([num(0)]) do |sum, s|
83
+ c = Scalar.mul(binomial_polynomial(Scalar.add(a, num(n)), n - s), binomial_polynomial(Scalar.add(b, num(n)), s))
84
+ padd(sum, pscale(pmul(ppow(left, s), ppow(right, n - s)), c))
85
+ end
86
+ build(coeffs, x)
87
+ end
88
+
89
+ # Poly.bernoulli(4, x): the Bernoulli polynomial B_n, with B_n(0) the Bernoulli number
90
+ def bernoulli(n, x = :x) = build(bernoulli_coeffs(degree(n, "bernoulli")), x)
91
+
92
+ # Poly.euler(3, x): the Euler polynomial E_n, from B_(n+1) by E_n(x) = 2/(n + 1)*(B_(n+1)(x) - 2**(n+1)*B_(n+1)(x/2))
93
+ def euler(n, x = :x)
94
+ n = degree(n, "euler")
95
+ coeffs = bernoulli_coeffs(n + 1).each_with_index.map do |c, k|
96
+ scale(c, Rational(2, n + 1) * (1 - 2**(n + 1 - k)))
97
+ end
98
+ build(coeffs, x)
99
+ end
100
+
101
+ # Poly.cyclotomic(12, x): the cyclotomic polynomial Phi_n, the minimal polynomial of a primitive n-th root of unity
102
+ def cyclotomic(n, x = :x)
103
+ n = degree(n, "cyclotomic")
104
+ raise ArgumentError, "cyclotomic: n must be positive" if n.zero?
105
+ build(cyclotomic_coeffs(n).map { |c| num(c) }, x)
106
+ end
107
+
108
+ # Poly.swinnerton_dyer(2, x): the minimal polynomial of sqrt(2) + sqrt(3) + ... over the first n primes, of degree 2**n
109
+ def swinnerton_dyer(n, x = :x)
110
+ n = degree(n, "swinnerton_dyer")
111
+ coeffs = primes(n).reduce([0, 1]) { |f, p| conjugate_product(f, p) }
112
+ build(coeffs.map { |c| num(c) }, x)
113
+ end
114
+
115
+ # Poly.abel(3, x) or Poly.abel(3, x, a: 2): the Abel polynomial A_n(x; a) = x*(x - a*n)**(n - 1)
116
+ def abel(n, x = :x, a: 1)
117
+ n = degree(n, "abel")
118
+ return build([num(1)], x) if n.zero?
119
+ root = Scalar.mul(Expression.lift(a), num(-n))
120
+ coeffs = ppow([root, num(1)], n - 1)
121
+ build(pshift(coeffs), x)
122
+ end
123
+
124
+ # Poly.fibonacci(6, x): the Fibonacci polynomial F_n, F_n(1) the Fibonacci number
125
+ def fibonacci(n, x = :x)
126
+ build(three_term(degree(n, "fibonacci"), [num(0)], [num(1)]) { [num(1), num(0), num(1)] }, x)
127
+ end
128
+
129
+ # Poly.lucas(5, x): the Lucas polynomial L_n, L_n(1) the Lucas number
130
+ def lucas(n, x = :x)
131
+ build(three_term(degree(n, "lucas"), [num(2)], [num(0), num(1)]) { [num(1), num(0), num(1)] }, x)
132
+ end
133
+
134
+ # Poly.bell(4, x): the Bell (Touchard) polynomial, the Stirling numbers of the second kind as coefficients; bell(n, 1) is the Bell number
135
+ def bell(n, x = :x)
136
+ build(stirling2_row(degree(n, "bell")).map { |c| num(c) }, x)
137
+ end
138
+
139
+ # ---- the machinery ------------------------------------------------------
140
+ #
141
+ # Polynomials are lists of coefficients, lowest degree first; every entry
142
+ # is an Expression, so a symbolic parameter is no different from a number.
143
+
144
+ private
145
+
146
+ def num(value) = Num.new(value)
147
+
148
+ # p_k = (a*x + b)*p_(k-1) + c*p_(k-2), from the two given lists.
149
+ def three_term(n, first, second)
150
+ return first if n.zero?
151
+ prev, cur = first, second
152
+ (2..n).each do |k|
153
+ a, b, c = yield(k)
154
+ prev, cur = cur, padd(padd(pscale(pshift(cur), a), pscale(cur, b)), pscale(prev, c))
155
+ end
156
+ cur
157
+ end
158
+
159
+ def padd(f, g) = Array.new([f.size, g.size].max) { |i| Scalar.add(f[i] || num(0), g[i] || num(0)) }
160
+ def pscale(f, c) = f.map { |a| Scalar.mul(a, c) }
161
+ def pshift(f, k = 1) = Array.new(k, num(0)) + f
162
+ def scale(a, r) = Scalar.mul(a, num(r))
163
+
164
+ def pmul(f, g)
165
+ out = Array.new(f.size + g.size - 1) { num(0) }
166
+ f.each_with_index do |a, i|
167
+ next if a.is_a?(Num) && a.value.zero?
168
+ g.each_with_index { |b, j| out[i + j] = Scalar.add(out[i + j], Scalar.mul(a, b)) }
169
+ end
170
+ out
171
+ end
172
+
173
+ def ppow(f, k) = k.zero? ? [num(1)] : (2..k).reduce(f) { |acc, _| pmul(acc, f) }
174
+
175
+ # The coefficient list as an expanded expression in x.
176
+ def build(coeffs, x)
177
+ v = Expression.lift(x)
178
+ terms = coeffs.each_with_index.reject { |c, _| c.is_a?(Num) && c.value.zero? }
179
+ return num(0) if terms.empty?
180
+ sum = terms.map { |c, i| i.zero? ? c : c * v**i }.reduce(:+)
181
+ sum.variables.empty? ? sum.simplify : sum.expand
182
+ end
183
+
184
+ def degree(n, name)
185
+ n = n.value if n.is_a?(Num)
186
+ raise ArgumentError, "#{name}: the degree must be a non-negative integer, got #{n}" unless n.is_a?(Integer) && !n.negative?
187
+ n
188
+ end
189
+
190
+ # binomial(u, k) with an integer k as the polynomial u(u-1)...(u-k+1)/k!,
191
+ # so that a symbolic alpha survives into the coefficients.
192
+ def binomial_polynomial(u, k) = Combinatorics.expand_binomial(u, num(k))
193
+
194
+ # B_n(x) = sum(binomial(n, k)*B_(n-k)*x**k), with the Bernoulli numbers of summation.rb.
195
+ def bernoulli_coeffs(n)
196
+ (0..n).map { |k| num(Summation.bernoulli(n - k) * binomial_integer(n, k)) }
197
+ end
198
+
199
+ def binomial_integer(n, k) = (0...k).reduce(1) { |acc, i| acc * (n - i) } / (1..k).reduce(1, :*)
200
+
201
+ # Stirling numbers of the second kind, S(n, k) for k = 0..n, by the
202
+ # recurrence S(n, k) = k*S(n-1, k) + S(n-1, k-1).
203
+ def stirling2_row(n)
204
+ (1..n).reduce([1]) do |row, _|
205
+ Array.new(row.size + 1) { |k| (k * (row[k] || 0)) + (k.positive? ? row[k - 1] : 0) }
206
+ end
207
+ end
208
+
209
+ # ---- cyclotomic and Swinnerton-Dyer, over the integers -------------------
210
+
211
+ @cyclotomic = {}
212
+
213
+ # x**n - 1 = product(Phi_d(x), d | n), so Phi_n is one exact division away.
214
+ def cyclotomic_coeffs(n)
215
+ @cyclotomic[n] ||= begin
216
+ numerator = Array.new(n + 1, 0)
217
+ numerator[0] = -1
218
+ numerator[n] = 1
219
+ divisors = (1...n).select { |d| (n % d).zero? }
220
+ divisors.reduce(numerator) { |acc, d| divide_exactly(acc, cyclotomic_coeffs(d)) }
221
+ end
222
+ end
223
+
224
+ # Division of integer coefficient lists, the divisor monic (it always is here).
225
+ def divide_exactly(f, g)
226
+ f = f.dup
227
+ quotient = Array.new(f.size - g.size + 1, 0)
228
+ (quotient.size - 1).downto(0) do |i|
229
+ c = f[i + g.size - 1]
230
+ quotient[i] = c
231
+ next if c.zero?
232
+ g.each_with_index { |b, j| f[i + j] -= c * b }
233
+ end
234
+ quotient
235
+ end
236
+
237
+ def primes(n)
238
+ found = []
239
+ candidate = 2
240
+ while found.size < n
241
+ found << candidate
242
+ candidate = NumberTheory.nextprime(candidate)
243
+ end
244
+ found
245
+ end
246
+
247
+ # f(x - sqrt(p))*f(x + sqrt(p)): writing f(x + s) = u(x) + s*v(x) with
248
+ # s**2 = p, the product is u**2 - p*v**2 and the radical is gone.
249
+ def conjugate_product(f, p)
250
+ u = Array.new(f.size, 0)
251
+ v = Array.new(f.size, 0)
252
+ f.each_with_index do |a, m|
253
+ next if a.zero?
254
+ (0..m).each do |j|
255
+ c = a * binomial_integer(m, j)
256
+ if j.even? then u[m - j] += c * p**(j / 2)
257
+ else v[m - j] += c * p**((j - 1) / 2)
258
+ end
259
+ end
260
+ end
261
+ square = multiply_integers(u, u)
262
+ correction = multiply_integers(v, v).map { |c| c * p }
263
+ Array.new(square.size) { |i| square[i] - (correction[i] || 0) }
264
+ end
265
+
266
+ def multiply_integers(f, g)
267
+ out = Array.new(f.size + g.size - 1, 0)
268
+ f.each_with_index { |a, i| g.each_with_index { |b, j| out[i + j] += a * b } unless a.zero? }
269
+ out
270
+ end
271
+ end
272
+
273
+ Constants.const_set(:Poly, Poly)
274
+ end