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/app.rb
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rcas"
|
|
4
|
+
require "rcas/chat"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
module RCAS
|
|
8
|
+
# A desktop window for rcas (bin/rcas-app): a worksheet of numbered
|
|
9
|
+
# In/Out cells, results typeset by KaTeX, plots drawn as SVG.
|
|
10
|
+
#
|
|
11
|
+
# It is a local Ruby program and a borrowed browser engine, and nothing
|
|
12
|
+
# else. App::Server (a page and two JSON calls, on the loopback interface)
|
|
13
|
+
# holds the session; App::Window opens one Chromium-family browser with
|
|
14
|
+
# `--app=URL`, the flag that draws a window with no tabs and no address
|
|
15
|
+
# bar. Closing the window ends the program.
|
|
16
|
+
#
|
|
17
|
+
# Borrowing the engine rather than shipping it is the one design decision
|
|
18
|
+
# worth knowing about: an Electron application of this kind carries its
|
|
19
|
+
# own copy of Chromium and weighs several hundred megabytes, while this
|
|
20
|
+
# is the library, one HTML page and three small Ruby files. The cost is
|
|
21
|
+
# the requirement that such a browser be installed - which rcas already
|
|
22
|
+
# asks for when it typesets with the :katex backend.
|
|
23
|
+
#
|
|
24
|
+
# The window is a third front end, beside bin/rcas (irb) and
|
|
25
|
+
# bin/rcas-chat (terminal); all three share the session of RCAS::Results,
|
|
26
|
+
# so In[3] and Out[3] mean the same thing in each.
|
|
27
|
+
module App
|
|
28
|
+
class Error < StandardError; end
|
|
29
|
+
|
|
30
|
+
# Where the window starts looking for a free port.
|
|
31
|
+
DEFAULT_PORT = 0
|
|
32
|
+
|
|
33
|
+
USAGE = <<~TEXT
|
|
34
|
+
usage: rcas-app [options]
|
|
35
|
+
|
|
36
|
+
--port N listen on this port (default: any free one)
|
|
37
|
+
--output MODE text, typeset (default), both or latex
|
|
38
|
+
--theme NAME dark, light or auto (default)
|
|
39
|
+
--no-window run the server only and print the address
|
|
40
|
+
--install add rcas to the Dock / Start menu / applications
|
|
41
|
+
--uninstall remove it again
|
|
42
|
+
--version print the version
|
|
43
|
+
--help this text
|
|
44
|
+
TEXT
|
|
45
|
+
|
|
46
|
+
module_function
|
|
47
|
+
|
|
48
|
+
def root = File.expand_path("../..", __dir__)
|
|
49
|
+
def assets = File.join(__dir__, "app", "public")
|
|
50
|
+
def logo = [File.join(root, "assets", "rcas-logo.jpeg")].find { |f| File.file?(f) }
|
|
51
|
+
|
|
52
|
+
# The KaTeX distribution, when it is installed (npm install): the page
|
|
53
|
+
# typesets with it and falls back to the LaTeX source without it.
|
|
54
|
+
def katex_dir
|
|
55
|
+
return @katex_dir if defined?(@katex_dir)
|
|
56
|
+
package = Render::KaTeX.katex_dir
|
|
57
|
+
@katex_dir = package && File.directory?(File.join(package, "dist")) ? File.join(package, "dist") : nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Build the server for +worksheet+: the page, its assets, and the two
|
|
61
|
+
# calls the page makes.
|
|
62
|
+
def build(worksheet, port: DEFAULT_PORT)
|
|
63
|
+
server = Server.new(root: assets, port: port)
|
|
64
|
+
server.mount("/katex", katex_dir) if katex_dir
|
|
65
|
+
server.post("/api/eval") do |request|
|
|
66
|
+
json(worksheet.submit(request.json["source"], theme: request.json["theme"]))
|
|
67
|
+
end
|
|
68
|
+
server.post("/api/complete") do |request|
|
|
69
|
+
json(candidates: worksheet.complete(request.json["prefix"]))
|
|
70
|
+
end
|
|
71
|
+
server.post("/api/incomplete") do |request|
|
|
72
|
+
json(incomplete: worksheet.incomplete?(request.json["source"]))
|
|
73
|
+
end
|
|
74
|
+
server.post("/api/state") { json(worksheet.state) }
|
|
75
|
+
server.post("/api/cells") { json(cells: worksheet.cells) }
|
|
76
|
+
server.get("/api/logo") do
|
|
77
|
+
logo ? [200, "image/jpeg", File.binread(logo)] : [404, "text/plain", Server::NOT_FOUND]
|
|
78
|
+
end
|
|
79
|
+
server
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def json(value) = [200, "application/json; charset=utf-8", JSON.generate(value)]
|
|
83
|
+
|
|
84
|
+
# ---- the program ----------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
def start(argv = [])
|
|
87
|
+
options = parse(argv)
|
|
88
|
+
return puts(USAGE) if options[:help]
|
|
89
|
+
return puts("rcas #{RCAS::VERSION}") if options[:version]
|
|
90
|
+
return install if options[:install]
|
|
91
|
+
return uninstall if options[:uninstall]
|
|
92
|
+
run(options)
|
|
93
|
+
rescue Error => e
|
|
94
|
+
warn "rcas-app: #{e.message}"
|
|
95
|
+
1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def parse(argv)
|
|
99
|
+
options = { port: DEFAULT_PORT, window: true }
|
|
100
|
+
until argv.empty?
|
|
101
|
+
case (flag = argv.shift)
|
|
102
|
+
when "--help", "-h" then options[:help] = true
|
|
103
|
+
when "--version", "-v" then options[:version] = true
|
|
104
|
+
when "--install" then options[:install] = true
|
|
105
|
+
when "--uninstall" then options[:uninstall] = true
|
|
106
|
+
when "--no-window" then options[:window] = false
|
|
107
|
+
when "--port" then options[:port] = argv.shift.to_i
|
|
108
|
+
when "--output" then options[:mode] = argv.shift
|
|
109
|
+
when "--theme" then options[:theme] = argv.shift
|
|
110
|
+
else raise Error, "unknown option #{flag} (try --help)"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
options
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def install
|
|
117
|
+
path = Launcher.install
|
|
118
|
+
puts "rcas: installed #{path}"
|
|
119
|
+
puts "rcas: #{Window.missing}" unless Window.available?
|
|
120
|
+
0
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def uninstall
|
|
124
|
+
path = Launcher.uninstall
|
|
125
|
+
puts path ? "rcas: removed #{path}" : "rcas: nothing installed"
|
|
126
|
+
0
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def run(options)
|
|
130
|
+
# The program prints one line and then waits for the window, so its
|
|
131
|
+
# output has to arrive now and not when the pipe is closed.
|
|
132
|
+
$stdout.sync = true
|
|
133
|
+
settings = Chat::Settings.apply(Chat::Settings.load)
|
|
134
|
+
worksheet = Worksheet.new(mode: options[:mode] || settings["output"], theme: options[:theme] || settings["theme"])
|
|
135
|
+
server = build(worksheet, port: options[:port])
|
|
136
|
+
server.start
|
|
137
|
+
raise Error, Window.missing if options[:window] && !Window.available?
|
|
138
|
+
listener = Thread.new { server.run }
|
|
139
|
+
listener.abort_on_exception = false
|
|
140
|
+
options[:window] ? show(server) : serve(server, listener)
|
|
141
|
+
0
|
|
142
|
+
ensure
|
|
143
|
+
server&.stop
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Open the window and stay alive while it is open.
|
|
147
|
+
def show(server)
|
|
148
|
+
puts "rcas #{RCAS::VERSION} - #{File.basename(Window.browser)} window on #{server.host}:#{server.port}"
|
|
149
|
+
pid = Window.open(server.url)
|
|
150
|
+
Window.wait(pid)
|
|
151
|
+
rescue Interrupt
|
|
152
|
+
nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# --no-window: print the address and wait to be interrupted.
|
|
156
|
+
def serve(server, listener)
|
|
157
|
+
puts "rcas #{RCAS::VERSION} - open #{server.url}"
|
|
158
|
+
listener.join
|
|
159
|
+
rescue Interrupt
|
|
160
|
+
nil
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
require_relative "app/server"
|
|
166
|
+
require_relative "app/worksheet"
|
|
167
|
+
require_relative "app/window"
|
|
168
|
+
require_relative "app/launcher"
|