quartic.red 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. module quartic; % Procedures for solving cubic, quadratic and quartic
  2. % eqns.
  3. % Author: Anthony C. Hearn.
  4. % Modifications by: Stanley L. Kameny.
  5. fluid '(!*sub2 !*rounded !*trigform dmode!*);
  6. !*trigform := t; % Default value.
  7. switch trigform;
  8. symbolic procedure multfq(u,v);
  9. % Multiplies standard form U by standard quotient V.
  10. begin scalar x;
  11. x := gcdf(u,denr v);
  12. return multf(quotf(u,x),numr v) ./ quotf(denr v,x)
  13. end;
  14. symbolic procedure quotsqf(u,v);
  15. % Forms quotient of standard quotient U and standard form V.
  16. begin scalar x;
  17. x := gcdf(numr u,v);
  18. return quotf(numr u,x) ./ multf(quotf(v,x),denr u)
  19. end;
  20. symbolic procedure cubertq u;
  21. % Rationalizing the value in this and the following function leads
  22. % usually to neater results.
  23. % rationalizesq
  24. simpexpt list(mk!*sq subs2!* u,'(quotient 1 3));
  25. % simprad(u,3);
  26. symbolic procedure sqrtq u;
  27. % rationalizesq
  28. simpexpt list(mk!*sq subs2!* u,'(quotient 1 2));
  29. % simprad(u,2);
  30. % symbolic procedure subs2!* u; <<!*sub2 := t; subs2 u>>;
  31. symbolic procedure solvequadratic(a2,a1,a0);
  32. % A2, a1 and a0 are standard quotients.
  33. % Solves a2*x**2+a1*x+a0=0 for x.
  34. % Returns a list of standard quotient solutions.
  35. % Modified to use root_val to compute numeric roots. SLK.
  36. if !*rounded and numcoef a0 and numcoef a1 and numcoef a2
  37. then for each z in cdr root_val list mkpolyexp2(a2,a1,a0)
  38. collect simp!* z else
  39. begin scalar d;
  40. d := sqrtq subtrsq(quotsqf(exptsq(a1,2),4),multsq(a2,a0));
  41. a1 := quotsqf(negsq a1,2);
  42. return list(subs2!* quotsq(addsq(a1,d),a2),
  43. subs2!* quotsq(subtrsq(a1,d),a2))
  44. end;
  45. symbolic procedure numcoef a; denr a = 1 and domainp numr a;
  46. symbolic procedure mkpolyexp2(a2,a1,a0);
  47. % The use of 'x is arbitrary here, since it is not used by root_val.
  48. <<a0 := numr a0;
  49. if numr a1 then a0 := (('x . 1) . numr a1) . a0;
  50. mk!*sq(((('x . 2) . numr a2) . a0) . 1)>>;
  51. symbolic procedure solvecubic(a3,a2,a1,a0);
  52. % A3, a2, a1 and a0 are standard quotients.
  53. % Solves a3*x**3+a2*x**2+a1*x+a0=0 for x.
  54. % Returns a list of standard quotient solutions.
  55. % See Abramowitz and Stegun, Sect. 3.8.2, for details.
  56. begin scalar q,r,sm,sp,s1,s2,x;
  57. a2 := quotsq(a2,a3);
  58. a1 := quotsq(a1,a3);
  59. a0 := quotsq(a0,a3);
  60. q := subtrsq(quotsqf(a1,3),quotsqf(exptsq(a2,2),9));
  61. r := subtrsq(quotsqf(subtrsq(multsq(a1,a2),multfq(3,a0)),6),
  62. quotsqf(exptsq(a2,3),27));
  63. if null numr q or not !*trigform or not all_real(a0,a1,a2)
  64. then go to cbr;
  65. % this section uses trig functions, but only when a0,a1,a2 are real.
  66. x := sqrtq negsq addsq(exptsq(q,3),exptsq(r,2));
  67. if one_real simp list('times,'i,mk!*sq x) and not pos_num q
  68. then x := negsq x;
  69. s1 := quotsqf(atan2q(x,r),3);
  70. s2 := negsq sqrtq negsq q;
  71. sp := negsq multfq(2,multsq(s2,cossq s1));
  72. sm := multsq(simp '(sqrt 3),multsq(s2,sinsq s1));
  73. % sp := -2*sqrt(-q)*cos(atan2(sqrt( - q**3 - r**2),r)/3)$
  74. % sm := - sqrt(-q)*sqrt(3)*sin(atan2(sqrt( - q**3 - r**2),r)/3)$
  75. go to com;
  76. cbr: x := sqrtq addsq(exptsq(q,3),exptsq(r,2));
  77. s1 := cubertq addsq(r,x);
  78. s2 := if numr s1 then negsq quotsq(q,s1)
  79. else cubertq subtrsq(r,x);
  80. % This optimization only works if s1 is non zero.
  81. sp := addsq(s1,s2);
  82. sm := quotsqf(multsq(simp '(times i (sqrt 3)),subtrsq(s1,s2)),2);
  83. com: x := subtrsq(sp,quotsqf(a2,3));
  84. sp := negsq addsq(quotsqf(sp,2),quotsqf(a2,3));
  85. return list(subs2!* x,subs2!* addsq(sp,sm),
  86. subs2!* subtrsq(sp,sm))
  87. end;
  88. symbolic procedure pos_num a;
  89. begin scalar dmode,!*msg,!*numval;
  90. dmode := dmode!*;
  91. !*numval := t;
  92. on rounded,complex;
  93. a := resimp a;
  94. a := real_1 a and (numr simp list('sign,mk!*sq a)=1);
  95. off rounded,complex;
  96. if dmode then onoff(get(dmode,'dname),t);
  97. return a
  98. end;
  99. symbolic procedure sinsq a;
  100. simpiden list('sin,mk!*sq subs2!* a);
  101. symbolic procedure cossq a;
  102. simpiden list('cos,mk!*sq subs2!* a);
  103. symbolic procedure all_real(a,b,c);
  104. begin scalar dmode,!*ezgcd,!*msg,!*numval;
  105. % If ezgcd is on, modular arithmetic with rounded numbers can be
  106. % attempted.
  107. dmode := dmode!*;
  108. !*numval := t;
  109. on complex,rounded;
  110. % We should probably put an errorset here, so mode is correctly
  111. % reset with an error.
  112. a := real_1(a := resimp a) and real_1(b := resimp b)
  113. and real_1(c := resimp c);
  114. off rounded,complex;
  115. if dmode then onoff(get(dmode,'dname),t);
  116. return a
  117. end;
  118. symbolic procedure real_1 x;
  119. numberp denr x and domainp numr x and null numr impartsq x;
  120. symbolic procedure one_real a;
  121. begin scalar dmode,!*msg,!*numval;
  122. dmode := dmode!*;
  123. !*numval := t;
  124. on complex,rounded;
  125. a := real_1 resimp a;
  126. off rounded,complex;
  127. if dmode then onoff(get(dmode,'dname),t);
  128. return a end;
  129. symbolic procedure atan2q(b,a);
  130. % Used by solvecubic to set up trig form expressions for atan2 in
  131. % terms of atan and, where necessary, a bias of +/- pi; or to call
  132. % atan2 directly if numerical solution is called for.
  133. ((begin scalar !*msg,x,y,dmode,q,fg,s1,s2,s3,s4,s5;
  134. y := b := simp!*(b := mk!*sq subs2!* b);
  135. x := a := simp!*(a := mk!*sq subs2!* a);
  136. if domainp numr y and domainp numr x
  137. and numberp denr y and numberp denr x then go to aret;
  138. dmode := dmode!*;
  139. on complex,rounded;
  140. y := resimp b; x := resimp a;
  141. if not(domainp numr y and domainp numr x
  142. and numberp denr y and numberp denr x) then go to ret;
  143. q := sqrtq addsq(exptsq(x,2),exptsq(y,2));
  144. x := quotsq(x,q); y := quotsq(y,q);
  145. if null numr x then
  146. <<s1 := t;
  147. if numr simp list('sqn,list('repart,mk!*sq y))=-1
  148. then s2 := t;
  149. go to ret>>;
  150. s3 := t;
  151. x := numr simp list('sign,list('repart,mk!*sq x));
  152. if x=-1 then
  153. <<y := numr simp list('sign,list('repart,mk!*sq y));
  154. if y=-1 then s4 := t else s5 := t>>;
  155. ret: off rounded,complex;
  156. if dmode then onoff(get(dmode,'dname),t);
  157. if s1 then
  158. fg := quotsqf(simp 'pi,2);
  159. if s2 then fg := negsq fg;
  160. if s3 then fg := simpiden list('atan,mk!*sq quotsq(b,a));
  161. if s4 then fg := subtrsq(fg,simp 'pi);
  162. if s5 then fg := addsq(fg,simp 'pi);
  163. aret: return if fg then fg else
  164. simpiden list('atan2,mk!*sq b,mk!*sq a) end)
  165. where !*numval=t);
  166. symbolic procedure solvequartic(a4,a3,a2,a1,a0);
  167. % Solve the quartic equation a4*x**4+a3*x**3+a2*x**2+a1*x+a0 = 0,
  168. % where the ai are standard quotients, using technique described in
  169. % Section 3.8.3 of Abramowitz and Stegun;
  170. begin scalar x,y,yy,cx,z,s,l,zz1,zz2,dmode,neg,!*msg,!*numval;
  171. % Convert equation to monomial form.
  172. dmode := dmode!*;
  173. a3 := quotsq(a3,a4);
  174. a2 := quotsq(a2,a4);
  175. a1 := quotsq(a1,a4);
  176. a0 := quotsq(a0,a4);
  177. % Build and solve the resultant cubic equation. We select the
  178. % real root if there is only one; or if there are three, we choose
  179. % one that yields real coefficients for the quadratics. If no
  180. % roots are known to be real, we use an arbitrary one.
  181. yy := subtrsq(exptsq(a3,2),multfq(4,a2));
  182. x := solvecubic(!*f2q 1,
  183. negsq a2,
  184. subs2!* subtrsq(multsq(a1,a3),multfq(4,a0)),
  185. subs2!* negsq addsq(exptsq(a1,2),
  186. multsq(a0,yy)));
  187. cx := car x;
  188. % Now check for real roots of the cubic.
  189. for each rr in x do if one_real rr then s := append(s,list rr);
  190. x := if (l := length s)=1 then car s else cx;
  191. % Now solve the two equivalent quadratic equations.
  192. a3 := quotsqf(a3,2); yy := quotsqf(yy,4);
  193. % select real coefficient for quadratic if possible.
  194. y := addsq(yy,x);
  195. if l<2 then go to zz;
  196. loop: if not pos_num negsq y then go to zz else if l=1 then
  197. <<x := cx; y := addsq(yy,x); go to zz>>;
  198. l := l-1; s := cdr s; x := car s;
  199. y := addsq(yy,x); go to loop;
  200. zz: y := sqrtq y;
  201. x := quotsqf(x,2);
  202. z := sqrtq subtrsq(exptsq(x,2),a0);
  203. % the following test is needed, according to some editions of
  204. % Abramowitz and Stegun, to select the correct signs
  205. % (for the terms z) in the quadratics to produce correct roots.
  206. % Unfortunately, this test may fail for coefficients which are not
  207. % numeric because of the inability to recognize zero.
  208. !*numval := t;
  209. on rounded,complex;
  210. if null numr
  211. (zz1 :=
  212. resimp subtrsq(a1,addsq(multsq(subtrsq(a3,y),addsq(x,z)),
  213. multsq(addsq(a3,y),subtrsq(x,z))))) then go to rst;
  214. if null numr
  215. (zz2 :=
  216. resimp subtrsq(a1,addsq(multsq(subtrsq(a3,y),subtrsq(x,z)),
  217. multsq(addsq(a3,y),addsq(x,z)))))
  218. then <<neg := t; go to rst>>;
  219. if domainp numr zz1 and domainp numr zz2
  220. and numberp denr zz1 and numberp denr zz2 and
  221. numr simp list('sign,list('difference,list('norm,mk!*sq zz1),
  222. list('norm,mk!*sq zz2)))=1 then neg := t;
  223. rst: off rounded,complex;
  224. if dmode then onoff(get(dmode,'dname),t);
  225. if neg then z := negsq z;
  226. return append(solvequadratic(!*f2q 1,subtrsq(a3,y),subtrsq(x,z)),
  227. solvequadratic(!*f2q 1,addsq(a3,y),addsq(x,z)))
  228. end;
  229. endmodule;
  230. end;