├── .Rbuildignore ├── .gitmodules ├── CRAN-SUBMISSION ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── RcppExports.R ├── engine.R ├── engine.utils.R ├── hooks.R ├── interface.R ├── options.R ├── package.R ├── repl.R └── zzz.R ├── README.Rmd ├── README.md ├── cran-comments.md ├── docs ├── index.Rmd └── index.html ├── index.R ├── inst └── extdata │ ├── display.lisp │ ├── display.mac │ ├── draw-ref.png │ ├── draw2d-ref.png │ ├── draw3d-ref.png │ ├── maxima-init-2d.mac │ ├── maxima-init-lin.mac │ ├── maxima-init-mathml.mac │ ├── maxima-init-tex.mac │ ├── maxima-init.mac │ ├── parser.lisp │ ├── plot-knitr-pdf.lisp │ ├── plot-knitr-png.lisp │ ├── plot2d-ref.png │ ├── plot3d-ref.png │ ├── rds.lisp │ ├── result.md │ ├── test-forms.lisp │ ├── test.Rmd │ └── test.lisp ├── man ├── maxima.engine.Rd ├── maxima.options.Rd ├── maxima.repl.Rd ├── rim-package.Rd └── rim_global.Rd ├── rim-logo.svg ├── run-in-docker.sh ├── src ├── RcppExports.cpp └── utils.cpp └── tests ├── testthat.R └── testthat ├── test-apropos.R ├── test-ask-user.R ├── test-engine.R ├── test-errors.R ├── test-execute.R ├── test-load.R ├── test-parser.R └── test-warnings.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^R/rmaxima.R$ 2 | ^index.R$ 3 | ^[[:print:]]+\.sh$ 4 | ^[[:print:]]+\.pdf$ 5 | ^README\.html$ 6 | ^[[:print:]]+\.o$ 7 | ^tests/testthat/testthat-problems.rds$ 8 | ^boost_dependencies.html$ 9 | ^test\.pdf$ 10 | ^src/libexecstream/.git$ 11 | ^src/tiny-process-library 12 | ^docs 13 | ^cran-comments.md$ 14 | ^README.Rmd$ 15 | ^CRAN-RELEASE$ 16 | ^.*\.Rproj$ 17 | ^\.Rproj\.user$ 18 | ^rim-logo\.svg$ 19 | ^inst/extdata/binary 20 | ^CRAN-SUBMISSION$ 21 | ^run-in-docker.sh$ 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcst/rim/6a54a3d761e621797673aed07099c1d13d708d87/.gitmodules -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 0.8.0 2 | Date: 2025-04-01 06:54:37 UTC 3 | SHA: 4c1aea98258d9b038147d2c4fd6c1c8851dea764 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rim 2 | Type: Package 3 | Title: Interface to 'Maxima', Enabling Symbolic Computation 4 | Version: 0.8.0 5 | Date: 2025-04-01 6 | Authors@R: c( 7 | person("Eric", "Stemmler", email = "stemmler.eric@gmail.com", role = c("aut", "cre")), 8 | person("Kseniia", "Shumelchyk", email = "shumelchyk@gmail.com", role = c("aut")), 9 | person("Hans W.", "Borchers", email = "hwborchers@googlemail.com", role = c("aut"))) 10 | Description: An interface to the powerful and fairly complete computer algebra system 'Maxima'. 11 | It can be used to start and control 'Maxima' from within R by entering 'Maxima' commands. 12 | Results from 'Maxima' can be parsed and evaluated in R. 13 | It facilitates outputting results from 'Maxima' in 'LaTeX' and 'MathML'. 14 | 2D and 3D plots can be displayed directly. 15 | This package also registers a 'knitr'-engine enabling 'Maxima' code chunks 16 | to be written in 'RMarkdown' documents. 17 | URL: https://rcst.github.io/rim/ 18 | BugReports: https://github.com/rcst/rim/issues 19 | SystemRequirements: Maxima (https://sourceforge.net/projects/maxima/, tested with versions 5.42.0 - 5.46.0), needs to be on PATH 20 | License: GPL (>= 3) 21 | Imports: 22 | methods, 23 | Rcpp, 24 | R6, 25 | knitr, 26 | GlobalOptions 27 | LinkingTo: Rcpp 28 | RoxygenNote: 7.3.2 29 | Suggests: 30 | testthat (>= 3.0.0), 31 | rmarkdown 32 | Config/testthat/edition: 3 33 | Encoding: UTF-8 34 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,maxima) 4 | export(iprint) 5 | export(maxima.apropos) 6 | export(maxima.eval) 7 | export(maxima.get) 8 | export(maxima.inline) 9 | export(maxima.isInstalled) 10 | export(maxima.load) 11 | export(maxima.options) 12 | export(maxima.repl) 13 | export(maxima.start) 14 | export(maxima.stop) 15 | export(maxima.version) 16 | export(rim_global) 17 | import(GlobalOptions) 18 | import(R6) 19 | import(methods) 20 | importFrom(Rcpp,sourceCpp) 21 | importFrom(utils,tail) 22 | useDynLib(rim, .registration = TRUE) 23 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # rim 0.8.0 2 | ## Modifications 3 | - fixed NOTE for assigment into `globalenv()` when checking package 4 | 5 | ## Minor 6 | - added REPL mode (`maxima.repl()`) 7 | 8 | # rim 0.7.1 9 | ## Modifications 10 | - R6 class finalize() now private, as required by package 11 | - fixed failing example for `maxima.load()` under Fedora (#47) and added testing file 12 | 13 | # rim 0.7.0 14 | ## Modification 15 | - fixes faulty parsing of matrices 16 | - re-factored printing method `maxima.print` and `iprint`, which now reliably detected whether called from interactive mode or from knitr engine, thus removed internal function `engine_print()` (Maxima S3 object now gained `from_engine` flag) 17 | - running code chunks interactively (from RStudio) now works as expected using the "interactive" Maxima connection object it's options accordingly 18 | - comment handling now works (both interactive and engine), fixed an infinite loop occurrence when sent command ended with a comment, comment-only lines of code inside code-chunks are now properly printed 19 | - empty lines in code chunks are now preserved as inputted 20 | - fixed engine test failure caused by different output code width settings and unneccessarily inserted linebreaks between code input and output 21 | 22 | # rim 0.6.4 23 | ## Modification 24 | - new options `max.attempts` and `sleep.seconds` to control repeated testing for finished rendering of images 25 | - fixed issue #37 26 | 27 | # rim 0.6.3 28 | ## Modification 29 | - fixed error under Fedora 36+ caused by Maxima image inclusion, when the image 30 | file isn't guaranteed to exist when it's file path is returned from Maxima 31 | 32 | # rim 0.6.2 33 | ## Modification 34 | - added more reliable detection for output document - Maxima knitr engine 35 | (fixes previous CRAN test failure for Fedora 36+). 36 | 37 | # rim 0.6.1 38 | ## Modification 39 | - fixed bug from CRAN check failure on Fedora and R-devel 40 | 41 | # rim 0.6.0 42 | ## Minor 43 | - added new function `maxima.eval()` that can either take a character string or an S3 object of class "maxima" (as returned from `maxima.get()`). In either case, the output result from Maxima (if not suppressed) is parsed into a R-expression and gets evaluated by this function. Objects returned from `maxima.get()` carry an attributed named "parsed" that is the unevaluted R-expression of the returned Maxima result. Parsing works for 44 | - unary and binary operators 45 | - named function calls 46 | - function definitions 47 | - lambda function calls 48 | - memoizing array function definition (those are currently simply parsed into "normal" functions) 49 | - arrays: creating and setting 50 | - matrices: creation, multiplication, transposition, inversion, determinants 51 | - lists 52 | - conditionals 53 | - added engine option `output.var` that can name a variable to capture parsed Maxima output into a named list. The names of this list are the output labels. 54 | 55 | # rim 0.5.3 56 | ## Modification 57 | - user can specify custom path to Maxima executable by setting environment variable "RIM_MAXIMA_PATH" 58 | 59 | # rim 0.5.2 60 | ## Modification 61 | - fixed tests to pass for Fedora 36 62 | - built-in safeguards against infinite loops when Maxima couldn't be started or initialized (issue #29) 63 | - reading process ID and version number now using class MReader 64 | 65 | # rim 0.5.1 66 | ## Modification 67 | - `knitr`-engine now supports chunk options `echo`, `eval` and `include`, a description can be found in the documentation page 68 | - fixed several issues related to changes with Maxima version 5.46.0 69 | 70 | # rim 0.5.0 71 | ## Minor 72 | - `knitr`-engine now supports plotting commands: plot2d(), plot3d(), draw(), draw2d(), draw3d() (and others that are based on those mentioned), depending on the output format (PDF or HTML), plots are saved as PDF or PNG respectively 73 | - Added inline output function to be used when knitting `RMarkdown` documents. 74 | - Restructured the return type of the get function: Instead of a character string, now a list is returned storing every output format of the result including both with and without reference labels. 75 | - Added a global option handling function `maxima.options()` and removed individual option setting functions `maxima.setformat()`, `maxima.engine.format()` and friends 76 | - Whether reference labels are printed or not when knitting a `RMarkdown` document can now be controlled by setting `maxima.options(engine.label)` 77 | 78 | ## Modification 79 | - Removed some unused utility functions from code 80 | - Removed unnecessary C++17 compiler flag 81 | - Improved documentation 82 | - Updated heuristics for catching warnings and errors 83 | - Improved typesetting of output labels when knitting `RMarkdown` documents 84 | - Improved warning/ error handling heuristics (fixing issue #20) 85 | 86 | # rim 0.4.1 87 | ## Modification 88 | - fixed issue #16. code chunks that contain commands spanning multiple lines are now printed as-is (except for empty lines, which are removed) with the preceding input reference label 89 | - fixed the issue #17, caused by a socket-read-write-concurrency conflict which led to split lines read from Maxima and a failure to validate it's output. This is achieved now by an internal helper R6 class `MReader` that stashes away incomplete lines and completes it upon re-calling. 90 | - fixed reference manual that is generated by CRAN, but previously did not contain the package main functions 91 | - Improved description in `DESCRIPTION` of `SystemRequirements` 92 | - added exported function `maxima.isInstalled()` that is now used upon package attachment, where a message is signaled to install Maxima, if no installation was found. 93 | - added exported function `maxima.version()` which is now used upon package attachment, where a message is signaled when the installed version is lower than the version with which this package has been tested 94 | - Test file `tests/testthat/test-engine.R`: knitting test page now suppresses Warnings, which are subject of `tests/testthat/test-warnings.R` 95 | 96 | # rmaxima 0.4.0/ rim 0.4.0 97 | - added `maxima.engine.format()` function that can be used to change the output format of the `knitr` engine 98 | - fixed issue that arises when Maxima takes longer to start (which previously caused the interface to freeze) 99 | - added functions to record the version number of Maxima that is being used 100 | - renamed package to "rim" to avoid confusion with `maxima`'s `rmaxima` 101 | - communication is now implemented using sockets and the processing is handles by two nested R6 classes 102 | - return type is now a S3 class of type maxima. There are two methods `iprint()` and `oprint()` for printing an maxima S3 object: printing the input command and output respectively, including reference labels. 103 | - removed external C++ library dependencies (now using sockets for communicating with Maxima) 104 | - `knitr` engine now prints output after each input line, it also prints the input reference label in front of the command 105 | 106 | # rmaxima 0.0.0.9000 107 | 108 | - removed dependency from boost (switched to libexecstream) 109 | - maxima.get() returns character vector with added attributes for reference labels, format and it's originating command 110 | - fixed Maxima error (message) forwarding to R 111 | - implement `knitr`-engine for maxima using this interface 112 | - Implement stop function to end maxima child process for debugging purposes 113 | - Handles `asksign` and similar feedback interruptions 114 | - Added interface to Maxima's apropos()-function 115 | - Fixed: If command terminates with `\$` then this causes a segmentation fault, which kills the R process. The cause being that maxima returns immediately with the next input prompt 116 | - added `roxygen2` documentation 117 | - fixed `system.file` call inside constructor to work with `devtools::load_all()` 118 | - Added a `NEWS.md` file to track changes to the package. 119 | - Add test files 120 | - implemented output display as `tex` (not yet user-friendly) 121 | - added initialization files 122 | - added function void `loadModule(const std::string &s)` 123 | - Removed boost::regex linking dependency 124 | - fixed pipe stream synchronization 125 | -------------------------------------------------------------------------------- /R/RcppExports.R: -------------------------------------------------------------------------------- 1 | # Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | trim <- function(s) { 5 | .Call(`_rim_trim`, s) 6 | } 7 | 8 | checkCommand <- function(command) { 9 | .Call(`_rim_checkCommand`, command) 10 | } 11 | 12 | dissect_chunk <- function(code) { 13 | .Call(`_rim_dissect_chunk`, code) 14 | } 15 | 16 | dissect_repl_input <- function(input) { 17 | .Call(`_rim_dissect_repl_input`, input) 18 | } 19 | 20 | -------------------------------------------------------------------------------- /R/engine.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables(c("engine", "mx", "plots")) 2 | #' \code{knitr} maxima engine 3 | #' 4 | #' Functions to process Maxima code chunks by \code{knitr}. 5 | #' 6 | #' Upon attachment, i.e. \code{library(rim)} function \code{maxima.engine} is registered as a \code{knitr} engine. Thus, \code{maxima.engine()} is called by \code{knit()} to evaluate Maxima code chunks. When called upon the first code chunk of a document it starts Maxima in a separate process in server mode. This means that a single Maxima session is used for all Maxima code chunks of an \code{RMarkdown} document. Inputs and outputs can thus be used across chunks (e.g. by using Maxima reference labels). \code{maxima.options(engine.format = ..., engine.label = ...)} configures the output format and whether or not output reference labels should be printed. 7 | #' 8 | #' The purpose of \code{maxima.inline} is to insert Maxima results as inline text, i.e. on the same line of the preceding text, if it is actually written on the same line of the \code{RMarkdown} file. It uses the same running Maxima process as \code{maxima.engine}. The output format for inline results can be configured separately from the settings of \code{maxima.engine}, i.e. \code{maxima.options(inline.format = ..., inline.label = ...)}. 9 | #' 10 | #' @param options named \code{list} of \code{knitr} options. Supported options are \code{echo}, \code{eval}, \code{include} and \code{output.var}. To change the output format of the Maxima engine set the option \code{maxima.options(engine.format)} to either \code{"linear"} (default), \code{"ascii"}, \code{"latex"} or \code{"mathml"}. 11 | #' 12 | #' @return This functions prints the resulting output from maxima together with it's cod 13 | maxima.engine <- function(options) { 14 | maxima.engine.start() 15 | 16 | out_code <- options$code 17 | ccode <- character(0) 18 | output.data <- list() 19 | out_res <- character(0) 20 | ll <- list() 21 | 22 | ov <- !is.null(varname <- options$output.var) 23 | cmds <- dissect_chunk(out_code) 24 | 25 | if (options$eval) { 26 | for (i in 1:length(cmds)) { 27 | pc <- paste0(out_code[cmds[[i]]], collapse = "\n") 28 | # if plotting command, then it needs to end with ";" 29 | if (grepl(pattern = "^(?:plot|draw)(?:2d|3d)?\\([[:print:]|[:space:]]+\\)[[:space:]]*\\$$", x = pc)) { 30 | pc <- gsub(pattern = "\\$", replacement = ";", x = pc) 31 | } 32 | tt <- maxima.env$mx$get(pc) 33 | 34 | attr(tt, "from_engine") <- TRUE 35 | 36 | out_code[cmds[[i]]] <- unlist(strsplit(x = iprint(tt), split = '\n', fixed = TRUE)) 37 | ccode <- append(ccode, unlist(strsplit(x = iprint(tt), split = '\n', fixed = TRUE))) 38 | if (!attr(tt, "suppressed")) { 39 | ll <- append(ll, list(structure(list(src = ccode), class = "source"))) 40 | if (ov) 41 | output.data[[substring(attr(tt, "output.label"), 2L)]] <- attr(tt, "parsed") 42 | 43 | # +++ FIGURE OUTPUT +++ 44 | if (grepl(pattern = "^(?:plot|draw)(?:2d|3d)?\\([[:print:]|[:space:]]+\\)[[:space:]]*;", x = pc) & 45 | !is.null(knitr::all_labels(engine == "maxima"))) { 46 | tt$wol$ascii <- paste0(tt$wol$ascii, collapse = "") 47 | pm <- regexec(pattern = "\\[?([[:graph:]]*/?\\.?(?:plot|draw)(?:2d|3d)?-[a-z0-9]+\\.(?:png|pdf))\\]$", text = tt$wol$ascii) 48 | pm <- trim(unlist(regmatches(m = pm, x = tt$wol$ascii))[2]) 49 | 50 | # possibly image not yet written to disk 51 | pm <- retry_include_graphics(pm) 52 | ll <- append(ll, list(pm)) 53 | out_res <- c(out_res, list(pm)) 54 | maxima.env$plots <- append(maxima.env$plots, normalizePath(pm, mustWork = FALSE)) 55 | } else { 56 | # +++ TEXT OUTPUT +++ 57 | ll <- append(ll, ttt <- print(tt)) 58 | out_res <- c(out_res, ttt) 59 | } 60 | ccode <- character(0) 61 | } 62 | } 63 | 64 | if (ov) { 65 | assign(varname, output.data, envir = rim_global()) 66 | } 67 | } 68 | 69 | # called_from_fn("knit") not needed since engine is ALWAYS called from knit 70 | if (last_label(options$label) & called_from_fn("knit")) { 71 | maxima.engine.stop() 72 | } 73 | 74 | # special handling for RStudio 75 | if(is_interactive()) { 76 | knitr::engine_output(knitr::opts_current$merge(list(engine = 'maxima', 77 | lang = 'maxima', 78 | results = 'markup')), 79 | code = NULL, 80 | out = out_res) 81 | } else { 82 | knitr::engine_output(knitr::opts_current$merge(list(engine = 'maxima', 83 | lang = 'maxima', 84 | results = maxima.options$engine.results)), 85 | out = ll) 86 | } 87 | } 88 | 89 | maxima.engine.start <- function() { 90 | if(is.null(knitr::all_labels(engine == "maxima"))) { 91 | maxima.env$mx <- maxima.env$maxima 92 | } else { 93 | if (!exists("mx", envir = maxima.env)) { 94 | maxima.env$mx <- RMaxima$new( 95 | display = maxima.options$display, 96 | preload = maxima.options$preload[knitr::is_latex_output() + 1] 97 | ) 98 | maxima.env$plots <- character() 99 | } 100 | } 101 | } 102 | 103 | maxima.engine.stop <- function() { 104 | if(!is.null(knitr::all_labels(engine == "maxima"))) { 105 | maxima.env$mx$stop() 106 | e <- sys.frame(which = 1) 107 | do.call("on.exit", list(quote(if (exists("maxima.env")) file.remove(maxima.env$plots)), add = TRUE), envir = e) 108 | do.call("on.exit", list(quote(if (exists("maxima.env")) rm(plots, envir = maxima.env)), add = TRUE), envir = e) 109 | rm(mx, envir = maxima.env) 110 | } 111 | } 112 | 113 | last_label <- function(label = knitr::opts_current$get("label")) { 114 | # if (knitr:::child_mode()) return(FALSE) 115 | if(knitr::opts_knit$get("child")) { 116 | return(FALSE) 117 | } 118 | 119 | if(is_interactive()) { 120 | return(TRUE) 121 | } 122 | 123 | labels <- knitr::all_labels(engine == "maxima") 124 | utils::tail(labels, 1) == label 125 | } 126 | 127 | #' @describeIn maxima.engine This function can be used to insert maxima outputs as inline. 128 | #' @param command character string containing the Maxima command to be executed. 129 | #' @return character string containing the maxima result printed according options set by \code{maxima.options(inline.format = ..., inline.label = ...)}. 130 | #' @export 131 | #' @examples 132 | #' if (maxima.isInstalled()) { 133 | #' maxima.inline("2+2;") 134 | #' maxima.stop(engine = TRUE) 135 | #' } 136 | maxima.inline <- function(command) { 137 | maxima.engine.start() 138 | x <- maxima.env$mx$get(command) 139 | 140 | switch(maxima.options$inline.label + 1, 141 | paste0(c(x[["wol"]][[maxima.options$inline.format]], ""), collapse = "\n"), 142 | paste0(c(x[["wtl"]][[maxima.options$inline.format]], ""), collapse = "\n") 143 | ) 144 | } 145 | -------------------------------------------------------------------------------- /R/engine.utils.R: -------------------------------------------------------------------------------- 1 | #' Cluster a code chunk into commands 2 | #' @param code Character vector containing the code chunk, one line per element. 3 | #' @return A \code{list} where each element holds the indices of \code{code} that make up one command, i.e. terminates with either ';' or '$' 4 | #' @noRd 5 | gather <- function(code) { 6 | hits <- grepl(pattern = ";|\\$", x = code) 7 | comments <- grepl(pattern = "^[[:space:]]*/\\*.*?\\*/[[:space:]]*$", x = code) 8 | hits <- hits | comments 9 | marks <- rev(cumsum(rev(hits))) 10 | marks[code == ""] <- NA 11 | sapply( 12 | X = unique(marks), 13 | FUN = function(um, m) which(um == m), 14 | m = marks, 15 | simplify = FALSE 16 | ) 17 | } 18 | 19 | #' retry 20 | #' @param fun Character vector containing the code chunk, one line per element. 21 | #' @param ... Arguments to be passed on to \code{retry_expr} and \code{knitr::include_graphics}. 22 | #' @return Same return value as function \code{retry_expr()}. 23 | #' @noRd 24 | retry <- function(fun, ...) { 25 | expr <- substitute(fun) 26 | retry_expr(expr, ...) 27 | } 28 | 29 | #' Retry sub-function - recursively run \code{expr} until success or final failure 30 | #' @param expr Character vector containing the code chunk, one line per element. 31 | #' @param max.attempts Maximum number of times \code{expr} will be evaluated. 32 | #' @param sleep.seconds Number of seconds to wait after each attempt. 33 | #' @return Either returns the result of evaluating \code{expr} or an error condition object. 34 | #' @noRd 35 | retry_expr <- function(expr, max.attempts = 3, sleep.seconds = 0.5) { 36 | tryCatch( 37 | eval(expr), 38 | error = function(cnd) { 39 | if (max.attempts > 0) { 40 | Sys.sleep(sleep.seconds) 41 | retry_expr(expr, max.attempts - 1, sleep.seconds) 42 | } else { 43 | cnd 44 | } 45 | } 46 | ) 47 | } 48 | 49 | #' Check whether file is a PNG file 50 | #' @param path Character vector of length 1, specifing a filepath. 51 | #' @return TRUE if file is a PNG file, FALSE otherwise. 52 | #' @noRd 53 | is.png <- function(path) { 54 | stopifnot(is.character(path)) 55 | sig <- c( 56 | "89", "50", "4e", "47", 57 | "0d", "0a", "1a", "0a" 58 | ) 59 | 60 | data <- readBin(con = path, 61 | what = "raw", 62 | n = length(sig)) 63 | all(data == sig) 64 | } 65 | 66 | #' Check whether PNG file is complete. 67 | #' @param path Character vector of length 1, specifing a filepath. 68 | #' @return TRUE if \code{path} is a PNG file and complete, FALSE otherwise. 69 | #' @noRd 70 | is.complete.png <- function(path) { 71 | is.png(path) 72 | 73 | # ending chunk 74 | sig <- c( 75 | "00", "00", "00", "00", 76 | "49", "45", "4e", "44", 77 | "ae", "42", "60", "82" 78 | ) 79 | 80 | data <- tail(x = readBin(con = path, 81 | what = "raw", 82 | n = file.size(path)), 83 | n = length(sig)) 84 | all(data == sig) 85 | } 86 | 87 | #' Check whether file is a PDF 88 | #' @param path Character vector of length 1, specifing a filepath. 89 | #' @return TRUE if file is a PDF file, FALSE otherwise. 90 | #' @noRd 91 | is.pdf <- function(path) { 92 | stopifnot(is.character(path)) 93 | grepl(pattern = "^\\%PDF", 94 | x = readLines(con = path, n = 1)) 95 | } 96 | 97 | #' Check whether PDF is completely rendered 98 | #' @param path Character vector of length 1, specifing a filepath. 99 | #' @return TRUE if \code{path} is a PDF file and complete, FALSE otherwise. 100 | #' @noRd 101 | is.complete.pdf <- function(path) { 102 | stopifnot(is.pdf(path)) 103 | grepl(pattern = "\\%\\%EOF$", 104 | x = tail(x = readLines(con = path), 105 | n = 1L)) 106 | } 107 | 108 | #' A wrapper for \code{knitr::include_graphics()} that includes check-and-wait. 109 | #' @param path Character vector of length 1, specifing a filepath. 110 | #' @param max.attempts Maximum number of times \code{expr} will be evaluated, defaults to 3. 111 | #' @param sleep.seconds Number of seconds to wait after each attempt, defaults to 0.1. 112 | #' @param ... additional arguments for \code{knitr::include_graphics()}. 113 | #' @return Either the same return value of \code{knitr::include_graphics(path)} or 114 | #' @noRd 115 | retry_include_graphics <- function(path, 116 | max.attempts = maxima.options$max.attempts, 117 | sleep.seconds = maxima.options$sleep.seconds, 118 | ...) { 119 | stopifnot( 120 | is.character(path), 121 | is.integer(max.attempts), 122 | is.numeric(sleep.seconds) 123 | ) 124 | 125 | if (is_html_output()) { 126 | fun <- is.complete.png 127 | } else { 128 | fun <- is.complete.pdf 129 | } 130 | 131 | retry(stopifnot(fun(path)), 132 | max.attempts = max.attempts, 133 | sleep.seconds = sleep.seconds 134 | ) 135 | 136 | knitr::include_graphics(path, ...) 137 | } 138 | 139 | called_from_fn <- function(pattern) { 140 | call_st <- lapply(sys.calls(), `[[`, 1) 141 | any(unlist(lapply(call_st, function(x) grepl(pattern, deparse(x))))) 142 | } 143 | 144 | is_html_output <- function() { 145 | if(is.null(p <- knitr::opts_knit$get("rmarkdown.pandoc.to"))) 146 | return(FALSE) 147 | knitr::is_html_output() & p == "html" 148 | } 149 | 150 | is_interactive <- function() { 151 | is.null(knitr::all_labels(engine == "maxima")) 152 | } 153 | 154 | #' rim_global 155 | #' 156 | #' Returns knitr::knit_global() unless this returs the GlobalEnv() in which case it returns maxima.env, rim's internal environment. 157 | #' Main purpose is to prevent `knitr::knit_global()` from returning `globalenv()`. 158 | #' 159 | #' @return An environment, either the result of knitr::knit_global or maxima.env 160 | #' @export 161 | rim_global <- function() { 162 | kg <- knitr::knit_global() 163 | if(identical(globalenv(), kg)) 164 | maxima.env 165 | else 166 | kg 167 | } 168 | -------------------------------------------------------------------------------- /R/hooks.R: -------------------------------------------------------------------------------- 1 | setup_hooks <- function() { 2 | old_output <- knitr::knit_hooks$get("output") 3 | knitr::knit_hooks$set(output = function(x, options) old_output(x, options)) 4 | } 5 | -------------------------------------------------------------------------------- /R/interface.R: -------------------------------------------------------------------------------- 1 | #' @import R6 2 | MReader <- R6::R6Class("MReader", 3 | public = list( 4 | initialize = function(con) { 5 | private$rc <- con 6 | private$stash <- "" 7 | private$complete <- TRUE 8 | private$Nnon <- 0L 9 | }, 10 | wasComplete = function() { 11 | private$complete 12 | } 13 | ), 14 | active = list( 15 | trials = function(value) { 16 | if(missing(value)) 17 | private$Nnon 18 | else 19 | private$Nnon <- value 20 | }, 21 | read = function(value) { 22 | if(!missing(value)) 23 | message("Input value ignored.") 24 | private$complete <- TRUE 25 | suppressWarnings(withCallingHandlers( 26 | { 27 | x <- readLines(private$rc, n = 1) 28 | z <- paste0(private$stash, x) 29 | }, 30 | warning = function(cnd) { 31 | private$complete <- FALSE 32 | })) 33 | if(private$complete & length(z)) { 34 | private$stash <- "" 35 | private$Nnon <- 0L 36 | } else { 37 | private$stash <- z 38 | private$complete <- FALSE 39 | private$Nnon <- private$Nnon + 1L 40 | } 41 | 42 | return(z) 43 | } 44 | ), 45 | private = list( 46 | stash = character(0), 47 | complete = logical(0), 48 | rc = NULL, 49 | Nnon = integer(0) 50 | ) 51 | ) 52 | 53 | Reply <- R6::R6Class("Reply", 54 | public = list( 55 | initialize = function(con) { 56 | if(missing(con)) 57 | stop("Please provide a connection object to initialize.") 58 | 59 | if(!(isOpen(con, rw = "read") && isOpen(con, rw = "write"))) 60 | stop("Connection without read/write access") 61 | 62 | # read socket until including prompt 63 | promptExpr <- "<>([[:space:]|[:print:]]*?)<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|<>|< 0 && nchar(module)) 427 | self$get(paste0("load(", module, ")$")) 428 | }, 429 | get_stuck = function(command) { 430 | # executes command without advancing reference labels 431 | if(length(command) && nchar(command)) 432 | self$get(paste0("(", command, ", linenum:linenum-1, %)$")) 433 | }, 434 | getPort = function() { 435 | if(private$running) 436 | return(private$port) 437 | else 438 | return(NA_integer_) 439 | }, 440 | getVersion = function() { 441 | if(private$running) { 442 | return(private$version) 443 | } 444 | else { 445 | message("Version number undetermined. Maxima needs to be running.") 446 | return(NULL) 447 | } 448 | } 449 | ), 450 | private = list( 451 | maximaSocket = NULL, 452 | port = NULL, 453 | pid = NULL, 454 | version = NULL, 455 | workDir = character(), 456 | utilsDir = character(), 457 | maximaPath = NA_character_, 458 | display = character(), 459 | preload = character(), 460 | reply = NULL, 461 | running = FALSE, 462 | lastPromptID = integer(), 463 | lastInputLabel = character(), 464 | lastOutputLabel = character(), 465 | run = function() { 466 | # try until free port is found 467 | # starting from given port 468 | for(port in private$port:65536) { 469 | try(scon <- serverSocket(port), silent = TRUE) 470 | if(exists("scon")) 471 | if(isOpen(con = scon)) { 472 | private$port <- port 473 | break 474 | } 475 | } 476 | if(!exists("scon")) 477 | stop("Couldn't find available port") 478 | 479 | system2(private$maximaPath, 480 | c(# "-q", 481 | paste0("-s ", private$port), 482 | paste0("--userdir=", private$utilsDir), 483 | paste0("--init=", private$display), 484 | paste0(private$preload)), 485 | wait = FALSE, 486 | stdout = FALSE, 487 | stderr = stdout()) 488 | 489 | private$maximaSocket <- socketAccept(socket = scon, 490 | blocking = FALSE, 491 | open = "r+b") 492 | close(scon) 493 | 494 | private$parseStartUp() 495 | private$reply <- Reply$new(private$maximaSocket) 496 | private$lastInputLabel <- private$reply$getInputLabel() 497 | private$running <- TRUE 498 | 499 | }, 500 | sendCommand = function(command){ 501 | if(missing(command)) 502 | stop("Missing command.") 503 | writeLines(text = command, con = private$maximaSocket) 504 | return(command) 505 | }, 506 | crudeExecute = function(command) { 507 | command <- private$sendCommand(command) 508 | private$reply <- Reply$new(private$maximaSocket) 509 | if(grepl("\\$$", command)) 510 | private$reply$suppressed <- TRUE 511 | }, 512 | parseStartUp = function(nmax = as.integer(1e6)) { 513 | # pid 514 | pidExpr <- "pid=(\\d+)" 515 | rdr <- MReader$new(private$maximaSocket) 516 | repeat { 517 | # z <- readLines(private$maximaSocket, n = 1, warn = FALSE) 518 | z <- rdr$read 519 | if(length(z)) { 520 | if(grepl(pattern = pidExpr, x = z)) { 521 | private$pid <- as.integer(regex(text = z, pattern = pidExpr)[2]) 522 | break 523 | } 524 | } 525 | if(rdr$trials > nmax) 526 | stop("Failed to read process ID - couldn't start Maxima.") 527 | } 528 | 529 | # version 530 | rdr$trials <- 0L 531 | verExpr <- "Maxima ((\\d+\\.)?(\\d+\\.)?(\\d+))" 532 | repeat { 533 | # z <- readLines(private$maximaSocket, n = 1, warn = FALSE) 534 | z <- rdr$read 535 | if(length(z)) { 536 | if(grepl(pattern = verExpr, x = z)) { 537 | private$version <- numeric_version(regex(text = z, pattern = verExpr)[2]) 538 | break 539 | } 540 | } 541 | if(rdr$trials > nmax) { 542 | stop("Failed to read version number - Maxima seems stuck.\n", 543 | "Has read: ", z, "\n", 544 | "Maxima call: ", paste0(private$maximaPath, 545 | " -s ", private$port, 546 | " --userdir=", private$utilsDir, 547 | " --init=", private$display, 548 | " ", private$preload)) 549 | } 550 | } 551 | }, 552 | finalize = function() { 553 | # in here no more method calls are possible 554 | # suppressMessages(self$stop()) 555 | if(private$running) { 556 | private$sendCommand("quit();") 557 | close(private$maximaSocket) 558 | } 559 | } 560 | ) 561 | ) 562 | 563 | #' Extract substring by regular expression 564 | #' @param text Character vector containing the text to be searched 565 | #' @param pattern Character vector of length 1 containing the regular expression to be matched 566 | #' @return Character vector of the same length as \code{text} 567 | #' @noRd 568 | regex <- function(text, pattern) { 569 | r <- regexec(pattern, text) 570 | starts <- unlist(r) 571 | starts[starts == -1] <- NA 572 | stops <- starts + unlist(lapply(X = r, 573 | FUN = attr, 574 | which = "match.length")) - 1L 575 | substring(text, starts, stops) 576 | } 577 | 578 | swipe = function(x, inclusive = TRUE) { 579 | stopifnot(is.logical(x)) 580 | ix <- which(x) 581 | r <- cumsum(x) %% 2L 582 | r[x] <- FALSE 583 | if(inclusive) 584 | r[x] <- TRUE 585 | as.logical(r) 586 | } 587 | 588 | odd <- function(x) x%%2!=0 589 | 590 | vseq <- function(from, to, unique = TRUE) { 591 | if(unique) 592 | unique(unlist(Map(':', from, to))) 593 | else 594 | unlist(Map(':', from, to)) 595 | } 596 | 597 | pvseq <- function(x) { 598 | if(length(x) >= 2 && length(x) %% 2 == 0) { 599 | vseq(from = x[odd(1:length(x))], 600 | to = x[!odd(1:length(x))]) 601 | } 602 | else 603 | x 604 | } 605 | 606 | #' Extract elements from character vector 'from' by specifiying delimiters as a single regular expression. 607 | #' @param delimiters Character vector specifying the delimiters that fence the elements to be extracted 608 | #' @param from Character vector of length 1 containing the regular expression to be matched 609 | #' @param inform logical of length 1. Whether information about the extraction indices should be set as attributes to the return value (TRUE), or not (FALSE, default). 610 | #' @param without logical of length 1. Should the delimiters be excluded from the output (default)? 611 | #' @return character vector with the elements extracted. Possibly empty, in case delimiters did not match 612 | #' @noRd 613 | extract <- function(delimiters, from, inform = FALSE, without = TRUE) { 614 | if(missing(delimiters)) 615 | stop("Parameter 'delimiters' not provided") 616 | if(length(delimiters) > 2L) 617 | warning("Length of 'delimiters > 2, only the first two will be used.") 618 | if(length(delimiters) == 1L) 619 | delimiters <- c(delimiters, delimiters) 620 | if(missing(from)) 621 | stop("Parameter 'from' not provided") 622 | 623 | i <- c(grep(pattern = delimiters[1], from), grep(pattern = delimiters[2], from)) 624 | if(length(i) %% 2 != 0) 625 | stop(paste("Could not fetch delimiter pair:", 626 | paste0(from, collapse = "\n"))) 627 | ii <- pvseq(i) 628 | ee <- ii[!(ii %in% i)] 629 | if(without) 630 | r <- from[ee] 631 | else 632 | r <- from[ii] 633 | 634 | if(inform) { 635 | attr(r, "all") <- ii 636 | attr(r, "elements") <- ee 637 | attr(r, "delimiters") <- i 638 | attr(r, "without") <- without 639 | } 640 | 641 | return(r) 642 | } 643 | -------------------------------------------------------------------------------- /R/options.R: -------------------------------------------------------------------------------- 1 | #' maxima.options 2 | #' 3 | #' Function for globally setting and retrieving options. 4 | #' 5 | #' \describe{ 6 | #' \item{\code{format}: }{character vector of length 1 setting the output format for \code{\link[=rim]{maxima.get()}} and \code{\link[=rim]{maxima.repl()}}. Can be one of \code{"linear",} \code{"ascii",} \code{"latex"} or \code{"mathml"}.} 7 | #' \item{\code{engine.format}: }{same as option \code{format}, but for outputs in \code{RMarkdown} documents.} 8 | #' \item{\code{inline.format}: }{character string setting the output format for \code{\link[=rim]{maxima.inline()}}, for knitting outputs inline into \code{RMarkdown} documents. Can be one of \code{"linear"}, \code{"latex"} or \code{"mathml"}, but \emph{not} \code{"ascii"}.} 9 | #' \item{\code{label}: }{logical of length 1, whether reference labels should be printed for returned S3 objects from \code{\link[=rim]{maxima.get}()} and \code{\link[=rim]{maxima.repl()}} (TRUE, default), or not (FALSE). This also applies to printing of input commands using \code{\link{iprint}()}.} 10 | #' \item{\code{engine.label}: }{same as \code{label}, but for outputs in \code{RMarkdown} documents.} 11 | #' \item{\code{inline.label}: }{same as \code{label}, but for inline outputs in \code{RMarkdown} documents.} 12 | #' } 13 | # #' 14 | # #' To print a table of available options, current settings and a description simply print the function object (without parentheses), i.e. \code{maxima.options} or \code{print(maxima.options)}. 15 | # #' 16 | #' @param ... options to be accessed by using \code{name = value}. Options can be added by setting \code{ADD = TRUE}, but only those mentioned below will be used my rim. 17 | #' @param RESET logical of length 1, whether to reset all options to default values. 18 | #' @param READ.ONLY logical of length 1, can be used to output all options that are read-only. 19 | #' @param LOCAL logical of length 1, to output all options that are defined locally. 20 | #' @param ADD logical of length 1, whether to add the specified option in \code{...} (TRUE), default is FALSE. 21 | # 22 | #' 23 | #' @import GlobalOptions 24 | #' @export 25 | #' @examples 26 | #' maxima.options(format = "latex") 27 | #' maxima.options(label = FALSE) 28 | #' maxima.options(label = TRUE, format = "ascii") 29 | #' # reset to default 30 | #' maxima.options(label = TRUE, format = "linear") 31 | maxima.options <- function(..., RESET = FALSE, READ.ONLY = NULL, LOCAL = FALSE, ADD = FALSE) {} 32 | maxima.options <- set_opt( 33 | format = list( 34 | .value = "linear", 35 | .length = 1L, 36 | .class = "character", 37 | .read.only = FALSE, 38 | .validate = function(x) x %in% c("linear", "ascii", "latex", "mathml"), 39 | .failed_msg = "'format' must be one of 'linear', 'ascii', 'latex' or 'mathml'", 40 | .description = "Printing format of returned object from maxima.get()" 41 | ), 42 | engine.format = list( 43 | .value = "linear", 44 | .length = 1L, 45 | .class = "character", 46 | .read.only = FALSE, 47 | .validate = function(x) { 48 | r <- x %in% c("linear", "ascii", "latex", "mathml") 49 | return(r) 50 | }, 51 | .failed_msg = "'format' must be one of 'linear', 'ascii', 'latex' or 'mathml'", 52 | .description = "Same as 'format', but for maxima code chunks in 'RMarkdown' documents." 53 | ), 54 | engine.results = list( 55 | .value = function() { 56 | switch(.v$engine.format, 57 | latex = "asis", 58 | linear = "markup", 59 | ascii = "markup", 60 | mathml = "asis", 61 | ) 62 | }, 63 | .class = "character", 64 | .length = 1L, 65 | .private = TRUE, 66 | .visible = FALSE 67 | ), 68 | inline.format = list( 69 | .value = "linear", 70 | .length = 1L, 71 | .class = "character", 72 | .read.only = FALSE, 73 | .validate = function(x) x %in% c("linear", "inline", "latex", "mathml"), 74 | .filter = function(x) ifelse(x == "latex", "inline", x), 75 | .failed_msg = "'format' must be one of 'linear', 'inline' or 'mathml'", 76 | .description = "Same as 'engine.format', but for printing output inline via maxima.inline(). Cannot be set to 'ascii'." 77 | ), 78 | label = list( 79 | .value = TRUE, 80 | .length = 1L, 81 | .class = "logical", 82 | .read.only = FALSE, 83 | .description = "Sets whether a maxima reference label should be printed when printing a maxima return object." 84 | ), 85 | engine.label = list( 86 | .value = TRUE, 87 | .length = 1L, 88 | .class = "logical", 89 | .read.only = FALSE, 90 | .description = "Same as 'label', but for maxima code chunks." 91 | ), 92 | inline.label = list( 93 | .value = TRUE, 94 | .length = 1L, 95 | .class = "logical", 96 | .read.only = FALSE, 97 | .description = "Same as 'label', but for inline code chunks" 98 | ), 99 | display = list( 100 | .value = "display", 101 | .length = 1L, 102 | .class = "character", 103 | .private = TRUE, 104 | .visible = FALSE 105 | ), 106 | preload = list( 107 | .value = c("plot-knitr-png", "plot-knitr-pdf"), 108 | .length = 1L, 109 | .class = "character", 110 | .private = TRUE, 111 | .visible = FALSE, 112 | .description = "Specifies a file name that will be loaded after initialization to overwrite plotting function to include graphics into 'RMarkdown' documents." 113 | ), 114 | max.attempts = list( 115 | .value = 3L, 116 | .length = 1L, 117 | .class = "integer", 118 | .private = FALSE, 119 | .visible = FALSE, 120 | .description = "Maximum number of attempts to include Maxima generated images." 121 | ), 122 | sleep.seconds = list( 123 | .value = 0.1, 124 | .length = 1L, 125 | .class = "numeric", 126 | .private = FALSE, 127 | .visible = FALSE, 128 | .description = "Number of seconds to wait before re-attempting to include Maxima generated images." 129 | ) 130 | ) 131 | 132 | -------------------------------------------------------------------------------- /R/package.R: -------------------------------------------------------------------------------- 1 | #' rim 2 | #' 3 | #' @description 4 | #' Provides an interface to Maxima, a computer algebra system. 5 | #' 6 | #' @details 7 | #' Note: You need to install the Maxima software separately in order to make use of this package. 8 | #' 9 | #' Maxima is set up automatically on attachment via \code{library(rim)} and automatically started when a command is send (if it isn't running already) using \code{\link{maxima.get}()}. If environment variable RIM_MAXIMA_PATH is not set, rim will search for the Maxima executable, or use the former otherwise. Using \code{\link{maxima.start}()} and \code{\link{maxima.stop}()}, one can stop and (re-)start the current Maxima session if needed, e.g. to clear Maxima command and output history. 10 | #' 11 | #' To send a single command to Maxima and receive the corresponding output use \code{\link{maxima.get}()}. This function returns a S3 object of class "maxima". The output is printed by printing the object and will be printed in a format currently set by \code{\link{maxima.options}(format)}. The output format can be changed by setting it, e.g. \code{\link{maxima.options}(format = "ascii")}. Output labels are printed according to option \code{\link{maxima.options}(label)}. 12 | #' 13 | #' @import methods 14 | ## usethis namespace: start 15 | #' @importFrom Rcpp sourceCpp 16 | #' @importFrom utils tail 17 | #' @useDynLib rim, .registration = TRUE 18 | ## usethis namespace: start 19 | #' 20 | "_PACKAGE" 21 | #> [1] "_PACKAGE" 22 | 23 | maxima.env <- new.env() 24 | 25 | #' @describeIn rim-package (re-)starts Maxima. 26 | #' @param restart if FALSE (default), then Maxima is started provided it is not running already. If TRUE starts or restarts Maxima. 27 | #' @export 28 | #' @examples 29 | #' if(maxima.isInstalled()) maxima.start(restart = TRUE) 30 | maxima.start <- function(restart = FALSE) { 31 | maxima.env$maxima$start(restart) 32 | maxima.options(format = "linear") 33 | maxima.options(label = TRUE) 34 | } 35 | 36 | #' @describeIn rim-package Quits Maxima. 37 | #' @param engine if FALSE (default), quits the (running) maxima instance and closes the connection, otherwise quits and closes the (running) maxima instance used for the knitr engine. 38 | #' @export 39 | #' @examples 40 | #' if(maxima.isInstalled()) { 41 | #' maxima.start(restart = TRUE) 42 | #' maxima.stop() 43 | #' } 44 | maxima.stop <- function(engine = FALSE) { 45 | if(!engine) 46 | maxima.env$maxima$stop() 47 | else { 48 | if(exists("mx", envir = maxima.env)) 49 | maxima.env$mx$stop() 50 | } 51 | } 52 | 53 | #' @describeIn rim-package Executes a single Maxima command provided by \code{command}. If no command ending character (\code{;} or \code{$} is provided, \code{;} is appended. 54 | #' @param command character string containing the Maxima command. 55 | #' @seealso \code{\link{maxima.engine}}, \code{\link{maxima.options}} 56 | #' @export 57 | #' @examples 58 | #' if(maxima.isInstalled()) maxima.get("2+2;") 59 | maxima.get <- function(command) { 60 | return(maxima.env$maxima$get(command)) 61 | } 62 | 63 | #' @describeIn rim-package A wrapper to load a Maxima module named by \code{module} 64 | #' @param module character vector naming the Maxima module (typically a *.mac or *.lisp file) to be loaded. 65 | #' @return invisibly returns NULL. 66 | #' @export 67 | #' @examples 68 | #' if(maxima.isInstalled()) maxima.load("ratpow") 69 | maxima.load <- function(module) { 70 | maxima.env$maxima$loadModule(module) 71 | } 72 | 73 | #' @describeIn rim-package A wrapper to the Maxima helper function \code{apropos} to lookup existing Maxima functions that match \code{keystring}. 74 | #' @param keystring character vector containing a search term. 75 | #' @export 76 | #' @examples 77 | #' if(maxima.isInstalled()) maxima.apropos("integrate") 78 | maxima.apropos <- function(keystring) { 79 | return(maxima.env$maxima$get(paste0("apropos(\"", keystring, "\");"))) 80 | } 81 | 82 | 83 | #' @describeIn rim-package Returns the version number of Maxima that is used 84 | #' @export 85 | #' @examples 86 | #' maxima.version() 87 | maxima.version <- function() { 88 | maxima.env$maxima$getVersion() 89 | } 90 | 91 | #' @describeIn rim-package Returns TRUE when an installation of Maxima has been detected, otherwise FALSE 92 | #' @export 93 | #' @examples 94 | #' maxima.isInstalled() 95 | maxima.isInstalled <- function() { 96 | maxima.env$maxima$isInstalled() 97 | } 98 | 99 | #' @describeIn rim-package Prints the input command of an maxima S3-object returned by \code{\link{maxima.get}()} 100 | #' @param x S3-Object of class "maxima", the returned type of object from \code{maxima.get()}. 101 | #' @return Character vector of length 1 of the input command. Depending on whether option "label" is set to TRUE, the corresponding input reference label is printed preceding the input command. 102 | #' @export 103 | #' @examples 104 | #' if(maxima.isInstalled()) { 105 | #' a <- maxima.get("2+2;") 106 | #' iprint(a) 107 | #' } 108 | iprint <- function(x) { 109 | stopifnot(isa(x, what = "maxima")) 110 | 111 | # TODO: Determin calling function to distinguish return value 112 | label <- character(0) 113 | if(exists("mx", maxima.env)) { 114 | if(maxima.options$engine.label) 115 | label <- paste0("(", attr(x, "input.label"), ") ") 116 | } else { 117 | if(maxima.options$label) 118 | label <- paste0("(", attr(x, "input.label"), ") ") 119 | } 120 | 121 | paste0(label, attr(x, "command")) 122 | } 123 | 124 | #' @describeIn rim-package Prints the maxima output part of an S3 object returned by \code{\link{maxima.get}()} 125 | #' @param x S3 object of class "maxima" 126 | #' @param ... other arguments (ignored). 127 | #' @method print maxima 128 | #' @export 129 | #' @examples 130 | #' if(maxima.isInstalled()) { 131 | #' a <- maxima.get("2+2;") 132 | #' print(a) 133 | #' } 134 | print.maxima <- function(x, ...) { 135 | if(!attr(x, "suppressed")) { 136 | if(attr(x, "from_engine") & !is_interactive()) { 137 | label_opt <- maxima.options$engine.label 138 | format_opt <- maxima.options$engine.format 139 | } else { 140 | label_opt <- maxima.options$label 141 | format_opt <- maxima.options$format 142 | } 143 | 144 | label_opt <- ifelse(label_opt, "wtl", "wol") 145 | 146 | txt <- x[[label_opt]][[format_opt]] 147 | 148 | if(is_html_output()) { 149 | txt <- gsub(pattern = "\\\\%", replacement = "%", x = txt) 150 | } 151 | 152 | # if(!(attr(x, "from_engine") | is_interactive())) 153 | if(is_interactive()) 154 | cat(txt, sep = '\n') 155 | invisible(txt) 156 | } 157 | } 158 | 159 | #' @describeIn rim-package Evaluates the parsed and quoted R-expression which is part of an S3 object returned by \code{\link{maxima.get}()} 160 | #' @param x S3 object of class "maxima" 161 | #' @param x Either a character vector of length 1L or an S3 object of class "maxima" 162 | #' @param code A logical vector of length 1L, whether to attach the original expression (TRUE) or not (FALSE, default) 163 | #' @param envir A environment object. \code{globalenv()} (default), is passed to eval(). 164 | #' @return The evaluated R-object 165 | #' @export 166 | #' @examples 167 | #' if(maxima.isInstalled()) { 168 | #' a <- maxima.get("2+2;") 169 | #' maxima.eval(a) 170 | #' # same 171 | #' maxima.eval("2+2;") 172 | #' # evaluate with data.frame 173 | #' df <- data.frame(x = seq(0, 1, by = 0.1)) 174 | #' maxima.eval("integrate(1 / (1 + x^4), x);", code = TRUE, envir = df) 175 | #' maxima.stop() 176 | #' } 177 | maxima.eval <- function(x, code = FALSE, envir = globalenv()) { 178 | expr <- NA 179 | if(is.character(x)) 180 | x <- maxima.get(x) 181 | if(is(x, "maxima")) 182 | expr <- attr(x, "parsed") 183 | r <- eval(expr, envir = envir) 184 | if(code) 185 | attr(r, "maxima") <- expr 186 | return(r) 187 | } 188 | -------------------------------------------------------------------------------- /R/repl.R: -------------------------------------------------------------------------------- 1 | #' Run a Maxima REPL 2 | #' 3 | #' This function provides a Maxima REPL in the \R session, which can be used 4 | #' to interactively run Maxima code. All code executed within the REPL is 5 | #' run within the interactive Maxima session that is started when rim is 6 | #' attached and any generated Maxima objects will persist in the Maxima 7 | #' session after the REPL is detached. 8 | #' 9 | #' When working with R and Maxima scripts interactively, one can activate 10 | #' the Python REPL with `maxima.repl()`, run Maxima code, and later run `exit;` 11 | #' to return to the \R console. 12 | #' 13 | #' Note that, inside the REPL, multiple commands are allowed. 14 | #' 15 | #' The output format displayed inside the REPL is controlled by `maxima.options(repl.format = ...)`. 16 | #' 17 | #' @param history_filename A (optional) filename to which all Maxima commands 18 | #' during the repl session are saved. If not provided (default), command history 19 | #' lookup will still be available (if supported by OS) in the same way as in the 20 | #' R session. 21 | #' 22 | #' @export 23 | maxima.repl <- function(history_filename = NA_character_) { 24 | stopifnot(is.character(history_filename), 25 | length(history_filename) == 1L) 26 | 27 | cat("Starting Maxima REPL... Type 'exit' to detach.\n") 28 | 29 | # check to see if the current environment supports history 30 | # (check for case where working directory not writable) 31 | use_history <- 32 | !"--vanilla" %in% commandArgs() && 33 | !"--no-save" %in% commandArgs() && 34 | !is.null(getwd()) && 35 | tryCatch( 36 | { utils::savehistory(tempfile()); TRUE }, 37 | error = function(e) FALSE 38 | ) 39 | 40 | if (use_history) { 41 | # if we have history, save and then restore the current 42 | # R history 43 | utils::savehistory() 44 | on.exit(utils::loadhistory(), add = TRUE) 45 | 46 | # file to be used for command history during sessions 47 | histfile <- ifelse(missing(history_filename) | is.na(history_filename), 48 | file.path(tempdir(), ".maxima_repl_history.mac"), 49 | file.path(history_filename)) 50 | 51 | # load history (create empty file if none exists yet) 52 | if (!file.exists(histfile)) 53 | file.create(histfile) 54 | utils::loadhistory(histfile) 55 | } 56 | 57 | # Start a loop for the REPL 58 | repeat { 59 | prmpt <- paste0("(", maxima.env$maxima$getCurrentInputLabel(), ") ") 60 | # Prompt the user for input 61 | user_input <- readline(prompt = prmpt) 62 | 63 | # Exit condition 64 | if (user_input == "exit") { 65 | cat("Exiting Maxima REPL...\n") 66 | break 67 | } 68 | 69 | # splice and send the commands to Maxima and evaluate 70 | xx <- dissect_repl_input(user_input) 71 | for(i in 1:length(xx)) { 72 | tryCatch({ 73 | x <- maxima.env$maxima$get(xx[[i]]) 74 | print(x) 75 | }, error = function(e) { 76 | # Handle any errors (e.g., invalid Maxima commands) 77 | cat("\nError:", e$message, sep = '\n') 78 | }) 79 | } 80 | 81 | # update history file 82 | if (use_history) { 83 | cat(user_input, file = histfile, sep = "\n", append = TRUE) 84 | utils::loadhistory(histfile) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname) { 2 | maxima.env$maxima <- RMaxima$new() 3 | } 4 | 5 | .onAttach <- function(libname, pkgname) { 6 | if(maxima.isInstalled()) { 7 | maxima.start() 8 | if(maxima.version() < "5.42.1") { 9 | packageStartupMessage(paste("Installed Maxima version ", maxima.version(), 10 | "is untested. Consider updating.")) 11 | } 12 | maxima.stop() 13 | } 14 | else { 15 | packageStartupMessage(paste("Could not find Maxima executable, please download from\n", 16 | "https://maxima.sourceforge.io/download.html\n", 17 | "and install")) 18 | if(.Platform$OS != "unix") 19 | packageStartupMessage(paste("Please make sure maxima executable", 20 | "is on PATH environment variable.")) 21 | } 22 | 23 | if(requireNamespace("knitr", quietly = TRUE)) { 24 | knitr::knit_engines$set(maxima = maxima.engine) 25 | setup_hooks() 26 | packageStartupMessage("Maxima successfully registered as knitr engine!") 27 | } else 28 | packageStartupMessage("Install package knitr if you want to register maxima a knitr engine first") 29 | } 30 | 31 | .onUnload <- function (libpath) { 32 | suppressMessages({ 33 | library.dynam.unload("rim", libpath) 34 | rm(list = "maxima", envir = maxima.env) 35 | if(exists(x = "mx", envir = maxima.env)) 36 | rm(list = "mx", envir = maxima.env) 37 | }) 38 | } 39 | 40 | .onDetach <- function(libpath) { 41 | suppressMessages({ 42 | maxima.env$maxima$stop() 43 | if(exists(x = "mx", envir = maxima.env)) 44 | maxima.env$mx$stop() 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "rim - R's interface to Maxima" 3 | output: github_document 4 | --- 5 | 6 | 7 | 8 | [![](https://cranlogs.r-pkg.org/badges/rim)](https://CRAN.R-project.org/package=rim) 9 | [![](https://cranlogs.r-pkg.org/badges/grand-total/rim)](https://CRAN.R-project.org/package=rim) 10 | 11 | Development version of CRAN package. `rim` provides an interface to the powerful and fairly complete maxima computer algebra system 12 | 13 | This repository is a fork from RMaxima, the original version created by Kseniia Shumelchyk and Hans W. Borcher [shumelchyk/rmaxima](https://github.com/shumelchyk/rmaxima), which is currently not maintained. 14 | 15 | # Installation 16 | 17 | ```{r, eval = FALSE} 18 | install.packages("rim") 19 | ``` 20 | 21 | 22 | ## Requirements 23 | - Maxima (https://sourceforge.net/projects/maxima/, tested with versions >=5.42.1), needs to be on PATH 24 | 25 | ## Latest Version 26 | 27 | If you want to install the latest version install the R package `remotes` first and then install the package from this github repo: 28 | 29 | ```{r, eval = FALSE} 30 | install.packages("remotes") 31 | remotes::install_github("rcst/rim") 32 | ``` 33 | 34 | # Usage 35 | 36 | This section only demonstrates the package's R-function directly accessible to the user. On how to use the package's `knitr` engine see [this page](https://rcst.github.io/rim/). 37 | 38 | ```{r, eval = FALSE} 39 | library(rim) 40 | ``` 41 | 42 | ```{r} 43 | maxima.start(restart = TRUE) 44 | maxima.get("1+1;") 45 | r <- maxima.get("sum(1/x^2, x, 1, 10000)") 46 | print(r) 47 | iprint(r) 48 | maxima.isInstalled() 49 | maxima.version() 50 | maxima.options 51 | maxima.options(format = "ascii") 52 | maxima.apropos("int") 53 | maxima.get("integrate(1 / (x^4 + 1), x);") 54 | maxima.get("jacobian( [alpha / (alpha + beta), 1 / sqrt(alpha + beta)], [alpha, beta] )") 55 | l <- maxima.get("%;") 56 | maxima.eval(l, code = TRUE, envir = list(alpha = 0.3, beta = 2.5)) 57 | unclass(l) 58 | maxima.load("abs_integrate") 59 | maxima.stop() 60 | ``` 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rim - R’s interface to Maxima 2 | ================ 3 | 4 | 5 | 6 | [![](https://cranlogs.r-pkg.org/badges/rim)](https://CRAN.R-project.org/package=rim) 7 | [![](https://cranlogs.r-pkg.org/badges/grand-total/rim)](https://CRAN.R-project.org/package=rim) 8 | 9 | Development version of CRAN package. `rim` provides an interface to the 10 | powerful and fairly complete maxima computer algebra system 11 | 12 | This repository is a fork from RMaxima, the original version created by 13 | Kseniia Shumelchyk and Hans W. Borcher 14 | [shumelchyk/rmaxima](https://github.com/shumelchyk/rmaxima), which is 15 | currently not maintained. 16 | 17 | # Installation 18 | 19 | ``` r 20 | install.packages("rim") 21 | ``` 22 | 23 | ## Requirements 24 | 25 | - Maxima (, tested with 26 | versions >=5.42.1), needs to be on PATH 27 | 28 | ## Latest Version 29 | 30 | If you want to install the latest version install the R package 31 | `remotes` first and then install the package from this github repo: 32 | 33 | ``` r 34 | install.packages("remotes") 35 | remotes::install_github("rcst/rim") 36 | ``` 37 | 38 | # Usage 39 | 40 | This section only demonstrates the package’s R-function directly 41 | accessible to the user. On how to use the package’s `knitr` engine see 42 | [this page](https://rcst.github.io/rim/). 43 | 44 | ``` r 45 | library(rim) 46 | ``` 47 | 48 | ``` r 49 | maxima.start(restart = TRUE) 50 | maxima.get("1+1;") 51 | ``` 52 | 53 | ## (%o1) 2 54 | 55 | ``` r 56 | r <- maxima.get("sum(1/x^2, x, 1, 10000)") 57 | ``` 58 | 59 | ## Warning in value[[3L]](cond): Caught error while parsing 60 | ## Überlauf des Eingabebuffers in Zeile 1 61 | ## Returning NA. 62 | 63 | ``` r 64 | print(r) 65 | ``` 66 | 67 | ## (%o2) 54714423173933343999582177160129362529870283624174963297531328605746589773430778183198620050736675427362803973347189596920858374826198299243875944018790714269022039209429246957120164226897122221467774889383041528298564231525726005885733604890567741863788282223101091820040246774500899582880738779441903043993446797207474982772365818204209418017874472746075929430402719315899195398091579911840793724334501466809497514690646349026417408657798612724412146815881907468466138871837138164123922629387751665906831009172724807380344362641581163733346203555718850891948260773752299205407726870601562897167876485357851589965396280602666157317110387959451092855487589490685186018122043775191659379508292628762992096830369387377564339210809700754302538398507252386941602236810441631669594849503782189852039566430411366575909380827769724447338940111761588124004276515786306137226537894846082264669311720594628838458236534704442082294084133019729052294375893488930238771136044176775587032445127166320941961770547230342282891720711094680202323745272645863298738706071559533479377820073676651860866476014981202902915165425984274917243253526013326104533936418682251997405429350574846738597941596465847889444363702434675773342527460766142607487599035002858371082612263524228218717418382091490999753564503938592826918552223027205803181181888741408228059697739547051812861769394298478975745066997066788703540179782594827175983300181264325697248430726629456282185376474013917829429243519811721072662085485111045703987412094220689686673634241955047247609854041200778578542832459371567518406936908136456278580855326355096792872324174145562813303949605379230174018494117853603702152059335440764083293322471807283069472531202462722202917551408494598048677137074200859816690275986663030277286569976460908346358904422964424746645104732745352326121282910390649387427416055754359188091700147618060211231503082679247785691660641065431271701057523938788707846180991151470917443986898990696238399554440903455233022468088651148799684030267146193796334043119080594644322358796906759836770505103557429631622296333030266327046011619967776159634366887279937028243689380001409729105518639839675945116128815330668016315503185899714161216687671230574073946303172784011192584424549790851949628053757067125708116500923344172251805167855674009789352798000845818461242899224835938000020378170885269055841255490568216829243335871796252244109330529902983716481736108361446573154715062745211442983313584589859718458201409644684427903762533836045799364210919834689625426037370437764039595607214082990598178228046116112630636040001663147483205019386481519822689029391114522931957183171963601512521439959563534069438676951975087909832523209070515108411852576337766181331210245202556778537611245618032446627001987176726069215070633346030796232494244019728751779926096081430988292745260805874652716348627255093805602013625670873133828325514622056327230677993441485202456141624053594915112957418286324951364793572836369723116492808563095213991319047761945317187869248729375289646267504478167028273396579524813913334151803023358073522405222549035123864804815678071618740638054974112593483726390502113599929647317259818412046014321600804441793135484014715991040128640051384584336103707601446933382393773627271309500975826400733339412405476577984912684169457984989567273831263186466544309763153431065963866240968246813680236626161775578250353653632304278658916659205865168664633596162571073255555785527047441982726477428415441608391620914299858924649341080842414427042884773862327960684379853819918113906923732799168708234502574103035248876408173859907743983277977179889939690429027728899836034303103846198087243860133694609495003085594474285042927122313337572294151335513419111029590872832714904180393358701596366385951443810672106202469574018799513459696965569155880094143095244341376943866450111670328409290239406024323584249461010638411786389182375518466075581091820058065184342719352703433649383541885319070885434140096435888895915838399889468766955213317926658229843966956015883000989184322171710713223649126536034372588359814912282338149828331742475288776165758624668302616582826157942988548761128057396876656217465326482735010879330795681553493747259618459826243669763540270027708731304870445825284361682698093387761793742048304278104093500532463894820462859057282396283759525305867486353270235939534246792138612608840528996034304412039332005879449134846209923941893166636009769500421980450818388532886689781787068817196712858933448063045608771626658106014788519330242475177372495165820226543420903872341980336920464845619553623926915434440577108858461813046669675213100608647130957218743496405831782804948156306093884408227146007099300059879697593185055729716721089763708268661326778789153938771996075981554131700962184855700361219028651893129459248410229172748625837731140325878800075436691689542794858719884224083845593868131724898886440575779158384233204867975224464329634148174423778660117199058743344643347405140437467793625685821216020779247523666721758801733589747337849247346899425349147268152461678125906071471834676112405343343956698976393725993977695617708488484909616454320408236182254100663514392663988532469781250088749171558917572313626520320178676148544919877041358098554483954790172091138185361565655261012863907079644076228772780748351151926491215503647185415971443705202181767609730806089770801035429749998043792312851884644743752813258139875276310149574144396379172372433319740886535397849571861903477542572434740208689196188897381758130683224720946734486870662993972958242564498642289481257673061271646154799451748897408440149105922652489081901881190687295443602490373855311584574590569819672287646003467989460539878700753688516834744400655248098721042798094922853807034173937553014758910903166723693219094324958985538822226275412837868767849455864993908045703165812516453925106076720925664553211297349630885657515014079813436986731838509436367169544767983588348172639936282163815402727935993699213679304559824496017851460676697897561329983501996510963688138874655152475344904323788272122634729260781509246326358372566482432472194713353700628497335073777469597999790082549366484067173305172474929907836440985423540565767243778594081828708945224070163357648368651464839780435877072249325792168345131643285292301344844968174212466176541002362066701716438334393602510379752410453535501774417268612253289131889040539333102428437613225420554729020100465702534065578988787694649021448405621508669423820835999946793329162317636529741456662520053535310703552737556758998800108529809665211831386755353866127149625163666779521322727930638163656890122830323910546848030636543332249360303039404438910588249720932306541563590770596925332441479321749157111249066406550557696231957852075133719500736203578650066813155988760390174715181370216345598412623392598864950301027169922675662182959278772058241220076908068561820691331178873268044491717169569011045123586361336089584037973586874622600834671824806671811098540809367330724392994098844290463613143289532328775319183024595903171732536374124988795744073909040413308180261874890993381644921381274607102275620405042127210382719983082685633866484392100738884373676686979899044294781411673631215505276518116221540575820837344924985303468053846195655413116266031120844021436834615165161472761479297894656802478387122781254486866876028528475220274133079206008736124537893667972984881218229985926338252796260887834458813714575859009379935614974051839193281981297824005736639857016030291148184720157796439522000826862540346487723465189977369189882601926042049659661063643206563237843559200905087283130352202990209189223108945473494240717500360891066395194741480745156765009222906935515433358682588895905416741543996511360937592478388725605389719306466810380027885711190100698281872210494382939688609171235794755465526434836854853832912247923196199966028777141052554294671972317063437414723165040433612516879993128928872841172738808153392441022928739731085880277177493185489461756392591259000087065681837528998559187231981473331080573187926672726930585739939888272495695285777375302525902472988714375966921511442117458516678245895080868501740982654276036044560620520065518030631867236770513316758754103689961254256359861141701271540896101025082179652139128056988035169507273892915247469263858016614803662933936230160620854212420346408215267232249922568522099691758589620507335724997813111105030641744067465593772218513211477334999346130140206221426772875328790818589390667159108462025159490124949540066157019389321256733646476754426996654717162188226741532138935795707734537510573574153893253425413281031566662480032673914324808453322722499260898566901940251790312824537149175649667700005717 68 | ## /33264402841837255560349406117879831215495039816338611012209182619472191541894259205428296072930514706148313134532448580419087078279090789069870230300818780265008134430774247569464925797913696964537456409532700489437025357773021227550021255963905299597172887614582950137545724040239246041764170911555793687677112086521835611732481117177891957235524825887374670849621010850571181204040715695709171052015096794232454864112868733701628615574721969904245609843733230781579664977309021287316159369744484331843163531748971992510242076371925431946730355919226785500693640759946794924682020358155850952618123050460526190754784693449780957488164347534999409564002074580271536847087086921145339279315352860434862497234729590853650485757056523379668241827747611156476419336350315323486974881243667917367731781048684363030264673830233062520433713740449151979443447490906138069875150399751903544550577624991850469340823946765869330376668000189495775594381782682778031285713870836709801997767637441119303671434695417613684153460870305342868472240468955500320890696390793105154293470049652304615308661936325097106048943250272356864722920331774440222138731369136540448795634537068940345750535130938347798162166175117895731089408143893464595500791049476638884311787505050714379895085363709493095166800524328381354895661597285981234101635802065644444502628624669032694493703359968894571040979464403864941299058911577015609568259581919982283009672135689645081659860658519494654028239164392931394859922056178870907135255434432869831154867097938557964192234165001977565504294742789037015037400452220446520067482124465665190845782618458856184961946589346404166132228507878943325076670404741876701455524715344277836571497693045572973375747362295671741701807931727693040653343465311124801855857254443794700369517474044267706844153944398161827202196602896389363565320448794778738094307360294100245018282541478067967049003121446997827752272130857020166454334273703688901260601073356756676910053224728662497449982731176317461481413221354237773567426238784510530856083089122577263786912007877984248779746887199496568258985031981745699075184418063407090421186497027321221634397838860454106798846779561865849835990934702798367109223472452837161048970909474492933210441446045137728124348097835645661870949154259825078332193490106906957884306275168868076077093193480076981926191392790143004882887302295103849066909668866503667925481555791382815071759138446335291510588351454746567603250701036180024307736270619726666399054055820740137214908290461246130705019921390550874356384984265520768025502519236722377296675364612175096266535210161446882313898204323805042131313890489543841109198909227369117582144432442198160140324878965724386708682285816720913485765962288839863851625971850672279417411764036414084732202481118856781737258330101182200968849260848307881877914570552473301844781294382619293360202598822198575551498215240062173118925032996319979761741830312100000147195346451370469415564050152187026595454332829011850743472599810154902228512712967921338617921281414264629584544511134406928428179792402654347282832655721235837631489717455480272168085055391950256212832656595947896552653764783508990702728431541488849965211226645225519044138614048090116493362127783469943458667907819151385091811814552197080361798277165354840326324099882143215396459357755198526608135182333850908437876040208597117786233154368061101603107598197256783296211739735558842491358443427682396127666029970557542464481674870797420720487182264967048698280464543180486039223366355861247450513367356835591209333722147683011339805720250543383372473396275839578797415552911151926799463881045084759094078004596796892072608745970157502804412417888731418195702085382719463878114493773698596581254200300387111827288513437803152354147064931784194427600379762105179046152666945649652907627804754496400395696477932219873340528413307213931247296286867072249428145798741977510787744904008248393155032324940410962721229092722379339539861719264238283712253805003748760884933288442527628486217842453934704772150794530231503283278089565505168620177892958213853026815386541404715220153652545856865986923929778749536982423525032916892842949554771645147152083669905146632991549232311925529673744618457966305211578933862381189871744709120954014942021581791625306655692824062256889600740993512524990096331708048036366571948952907509257270969423846457941898405496272090362279785959795181744588650122759945949081887838534365129271878710921318092021788460152659314462189318741633728680168208222928998218292783321173363686981529877572962248051621726093634859093941972137262493586948257012306286747967013082743329999247973893673081597262419260171281273490963890193116729138383416104944415269484841564099108038453618669065429120256092765171228082675810488264622796118853633866122991514821794184150740812246074751983200525837532858794659782784622615626836093385918544309782879099877179902121495225072612400010817450415545329420072737269835594167923371449491072932070624592681558694572707768956889327480435267432510190501177515725808362421019160229086420362092967948491793634153703214463588506996558499722779629923668094605915423195631572470764238633590255662125991835866355941842332198480913676625450188569960308451906289527117969094587824814470058885056081417133412767443339670896413969830537619538970176124887841696308216810943296383559500134949665993684928873627638699525791956398577734738994891596400994844249726102400944716525461712485203259398296485102557311636680484477552730814681845987476342273189477508739378373121352983539924761597482504364519321088456402274911461181514144830638556514696656847114340044778831826180982709021038305150445967243015955163912943126218632586487956271430395743890720548471837484731949814734485523523998727547393899605823924385089991318450121081178528243764673256161706784775854196446900973979672782753497517567985383313134414846684670387662643538721087547173295763830033198259702065595263039968305694259042822085142235103285993782978615887428173092189977846684761838363721490671386488846048408200918150803901198053890796285689694323167048240376786488600906735478814959460899161883137618579846359798418294209436649286559925662103975491713598755617015934110547768284554944478036488859390163205373042832190923808964631989146651072692491006811396402738795366557398341911218977614695670815730533569809694824196890006055878006454108489936668911082741607188988463196571428652073639554751575232389071210793434612825330805437992585615237827050636793497381238153484089205286860770633219792429961782976462193366367866791864719508480982292217534264549738584007068775735480394416835481403455817589920031201290556253536456036610688217111644491171269193561162398786454710716183190114899487341528476773882186512310827069112494481912585593360293453345205620122988419303457331674163827617236935071659762909831466360008440141555324932144952971274474537376264554687854379678628034748170796612684104505881097033772586636808929594109962393683728837371021778692307565437884189525703187497742739990243925011600363197093423965985115356578555432404021897972588598036476773451067161509135663143450811411986213361049161516465445773298837516589129787104314541601579417501236864492550766578261048370032013161070726860032209214134931342985987629142820893274771434806903214787375372778373370706103857998979941153374806575871083415439042230462963103634053847109972645460439720828415003193524703979383370133072534900668546683235995478363653653195114262237020838649504970998021249362492168196239186603983767942097894916755461694307551477154681114831402126069473779539970316039319986720036446305432725174898264760816423873714389780315904921963418760894889610952095835066891394318524364729100362458731755867661527245519850209107298781078041399724377569864732449895875542557219164511267995824339480872731173499186782391832829564918280937833935927611404299888631265559539486723814521074677744446633385106520472438884215545943530388553006662382424129871601979026884378569431849646272074539539584660768204573450044406445190866516025008258454344958749612909545168167640200038030904814229214358370820446197819040500716555513500536640814823879805431577609790132049303870745004630414173903227918192132116798703961633720220159794695895892129620885189962065533528870545023650911936062839656491987620337222408560457872872488559448946173026871601759807326578999201633410398565099094020751344023413680048350699713441437881843014697501248387719839832864530039699146049618899448561805422839678172391612177718904777699104987723632979965333342423421020715661865014763847773042161439279283022603223015527076982157542643853382180549104283340100656314099214441929166761859014799586372485120000000000 69 | 70 | ``` r 71 | iprint(r) 72 | ``` 73 | 74 | ## [1] "(%i2) sum(1/x^2, x, 1, 10000)" 75 | 76 | ``` r 77 | maxima.isInstalled() 78 | ``` 79 | 80 | ## [1] TRUE 81 | 82 | ``` r 83 | maxima.version() 84 | ``` 85 | 86 | ## [1] '5.44.0' 87 | 88 | ``` r 89 | maxima.options 90 | ``` 91 | 92 | ## Option Value 93 | ## -------------:------------------------------------------------------------------------------------------------------ 94 | ## format linear 95 | ## (Printing format of returned object from maxima.get()) 96 | ## engine.format linear 97 | ## (Same as 'format', but for maxima code chunks in 'RMarkdown' documents.) 98 | ## inline.format linear 99 | ## (Same as 'engine.format', but for printing output inline via maxima.inline(). Cannot be set to 'ascii'.) 100 | ## label TRUE 101 | ## (Sets whether a maxima reference label should be printed when printing a maxima return object.) 102 | ## engine.label TRUE 103 | ## (Same as 'label', but for maxima code chunks.) 104 | ## inline.label TRUE 105 | ## (Same as 'label', but for inline code chunks) 106 | 107 | ``` r 108 | maxima.options(format = "ascii") 109 | maxima.apropos("int") 110 | ``` 111 | 112 | ## (%o3) [adjoint, adjoint-impl, askinteger, askinteger-impl, 113 | ## beta_args_sum_to_integer, cint, defint, defint-impl, disjointp, 114 | ## disjointp-impl, display_format_internal, expint, expint-impl, expintegral_chi, 115 | ## expintegral_chi-impl, expintegral_ci, expintegral_ci-impl, expintegral_e, 116 | ## expintegral_e-impl, expintegral_e1, expintegral_e1-impl, expintegral_ei, 117 | ## expintegral_ei-impl, expintegral_hyp, expintegral_li, expintegral_li-impl, 118 | ## expintegral_shi, expintegral_shi-impl, expintegral_si, expintegral_si-impl, 119 | ## expintegral_trig, expintexpand, expintrep, factor_max_degree_print_warning, 120 | ## floatint, fpprintprec, gcprint, intanalysis, integer, integer_partitions, 121 | ## integer_partitions-impl, integerp, integerp-impl, integervalued, integral, 122 | ## integrate, integrate-impl, integrate_use_rootsof, integration_constant, 123 | ## integration_constant_counter, interaction, interpolate, intersect, 124 | ## intersect-impl, intersection, intersection-impl, intervalp, intfaclim, 125 | ## intopois, intopois-impl, intosum, intosum-impl, invert_by_adjoint, 126 | ## invert_by_adjoint-impl, invert_by_adjoint_size_limit, ldefint, ldefint-impl, 127 | ## linespoints, lisp_print, loadprint, lognegint, mapprint, maxfpprintprec, 128 | ## maxpsinegint, maxpsiposint, mdebug_print_length, ninth, nointegrate, 129 | ## noninteger, nonnegintegerp, noprint, point_type, pointbound, points, poisint, 130 | ## poisint-impl, print, print-impl, printf, printfile, printfile-impl, printpois, 131 | ## printpois-impl, printprops, printvarlist, printvarlist-impl, ratprint, 132 | ## require_integer, require_posinteger, require_selfadjoint_matrix, specint, 133 | ## specint-impl, sprint, tldefint, tldefint-impl, e-integer-coeff] 134 | 135 | ``` r 136 | maxima.get("integrate(1 / (x^4 + 1), x);") 137 | ``` 138 | 139 | ## 2 x + sqrt(2) 140 | ## 2 2 atan(-------------) 141 | ## log(x + sqrt(2) x + 1) log(x - sqrt(2) x + 1) sqrt(2) 142 | ## (%o4) ----------------------- - ----------------------- + ------------------- 143 | ## 5/2 5/2 3/2 144 | ## 2 2 2 145 | ## 2 x - sqrt(2) 146 | ## atan(-------------) 147 | ## sqrt(2) 148 | ## + ------------------- 149 | ## 3/2 150 | ## 2 151 | 152 | ``` r 153 | maxima.get("jacobian( [alpha / (alpha + beta), 1 / sqrt(alpha + beta)], [alpha, beta] )") 154 | ``` 155 | 156 | ## [ 1 alpha alpha ] 157 | ## [ ------------ - --------------- - --------------- ] 158 | ## [ beta + alpha 2 2 ] 159 | ## [ (beta + alpha) (beta + alpha) ] 160 | ## (%o5) [ ] 161 | ## [ 1 1 ] 162 | ## [ - ------------------- - ------------------- ] 163 | ## [ 3/2 3/2 ] 164 | ## [ 2 (beta + alpha) 2 (beta + alpha) ] 165 | 166 | ``` r 167 | l <- maxima.get("%;") 168 | maxima.eval(l, code = TRUE, envir = list(alpha = 0.3, beta = 2.5)) 169 | ``` 170 | 171 | ## [,1] [,2] 172 | ## [1,] 0.3188776 -0.03826531 173 | ## [2,] -0.1067168 -0.10671684 174 | ## attr(,"maxima") 175 | ## matrix(data = c(((-1L * alpha * ((alpha + beta)^-2L)) + ((alpha + 176 | ## beta)^-1L)), ((-1L/2L) * ((alpha + beta)^(-3L/2L))), (-1L * 177 | ## alpha * ((alpha + beta)^-2L)), ((-1L/2L) * ((alpha + beta)^(-3L/2L)))), 178 | ## ncol = 2, nrow = 2) 179 | 180 | ``` r 181 | unclass(l) 182 | ``` 183 | 184 | ## $wtl 185 | ## $wtl$linear 186 | ## [1] "(%o6) matrix([1/(beta+alpha)-alpha/(beta+alpha)^2,-alpha/(beta+alpha)^2]," " [-1/(2*(beta+alpha)^(3/2)),-1/(2*(beta+alpha)^(3/2))])" 187 | ## 188 | ## $wtl$ascii 189 | ## [1] " [ 1 alpha alpha ]" " [ ------------ - --------------- - --------------- ]" 190 | ## [3] " [ beta + alpha 2 2 ]" " [ (beta + alpha) (beta + alpha) ]" 191 | ## [5] "(%o6) [ ]" " [ 1 1 ]" 192 | ## [7] " [ - ------------------- - ------------------- ]" " [ 3/2 3/2 ]" 193 | ## [9] " [ 2 (beta + alpha) 2 (beta + alpha) ]" 194 | ## 195 | ## $wtl$latex 196 | ## [1] "$$\\mathtt{(\\textit{\\%o}_{6})}\\quad \\begin{pmatrix}\\frac{1}{\\beta+\\alpha}-\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} & -\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} \\\\ -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} & -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} \\\\ \\end{pmatrix}$$" 197 | ## 198 | ## $wtl$inline 199 | ## [1] "$\\mathtt{(\\textit{\\%o}_{6})}\\quad \\begin{pmatrix}\\frac{1}{\\beta+\\alpha}-\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} & -\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} \\\\ -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} & -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} \\\\ \\end{pmatrix}$" 200 | ## 201 | ## $wtl$mathml 202 | ## [1] " mlabel " " %o 6 " 203 | ## [3] " ," " 1 β " 204 | ## [5] " + α - " " α " 205 | ## [7] " β + α " " 2 -" 206 | ## [9] " α " " β + α " 207 | ## [11] " 2 " " -1 " 208 | ## [13] " 2 " " β + α " 209 | ## [15] " 3 " " 2 " 210 | ## [17] " -1 " " 2 " 211 | ## [19] " β + α " " 3 " 212 | ## [21] " 2 " " " 213 | ## 214 | ## 215 | ## $wol 216 | ## $wol$linear 217 | ## [1] "matrix([1/(beta+alpha)-alpha/(beta+alpha)^2,-alpha/(beta+alpha)^2]," " [-1/(2*(beta+alpha)^(3/2)),-1/(2*(beta+alpha)^(3/2))])" 218 | ## 219 | ## $wol$ascii 220 | ## [1] "[ 1 alpha alpha ]" "[ ------------ - --------------- - --------------- ]" "[ beta + alpha 2 2 ]" 221 | ## [4] "[ (beta + alpha) (beta + alpha) ]" "[ ]" "[ 1 1 ]" 222 | ## [7] "[ - ------------------- - ------------------- ]" "[ 3/2 3/2 ]" "[ 2 (beta + alpha) 2 (beta + alpha) ]" 223 | ## 224 | ## $wol$latex 225 | ## [1] "$$\\begin{pmatrix}\\frac{1}{\\beta+\\alpha}-\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} & -\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} \\\\ -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} & -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} \\\\ \\end{pmatrix}$$" 226 | ## 227 | ## $wol$inline 228 | ## [1] "$\\begin{pmatrix}\\frac{1}{\\beta+\\alpha}-\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} & -\\frac{\\alpha}{\\left(\\beta+\\alpha\\right)^2} \\\\ -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} & -\\frac{1}{2\\,\\left(\\beta+\\alpha\\right)^{\\frac{3}{2}}} \\\\ \\end{pmatrix}$" 229 | ## 230 | ## $wol$mathml 231 | ## [1] " " " " 232 | ## [3] " 1 β + " " α - " 233 | ## [5] " α " " β + α " 234 | ## [7] " 2 -" " α " 235 | ## [9] " β + α " " 2 " 236 | ## [11] " -1 " " 2 " 237 | ## [13] " β + α " " 3 " 238 | ## [15] " 2 " " -1 " 239 | ## [17] " 2 " " β + α " 240 | ## [19] " 3 " " 2 " 241 | ## [21] " " 242 | ## 243 | ## $wol$rstr 244 | ## [1] "matrix(data = c(((-1L * alpha * ((alpha + beta) ^ -2L)) + ((alpha + beta) ^ -1L)), ((-1L / 2L) * ((alpha + beta) ^ (-3L / 2L))), (-1L * alpha * ((alpha + beta) ^ -2L)), ((-1L / 2L) * ((alpha + beta) ^ (-3L / 2L)))), ncol = 2, nrow = 2)" 245 | ## 246 | ## 247 | ## attr(,"input.label") 248 | ## [1] "%i6" 249 | ## attr(,"output.label") 250 | ## [1] "%o6" 251 | ## attr(,"command") 252 | ## [1] "%;" 253 | ## attr(,"suppressed") 254 | ## [1] FALSE 255 | ## attr(,"parsed") 256 | ## matrix(data = c(((-1L * alpha * ((alpha + beta)^-2L)) + ((alpha + 257 | ## beta)^-1L)), ((-1L/2L) * ((alpha + beta)^(-3L/2L))), (-1L * 258 | ## alpha * ((alpha + beta)^-2L)), ((-1L/2L) * ((alpha + beta)^(-3L/2L)))), 259 | ## ncol = 2, nrow = 2) 260 | 261 | ``` r 262 | maxima.load("abs_integrate") 263 | maxima.stop() 264 | ``` 265 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | # Test environments 2 | * Debian 12 bookworm (local install), R 4.4.3 3 | * win-builder (devel, release and oldrel) 4 | * Windows 10 (local install), R 4.4.3 5 | * Fedora 42 (devel + clang) 6 | * macOS 13.3.1 7 | 8 | # R CMD check results 9 | * There are no ERRORs or WARNINGs. 10 | 11 | # Downstream dependencies 12 | There are currently no downstream dependencies for this package. 13 | -------------------------------------------------------------------------------- /docs/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "rim - R's interface to Maxima" 3 | output: 4 | html_document: default 5 | pdf_document: default 6 | --- 7 | 8 | 9 | 10 | Development version of the CRAN package. `rim` provides an interface to the powerful and fairly complete maxima computer algebra system 11 | 12 | This repository is a fork from the original `RMaxima` created by Kseniia Shumelchyk and Hans W. Borcher [shumelchyk/rmaxima](https://github.com/shumelchyk/rmaxima), which is currently not maintained. 13 | 14 | # Requirements 15 | - Maxima (>= 5.42.1), this package has been tested with version between 5.42.1 and 5.46.0, but may also work with older versios 16 | 17 | # Installation 18 | 19 | To install the current release version from CRAN: 20 | 21 | ```{r, eval = FALSE} 22 | install.packages("rim") 23 | ``` 24 | 25 | If you want to install the latest version the easiest way is to install the R package `remotes` first and install the package from this github repo: 26 | 27 | ```{r, eval = FALSE} 28 | install.packages("remotes") 29 | remotes::install_github(repo = "rcst/rim") 30 | ``` 31 | 32 | # Usage 33 | 34 | If you want to learn how to use Maxima you can check out a number of ressources from it's [project site](https://maxima.sourceforge.io/documentation.html). 35 | 36 | ## RMarkdown 37 | 38 | This section demonstrates the use of `rim` via it's knitr engine. Alternatively, you can run each line of the Maxima code chunks via `maxima.get()`. 39 | 40 | This page has been generated using `rim`'s knitr engine itself. 41 | 42 | Note that you can set the output format, e.g. `maxima.options(engine.format = "latex")` to get LaTeX-style expression rendering. When using pandoc to render the resulting file, MathJax is loaded or embedded automatically, when the ouput is a HTML document and so the Maxima output expressions are rendered nicely. Alternatively, expression outputs can be printed in MathML directly using `maxima.options(engine.format = "mathml")`. 43 | 44 | When attaching the package and Maxima is installed already, then it will be set up and started in the background automatically, when a command is send. In addition, if you have installed the R-package `knitr` (which get's installed by default with this package as well), it will register maxima as a `knitr` engine. If this worked, maxima code can be used and rendered in RMarkdown documents. 45 | 46 | ```{r} 47 | # library(rim) 48 | devtools::load_all() 49 | maxima.options(engine.format = "latex", 50 | engine.label = TRUE, 51 | inline.format = "latex", 52 | inline.label = FALSE) 53 | ``` 54 | 55 | For example, to generate this page you can download it's source `index.Rmd` from [here](https://raw.githubusercontent.com/rcst/rim/master/docs/index.Rmd) and then use `rmarkdown::render` to generate it. 56 | 57 | ```{r, eval = FALSE} 58 | download.file(url = "https://raw.githubusercontent.com/rcst/rim/master/docs/index.Rmd", 59 | destfile = "index.Rmd") 60 | rmarkdown::render("index.Rmd") 61 | ``` 62 | 63 | Now we can enter Maxima expression inside code chunks. Note that we need to end each line by `;` or `$` (suppressing output). For example we can type the following code chunk into our RMarkdown document file: 64 | 65 | ```` 66 | 67 | ```{maxima}`r ''` 68 | L: sqrt(1 - 1/R^2); 69 | assume(R > 0)$ 70 | 'integrate(x, x, 0, L) = integrate(x, x, 0, L); 71 | ``` 72 | 73 | ```` 74 | 75 | In the above code chunk we define a variable `L` (that depends on another variable `R`). We tell Maximas database to assume `R` being larger than zero and suppress any output resulting from this command. The last line prints an unevaluated integral on the left-hand side of the equal sign and on the right-hand side evaluates that same definit integral. The tick quotation mark tells Maxima not to evaluate the suceeding expression. This, when rendered into a document will be printed as 76 | 77 | ```{maxima first-example} 78 | L: sqrt(1 - 1/R^2); 79 | assume(R > 0)$ 80 | 'integrate(x, x, 0, L) = integrate(x, x, 0, L); 81 | ``` 82 | 83 | The resulting output (if not suppressed) is printed after each line of code, and thus apprears as several. Input and output reference labels that are assign by Maxima are printed into he code chunk. Those can be used to refer to previous commands in other code chunks. For example 84 | 85 | ```{maxima rhs} 86 | sqrt(rhs(%o3)); 87 | ``` 88 | 89 | takes the right-hand side of the result of the equation from input label `%i3`, which is assigned to output label `%o3` and computes the square-root of it. 90 | 91 | Of course exercising on such simple computations is of little benefit. The real benefit comes from more complicated expression and the effort that we would need to put if we wanted to typeset the result, such as this 92 | 93 | ```{maxima integration, output.var = "foo"} 94 | integrate(1 / (1 + x^4), x); 95 | ``` 96 | 97 | `pandoc` automatically renders the LaTeX output format from Maxima by including MathJax JavaScript script. In general, `pandoc` takes care of how mathematical equations delimited by `$$` are rendered. 98 | 99 | 100 | However, we can also change Maxima's output format to MathML, which works if the output document is a HTML document. 101 | 102 | ```{r changing-options} 103 | maxima.options(engine.format = "mathml") 104 | maxima.options() 105 | ``` 106 | 107 | ```{maxima simple-mathml} 108 | sqrt(3/4); 109 | ``` 110 | 111 | ```{maxima differentiate} 112 | f(x) := e^(x^2)$ 113 | diff(f(x), x); 114 | ``` 115 | 116 | ```{maxima repeat} 117 | %; 118 | ``` 119 | 120 | Notice, that we can use the symbol `%` to refer to the last expression and that this works across code chunks. 121 | 122 | You can also replay other previous expressions by referring to their output labels, as already demonstrated above. Whether or not reference labels are printed via the `knitr` engine has no influence on their existence in Maxima internally. 123 | 124 | `rim` also supports printing Maxima output as inline. This works by putting an `r` inline code chunk and using `maxima.inline()`. Example: `` `r '\x60r maxima.inline("log(%o1);")\x60'` `` results in `r maxima.inline("log(%o1);")`. 125 | 126 | ```{r changing-options-back} 127 | maxima.options(engine.format = "latex") 128 | ``` 129 | 130 | Comments can be added to Maxima code as text between `/*` and `*/` (example taken from the [Maxima manual](https://maxima.sourceforge.io/docs/manual/maxima_73.html)). 131 | 132 | ```{maxima comments} 133 | /* aa is a variable of interest */ aa : 1234; 134 | bb : aa^2; /* Value of bb depends on aa */ 135 | /* User-defined infix operator */ infix ("Q"); 136 | /* Parses same as a b c, not abc */ a/* foo */Q/* bar */c; 137 | /* Comments /* can be nested /* to any depth */ */ */ 1 + xyz; 138 | ``` 139 | 140 | 141 | ### Chunk Options 142 | 143 | The following chunk options are currently available: 144 | 145 | - `echo`: Should the code chunk be printed (default is `TRUE`). If `eval=FALSE`, individual code lines are printed without without input labels, since these are assigned by Maxima upon evaluation 146 | - `eval`: Should the code chunk be evaluated by Maxima (default is `TRUE`) 147 | - `include`: whether code chunk and results should be printed (default is `TRUE`). If `FALSE`, code is still evaluated 148 | - `output.var`: A character string for a variable that caputures the parsed output expression(s) (i.e., translated into R-code), see example below. 149 | 150 | **NOTE:** The `results` chunk option is explicitly ignored from chunk header and implicitly controlled via `maxima.options(engine.format = ...)`. 151 | 152 | 153 | #### Capturing parsed Maxima output 154 | 155 | One of the above Maxima code chunk was actually set with option `output.var = "foo"` 156 | 157 | ```` 158 | 159 | ```{maxima output-var, output.var = "foo"}`r ''` 160 | integrate(1 / (1 + x^4), x); 161 | ``` 162 | 163 | ```` 164 | 165 | which captured the Maxima result(s) as parsed R-code (unevaluated expressions) into a named list. Those expressions can subsequently be evaluated in R e.g., to evaluate an expression in the context of a data.frame 166 | 167 | ```{r using-output-var} 168 | df <- data.frame(x = 1:10, y = letters[1:10]) 169 | df$z <- eval(foo[["o5"]], envir = df) 170 | df 171 | foo 172 | ``` 173 | 174 | In this way, Maxima can assist us with symolic computations in our statistical modelling in R. 175 | 176 | # Demos 177 | 178 | ## Plots 179 | 180 | ```{maxima plot2d, fig.cap = "plot2d()", fig.align="center"} 181 | r: (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)$ 182 | plot2d([parametric, r*sin(t), r*cos(t), [t,-8*%pi,8*%pi]]); 183 | ``` 184 | 185 | ```{maxima plot3d, fig.cap = "plot3d()", fig.align="center"} 186 | plot3d(log (x^2*y^2), [x, -2, 2], [y, -2, 2],[grid, 29, 29], 187 | [palette, [gradient, red, orange, yellow, green]], 188 | color_bar, [xtics, 1], [ytics, 1], [ztics, 4], 189 | [color_bar_tics, 4]); 190 | ``` 191 | 192 | ```{maxima draw, fig.cap = "draw()", fig.align="center"} 193 | example1: 194 | gr3d (title = "Controlling color range", 195 | enhanced3d = true, 196 | color = green, 197 | cbrange = [-3,10], 198 | explicit(x^2+y^2, x,-2,2,y,-2,2)) $ 199 | 200 | example2: 201 | gr3d (title = "Playing with tics in colorbox", 202 | enhanced3d = true, 203 | color = green, 204 | cbtics = {["High",10],["Medium",05],["Low",0]}, 205 | cbrange = [0, 10], 206 | explicit(x^2+y^2, x,-2,2,y,-2,2))$ 207 | 208 | example3: 209 | gr3d (title = "Logarithmic scale to colors", 210 | enhanced3d = true, 211 | color = green, 212 | logcb = true, 213 | logz = true, 214 | palette = [-15,24,-9], 215 | explicit(exp(x^2-y^2), x,-2,2,y,-2,2))$ 216 | 217 | draw( 218 | dimensions = [500,1500], 219 | example1, example2, example3)$ 220 | ``` 221 | 222 | ```{maxima draw2d, fig.cap = "draw2d()", fig.align="center"} 223 | draw2d( 224 | dimensions = [1000, 1000], 225 | proportional_axes = xy, 226 | fill_color = sea_green, 227 | color = aquamarine, 228 | line_width = 6, 229 | ellipse(7,6,2,3,0,360))$ 230 | ``` 231 | 232 | ```{maxima draw3d, fig.cap = "draw3d()", fig.align="center"} 233 | draw3d( 234 | dimensions = [1000, 1000], 235 | surface_hide = true, 236 | axis_3d = false, 237 | proportional_axes = xyz, 238 | 239 | color = blue, 240 | cylindrical(z,z,-2,2,a,0,2*%pi), 241 | 242 | color = brown, 243 | cylindrical(3,z,-2,2,az,0,%pi), 244 | 245 | color = green, 246 | cylindrical(sqrt(25-z^2),z,-5,5,a,0,%pi))$ 247 | ``` 248 | 249 | ## Distributions 250 | 251 | This is basically a reproduced demo for `Sympy` from [Bryan Zhang's Blog](https://brianzhang01.github.io/2018/04/distributions-with-sympy/). 252 | 253 | First we define some helper functions: 254 | 255 | ```{maxima dist-functions} 256 | area(dist) := integrate(dist, x, minf, inf)$ 257 | mean(dist) := area(dist*x)$ 258 | EX2(dist) := area(dist*x^2)$ 259 | variance(dist) := EX2(dist) - mean(dist)^2$ 260 | mgf(dist) := area(dist*%e^(x*t))$ 261 | ``` 262 | 263 | ### Normal Distribution 264 | 265 | ```{maxima normal} 266 | normal(x) := 267 | (2*%pi*sigma^2)^(-1/2) * 268 | exp(-(x-mu)^2/(2*sigma^2)); 269 | 270 | assume(sigma > 0)$ 271 | 272 | area(normal(x)); 273 | mean(normal(x)); 274 | variance(normal(x)); 275 | mgf(normal(x)); 276 | ``` 277 | 278 | ### Pareto Distribution 279 | 280 | ```{maxima pareto} 281 | pareto(x) := (alpha * (x[m])^alpha) / x^(alpha+1); 282 | assume(x[m] > 0)$ 283 | assume(alpha > 0)$ 284 | ``` 285 | 286 | 287 | ### Laplace Distribution 288 | 289 | ```{maxima laplace} 290 | laplace(x) := (2*b)^-1 * exp(-abs(x - mu)/b); 291 | 292 | load("abs_integrate")$ 293 | 294 | assume(b > 0)$ 295 | 296 | area(laplace(x)); 297 | mean(laplace(x)); 298 | variance(laplace(x)); 299 | ``` 300 | 301 | ### Exponential Distribution 302 | 303 | ```{maxima exponential} 304 | expo(x) := unit_step(x) * lambda * exp(-lambda * x); 305 | 306 | assume(lambda > 0)$ 307 | 308 | area(expo(x)); 309 | mean(expo(x)); 310 | variance(expo(x)); 311 | ``` 312 | 313 | ## Matrices 314 | 315 | ```{maxima matrices} 316 | m: matrix([0, 1, a], [1, 0, 1], [1, 1, 0]); 317 | transpose(m); 318 | determinant(m); 319 | f: invert(m), detout; 320 | m . f; 321 | expand(%); 322 | factor(%); 323 | ``` 324 | 325 | ## If-then-else 326 | 327 | ```{maxima x-y} 328 | x: 1234; 329 | y: 2345; 330 | ``` 331 | 332 | ```{maxima if-then-else} 333 | if x > y 334 | then x 335 | else y; 336 | ``` 337 | -------------------------------------------------------------------------------- /index.R: -------------------------------------------------------------------------------- 1 | if (require(devtools) & require(roxygen2) & require(Rcpp) & require(drat)) { 2 | rmarkdown::render(input = "docs/index.Rmd", output_file = "index.html") 3 | rmarkdown::render(input = "README.Rmd", output_file = "README.md") 4 | 5 | 6 | Rcpp::compileAttributes() 7 | devtools::document() 8 | devtools::load_all() 9 | devtools::test() 10 | devtools::test_active_file("tests/testthat/test-parser.R") 11 | devtools::test_active_file("tests/testthat/test-engine.R") 12 | devtools::build() 13 | devtools::run_examples() 14 | devtools::check() 15 | 16 | rmarkdown::render(input="inst/extdata/test.Rmd", 17 | output_dir = "inst/extdata/", 18 | output_file = "result.html") 19 | 20 | rmarkdown::render(input="inst/extdata/test.Rmd", 21 | output_dir = "inst/extdata/", 22 | output_file = "test.html") 23 | 24 | knitr::knit(input = "inst/extdata/test.Rmd", output = "inst/extdata/result.md") 25 | knitr::knit(input = "inst/extdata/test.Rmd", output = fo <- tempfile(fileext = ".md")) 26 | 27 | devtools::check_win_release() 28 | devtools::check_win_oldrelease() 29 | devtools::check_win_devel() 30 | devtools::check_mac_release() 31 | devtools::check_rhub() 32 | 33 | devtools::release() 34 | } 35 | 36 | # command reception in Maxima 37 | 38 | # [x] multiple complete 39 | # (%i1) 1+1;2+2; 40 | # -> runs both commands, displays two consecutive output labels 41 | dissect_repl_input("1+1;2+2;") 42 | 43 | # [x] mutltiple, partial incomplete 44 | # (%i1) 1+1; 2+ 45 | # -> runs only first, ignores second 46 | dissect_repl_input("1+1;2+") 47 | 48 | # [x] incomplete 49 | # (%i1) 1+; 50 | # -> returns error 51 | dissect_repl_input("1+;") 52 | 53 | # [x] multiple complete with comments 54 | dissect_repl_input("1+1; /* hello */ 2+2;") 55 | dissect_repl_input("1+1 /* hello /* world */ */; 2+2;") 56 | dissect_repl_input("1+1 /* hello 0.5*2; /* world */ */; 2+2;") 57 | 58 | # [ ] mutltiple, partial incomplete, with comments 59 | dissect_repl_input("1+1;2+ /* xxx */") 60 | dissect_repl_input("/*xxx */1+1$2+ /* xxx */") 61 | dissect_repl_input("/*xxx */1+1;2+ /* xxx */; 3+3;") 62 | 63 | # split user input line into separate commands and sequentially pass to maxima$get() 64 | assign(x = "a", value = 1L, env = rim_global()) 65 | a 66 | names(rim_global()) 67 | rim_global()$a 68 | parent.env(env = globalenv()) |> parent.env() |> parent.env() 69 | -------------------------------------------------------------------------------- /inst/extdata/display.lisp: -------------------------------------------------------------------------------- 1 | (defun tex-stripdollar (sym) 2 | (let 3 | ((nn-list (extract-trailing-digits (symbol-name sym)))) 4 | (if nn-list 5 | ;; SYM matches foo_mm_nn. 6 | (apply #'concatenate 'string (tex-array `((,(intern (first nn-list)) 'array) ,@(rest nn-list)) nil nil)) 7 | ;; SYM is a simple symbol. 8 | (let ((s (maybe-invert-string-case (quote-% (stripdollar sym))))) 9 | (if (> (length s) 1) 10 | (concatenate 'string "\\textit{" s "}") 11 | s))))) 12 | 13 | (defun tex-mlabel (x l r) 14 | (tex (caddr x) 15 | (append l 16 | (if (cadr x) 17 | (list (format nil "\\mathtt{(~A)}\\quad " (tex-stripdollar (cadr x)))) 18 | ;- (list (format nil "" (tex-stripdollar (cadr x)))) 19 | nil)) 20 | r 'mparen 'mparen)) 21 | -------------------------------------------------------------------------------- /inst/extdata/display.mac: -------------------------------------------------------------------------------- 1 | load("parser.lisp")$ 2 | load("mactex-utilities")$ 3 | load("alt-display.mac")$ 4 | 5 | define_alt_display(twod_display(x), 6 | block([alt_display1d:false,alt_display2d:false,display2d:true],displa(x)))$ 7 | define_alt_display(oned_display(x), 8 | block([alt_display1d:false,alt_display2d:false,display2d:false],displa(x)))$ 9 | define_alt_display(tex_display(x), 10 | block([alt_display1d:false,alt_display2d:false,display2d:false], 11 | printf(true,"~a~a~a",get_tex_environment_default()[1],tex1(x),get_tex_environment_default()[2])))$ 12 | define_alt_display(tin_display(x), 13 | block([alt_display1d:false,alt_display2d:false,display2d:false], 14 | printf(true,"~a~a~a","$",tex1(x),"$")))$ 15 | define_alt_display( 16 | mathml_display(x), 17 | block([display2d:true, alt_display1d:false, alt_display2d:false], mathml(x)))$ 18 | 19 | define_alt_display(twod_display_noref(x), 20 | block([alt_display1d:false,alt_display2d:false,display2d:true],displa(?caddr(x))))$ 21 | define_alt_display(oned_display_noref(x), 22 | block([alt_display1d:false,alt_display2d:false,display2d:false],displa(?caddr(x))))$ 23 | define_alt_display(tex_display_noref(x), 24 | block([alt_display1d:false,alt_display2d:false,display2d:false], 25 | printf(true,"~a~a~a",get_tex_environment_default()[1],tex1(?caddr(x)),get_tex_environment_default()[2])))$ 26 | define_alt_display(tin_display_noref(x), 27 | block([alt_display1d:false,alt_display2d:false,display2d:false], 28 | printf(true,"~a~a~a","$",tex1(?caddr(x)),"$")))$ 29 | define_alt_display( 30 | mathml_display_noref(x), 31 | block([display2d:true, alt_display1d:false, alt_display2d:false], mathml(?caddr(x))))$ 32 | define_alt_display(m2r_display_out(x), 33 | block([alt_display1d:false,alt_display2d:false,display2d:false], 34 | printf(true, "~a", ?maxima2r(?caddr(x)))))$ 35 | 36 | /* overwrittes default setting from alt-display package */ 37 | alt_display_text_prefix: "TEXT;>>"; 38 | alt_display_text_suffix: "<>~a<>"), 46 | printf(true,"~&lab;>>~%~a~%<>"), 48 | printf(true,"~&tex;>>~%") ,tex_display(x) ,printf(true,"~&<>~%") ,tin_display(x) ,printf(true,"~&<>~%") ,mathml_display(x) ,printf(true,"~&<>~%") ,oned_display(x) ,printf(true,"~&<>~%") ,twod_display(x) ,printf(true,"~&<>"), 55 | printf(true,"~&tex;>>~%") ,tex_display_noref(x) ,printf(true,"~&<>~%") ,tin_display_noref(x) ,printf(true,"~&<>~%") ,mathml_display_noref(x) ,printf(true,"~&<>~%") ,oned_display_noref(x) ,printf(true,"~&<>~%") ,twod_display_noref(x) ,printf(true,"~&<>~%") ,m2r_display_out(x) ,printf(true,"~&<>",'suffix, "<>",'suffix, "<>",'suffix, "<>~a<>~%(~a) ~a~%<>",'suffix, "<>",'suffix, "<>~a<>~%(~a) ~a~a~a~%<)) 39 | (setf (gethash 'mequal ht) '(comp-op ==)) 40 | (setf (gethash 'mnotequal ht) '(comp-op !=)) 41 | (setf (gethash 'mlessp ht) '(comp-op <)) 42 | (setf (gethash 'mgeqp ht) '(comp-op >=)) 43 | (setf (gethash 'mleqp ht) '(comp-op <=)) 44 | (setf (gethash '$floor ht) '(funcall (symbol "floor"))) 45 | (setf (gethash '$fix ht) '(funcall (symbol "floor"))) 46 | (setf (gethash '%fix ht) '(funcall (symbol "floor"))) 47 | (setf (gethash '%sqrt ht) '(funcall (symbol "sqrt"))) 48 | (setf (gethash '%log ht) '(funcall (symbol "log"))) 49 | (setf (gethash '%gamma ht) '(funcall (symbol "gamma"))) 50 | (setf (gethash 'mreturn ht) '(funcall (symbol "return"))) 51 | (setf (gethash 'mabs ht) '(funcall (symbol "abs"))) 52 | (setf (gethash '$invert ht) '(funcall (symbol "solve"))) 53 | (setf (gethash '$determinant ht) '(funcall (symbol "det"))) 54 | (setf (gethash '$transpose ht) '(funcall (symbol "t"))) 55 | (setf (gethash 'mfactorial ht) '(funcall (symbol "factorial"))) 56 | ht)) 57 | 58 | (defparameter *maxima-special-ir-map* 59 | (let ((ht (make-hash-table))) 60 | (setf (gethash 'mdefine ht) 'func-def-to-ir) 61 | (setf (gethash '$MATRIX ht) 'matrix-to-ir) 62 | (setf (gethash '$array ht) 'array-def-to-ir) 63 | (setf (gethash 'mprog ht) 'no-convert) 64 | ; (setf (gethash 'mprogn ht) 'mprogn-to-ir) 65 | (setf (gethash 'mcond ht) 'mcond-to-ir) 66 | (setf (gethash 'lambda ht) 'lambda-to-ir) 67 | ; (setf (gethash 'mdoin ht) 'for-list-to-ir) 68 | ; (setf (gethash 'mdo ht) 'for-loop-to-ir) 69 | ; (setf (gethash '%endcons ht) 'endcons-to-ir) 70 | ; (setf (gethash '$endcons ht) 'endcons-to-ir) 71 | ; (setf (gethash '$plot3d ht) 'plot-to-ir) 72 | ; (setf (gethash '$plot2d ht) 'plot-to-ir) 73 | ; (setf (gethash 'mexpt ht) 'mexpt-to-ir) 74 | ;(setf (gethash 'mfactorial ht) 'mfactorial-to-ir) 75 | ht)) 76 | 77 | (defun symbol-name-to-string (form) 78 | (string-left-trim "$%" (symbol-name form))) 79 | 80 | (defun symbol-to-ir (form) 81 | `(symbol ,(maybe-invert-string-case (symbol-name-to-string form)))) 82 | 83 | (defun int32p (x) 84 | (and (integerp x) 85 | (< (abs x) (expt 2 32)))) 86 | 87 | ;;; Generates IR for atomic forms 88 | (defun atom-to-ir (form) 89 | (cond 90 | ((eq form 'nil) `(symbol "NULL")) 91 | ((eq form '$true) `(symbol "TRUE")) 92 | ((eq form 'T) T) 93 | ((stringp form) `(string ,form)) 94 | ((and (not (symbolp form)) (floatp form)) `(num ,form)) 95 | ((and (not (symbolp form)) (int32p form)) `(int ,form)) 96 | ((and (not (symbolp form)) (integerp form)) `(num ,form)) 97 | ((eq form '$%i) '(cplx 0 1)) ; iota complex number 98 | ((eq form '$%pi) '(symbol "pi")) ; Pi 99 | ((eq form '$%e) '(funcall (symbol "exp"))) ; Euler's Constant 100 | ((eq form '$inf) '(symbol "Inf")) 101 | (t (symbol-to-ir form)))) 102 | 103 | (defun cons-to-ir (form) 104 | (cond ((atom (caar form)) 105 | (let 106 | ((type (gethash (caar form) *maxima-direct-ir-map*))) 107 | (cond 108 | (type (append type (mapcar #'maxima-to-ir (cdr form)))) 109 | ((setf type (gethash (caar form) *maxima-special-ir-map*)) 110 | (funcall type form)) 111 | ((member 'ARRAY (car form) :test #'eq) 112 | (array-index-to-ir form)) 113 | (t 114 | `(funcall 115 | ,(symbol-to-ir (caar form)) 116 | ,@(mapcar 117 | #'maxima-to-ir 118 | (cdr form))))))))) 119 | 120 | (defun maxima-to-ir (form) 121 | (cond ((atom form) 122 | (atom-to-ir form)) 123 | ((and (consp form) (consp (car form))) 124 | (cons-to-ir form)) 125 | (t (cons 'no-convert form)))) 126 | 127 | ; (defun mfactorial-to-ir (form) 128 | ; `(funcall 'factorial ,@(mapcar #'maxima-to-ir (cdr form)))) 129 | 130 | ; (defun mexpt-to-ir (form) 131 | ; `(funcall (string "exp") ,@(mapcar #'maxima-to-ir (cdr form)))) 132 | 133 | (defun mlist-to-ir (form) 134 | `(STRUCT-LIST ,@(mapcar #'maxima-to-ir (cdr form)))) 135 | 136 | (defun mcond-to-ir (form) 137 | `(COND ,@(mapcar #'maxima-to-ir (cdr form)))) 138 | 139 | (defun lambda-to-ir (form) 140 | `(LAMBDA (FUNC-ARGS ,@(mapcar #'maxima-to-ir (cdadr form))) 141 | ,@(mapcar #'maxima-to-ir (cddr form)))) 142 | 143 | (defun func-def-to-ir (form) 144 | `(func-def ,(maxima-to-ir (caaadr form)) 145 | (func-args ,@(mapcar (lambda (elm) (maxima-to-ir elm)) 146 | (cdadr form))) 147 | ,@(mapcar #'maxima-to-ir (cddr form)))) 148 | 149 | (defun matrix-to-ir (form) 150 | (cons 'MATRIX 151 | (mapcar 152 | (lambda (elm) (maxima-to-ir elm)) 153 | (cdr form)))) 154 | 155 | (defun array-def-to-ir (form) 156 | `(op-no-bracket = 157 | ,(maxima-to-ir (cadr form)) 158 | (ARRAY ,@(mapcar #'maxima-to-ir (cddr form))))) 159 | 160 | (defun array-index-to-ir (form) 161 | `(ARRAY-INDEX ,(maxima-to-ir (caar form)) 162 | ,@(mapcar (lambda (elm) (maxima-to-ir elm)) 163 | (cdr form)))) 164 | 165 | (defvar *maxima-special-r-map* ()) 166 | 167 | (defparameter *ir-r-direct-templates* 168 | (let ((ht (make-hash-table))) 169 | (setf (gethash 'symbol ht) 'symbol-to-r) 170 | (setf (gethash 'op ht) 'op-to-r) 171 | (setf (gethash 'boolop ht) 'boolop-to-r) 172 | (setf (gethash 'op-no-bracket ht) 'op-no-bracket-to-r) 173 | (setf (gethash 'comp-op ht) 'op-to-r) 174 | (setf (gethash 'unary-op ht) 'unary-op-to-r) 175 | (setf (gethash 'funcall ht) 'funcall-to-r) 176 | (setf (gethash 'int ht) 'int-to-r) 177 | (setf (gethash 'num ht) 'num-to-r) 178 | (setf (gethash 'cplx ht) 'cplx-to-r) 179 | (setf (gethash 'string ht) 'string-to-r) 180 | (setf (gethash 'struct-list ht) 'struct-list-to-r) 181 | (setf (gethash 'func-def ht) 'func-def-to-r) 182 | (setf (gethash 'lambda ht) 'lambda-to-r) 183 | (setf (gethash 'matrix ht) 'matrix-to-r) 184 | (setf (gethash 'cond ht) 'cond-to-r) 185 | (setf (gethash 'array ht) 'array-to-r) 186 | (setf (gethash 'array-index ht) 'array-index-to-r) 187 | ht)) 188 | 189 | (defun atom-to-r (form) 190 | (cond 191 | ((eq form 'NIL) 'FALSE) 192 | ((eq form '$true) 'TRUE) 193 | (t form))) 194 | 195 | (defun cons-to-r (form) 196 | (cond ((atom (caar form)) 197 | (let ((fel (gethash (car form) *ir-r-direct-templates*))) 198 | (cond (fel 199 | (funcall fel form)) 200 | ;;((setf type (gethash (caar form *maxima-r-map*))) (funcall type form)) 201 | (t 'no-convert)))))) 202 | 203 | (defun ir-to-r (form) 204 | (typecase form 205 | (cons 206 | (let ((type (gethash (car form) *ir-r-direct-templates*))) 207 | (cond 208 | (type (funcall type form)) 209 | (t (format nil "no-convert: ~a" form))))) 210 | (t 211 | (format nil "~a" form)))) 212 | 213 | (defun op-template (op) 214 | ;; replaces the control-string by the argument 215 | ;; print (in brackets) all elements of the operator 216 | ;; separated by the operator as a string 217 | (format nil "~@?" "(~~{~~#[~~;~~a~~:;~~a ~a ~~]~~})" 218 | op)) 219 | 220 | (defun op-no-bracket-template (op) 221 | (format nil "~@?" "~~{~~#[~~;~~a~~:;~~a ~a ~~]~~}" 222 | op)) 223 | 224 | (defun op-to-r (form) 225 | (format nil (op-template (cadr form)) 226 | (mapcar 227 | (lambda (elm) (ir-to-r elm)) 228 | (cddr form)))) 229 | 230 | (defun boolop-to-r (form) 231 | (let ((*with-r-list* NIL)) 232 | (format nil (op-template (cadr form)) 233 | (mapcar 234 | (lambda (elm) (ir-to-r elm)) 235 | (cddr form))))) 236 | 237 | (defun op-no-bracket-to-r (form) 238 | (format nil (op-no-bracket-template (cadr form)) 239 | (mapcar 240 | (lambda (elm) (ir-to-r elm)) 241 | (cddr form)))) 242 | 243 | (defun symbol-to-r (form) 244 | (cadr form)) 245 | 246 | (defun unary-op-to-r (form) 247 | (format nil "(~a~a)" 248 | (cadr form) 249 | (ir-to-r (caddr form)))) 250 | 251 | (defun funcall-to-r (form) 252 | (format nil "~a(~a)" 253 | (ir-to-r (cadr form)) 254 | (ir-to-r (caddr form)))) 255 | 256 | (defun int-to-r (form) 257 | (format nil "~aL" (cadr form))) 258 | 259 | (defun num-to-r (form) 260 | (format nil "~a" (cadr form))) 261 | 262 | (defun cplx-to-r (form) 263 | (format nil "complex(1, ~a, ~a)" 264 | (cadr form) 265 | (caddr form))) 266 | 267 | (defun string-to-r (form) 268 | (format nil "~c~a~c" #\" (cadr form) #\")) 269 | 270 | (defun struct-list-to-r (form) 271 | (format nil "~:[(~{~a~^, ~})~;list(~{~a~^, ~})~]" 272 | *with-r-list* 273 | (let ((*with-r-list* T)) 274 | (mapcar 275 | (lambda (elm) (ir-to-r elm)) 276 | (cdr form))))) 277 | 278 | (defun func-args-to-r (form) 279 | (format nil "~{~a~^, ~}" 280 | (mapcar 281 | (lambda (elm) (ir-to-r elm)) 282 | (cdr form)))) 283 | 284 | (defun func-def-to-r (form) 285 | (format nil "~a <- function(~a) { ~{~a~^~%~} }" 286 | (ir-to-r (cadr form)) 287 | (func-args-to-r (caddr form)) 288 | (mapcar 289 | (lambda (elm) (ir-to-r elm)) 290 | (cdddr form)))) 291 | 292 | (defun lambda-to-r (form) 293 | (format nil "function(~a) { ~a }" 294 | (func-args-to-r (cadr form)) 295 | (ir-to-r (caddr form)))) 296 | 297 | (defun func-args-to-r (form) 298 | (format nil "~{~a~^, ~}" 299 | (mapcar 300 | (lambda (elm) (ir-to-r elm)) 301 | (cdr form)))) 302 | 303 | (defun matrix-to-r (form) 304 | (format nil "matrix(data = c(~{~a~^, ~}), ncol = ~a, nrow = ~a)" 305 | (let 306 | ((ll NIL)) 307 | (dolist 308 | (i (cdr form) ll) 309 | (setf ll (append ll (mapcar 310 | (lambda (d) 311 | (ir-to-r d)) 312 | (cdr i)))))) 313 | (length (cdr form)) 314 | (- (length (cadr form)) 1))) 315 | 316 | ; the last element in a COND form 317 | ; is alsways the default case 318 | (defun cond-to-r (form) 319 | (cond ((= (length (cdr form)) 2) 320 | (format nil "if(~a) { ~a }" 321 | (ir-to-r (cadr form)) 322 | (ir-to-r (caddr form)))) 323 | ((= (length (cdr form)) 4) 324 | (format nil "if(~a) { ~a } else { ~a }" 325 | (ir-to-r (cadr form)) 326 | (ir-to-r (caddr form)) 327 | (ir-to-r (car (last form))))) 328 | ((>= (length (cdr form)) 4) 329 | (format nil "if(~a) { ~a } else ~a" 330 | (ir-to-r (cadr form)) 331 | (ir-to-r (caddr form)) 332 | (cond-to-r (cddr form)))))) 333 | 334 | (defun array-to-r (form) 335 | (format nil "array(dim = c(~{~a~^, ~}))" 336 | (mapcar 337 | (lambda (elm) (ir-to-r elm)) 338 | (cdr form)))) 339 | 340 | (defun array-index-to-r (form) 341 | (format nil "~a[~{~a~^,~}]" 342 | (ir-to-r (cadr form)) 343 | (mapcar (lambda (elm) (ir-to-r elm)) 344 | (cddr form)))) 345 | 346 | ; (defun stripdollar (form) 347 | ; (string-left-trim "$" (symbol-name form))) 348 | 349 | (defun maxima2r (form) 350 | (ir-to-r (maxima-to-ir form))) 351 | 352 | (defun maybe-invert-string-case (string) 353 | (let ((all-upper t) 354 | (all-lower t) 355 | (length (length string))) 356 | (dotimes (i length) 357 | (let ((ch (char string i))) 358 | (when (both-case-p ch) 359 | (if (upper-case-p ch) 360 | (setq all-lower nil) 361 | (setq all-upper nil))))) 362 | (cond (all-upper 363 | (string-downcase string)) 364 | (all-lower 365 | (string-upcase string)) 366 | (t 367 | string)))) 368 | -------------------------------------------------------------------------------- /inst/extdata/plot-knitr-pdf.lisp: -------------------------------------------------------------------------------- 1 | ($load "draw") 2 | ;;by default denotes the current working directory 3 | (defvar $plot_output_folder ".") 4 | ; (defvar *builtin-plot2d* (symbol-function '$plot2d)) 5 | (defvar *builtin-plot2d* (if (fboundp 'plot2d-impl) (symbol-function 'plot2d-impl) 6 | (if (fboundp '$plot2d-impl) (symbol-function '$plot2d-impl) 7 | (if (fboundp '$plot2d) (symbol-function '$plot2d) 8 | (error "rim: failed to identify built-in plot2d function."))))) 9 | ; (defvar *builtin-plot3d* (symbol-function '$plot3d)) 10 | (defvar *builtin-plot3d* (if (fboundp 'plot3d-impl) (symbol-function 'plot3d-impl) 11 | (if (fboundp '$plot3d-impl) (symbol-function '$plot3d-impl) 12 | (if (fboundp '$plot3d) (symbol-function '$plot3d) 13 | (error "rim: failed to identify built-in plot3d function."))))) 14 | (defvar *builtin-draw* (symbol-function '$draw)) 15 | (defvar *builtin-draw2d* (symbol-function '$draw2d)) 16 | (defvar *builtin-draw3d* (symbol-function '$draw3d)) 17 | 18 | (defmfun $plot2d (&rest args) 19 | (declare (notinline $substring) 20 | (notinline $simplode) 21 | (notinline $sha256sum) 22 | (notinline $sconcat)) 23 | (if (member '$pdf_file args) 24 | (apply *builtin-plot2d* args) 25 | (let* 26 | ((nnn ($substring ($sha256sum ($sconcat "/plot2d" ($simplode args))) 1 7)) 27 | (pdf-file ($sconcat $plot_output_folder "plot2d-" nnn ".pdf")) 28 | (args-new (append args (list `((mlist) $pdf_file ,pdf-file))))) 29 | (apply *builtin-plot2d* args-new)))) 30 | 31 | (defmfun $plot3d (&rest args) 32 | (declare (notinline $substring) 33 | (notinline $simplode) 34 | (notinline $sha256sum) 35 | (notinline $sconcat)) 36 | (if (member '$pdf_file args) 37 | (apply *builtin-plot3d* args) 38 | (let* 39 | ((nnn ($substring ($sha256sum ($sconcat "plot3d" ($simplode args))) 1 7)) 40 | (pdf-file ($sconcat $plot_output_folder "plot3d-" nnn ".pdf")) 41 | (args-new (append args (list `((mlist) $pdf_file ,pdf-file))))) 42 | (apply *builtin-plot3d* args-new)))) 43 | 44 | ;; a utility function 45 | (defun flatten (lst) 46 | (labels ((rflatten (lst1 acc) 47 | (dolist (el lst1) 48 | (if (listp el) 49 | (setf acc (rflatten el acc)) 50 | (push el acc))) 51 | acc)) 52 | (reverse (rflatten lst nil)))) 53 | 54 | (defmfun $draw (&rest args) 55 | (declare (notinline $substring) 56 | (notinline $simplode) 57 | (notinline $sha256sum) 58 | (notinline $sconcat)) 59 | (if (member '$file_name (flatten args)) 60 | (apply *builtin-draw* args) 61 | (let* 62 | ((nnn ($substring ($sha256sum ($sconcat "draw" ($simplode args))) 1 7)) 63 | (file_name ($sconcat $plot_output_folder "draw-" nnn)) 64 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 65 | (args-new (append args-new (list `((mequal) $terminal $pdf))))) 66 | (apply *builtin-draw* args-new) 67 | ;; (format t "inside draw~%") 68 | ;; (format t "~a~%" (flatten args-new)) 69 | `((mlist) ,file_name ,($sconcat file_name ".pdf"))))) 70 | 71 | (defmfun $draw2d (&rest args) 72 | (declare (notinline $substring) 73 | (notinline $simplode) 74 | (notinline $sha256sum) 75 | (notinline $sconcat)) 76 | (if (member '$file_name (flatten args)) 77 | (apply *builtin-draw2d* args) 78 | (let* 79 | ((nnn ($substring ($sha256sum ($sconcat "draw2d" ($simplode args))) 1 7)) 80 | (file_name ($sconcat $plot_output_folder "draw2d-" nnn)) 81 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 82 | (args-new (append args-new (list `((mequal) $terminal $pdfcairo))))) 83 | (apply *builtin-draw2d* args-new) 84 | ;; (format t "inside draw2d") 85 | ;; (format t "~a~%" (flatten args-new)) 86 | `((mlist) ,file_name ,($sconcat file_name ".pdf"))))) 87 | 88 | (defmfun $draw3d (&rest args) 89 | (declare (notinline $substring) 90 | (notinline $simplode) 91 | (notinline $sha256sum) 92 | (notinline $sconcat)) 93 | (if (member '$file_name (flatten args)) 94 | (apply *builtin-draw3d* args) 95 | (let* 96 | ((nnn ($substring ($sha256sum ($sconcat "draw3d" ($simplode args))) 1 7)) 97 | (file_name ($sconcat $plot_output_folder "draw3d-" nnn)) 98 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 99 | (args-new (append args-new (list `((mequal) $terminal $pdf))))) 100 | (apply *builtin-draw3d* args-new) 101 | ;; (format t "inside draw3d") 102 | ;; (format t "~a~%" (cdr (cadr (reverse args)))) 103 | `((mlist) ,file_name ,($sconcat file_name ".pdf"))))) 104 | -------------------------------------------------------------------------------- /inst/extdata/plot-knitr-png.lisp: -------------------------------------------------------------------------------- 1 | ($load "draw") 2 | ;;by default denotes the current working directory 3 | (defvar $plot_output_folder ".") 4 | ;; (defvar $plot_output_folder (os-getcurrentdirectory)) 5 | ; (defvar *builtin-plot2d* (symbol-function '$plot2d)) 6 | (defvar *builtin-plot2d* (if (fboundp 'plot2d-impl) (symbol-function 'plot2d-impl) 7 | (if (fboundp '$plot2d-impl) (symbol-function '$plot2d-impl) 8 | (if (fboundp '$plot2d) (symbol-function '$plot2d) 9 | (error "rim: failed to identify built-in plot2d function."))))) 10 | ; (defvar *builtin-plot3d* (symbol-function '$plot3d)) 11 | (defvar *builtin-plot3d* (if (fboundp 'plot3d-impl) (symbol-function 'plot3d-impl) 12 | (if (fboundp '$plot3d-impl) (symbol-function '$plot3d-impl) 13 | (if (fboundp '$plot3d) (symbol-function '$plot3d) 14 | (error "rim: failed to identify built-in plot3d function."))))) 15 | (defvar *builtin-draw* (symbol-function '$draw)) 16 | (defvar *builtin-draw2d* (symbol-function '$draw2d)) 17 | (defvar *builtin-draw3d* (symbol-function '$draw3d)) 18 | 19 | (defmfun $plot2d (&rest args) 20 | (declare (notinline $substring) 21 | (notinline $simplode) 22 | (notinline $sha256sum) 23 | (notinline $sconcat)) 24 | (if (member '$png_file args) 25 | (apply *builtin-plot2d* args) 26 | (let* 27 | ((nnn ($substring ($sha256sum ($sconcat "plot2d" ($simplode args))) 1 7)) 28 | (png-file ($sconcat $plot_output_folder "/plot2d-" nnn ".png")) 29 | ;; (png-file ($sconcat (os-getcurrentdirectory) "plot2d-" nnn ".png")) 30 | (args-new (append args (list `((mlist) $png_file ,png-file))))) 31 | (apply *builtin-plot2d* args-new)))) 32 | 33 | (defmfun $plot3d (&rest args) 34 | (declare (notinline $substring) 35 | (notinline $simplode) 36 | (notinline $sha256sum) 37 | (notinline $sconcat)) 38 | (if (member '$png_file args) 39 | (apply *builtin-plot3d* args) 40 | (let* 41 | ((nnn ($substring ($sha256sum ($sconcat "plot3d" ($simplode args))) 1 7)) 42 | (png-file ($sconcat $plot_output_folder "/plot3d-" nnn ".png")) 43 | ;; (png-file ($sconcat (os-getcurrentdirectory) "plot3d-" nnn ".png")) 44 | (args-new (append args (list `((mlist) $png_file ,png-file))))) 45 | (apply *builtin-plot3d* args-new)))) 46 | 47 | ;; a utility function 48 | (defun flatten (lst) 49 | (labels ((rflatten (lst1 acc) 50 | (dolist (el lst1) 51 | (if (listp el) 52 | (setf acc (rflatten el acc)) 53 | (push el acc))) 54 | acc)) 55 | (reverse (rflatten lst nil)))) 56 | 57 | (defmfun $draw (&rest args) 58 | (declare (notinline $substring) 59 | (notinline $simplode) 60 | (notinline $sha256sum) 61 | (notinline $sconcat)) 62 | (if (member '$file_name (flatten args)) 63 | (apply *builtin-draw* args) 64 | (let* 65 | ((nnn ($substring ($sha256sum ($sconcat "draw" ($simplode args))) 1 7)) 66 | (file_name ($sconcat $plot_output_folder "/draw-" nnn)) 67 | ;; (file_name ($sconcat (os-getcurrentdirectory) "draw-" nnn)) 68 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 69 | (args-new (append args-new (list `((mequal) $terminal $png))))) 70 | (apply *builtin-draw* args-new) 71 | ;; (format t "inside draw~%") 72 | ;; (format t "~a~%" (flatten args-new)) 73 | `((mlist) ,file_name ,($sconcat file_name ".png"))))) 74 | 75 | (defmfun $draw2d (&rest args) 76 | (declare (notinline $substring) 77 | (notinline $simplode) 78 | (notinline $sha256sum) 79 | (notinline $sconcat)) 80 | (if (member '$file_name (flatten args)) 81 | (apply *builtin-draw2d* args) 82 | (let* 83 | ((nnn ($substring ($sha256sum ($sconcat "draw2d" ($simplode args))) 1 7)) 84 | (file_name ($sconcat $plot_output_folder "/draw2d-" nnn)) 85 | ;; (file_name ($sconcat (os-getcurrentdirectory) "draw2d-" nnn)) 86 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 87 | (args-new (append args-new (list `((mequal) $terminal $png))))) 88 | (apply *builtin-draw2d* args-new) 89 | ;; (format t "inside draw2d") 90 | ;; (format t "~a~%" (flatten args-new)) 91 | `((mlist) ,file_name ,($sconcat file_name ".png"))))) 92 | 93 | (defmfun $draw3d (&rest args) 94 | (declare (notinline $substring) 95 | (notinline $simplode) 96 | (notinline $sha256sum) 97 | (notinline $sconcat)) 98 | (if (member '$file_name (flatten args)) 99 | (apply *builtin-draw3d* args) 100 | (let* 101 | ((nnn ($substring ($sha256sum ($sconcat "draw3d" ($simplode args))) 1 7)) 102 | (file_name ($sconcat $plot_output_folder "/draw3d-" nnn)) 103 | ;; (file_name ($sconcat (os-getcurrentdirectory) "draw3d-" nnn)) 104 | (args-new (append args (list `((mequal) $file_name ,file_name)))) 105 | (args-new (append args-new (list `((mequal) $terminal $png))))) 106 | (apply *builtin-draw3d* args-new) 107 | `((mlist) ,file_name ,($sconcat file_name ".png"))))) 108 | -------------------------------------------------------------------------------- /inst/extdata/plot2d-ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcst/rim/6a54a3d761e621797673aed07099c1d13d708d87/inst/extdata/plot2d-ref.png -------------------------------------------------------------------------------- /inst/extdata/plot3d-ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcst/rim/6a54a3d761e621797673aed07099c1d13d708d87/inst/extdata/plot3d-ref.png -------------------------------------------------------------------------------- /inst/extdata/rds.lisp: -------------------------------------------------------------------------------- 1 | ; a hashmap to list SEXPTYPE numbers 2 | (defparameter *SXPS* 3 | (let ((ht (make-hash-table))) 4 | (setf (gethash 'NILSXP ht) 0) 5 | (setf (gethash 'SYMSXP ht) 1) 6 | (setf (gethash 'LISTSXP ht) 2) 7 | (setf (gethash 'CLOSXP ht) 3) 8 | (setf (gethash 'ENVSXP ht) 4) 9 | (setf (gethash 'PROMSXP ht) 5) 10 | (setf (gethash 'LANGSXP ht) 6) 11 | (setf (gethash 'SPECIALSXP ht) 7) 12 | (setf (gethash 'BUILTINSXP ht) 8) 13 | (setf (gethash 'CHARSXP ht) 9) 14 | (setf (gethash 'LGLSXP ht) 10) 15 | (setf (gethash 'INTSXP ht) 13) 16 | (setf (gethash 'REALSXP ht) 14) 17 | (setf (gethash 'CPLXSXP ht) 15) 18 | (setf (gethash 'STRSXP ht) 16) 19 | (setf (gethash 'DOTSXP ht) 17) 20 | (setf (gethash 'ANYSXP ht) 18) 21 | (setf (gethash 'VECSXP ht) 19) 22 | (setf (gethash 'EXPRSXP ht) 20) 23 | (setf (gethash 'BCODESXP ht) 21) 24 | ht)) 25 | 26 | ;; +++++++++++++ RDS encoding ++++++++++++ 27 | (defun rds-character (x) 28 | ;; serializes a single character vector element 29 | (format nil "~A~%~A~%~A" 30 | (logior (gethash 'CHARSXP *SXPS*) (expt 2 18)) 31 | (length x) 32 | x)) 33 | 34 | (defun rds-string (x) 35 | ;; serializes a single string or a LIST of strings i.e., a character vector (in R) 36 | (if (listp x) 37 | (format nil "~A~%~A~%~{~A~^~%~}" 38 | (gethash 'STRSXP *SXPS*) 39 | (list-length x) 40 | (mapcar #'rds_character x)) 41 | (format nil "~A~%~A~%~A" 42 | (gethash 'STRSXP *SXPS*) 43 | 1 44 | (rds_character x)))) 45 | 46 | ;; (rds_string '("hello" "world" "you")) 47 | 48 | (defun rds-symbol (x) 49 | (format nil "~A~%~A" 50 | (gethash 'SYMSXP *SXPS*) 51 | (rds-character x))) 52 | 53 | (defun rds-number (x numtype) 54 | ;; serializes a LIST of numbers one of integer, real or complex 55 | ;; defined by numtype 56 | (format nil "~A~%~A~%~{~A~^~%~}" 57 | numtype 58 | (length x) 59 | x)) 60 | 61 | (defun rds-pairlist (x)) 62 | 63 | (defun rds-list (x)) 64 | 65 | (defun rds_language (sym) 66 | ; function that serializes a language object 67 | (format nil "~A~%~A~%262153~%~A~%~A" 68 | (gethash 'LANGSXP *SXPS*) 69 | (gethash 'SYMSXP *SXPS*) 70 | (length sym) 71 | sym)) 72 | 73 | ;; working examples of forms 74 | ;; ((MPLUS) ((MQUOTIENT) ((%LOG SIMP) ((MPLUS SIMP) 1 $X)) 2) ((MMINUS) ((MQUOTIENT) ((%LOG SIMP) ((MPLUS SIMP) -1 $X)) 2))) 75 | -------------------------------------------------------------------------------- /inst/extdata/result.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "knitr engine test page" 3 | output: html_document 4 | --- 5 | 6 | options(width = 80)maxima.options(engine.format = "latex", 7 | engine.label = TRUE, 8 | inline.format = "inline", 9 | inline.label = FALSE) 10 | 11 | (%i1) L: sqrt(1 - 1/R^2);$$\mathtt{(\textit{\%o}_{1})}\quad \sqrt{1-\frac{1}{R^2}}$$(%i2) assume(R > 0);$$\mathtt{(\textit{\%o}_{2})}\quad \left[ R>0 \right] $$(%i3) 'integrate(x, x, 0, L) = integrate(x, x, 0, L);$$\mathtt{(\textit{\%o}_{3})}\quad \int_{0}^{\sqrt{1-\frac{1}{R^2}}}{x\;dx}=\frac{R^2-1}{2\,R^2}$$ 12 | 13 | (%i4) 'L = L;$$\mathtt{(\textit{\%o}_{4})}\quad L=\sqrt{1-\frac{1}{R^2}}$$(%i5) 'integrate(x, x, 0, 'L) = integrate(x, x, 0, L);$$\mathtt{(\textit{\%o}_{5})}\quad \int_{0}^{L}{x\;dx}=\frac{R^2-1}{2\,R^2}$$ 14 | 15 | This is an inline test: $L=\sqrt{1-\frac{1}{R^2}}$ 16 | . 17 | 18 | 19 | (%i7) sqrt(3/4);$$\mathtt{(\textit{\%o}_{7})}\quad \frac{\sqrt{3}}{2}$$ 20 | 21 | (%i8) f(x) := e^(x^2)$(%i9) diff(f(x), x);$$\mathtt{(\textit{\%o}_{9})}\quad 2\,e^{x^2}\,\log e\,x$$ 22 | 23 | (%i10) %;$$\mathtt{(\textit{\%o}_{10})}\quad 2\,e^{x^2}\,\log e\,x$$ 24 | 25 | (%i11) log(%o1);$$\mathtt{(\textit{\%o}_{11})}\quad \frac{\log \left(1-\frac{1}{R^2}\right)}{2}$$ 26 | 27 | moo## $o11 28 | ## ((1L/2L) * log((1L + (-1L * (R^-2L))))) 29 | eval(moo[[1]], list(R = 12))## [1] -0.003484335 30 | 31 | 32 | (%i12) /* aa is a variable of interest */ aa : 1234;$$\mathtt{(\textit{\%o}_{12})}\quad 1234$$(%i13) bb : aa^2; /* Value of bb depends on aa */ $$\mathtt{(\textit{\%o}_{13})}\quad 1522756$$(%i14) /* User-defined infix operator */ infix ("Q");$$\mathtt{(\textit{\%o}_{14})}\quad \mbox{ Q }$$(%i15) /* Parses same as a b c, not abc */ a/* foo */Q/* bar */c;$$\mathtt{(\textit{\%o}_{15})}\quad \textit{aQc}$$(%i16) /* Comments /* can be nested /* to any depth */ */ */ 1 + xyz;$$\mathtt{(\textit{\%o}_{16})}\quad \textit{xyz}+1$$ 33 | 34 | (%i17) load(fourier_elim)$(%i18) fourier_elim([x^2-1>0], [x]);$$\mathtt{(\textit{\%o}_{18})}\quad \left[ 1 0)$(%i35) area(normal(x));$$\mathtt{(\textit{\%o}_{35})}\quad 1$$(%i36) mean(normal(x));$$\mathtt{(\textit{\%o}_{36})}\quad \mu$$(%i37) variance(normal(x));$$\mathtt{(\textit{\%o}_{37})}\quad \frac{2^{\frac{3}{2}}\,\sqrt{\pi}\,\sigma^3+2^{\frac{3}{2}}\,\sqrt{\pi}\,\mu^2\,\sigma}{2^{\frac{3}{2}}\,\sqrt{\pi}\,\sigma}-\mu^2$$(%i38) mgf(normal(x));$$\mathtt{(\textit{\%o}_{38})}\quad e^{\frac{\sigma^2\,t^2+2\,\mu\,t}{2}}$$ 81 | 82 | # Matrices 83 | 84 | (%i39) m: matrix([0, 1, a], [1, 0, 1], [1, 1, 0]);$$\mathtt{(\textit{\%o}_{39})}\quad \begin{pmatrix}0 & 1 & a \\ 1 & 0 & 1 \\ 1 & 1 & 0 \\ \end{pmatrix}$$(%i40) transpose(m);$$\mathtt{(\textit{\%o}_{40})}\quad \begin{pmatrix}0 & 1 & 1 \\ 1 & 0 & 1 \\ a & 1 & 0 \\ \end{pmatrix}$$(%i41) determinant(m);$$\mathtt{(\textit{\%o}_{41})}\quad a+1$$(%i42) f: invert(m), detout;$$\mathtt{(\textit{\%o}_{42})}\quad \frac{\begin{pmatrix}-1 & a & 1 \\ 1 & -a & a \\ 1 & 1 & -1 \\ \end{pmatrix}}{a+1}$$(%i43) m . f;$$\mathtt{(\textit{\%o}_{43})}\quad \begin{pmatrix}0 & 1 & a \\ 1 & 0 & 1 \\ 1 & 1 & 0 \\ \end{pmatrix}\cdot \left(\frac{\begin{pmatrix}-1 & a & 1 \\ 1 & -a & a \\ 1 & 1 & -1 \\ \end{pmatrix}}{a+1}\right)$$(%i44) expand(%);$$\mathtt{(\textit{\%o}_{44})}\quad \begin{pmatrix}\frac{a}{a+1}+\frac{1}{a+1} & 0 & 0 \\ 0 & \frac{a}{a+1}+\frac{1}{a+1} & 0 \\ 0 & 0 & \frac{a}{a+1}+\frac{1}{a+1} \\ \end{pmatrix}$$(%i45) factor(%);$$\mathtt{(\textit{\%o}_{45})}\quad \begin{pmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}$$ 85 | 86 | # If-then-else 87 | 88 | (%i46) x: 1234;$$\mathtt{(\textit{\%o}_{46})}\quad 1234$$(%i47) y: 2345;$$\mathtt{(\textit{\%o}_{47})}\quad 2345$$ 89 | 90 | (%i48) if x > y then x else y;$$\mathtt{(\textit{\%o}_{48})}\quad 2345$$ 91 | -------------------------------------------------------------------------------- /inst/extdata/test-forms.lisp: -------------------------------------------------------------------------------- 1 | ; test forms 2 | (setf func-form '(($F SIMP) $X)) ; f(x) 3 | (setf expt-form '((MEXPT) $E ((MEXPT SIMP) $X 2))) 4 | (setf simple-form '((MPLUS) ((MMINUS) $D) $C $B $A)) 5 | (setf factorial-form '((MFACTORIAL SIMP) $A)) 6 | (setf cplx-form '((MPLUS) ((MTIMES) 3 $%I) 4)) 7 | (setf funcdef-form '((MDEFINE SIMP) (($F) $X) ((%SIN) $X))) 8 | (setf val-assign-form '((MSETQ SIMP) $A 4)) 9 | (setf list-form '((MLIST SIMP) 1 2 7 ((MPLUS SIMP) $X $Y))) 10 | (setf list-form2 '((MLIST SIMP) ((MSETQ SIMP) $A 100) ((MSETQ SIMP) $B 200))) 11 | (setf lambda-form '((LAMBDA SIMP) ((MLIST) $I) ((MPLUS) $I 1))) 12 | (setf adv-form '((MPLUS) ((MQUOTIENT) ((%LOG SIMP) ((MPLUS SIMP) 1 $X)) 2) ((MMINUS) ((MQUOTIENT) ((%LOG SIMP) ((MPLUS SIMP) -1 $X)) 2)))) 13 | (setf matrix-form '(($MATRIX SIMP) ((MLIST SIMP) 1 2) ((MLIST SIMP) 2 3))) 14 | (setf matrix-form-2 '(($MATRIX SIMP) ((MLIST SIMP) 1 0 0) ((MLIST SIMP) 0 1 0) ((MLIST SIMP) 0 0 1))) 15 | (setf array-function '((MDEFINE SIMP) (($C ARRAY) $X $Y) ((MQUOTIENT) $Y $X))) 16 | (setf matrix-compl '(($MATRIX SIMP) ((1 2) (2 3)))) 17 | ; lists directly under a logical operator should not be turned into R-lists 18 | (setf boolexpror '((MOR SIMP) ((MLIST SIMP) ((MLESSP SIMP) 1 $X))((MLIST SIMP) ((MLESSP SIMP) $X -1)))) 19 | 20 | (maxima2r boolexpror) 21 | (maxima2r matrix-form) 22 | (maxima2r matrix-form-2) 23 | (maxima2r lambda-form) 24 | (maxima2r cplx-form) 25 | (maxima2r expt-form) 26 | (maxima2r factorial-form) 27 | (maxima2r adv-form) 28 | (maxima2r list-form) 29 | (maxima2r list-form2) 30 | (maxima2r val-assign-form) 31 | (maxima2r funcdef-form) 32 | (maxima2r simple-form) 33 | (maxima2r array-function) 34 | 35 | -------------------------------------------------------------------------------- /inst/extdata/test.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "knitr engine test page" 3 | output: html_document 4 | --- 5 | 6 | ```{r options-init} 7 | options(width = 80) 8 | maxima.options(engine.format = "latex", 9 | engine.label = TRUE, 10 | inline.format = "inline", 11 | inline.label = FALSE) 12 | ``` 13 | 14 | ```{maxima first-example} 15 | L: sqrt(1 - 1/R^2); 16 | assume(R > 0); 17 | 'integrate(x, x, 0, L) = integrate(x, x, 0, L); 18 | ``` 19 | 20 | ```{maxima second-example} 21 | 'L = L; 22 | 'integrate(x, x, 0, 'L) = integrate(x, x, 0, L); 23 | ``` 24 | 25 | This is an inline test: `r maxima.inline("'L = L;")`. 26 | 27 | 28 | ```{maxima square-root} 29 | sqrt(3/4); 30 | ``` 31 | 32 | ```{maxima output-var, output.var = "moo"} 33 | f(x) := e^(x^2)$ 34 | diff(f(x), x); 35 | ``` 36 | 37 | ```{maxima repeat} 38 | %; 39 | ``` 40 | 41 | ```{maxima output-reference-label, output.var = "moo"} 42 | log(%o1); 43 | ``` 44 | 45 | ```{r using-output-var} 46 | moo 47 | eval(moo[[1]], list(R = 12)) 48 | ``` 49 | 50 | ```{maxima comments} 51 | /* aa is a variable of interest */ aa : 1234; 52 | bb : aa^2; /* Value of bb depends on aa */ 53 | /* User-defined infix operator */ infix ("Q"); 54 | /* Parses same as a b c, not abc */ a/* foo */Q/* bar */c; 55 | /* Comments /* can be nested /* to any depth */ */ */ 1 + xyz; 56 | ``` 57 | 58 | ```{maxima logical-list-parsing, output.var = "boollist"} 59 | load(fourier_elim)$ 60 | fourier_elim([x^2-1>0], [x]); 61 | ``` 62 | 63 | ```{r, eval = TRUE} 64 | # from previous chunk: output.var = "boollist" 65 | boollist 66 | eval(boollist[["o18"]], list(x = seq(-10, 10, 0.5))) 67 | ``` 68 | 69 | 70 | # Plots 71 | 72 | ```{maxima plot2d, fig.cap = "plot2d()", fig.align="center", fig.show='hide'} 73 | r: (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)$ 74 | plot2d([parametric, r*sin(t), r*cos(t), [t,-8*%pi,8*%pi]]); 75 | ``` 76 | 77 | ```{maxima plot3d, fig.cap = "plot3d()", fig.align="center", fig.show='hide'} 78 | plot3d(log (x^2*y^2), [x, -2, 2], [y, -2, 2],[grid, 29, 29], 79 | [palette, [gradient, red, orange, yellow, green]], 80 | color_bar, [xtics, 1], [ytics, 1], [ztics, 4], 81 | [color_bar_tics, 4]); 82 | ``` 83 | 84 | ```{maxima draw, fig.cap = "draw()", fig.align="center", fig.show='hide'} 85 | example1: 86 | gr3d (title = "Controlling color range", 87 | enhanced3d = true, 88 | color = green, 89 | cbrange = [-3,10], 90 | explicit(x^2+y^2, x,-2,2,y,-2,2)) $ 91 | 92 | example2: 93 | gr3d (title = "Playing with tics in colorbox", 94 | enhanced3d = true, 95 | color = green, 96 | cbtics = {["High",10],["Medium",05],["Low",0]}, 97 | cbrange = [0, 10], 98 | explicit(x^2+y^2, x,-2,2,y,-2,2))$ 99 | 100 | example3: 101 | gr3d (title = "Logarithmic scale to colors", 102 | enhanced3d = true, 103 | color = green, 104 | logcb = true, 105 | logz = true, 106 | palette = [-15,24,-9], 107 | explicit(exp(x^2-y^2), x,-2,2,y,-2,2))$ 108 | 109 | draw( 110 | dimensions = [500,1500], 111 | example1, example2, example3)$ 112 | ``` 113 | 114 | ```{maxima draw2d, fig.cap = "draw2d()", fig.align="center", fig.show='hide'} 115 | draw2d( 116 | dimensions = [1000, 1000], 117 | proportional_axes = xy, 118 | fill_color = sea_green, 119 | color = aquamarine, 120 | line_width = 6, 121 | ellipse(7,6,2,3,0,360))$ 122 | ``` 123 | 124 | ```{maxima draw3d, fig.cap = "draw3d()", fig.align="center", fig.show='hide'} 125 | draw3d( 126 | dimensions = [1000, 1000], 127 | surface_hide = true, 128 | axis_3d = false, 129 | proportional_axes = xyz, 130 | 131 | color = blue, 132 | cylindrical(z,z,-2,2,a,0,2*%pi), 133 | 134 | color = brown, 135 | cylindrical(3,z,-2,2,az,0,%pi), 136 | 137 | color = green, 138 | cylindrical(sqrt(25-z^2),z,-5,5,a,0,%pi))$ 139 | ``` 140 | 141 | 142 | ```{r image-count, warning=FALSE, results="markup"} 143 | pft <- list.files(pattern = "(?:plot|draw)(2d|3d)?-[[:print:]]{6}\\.png", full.names = TRUE) 144 | 145 | if(length(pft) == 5L) { 146 | paste0("OK") 147 | } else { 148 | paste0("Error: Unexpected number of Maxima plots: ", 149 | paste0(pft, collapse = ", ")) 150 | } 151 | 152 | if(length(pft)) { 153 | if(all(as.logical(file.size(pft)))) { 154 | paste0("OK") 155 | } 156 | else { 157 | errfiles <- pft[file.size(pft) == 0] 158 | paste0("Error: Maxima plot file(s) ", paste0(errfiles, collapse = ", "), 159 | "are empty.") 160 | } 161 | } 162 | ``` 163 | 164 | # Normal Distribution 165 | 166 | ```{maxima summary-functions} 167 | area(dist) := integrate(dist, x, minf, inf)$ 168 | mean(dist) := area(dist*x)$ 169 | EX2(dist) := area(dist*x^2)$ 170 | variance(dist) := EX2(dist) - mean(dist)^2$ 171 | mgf(dist) := area(dist*%e^(x*t))$ 172 | ``` 173 | 174 | ```{maxima normal} 175 | normal(x) := 176 | (2*%pi*sigma^2)^(-1/2) * 177 | exp(-(x-mu)^2/(2*sigma^2)); 178 | 179 | assume(sigma > 0)$ 180 | 181 | area(normal(x)); 182 | mean(normal(x)); 183 | variance(normal(x)); 184 | mgf(normal(x)); 185 | ``` 186 | 187 | # Matrices 188 | 189 | ```{maxima matrices} 190 | m: matrix([0, 1, a], [1, 0, 1], [1, 1, 0]); 191 | transpose(m); 192 | determinant(m); 193 | f: invert(m), detout; 194 | m . f; 195 | expand(%); 196 | factor(%); 197 | ``` 198 | 199 | # If-then-else 200 | 201 | ```{maxima x-y} 202 | x: 1234; 203 | y: 2345; 204 | ``` 205 | 206 | ```{maxima if-then-else} 207 | if x > y 208 | then x 209 | else y; 210 | ``` 211 | -------------------------------------------------------------------------------- /inst/extdata/test.lisp: -------------------------------------------------------------------------------- 1 | ;imported just for testing purposes 2 | (defun maybe-invert-string-case (string) 3 | (let ((all-upper t) 4 | (all-lower t) 5 | (length (length string))) 6 | (dotimes (i length) 7 | (let ((ch (char string i))) 8 | (when (both-case-p ch) 9 | (if (upper-case-p ch) 10 | (setq all-lower nil) 11 | (setq all-upper nil))))) 12 | (cond (all-upper 13 | (string-downcase string)) 14 | (all-lower 15 | (string-upcase string)) 16 | (t 17 | string)))) 18 | 19 | ; (load "maxima-to-ir.lisp") 20 | ; (load "ir-to-r.lisp") 21 | (load "parser.lisp") 22 | (load "test-forms.lisp") 23 | ; (load "rds.lisp") 24 | 25 | (declaim (optimize (debug 3))) 26 | 27 | (maxima-to-ir simple-form) 28 | (maxima-to-ir func-form) 29 | (maxima-to-ir expt-form) 30 | (maxima-to-ir cplx-form) 31 | (maxima-to-ir factorial-form) 32 | (maxima-to-ir list-form) 33 | (maxima-to-ir list-form2) 34 | (maxima-to-ir funcdef-form) 35 | (maxima-to-ir jfunc-form) 36 | (maxima-to-ir val-assign-form) 37 | (maxima-to-ir lambda-form) 38 | (maxima-to-ir adv-form) 39 | (maxima-to-ir matrix-form) 40 | (maxima-to-ir pi-form) 41 | (maxima-to-ir atan-form) 42 | (maxima-to-ir compare-form) 43 | (maxima-to-ir longcompare-form) 44 | (maxima-to-ir matmult-form) 45 | (maxima-to-ir determinant-form) 46 | (maxima-to-ir mattp-form) 47 | (maxima-to-ir create-array-form) 48 | (maxima-to-ir set-array-form) 49 | (maxima-to-ir index-array-form) 50 | 51 | (maxima2r simple-form) 52 | (maxima2r func-form) 53 | (maxima2r adv-form) 54 | (maxima2r expt-form) 55 | (maxima2r cplx-form) 56 | (maxima2r factorial-form) 57 | (maxima2r list-form) 58 | (maxima2r list-form2) 59 | (maxima2r funcdef-form) 60 | (maxima2r jfunc-form) 61 | (maxima2r val-assign-form) 62 | (maxima2r matrix-form) 63 | (maxima2r matrix-compl) 64 | (maxima2r lambda-form) 65 | (maxima2r pi-form) 66 | (maxima2r atan-form) 67 | (maxima2r compare-form) 68 | (maxima2r longcompare-form) 69 | (maxima2r matmult-form) 70 | (maxima2r determinant-form) 71 | (maxima2r mattp-form) 72 | (maxima2r create-array-form) 73 | (maxima2r set-array-form) 74 | (maxima2r index-array-form) 75 | 76 | ;;; TODO 77 | (maxima-to-ir string-form) 78 | 79 | 80 | (;;;) NOTES 81 | ;;; 82 | ;;; function maybe-invert-string-case turns the string 83 | ;;; FOO into foo 84 | ;;; foo into FOO 85 | ;;; Foo into Foo 86 | ;;; Lisp symbols are always upper case 87 | ;;; So, in a Maxima expression in LISP form 88 | ;;; all uppercase symbols are to be made lower case 89 | ;;; except for when the symbol is surrounded by vertical bars 90 | -------------------------------------------------------------------------------- /man/maxima.engine.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/engine.R 3 | \name{maxima.engine} 4 | \alias{maxima.engine} 5 | \alias{maxima.inline} 6 | \title{\code{knitr} maxima engine} 7 | \usage{ 8 | maxima.engine(options) 9 | 10 | maxima.inline(command) 11 | } 12 | \arguments{ 13 | \item{options}{named \code{list} of \code{knitr} options. Supported options are \code{echo}, \code{eval}, \code{include} and \code{output.var}. To change the output format of the Maxima engine set the option \code{maxima.options(engine.format)} to either \code{"linear"} (default), \code{"ascii"}, \code{"latex"} or \code{"mathml"}.} 14 | 15 | \item{command}{character string containing the Maxima command to be executed.} 16 | } 17 | \value{ 18 | This functions prints the resulting output from maxima together with it's cod 19 | 20 | character string containing the maxima result printed according options set by \code{maxima.options(inline.format = ..., inline.label = ...)}. 21 | } 22 | \description{ 23 | Functions to process Maxima code chunks by \code{knitr}. 24 | } 25 | \details{ 26 | Upon attachment, i.e. \code{library(rim)} function \code{maxima.engine} is registered as a \code{knitr} engine. Thus, \code{maxima.engine()} is called by \code{knit()} to evaluate Maxima code chunks. When called upon the first code chunk of a document it starts Maxima in a separate process in server mode. This means that a single Maxima session is used for all Maxima code chunks of an \code{RMarkdown} document. Inputs and outputs can thus be used across chunks (e.g. by using Maxima reference labels). \code{maxima.options(engine.format = ..., engine.label = ...)} configures the output format and whether or not output reference labels should be printed. 27 | 28 | The purpose of \code{maxima.inline} is to insert Maxima results as inline text, i.e. on the same line of the preceding text, if it is actually written on the same line of the \code{RMarkdown} file. It uses the same running Maxima process as \code{maxima.engine}. The output format for inline results can be configured separately from the settings of \code{maxima.engine}, i.e. \code{maxima.options(inline.format = ..., inline.label = ...)}. 29 | } 30 | \section{Functions}{ 31 | \itemize{ 32 | \item \code{maxima.inline()}: This function can be used to insert maxima outputs as inline. 33 | 34 | }} 35 | \examples{ 36 | if (maxima.isInstalled()) { 37 | maxima.inline("2+2;") 38 | maxima.stop(engine = TRUE) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /man/maxima.options.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/options.R 3 | \name{maxima.options} 4 | \alias{maxima.options} 5 | \title{maxima.options} 6 | \usage{ 7 | maxima.options( 8 | ..., 9 | RESET = FALSE, 10 | READ.ONLY = NULL, 11 | LOCAL = FALSE, 12 | ADD = FALSE 13 | ) 14 | } 15 | \arguments{ 16 | \item{...}{options to be accessed by using \code{name = value}. Options can be added by setting \code{ADD = TRUE}, but only those mentioned below will be used my rim.} 17 | 18 | \item{RESET}{logical of length 1, whether to reset all options to default values.} 19 | 20 | \item{READ.ONLY}{logical of length 1, can be used to output all options that are read-only.} 21 | 22 | \item{LOCAL}{logical of length 1, to output all options that are defined locally.} 23 | 24 | \item{ADD}{logical of length 1, whether to add the specified option in \code{...} (TRUE), default is FALSE.} 25 | } 26 | \description{ 27 | Function for globally setting and retrieving options. 28 | } 29 | \details{ 30 | \describe{ 31 | \item{\code{format}: }{character vector of length 1 setting the output format for \code{\link[=rim]{maxima.get()}} and \code{\link[=rim]{maxima.repl()}}. Can be one of \code{"linear",} \code{"ascii",} \code{"latex"} or \code{"mathml"}.} 32 | \item{\code{engine.format}: }{same as option \code{format}, but for outputs in \code{RMarkdown} documents.} 33 | \item{\code{inline.format}: }{character string setting the output format for \code{\link[=rim]{maxima.inline()}}, for knitting outputs inline into \code{RMarkdown} documents. Can be one of \code{"linear"}, \code{"latex"} or \code{"mathml"}, but \emph{not} \code{"ascii"}.} 34 | \item{\code{label}: }{logical of length 1, whether reference labels should be printed for returned S3 objects from \code{\link[=rim]{maxima.get}()} and \code{\link[=rim]{maxima.repl()}} (TRUE, default), or not (FALSE). This also applies to printing of input commands using \code{\link{iprint}()}.} 35 | \item{\code{engine.label}: }{same as \code{label}, but for outputs in \code{RMarkdown} documents.} 36 | \item{\code{inline.label}: }{same as \code{label}, but for inline outputs in \code{RMarkdown} documents.} 37 | } 38 | } 39 | \examples{ 40 | maxima.options(format = "latex") 41 | maxima.options(label = FALSE) 42 | maxima.options(label = TRUE, format = "ascii") 43 | # reset to default 44 | maxima.options(label = TRUE, format = "linear") 45 | } 46 | -------------------------------------------------------------------------------- /man/maxima.repl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repl.R 3 | \name{maxima.repl} 4 | \alias{maxima.repl} 5 | \title{Run a Maxima REPL} 6 | \usage{ 7 | maxima.repl(history_filename = NA_character_) 8 | } 9 | \arguments{ 10 | \item{history_filename}{A (optional) filename to which all Maxima commands 11 | during the repl session are saved. If not provided (default), command history 12 | lookup will still be available (if supported by OS) in the same way as in the 13 | R session.} 14 | } 15 | \description{ 16 | This function provides a Maxima REPL in the \R session, which can be used 17 | to interactively run Maxima code. All code executed within the REPL is 18 | run within the interactive Maxima session that is started when rim is 19 | attached and any generated Maxima objects will persist in the Maxima 20 | session after the REPL is detached. 21 | } 22 | \details{ 23 | When working with R and Maxima scripts interactively, one can activate 24 | the Python REPL with `maxima.repl()`, run Maxima code, and later run `exit;` 25 | to return to the \R console. 26 | 27 | Note that, inside the REPL, multiple commands are allowed. 28 | 29 | The output format displayed inside the REPL is controlled by `maxima.options(repl.format = ...)`. 30 | } 31 | -------------------------------------------------------------------------------- /man/rim-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \docType{package} 4 | \name{rim-package} 5 | \alias{rim} 6 | \alias{rim-package} 7 | \alias{maxima.start} 8 | \alias{maxima.stop} 9 | \alias{maxima.get} 10 | \alias{maxima.load} 11 | \alias{maxima.apropos} 12 | \alias{maxima.version} 13 | \alias{maxima.isInstalled} 14 | \alias{iprint} 15 | \alias{print.maxima} 16 | \alias{maxima.eval} 17 | \title{rim} 18 | \usage{ 19 | maxima.start(restart = FALSE) 20 | 21 | maxima.stop(engine = FALSE) 22 | 23 | maxima.get(command) 24 | 25 | maxima.load(module) 26 | 27 | maxima.apropos(keystring) 28 | 29 | maxima.version() 30 | 31 | maxima.isInstalled() 32 | 33 | iprint(x) 34 | 35 | \method{print}{maxima}(x, ...) 36 | 37 | maxima.eval(x, code = FALSE, envir = globalenv()) 38 | } 39 | \arguments{ 40 | \item{restart}{if FALSE (default), then Maxima is started provided it is not running already. If TRUE starts or restarts Maxima.} 41 | 42 | \item{engine}{if FALSE (default), quits the (running) maxima instance and closes the connection, otherwise quits and closes the (running) maxima instance used for the knitr engine.} 43 | 44 | \item{command}{character string containing the Maxima command.} 45 | 46 | \item{module}{character vector naming the Maxima module (typically a *.mac or *.lisp file) to be loaded.} 47 | 48 | \item{keystring}{character vector containing a search term.} 49 | 50 | \item{x}{Either a character vector of length 1L or an S3 object of class "maxima"} 51 | 52 | \item{...}{other arguments (ignored).} 53 | 54 | \item{code}{A logical vector of length 1L, whether to attach the original expression (TRUE) or not (FALSE, default)} 55 | 56 | \item{envir}{A environment object. \code{globalenv()} (default), is passed to eval().} 57 | } 58 | \value{ 59 | invisibly returns NULL. 60 | 61 | Character vector of length 1 of the input command. Depending on whether option "label" is set to TRUE, the corresponding input reference label is printed preceding the input command. 62 | 63 | The evaluated R-object 64 | } 65 | \description{ 66 | Provides an interface to Maxima, a computer algebra system. 67 | } 68 | \details{ 69 | Note: You need to install the Maxima software separately in order to make use of this package. 70 | 71 | Maxima is set up automatically on attachment via \code{library(rim)} and automatically started when a command is send (if it isn't running already) using \code{\link{maxima.get}()}. If environment variable RIM_MAXIMA_PATH is not set, rim will search for the Maxima executable, or use the former otherwise. Using \code{\link{maxima.start}()} and \code{\link{maxima.stop}()}, one can stop and (re-)start the current Maxima session if needed, e.g. to clear Maxima command and output history. 72 | 73 | To send a single command to Maxima and receive the corresponding output use \code{\link{maxima.get}()}. This function returns a S3 object of class "maxima". The output is printed by printing the object and will be printed in a format currently set by \code{\link{maxima.options}(format)}. The output format can be changed by setting it, e.g. \code{\link{maxima.options}(format = "ascii")}. Output labels are printed according to option \code{\link{maxima.options}(label)}. 74 | } 75 | \section{Functions}{ 76 | \itemize{ 77 | \item \code{maxima.start()}: (re-)starts Maxima. 78 | 79 | \item \code{maxima.stop()}: Quits Maxima. 80 | 81 | \item \code{maxima.get()}: Executes a single Maxima command provided by \code{command}. If no command ending character (\code{;} or \code{$} is provided, \code{;} is appended. 82 | 83 | \item \code{maxima.load()}: A wrapper to load a Maxima module named by \code{module} 84 | 85 | \item \code{maxima.apropos()}: A wrapper to the Maxima helper function \code{apropos} to lookup existing Maxima functions that match \code{keystring}. 86 | 87 | \item \code{maxima.version()}: Returns the version number of Maxima that is used 88 | 89 | \item \code{maxima.isInstalled()}: Returns TRUE when an installation of Maxima has been detected, otherwise FALSE 90 | 91 | \item \code{iprint()}: Prints the input command of an maxima S3-object returned by \code{\link{maxima.get}()} 92 | 93 | \item \code{print(maxima)}: Prints the maxima output part of an S3 object returned by \code{\link{maxima.get}()} 94 | 95 | \item \code{maxima.eval()}: Evaluates the parsed and quoted R-expression which is part of an S3 object returned by \code{\link{maxima.get}()} 96 | 97 | }} 98 | \examples{ 99 | if(maxima.isInstalled()) maxima.start(restart = TRUE) 100 | if(maxima.isInstalled()) { 101 | maxima.start(restart = TRUE) 102 | maxima.stop() 103 | } 104 | if(maxima.isInstalled()) maxima.get("2+2;") 105 | if(maxima.isInstalled()) maxima.load("ratpow") 106 | if(maxima.isInstalled()) maxima.apropos("integrate") 107 | maxima.version() 108 | maxima.isInstalled() 109 | if(maxima.isInstalled()) { 110 | a <- maxima.get("2+2;") 111 | iprint(a) 112 | } 113 | if(maxima.isInstalled()) { 114 | a <- maxima.get("2+2;") 115 | print(a) 116 | } 117 | if(maxima.isInstalled()) { 118 | a <- maxima.get("2+2;") 119 | maxima.eval(a) 120 | # same 121 | maxima.eval("2+2;") 122 | # evaluate with data.frame 123 | df <- data.frame(x = seq(0, 1, by = 0.1)) 124 | maxima.eval("integrate(1 / (1 + x^4), x);", code = TRUE, envir = df) 125 | maxima.stop() 126 | } 127 | } 128 | \seealso{ 129 | Useful links: 130 | \itemize{ 131 | \item \url{https://rcst.github.io/rim/} 132 | \item Report bugs at \url{https://github.com/rcst/rim/issues} 133 | } 134 | 135 | 136 | \code{\link{maxima.engine}}, \code{\link{maxima.options}} 137 | } 138 | \author{ 139 | \strong{Maintainer}: Eric Stemmler \email{stemmler.eric@gmail.com} 140 | 141 | Authors: 142 | \itemize{ 143 | \item Kseniia Shumelchyk \email{shumelchyk@gmail.com} 144 | \item Hans W. Borchers \email{hwborchers@googlemail.com} 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /man/rim_global.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/engine.utils.R 3 | \name{rim_global} 4 | \alias{rim_global} 5 | \title{rim_global} 6 | \usage{ 7 | rim_global() 8 | } 9 | \value{ 10 | An environment, either the result of knitr::knit_global or maxima.env 11 | } 12 | \description{ 13 | Returns knitr::knit_global() unless this returs the GlobalEnv() in which case it returns maxima.env, rim's internal environment 14 | } 15 | -------------------------------------------------------------------------------- /rim-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 26 | 30 | 34 | 35 | 38 | 42 | 43 | 46 | 50 | 51 | 54 | 58 | 59 | 60 | 78 | 80 | 81 | 83 | image/svg+xml 84 | 86 | 87 | 88 | 89 | 90 | 94 | 110 | 122 | RIM 136 | RIM 152 | 157 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /run-in-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | maxima --userdir=/packages/rim/inst/extdata --init=display -p inst/extdata/plot-knitr-png.lisp 3 | 4 | -------------------------------------------------------------------------------- /src/RcppExports.cpp: -------------------------------------------------------------------------------- 1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #include 5 | 6 | using namespace Rcpp; 7 | 8 | #ifdef RCPP_USE_GLOBAL_ROSTREAM 9 | Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); 10 | Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); 11 | #endif 12 | 13 | // trim 14 | std::string trim(std::string s); 15 | RcppExport SEXP _rim_trim(SEXP sSEXP) { 16 | BEGIN_RCPP 17 | Rcpp::RObject rcpp_result_gen; 18 | Rcpp::RNGScope rcpp_rngScope_gen; 19 | Rcpp::traits::input_parameter< std::string >::type s(sSEXP); 20 | rcpp_result_gen = Rcpp::wrap(trim(s)); 21 | return rcpp_result_gen; 22 | END_RCPP 23 | } 24 | // checkCommand 25 | std::string checkCommand(std::string command); 26 | RcppExport SEXP _rim_checkCommand(SEXP commandSEXP) { 27 | BEGIN_RCPP 28 | Rcpp::RObject rcpp_result_gen; 29 | Rcpp::RNGScope rcpp_rngScope_gen; 30 | Rcpp::traits::input_parameter< std::string >::type command(commandSEXP); 31 | rcpp_result_gen = Rcpp::wrap(checkCommand(command)); 32 | return rcpp_result_gen; 33 | END_RCPP 34 | } 35 | // dissect_chunk 36 | Rcpp::List dissect_chunk(std::vector code); 37 | RcppExport SEXP _rim_dissect_chunk(SEXP codeSEXP) { 38 | BEGIN_RCPP 39 | Rcpp::RObject rcpp_result_gen; 40 | Rcpp::RNGScope rcpp_rngScope_gen; 41 | Rcpp::traits::input_parameter< std::vector >::type code(codeSEXP); 42 | rcpp_result_gen = Rcpp::wrap(dissect_chunk(code)); 43 | return rcpp_result_gen; 44 | END_RCPP 45 | } 46 | // dissect_repl_input 47 | Rcpp::List dissect_repl_input(std::string input); 48 | RcppExport SEXP _rim_dissect_repl_input(SEXP inputSEXP) { 49 | BEGIN_RCPP 50 | Rcpp::RObject rcpp_result_gen; 51 | Rcpp::RNGScope rcpp_rngScope_gen; 52 | Rcpp::traits::input_parameter< std::string >::type input(inputSEXP); 53 | rcpp_result_gen = Rcpp::wrap(dissect_repl_input(input)); 54 | return rcpp_result_gen; 55 | END_RCPP 56 | } 57 | 58 | static const R_CallMethodDef CallEntries[] = { 59 | {"_rim_trim", (DL_FUNC) &_rim_trim, 1}, 60 | {"_rim_checkCommand", (DL_FUNC) &_rim_checkCommand, 1}, 61 | {"_rim_dissect_chunk", (DL_FUNC) &_rim_dissect_chunk, 1}, 62 | {"_rim_dissect_repl_input", (DL_FUNC) &_rim_dissect_repl_input, 1}, 63 | {NULL, NULL, 0} 64 | }; 65 | 66 | RcppExport void R_init_rim(DllInfo *dll) { 67 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 68 | R_useDynamicSymbols(dll, FALSE); 69 | } 70 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace Rcpp; 6 | 7 | std::string ltrim(std::string s) { 8 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { 9 | return !std::isspace(ch); 10 | })); 11 | 12 | return s; 13 | } 14 | 15 | std::string rtrim(std::string s) { 16 | s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { 17 | return !std::isspace(ch); 18 | }).base(), s.end()); 19 | 20 | return s; 21 | } 22 | 23 | // [[Rcpp::export]] 24 | std::string trim(std::string s) { 25 | return rtrim(ltrim(s)); 26 | } 27 | 28 | const int STATE_END = -1; 29 | 30 | enum Input { 31 | IN_TERMINATOR, 32 | IN_BACKSLASH, 33 | IN_QUOTEMARK, 34 | IN_SLASH, 35 | IN_ASTERIX, 36 | IN_COMMENT_ASTERIX, 37 | IN_COMMENT_START, 38 | IN_COMMENT, 39 | IN_COMMENT_END, 40 | IN_OTHER 41 | }; 42 | 43 | int checkInput(char c, int state, int comment_count) { 44 | 45 | Input in = IN_OTHER; 46 | if (c == ';' || c == '$') { 47 | in = IN_TERMINATOR; 48 | } else if (c == '\\') { 49 | in = IN_BACKSLASH; 50 | } else if (c == '"') { 51 | in = IN_QUOTEMARK; 52 | } else if (c == '*') { 53 | in = IN_ASTERIX; 54 | } else if (c == '/') { 55 | in = IN_SLASH; 56 | } 57 | 58 | switch (state) { 59 | case IN_TERMINATOR: // IN_TERMINATOR 60 | switch (in) { 61 | case IN_BACKSLASH: 62 | return IN_BACKSLASH; 63 | case IN_TERMINATOR: 64 | return STATE_END; 65 | case IN_QUOTEMARK: 66 | return IN_QUOTEMARK; 67 | case IN_SLASH: 68 | return IN_SLASH; 69 | default: 70 | return IN_TERMINATOR; 71 | } 72 | 73 | case IN_BACKSLASH: 74 | return IN_TERMINATOR; 75 | case IN_QUOTEMARK: 76 | switch (in) { 77 | case IN_BACKSLASH: 78 | return IN_OTHER; 79 | case IN_QUOTEMARK: 80 | return IN_TERMINATOR; 81 | default: 82 | return IN_QUOTEMARK; 83 | } 84 | 85 | case IN_SLASH: 86 | switch (in) { 87 | case IN_ASTERIX: 88 | return IN_COMMENT_START; 89 | case IN_TERMINATOR: 90 | return STATE_END; 91 | default: 92 | return IN_TERMINATOR; 93 | } 94 | 95 | case IN_COMMENT: 96 | switch (in) { 97 | case IN_SLASH: 98 | return IN_SLASH; 99 | case IN_ASTERIX: 100 | return IN_COMMENT_ASTERIX; 101 | default: 102 | return IN_COMMENT; 103 | } 104 | 105 | case IN_COMMENT_START: 106 | switch (in) { 107 | case IN_ASTERIX: 108 | return IN_COMMENT_ASTERIX; 109 | default: 110 | return IN_COMMENT; 111 | } 112 | 113 | case IN_COMMENT_ASTERIX: 114 | switch (in) { 115 | case IN_SLASH: 116 | return IN_COMMENT_END; 117 | default: 118 | return IN_COMMENT; 119 | } 120 | 121 | case IN_COMMENT_END: 122 | if (comment_count == 0) { 123 | switch (in) { 124 | case IN_TERMINATOR: 125 | return STATE_END; 126 | case IN_BACKSLASH: 127 | return IN_BACKSLASH; 128 | case IN_QUOTEMARK: 129 | return IN_QUOTEMARK; 130 | case IN_SLASH: 131 | return IN_SLASH; 132 | default: 133 | return IN_TERMINATOR; 134 | } 135 | } else { 136 | switch (in) { 137 | case IN_SLASH: 138 | return IN_SLASH; 139 | case IN_ASTERIX: 140 | return IN_COMMENT_ASTERIX; 141 | default: 142 | return IN_COMMENT; 143 | } 144 | } 145 | 146 | case IN_OTHER: 147 | return IN_QUOTEMARK; 148 | 149 | case STATE_END: 150 | switch (in) { 151 | case IN_BACKSLASH: 152 | return IN_BACKSLASH; 153 | case IN_TERMINATOR: 154 | return STATE_END; 155 | case IN_QUOTEMARK: 156 | return IN_QUOTEMARK; 157 | case IN_SLASH: 158 | return IN_SLASH; 159 | default: 160 | return IN_TERMINATOR; 161 | } 162 | } 163 | return state; 164 | } 165 | 166 | // [[Rcpp::export]] 167 | std::string checkCommand(std::string command) { 168 | trim(command); 169 | int state = 0; 170 | int comment_count = 0; 171 | bool terminated = false; 172 | std::vector csp, cep; 173 | 174 | for (size_t i = 0; i < command.size(); ++i) { 175 | state = checkInput(command[i], state, comment_count); 176 | 177 | if (state == IN_COMMENT_START) { 178 | if (comment_count == 0) 179 | csp.push_back(i-1); 180 | 181 | comment_count++; 182 | } 183 | else if (state == IN_COMMENT_END) { 184 | comment_count--; 185 | if(comment_count == 0) 186 | cep.push_back(i); 187 | } 188 | 189 | if (state == STATE_END && terminated == true) { 190 | Rcpp::stop("Bad expression: only one ;|$ terminated expression at a time is allowed"); 191 | } 192 | 193 | if (state == STATE_END) { 194 | terminated = true; 195 | } 196 | } 197 | 198 | if(csp.size() != cep.size()) { 199 | stop("Bad expression: Found Non-ending comment"); 200 | } 201 | 202 | for (int i = csp.size() - 1; i >= 0; --i) { 203 | command.erase(csp.at(i), cep.at(i) - csp.at(i) + 1); 204 | } 205 | 206 | if (!terminated) { 207 | command += ";"; 208 | } 209 | 210 | return command; 211 | } 212 | 213 | // [[Rcpp::export]] 214 | Rcpp::List dissect_chunk(std::vector code) { 215 | int state = 0; 216 | int comment_count = 0; 217 | bool terminated = false; 218 | std::vector csp, cep; 219 | List L = List::create(); 220 | IntegerVector temp = IntegerVector::create(); 221 | 222 | for (size_t i = 0; i < code.size(); ++i) { 223 | code[i] = trim(code[i]); 224 | // code(i) = trim(code[i]); 225 | for (size_t j = 0; j < code[i].size(); ++j) { 226 | state = checkInput(code[i][j], state, comment_count); 227 | 228 | if (state == IN_COMMENT_START) { 229 | if (comment_count == 0) 230 | csp.push_back(i-1); 231 | 232 | comment_count++; 233 | } 234 | else if (state == IN_COMMENT_END) { 235 | comment_count--; 236 | if(comment_count == 0) 237 | cep.push_back(i); 238 | } 239 | 240 | if(state == STATE_END) { 241 | terminated = true; 242 | } 243 | } 244 | 245 | // skip empty lines 246 | if(code[i].size() > 0) { 247 | temp.push_back(i+1); 248 | if(terminated == true) { 249 | terminated = false; 250 | L.push_back(temp); 251 | temp.erase(0, temp.size()); 252 | } 253 | } 254 | 255 | } 256 | 257 | return L; 258 | } 259 | 260 | 261 | // [[Rcpp::export]] 262 | Rcpp::List dissect_repl_input(std::string input) { 263 | int state = 0; 264 | int comment_count = 0; 265 | size_t term_pos = 0; 266 | std::string in = trim(input); 267 | List out = List::create(); 268 | 269 | for (size_t i = 0; i < in.size(); ++i) { 270 | state = checkInput(in[i], state, comment_count); 271 | 272 | if (state == IN_COMMENT_START) { 273 | comment_count++; 274 | } else if (state == IN_COMMENT_END) { 275 | comment_count--; 276 | } 277 | 278 | // if(state == STATE_END) { 279 | // terminated = true; 280 | // } 281 | 282 | // skip empty lines 283 | //if(terminated == true) { 284 | if(state == STATE_END) { 285 | // copy command to list 286 | // terminated = false; 287 | out.push_back(in.substr(term_pos, (i - term_pos) + 1)); 288 | term_pos = i + 1; 289 | // temp.erase(0, temp.size()); 290 | } 291 | } 292 | return out; 293 | } 294 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | test_check("rim") -------------------------------------------------------------------------------- /tests/testthat/test-apropos.R: -------------------------------------------------------------------------------- 1 | test_that("maxima.apropos returns exact result", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | to <- maxima.apropos("int") 6 | 7 | expect_s3_class(to, "maxima") 8 | expect_type(to, "list") 9 | expect_match(attr(to, "input.label"), "^%\\i[[:digit::]]*$") 10 | expect_match(attr(to, "output.label"), "^\\%o[[:digit::]]*$") 11 | expect_equal(attr(to, "command"), "apropos(\"int\");") 12 | expect_true(!attr(to, "suppressed")) 13 | 14 | expect_named(to, expected = c("wtl", "wol")) 15 | expect_named(to$wtl, expected = c("linear", "ascii", "latex", "inline", "mathml")) 16 | expect_named(to$wol, expected = c("linear", "ascii", "latex", "inline", "mathml", "rstr")) 17 | 18 | expect_gt(length(to$wtl$linear), 10L) 19 | expect_gt(length(to$wtl$ascii), 10L) 20 | expect_length(to$wtl$latex, 1L) 21 | expect_gt(length(to$wtl$mathml), 10L) 22 | 23 | expect_gt(length(to$wol$linear), 10L) 24 | expect_gt(length(to$wol$ascii), 10L) 25 | expect_length(to$wol$latex, 1L) 26 | expect_gt(length(to$wol$mathml), 10L) 27 | }) 28 | -------------------------------------------------------------------------------- /tests/testthat/test-ask-user.R: -------------------------------------------------------------------------------- 1 | test_that("ask prompts are returned", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | expect_match(maxima.get("integrate(x^n,x)"), "Is n equal to -1?") 5 | 6 | to <- maxima.get("y;") 7 | expect_s3_class(to, "maxima") 8 | expect_type(to, "list") 9 | expect_match(attr(to, "input.label"), "^%\\i[[:digit::]]*$") 10 | expect_match(attr(to, "output.label"), "^\\%o[[:digit::]]*$") 11 | expect_equal(attr(to, "command"), "y;") 12 | expect_true(!attr(to, "suppressed")) 13 | # expect_match(maxima.get("y;"), "^[[:space:]|[:print:]]*$") 14 | 15 | expect_length(to$wtl$linear, 1L) 16 | expect_length(to$wtl$ascii, 1L) 17 | expect_length(to$wtl$latex, 1L) 18 | expect_length(to$wtl$mathml, 3L) 19 | 20 | expect_length(to$wol$linear, 1L) 21 | expect_length(to$wol$ascii, 1L) 22 | expect_length(to$wol$latex, 1L) 23 | expect_length(to$wol$mathml, 2L) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/testthat/test-engine.R: -------------------------------------------------------------------------------- 1 | test_that("maxima knitr engine works", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | fr <- system.file("extdata", c("test.Rmd", "result.md"), 6 | package = "rim", mustWork = TRUE) 7 | # fo <- paste0(dirname(fr[1]), "/test.md") 8 | fo <- tempfile(fileext = ".md") 9 | 10 | result <- readLines(con = fr[2]) 11 | suppressWarnings(knitr::knit(input = fr[1], output = fo, quiet = TRUE)) 12 | test <- readLines(con = fo) 13 | 14 | # expect_match(digest::digest(readLines(fo), "sha256"), hash) 15 | expect_equal(object = test, expected = result) 16 | 17 | # clean up 18 | file.remove(fo) 19 | 20 | # gnuplot files 21 | td <- dirname(dirname(tempfile())) 22 | rf <- list.files(path = td, 23 | pattern = "(?:maxout|data)[[:digit:]]*\\.gnuplot", 24 | full.names = TRUE) 25 | 26 | file.remove(rf) 27 | }) 28 | 29 | -------------------------------------------------------------------------------- /tests/testthat/test-errors.R: -------------------------------------------------------------------------------- 1 | test_that("errors are forwarded", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | expect_error(maxima.get("1/0;")) 6 | }) 7 | 8 | -------------------------------------------------------------------------------- /tests/testthat/test-execute.R: -------------------------------------------------------------------------------- 1 | test_that("execution returns formatted strings", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | to <- maxima.get("jacobian( [alpha / (alpha + beta), 1 / sqrt(alpha + beta)], [alpha, beta] );") 6 | expect_s3_class(to, "maxima") 7 | expect_type(to, "list") 8 | expect_match(attr(to, "input.label"), "^%\\i[[:digit::]]*$") 9 | expect_match(attr(to, "output.label"), "^\\%o[[:digit::]]*$") 10 | expect_equal(attr(to, "command"), "jacobian( [alpha / (alpha + beta), 1 / sqrt(alpha + beta)], [alpha, beta] );") 11 | expect_true(!attr(to, "suppressed")) 12 | 13 | to <- maxima.get("2+2$") 14 | expect_s3_class(to, "maxima") 15 | expect_type(to, "list") 16 | expect_length(to, 0L) 17 | expect_match(attr(to, "input.label"), "^%\\i[[:digit::]]*$") 18 | expect_match(attr(to, "output.label"), "^\\%o[[:digit::]]*$") 19 | expect_equal(attr(to, "command"), "2+2$") 20 | expect_true(attr(to, "suppressed")) 21 | }) 22 | -------------------------------------------------------------------------------- /tests/testthat/test-load.R: -------------------------------------------------------------------------------- 1 | test_that("maxima.load returns exact result", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | to <- maxima.load("ratpow") 6 | 7 | expect_s3_class(to, "maxima") 8 | expect_type(to, "list") 9 | expect_match(attr(to, "input.label"), "^%\\i[[:digit::]]*$") 10 | expect_match(attr(to, "output.label"), "^\\%o[[:digit::]]*$") 11 | expect_equal(attr(to, "command"), "load(ratpow)$") 12 | expect_true(attr(to, "suppressed")) 13 | }) 14 | 15 | -------------------------------------------------------------------------------- /tests/testthat/test-parser.R: -------------------------------------------------------------------------------- 1 | test_that("parsing and evaluation of parsed results works", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | 5 | t1 <- maxima.get("integrate(1/(1 - x^3), x);") 6 | p1 <- maxima.eval(t1, code = TRUE, envir = list(x = 1:5)) 7 | expect_s3_class(t1, "maxima") 8 | expect_type(t1, "list") 9 | expect_equal(nchar(paste0(deparse(attr(t1, "parsed")), collapse = "")), 130L) 10 | expect_length(p1, 5) 11 | expect_equal(p1[1], Inf) 12 | expect_false(attr(t1, "suppressed")) 13 | 14 | t2 <- maxima.get("2+2$") 15 | p2 <- maxima.eval(t2, code = TRUE) 16 | expect_s3_class(t2, "maxima") 17 | expect_type(t2, "list") 18 | expect_true(is.na(nchar(attr(t2, "parsed")))) 19 | expect_true(is.na(p2)) 20 | expect_length(p2, 1) 21 | expect_true(attr(t2, "suppressed")) 22 | 23 | p3 <- maxima.eval("integrate(x, x);", code = TRUE, list(x = 1)) 24 | expect_length(p3, 1) 25 | expect_gte(nchar(deparse(attr(p3, "maxima"))), 18L) 26 | 27 | p4 <- maxima.eval("x^2+1;", code = FALSE, list(x = 3)) 28 | expect_null(attr(p4, "maxima")) 29 | 30 | p5 <- maxima.eval("matrix([1, 2, 3, b], [4, a, a+a^2, c*b], [5, 6, 7, b^2]);", 31 | envir = list(a = 10, b = 2, c = 3)) 32 | expect_true(is.matrix(p5)) 33 | 34 | p6 <- maxima.eval("1+%i*2;") 35 | expect_type(p6, "complex") 36 | }) 37 | 38 | -------------------------------------------------------------------------------- /tests/testthat/test-warnings.R: -------------------------------------------------------------------------------- 1 | test_that("warnings are forwarded", { 2 | if(!maxima.env$maxima$isInstalled()) 3 | skip("Maxima not installed") 4 | if(maxima.version() < "5.43" | maxima.version() >= "5.45.1") 5 | skip(paste("No warnings to be tested under Maxima", maxima.version(), "\n")) 6 | 7 | maxima.get("float2bf: false$") 8 | maxima.get("x: float(%pi)$") 9 | expect_warning(maxima.get("bfloat(x);")) 10 | expect_warning(maxima.eval("sum(1/x^2, x, 1, 10000);")) 11 | }) 12 | --------------------------------------------------------------------------------