1 /* genut.mac -*- mode: Maxima; -*- */
3 /* Copyright (C) 2002 Wolfgang Jenkner <wjenkner@inode.at>
5 * This file is part of GNU Maxima.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be
13 * useful, but WITHOUT ANY WARRANTY; without even the implied
14 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
18 * Utility functions needed for stopex, facexp, facex1, declin and disol.
21 * The original version seems to be lost. However, the headers of
22 * some of the files above (and of rncomb.mac) suggest the following
23 * story (please tell me if you know more about it):
25 * The files mentioned above were developed by ASB (who is this/are they?)
26 * at least between November 1981 and May 1983. The now missing functions
27 * used to live in a file called GENUT (also written by ASB?). This file
28 * (or at least the functionality it implements) became part of the Symbolics
29 * branch of Macsyma but it didn't get into DOE Macsyma (although they were
30 * aware of its existence since they did have the files depending on it).
32 * Note that facexp contains also some functions (mentioned at the end of
33 * facexp.usg) which rely on some tensor package called DIAGEVAL to which
34 * GENUT seems to be somehow related. However, in this case, neither the
35 * package itself nor any description seems to be extant in Maxima.
37 * v. 0.1 (March 2002):
38 * First public version.
40 * v. 1.0 = CVS:1.1 (June 2002):
41 * Changes for translation.
42 * Rewrote some functions and added comments.
43 * Added missing functions for declin.
44 * Except for bug fixes, genut seems to be complete. Note that, as
45 * far as I can tell, the tensor manipulation functions mentioned
46 * above were never, and will never be, supported by genut.
48 * For more recent changes, please see the file `ChangeLog' in the
49 * top-level directory of the Maxima distribution.
52 /* Note that we have to use upper case for function or variable names
53 which are to be used by old code (because FOO is translated to $FOO
54 and foo to |$foo|). */
57 declare_translated(orpartition,orpartitionl,nonumfactor,
58 genut_prodmap,listofops_nonratl,setlist,
60 mode_declare(function(nulllistp,freeofl,zerolistp,lcpred),boolean))$
66 catch(for exp in list do if exp#0 then throw(false),true)$
69 catch(for var in varl do if not freeof(var,exp) then throw(false),true)$
71 /* Note that PARTITION(EXP,VAR) gives an error when EXP is atomic.
72 This is quite reasonable since the function can't know which kind
73 of neutral element it is supposed to return.
74 The argument list of ORPARTITION supplies an operator and hence
75 this missing piece of information. More generally, EXP is somehow
76 coerced to have OP as main operator. Otherwise this function behaves
77 just like PARTITION. */
79 orpartition(exp,op,var) :=
80 if not(atom(exp)) and inpart(exp,0)=op
81 then partition(exp,var)
82 else if freeof(var,exp)
83 then [exp,apply(op,[])]
84 else [apply(op,[]),exp]$
86 /* Same thing for a list of variables. */
88 orpartitionl(exp,op,varl) :=
89 block([free,notfree,partit],
93 (partit:orpartition(free,op,var),
96 then append(notfree,last(partit))
97 else apply(op,[notfree,last(partit)])),
100 orpartitionlist(list,op,[vars]) :=
101 block([partitl:map(lambda([exp],orpartitionl(exp,op,vars)),list)],
102 [map('first,partitl),map('last,partitl)])$
105 (for var in varl do exp:delete(var,exp),exp)$
113 /* The following mimics the behaviour of APPEND. Note, however, that
114 INTERSECT_LIST takes exactly two arguments */
116 intersect_list(exp1,exp2) :=
118 then error(concat("argument value `", exp1, "' to `intersect_list' was atomic")),
120 then error(concat("argument value `", exp2, "' to `intersect_list' was atomic")),
121 block([op:inpart(exp1,0)],
123 then error("Arguments to `intersect_list' are not compatible."),
124 block([inflag:true,cap:[]],
127 then cap:endcons(term,cap),
130 setlist(lst,l1,l2) :=
131 (l1::first(lst),l2::last(lst))$
138 /* If a non-trivial factorisation is not found you might prefer to retain
139 the expression in its original shape. Recall that you can arrange for
140 this function to be called instead of NONUMFACTOR by setting FACSUM's
141 AUTOMATIC property: put('facsum,'nonumfactor_alt,'automatic) */
143 nonumfactor_alt(exp) :=
144 block([dum:nonumfactor(exp)],
145 if atom(dum) or inpart(dum,0)="+"
149 /* Better not. Aliasing is too weird, e.g, DECLARE(FOO,NOUN) would
150 be displayed as DECLARE(FOO, FORMAL).
154 /* LST is an alternating list of operators and associated functions.
155 OP_FCN_LIST is used to pass LST to recursive calls to OPMAP. */
157 /* The change from ampersand symbols to CL strings breaks opmap (the call to
158 ?getf in particular).
163 else block([fun:?getf(?cdr(lst),inpart(exp,0))],
165 then block([op_fcn_list:lst],
170 opmap(exp, lst) := block([op_fcn_list : lst, e_op, x, fun : false, inflag : true],
171 if mapatom(exp) then exp
177 if x = e_op and lst # [ ] then (
180 if fun = false then exp else apply(fun, [exp])))$
182 iflopmap(op,fun,exp) :=
184 then genut_prodmap(fun,exp)
187 else if inpart(exp,0)=op
189 else apply(fun,[exp])$
191 genut_prodmap(fun,exp):=
194 else block([mop:inpart(exp,0)],
196 then map(lambda([exp],genut_prodmap(fun,exp)),exp)
197 else if mop="^" and askinteger(inpart(exp,2))='yes
198 then substinpart(genut_prodmap(fun,inpart(exp,1)),exp,1)
199 else apply(fun,[exp]))$
201 /* Returns a list of the sub-expressions of EXP which have one of the
202 operators in OPL as main operator. */
204 listofops_nonrat(exp,[opl]) :=
205 listofops_nonratl(exp,opl,[])$
207 listofops_nonratl(exp,opl,lst) :=
208 block(if atom(exp) then return(lst),
209 if member(inpart(exp,0), opl)
210 then return(cons(exp,lst))
211 else for expdum in exp do
212 lst:listofops_nonratl(expdum,opl,lst),
215 /* Extracts a common factor from the list of expressions EXPL and
216 returns it as first element COMMON of a list. The second element
217 is a list OTHER such that EXPL = COMMON * OTHER. */
219 explicitfactor(expl) :=
220 block([vars:map(lambda([dum],?gensym()),expl),common,other],
221 setlist(orpartitionl(factor(expl . vars),"*",vars),'common,'other),
222 [common,map(lambda([var],coeff(other,var)),vars)])$
224 /* Don't ask. It's only used once, in declin.mac. */
226 lcpred(linp,is_op,exp) :=
229 else if apply(is_op,[exp])
231 else if inpart(exp,0)="*"
232 then is(length(last(predpartition(exp,is_op)))=1)
233 else if inpart(exp,0)="+"
234 then is(first(predpartition(exp,linp))=[])
237 /* The following two functions are copied from rncomb.mac */
239 rloiewl(op,exp):=block(
240 [partswitch:true,inflag:true,piece],
245 predpartition(list,predicate):=block(
246 [nolist:[],yeslist:[]],
247 for idum in reverse(list) do
248 if mode_identity(boolean,apply(predicate,[idum]))
249 then yeslist:cons(idum,yeslist)
250 else nolist:cons(idum,nolist),
253 /* a sort of `provide' */
254 put('gnauto,true,'diageval_version)$
256 /* genut.mac ends here */