#+Title: LARCS #+Subtitle: The "Lisp Automated Rewrite and Calculation System" #+AUTHOR: Samuel W. Flint #+EMAIL: swflint@flintfam.org #+DATE: \today #+INFOJS_OPT: view:info toc:nil path:http://flintfam.org/org-info.js #+OPTIONS: toc:nil H:5 ':t *:t todo:nil stat:nil d:nil #+PROPERTY: noweb no-export #+PROPERTY: comments noweb #+LATEX_HEADER: \usepackage[margins=0.75in]{geometry} #+LATEX_HEADER: \parskip=5pt #+LATEX_HEADER: \parindent=0pt #+LATEX_HEADER: \lstset{texcl=true,breaklines=true,columns=fullflexible,basicstyle=\ttfamily,frame=lines,literate={<=}{$\leq$}1 {>=}{$\geq$}1} #+LATEX_CLASS_OPTIONS: [10pt,twoside] #+LATEX_HEADER: \pagestyle{headings} * Export :noexport: :PROPERTIES: :CREATED: <2016-06-09 Thu 12:49> :END: #+Caption: Export Document #+Name: export-document #+BEGIN_SRC emacs-lisp :exports none :results none (save-buffer) (let ((org-confirm-babel-evaluate (lambda (lang body) (declare (ignorable lang body)) nil))) (org-latex-export-to-pdf)) #+END_SRC * Tangle :noexport: :PROPERTIES: :CREATED: <2016-06-09 Thu 12:50> :END: #+Caption: Tangle Document #+Name: tangle-document #+BEGIN_SRC emacs-lisp :exports none :results none (save-buffer) (let ((python-indent-offset 4)) (org-babel-tangle)) #+END_SRC * WORKING Introduction :nonum: :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :END: It's a bold move to do what this does, building a Computer Algebra System from scratch, but I'm doing it anyway. I've chosen to do this because I wanted to understand how most CASs work, and that can be accomplished by either reading thhe source code for one, or by building one. While there are several very good CASs, the majority of them are non-free, and thus I'm not able to learn how exactly they work. Those that are free software are either not complete, or are too complex to be able to learn from easily. This is my Computer Algebra System, and it contains the following components: - [[id:f153a0fe-ec04-47b1-bdc5-290cc62bc985][Common Functionality]] - [[id:c6921b1e-d269-4243-acff-5a77685c331e][Expression Typing]] - [[id:b2c1fd45-b631-48f9-a093-66e1a0faa77f][Algebraic Manipulation]] - [[id:ffa664b6-e147-471c-9e97-f49c4619fc99][Symbolic Solver]] - [[id:b0259b8c-e285-48ca-bda0-4f04e3bc6ef2][Symbolic Trigonometry]] - [[id:552f402a-a25d-4f28-94af-17934c38a529][Symbolic Differentiation]] - [[id:61add971-a129-40d0-be13-24f8a4afc522][Symbolic Integration]] - [[id:75f65e8a-0cc9-477f-b5e9-3c563fe7ef5c][Symbolic To Typeset Form]] - [[id:89370949-8f58-41cf-8c4f-92f81d48ac23][Library Assembly]] - [[id:65c8092c-1f7a-44e1-93d5-8d39e179f447][Text User Interface]] - [[id:b70027f8-a665-4282-ab4b-3d0ce6bd8d17][Graphical User Interface]] * DONE What's in a name? :nonum: CLOSED: [2016-06-09 Thu 12:48] :PROPERTIES: :CREATED: <2016-06-09 Thu 12:37> :END: The CAS contained in this is called LARCS, or the Lisp Automated Rewrite and Calculation System. This describes the system as follows: - Lisp :: The CAS is written in Lisp. This is not novel, as other CAS have been written in Lisp before (Macsyma/Maxima), but it is unusual in that most new ones have been written in other languages. - Automated :: The CAS will perform rewrites and calculations automatically. - Rewrite :: The system is built on the concept of a rewrite system. This workse because to perform many actions in the algebra, you rewrite an equation in one way or another. - Calculation :: The ability to go from a symbolic equation, something like $3x^3 + x^2 + 10x - 3$ (~(+ (* 3 (expt x 3)) (expt x 2) (* 10 x) -3)~), to the result where $x \gets 4$ (245). - System :: A complete library and application for symbolic algebra. * TOC :ignore: :PROPERTIES: :CREATED: <2016-06-09 Thu 09:19> :END: #+TOC: headlines 3 #+TOC: listings * WORKING Common Functionality [0/4] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:23> :ID: f153a0fe-ec04-47b1-bdc5-290cc62bc985 :END: ** TODO Match Expression Generation :PROPERTIES: :CREATED: <2016-06-13 Mon 17:18> :ID: f7876b1d-3b67-48c1-863a-85e1b3026ed6 :END: To be able to apply an expansion, you need to determine eligibility. To do this, you need an expression that matches on two things, function name and arity. To generate this, it takes an operation name and the arity. Based on the arity type ($=$, $>$, $\leq$), it will construct a simple boolean statement in the format of $(function = operator) \land (argument-count == arity)$, where $==$ is one of the above arity types. #+Caption: Match Expression Generation #+Name: common-match-expression-generation #+BEGIN_SRC lisp (defun generate-match-expression (on arity &optional (type '=) (function-var 'function) (arg-count-var 'arg-count)) (check-type on symbol) (check-type type (member = > >=)) (check-type arity (integer 0)) (case type (= `(and (eq ,function-var ',on) (= ,arg-count-var ,arity))) (> `(and (eq ,function-var ',on) (> ,arg-count-var ,arity))) (>= `(and (eq ,function-var ',on) (>= ,arg-count-var ,arity))))) #+END_SRC ** TODO Generate an Args List :PROPERTIES: :CREATED: <2016-06-13 Mon 17:19> :ID: 49596957-2fc6-4458-ad85-99cbcf337b42 :END: #+Caption: Generate an Args List #+Name: common-generate-an-args-list #+BEGIN_SRC lisp (defun gen-args-list (count) (let ((letters '(a b c d e f g h i j k l m n o p q r s t u v w x y z))) (let ((variables-list '())) (dotimes (i count) (pushnew (symbolicate 'expression- (nth i letters)) variables-list)) (reverse variables-list)))) #+END_SRC ** TODO Constants and Greeks :PROPERTIES: :CREATED: <2016-06-13 Mon 20:57> :ID: 907fcf64-51eb-4a2c-a8bc-29e4f75f1dd3 :END: #+Caption: Constants and Greeks #+Name: constants-and-greeks #+BEGIN_SRC lisp (defvar *special-symbols-to-sequences* '((alpha . "\\alpha") (beta . "\\beta") (gamma . "\\gamma") (delta . "\\delta") (epsilon . "\\epsilon") (varepsilon . "\\varepsilon") (zeta . "\\zeta") (eta . "\\eta") (theta . "\\theta") (vartheta . "\\vartheta") (gamma . "\\gamma") (kappa . "\\kappa") (lambda . "\\lambda") (mu . "\\mu") (nu . "\\nu") (xi . "\\xi") (omicron . "\\o") (pi . "\\pi") (varpi . "\\varpi") (rho . "\\rho") (varrho . "\\varrho") (sigma . "\\sigma") (varsigm . "\\varsigm") (tau . "\\tau") (upsilon . "\\upsilon") (phi . "\\phi") (varphi . "\\varphi") (chi . "\\chi") (psi . "\\psi") (omega . "\\omega") (big-gamma . "\\Gamma") (big-delta . "\\Delta") (big-theta . "\\Theta") (big-lambda . "\\Lambda") (big-xi . "\\Xi") (big-pi . "\\Pi") (big-sigma . "\\Sigma") (big-upsilon . "\\Upsilon") (big-phi . "\\Phi") (big-psi . "\\Psi") (big-omega . "\\Omega"))) (defvar *constant-names* (mapcar #'car *special-symbols-to-sequences*)) (mapcar #'export *constant-names*) #+END_SRC ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-13 Mon 17:20> :ID: d583d5e4-a2c9-432c-9486-cc6baa4239f4 :END: #+Caption: Assemble Common Functions #+Name: assemble-common-functions #+BEGIN_SRC lisp :tangle "larcs-common.lisp" (in-package #:larcs.common) <> <> <> #+END_SRC * DONE Expression Typing [8/8] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:15> :ID: c6921b1e-d269-4243-acff-5a77685c331e :END: To be able to provide various forms of matching and manipulation, the type of an expression must be determined. This is done by analyzing the contents of the expression. To accomplish this, there must be a way to define a classifier, store all possible classifiers, check a classifier and produce a classification. To provide more flexibility in programming, there is also a special version of case, called ~classification-case~ and a when-pattern macro called ~when-classified-as~. ** DONE Define Classification CLOSED: [2016-06-14 Tue 23:00] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: d8826a51-50b8-467a-9e52-158502bd4138 :END: Classifications are defined as ~define-classification~. This macro takes a ~name~, which is the name of the classification, and a body, which is classified within a function. Inside the function, the following are bound: ~expression~, the expression to be classified; and, ~length~, which is the length of the expression if it's a list, otherwise, 0 if it's atomic. A cons cell containing the name of the classification and the name of the classifier is pushed onto classification storage, and the classifier name is exported. #+Caption: Define Classification #+Name: et-define-classification #+BEGIN_SRC lisp (defmacro define-classification (name &body body) (check-type name symbol) (let ((classifier-name (symbolicate name '-classifier))) `(progn (defun ,classifier-name (expression &aux (length (if (listp expression) (length expression) 0))) (declare (ignorable length)) ,@body) (pushnew '(,name . ,classifier-name) *classifications*) (export ',name) ',name))) #+END_SRC ** DONE Check Classification CLOSED: [2016-06-14 Tue 23:10] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: 6505b0b1-ffd8-4dd6-b81a-3e49483d8437 :END: To classify an expression, the expression and name of the possible classification is passed in. If the given name of the classification is ~*~, then ~t~ is returned, as this is a catch all; otherwise the classification is retrieved by name, and the expression is passed to the classifier, which will return either ~t~ or ~nil~. #+Caption: Check Classification #+Name: et-check-classification #+BEGIN_SRC lisp (defun classified-as-p (expression classification) (if (eq '* classification) t (funcall (cdr (assoc classification *classifications*)) expression))) #+END_SRC ** DONE Classify Expression CLOSED: [2016-06-14 Tue 23:23] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:09> :ID: 82d75d54-1d33-400b-86a3-7d16af938ac8 :END: While being able to check if an expression is given a specific classification is vital, for some things, being able to see what all possible classifications for an expression are can be quite useful. To do this, an expression is passed in, and for each possible classification in the classification storage, it is checked to see whether or not the classification is possible. If it is, the classification is pushed on to a list of valid classifications. When the possible classifications are exhausted, the list of valid classifications is reversed and returned. #+Caption: Classify Expression #+Name: et-classify-expression #+BEGIN_SRC lisp (defun classify (expression) (let ((classifications '())) (dolist (possible ,*classifications* (reverse classifications)) (let ((name (car possible)) (checker (cdr possible))) (when (funcall checker expression) (push name classifications)))))) #+END_SRC ** DONE Classification Case CLOSED: [2016-06-14 Tue 23:34] :PROPERTIES: :CREATED: <2016-05-20 Fri 14:15> :ID: 19a4e467-baa0-47eb-9267-93ff3801b1fd :END: Because case is such a useful tool, and because it provides a way to ensure that an expression doesn't fall through when acting on it, I've written the ~classification-case~ macro. It takes an expression, named ~var~ and a list of cases, in the form of ~(classification body-form-1 body-form-2 body-form-n)~. It transforms the cases, converting them to the form ~((classified-as-p expression 'type) body-form-1 body-form-2 body-form-n)~. It finally expands to a ~cond~ in which ~the-classification~ is bound to the full and complete classification of the passed expression. #+Caption: Classification Case #+Name: et-classification-case #+BEGIN_SRC lisp (defmacro classification-case (var &rest cases) (let ((conditions (map 'list #'(lambda (case) (destructuring-bind (type &body body) case (if (eq type 't) `((classified-as-p ,var '*) ,@body) `((classified-as-p ,var ',type) ,@body)))) cases))) `(let ((the-classification (classify ,var))) (declare (ignorable the-classification)) (cond ,@conditions)))) #+END_SRC ** DONE When Classified CLOSED: [2016-06-14 Tue 23:44] :PROPERTIES: :CREATED: <2016-05-30 Mon 18:31> :ID: 5c7c3e0b-9170-48e9-a414-6ac4528f9ac3 :END: Another utility macro is ~when-classified-as~, which takes a ~classification~, an expressiond named ~variable~ and a body. It expands fairly simply to a ~when~ form, with the predicate taking the following form ~(classified-as-p variable 'classification)~, wrapping around the passed in body. #+Caption: When Classified #+Name: et-when-classified #+BEGIN_SRC lisp (defmacro when-classified-as (classification variable &body body) `(when (classified-as-p ,variable ',classification) ,@body)) #+END_SRC ** DONE Classifications [13/13] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:56> :ID: dcce4a6b-1b2d-4638-a82b-0c4917b0698a :END: I define the following classifications: - Numerics :: All numbers - Variables :: Any symbols - Non-atomics :: Anything that isn't simply a number or a variable - Additives :: Expressions that are adding multiple terms - Subtractives :: Expressions subtracting multiple terms - Powers :: Expressions of the form $x^n$, where $x$ is a variable, and $n$ is a numeric. - Exponentials :: Expressions of the form $x^y$ or $e^y$, where $x$ and $y$ are generic expressions, and $e$ is Euler's constant. - Logarithmics :: Expressions of the form of $\ln x$ or $\log_b x$, where $x$ and $b$ are generic expressions. - Rationals :: Expressions of the form $\frac{f(x)}{g(x)}$. - Polynomial Terms :: Any integers, multiplicatives of the form $nx^m$ or powers of the form $x^m$, where $x$ is a variable and $n$ and $m$ are numerics. - Polynomials :: Additives or Subtractives consisting solely of Polynomial Terms. - Trigonometrics :: The trig functions: $\sin$, $\cos$, $\tan$, $\csc$, $\sec$ and $\cot$. #+Caption: Possible Classifications #+Name: et-possible-classifications #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC *** DONE Numbers CLOSED: [2016-06-14 Tue 23:58] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 42081153-7cc5-42ff-a17f-53e171c6d1a7 :END: A number is defined as anything that satisfies the built-in ~numberp~. This includes integers, rationals, floats and complex numbers. #+Caption: Classify Numbers #+Name: et-classify-numbers #+BEGIN_SRC lisp (define-classification numeric (numberp expression)) #+END_SRC *** DONE Variables CLOSED: [2016-06-15 Wed 00:00] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 4c676754-ef9a-485f-91a2-8f1bd83c7659 :END: Variables are defined as anything that satisfies the Common Lisp predicate, ~symbolp~. #+Caption: Classify Variables #+Name: et-classify-variables #+BEGIN_SRC lisp (define-classification variable (symbolp expression)) #+END_SRC *** DONE Non Atomics CLOSED: [2016-06-15 Wed 00:02] :PROPERTIES: :CREATED: <2016-05-04 Wed 19:52> :ID: 414df063-0be1-4849-8b9f-d71aa828be2a :END: Non-atomic is a classification for anything other than numerics and variables. It is defined as anything that satisfies the predicate ~listp~. #+Caption: Classify Non-Atomics #+Name: et-classify-non-atomics #+BEGIN_SRC lisp (define-classification non-atomic (listp expression)) #+END_SRC *** DONE Additives CLOSED: [2016-06-15 Wed 00:03] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: 736d79dc-f34c-4247-b592-690d7f2fddd9 :END: When an expression is non-atomic, and the first element is the symbol ~+~, it is classified as an additive expression. #+Caption: Classify Additives #+Name: et-classify-additives #+BEGIN_SRC lisp (define-classification additive (when-classified-as non-atomic expression (eq '+ (first expression)))) #+END_SRC *** DONE Subtractive CLOSED: [2016-06-15 Wed 00:06] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:26> :ID: c59d086f-2f49-485a-8f96-57d85e774f60 :END: A non-atomic expression for which the first element is the symbol ~-~ is a subtractive expression. #+Caption: Classify Subtractives #+Name: et-classify-subtractives #+BEGIN_SRC lisp (define-classification subtractive (when-classified-as non-atomic expression (eq '- (first expression)))) #+END_SRC *** DONE Powers CLOSED: [2016-06-15 Wed 00:07] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: cc15dd10-7cc0-4370-9e69-daf903b30ad5 :END: A power is any expression that is non-atomic, the first element is the symbol ~expt~, the second is a variable and the third is a numeric. #+Caption: Classify Powers #+Name: et-classify-powers #+BEGIN_SRC lisp (define-classification power (when-classified-as non-atomic expression (and (eq 'expt (first expression)) (classified-as-p (second expression) 'variable) (classified-as-p (third expression) 'numeric)))) #+END_SRC *** DONE Exponentials CLOSED: [2016-06-15 Wed 00:11] :PROPERTIES: :CREATED: <2016-05-02 Mon 15:04> :ID: a11fdd94-d56c-4749-bb22-dca75159dbcb :END: There are two types of exponentials, natural and non-natural. Natural exponentials are defined as being non-atomic, two elements long, and the first element being ~exp~. Non-natural exponentials are defined similarly, but are three elements long, and the first of which is the symbol ~expt~. #+Caption: Classify Exponentials #+Name: et-classify-exponentials #+BEGIN_SRC lisp (define-classification natural-exponential (when-classified-as non-atomic expression (and (= 2 length) (eq 'exp (first expression))))) (define-classification exponential (when-classified-as non-atomic expression (and (= 3 length) (eq 'expt (first expression))))) #+END_SRC *** DONE Multiplicatives CLOSED: [2016-06-15 Wed 00:12] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: feb85a20-93e3-45a1-be01-9893ecc07c53 :END: A multiplicative expression is non-atomic, of any length, and the first element is the symbol ~*~. #+Caption: Classify Multiplicatives #+Name: et-classify-multiplicatives #+BEGIN_SRC lisp (define-classification multiplicative (when-classified-as non-atomic expression (eq '* (first expression)))) #+END_SRC *** DONE Logarithmics CLOSED: [2016-06-15 Wed 00:14] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:27> :ID: 0b733d75-e1ab-413f-8f8a-6a8a47db409c :END: There are two types of logarithmic classifications, natural and non-natural. Natural logarithmics are non-atomic, two elements long, and the first element is the symbol ~log~. Natural logarithmics are also non-atomic, but they are three elements long, starting with the symbol ~log~. #+Caption: Classify Lograthmics #+Name: et-classify-logarithmics #+BEGIN_SRC lisp (define-classification natural-logarithmic (when-classified-as non-atomic expression (and (= 2 length) (eq 'log (first expression))))) (define-classification logarithmic (when-classified-as non-atomic expression (and (= 3 length) (eq 'log (first expression))))) #+END_SRC *** DONE Rationals CLOSED: [2016-06-15 Wed 00:15] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: a4505a66-c249-4438-a6df-81e21718e23e :END: Rationals are non-atomic, three elements long, and the first element is the symbol ~/~. #+Caption: Classify Rationals #+Name: et-classify-rationals #+BEGIN_SRC lisp (define-classification rational (when-classified-as non-atomic expression (and (= 3 length) (eq '/ (first expression))))) #+END_SRC *** DONE Polynomial Terms CLOSED: [2016-06-15 Wed 00:17] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 37da52b7-98a0-4a16-8a17-a62fcff2ba59 :END: Polynomials are a compound classification: - Numerics - Variables - Powers - Multiplicatives that are a numeric and a variable - Multiplicatives that are a numeric and a power #+Caption: Classify Polynomial Term #+Name: et-classify-polynomial-term #+BEGIN_SRC lisp (define-classification polynomial-term (or (classified-as-p expression 'numeric) (classified-as-p expression 'variable) (classified-as-p expression 'power) (and (classified-as-p expression 'multiplicative) (= (length (rest expression)) 2) (or (and (classified-as-p (second expression) 'numeric) (or (classified-as-p (third expression) 'power) (classified-as-p (third expression) 'variable))) (and (classified-as-p (third expression) 'numeric) (or (classified-as-p (second expression) 'power) (classified-as-p (second expression) 'variable))))))) #+END_SRC *** DONE Polynomials CLOSED: [2016-06-15 Wed 00:19] :PROPERTIES: :CREATED: <2016-05-02 Mon 14:28> :ID: 8cd9045b-81dd-4571-930a-a852f81969c9 :END: Polynomials are compound classifications that are defined as expressions which are either additive or subtrative, for which each term is a polynomial term. #+Caption: Classify Polynomials #+Name: et-classify-polynomials #+BEGIN_SRC lisp (define-classification polynomial (when-classified-as non-atomic expression (and (or (eq '- (first expression)) (eq '+ (first expression))) (reduce #'(lambda (a b) (and a b)) (map 'list #'(lambda (the-expression) (classified-as-p the-expression 'polynomial-term)) (rest expression)))))) #+END_SRC *** DONE Trigonometrics CLOSED: [2016-06-15 Wed 00:22] :PROPERTIES: :CREATED: <2016-05-04 Wed 13:38> :ID: 6f433cad-4b81-4a6f-ab65-981f4a924812 :END: Trigonometrics are defined as non atomic expressions that are two elements long, for which the first element of the expression is either ~sin~, ~cos~, ~tan~, ~csc~, ~sec~, or ~cot~. For each of these there is a classification seperate from the generic ~trigonometric~ classification. #+Caption: Classify Trigonometrics #+Name: et-classify-trigonometrics #+BEGIN_SRC lisp (define-classification trigonometric (when-classified-as non-atomic expression (member (first expression) '(sin cos tan csc sec cot)))) (define-classification sin (when-classified-as non-atomic expression (eq 'sin (first expression)))) (define-classification cos (when-classified-as non-atomic expression (eq 'cos (first expression)))) (define-classification tan (when-classified-as non-atomic expression (eq 'tan (first expression)))) (define-classification csc (when-classified-as non-atomic expression (eq 'csc (first expression)))) (define-classification sec (when-classified-as non-atomic expression (eq 'sec (first expression)))) (define-classification cot (when (classified-as-p expression 'non-atomic) (eq 'cot (first expression)))) #+END_SRC ** DONE Classification Storage CLOSED: [2016-06-14 Tue 23:48] :PROPERTIES: :CREATED: <2016-05-02 Mon 13:55> :ID: ff35cd33-3c10-4a45-a2c5-32bc3fdc1acc :END: Classifications are stored in an alist, with the key being the name of the classification, and the value being the classifier itself. These cons cells are stored in the ~*classifications*~ variable. #+Caption: Classification Storage #+Name: et-classification-storage #+BEGIN_SRC lisp (defvar *classifications* '()) #+END_SRC ** DONE Assembly CLOSED: [2016-06-15 Wed 00:26] :PROPERTIES: :CREATED: <2016-06-14 Tue 16:59> :ID: bb1d3eb5-b9bf-4378-9716-87ab57dcc8a3 :END: This assembles the classification library, which in the ~#:larcs.classify~ package. It correctly resolves the order of the code, taking it from simple blocks to a complete file. #+Caption: Expression Typing Assembly #+Name: et-assembly #+BEGIN_SRC lisp :tangle "larcs-classify.lisp" (in-package #:larcs.classify) <> <> <> <> <> <> <> #+END_SRC * WORKING Algebraic Manipulation [1/5] :PROPERTIES: :CREATED: <2016-06-09 Thu 09:20> :ID: b2c1fd45-b631-48f9-a093-66e1a0faa77f :END: One of the most important parts of this system is the "algebraic manipulator", a sub-system that provides utilities for symbolic arithmetic, that is to say addition, subtraction, multiplication and division, along with trigonometric functions and exponential/logarithmic functions. These function, as many other portions of this system, using rewrite rules, implementing a form of specialized generic programming. ** TODO Collect Variables :PROPERTIES: :CREATED: <2016-05-20 Fri 15:15> :ID: 6333322c-e12f-4ef6-8394-2fe219a72836 :END: Variable collection is somewhat important, and to accomplish this, I use a recursive algorithm. An expression is passed to the function, and if the expression is a variable, then the variable is collected and spit out; otherwise, if the expression is non-atomic, it is passed to the function recursively, and the returned variables are then merged into the variables list. Upon termination (no further sub-expressions), all variables are returned. (See Figure [[fig:variable-collection]].) #+Caption: Variable Collection #+Name: variable-collection #+BEGIN_SRC dot :file "imgs/variable-collection.png" :export results :cache yes digraph { start [label = "Start"]; stop [label = "Stop"]; collect [label = "Collect"]; if_var [label = "If Variable", shape = rectangle]; recurse_collect [label = "Iterate, Recurse and Collect Results"]; start -> if_var; if_var -> collect [label = "True"]; collect -> stop; if_var -> recurse_collect [label = "Non-atomic"]; recurse_collect -> start; } #+END_SRC #+Caption: Variable Collection Algorithm #+Name: fig:variable-collection #+ATTR_LATEX: :width 8cm #+RESULTS[e1586dc50921f7ba260f125e7221a978d489bd34]: variable-collection [[file:imgs/variable-collection.png]] #+Caption: Collect Variables #+Name: am-collect-variables #+BEGIN_SRC lisp (defun collect-variables (expression) (let ((variables '())) (flet ((merge-variables (variable) (pushnew variable variables))) (classification-case expression (variable (merge-variables expression)) (non-atomic (map 'list #'(lambda (expr) (dolist (variable (collect-variables expr)) (merge-variables variable))) (rest expression))))) (reverse variables))) #+END_SRC ** WORKING Term Collection :noexport: :PROPERTIES: :CREATED: <2016-04-30 Sat 22:59> :ID: c1856735-914b-4f73-8825-3e5a062113d2 :END: As there are various forms of expressions, and to provide for simplification, there must be a way to collect terms and return them in a way that allows a programmer to select all sub-expressions of a type within a large expression. #+Caption: Collect Terms #+Name: am-collect-terms #+BEGIN_SRC lisp (defun collect-terms (expression &aux (terms (rest expression))) (let ((numerics '()) (variables '()) (additives '()) (subtractives '()) (multiplicatives '()) (polynomial-terms '()) (rationals '()) (powers '()) (natural-exponentials '()) (exponentials '()) (natural-logarithmics '()) (trigonometrics '())) (dolist (term terms) (classification-case term (numeric (pushnew term numerics)) (variable (pushnew term variables)) (power (pushnew term powers)) (additive (pushnew term additives)) (subtractive (pushnew term subtractives)) (polynomial-term (pushnew term polynomial-terms)) (multiplicative (pushnew term multiplicatives)) (rational (pushnew term rationals)) (power (pushnew term powers)) (natural-exponential (pushnew term natural-exponentials)) (exponential (pushnew term exponentials)) (natural-logarithmic (pushnew term natural-logarithmics)) (trigonometric (pushnew term trigonometrics)))) (remove-if #'(lambda (expr) (null (cdr expr))) (list (cons :numerics numerics) (cons :variables variables) (cons :powers powers) (cons :additives additives) (cons :subtractives subtractives) (cons :multiplicatives multiplicatives) (cons :polynomial-terms polynomial-terms) (cons :rationals rationals) (cons :powers powers) (cons :natural-exponentials natural-exponentials) (cons :exponentials exponentials) (cons :natural-logarithmics natural-logarithmics) (cons :trigonometrics trigonometrics))))) #+END_SRC ** WORKING Polynomial Related Functions [0/6] :PROPERTIES: :CREATED: <2016-05-01 Sun 12:29> :ID: 984d0f52-4c52-4bfa-a150-f3289d25bdf1 :END: #+Caption: Polynomial Related Functions #+Name: am-polynomial-related-functions #+BEGIN_SRC lisp <> <> <> <> <> <> #+END_SRC *** TODO Get Coefficient :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: cbc927fc-ae5e-46bf-a028-2872b5c31831 :END: #+Caption: Get Coefficient #+Name: am-get-coefficient #+BEGIN_SRC lisp (defun coefficient (term) (when (classified-as-p term 'polynomial-term) (classification-case term (variable 1) (power 1) (multiplicative (second term)) (numeric term)))) #+END_SRC *** TODO Get Term Variables :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 55729698-bd51-48af-ab42-197871c54dbb :END: #+Caption: Get Term Variable #+Name: am-get-term-variable #+BEGIN_SRC lisp (defun term-variable (term) (when (classified-as-p term 'polynomial-term) (classification-case term (power (second term)) (multiplicative (if (listp (third term)) (second (third term)) (third term))) (numeric nil)))) #+END_SRC *** TODO Get Power :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 7d5a10da-bb30-496f-b285-470057a46db0 :END: #+Caption: Get Power #+Name: am-get-power #+BEGIN_SRC lisp (defun get-power (term) (classification-case term (numeric 0) (variable 1) (power (third term)) (multiplicative (if (listp (third term)) (third (third term)) 1)) (* 0))) #+END_SRC *** TODO Same Order :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: c56a1496-f4c2-4693-9448-5043570a752f :END: #+Caption: Same Order #+Name: am-same-order #+BEGIN_SRC lisp (defun same-order-p (term-a term-b) (= (get-power term-a) (get-power term-b))) #+END_SRC *** TODO Same Variable :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: 3806c97a-12fa-4488-b38c-d9ff3570c139 :END: #+Caption: Same Variable #+Name: am-same-variable #+BEGIN_SRC lisp (defun same-variable-p (term-a term-b) (eq (term-variable term-a) (term-variable term-b))) #+END_SRC *** TODO Is Combinable :PROPERTIES: :CREATED: <2016-05-31 Tue 19:08> :ID: db0410aa-bb12-4933-9be7-1a50d70ae90f :END: #+Caption: Is Combinable #+Name: am-is-combinable #+BEGIN_SRC lisp (defun single-term-combinable-p (term-a term-b) (and (same-order-p term-a term-b) (same-variable-p term-a term-b))) #+END_SRC ** WORKING Expression Manipulators [0/8] :PROPERTIES: :CREATED: <2016-04-30 Sat 22:58> :ID: 4fe60cc1-be66-4d5e-8922-590554d99004 :END: Foo #+Caption: Expression Manipulation #+Name: am-expression-manipulation #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> #+END_SRC *** TODO Manipulator Miscellaneous Functions :PROPERTIES: :CREATED: <2016-05-03 Tue 15:38> :ID: 20450528-d763-4c14-a085-5ac54d4d4b85 :END: This defines the ~*manipulator-map*~, where the manipulators for various functions are stored, and defines a function to generate an arguments list given a count of arguments. #+Caption: Misc Manipulator Functions #+Name: am-misc-manipulator-functions #+BEGIN_SRC lisp (defvar *manipulator-map* '()) #+END_SRC *** WORKING Define Expression Manipulator :PROPERTIES: :CREATED: <2016-04-30 Sat 22:57> :ID: 63909972-428d-47f3-9dc3-3e1fb213aa70 :END: #+Caption: Define Expression Manipulator #+Name: am-define-expression-manipulator #+BEGIN_SRC lisp (defmacro define-operation (name arity short) (check-type name symbol) (check-type arity (integer 1 26)) (check-type short symbol) (let* ((args (gen-args-list arity)) (expression-types (map 'list #'(lambda (x) (symbolicate x '-type)) args)) (rules-name (symbolicate '*manipulators- name '*)) (base-manipulator-name (symbolicate name '-manipulator-)) (manipulator-define-name (symbolicate 'define- name '-manipulator)) (is-applicable-name (symbolicate name '-is-applicable-p)) (get-operations-name (symbolicate 'get- name '-manipulators)) (type-check-list (let ((i 0)) (loop for arg in args collect (prog1 `(classified-as-p ,arg (nth ,i types)) (incf i)))))) `(progn (push '(,short . ,name) *manipulator-map*) (defvar ,rules-name '()) (defun ,is-applicable-name (types ,@args) (and ,@type-check-list)) (defun ,get-operations-name (,@args) (remove-if #'null (map 'list #'(lambda (option) (let ((types (car option)) (name (cdr option))) (if (,is-applicable-name types ,@args) name))) ,rules-name))) (defun ,name (,@args) (funcall (first (,get-operations-name ,@args)) ,@args)) (defmacro ,manipulator-define-name ((,@expression-types) &body body) (let ((manipulator-name (symbolicate ',base-manipulator-name ,@expression-types))) `(progn (setf ,',rules-name (append ,',rules-name '(((,,@expression-types) . ,manipulator-name)))) (defun ,manipulator-name ,',args ,@body))))))) #+END_SRC #+Caption: Manipulation Example #+Name: am-ex-manip-example #+BEGIN_SRC lisp :results output raw :exports results :cache yes (defpackage #:manipulator (:use #:cl) (:import-from #:alexandria #:symbolicate) (:export #:manipulate #:classify #:classified-as-p #:classification-case #:collect-variables #:collect-terms)) (load "larcs-manipulation") (in-package #:manipulator) (format t "#+Caption: Expression Manipulator Expansion~%#+Name: am-ex-manip-expansion~%#+BEGIN_SRC lisp :exports code~%~a~%#+END_SRC" (macroexpand-1 '(define-operation frobnicate 2 frob))) #+END_SRC #+RESULTS[130aac3873c71d5e7f3a237792267b51206600c5]: am-ex-manip-example #+Caption: Expression Manipulator Expansion #+Name: am-ex-manip-expansion #+BEGIN_SRC lisp :exports code (PROGN (PUSH '(FROB . FROBNICATE) *MANIPULATOR-MAP*) (DEFVAR *MANIPULATORS-FROBNICATE* 'NIL) (DEFUN FROBNICATE-IS-APPLICABLE-P (TYPES EXPRESSION-A EXPRESSION-B) (AND (CLASSIFIED-AS-P EXPRESSION-A (NTH 0 TYPES)) (CLASSIFIED-AS-P EXPRESSION-B (NTH 1 TYPES)))) (DEFUN GET-FROBNICATE-MANIPULATORS (EXPRESSION-A EXPRESSION-B) (REMOVE-IF #'NULL (MAP 'LIST #'(LAMBDA (OPTION) (LET ((TYPES (CAR OPTION)) (NAME (CDR OPTION))) (IF (FROBNICATE-IS-APPLICABLE-P TYPES EXPRESSION-A EXPRESSION-B) NAME))) *MANIPULATORS-FROBNICATE*))) (DEFUN FROBNICATE (EXPRESSION-A EXPRESSION-B) (FUNCALL (FIRST (GET-FROBNICATE-MANIPULATORS EXPRESSION-A EXPRESSION-B)) EXPRESSION-A EXPRESSION-B)) (DEFMACRO DEFINE-FROBNICATE-MANIPULATOR ((EXPRESSION-A-TYPE EXPRESSION-B-TYPE) &BODY BODY) (LET ((MANIPULATOR-NAME (SYMBOLICATE 'FROBNICATE-MANIPULATOR- EXPRESSION-A-TYPE EXPRESSION-B-TYPE))) `(PROGN (SETF ,'*MANIPULATORS-FROBNICATE* (APPEND ,'*MANIPULATORS-FROBNICATE* '(((,EXPRESSION-A-TYPE ,EXPRESSION-B-TYPE) ,@MANIPULATOR-NAME)))) (DEFUN ,MANIPULATOR-NAME ,'(EXPRESSION-A EXPRESSION-B) ,@BODY))))) #+END_SRC *** TODO External Manipulator :PROPERTIES: :CREATED: <2016-05-01 Sun 14:33> :ID: 6419490c-3cb0-47e4-840a-c20af4bfb3d7 :END: The Expression Manipulators should not be touched outside of this package, as they are not designed to be used outside of it. Instead, they should be used through this simple function. It takes an action and a list of expressions. The function used to perform the action correctly is determined, and used to reduce the expressions. #+Caption: External Manipulator #+Name: am-external-manipulator #+BEGIN_SRC lisp (defun manipulate (action &rest expressions) (let ((the-manipulator (cdr (assoc action *manipulator-map*)))) (reduce the-manipulator expressions))) #+END_SRC *** WORKING Addition :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: b794486c-e493-408f-b80c-a440edae1bc8 :END: Foo #+Caption: Addition Manipulator #+Name: am-addition-manipulator #+BEGIN_SRC lisp (define-operation add 2 +) (define-add-manipulator (numeric numeric) (+ expression-a expression-b)) (define-add-manipulator (numeric additive) (let ((total expression-a) (remainder (rest expression-b)) (non-numeric '())) (dolist (element remainder) (if (classified-as-p element 'numeric) (incf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total ,@non-numeric))))) (define-add-manipulator (additive additive) (let ((total 0) (elements (append (rest expression-a) (rest expression-b))) (non-numeric '())) (dolist (element elements) (if (classified-as-p element 'numeric) (incf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total ,@non-numeric))))) (define-add-manipulator (numeric subtractive) (let ((total expression-a) (the-other (rest expression-b)) (non-numeric '())) (dolist (element the-other) (if (classified-as-p element 'numeric) (decf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(+ ,@non-numeric)) (t `(+ ,total (-,@non-numeric)))))) (define-add-manipulator (numeric polynomial-term) `(+ ,expression-a ,expression-b)) (define-add-manipulator (polynomial-term polynomial-term) (if (single-term-combinable-p expression-a expression-b) (let ((new-coefficient (+ (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-a)) (power (get-power expression-a))) `(* ,new-coefficient (expt ,variable ,power))) `(+ ,expression-a ,expression-b))) (define-add-manipulator (* numeric) (add expression-b expression-a)) #+END_SRC *** WORKING Subtraction :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: f675fd81-e995-41ee-9570-cc78261d9dc1 :END: Foo #+Caption: Subtraction Manipulator #+Name: am-subtraction-manipulator #+BEGIN_SRC lisp (define-operation subtract 2 -) (define-subtract-manipulator (numeric numeric) (- expression-a expression-b)) (define-subtract-manipulator (numeric subtractive) (let ((total expression-a) (elements (rest expression-b)) (non-numeric '())) (dolist (element elements) (if (classified-as-p element 'numeric) (decf total element) (push element non-numeric))) (cond ((null non-numeric) total) ((= 0 total) `(- ,@(reverse non-numeric))) (t `(- ,total ,@(reverse non-numeric)))))) (define-subtract-manipulator (* numeric) (subtract expression-b expression-a)) #+END_SRC *** WORKING Multiplication :PROPERTIES: :CREATED: <2016-04-30 Sat 23:08> :ID: cddffdaa-10dd-425f-9697-3f0617162953 :END: Foo #+Caption: Multiplication Manipulators #+Name: am-multiplication-manipulators #+BEGIN_SRC lisp (define-operation multiply 2 *) (define-multiply-manipulator (numeric numeric) (* expression-a expression-b)) (define-multiply-manipulator (numeric polynomial-term) (let ((new-coefficient (* expression-a (coefficient expression-b))) (variable (term-variable expression-b)) (power (get-power expression-b))) (if (= 1 power) `(* ,new-coefficient ,variable) `(* ,new-coefficient (expt ,variable ,power))))) (define-multiply-manipulator (polynomial-term polynomial-term) (let ((new-coefficient (* (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-b)) (power (+ (get-power expression-a) (get-power expression-b)))) `(* ,new-coefficient (expt ,variable ,power)))) #+END_SRC *** WORKING Division :PROPERTIES: :CREATED: <2016-04-30 Sat 23:09> :ID: 4c4f7034-555a-46b0-85b9-56a08cf48f9b :END: Foo #+Caption: Division Manipulators #+Name: am-division-manipulators #+BEGIN_SRC lisp (define-operation division 2 /) (define-division-manipulator (numeric numeric) (/ expression-a expression-b)) (define-division-manipulator (polynomial-term polynomial-term) (let ((new-coefficient (/ (coefficient expression-a) (coefficient expression-b))) (variable (term-variable expression-b)) (power (- (get-power expression-a) (get-power expression-b)))) `(* ,new-coefficient (expt ,variable ,power)))) #+END_SRC *** WORKING Trigonometric [0/6] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:09> :ID: ba4acf37-9074-429b-a2c8-a23094e1c86b :END: Foo #+Caption: Trigonometric Manipulators #+Name: am-trigonometric-manipulators #+BEGIN_SRC lisp <> <> <> <> <> <> #+END_SRC **** WORKING Sine :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: c733c6b3-a44a-488f-8b6e-38346830b257 :END: Foo #+Caption: Sine Manipulators #+Name: am-sine-manipulators #+BEGIN_SRC lisp (define-operation sine 1 sin) (define-sine-manipulator (numeric) (sin expression-a)) #+END_SRC **** WORKING Cosine :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: c2fbd362-6932-4483-8270-e3ad72a308fd :END: Foo #+Caption: Cosine Manipulators #+Name: am-cosine-manipulators #+BEGIN_SRC lisp (define-operation cosine 1 cos) (define-cosine-manipulator (numeric) (cosine expression-a)) #+END_SRC **** WORKING Tangent :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: 07222206-1c22-411e-a8ab-13e1a627e9ef :END: Foo #+Caption: Tangent Manipulators #+Name: am-tangent-manipulators #+BEGIN_SRC lisp (define-operation tangent 1 tan) (define-tangent-manipulator (numeric) (tan expression-a)) #+END_SRC **** WORKING Cosecant :PROPERTIES: :CREATED: <2016-05-08 Sun 16:22> :ID: e77c0317-7281-45ff-b86b-8d66fb8c38ef :END: Foo #+Caption: Cosecant Manipulators #+Name: am-cosecant-manipulators #+BEGIN_SRC lisp (define-operation cosecant 1 csc) #+END_SRC **** WORKING Secant :PROPERTIES: :CREATED: <2016-05-08 Sun 16:23> :ID: 6c377c7d-ec84-4fcf-be94-db89b832c2d8 :END: Foo #+Caption: Secant Manipulators #+Name: am-secant-manipulators #+BEGIN_SRC lisp (define-operation secant 1 sec) #+END_SRC **** WORKING Cotangent :PROPERTIES: :CREATED: <2016-05-08 Sun 16:23> :ID: 70a9fb76-7ca7-4c7d-b25b-0fa94d390b6c :END: Foo #+Caption: Cotangent Manipulators #+Name: am-cotangent-manipulators #+BEGIN_SRC lisp (define-operation cotangent 1 cot) #+END_SRC ** DONE Assembly CLOSED: [2016-06-18 Sat 13:38] :PROPERTIES: :CREATED: <2016-04-30 Sat 23:07> :ID: d487ed31-295b-4274-aef2-b45e4fa7bec2 :END: This is the assembly of the ~#:larcs.manipulate~ package. It includes, in correct order, all bits of functionality. It places all of this in the ~larcs-manipulation.lisp~ file. #+Caption: Packaging #+Name: am-packaging #+BEGIN_SRC lisp :tangle "larcs-manipulation.lisp" (in-package #:larcs.manipulate) <> <> <> <> <> #+END_SRC * WORKING Symbolic Solver [0/3] :PROPERTIES: :CREATED: <2016-06-11 Sat 17:55> :ID: ffa664b6-e147-471c-9e97-f49c4619fc99 :END: ** TODO Techniques :PROPERTIES: :CREATED: <2016-06-11 Sat 17:55> :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 17:56> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 17:56> :END: * WORKING Symbolic Trigonometry [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :ID: b0259b8c-e285-48ca-bda0-4f04e3bc6ef2 :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 17:58> :END: * WORKING Symbolic Differentiation [0/4] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:45> :ID: 552f402a-a25d-4f28-94af-17934c38a529 :END: ** WORKING Rule Definition [0/3] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:51> :END: *** TODO Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 22:51> :ID: de915ee7-47bd-4f7f-ad06-39f0201a4651 :END: #+Caption: Rule Definition #+Name: sd-rule-definition #+BEGIN_SRC lisp (defmacro define-derivative (expression-type (&rest arguments-list) &body body) (let ((expansion-name (symbolicate expression-type '-expansion))) `(progn (when (not (member ',expression-type (mapcar #'car *rules*))) (setq *rules* (append *rules* '((,expression-type . ,expansion-name))))) (defun ,expansion-name (,@arguments-list) ,@body)))) #+END_SRC *** TODO Retrieval :PROPERTIES: :CREATED: <2016-06-13 Mon 23:08> :ID: 97d8b24e-dd75-4919-a953-cba8035cb691 :END: #+Caption: Rule Retrieval #+Name: sd-rule-retrieval #+BEGIN_SRC lisp (defun get-rule (expression) (cdr (first (remove-if #'(lambda (pair) (let ((type (first pair))) (not (classified-as-p expression type)))) ,*rules*)))) #+END_SRC *** TODO Storage :PROPERTIES: :CREATED: <2016-06-13 Mon 22:52> :ID: 372dc2d7-ee67-4eba-a9f7-3633eaf0996e :END: #+Caption: Rule Storage #+Name: sd-rule-storage #+BEGIN_SRC lisp (defvar *rules* '()) #+END_SRC ** WORKING Rules [0/9] :PROPERTIES: :CREATED: <2016-06-13 Mon 22:52> :ID: fdcebadd-b53d-4f59-99a4-4a3782e017a2 :END: #+Caption: Rules #+Name: sd-rules #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> #+END_SRC *** TODO Numbers :PROPERTIES: :CREATED: <2016-06-13 Mon 23:18> :ID: bb1f9175-2e86-43a3-94b3-9467d233539c :END: #+Caption: Numbers #+Name: sd-numbers #+BEGIN_SRC lisp (define-derivative numeric (&rest junk) (declare (ignorable junk)) 0) #+END_SRC *** TODO Variables :PROPERTIES: :CREATED: <2016-06-13 Mon 23:19> :ID: ecc17ca3-2989-4908-aded-4b6e20b1855c :END: #+Caption: Variables #+Name: sd-variables #+BEGIN_SRC lisp (define-derivative variable (&rest junk) (declare (ignorable junk)) 1) #+END_SRC *** TODO Polynomial Terms :PROPERTIES: :CREATED: <2016-06-13 Mon 23:33> :ID: 6ca719d7-b584-4ae6-ae44-23bed186c6e9 :END: #+Caption: Polynomial Terms #+Name: sd-polynomial-terms #+BEGIN_SRC lisp (define-derivative polynomial-term (&rest term) (let* ((coefficient (coefficient term)) (variable (term-variable term)) (power (get-power term))) (cond ((= 1 power) coefficient) ((= 2 power) `(* ,(* coefficient power) ,variable)) (t `(* ,(* coefficient power) (expt ,variable ,(1- power))))))) #+END_SRC *** TODO Multiplicatives :PROPERTIES: :CREATED: <2016-06-14 Tue 09:57> :ID: 161906a4-5c14-4a84-bf1d-7fae9e20b14f :END: #+Caption: Multiplicatives #+Name: sd-multiplicatives #+BEGIN_SRC lisp (define-derivative multiplicative (function first &rest rest) (declare (ignore function)) (if (= 1 (length rest)) (let ((second (first rest))) (cond ((and (classified-as-p first 'numeric) (classified-as-p second 'numeric)) (* first second)) ((classified-as-p first 'numeric) `(* ,first ,(differentiate second))) ((classified-as-p second 'numeric) `(* ,second ,(differentiate first))) (t `(+ (* ,first ,(differentiate second)) (* ,second ,(differentiate first)))))) (differentiate `(* ,first (* ,@rest))))) #+END_SRC *** TODO Rationals :PROPERTIES: :CREATED: <2016-06-14 Tue 10:21> :ID: cd681a61-a143-4e02-a6a9-e7b8f9b9c77d :END: #+Caption: Rational Derivatives #+Name: sd-rationals #+BEGIN_SRC lisp (define-derivative rational (function numerator denominator) (declare (ignore function)) `(/ (- (* ,numerator ,(differentiate denominator)) (* ,denominator ,(differentiate numerator))) (expt ,denominator 2))) #+END_SRC *** TODO Additives :PROPERTIES: :CREATED: <2016-06-14 Tue 10:30> :ID: d3a07d51-977c-4b1e-9a63-0eb415977f46 :END: #+Caption: Additives #+Name: sd-additives #+BEGIN_SRC lisp (define-derivative additive (function &rest terms) (declare (ignore function)) `(+ ,@(map 'list #'(lambda (term) (differentiate term)) terms))) #+END_SRC *** TODO Subtractives :PROPERTIES: :CREATED: <2016-06-14 Tue 10:30> :ID: 063f61ee-6fd9-4286-9008-9c80ef0985a5 :END: #+Caption: Subtractives #+Name: sd-subtractives #+BEGIN_SRC lisp (define-derivative subtractive (function &rest terms) (declare (ignore function)) `(- ,@(map 'list #'(lambda (term) (differentiate term)) terms))) #+END_SRC *** TODO Exponentials and Logarithmics :PROPERTIES: :CREATED: <2016-06-14 Tue 10:37> :END: #+Caption: Exponentials and Logarithms #+Name: sd-exponentials-and-logarithms #+BEGIN_SRC lisp (define-derivative natural-exponential (function expression) (declare (ignore function)) `(exp ,expression)) (define-derivative exponential (function base power) (declare (ignore function)) (if (numberp power) (if (listp base) `(* ,power (expt ,base ,(1- power)) ,(differentiate base)) `(* ,power (expt ,base ,(1- power)))) `(* (expt ,base ,power) (log ,base)))) (define-derivative natural-logarithmic (function expression) (declare (ignore function)) `(/ ,(differentiate expression) ,expression)) (define-derivative logarithmic (function number base) (declare (ignore function)) `(/ ,(differentiate (cons 'log number)) (* (log ,base) ,number))) #+END_SRC *** TODO Trigonometric Functions :PROPERTIES: :CREATED: <2016-06-14 Tue 10:45> :END: #+Caption: Trigonometric Functions #+Name: sd-trigonometric-functions #+BEGIN_SRC lisp (define-derivative sin (function expression) (declare (ignore function)) `(* ,(differentiate expression) (cos ,expression))) (define-derivative cos (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (sin ,expression)))) (define-derivative tan (function expression) (declare (ignore function)) `(* ,(differentiate expression) (expt (sec ,expression) 2))) (define-derivative csc (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (csc ,expression)) (cot ,expression))) (define-derivative cot (function expression) (declare (ignore function)) `(* ,(differentiate expression) (- (expt (csc ,expression) 2)))) #+END_SRC ** TODO Driver :PROPERTIES: :CREATED: <2016-06-13 Mon 22:59> :ID: b40ed5ad-2eb7-43b1-bab7-39592894e5be :END: #+Caption: Derivative Driver #+Name: sd-derivative-driver #+BEGIN_SRC lisp (defun differentiate (function) (let ((rule (get-rule function))) (when rule (apply rule (ensure-list function))))) #+END_SRC ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-13 Mon 22:46> :ID: d87d49e3-8245-4ff0-aaf0-57b9e19edeba :END: #+Caption: Symbolic Differentiation #+Name: sd-symbolic-differentiation #+BEGIN_SRC lisp :tangle "larcs-differentiate.lisp" (in-package #:larcs.differentiate) <> <> <> <> <> #+END_SRC * WORKING Symbolic Integration [0/3] :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :ID: 61add971-a129-40d0-be13-24f8a4afc522 :END: ** TODO Rules :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: ** TODO Techniques :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-11 Sat 18:02> :END: * WORKING Symbolic To Typeset Form [0/5] :PROPERTIES: :CREATED: <2016-06-14 Tue 17:13> :ID: 75f65e8a-0cc9-477f-b5e9-3c563fe7ef5c :END: ** WORKING Rule Management [0/3] :PROPERTIES: :CREATED: <2016-06-14 Tue 17:17> :END: *** TODO Define Rules :PROPERTIES: :CREATED: <2016-06-14 Tue 17:18> :ID: ec6fdb0d-546e-41fc-a7b7-5fbbfe4b7931 :END: #+Caption: Rule Definition #+Name: stf-define-rule #+BEGIN_SRC lisp (defmacro define-converter (expression-type (&rest arguments-list) &body body) (let ((expansion-name (symbolicate expression-type '-conversion))) `(progn (when (not (member ',expression-type (mapcar #'car *rules*))) (setq *rules* (append *rules* '((,expression-type . ,expansion-name))))) (defun ,expansion-name (,@arguments-list) ,@body)))) #+END_SRC *** TODO Rule Retrieval :PROPERTIES: :CREATED: <2016-06-14 Tue 17:18> :ID: 0c34c744-7847-46c2-bdef-228feee7c84e :END: #+Caption: Rule Retrieval #+Name: stf-rule-retrieval #+BEGIN_SRC lisp (defun get-rule (expression) (cdr (first (remove-if #'(lambda (pair) (let ((type (first pair))) (not (classified-as-p expression type)))) ,*rules*)))) #+END_SRC *** TODO Rule Storage :PROPERTIES: :CREATED: <2016-06-14 Tue 17:18> :ID: 2a75f850-7f42-47b1-91fa-7b6b467c3ea4 :END: #+Caption: Rule Storage #+Name: stf-rule-storage #+BEGIN_SRC lisp (defvar *rules* '()) #+END_SRC ** WORKING Rules [0/9] :PROPERTIES: :CREATED: <2016-06-14 Tue 17:18> :END: #+Caption: Rules #+Name: stf-rules #+BEGIN_SRC lisp <> <> <> <> <> <> <> <> <> #+END_SRC *** TODO Numbers :PROPERTIES: :CREATED: <2016-06-14 Tue 17:22> :END: #+Caption: Numerics #+Name: stf-numerics #+BEGIN_SRC lisp (define-converter numeric (number) (with-tex-output (format nil "{~A}" number))) #+END_SRC *** TODO Variables :PROPERTIES: :CREATED: <2016-06-14 Tue 17:22> :END: #+Caption: Variables #+Name: stf-variables #+BEGIN_SRC lisp (define-converter variable (var) (if (member var *constant-names*) (with-tex-output (format nil "{~A}" (cdr (assoc var *special-symbols-to-sequences*)))) (with-tex-output (format nil "{~A}" (string-downcase var))))) #+END_SRC *** TODO Polynomial Terms :PROPERTIES: :CREATED: <2016-06-14 Tue 17:23> :END: #+Caption: Polynomial Terms #+Name: stf-polynomial-terms #+BEGIN_SRC lisp (define-converter polynomial-term (&rest term) (let ((variable (term-variable term)) (coefficient (coefficient term)) (power (get-power term))) (cond ((= 1 power) (with-tex-output (format nil "{~A}{~A}" (convert-for-display coefficient) (convert-for-display power)))) ((= 0 coefficient) (with-tex-output (format nil "{~A}^{~A}" (convert-for-display variable) (convert-for-display power)))) (t (with-tex-output (format nil "{~A}{~A}^{~A}" (convert-for-display coefficient) (convert-for-display variable) (convert-for-display power))))))) #+END_SRC *** TODO Multiplicatives :PROPERTIES: :CREATED: <2016-06-14 Tue 17:23> :END: #+Caption: Multiplicatives #+Name: stf-multiplicatives #+BEGIN_SRC lisp (define-converter multiplicative (op &rest elements) (declare (ignore op)) (with-tex-output (format nil "{~{~A~^ \\cdot ~}}" (mapcar #'convert-for-display elements)))) #+END_SRC *** TODO Rationals :PROPERTIES: :CREATED: <2016-06-14 Tue 17:23> :END: #+Caption: Rationals #+Name: stf-rationals #+BEGIN_SRC lisp (define-converter rational (op numerator denominator) (declare (ignore op)) (with-tex-output (format nil "{\\frac{~A}{~A}}" (convert-for-display numerator) (convert-for-display denominator)))) #+END_SRC *** TODO Additives :PROPERTIES: :CREATED: <2016-06-14 Tue 17:23> :END: #+Caption: Additives #+Name: stf-additives #+BEGIN_SRC lisp (define-converter additive (op &rest terms) (declare (ignore op)) (with-tex-output (format nil "{~{~A~^ + ~}}" (mapcar #'convert-for-display terms)))) #+END_SRC *** TODO Subtractives :PROPERTIES: :CREATED: <2016-06-14 Tue 17:23> :END: #+Caption: Subtractives #+Name: stf-subtractives #+BEGIN_SRC lisp (define-converter subtractive (op &rest terms) (declare (ignore op)) (with-tex-output (format nil "{~{~A~^ - ~}}" (mapcar #'convert-for-display terms)))) #+END_SRC *** TODO Trigonometrics :PROPERTIES: :CREATED: <2016-06-14 Tue 18:38> :END: #+Caption: Trigonometrics #+Name: stf-trigonometrics #+BEGIN_SRC lisp (define-converter sin (op term) (declare (ignore op)) (with-tex-output (format nil "{\\sin {~A}}" (convert-for-display term)))) (define-converter cos (op term) (declare (ignore op)) (with-tex-output (format nil "{\\cos {~A}}" (convert-for-display term)))) (define-converter tan (op term) (declare (ignore op)) (with-tex-output (format nil "{\\tan {~A}}" (convert-for-display term)))) (define-converter csc (op term) (declare (ignore op)) (with-tex-output (format nil "{\\csc {~A}}" (convert-for-display term)))) (define-converter sec (op term) (declare (ignore op)) (with-tex-output (format nil "{\\sec {~A}}" (convert-for-display term)))) (define-converter cot (op term) (declare (ignore op)) (with-tex-output (format nil "{\\cot {~A}}" (convert-for-display term)))) #+END_SRC *** TODO Exponentials and Logarithmics :PROPERTIES: :CREATED: <2016-06-14 Tue 17:24> :END: #+Caption: Exponentials and Logarithmics #+Name: stf-exponentials-logarithmics #+BEGIN_SRC lisp (define-converter natural-exponential (op term) (declare (ignore op)) (with-tex-output (format nil "{e^~A}" (convert-for-display term)))) (define-converter exponential (op base power) (declare (ignore op)) (with-tex-output (format nil "{~A^~A}" (convert-for-display base) (convert-for-display power)))) (define-converter natural-logarithmic (op term) (declare (ignore op)) (with-tex-output (format nil "{\\ln ~A}" (convert-for-display term)))) (define-converter logarithmic (op term base) (declare (ignore op)) (with-tex-output (format nil "{\\log_~a ~a}" (convert-for-display base) (convert-for-display term)))) #+END_SRC ** WORKING Converter [0/7] :PROPERTIES: :CREATED: <2016-06-14 Tue 17:18> :ID: 88d433ad-e381-4747-8a29-2d78bc759fbf :END: #+Caption: Conversion Driver #+Name: stf-conversion-driver #+BEGIN_SRC lisp (defun convert-for-display (function) (if (and (listp function) (member (first function) '(and or not = sum integrate parens))) (let ((operator (first function))) (cond ((eq operator 'and) <> ) ((eq operator 'or) <> ) ((eq operator 'not) <> ) ((eq operator '=) <> ) ((eq operator 'sum) <> ) ((eq operator 'integrate) <> ) ((eq operator 'parens) <> ))) (let ((rule (get-rule function))) (when rule (apply rule (ensure-list function)))))) #+END_SRC *** TODO And :PROPERTIES: :CREATED: <2016-06-14 Tue 17:38> :ID: 733b98a1-90f1-4d13-abe8-cb86a5608aee :END: Foo #+Caption: And Operator #+Name: stf-and-operator #+BEGIN_SRC lisp (destructuring-bind (op &rest terms) function (declare (ignore op)) (with-tex-output (format nil "{~{~A~^ \\wedge ~}}" (mapcar #'convert-for-display terms)))) #+END_SRC *** TODO Or :PROPERTIES: :CREATED: <2016-06-14 Tue 17:38> :ID: 276de305-32c4-4f79-96e7-d0a99ff24f78 :END: Foo #+Caption: Or Operator #+Name: stf-or-operator #+BEGIN_SRC lisp (destructuring-bind (op &rest terms) function (declare (ignore op)) (with-tex-output (format nil "{~{~A~^ \\vee ~}}" (mapcar #'convert-for-display terms)))) #+END_SRC *** TODO Not :PROPERTIES: :CREATED: <2016-06-14 Tue 17:38> :ID: 1b0a28a4-744d-44d1-a328-7b2bb10bd0c7 :END: Foo #+Caption: Not Operator #+Name: stf-not-operator #+BEGIN_SRC lisp (destructuring-bind (op term) function (with-tex-output (format nil "{\\not ~A}" (convert-for-display term)))) #+END_SRC *** TODO Equality :PROPERTIES: :CREATED: <2016-06-14 Tue 17:41> :ID: 4ce4835c-e196-4494-ab4b-591690e4164c :END: Foo #+Caption: Equality Operator #+Name: stf-equality-operator #+BEGIN_SRC lisp (destructuring-bind (op lhs rhs) function (declare (ignore op)) (format nil "{~A = ~A}" (convert-for-display lhs) (convert-for-display rhs))) #+END_SRC *** TODO Summation :PROPERTIES: :CREATED: <2016-06-14 Tue 17:24> :ID: 98404213-b8b8-410f-b660-23b701518cea :END: #+Caption: Summation #+Name: stf-summation #+BEGIN_SRC lisp (destructuring-bind (op start stop expression) function (declare (ignore op)) (format nil "{\sum_~A^~A ~A}" (convert-for-display start) (convert-for-display stop) (convert-for-display expression))) #+END_SRC *** TODO Integration :PROPERTIES: :CREATED: <2016-06-14 Tue 17:39> :ID: 60c16d30-2bb3-497c-aaa0-4529ecfc523c :END: #+Caption: Integration #+Name: stf-integration #+BEGIN_SRC lisp (destructuring-bind (op from to expression wrt) function (declare (ignore op)) (with-tex-output (format nil "{\\int_~A^~A ~A\\,\\mathrm{d}~A}" (convert-for-display from) (convert-for-display to) (convert-for-display expression) (convert-for-display wrt)))) #+END_SRC *** TODO Parenthesis :PROPERTIES: :CREATED: <2016-06-14 Tue 17:24> :ID: 93d643d6-2219-4c49-bba5-190520a6ff29 :END: #+Caption: Parenthesis #+Name: stf-parenthesis #+BEGIN_SRC lisp (destructuring-bind (op type expression) function (declare (ignore op)) (let* ((types '((square . ("[" . "]")) (curly . ("{" . "}")) (smooth . ("(" . ")")))) (left (cadr (assoc type types))) (right (cddr (assoc type types)))) (with-tex-output (format nil "{\\left~a {~a} \\right~a}" left (convert-for-display expression) right)))) #+END_SRC ** TODO Special Macros :PROPERTIES: :CREATED: <2016-06-14 Tue 17:20> :ID: 56ca6afe-912a-4530-91e4-a63123dc6d9d :END: #+Caption: Special Macros #+Name: stf-special-macros #+BEGIN_SRC lisp (defvar *tex-outputp* nil) (declaim (special *tex-outputp*)) (defmacro with-tex-output (&body body) `(if *tex-outputp* (progn ,@body) (let ((*tex-outputp* t)) (format nil "$~a$" (progn ,@body))))) #+END_SRC ** TODO Assembly :PROPERTIES: :CREATED: <2016-06-14 Tue 17:15> :ID: bbd15b88-8256-4b5b-abcc-4783fc096c29 :END: #+Caption: Assemble Symbolic to Typeset Form #+Name: stf-assemble #+BEGIN_SRC lisp :tangle "larcs-typeset.lisp" (in-package #:larcs.typeset) <> <> <> <> <> #+END_SRC * WORKING Library Assembly [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:30> :ID: 89370949-8f58-41cf-8c4f-92f81d48ac23 :END: ** TODO Package Definitions :PROPERTIES: :CREATED: <2016-06-13 Mon 15:00> :ID: 573a8352-8cbe-408c-8c27-3cf0b66da885 :END: #+Caption: LARCS Packages #+Name: larcs-packages #+BEGIN_SRC lisp :tangle "larcs-packages.lisp" (defpackage #:larcs.common (:use #:cl) (:import-from #:alexandria #:symbolicate) (:export #:generate-match-expression #:gen-args-list #:*special-symbols-to-sequences* #:*constant-names*)) (defpackage #:larcs.classify (:use #:cl #:larcs.common) (:import-from #:alexandria #:symbolicate) (:export #:classify #:classified-as-p #:classification-case)) (defpackage #:larcs.manipulate (:use #:cl #:larcs.common #:larcs.classify) (:import-from #:alexandria #:symbolicate) (:export #:manipulate #:collect-variables #:collect-terms #:coefficient #:term-variable #:get-power #:same-order-p #:save-variable-p #:single-term-combinable-p)) (defpackage #:larcs.differentiate (:use #:cl #:larcs.common #:larcs.classify #:larcs.manipulate) (:import-from #:alexandria #:symbolicate) (:import-from #:com.informatimago.common-lisp.cesarum.list #:aget #:ensure-list) (:export :differentiate)) (defpackage #:larcs.typeset (:use #:cl #:larcs.common #:larcs.classify #:larcs.manipulate) (:import-from #:alexandria #:symbolicate) (:import-from #:com.informatimago.common-lisp.cesarum.list #:aget #:ensure-list) (:export #:convert-for-display)) #+END_SRC ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 15:00> :ID: 35b2ec01-a933-4b5b-af73-b6b7f1c45cb6 :END: #+Caption: Library System Definition #+Name: library-system-definition #+BEGIN_SRC lisp :tangle "larcs-lib.asd" (asdf:defsystem #:larcs-lib :description "A CAS Library for use within Lisp Software." :author "Samuel Flint " :license "GNU GPLv3 or Later" :depends-on (#:alexandria #:com.informatimago) :serial t :components ((:file "larcs-packages") (:file "larcs-common") (:file "larcs-classify") (:file "larcs-manipulation") (:file "larcs-differentiate") (:file "larcs-typeset"))) #+END_SRC * WORKING Text User Interface [0/2] :PROPERTIES: :CREATED: <2016-06-11 Sat 22:31> :ID: 65c8092c-1f7a-44e1-93d5-8d39e179f447 :END: ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: #+Caption: Text User System Definition #+Name: text-ui-system-definition #+BEGIN_SRC lisp '(#:alexandria #:command-line-arguments #:cl-readline) #+END_SRC ** TODO Functionality :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: * WORKING Graphical User Interface [0/3] :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :ID: b70027f8-a665-4282-ab4b-3d0ce6bd8d17 :END: ** TODO System Definition :PROPERTIES: :CREATED: <2016-06-13 Mon 14:51> :END: #+Caption: GUI System Definition #+Name: gui-system-definition #+BEGIN_SRC lisp '(#:alexandria #:command-line-arguments #:commonqt) #+END_SRC ** TODO Interface Elements :PROPERTIES: :CREATED: <2016-06-13 Mon 14:52> :END: ** TODO Interface Functionality :PROPERTIES: :CREATED: <2016-06-13 Mon 14:52> :END: