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
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"