Next: , Previous: Vector Lisp Functions, Up: Internals

17.5.7.7 Symbolic Functions

The functions described here operate on symbolic formulas in the Calculator.

— Function: calc-prepare-selection num

Prepare a stack entry for selection operations. If num is omitted, the stack entry containing the cursor is used; otherwise, it is the number of the stack entry to use. This function stores useful information about the current stack entry into a set of variables. calc-selection-cache-num contains the number of the stack entry involved (equal to num if you specified it); calc-selection-cache-entry contains the stack entry as a list (such as calc-top-list would return with entry as the selection mode); and calc-selection-cache-comp contains a special “tagged” composition (see Formatting Lisp Functions) which allows Calc to relate cursor positions in the buffer with their corresponding sub-formulas.

A slight complication arises in the selection mechanism because formulas may contain small integers. For example, in the vector ‘[1, 2, 1]’ the first and last elements are eq to each other; selections are recorded as the actual Lisp object that appears somewhere in the tree of the whole formula, but storing 1 would falsely select both 1's in the vector. So calc-prepare-selection also checks the stack entry and replaces any plain integers with “complex number” lists of the form ‘(cplx n 0)’. This list will be displayed the same as a plain n and the change will be completely invisible to the user, but it will guarantee that no two sub-formulas of the stack entry will be eq to each other. Next time the stack entry is involved in a computation, calc-normalize will replace these lists with plain numbers again, again invisibly to the user.

— Function: calc-encase-atoms x

This modifies the formula x to ensure that each part of the formula is a unique atom, using the ‘(cplx n 0)’ trick described above. This function may use setcar to modify the formula in-place.

— Function: calc-find-selected-part

Find the smallest sub-formula of the current formula that contains the cursor. This assumes calc-prepare-selection has been called already. If the cursor is not actually on any part of the formula, this returns nil.

— Function: calc-change-current-selection selection

Change the currently prepared stack element's selection to selection, which should be eq to some sub-formula of the stack element, or nil to unselect the formula. The stack element's appearance in the Calc buffer is adjusted to reflect the new selection.

— Function: calc-find-nth-part expr n

Return the nth sub-formula of expr. This function is used by the selection commands, and (unless j b has been used) treats sums and products as flat many-element formulas. Thus if expr is ‘((a + b) - c) + d’, calling calc-find-nth-part with n equal to four will return ‘d’.

— Function: calc-find-parent-formula expr part

Return the sub-formula of expr which immediately contains part. If expr is ‘a*b + (c+1)*d’ and part is eq to the ‘c+1’ term of expr, then this function will return ‘(c+1)*d’. If part turns out not to be a sub-formula of expr, the function returns nil. If part is eq to expr, the function returns t. This function does not take associativity into account.

— Function: calc-find-assoc-parent-formula expr part

This is the same as calc-find-parent-formula, except that (unless j b has been used) it continues widening the selection to contain a complete level of the formula. Given ‘a’ from ‘((a + b) - c) + d’, calc-find-parent-formula will return ‘a + b’ but calc-find-assoc-parent-formula will return the whole expression.

— Function: calc-grow-assoc-formula expr part

This expands sub-formula part of expr to encompass a complete level of the formula. If part and its immediate parent are not compatible associative operators, or if j b has been used, this simply returns part.

— Function: calc-find-sub-formula expr part

This finds the immediate sub-formula of expr which contains part. It returns an index n such that ‘(calc-find-nth-part expr n)’ would return part. If part is not a sub-formula of expr, it returns nil. If part is eq to expr, it returns t. This function does not take associativity into account.

— Function: calc-replace-sub-formula expr old new

This function returns a copy of formula expr, with the sub-formula that is eq to old replaced by new.

— Function: simplify expr

Simplify the expression expr by applying Calc's algebraic simplifications. This always returns a copy of the expression; the structure expr points to remains unchanged in memory.

