calc: Symbolic Lisp Functions

 
 18.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 (SeeFormatting 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.  SeeSolving 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.