multroot.red 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. module multroot; % Code for solving polynomial sets solvable by
  2. % backsubstitution.
  3. % Author: Stanley L. Kameny <stan_kameny@rand.org>.
  4. % Version and Date: Mod 1.96, 30 March 1995.
  5. % Copyright (c) 1994,1995. Stanley L. Kameny. All Rights Reserved.
  6. Comment modules allroot, bfauxil, bfdoer, bfdoer2, complxp, rootaux and
  7. realroot needed also;
  8. fluid '(rterr!! mrerr!! rootacc!#!#);
  9. switch fullprecision,compxroots;
  10. put ('multroot,'psopfn,'multroot1);
  11. symbolic procedure multroot1 u;
  12. if length u neq 2 then rederr
  13. "2 args required: pr=desired precision, pl=polynomial list"
  14. else multroot0(car u,cadr u);
  15. symbolic procedure multroot0(pr,pl);
  16. begin scalar v,ans,pr1,c,r,rterr!!,ra;
  17. !*protfg := t; pr1 := precision 0; r := !*rounded; c := !*complex;
  18. ra := rootacc!#!#;
  19. v := errorset!*({'multroot2,{'multroot01,mkquote pr,mkquote pl}},nil);
  20. !*protfg := nil; rootacc!#!# := ra;
  21. return if errorp v then
  22. <<precision pr1;
  23. (<<if !*rounded then if not r then off rounded else nil
  24. else if r then on rounded;
  25. if !*complex then if not c then off complex else nil
  26. else if c then on complex>> where !*msg=nil);
  27. (if rterr!! then lprim
  28. "for some root value(s), a variable depends on an arbitrary variable"
  29. else if mrerr!! then lprim mrerr!!) where !*msg=t;
  30. mk!*sq mksq({'multroot,pr,reval pl},1)>>
  31. else (<<v := car v; on rounded,complex; ans := aeval v;
  32. if !*rounded then if not r then off rounded else nil
  33. else if r then on rounded;
  34. if !*complex then if not c then off complex else nil
  35. else if c then on complex;
  36. ans>> where !*msg=nil) end;
  37. share npoly!*,pr!*,pl!*;
  38. algebraic procedure multroot01(pr,pl);
  39. comment
  40. pl is a list of n polynomials in a tree containing branches each one of
  41. which contains one univariate polynomial, one bivariate polynomial, ...
  42. one n-variable polynomial in which each successive polynomial adds one
  43. additional variable. These branches may branch, so that one variable
  44. gives rise to several branches. These polynomials are the standard
  45. Groebner output. All polynomials are real with integer coefficients.
  46. pr is the desired minimum precision of the solution set for real roots.
  47. This program will go through each branch by solving p_1 for the first
  48. variable, then will test each succeeding polynomial for the precision of
  49. each additional variable, returning to the initial solution at higher
  50. precision when necessary, until the last polynomial's solution has been
  51. obtained at precision >= pr. The solutions are collected in the
  52. variable solns!*. They are then combined into the final output form by
  53. the function combinesolns();
  54. begin scalar n,links,path,paths,fl,fl1,var,rts,solns; integer maxv;
  55. npoly!* := n := length pl - 1;
  56. % 0..n will be the indices of arrays.
  57. clear vll!#,vll2!#,varbl!#,poln!#,rtlst!#,derv!#,pra!#,fxer!#;
  58. array vll!#(n),vll2!#(n);
  59. pl!* := pl;
  60. pl := pl!* := reval cleardenr pl!*;
  61. % vll!# will be an array of {list of variables}
  62. for j := 0:n do vll!# j := getvars part(pl,j+1);
  63. % vll!# will be a useful tool for finding and tracing trees.
  64. % Trees might possibly be totally independent if there are more
  65. % than one of the vll!#(j) of length 1, or else trees might branch
  66. % if there is more than one way of extending the variable lists from
  67. % a given node. The tree following algorithm must allow for any of
  68. % these. It will be easiest to have a tree algorithm which simply
  69. % enumerates branches as a list of the j values which span the total
  70. % branch from root to tip.
  71. links := findlinks n;
  72. paths := links2paths links;
  73. % if paths = nil then multroot fails.
  74. lisp(mrerr!! := nil);
  75. if not paths then lisp(mrerr!! :=
  76. "multroot fails because no univariate polynomial was given.");
  77. lisp(if mrerr!! then rederr "1");
  78. fl := nil;
  79. for j := 0:n do
  80. <<fl1 := nil;
  81. for each path in paths do if member(j,path) then fl1 := t;
  82. if not fl1 then fl := t >>;
  83. if fl = t then lisp(mrerr!! :=
  84. "multroot failure: at least one polynomial has no single base.");
  85. lisp(if mrerr!! then rederr "2");
  86. % In multroot2, we solve for real roots under the condition that
  87. % the precision of each root >= pr;
  88. array varbl!#(n),poln!#(n),rtlst!#(n),derv!#(n),pra!#(n),fxer!#(n);
  89. % these arrays are used in solvepath, but we don't want them to be
  90. % redefined for different paths. Maximum index will be <=n, but we
  91. % will be careful not to go out of bounds.
  92. vlist!* := rlist!* := solns!* := {};
  93. pr!* := pr;
  94. return paths end;
  95. symbolic operator subsetp,algunion,cleardenr;
  96. symbolic procedure cleardenr pl;
  97. begin scalar plo;
  98. for each pol in cdr pl do
  99. plo := (if eqcar(pol,'quotient) then cadr pol else pol) . plo;
  100. return 'list . reversip plo end;
  101. symbolic procedure algunion(a,b); 'list . union(cdr a,cdr b);
  102. algebraic procedure findlinks n;
  103. begin scalar links,fl,fl1,var;
  104. links := {};
  105. % we have in vll!# an array of lists of variables. If they can form
  106. % a strict hierarchy, we have no problem in solving the polynomials.
  107. % But if they don't, we have to form an artificial hierarchy by
  108. % augmenting the lists.
  109. for m := 1:n do
  110. if m=1 then for j := 0:n do vll2!# j :=
  111. if length(fl := vll!# j)=1 then
  112. if not var then var := fl else vll!# j := append(fl,var)
  113. else fl
  114. else for k := 1:2 do
  115. for j := 0:n do
  116. if length(fl := vll!# j)=m then vll2!# j :=
  117. if length var<m then if subsetp(var,fl) then var := fl
  118. else fl
  119. else if fl=var then fl else vll!# j := algunion(fl,var)
  120. else fl;
  121. repeat <<fl := nil;
  122. for j := 0:n do
  123. <<fl1 := nil;
  124. for k := 0:n do
  125. if j neq k
  126. and length vll2!# j =1 and subset1(vll2!# j,vll2!# k) then
  127. <<links := append(links,{{j,k}}); fl1 := t>>;
  128. if fl1=t then
  129. <<fl := t;
  130. for k := 0:n do
  131. <<var := first vll2!# j;
  132. if j neq k then vll2!# k := delete(var,vll2!# k)>> >> >> >>
  133. until not fl ;
  134. return links end;
  135. algebraic procedure multroot2 paths;
  136. begin scalar path,lfp,fl,soln1,soln2,pr0,nlst;
  137. lp: path := first paths; paths := rest paths;
  138. lfp := last path; fl := nil;
  139. for each path2 in paths do if last path2 = lfp then fl := t;
  140. if fl then <<lisp(mrerr!! :=
  141. "multroot failure: This error should not occur!");
  142. rederr "3">>;
  143. % this is the place where it is reasonable to eliminate spurious
  144. % real or imaginary parts of complex roots.
  145. soln1 := solvepath path;
  146. if lisp !*compxroots and (nlst := spurival soln1) neq {} then
  147. <<pr0 := precision 0; pr!* := pr!*+5;
  148. soln2 := solvepath path; precision pr0; pr!* := pr!*-5;
  149. soln1 := spurifix(nlst,soln1,soln2)>>;
  150. if not member(v0!*,vlist!*) then
  151. % here we save the initial roots or realroots answers so they
  152. % won't be computed redundantly.
  153. <<vlist!* := append(vlist!*,{v0!*});
  154. rlist!* := append(rlist!*,{r0!*})>>;
  155. if soln1={} then <<fl := t; go to cl>>;
  156. solns!* := append(solns!*,{soln1});
  157. if paths neq {} then go to lp;
  158. cl: clear vll!#,vll2!#,varbl!#,poln!#,rtlst!#,derv!#,pra!#,fxer!#;
  159. return if fl then {} else combinesolns() end;
  160. algebraic procedure combinesolns();
  161. comment
  162. We have all of the separate solutions in solns!* and a list of the
  163. independent variables in vlist!* and the root values of the base
  164. variables in the list rlist!*. So we can use this information to
  165. combine the separate solutions into an outer product of all solutions.
  166. In doing this, we will first combine all terms with the same base
  167. variable, then combine all of those outer products into one grand
  168. product. Finally, we sort the variables into standard order and sort
  169. the values into order by the real part of the first variable.$
  170. begin scalar vlist,rlist,prod,solns,var,rts,grandprod;
  171. vlist := vlist!*; rlist := rlist!*; grandprod := {};
  172. lp2: var := first vlist; vlist := rest vlist;
  173. rts := first rlist; rlist := rest rlist;
  174. prod := {}; solns := solns!*;
  175. if rts neq {} and solns neq {{}} then
  176. for each soln1 in solns do
  177. if isvar(first soln1,var)
  178. then prod := if prod = {} then soln1
  179. else outcombine1(rts,prod,soln1);
  180. grandprod := if grandprod = {} then prod
  181. else outcombine2(grandprod,prod);
  182. if vlist neq {} then go to lp2;
  183. grandsoln!* := grandprod;
  184. sortvars();
  185. screensolns1();
  186. return sortvals();
  187. end;
  188. symbolic operator screensolns1;
  189. symbolic procedure screensolns1();
  190. begin scalar inlist,outlist,termout,vr,vr1,vl,vl1,fl;
  191. inlist := reversip cdr algebraic varsortsolns!*;
  192. for each rts in inlist do
  193. <<vr := vr1 := fl := termout := nil;
  194. for each term in cdr rts do if not fl then
  195. <<vr1 := cadr term; vl1 := caddr term;
  196. % if not vr or vr neq vr1 then
  197. if not vr or algebraic lisp {'difference,vr,vr1} neq 0 then
  198. <<vr := vr1; vl := vl1; termout := term . termout>>
  199. % else if vl1 neq vl then fl := t>>;
  200. else if algebraic lisp {'difference,vl1,vl} neq 0
  201. then fl := t>>;
  202. if not fl then outlist := ('list.reversip termout).outlist>>;
  203. return algebraic varsortsolns!* := ('list . outlist) end;
  204. algebraic procedure solvepath path;
  205. begin scalar
  206. n,vl,vlll,m,s,rts0,fl,fl1,b,tst,f,ff,pr1,prf,strt,dfx,rtl,rt1,!*msg,
  207. zz,r,c;
  208. n := length path;
  209. if (r := lisp !*rounded) then off rounded;
  210. if (c := lisp !*complex) then off complex;
  211. % now we assemble the polynomial tree which represents this path.
  212. vl := for j := 1:n collect part(pl!*,part(path,j)+1);
  213. % we now have ordered the polynomials in order of the number of
  214. % variables.
  215. vlll := for j := 1:n collect vll!# part(path,j);
  216. % and have now ordered the variable list in increasing number of
  217. % variables, so that vlll correctly lists the variables in the
  218. % reordered list vl;
  219. % Now we solve for real roots under the condition that the precision
  220. % of each root >= pr!*;
  221. n := n-1; % this is done because arrays will have indices 0..n.
  222. pra!#(0) := pr!*+10; on rounded;
  223. for j := 1:n do pra!#(j) := pr!*;
  224. % a starting point: this may have to be increased if necessary.
  225. v0!* := varbl!#(0) := first first vlll; strt := t;
  226. str: precision pra!#(0);
  227. rts0 := if strt and member(v0!*,vlist!*)
  228. then (r0!* := part(rlist!*,membno(v0!*,vlist!*)))
  229. else if lisp !*compxroots then roots first vl
  230. else realroots first vl;
  231. r0!* := rts0;
  232. rtlst!#(0) := for each rt in rts0 collect {rt};
  233. m := 0; strt := nil;
  234. nxt: fl := fl1 := b := 0;
  235. if (m := m+1) > n then <<m := m-1; go to ret>>;
  236. poln!#(m) := part(vl,m+1);
  237. rtl := {};
  238. for each rt in rtlst!#(m-1) do
  239. <<zz := sub(rt,poln!#(m));
  240. % zz is a polynomial, or possibly a constant. If it is 0
  241. % then whatever variables it might represent are arbitrary, but
  242. % if is a nonzero constant, the path stops here.
  243. rt1 := if mainvar zz=0 then if zz=0 then {rt} else {}
  244. else combinerts(rt,
  245. <<lisp(rterr!! := t);
  246. zz := if lisp !*compxroots then roots zz else realroots zz;
  247. lisp(rterr!! := nil); zz>>);
  248. if rt1 neq {} then rtl := append(rtl,rt1)>>;
  249. rtlst!#(m) := rtl;
  250. s := length rtlst!#(m);
  251. varbl!#(m) := elim(part(vlll,m),part(vlll,m+1));
  252. derv!#(m) :=
  253. {-(df(poln!#(m),varbl!#(0))+
  254. if m<2 then 0 else for j := 1:(m-1) sum
  255. (df(poln!#(m),varbl!#(j))*first derv!#(j)/second derv!#(j)))
  256. ,df(poln!#(m),varbl!#(m))};
  257. lp1: if (b := b+1) > s then
  258. <<prf := nil;
  259. if fl > 0 then
  260. <<fxer!#(m) := pfx(pr!*,fl); fl := 0;
  261. dfx := fxer!#(m) - fxer!#(m-1);
  262. for j := 0:m-1 do
  263. <<pr1 := pra!#(j) + dfx;
  264. if pr1>pra!#(j) then
  265. <<prf := t; pra!#(j) := pr1>> >> >>;
  266. if prf then go to str else go to nxt>>
  267. else
  268. <<if (tst :=
  269. abs(10.0^(-pra!#(0))*cabs
  270. testsub(part(rtlst!#(m),b),derv!#(m),m)) )>10.0^-pr!*
  271. then fl1 := tst;
  272. if fl1 > fl then fl := fl1;
  273. go to lp1>>;
  274. ret: if r then on rounded; if c then on complex;
  275. if not lisp !*fullprecision then go to rt2;
  276. precision pra!#(0); return
  277. if m=0 then rtlst!#(0)
  278. else for each rtl in rtlst!#(m) collect
  279. for j := 0:m collect roundroot(part(rtl,j+1),pra!#(j));
  280. rt2: precision pr!*;
  281. return rtlst!#(m)
  282. end;
  283. algebraic procedure testsub(lst,quotlst,m);
  284. % this substitutes the variable value list lst into the derivative
  285. % quotient quotlst, but avoids 0/0 errors. For the purposes to which
  286. % we are putting testsub, infinity is replaced by 1.
  287. begin scalar nmr,dnr,dnv;
  288. nmr := first quotlst; dnr := second quotlst;
  289. ex!! := algebraic nmr/algebraic dnr;
  290. nmr := num ex!!; dnr := den ex!!;
  291. while (dnv := sub(lst,dnr))=0 do if sub(lst,nmr)=0 then
  292. <<nmr := df(nmr,varbl!#(m)); dnr := df(dnr,varbl!#(m));
  293. if dnr neq 0 then
  294. <<ex!! := algebraic nmr/algebraic dnr;
  295. nmr := num ex!!; dnr := den ex!!>>
  296. else (<<nmr := 1; dnr := 1;
  297. lprim "stuffing 1 (dnr prob)">> where !*msg=t) >>
  298. else (<<nmr := 1; dnr := 1;
  299. lprim "stuffing 1 (nmr prob)">> where !*msg=t);
  300. return sub(lst,nmr)/dnv end;
  301. symbolic procedure sortvals1(a,b);
  302. begin scalar c,d; a := cdr a; b := cdr b;
  303. % since some variables (and hence their values) may not occur...
  304. lp: if not a or not b then return nil;
  305. c := caddar a; d := caddar b;
  306. algebraic (c := repart c); algebraic (d := repart d);
  307. algebraic if c < d then return t else if c = d then go to tst;
  308. return nil;
  309. tst: if (a := cdr a) then <<b := cdr b; go to lp>> end;
  310. algebraic procedure getvars p;
  311. begin scalar vl,v,v1,lt,c,!*msg;
  312. c := lisp !*complex; if not c then on complex;
  313. vl := {};
  314. lp1: if numberp(v := mainvar p) then go to ret
  315. else if not member(v,vl) then vl := v . vl;
  316. lt := lcof(p,v); p := reduct(p,v);
  317. lp2: if numberp(v1 := mainvar lt) then goto lp1
  318. else if not member(v1,vl) then vl := v1 . vl;
  319. lt := lcof(lt,v1); go to lp2;
  320. ret: if not c then off complex;
  321. return reverse vl end;
  322. symbolic operator spurival,spurifix;
  323. share val!$,val2!$,eps!$,pr!*;
  324. symbolic procedure spurival vals;
  325. % produces a list of flattened index of suspicious terms (starting at
  326. % 1) or {}. spurifix then checks the indexed items and trims spurious
  327. % values.
  328. begin scalar fl,r,c,!*msg; integer m; eps!$ := 10.0^-pr!*;
  329. r := !*rounded; c := !*complex; on rounded; off complex;
  330. for each rlst in cdr vals do for each val in cdr rlst do
  331. <<m := m+1; val!$ := prepsq simp!* caddr val;
  332. if eqcar(val!$,'plus) and algebraic testval1 val!$
  333. then fl := m . fl>>;
  334. if not r then off rounded; if c then on complex;
  335. return 'list . reversip fl end;
  336. algebraic procedure testval1 val;
  337. begin scalar rl,im;
  338. rl := abs repart val; im := abs impart val;
  339. if rl>0 and im>0 and im<eps!$*rl or rl<eps!$*im then return t end;
  340. symbolic procedure spurifix(ndx,soln1,soln2);
  341. % soln1 is vals used in spurival, soln2 is vals for higher precision,
  342. % and ndx is flattened index of those to be tested. All indexed
  343. % pairs of roots will be checked and a new soln1 list is made with
  344. % unmatched small parts of complex roots stripped off.
  345. if null ndx or length soln1 neq length soln2 then soln1 else
  346. begin scalar sol0,soln3,lst1,lst2,lst3,val1,val2,eq1,eq2,
  347. eq3,r,c,!*msg,tri;
  348. integer m,m0;
  349. r := !*rounded; c := !*complex; on rounded; off complex;
  350. soln1 := cdr (sol0 :=soln1); soln2 := cdr soln2; ndx := cdr ndx;
  351. m0 := car ndx; ndx := cdr ndx;
  352. lp1: if not soln1 then <<soln3 := 'list . reversip soln3; go to ret>>;
  353. lst1 := cdar soln1; lst2 := cdar soln2;
  354. soln1 := cdr soln1; soln2 := cdr soln2;
  355. if length lst1 neq length lst2 then <<soln3 := sol0; go to ret>>;
  356. lp2: if not lst1 then
  357. <<soln3 := ('list . reversip lst3) . soln3;
  358. lst3 := nil; go to lp1>>;
  359. eq1 := car lst1; eq2 := car lst2; m := m+1;
  360. lst1 := cdr lst1; lst2 := cdr lst2;
  361. if not m0 or m<m0 then <<lst3 := eq1 . lst3; go to lp2>>;
  362. if not ndx then m0 := nil else
  363. <<m0 := car ndx; ndx := cdr ndx>>;
  364. eq1 := eq1;
  365. val!$ := prepsq simp!* (val1 := caddr eq1);
  366. val2!$ := prepsq simp!* (val2 := caddr eq2);
  367. tri := algebraic testval2(val!$,val2!$);
  368. if tri=aeval 'failed then
  369. <<((lprim
  370. "match failed! root stripping aborted: raw roots returned")
  371. where !*msg=t); soln3 := sol0; go to ret>>;
  372. eq3 := if tri=0 then eq1
  373. else {'equal,cadr eq1,
  374. if freeof(car(val1 := cdr val1),'i)
  375. then if tri=1 then cadr val1 else car val1
  376. else if tri=1 then car val1 else cadr val1};
  377. lst3 := eq3 . lst3; go to lp2;
  378. ret: if not r then off rounded; if c then on complex;
  379. return soln3 end;
  380. algebraic procedure testval2(a,b);
  381. begin scalar rl1,rl2,im1,im2;
  382. rl1 := abs repart a; rl2 := abs repart b; im1 := abs impart a;
  383. im2 := abs impart b;
  384. return if rl1=rl2 then if im1=im2 then 0 else 2
  385. else if im1=im2 then 1 else failed end;
  386. %%end; %this is the end of the changed or added functions
  387. symbolic procedure isvar(x,var);
  388. (if eqcar(x,'list) then cadadr x else cadr x)=var;
  389. symbolic operator isvar;
  390. algebraic procedure outcombine1(rts,p1,p2);
  391. % here p1 is an outerproduct, and p2 is another outerproduct with
  392. % the same first variable. from the two, we form a single outerproduct
  393. % by forming all lists with the first variable appearing only once.
  394. % rts is the root list for the base variable. A complication is caused
  395. % by the possibility that one of more root values may be missing from
  396. % p1, p2, or both.
  397. begin scalar prod,p1strt,p1end,p2strt,p2end;
  398. prod := {};
  399. for each rt in rts do
  400. <<p1end := second(p1strt := findvals(p1,rt)); p1strt := first p1strt;
  401. p2end := second(p2strt := findvals(p2,rt)); p2strt := first p2strt;
  402. if p1strt > 0 and p2strt > 0 then
  403. for n1 := p1strt:p1end do for n2 := p2strt:p2end do
  404. prod := append(prod,{append(part(p1,n1),rest part(p2,n2))})>>;
  405. return prod end;
  406. symbolic procedure findvals(p,rt);
  407. begin scalar val,pp,pp1; integer n,b,e;
  408. val := algebraic lisp caddr rt; pp := cdr p;
  409. lp: pp1 := car pp; pp := cdr pp; n := n+1;
  410. % if algebraic lisp caddr cadr pp1 = val then
  411. if algebraic lisp {'difference,caddr cadr pp1,val} = 0 then
  412. <<if b = 0 then b := n; e := n>>;
  413. if pp then go to lp;
  414. return 'list . {b,e} end;
  415. symbolic operator findvals;
  416. algebraic procedure outcombine2(p1,p2);
  417. begin scalar prod;
  418. prod := {};
  419. for each r1 in p1 do for each r2 in p2 do
  420. prod := append(prod,{append(r1,r2)});
  421. return prod end;
  422. symbolic procedure sortvars();
  423. algebraic varsortsolns!* :=
  424. 'list . for each rts in cdr algebraic grandsoln!*
  425. collect 'list . sort(cdr rts,
  426. function (lambda(a,b); ordop(cadr a,cadr b)));
  427. symbolic operator sortvars;
  428. symbolic procedure sortvals();
  429. algebraic sortvals!* := 'list . sort(cdr algebraic varsortsolns!*,
  430. function sortvals1);
  431. symbolic operator sortvals;
  432. algebraic procedure cabs x;
  433. if lisp !*compxroots then sqrt((repart x)^2 + (impart x)^2) else x;
  434. algebraic procedure membno(n,l);
  435. if n=first l then 1
  436. else if rest l = 0 then 0 else 1 + membno(n,rest l);
  437. algebraic procedure getpaths n;
  438. % this is used to call links2paths for testing purposes only.
  439. begin scalar links; links := {};
  440. for j := 0:n do for k := 0:n do
  441. if j neq k and subset1(vll!# j,vll!# k) then
  442. links := append(links,{{j,k}});
  443. return links2paths links end;
  444. algebraic procedure links2paths links;
  445. begin scalar paths,paths2,bases,fl,fl2;
  446. paths := paths2 := {}; bases := root npoly!*;
  447. % multroot will not work if there are no bases;
  448. if bases = {} then return nil;
  449. % extend from bases if possible.
  450. for each base in bases do
  451. <<fl := nil;
  452. for each link in links do if base = first link then
  453. <<fl := t; paths2 := append(paths2,{link})>>;
  454. if not fl then paths := append(paths,{{base}})>>;
  455. % now extend each path in paths2 if possible. When fully
  456. % extended, add path to paths.
  457. ext: fl2 := nil;
  458. for each path in paths2 do
  459. <<fl := nil;
  460. for each link in links do if first link = last path then
  461. <<fl := t; fl2 := t;
  462. paths2 := append(paths2,{append(path,{second link})})>>;
  463. if not fl then paths := append(paths,{path});
  464. paths2 := delete(path,paths2)>>;
  465. if fl2 then go to ext;
  466. return paths end;
  467. symbolic operator delete;
  468. algebraic procedure last x; first reverse x;
  469. symbolic procedure subset1(a,b);
  470. length b-length a=1 and subsetp(a,b);
  471. symbolic operator subset1;
  472. algebraic procedure root n;
  473. begin scalar trrt; trrt := {};
  474. for j := 0:n do if length vll!# j=1 then trrt := append(trrt,{j});
  475. return trrt end;
  476. algebraic procedure pfx(pr,fl);
  477. begin scalar prf,f,ff;
  478. prf := fl*10.0^pr; ff := 1.0; f := 0;
  479. while prf*ff>1.0 do <<ff := ff/10; f := f+1>>;
  480. return f end;
  481. symbolic procedure roundroot(a,p);
  482. <<p := {'equal,cadr a, if caaddr a = 'minus then
  483. {'minus, rtrnda(cadr caddr a,p)}
  484. else rtrnda(caddr a,p)};
  485. algebraic p>>;
  486. symbolic operator roundroot;
  487. algebraic procedure elim(a,b);
  488. % compares list b with list a (whose length is shorter by 1) and
  489. % returns the unmatched member.
  490. begin scalar x;
  491. for each el in b do if not member(el,a) then x := el;
  492. return x end;
  493. algebraic procedure combinerts(r0,r1);
  494. begin scalar xout; xout := {};
  495. return if r1 = {} then {} else
  496. <<for each rt1 in r1 do
  497. xout := append(xout,{append(r0,{rt1})});
  498. xout>> end;
  499. endmodule;
  500. end;