More precisely, here is what simplify does: The expression is first normalized and evaluated by calling normalize. If any AlgSimpRules have been defined, they are then applied. Then the expression is traversed in a depth-first, bottom-up fashion; at each level, any simplifications that can be made are made until no further changes are possible. Once the entire formula has been traversed in this way, it is compared with the original formula (from before the call to normalize) and, if it has changed, the entire procedure is repeated (starting with normalize) until no further changes occur. Usually only two iterations are needed: one to simplify the formula, and another to verify that no further simplifications were possible.

— Function: simplify-extended expr

Simplify the expression expr, with additional rules enabled that help do a more thorough job, while not being entirely “safe” in all circumstances. (For example, this mode will simplify ‘sqrt(x^2)’ to ‘x’, which is only valid when x is positive.) This is implemented by temporarily binding the variable math-living-dangerously to t (using a let form) and calling simplify. Dangerous simplification rules are written to check this variable before taking any action.

— Function: simplify-units expr

Simplify the expression expr, treating variable names as units whenever possible. This works by binding the variable math-simplifying-units to t while calling simplify.

— Macro: math-defsimplify funcs body

Register a new simplification rule; this is normally called as a top-level form, like defun or defmath. If funcs is a symbol (like + or calcFunc-sqrt), this simplification rule is applied to the formulas which are calls to the specified function. Or, funcs can be a list of such symbols; the rule applies to all functions on the list. The body is written like the body of a function with a single argument called expr. The body will be executed with expr bound to a formula which is a call to one of the functions funcs. If the function body returns nil, or if it returns a result equal to the original expr, it is ignored and Calc goes on to try the next simplification rule that applies. If the function body returns something different, that new formula is substituted for expr in the original formula.

At each point in the formula, rules are tried in the order of the original calls to math-defsimplify; the search stops after the first rule that makes a change. Thus later rules for that same function will not have a chance to trigger until the next iteration of the main simplify loop.

Note that, since defmath is not being used here, body must be written in true Lisp code without the conveniences that defmath provides. If you prefer, you can have body simply call another function (defined with defmath) which does the real work.

