home *** CD-ROM | disk | FTP | other *** search
- ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; The data in this file contains enhancments. ;;;;;
- ;;; ;;;;;
- ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
- ;;; All rights reserved ;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; (c) Copyright 1982 Massachusetts Institute of Technology ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (in-package "MAXIMA")
- (macsyma-module solve)
-
- (load-macsyma-macros ratmac strmac)
-
- (DECLARE-TOP (GENPREFIX V_)
- (SPECIAL VAR-LIST EXPSUMSPLIT $DISPFLAG $NOLABELS CHECKFACTORS *G
- $ALGEBRAIC EQUATIONS ;List of E-labels
- *POWER *VARB *FLG $DERIVSUBST $NUMER $FLOAT
- $%EMODE WFLAG GENVAR GENPAIRS VARLIST BROKEN-NOT-FREEOF
- $FACTORFLAG
- MULT ;Some crock which tracks multiplicities.
- *ROOTS ;alternating list of solutions and multiplicities
- *FAILURES ;alternating list of equations and multiplicities
- *MYVAR $LISTCONSTVARS
- *HAS*VAR *VAR $DONTFACTOR $LINENUM $LINECHAR
- LINELABLE $KEEPFLOAT $RATFAC
- ERRRJFFLAG ;A substitute for condition binding.
- LSOLVEFLAG XM* XN* MUL* SOLVEXP)
- (ARRAY* (NOTYPE XA* 2))
- (FIXNUM THISN $LINENUM))
-
- (DEFMVAR $BREAKUP T
- "Causes solutions to cubic and quartic equations to be expressed in
- terms of common subexpressions.")
-
- (DEFMVAR $MULTIPLICITIES '$NOT_SET_YET
- "Set to a list of the multiplicities of the individual solutions
- returned by SOLVE, REALROOTS, or ALLROOTS.")
-
- (DEFMVAR $LINSOLVEWARN T
- "Needs to be documented.")
-
- (DEFMVAR $SOLVE_INCONSISTENT_ERROR T
- "If T gives an MAXIMA-ERROR if SOLVE meets up with inconsistent linear
- equations. If NIL, returns ((MLIST SIMP)) in this case.")
-
- (DEFMVAR $PROGRAMMODE T
- "Causes SOLVE to return its answers explicitly as elements
- in a list rather than printing E-labels.")
-
- (DEFMVAR $SOLVEDECOMPOSES T
- "Causes SOLVE to use POLYDECOMP in attempting to solve polynomials.")
-
- (DEFMVAR $SOLVEEXPLICIT NIL
- "Causes SOLVE to return implicit solutions i.e. of the form F(x)=0.")
-
- (DEFMVAR $SOLVEFACTORS T
- "If T, then SOLVE will try to factor the expression. The FALSE
- setting may be desired in zl-SOME cases where factoring is not
- necessary.")
-
- (DEFMVAR $SOLVENULLWARN T
- "Causes the user will be warned if SOLVE is called with either a
- null equation list or a null variable list. For example,
- SOLVE([],[]); would print two warning messages and return [].")
-
- (DEFMVAR $SOLVETRIGWARN T
- "Causes SOLVE to print a warning message when it is uses
- inverse trigonometric functions to solve an equation,
- thereby losing solutions.")
-
- (DEFMVAR $SOLVERADCAN NIL
- "SOLVE will use RADCAN which will make SOLVE slower but will allow
- certain problems containing exponentials and logs to be solved.")
-
- ;; Utility macros
-
- ;; In MacLisp, this turns into SUBRCALL if we are compiling, FUNCALL if
- ;; interpreted. In LMLisp and other random systems, just turn into FUNCALL.
-
- #+MacLisp
- (DEFMACRO SUBR-FUNCALL (FUNCTION . ARGS)
- (COND ((STATUS FEATURE COMPLR) `(SUBRCALL NIL ,FUNCTION . ,ARGS))
- (T `(FUNCALL ,FUNCTION . ,ARGS))))
-
- #-MacLisp
- (DEFMACRO SUBR-FUNCALL (FUNCTION . ARGS) `(FUNCALL ,FUNCTION . ,ARGS))
-
- ;; This macro returns the number of trivial equations. It counts up the
- ;; number of zeros in a list.
-
- (DEFMACRO NZLIST (lLIST)
- `(DO ((L ,lLIST (CDR L))
- (ZCOUNT 0))
- ((NULL L) ZCOUNT)
- (IF (AND (INTEGERP (CAR L)) (ZEROP (CAR L)))
- (INCREMENT ZCOUNT))))
-
- ;; This is only called on a variable.
-
- (DEFMACRO ALLROOT (EXP)
- `(SETQ *FAILURES (LIST* (MAKE-MEQUAL-SIMP ,EXP ,EXP) 1 *FAILURES)))
-
-
- ;; Finds variables, changes equations into expressions without MEQUAL.
- ;; Checks for consistency between the number of unknowns and equations.
- ;; Calls SOLVEX for simultaneous equations and SSOLVE for a single equation.
-
- (DEFMFUN $SOLVE (*EQL &OPTIONAL (VARL NIL VARL-P))
- (SETQ $MULTIPLICITIES (MAKE-MLIST))
- (PROG (EQL ;Expressions and variables being solved
- $KEEPFLOAT $RATFAC ;In case the user has set these
- *ROOTS *FAILURES ;*roots gets solutions, *failures "roots of"
- BROKEN-NOT-FREEOF) ;Has something to do with spliting up roots
- (SETQ EQL
- (COND ((ATOM *EQL) (NCONS *EQL))
- ((EQ (G-REP-OPERATOR *EQL) 'MLIST)
- (MAPCAR 'MEQHK (MAPCAR 'MEVAL (CDR *EQL))))
- ((MEMQ (G-REP-OPERATOR *EQL)
- '(MNOTEQUAL MGREATERP MLESSP MGEQP MLEQP))
- (MERROR "Cannot solve inequalities. -SOLVE"))
- (T (NCONS (MEQHK *EQL)))))
-
- (COND ((NULL VARL-P) ;If the variable list wasn't supplied
- (SETQ VARL ;we have to supply it ourselves.
- (LET (($LISTCONSTVARS NIL))
- (CDR ($LISTOFVARS
- ;If some trivial then use original equations
- ;(primarily for case of X=X etc.)
- (COND ((ZEROP (NZLIST EQL)) *EQL)
- (T EQL)))))) ;Usually throw trivia out!
- (IF VARL (SETQ VARL (REMC VARL)))) ;Remove all constants
- (T (SETQ VARL
- (COND (($LISTP VARL) (MAPCAR #'MEVAL (CDR VARL)))
- (T (LIST VARL))))))
-
- (IF (AND (NULL VARL) $SOLVENULLWARN)
- (MTELL "~&Got a null variable list, continuing - SOLVE~%"))
- (IF (AND (NULL EQL) $SOLVENULLWARN)
- (MTELL "~&Got a null equation list, continuing - SOLVE~%"))
- (IF (ORMAPC #'MNUMP VARL)
- (MERROR "A number was found where a variable was expected -SOLVE"))
-
- (COND ((EQUAL EQL '(0)) (RETURN '$ALL))
- ((OR (NULL VARL) (NULL EQL)) (RETURN (MAKE-MLIST-SIMP)))
- ((AND (NULL (CDR VARL)) (NULL (CDR EQL)))
- (RETURN (SSOLVE (CAR EQL) (CAR VARL))))
- ((OR VARL-P (= (LENGTH VARL) (LENGTH EQL)))
- (SETQ EQL (SOLVEX EQL VARL (NOT $PROGRAMMODE) T))
- (RETURN (COND ((AND (CDR EQL) (NOT ($LISTP (CADR EQL))))
- (MAKE-MLIST EQL))
- (T EQL)))))
- (LET ((U (MAKE-MLIST-L VARL))
- (E (COND (($LISTP *EQL) *EQL)
- (T (MAKE-MLIST *EQL)))))
- ;; MFORMAT doesn't have ~:[~] yet, so I just change this to
- ;; make one of two possible calls to MERROR. Smaller codesize
- ;; then what was here before anyway.
- (IF (> (LENGTH VARL) (LENGTH EQL))
- (MERROR "More unknowns than equations -SOLVE~
- ~%Unknowns given : ~%~M~
- ~%Equations given: ~%~M"
- U E)
- (MERROR "More equations than unknowns -SOLVE~
- ~%Unknowns given : ~%~M~
- ~%Equations given: ~%~M"
- U E)))))
-
-
- ;; Removes anything from its list arg which solve considers not to be a
- ;; variable, i.e. constants, functions or subscripted variables without
- ;; numeric args.
-
- (DEFUN REMC (LST)
- (DO ((L LST (CDR L)) (FL) (VL)) ((NULL L) VL)
- (COND ((ATOM (SETQ FL (CAR L)))
- (OR (MAXIMA-CONSTANTP FL) (SETQ VL (CONS FL VL))))
- ((ANDMAPC #'$CONSTANTP (CDR FL)) (SETQ VL (CONS FL VL))))))
-
- ;; List of multiplicities. Why is this special?
-
- (DECLARE-TOP (SPECIAL MULTI))
-
- ;; Solve a single equation for a single unknown.
- ;; Obtains roots via solve and prints them.
-
- (DEFUN SSOLVE (EXP *VAR &AUX EQUATIONS MULTI)
- (LET (($SOLVETRIGWARN $SOLVETRIGWARN))
- (COND ((NULL *VAR) '$ALL)
- (T (SOLVE EXP *VAR 1)
- (COND ((NOT (OR *ROOTS *FAILURES)) (MAKE-MLIST))
- ($PROGRAMMODE
- (PROG1 (MAKE-MLIST-L
- (NREVERSE
- (MAP2C #'(LAMBDA (EQN MULT) (PUSH MULT MULTI) EQN)
- (IF $SOLVEEXPLICIT
- *ROOTS
- (NCONC *ROOTS *FAILURES)))))
- (SETQ $MULTIPLICITIES
- (MAKE-MLIST-L (NREVERSE MULTI)))))
- (T (WHEN (AND *FAILURES (NOT $SOLVEEXPLICIT))
- (IF $DISPFLAG (MTELL "The roots of:~%"))
- (SOLVE2 *FAILURES))
- (WHEN *ROOTS
- (IF $DISPFLAG (MTELL "Solution:~%"))
- (SOLVE2 *ROOTS))
- (MAKE-MLIST-L EQUATIONS)))))))
-
- ;; Solve takes three arguments, the expression to solve for zero, the variable
- ;; to solve for, and what multiplicity this solution is assumed to have (from
- ;; higher-level Solve's). Solve returns NIL. Isn't that useful? The lists
- ;; *roots and *failures are special variables to which Solve prepends solutions
- ;; and their multiplicities in that order: *roots contains explicit solutions
- ;; of the form <var>=<function of independent variables>, and *failures
- ;; contains equations which if solved would yield additional solutions.
-
- ;; Factors expression and reduces exponents by their gcd (via solventhp)
-
- (DEFMFUN SOLVE (*EXP *VAR MULT
- &AUX (GENVAR NIL)
- ($DERIVSUBST NIL)
- (EXP (FLOAT2RAT (MRATCHECK *EXP)))
- (*MYVAR *VAR)
- ($SAVEFACTORS T))
- (PROG (FACTORS *HAS*VAR GENPAIRS $DONTFACTOR TEMP SYMBOL *G CHECKFACTORS
- VARLIST EXPSUMSPLIT)
- (LET (($RATFAC T)) (SETQ EXP (RATDISREP (RATF EXP))))
- ; Cancel out any simple
- ; (non-algebraic) common factors in numerator and
- ; denominator without altering the structure of the
- ; expression too much.
- ; Also, RJFPROB in TEST;SOLVE TEST is now solved.
- ; - JPG
- A (COND ((ATOM EXP)
- (COND ((EQ EXP *VAR)
- (SOLVE3 0 MULT))
- ((EQUAL EXP 0) (ALLROOT *VAR))
- (T NIL)))
- (T (SETQ EXP (MEQHK EXP))
- (COND ((EQUAL EXP '(0))
- (RETURN (ALLROOT *VAR)))
- ((FREE EXP *VAR)
- (RETURN NIL)))
- (COND ((NOT (ATOM *VAR))
- (SETQ SYMBOL (GENSYM))
- (SETQ EXP (MAXIMA-SUBSTITUTE SYMBOL *VAR EXP))
- (SETQ TEMP *VAR)
- (SETQ *VAR SYMBOL)
- (SETQ *MYVAR *VAR))) ;keep *MYVAR up-to-date
-
- (COND ($SOLVERADCAN (SETQ EXP (RADCAN1 EXP))
- (IF (ATOM EXP) (GO A))))
-
- (COND ((EASY-CASES EXP *VAR)
- (COND (SYMBOL (SETQ *ROOTS (SUBST TEMP *VAR *ROOTS))
- (SETQ *FAILURES (SUBST TEMP *VAR *FAILURES))))
- (ROOTSORT *ROOTS)
- (ROOTSORT *FAILURES)
- (RETURN NIL)))
-
- (COND ((SETQ FACTORS (FIRST-ORDER-P EXP *VAR))
- (SOLVE3 (RATDISREP
- (RATF (MAKE-MTIMES -1 (DIV* (CDR FACTORS)
- (CAR FACTORS)))))
- MULT))
-
- (T (SETQ VARLIST (LIST *VAR))
- (FNEWVAR EXP)
- (SETQ VARLIST (VARSORT VARLIST))
- (LET ((VARTEMP)
- (RATNUMER (MRAT-NUMER (RATREP* EXP)))
- (NUMER-VARLIST VARLIST)
- (SUBST-LIST (TRIG-SUBST-P VARLIST)))
- (SETQ VARLIST (NCONS *VAR))
- (COND (SUBST-LIST
- (SETQ EXP (TRIG-SUBST EXP SUBST-LIST))
- (FNEWVAR EXP)
- (SETQ VARLIST (VARSORT VARLIST))
- (SETQ EXP (MRAT-NUMER (RATREP* EXP)))
- (SETQ VARTEMP VARLIST))
- (T (SETQ VARTEMP NUMER-VARLIST)
- (SETQ EXP RATNUMER)))
- (SETQ VARLIST VARTEMP))
-
- (COND ((ATOM EXP) (GO A))
- ((SPECASEP EXP) (SOLVE1A EXP MULT))
- ((AND (NOT (PCOEFP EXP))
- (CDDR EXP)
- (NOT (EQUAL 1 (SETQ *G
- (SOLVENTHP (CDDDR EXP) (CADR EXP))))))
- (SOLVENTH EXP *G))
- (T (MAP2C #'SOLVE1A
- (COND ($SOLVEFACTORS (PFACTOR EXP))
- (T (LIST EXP 1))))))))))
-
- (COND (SYMBOL (SETQ *ROOTS (SUBST TEMP *VAR *ROOTS))
- (SETQ *FAILURES (SUBST TEMP *VAR *FAILURES))))
- (ROOTSORT *ROOTS)
- (ROOTSORT *FAILURES)
- (RETURN NIL)))
-
- (DEFUN FLOAT2RAT (EXP)
- (COND ((FLOATP EXP) (SETQ EXP (PREP1 EXP)) (MAKE-RAT-SIMP (CAR EXP) (CDR EXP)))
- ((OR (ATOM EXP) (SPECREPP EXP)) EXP)
- (T (RECUR-APPLY #'FLOAT2RAT EXP))))
-
- ;;; The following takes care of cases where the expression is already in
- ;;; factored form. This can introduce spurious roots if one of the factors
- ;;; is an expression that can be undefined or infinity for certain values of
- ;;; the variable in question. But soon this will be no worry because I will
- ;;; add a list of "possible bad roots" to what $SOLVE returns.
- ;;; Solve is not fully recursive when it due to globals, $MULTIPLICIES
- ;;; may be screwed here. (Solve should be made recursive)
-
- (DEFUN EASY-CASES (*EXP *VAR)
- (COND ((EQ (CAAR *EXP) 'MTIMES)
- (DO ((TERMS (CDR *EXP) (CDR TERMS)))
- ((NULL TERMS))
- (SOLVE (CAR TERMS) *VAR 1))
- 'MTIMES)
- ((EQ (CAAR *EXP) 'MEXP)
- (COND ((AND (INTEGERP (CADDR *EXP))
- (PLUSP (CADDR *EXP)))
- (SOLVE (CADR *EXP) *VAR (CADDR *EXP))
- 'MEXPRAT)))))
-
- ;;; Predicate to test for presence of troublesome trig functions to be
- ;;; canonicalized. A table of when to make substitutions should
- ;;; be used here.
- ;;; trig kind => SIN | COS | TAN ... subst to make
- ;;; number around in expression -> 1 1 0 ......
- ;;; what you want to be able to do for example is to see if SIN and COS^2
- ;;; are around and then make a reasonable substitution.
-
- (DEFUN TRIG-SUBST-P (VLIST)
- (AND (NOT (TRIG-NOT-SUBST-P VLIST))
- (DO ((VAR (CAR VLIST) (CAR VLIST))
- (VLIST (CDR VLIST) (CDR VLIST))
- (SUBST-LIST))
- ((NULL VAR) SUBST-LIST)
- (COND ((AND (NOT (ATOM VAR))
- (TRIG-CANNON (G-REP-OPERATOR VAR))
- (NOT (FREE VAR *VAR)))
- (PUSH VAR SUBST-LIST))))))
-
- ;; Predicate to see when obviously not to substitute for trigs.
- ;; A hack in the direction of expression properties-table driven
- ;; substition. The "measure" of the expression is the total number
- ;; of different kinds of trig functions in the expression.
-
- (DEFUN TRIG-NOT-SUBST-P (VLIST)
- (LET ((TRIGS '(%SIN %COS %TAN %COT %CSC %SEC)))
- (< (MEASURE #'SIGN-GJC (OPERATOR-FREQUENCY-TABLE VLIST TRIGS) TRIGS)
- 2)))
-
- ;; To get the total "value" of things in a table, this case an assoc list.
- ;; (MEASURE FUNCTION ASSOCIATION-LIST SET) where FUNCTION is a function mapping
- ;; the range of the ASSOCIATION-LIST viewed as a function on the SET, to the
- ;; integers.
-
- (DEFUN MEASURE (F ALIST SET &AUX (SUM 0))
- (DOLIST (ELEMENT SET)
- (INCREMENT SUM (FUNCALL F (CDR (ASSQ ELEMENT ALIST)))))
- SUM)
-
- ;; (defun MEASURE (F AL S)
- ;; (do ((j 0 (f1+ j))
- ;; (sum 0))
- ;; ((= j (length S)) sum)
- ;; (setq sum (f+ sum (funcall F (cdr (assoc (nth j S) al)))))))
-
- ;; Named for uniqueness only
-
- (DEFUN SIGN-GJC (X)
- (COND ((OR (NULL X) (= X 0)) 0)
- ((< 0 X) 1)
- (T -1)))
-
- ;; A function that can EXTEND a function
- ;; over two association lists. Note that I have been using association lists
- ;; as mere functions (that is, as sets of ordered pairs).
- ;; (EXTEND '+ L1 L2 S) could also be to take the union of two multi-sets in the
- ;; sample space S. (what the '&%%#?& has this got to do with SOLVE?)
-
- (DEFUN EXTEND (F L1 L2 S)
- (DO ((J 0 (f1+ J))
- (VALUE NIL))
- ((= J (LENGTH S)) VALUE)
- (SETQ VALUE (CONS (CONS (NTH J S)
- (FUNCALL F (CDR (zl-ASSOC (NTH J S) L1))
- (CDR (zl-ASSOC (NTH J S) L2))))
- VALUE))))
-
- ;; For the case where the value of assoc is NIL, we will need a special "+"
-
- (DEFUN +MSET (A B) (f+ (OR A 0) (OR B 0)))
-
- ;; To recursively looks through a list
- ;; structure (the VLIST) for members of the SET appearing in the MACSYMA
- ;; functional position (caar list). Returning an assoc. list of appearence
- ;; frequencies. Notice the use of EXTEND.
-
- (DEFUN OPERATOR-FREQUENCY-TABLE (VLIST SET)
- (DO ((J 0 (f1+ J))
- (IT)
- (ASSL (DO ((K 0 (f1+ K))
- (MADE NIL))
- ((= K (LENGTH SET)) MADE)
- (SETQ MADE (CONS (CONS (NTH K SET) 0)
- MADE)))))
- ((= J (LENGTH VLIST)) ASSL)
- (SETQ IT (NTH J VLIST))
- (COND ((ATOM IT))
- (T (SETQ ASSL (EXTEND #'+MSET (CONS (CONS (CAAR IT) 1) NIL)
- ASSL SET))
- (SETQ ASSL (EXTEND #'+MSET ASSL
- (OPERATOR-FREQUENCY-TABLE (CDR IT) SET)
- SET))))))
-
- (DEFUN TRIG-SUBST (EXP SUB-LIST)
- (DO ((EXP EXP)
- (SUB-LIST (CDR SUB-LIST) (CDR SUB-LIST))
- (VAR (CAR SUB-LIST) (CAR SUB-LIST)))
- ((NULL VAR) EXP)
- (SETQ EXP
- (MAXIMA-SUBSTITUTE (FUNCALL (TRIG-CANNON (G-REP-OPERATOR VAR))
- (MAKE-MLIST-L (G-REP-OPERANDS VAR)))
- VAR EXP))))
-
- ;; Here are the canonical trig substitutions.
-
- (DEFUN-prop (%SEC TRIG-CANNON) (X)
- (INV* (MAKE-G-REP '%COS (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%CSC TRIG-CANNON) (X)
- (INV* (MAKE-G-REP '%SIN (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%TAN TRIG-CANNON) (X)
- (DIV* (MAKE-G-REP '%SIN (G-REP-FIRST-OPERAND X))
- (MAKE-G-REP '%COS (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%COT TRIG-CANNON) (X)
- (DIV* (MAKE-G-REP '%COS (G-REP-FIRST-OPERAND X))
- (MAKE-G-REP '%SIN (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%SECH TRIG-CANNON) (X)
- (INV* (MAKE-G-REP '%COSH (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%CSCH TRIG-CANNON) (X)
- (INV* (MAKE-G-REP '%SINH (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%TANH TRIG-CANNON) (X)
- (DIV* (MAKE-G-REP '%SINH (G-REP-FIRST-OPERAND X))
- (MAKE-G-REP '%COSH (G-REP-FIRST-OPERAND X))))
-
- (DEFUN-prop (%COTH TRIG-CANNON) (X)
- (DIV* (MAKE-G-REP '%COSH (G-REP-FIRST-OPERAND X))
- (MAKE-G-REP '%SINH (G-REP-FIRST-OPERAND X))))
-
- ;; Predicate to replace ISLINEAR....Returns NIL if not of for A*X+B, A and B
- ;; freeof X, else returns (A . B)
-
- (DEFUN FIRST-ORDER-P (EXP VAR &AUX TEMP)
- ;; Expand the expression at one level, i.e. distribute products
- ;; over sums, but leave exponentiations alone.
- ;; (X+1)^2*(X+Y) --> X*(X+1)^2 + Y*(X+1)^2
- (SETQ EXP (EXPAND1 EXP 1 1))
- (COND ((ATOM EXP) NIL)
- (T (CASE (G-REP-OPERATOR EXP)
- (MTIMES
- (COND ((SETQ TEMP (LINEAR-TERM-P EXP VAR))
- (MAKE-LINEQ TEMP 0))
- (T NIL)))
- (MPLUS
- (DO ((ARG (CAR (G-REP-OPERANDS EXP)) (CAR REST))
- (REST (CDR (G-REP-OPERANDS EXP)) (CDR REST))
- (LINEAR-TERM-LIST)
- (CONSTANT-TERM-LIST)
- (TEMP))
- ((NULL ARG)
- (IF LINEAR-TERM-LIST
- (MAKE-LINEQ (MAKE-MPLUS-L LINEAR-TERM-LIST)
- (IF CONSTANT-TERM-LIST
- (MAKE-MPLUS-L CONSTANT-TERM-LIST)
- 0))))
- (COND ((SETQ TEMP (LINEAR-TERM-P ARG VAR))
- (PUSH TEMP LINEAR-TERM-LIST))
- ((BROKEN-FREEOF VAR ARG)
- (PUSH ARG CONSTANT-TERM-LIST))
- (T (RETURN NIL)))))
- (T NIL)))))
-
- ;; Function to test if a term from an expanded expression is a linear term
- ;; check and see that exactly one item in the product is the main var and
- ;; all others are free of the main var. Returns NIL or a G-REP expression.
-
- (DEFUN LINEAR-TERM-P (EXP VAR)
- (COND ((ATOM EXP)
- (COND ((EQ EXP VAR) 1)
- (T NIL)))
- (T (CASE (G-REP-OPERATOR EXP)
- (MTIMES
- (DO ((FACTOR (CAR (G-REP-OPERANDS EXP)) ;individual factors
- (CAR REST))
- (REST (CDR (G-REP-OPERANDS EXP)) ;factors yet to be done
- (CDR REST))
- (MAIN-VAR-P) ;nt -> main-var seen at top level
- (LIST-OF-FACTORS)) ;accumulate our factors
- ((NULL FACTOR) ;for all factors
- (AND MAIN-VAR-P
- ;no-main-var at top level -=> not linear
- (MAKE-MTIMES-L LIST-OF-FACTORS)))
- (COND ((EQ FACTOR VAR) ;if it's our main var
- ;note it...it has to be there to be a linear term
- (SETQ MAIN-VAR-P T))
- ((BROKEN-FREEOF VAR FACTOR) ;if
- (PUSH FACTOR LIST-OF-FACTORS))
- (T (RETURN NIL)))))
- (T NIL)))))
-
-
- ;;; DISPATCHING FUNCTION ON DEGREE OF EXPRESSION
- ;;; This is a crock of shit, it should be data driven and be able to
- ;;; dispatch to all manner of special cases that are in a table.
- ;;; EXP here is a polynomial in MRAT form. All of this well-structured,
- ;;; intelligently-designed code works by side effect. SOLVECUBIC
- ;;; takes something that looks like (G0003 3 4 1 1 0 10) as an argument
- ;;; and returns something like ((MEQUAL) $X ((MTIMES) ...)). You figure
- ;;; out where the $X comes from.
-
- ;;; It comes from GENVARS/VARLIST, of course. Isn't this wonderful rational
- ;;; function package irrational? If you don't know about GENVARS and
- ;;; VARLIST, you'd better bite the bullet and learn...everything depends
- ;;; on them. The canonical example of mis-use of special variables!
- ;;; --RWK
-
- (DEFUN SOLVE1A (EXP MULT)
- (LET ((*MYVAR *MYVAR)
- (*G NIL))
- (COND ((ATOM EXP) NIL)
- ((NOT (MEMALIKE (SETQ *MYVAR (PDIS (LIST (CAR EXP) 1 1)))
- *HAS*VAR))
- NIL)
- ((EQUAL (CADR EXP) 1) (SOLVELIN EXP))
- ((SPECASEP EXP) (SOLVESPEC EXP T))
- ((EQUAL (CADR EXP) 2) (SOLVEQUAD EXP))
- ((NOT (EQUAL 1 (SETQ *G (SOLVENTHP (CDDDR EXP) (CADR EXP)))))
- (SOLVENTH EXP *G))
- ((EQUAL (CADR EXP) 3) (SOLVECUBIC EXP))
- ((EQUAL (CADR EXP) 4) (SOLVEQUARTIC EXP))
- (T (LET ((TT (SOLVE-BY-DECOMPOSITION EXP *MYVAR)))
- (SETQ *FAILURES (APPEND (SOLUTION-LOSSES TT) *FAILURES))
- (SETQ *ROOTS (APPEND (SOLUTION-WINS TT) *ROOTS)))))))
-
- (DEFUN SOLVE-SIMPLIST (LIST-OF-THINGS)
- (G-REP-OPERANDS (SIMPLIFYA (MAKE-MLIST-L LIST-OF-THINGS) NIL)))
-
- ;; The Solve-by-decomposition program returns the cons of (ROOTS . FAILURES).
- ;; It returns a "Solution" object, that is, a CONS with the CAR being the
- ;; failures and the CDR being the successes.
- ;; It takes a POLY as an argument and returns a SOLUTION.
-
- (DEFUN SOLVE-BY-DECOMPOSITION (POLY *$VAR)
- (LET ((DECOMP))
- (COND ((OR (NOT $SOLVEDECOMPOSES)
- (= (LENGTH (SETQ DECOMP (POLYDECOMP POLY (POLY-VAR POLY)))) 1))
- (MAKE-SOLUTION NIL `(,(MAKE-MEQUAL 0 (PDIS POLY)) 1)))
- (T (DECOMP-TRACE (MAKE-MEQUAL 0 (RDIS (CAR DECOMP)))
- DECOMP
- (POLY-VAR POLY) *$VAR 1)))))
-
- ;; DECOMP-TRACE is the recursive function which maps itself down the
- ;; intermediate solutions until the end is reached. If it encounters
- ;; non-solvable equations it stops. It returns a SOLUTION object, that is, a
- ;; CONS with the CAR being the failures and the CDR being the successes.
-
- (DEFUN DECOMP-TRACE (EQN DECOMP VAR *$VAR MULT &AUX SOL CHAIN-SOL WINS LOSSES)
- (SETQ SOL (IF DECOMP
- (RE-SOLVE EQN *$VAR MULT)
- (MAKE-SOLUTION `(,EQN 1) NIL)))
- (COND ((SOLUTION-LOSSES SOL) SOL)
- ;; End test
- ((NULL DECOMP) SOL)
- (T (DO ((L (SOLUTION-WINS SOL) (CDDR L)))
- ((NULL L))
- (SETQ CHAIN-SOL
- (DECOMP-CHAIN (CAR L) (CDR DECOMP) VAR *$VAR (CADR L)))
- (SETQ WINS (NCONC WINS
- (COPY-TOP-LEVEL (SOLUTION-WINS CHAIN-SOL))))
- (SETQ LOSSES (NCONC LOSSES
- (COPY-TOP-LEVEL (SOLUTION-LOSSES CHAIN-SOL)))))
- (MAKE-SOLUTION WINS LOSSES))))
-
- ;; Decomp-chain is the function which formats the mess for the recursive call.
- ;; It returns a "Solution" object, that is, a CONS with the CAR being the
- ;; failures and the CDR being the successes.
-
- (DEFUN DECOMP-CHAIN (RSOL DECOMP VAR *$VAR MULT)
- (LET ((SOL (SIMPLIFY (MAKE-MEQUAL (RDIS (IF DECOMP (CAR DECOMP)
- ;; Include the var itself in the decomposition
- (MAKE-MRAT-BODY (MAKE-MRAT-POLY VAR '(1 1)) 1)))
- (MEQUAL-RHS RSOL)))))
- (DECOMP-TRACE SOL DECOMP VAR *$VAR MULT)))
-
- ;; RE-SOLVE calls SOLVE recursively, returning a SOLUTION object.
- ;; Will not decompose or factor.
-
- (DEFUN RE-SOLVE (EQN VAR MULT)
- (LET ((*ROOTS NIL)
- (*FAILURES NIL)
- ;; We've already decomposed and factored
- ($SOLVEDECOMPOSES)
- ($SOLVEFACTORS))
- (SOLVE EQN VAR MULT)
- (MAKE-SOLUTION *ROOTS *FAILURES)))
-
- ;; SOLVENTH programs test to see if the variable of interest appears
- ;; to some power in all terms. If so, a new variable is substituted for it
- ;; and the simpler expression solved with the multiplicity
- ;; adjusted accordingly.
- ;; SOLVENTHP returns gcd of exponents.
-
- (DEFUN SOLVENTHP (L GCD)
- (COND ((NULL L) GCD)
- ((EQUAL GCD 1) 1)
- (T (SOLVENTHP (CDDR L)
- (GCD (CAR L) GCD)))))
-
- ;; Reduces exponents by their gcd.
-
- (DEFUN SOLVENTH (EXP *G)
- (LET ((*VARB (PDIS (MAKE-MRAT-POLY (POLY-VAR EXP) '(1 1))))
- (EXP (MAKE-MRAT-POLY (POLY-VAR EXP) (SOLVENTH1 (POLY-TERMS EXP)))))
- (LET* ((RTS (RE-SOLVE-FULL (PDIS EXP) *VARB))
- (FAILS (SOLUTION-LOSSES RTS))
- (WINS (SOLUTION-WINS RTS))
- (*POWER (MAKE-MEXPT *VARB *G)))
- (MAP2C #'(LAMBDA (W Z)
- (COND ((ATOM *VARB)
- (SOLVE (MAKE-MEQUAL *POWER (MEQUAL-RHS W)) *VARB Z))
- (T (LET ((RTS (RE-SOLVE-FULL
- (MAKE-MEQUAL *POWER (MEQUAL-RHS W))
- *VARB)))
- (MAP2C #'(LAMBDA (ROOT MULT)
- (SOLVE (MAKE-MEQUAL (MEQUAL-RHS ROOT) 0)
- *MYVAR MULT))
- (SOLUTION-WINS RTS))))))
- WINS)
- (MAP2C #'(LAMBDA (W Z)
- (PUSH Z *FAILURES)
- (PUSH (SOLVENTH3 W *POWER *VARB) *FAILURES))
- FAILS)
- *ROOTS)))
-
- (DEFUN SOLVENTH3 (W *POWER *VARB &AUX VARLIST GENVAR *FLG W1 W2)
- (COND ((BROKEN-FREEOF *VARB W) W)
- (T (SETQ W1 (RATF (CADR W)))
- (SETQ W2 (RATF (CADDR W)))
- (SETQ VARLIST
- (MAPCAR #'(LAMBDA (H)
- (COND (*FLG H)
- ((ALIKE1 H *VARB)
- (SETQ *FLG T)
- *POWER)
- (T H)))
- VARLIST))
- (LIST (CAR W) (RDIS (CDR W1)) (RDIS (CDR W2))))))
-
- (DECLARE-TOP (MUZZLED T))
- (DEFUN SOLVENTH1 (L)
- (COND ((NULL L) NIL)
- (T (CONS (QUOTIENT (CAR L) *G)
- (CONS (CADR L) (SOLVENTH1 (CDDR L)))))))
- (DECLARE-TOP (MUZZLED NIL))
-
- ;; Will decompose or factor
-
- (DEFUN RE-SOLVE-FULL (X VAR &AUX *ROOTS *FAILURES)
- (SOLVE X VAR MULT)
- (MAKE-SOLUTION *ROOTS *FAILURES))
-
- ;; Sees if expression is of the form A*F(X)^N+B.
-
- (DEFUN SPECASEP (E)
- (AND (MEMALIKE (PDIS (LIST (CAR E) 1 1)) *HAS*VAR)
- (OR (ATOM (CADDR E))
- (NOT (MEMALIKE (PDIS (LIST (CAADDR E) 1 1))
- *HAS*VAR)))
- (OR (NULL (CDDDR E)) (EQUAL (CADDDR E) 0))))
-
- ;; Solves the special case A*F(X)^N+B.
-
- (DECLARE-TOP (MUZZLED T))
- (DEFUN SOLVESPEC (EXP $%EMODE)
- (PROG (A B C)
- (SETQ A (PDIS (CADDR EXP)))
- (SETQ C (PDIS (LIST (CAR EXP) 1 1)))
- (COND ((NULL (CDDDR EXP))
- (RETURN (SOLVE C *VAR (TIMES (CADR EXP) MULT)))))
- (SETQ B (PDIS (PMINUS (CADDDR (CDR EXP)))))
- (RETURN (SOLVESPEC1 C
- (SIMPNRT (DIV* B A) (CADR EXP))
- (MAKE-RAT 1 (CADR EXP))
- (CADR EXP)))))
- (DECLARE-TOP (MUZZLED NIL))
-
- (DEFUN SOLVESPEC1 (VAR ROOT N THISN)
- (DO ((THISN THISN (f1- THISN))) ((ZEROP THISN))
- (SOLVE (ADD* VAR (MUL* -1 ROOT
- (POWER* '$%E (MUL* 2 '$%PI '$%I THISN N))))
- *VAR MULT)))
-
-
- ;; ADISPLINE displays a line like DISPLINE, and in addition, notes that it is
- ;; not free of *VAR if it isn't.
-
- (DEFUN ADISPLINE (LINE)
- ;; This may be redundant, but nice if ADISPLINE gets used where not needed.
- (COND ((AND $BREAKUP (NOT $PROGRAMMODE))
- (LET ((LINELABEL (DISPLINE LINE)))
- (COND ((BROKEN-FREEOF *VAR LINE))
- (T (SETQ BROKEN-NOT-FREEOF
- (CONS LINELABEL BROKEN-NOT-FREEOF))))
- LINELABEL))
- (T (DISPLINE LINE))))
-
- ;; Predicate to check if an expression which may be broken up
- ;; is freeof
-
- (SETQ BROKEN-NOT-FREEOF NIL)
-
- ;; For consistency, use backwards args.
-
- (DEFUN BROKEN-FREEOF (VAR EXP)
- (COND ($BREAKUP
- (DO ((B-N-FO VAR (CAR B-N-FO-L))
- (B-N-FO-L BROKEN-NOT-FREEOF (CDR B-N-FO-L)))
- ((NULL B-N-FO) T)
- (AND (NOT (ARGSFREEOF B-N-FO EXP))
- (RETURN NIL))))
- (T (ARGSFREEOF VAR EXP))))
-
- ;; Adds solutions to roots list.
- ;; Solves for inverse of functions (via USOLVE)
-
- (DEFUN SOLVE3 (EXP MULT)
- (SETQ EXP (SIMPLIFY EXP))
- (COND ((NOT (BROKEN-FREEOF *VAR EXP))
- (PUSH MULT *FAILURES)
- (PUSH (MAKE-MEQUAL-SIMP (SIMPLIFY *MYVAR) EXP) *FAILURES))
- (T (COND ((EQ *MYVAR *VAR)
- (PUSH MULT *ROOTS)
- (PUSH (MAKE-MEQUAL-SIMP *VAR EXP) *ROOTS))
- ((ATOM *MYVAR)
- (PUSH MULT *FAILURES)
- (PUSH (MAKE-MEQUAL-SIMP *MYVAR EXP) *FAILURES))
- (T (USOLVE EXP (G-REP-OPERATOR *MYVAR)))))))
-
-
- ;; Solve a linear equation. Argument is a polynomial in pseudo-cre form.
- ;; This function is called for side-effect only.
-
- (DEFUN SOLVELIN (EXP)
- (COND ((EQUAL 0. (PTERM (CDR EXP) 0.))
- (SOLVE1A (CADDR EXP) MULT)))
- (SOLVE3 (RDIS (RATREDUCE (PMINUS (PTERM (CDR EXP) 0.))
- (CADDR EXP)))
- MULT))
-
- ;; Solve a quadratic equation. Argument is a polynomial in pseudo-cre form.
- ;; This function is called for side-effect only.
- ;; The code for handling the case where the discriminant = 0 seems to never
- ;; be run. Presumably, the expression is factored higher up.
-
- (DECLARE-TOP (MUZZLED T))
- (DEFUN SOLVEQUAD (EXP &AUX DISCRIM A B C)
- (SETQ A (CADDR EXP))
- (SETQ B (PTERM (CDR EXP) 1.))
- (SETQ C (PTERM (CDR EXP) 0.))
- (SETQ DISCRIM (SIMPLIFY (PDIS (PPLUS (PEXPT B 2.)
- (PMINUS (PTIMES 4. (PTIMES A C)))))))
- (SETQ B (PDIS (PMINUS B)))
- (SETQ A (PDIS (PTIMES 2. A)))
- ;; At this point, everything is back in general representation.
- (let ((varlist nil)) ;;2/6/2002 RJF
- (COND ((EQUAL 0. DISCRIM)
- (SOLVE3 (FULLRATSIMP `((MQUOTIENT) ,B ,A))
- (TIMES 2. MULT)))
- (T (SETQ DISCRIM (SIMPNRT DISCRIM 2.))
- (SOLVE3 (FULLRATSIMP `((MQUOTIENT) ((MPLUS) ,B ,DISCRIM) ,A))
- MULT)
- (SOLVE3 (FULLRATSIMP `((MQUOTIENT) ((MPLUS) ,B ((MMINUS) ,DISCRIM)) ,A))
- MULT)))))
- (DECLARE-TOP (MUZZLED NIL))
-
- ;; Reorders V so that members which contain the variable of
- ;; interest come first.
-
- (DEFUN VARSORT (V)
- (LET ((*U NIL)
- (*V (COPY-TOP-LEVEL V)))
- (MAPC #'(LAMBDA (Z)
- (COND ((BROKEN-FREEOF *VAR Z)
- (SETQ *U (CONS Z *U))
- (SETQ *V (zl-DELETE Z *V 1)))))
- V)
- (SETQ $DONTFACTOR *U)
- (SETQ *HAS*VAR *V)
- (APPEND *U *V)))
-
- ;; Solves for variable when it occurs within a function by taking the inverse.
- ;; When this code is fixed, the `((mplus) ,x ,y) forms should be rewritten as
- ;; (MAKE-MPLUS X Y). I didn't do this because the code was buggy and it should
- ;; be fixed first. - cwh
- ;; You mean you didn't do it because you were buggy. Hope you're fixed soon!
- ;; --RWK
-
- (DEFUN USOLVE (EXP OP)
- (PROG (INVERSE)
- (SETQ INVERSE
- (COND
- ((EQ OP 'MEXPT)
- (COND ((BROKEN-FREEOF *VAR
- (CADR *MYVAR))
- (COND ((EQUAL EXP 0)
- (GO FAIL)))
- `((mplus) ((mminus) ,(caddr *myvar))
- ,(div* `((%log) ,exp)
- `((%log) ,(cadr *myvar)))))
- ((BROKEN-FREEOF *VAR
- (CADDR *MYVAR))
- (COND ((EQUAL EXP 0)
- (COND ((MNEGP (CADDR *MYVAR))
- (GO FAIL))
- (T (CADR *MYVAR))))
- ;; There is a bug right here.
- ;; SOLVE(SQRT(U)+1) should return U=1
- ;; This code is entered with EXP = -1, OP = MEXPT
- ;; *VAR = U, and *MYVAR = ((MEXPT) U ((RAT) 1 2))
- ;; BULLSHIT -- RWK. That is precisely the bug
- ;; this code was added to fix!
- ((and (not (eq (ask-integer (caddr *myvar)
- '$INTEGER)
- '$yes))
- (free exp '$%i)
- (eq ($asksign exp) '$neg))
- (go fail))
- (T `((mplus) ,(cadr *myvar)
- ((mminus)
- ((mexpt) ,exp
- ,(div* 1 (caddr *myvar))))))))
- (T (GO FAIL))))
- ((SETQ INVERSE (GET OP '$INVERSE))
- (WHEN (AND $SOLVETRIGWARN
- (MEMQ OP '(%SIN %COS %TAN %SEC
- %CSC %COT %COSH %SECH)))
- (MTELL "~&SOLVE is using arc-trig functions to get ~
- a solution.~%Some solutions will be lost.~%")
- (SETQ $SOLVETRIGWARN NIL))
- `((MPLUS) ((MMINUS) ,(CADR *MYVAR))
- ((,INVERSE) ,EXP)))
- ((EQ OP '%LOG)
- `((MPLUS) ((MMINUS) ,(CADR *MYVAR))
- ((MEXPT) $%E ,EXP)))
- (T (GO FAIL))))
- (RETURN (SOLVE (SIMPLIFY INVERSE) *VAR MULT))
- FAIL (RETURN (SETQ *FAILURES
- (CONS (SIMPLIFY `((MEQUAL) ,*MYVAR ,EXP))
- (CONS MULT *FAILURES))))))
-
- ;; Predicate for determining if an expression is messy enough to
- ;; generate a new linelabel for it.
- ;; Expression must be in general form.
-
- (DEFUN COMPLICATED (EXP)
- (AND $BREAKUP
- (NOT $PROGRAMMODE)
- (NOT (FREE EXP 'MPLUS))))
-
- (DECLARE-TOP (MUZZLED T))
- (DEFUN ROOTSORT (L)
- (PROG (A FM FM1)
- G1 (COND ((NULL L) (RETURN NIL)))
- (SETQ A (CAR (SETQ FM L)))
- (SETQ FM1 (CDR FM))
- LOOP (COND ((NULL (CDDR FM)) (SETQ L (CDDR L)) (GO G1))
- ((ALIKE1 (CADDR FM) A)
- (RPLACA FM1 (PLUS (CAR FM1) (CADDDR FM)))
- (RPLACD (CDR FM) (CDDDDR FM))
- (GO LOOP)))
- (SETQ FM (CDDR FM))
- (GO LOOP)))
- (DECLARE-TOP (MUZZLED NIL))
-
- ;; Stuff moving in from MAT to get it out of core.
-
- (DEFMFUN $LINSOLVE (EQL VARL)
- (LET (($RATFAC))
- (SETQ EQL (COND (($LISTP EQL) (CDR EQL))
- (T (NCONS EQL))))
- (SETQ VARL (COND (($LISTP VARL) (REMRED (CDR VARL)))
- (T (NCONS VARL))))
- (DO ((VARL VARL (CDR VARL))) ((NULL VARL))
- (COND ((MNUMP (CAR VARL))
- (MERROR "Unacceptable variable to SOLVE: ~M"
- (CAR VARL)
- ))))
- (COND ((NULL VARL) (MAKE-MLIST-SIMP))
- (T (SOLVEX (MAPCAR 'MEQHK EQL) VARL (NOT $PROGRAMMODE) NIL)))
- ))
-
- ;; REMRED removes any repetition that may be in the variables list
- ;; The NREVERSE is significant here for some reason?
-
- (DEFUN REMRED (L) (IF L (NREVERSE (UNION1 L NIL))))
-
- (DEFUN SOLVEX (EQL VARL IND FLAG &AUX ($ALGEBRAIC $ALGEBRAIC))
- (declare (special xa*))
- (PROG (*VARL ANS VARLIST GENVAR LSOLVEFLAG XM* XN* MUL* SOLVEXP)
- (SETQ *VARL VARL)
- (SETQ SOLVEXP FLAG)
- (SETQ LSOLVEFLAG T)
- (SETQ EQL
- (MAPCAR #'(LAMBDA (X) ($RATDISREP ($RATNUMER X)))
- EQL))
- (COND ((ATOM (LET ((ERRRJFFLAG T))
- (CATCH 'RATERR (FORMX FLAG 'XA* EQL VARL))))
- ;; This flag is T if called from SOLVE
- ;; and NIL if called from LINSOLVE.
- (COND (FLAG (RETURN ($ALGSYS (MAKE-MLIST-L EQL)
- (MAKE-MLIST-L VARL))))
- (T (MERROR "LINSOLVE ran into a nonlinear equation.")))))
- (SETQ ANS (TFGELI 'XA* XN* XM*))
- (IF (AND $LINSOLVEWARN (CAR ANS))
- (MTELL "~&Dependent equations eliminated: ~A~%" (CAR ANS)))
- (IF (CADR ANS)
- (IF $SOLVE_INCONSISTENT_ERROR
- (MERROR "Inconsistent equations: ~A" (CADR ANS))
- (RETURN '((MLIST SIMP)))))
- (DO ((J 0 (f1+ J)))
- ((> J XM*))
- ;;I put this in the value cell--wfs
- ; (STORE ( XA* 0 J) NIL))
- (STORE (ARRAYCALL T XA* 0 J) NIL))
- (PTORAT 'XA* XN* XM*)
- (SETQ VARL
- (XRUTOUT 'XA* XN* XM*
- (MAPCAR #'(LAMBDA (X) (ITH VARL X))
- (CADDR ANS))
- IND))
- (*REARRAY 'XA*)
- (IF $PROGRAMMODE
- (SETQ VARL (MAKE-MLIST-L (LINSORT (CDR VARL) *VARL))))
- (RETURN VARL)))
-
- ;; (LINSORT '(((MEQUAL) A2 FOO) ((MEQUAL) A3 BAR)) '(A3 A2))
- ;; returns (((MEQUAL) A3 BAR) ((MEQUAL) A2 FOO)) .
-
- (DEFUN LINSORT (MEQ-LIST VAR-LIST)
- (MAPCAR #'(LAMBDA (X) (CONS (CAAR MEQ-LIST) X))
- (SORTCAR (MAPCAR #'CDR MEQ-LIST)
- #'(LAMBDA (X Y)
- (zl-MEMBER Y (zl-MEMBER X VAR-LIST))))))
-