The arguments of a function call will already have been simplified before any rules for the call itself are invoked. Since a new argument list is consed up when this happens, this means that the rule's body is allowed to rearrange the function's arguments destructively if that is convenient. Here is a typical example of a simplification rule:

          (math-defsimplify calcFunc-arcsinh
            (or (and (math-looks-negp (nth 1 expr))
                     (math-neg (list 'calcFunc-arcsinh
                                     (math-neg (nth 1 expr)))))
                (and (eq (car-safe (nth 1 expr)) 'calcFunc-sinh)
                     (or math-living-dangerously
                         (math-known-realp (nth 1 (nth 1 expr))))
                     (nth 1 (nth 1 expr)))))

This is really a pair of rules written with one math-defsimplify for convenience; the first replaces ‘arcsinh(-x)’ with ‘-arcsinh(x)’, and the second, which is safe only for real ‘x’, replaces ‘arcsinh(sinh(x))’ with ‘x’.

— Function: common-constant-factor expr

Check expr to see if it is a sum of terms all multiplied by the same rational value. If so, return this value. If not, return nil. For example, if called on ‘6x + 9y + 12z’, it would return 3, since 3 is a common factor of all the terms.

— Function: cancel-common-factor expr factor

Assuming expr is a sum with factor as a common factor, divide each term of the sum by factor. This is done by destructively modifying parts of expr, on the assumption that it is being used by a simplification rule (where such things are allowed; see above). For example, consider this built-in rule for square roots:

          (math-defsimplify calcFunc-sqrt
            (let ((fac (math-common-constant-factor (nth 1 expr))))
              (and fac (not (eq fac 1))
                   (math-mul (math-normalize (list 'calcFunc-sqrt fac))
                             (math-normalize
                              (list 'calcFunc-sqrt
                                    (math-cancel-common-factor
                                     (nth 1 expr) fac)))))))
— Function: frac-gcd a b

Compute a “rational GCD” of a and b, which must both be rational numbers. This is the fraction composed of the GCD of the numerators of a and b, over the GCD of the denominators. It is used by common-constant-factor. Note that the standard gcd function uses the LCM to combine the denominators.

— Function: map-tree func expr many

Try applying Lisp function func to various sub-expressions of expr. Initially, call func with expr itself as an argument. If this returns an expression which is not equal to expr, apply func again until eventually it does return expr with no changes. Then, if expr is a function call, recursively apply func to each of the arguments. This keeps going until no changes occur anywhere in the expression; this final expression is returned by map-tree. Note that, unlike simplification rules, func functions may not make destructive changes to expr. If a third argument many is provided, it is an integer which says how many times func may be applied; the default, as described above, is infinitely many times.

— Function: compile-rewrites rules

Compile the rewrite rule set specified by rules, which should be a formula that is either a vector or a variable name. If the latter, the compiled rules are saved so that later compile-rules calls for that same variable can return immediately. If there are problems with the rules, this function calls error with a suitable message.

— Function: apply-rewrites expr crules heads

Apply the compiled rewrite rule set crules to the expression expr. This will make only one rewrite and only checks at the top level of the expression. The result nil if no rules matched, or if the only rules that matched did not actually change the expression. The heads argument is optional; if is given, it should be a list of all function names that (may) appear in expr. The rewrite compiler tags each rule with the rarest-looking function name in the rule; if you specify heads, apply-rewrites can use this information to narrow its search down to just a few rules in the rule set.

— Function: rewrite-heads expr

Compute a heads list for expr suitable for use with apply-rewrites, as discussed above.

— Function: rewrite expr rules many

This is an all-in-one rewrite function. It compiles the rule set specified by rules, then uses map-tree to apply the rules throughout expr up to many (default infinity) times.

— Function: match-patterns pat vec not-flag

Given a Calc vector vec and an uncompiled pattern set or pattern set variable pat, this function returns a new vector of all elements of vec which do (or don't, if not-flag is non-nil) match any of the patterns in pat.

— Function: deriv expr var value symb

Compute the derivative of expr with respect to variable var (which may actually be any sub-expression). If value is specified, the derivative is evaluated at the value of var; otherwise, the derivative is left in terms of var. If the expression contains functions for which no derivative formula is known, new derivative functions are invented by adding primes to the names; see Calculus. However, if symb is non-nil, the presence of nondifferentiable functions in expr instead cancels the whole differentiation, and deriv returns nil instead.

Derivatives of an n-argument function can be defined by adding a math-derivative-n property to the property list of the symbol for the function's derivative, which will be the function name followed by an apostrophe. The value of the property should be a Lisp function; it is called with the same arguments as the original function call that is being differentiated. It should return a formula for the derivative. For example, the derivative of ln is defined by

          (put 'calcFunc-ln\' 'math-derivative-1
               (function (lambda (u) (math-div 1 u))))

The two-argument log function has two derivatives,

          (put 'calcFunc-log\' 'math-derivative-2     ; d(log(x,b)) / dx
               (function (lambda (x b) ... )))
          (put 'calcFunc-log\'2 'math-derivative-2    ; d(log(x,b)) / db
               (function (lambda (x b) ... )))
— Function: tderiv expr var value symb

Compute the total derivative of expr. This is the same as deriv, except that variables other than var are not assumed to be constant with respect to var.

— Function: integ expr var low high

Compute the integral of expr with respect to var. See Calculus, for further details.

— Macro: math-defintegral funcs body

Define a rule for integrating a function or functions of one argument; this macro is very similar in format to math-defsimplify. The main difference is that here body is the body of a function with a single argument u which is bound to the argument to the function being integrated, not the function call itself. Also, the variable of integration is available as math-integ-var. If evaluation of the integral requires doing further integrals, the body should call ‘(math-integral x)’ to find the integral of x with respect to math-integ-var; this function returns nil if the integral could not be done. Some examples:

          (math-defintegral calcFunc-conj
            (let ((int (math-integral u)))
              (and int
                   (list 'calcFunc-conj int))))
          
          (math-defintegral calcFunc-cos
            (and (equal u math-integ-var)
                 (math-from-radians-2 (list 'calcFunc-sin u))))

In the cos example, we define only the integral of ‘cos(x) dx’, relying on the general integration-by-substitution facility to handle cosines of more complicated arguments. An integration rule should return nil if it can't do the integral; if several rules are defined for the same function, they are tried in order until one returns a non-nil result.

— Macro: math-defintegral-2 funcs body

Define a rule for integrating a function or functions of two arguments. This is exactly analogous to math-defintegral, except that body is written as the body of a function with two arguments, u and v.

— Function: solve-for lhs rhs var full

Attempt to solve the equation ‘lhs = rhs’ by isolating the variable var on the lefthand side; return the resulting righthand side, or nil if the equation cannot be solved. The variable var must appear at least once in lhs or rhs. Note that the return value is a formula which does not contain var; this is different from the user-level solve and finv functions, which return a rearranged equation or a functional inverse, respectively. If full is non-nil, a full solution including dummy signs and dummy integers will be produced. User-defined inverses are provided as properties in a manner similar to derivatives:

          (put 'calcFunc-ln 'math-inverse
               (function (lambda (x) (list 'calcFunc-exp x))))

This function can call ‘(math-solve-get-sign x)’ to create a new arbitrary sign variable, returning x times that sign, and ‘(math-solve-get-int x)’ to create a new arbitrary integer variable multiplied by x. These functions simply return x if the caller requested a non-“full” solution.

— Function: solve-eqn expr var full

This version of solve-for takes an expression which will typically be an equation or inequality. (If it is not, it will be interpreted as the equation ‘expr = 0’.) It returns an equation or inequality, or nil if no solution could be found.

— Function: solve-system exprs vars full

This function solves a system of equations. Generally, exprs and vars will be vectors of equal length. See Solving Systems of Equations, for other options.

— Function: expr-contains expr var

Returns a non-nil value if var occurs as a subexpression of expr.

This function might seem at first to be identical to calc-find-sub-formula. The key difference is that expr-contains uses equal to test for matches, whereas calc-find-sub-formula uses eq. In the formula ‘f(a, a)’, the two ‘a’s will be equal but not eq to each other.

— Function: expr-contains-count expr var

Returns the number of occurrences of var as a subexpression of expr, or nil if there are no occurrences.

— Function: expr-depends expr var

Returns true if expr refers to any variable the occurs in var. In other words, it checks if expr and var have any variables in common.

— Function: expr-contains-vars expr

Return true if expr contains any variables, or nil if expr contains only constants and functions with constant arguments.

— Function: expr-subst expr old new

Returns a copy of expr, with all occurrences of old replaced by new. This treats lambda forms specially with respect to the dummy argument variables, so that the effect is always to return expr evaluated at old = new.

— Function: multi-subst expr old new

This is like expr-subst, except that old and new are lists of expressions to be substituted simultaneously. If one list is shorter than the other, trailing elements of the longer list are ignored.

— Function: expr-weight expr

Returns the “weight” of expr, basically a count of the total number of objects and function calls that appear in expr. For “primitive” objects, this will be one.

— Function: expr-height expr

Returns the “height” of expr, which is the deepest level to which function calls are nested. (Note that ‘a + b’ counts as a function call.) For primitive objects, this returns zero.

— Function: polynomial-p expr var

Check if expr is a polynomial in variable (or sub-expression) var. If so, return the degree of the polynomial, that is, the highest power of var that appears in expr. For example, for ‘(x^2 + 3)^3 + 4’ this would return 6. This function returns nil unless expr, when expanded out by a x (calc-expand), would consist of a sum of terms in which var appears only raised to nonnegative integer powers. Note that if var does not occur in expr, then expr is considered a polynomial of degree 0.

— Function: is-polynomial expr var degree loose

Check if expr is a polynomial in variable or sub-expression var, and, if so, return a list representation of the polynomial where the elements of the list are coefficients of successive powers of var: ‘a + b x + c x^3’ would produce the list ‘(a b 0 c)’, and ‘(x + 1)^2’ would produce the list ‘(1 2 1)’. The highest element of the list will be non-zero, with the special exception that if expr is the constant zero, the returned value will be ‘(0)’. Return nil if expr is not a polynomial in var. If degree is specified, this will not consider polynomials of degree higher than that value. This is a good precaution because otherwise an input of ‘(x+1)^1000’ will cause a huge coefficient list to be built. If loose is non-nil, then a looser definition of a polynomial is used in which coefficients are no longer required not to depend on var, but are only required not to take the form of polynomials themselves. For example, ‘sin(x) x^2 + cos(x)’ is a loose polynomial with coefficients ‘((calcFunc-cos x) 0 (calcFunc-sin x))’. The result will never be nil in loose mode, since any expression can be interpreted as a “constant” loose polynomial.

— Function: polynomial-base expr pred

Check if expr is a polynomial in any variable that occurs in it; if so, return that variable. (If expr is a multivariate polynomial, this chooses one variable arbitrarily.) If pred is specified, it should be a Lisp function which is called as ‘(pred subexpr)’, and which should return true if mpb-top-expr (a global name for the original expr) is a suitable polynomial in subexpr. The default predicate uses ‘(polynomial-p mpb-top-expr subexpr)’; you can use pred to specify additional conditions. Or, you could have pred build up a list of every suitable subexpr that is found.

— Function: poly-simplify poly

Simplify polynomial coefficient list poly by (destructively) clipping off trailing zeros.

— Function: poly-mix a ac b bc

Mix two polynomial lists a and b (in the form returned by is-polynomial) in a linear combination with coefficient expressions ac and bc. The result is a (not necessarily simplified) polynomial list representing ‘ac a + bc b’.

— Function: poly-mul a b

Multiply two polynomial coefficient lists a and b. The result will be in simplified form if the inputs were simplified.

— Function: build-polynomial-expr poly var

Construct a Calc formula which represents the polynomial coefficient list poly applied to variable var. The a c (calc-collect) command uses is-polynomial to turn an expression into a coefficient list, then build-polynomial-expr to turn the list back into an expression in regular form.

— Function: check-unit-name var

Check if var is a variable which can be interpreted as a unit name. If so, return the units table entry for that unit. This will be a list whose first element is the unit name (not counting prefix characters) as a symbol and whose second element is the Calc expression which defines the unit. (Refer to the Calc sources for details on the remaining elements of this list.) If var is not a variable or is not a unit name, return nil.

— Function: units-in-expr-p expr sub-exprs

Return true if expr contains any variables which can be interpreted as units. If sub-exprs is t, the entire expression is searched. If sub-exprs is nil, this checks whether expr is directly a units expression.

— Function: single-units-in-expr-p expr

Check whether expr contains exactly one units variable. If so, return the units table entry for the variable. If expr does not contain any units, return nil. If expr contains two or more units, return the symbol wrong.

— Function: to-standard-units expr which

Convert units expression expr to base units. If which is nil, use Calc's native base units. Otherwise, which can specify a units system, which is a list of two-element lists, where the first element is a Calc base symbol name and the second is an expression to substitute for it.

— Function: remove-units expr

Return a copy of expr with all units variables replaced by ones. This expression is generally normalized before use.

— Function: extract-units expr

Return a copy of expr with everything but units variables replaced by ones.