quartic.red 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. module quartic; % Procedures for solving cubic, quadratic and quartic
  2. % eqns.
  3. % Author: Anthony C. Hearn.
  4. % Modifications by: Stanley L. Kameny, Eberhard Schruefer.
  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!* (if eqcar(z,'equal) then caddr z
  39. else errach {"Quadratic confusion",z})
  40. else begin scalar d;
  41. d := sqrtq subtrsq(quotsqf(exptsq(a1,2),4),multsq(a2,a0));
  42. a1 := quotsqf(negsq a1,2);
  43. return list(subs2!* quotsq(addsq(a1,d),a2),
  44. subs2!* quotsq(subtrsq(a1,d),a2))
  45. end;
  46. symbolic procedure numcoef a; denr a = 1 and domainp numr a;
  47. symbolic procedure mkpolyexp2(a2,a1,a0);
  48. % The use of 'x is arbitrary here, since it is not used by root_val.
  49. <<a0 := numr a0;
  50. if numr a1 then a0 := (('x . 1) . numr a1) . a0;
  51. mk!*sq(((('x . 2) . numr a2) . a0) . 1)>>;
  52. symbolic procedure solvecubic(a3,a2,a1,a0);
  53. % A3, a2, a1 and a0 are standard quotients.
  54. % Solves a3*x**3+a2*x**2+a1*x+a0=0 for x.
  55. % Returns a list of standard quotient solutions.
  56. % See Abramowitz and Stegun, Sect. 3.8.2, for solutions in
  57. % terms of surds and Bronstein for solutions in terms of
  58. % trig functions.
  59. begin scalar q,r,sm,sp,s1,s2,x;
  60. a2 := quotsq(a2,a3);
  61. a1 := quotsq(a1,a3);
  62. a0 := quotsq(a0,a3);
  63. q := subtrsq(quotsqf(a1,3),quotsqf(exptsq(a2,2),9));
  64. r := subtrsq(quotsqf(subtrsq(multsq(a1,a2),multfq(3,a0)),6),
  65. quotsqf(exptsq(a2,3),27));
  66. if null numr q or not !*trigform or not all_real(a0,a1,a2)
  67. then go to cbr;
  68. % this section uses trig functions, but only when a0,a1,a2 are real.
  69. s2 := sqrtq simp {'abs,prepsq q};
  70. if pos_num r then s2 := negsq s2;
  71. if pos_num q
  72. then <<s1 := quotsqf(trigsq(quotsq(negsq r,exptsq(s2,3)),'asinh),3);
  73. sp := trigsq(s1,'sinh);
  74. sm := multsq(simp '(times (sqrt 3) i),trigsq(s1,'cosh))>>
  75. else if pos_num addsq(exptsq(q,3),exptsq(r,2))
  76. then <<s1 := quotsqf(trigsq(quotsq(negsq r,exptsq(s2,3)),'acosh),3);
  77. sp := trigsq(s1,'cosh);
  78. sm := multsq(simp '(times (sqrt 3) i),trigsq(s1,'sinh))>>
  79. else <<s1 := quotsqf(trigsq(quotsq(negsq r,exptsq(s2,3)),'acos),3);
  80. sp := trigsq(s1,'cos);
  81. sm := multsq(simp '(sqrt 3),trigsq(s1,'sin))>>;
  82. return {subs2!* subtrsq(multsq(s2,multsq(-2 ./ 1,sp)),quotsqf(a2,3)),
  83. subs2!* subtrsq(multsq(s2,addsq(sp,sm)),quotsqf(a2,3)),
  84. subs2!* subtrsq(multsq(s2,subtrsq(sp,sm)),quotsqf(a2,3))};
  85. cbr: x := sqrtq addsq(exptsq(q,3),exptsq(r,2));
  86. s1 := cubertq addsq(r,x);
  87. s2 := if numr s1 then negsq quotsq(q,s1)
  88. else cubertq subtrsq(r,x);
  89. % This optimization only works if s1 is non zero.
  90. sp := addsq(s1,s2);
  91. sm := quotsqf(multsq(simp '(times i (sqrt 3)),subtrsq(s1,s2)),2);
  92. com: x := subtrsq(sp,quotsqf(a2,3));
  93. sp := negsq addsq(quotsqf(sp,2),quotsqf(a2,3));
  94. return list(subs2!* x,subs2!* addsq(sp,sm),
  95. subs2!* subtrsq(sp,sm))
  96. end;
  97. symbolic procedure pos_num a;
  98. begin scalar dmode,!*msg,!*numval;
  99. dmode := dmode!*;
  100. !*numval := t;
  101. on rounded,complex;
  102. a := resimp a;
  103. a := real_1 a and (numr simp list('sign,mk!*sq a)=1);
  104. off rounded,complex;
  105. if dmode then onoff(get(dmode,'dname),t);
  106. return a
  107. end;
  108. symbolic procedure trigsq(a,fn);
  109. simpiden list(fn,mk!*sq subs2!* a);
  110. symbolic procedure all_real(a,b,c);
  111. begin scalar dmode,!*ezgcd,!*msg,!*numval;
  112. % If ezgcd is on, modular arithmetic with rounded numbers can be
  113. % attempted.
  114. dmode := dmode!*;
  115. !*numval := t;
  116. on complex,rounded;
  117. % We should probably put an errorset here, so mode is correctly
  118. % reset with an error.
  119. a := real_1(a := resimp a) and real_1(b := resimp b)
  120. and real_1(c := resimp c);
  121. off rounded,complex;
  122. if dmode then onoff(get(dmode,'dname),t);
  123. return a
  124. end;
  125. symbolic procedure real_1 x;
  126. numberp denr x and domainp numr x and null numr impartsq x;
  127. symbolic procedure one_real a;
  128. begin scalar dmode,!*msg,!*numval;
  129. dmode := dmode!*;
  130. !*numval := t;
  131. on complex,rounded;
  132. a := real_1 resimp a;
  133. off rounded,complex;
  134. if dmode then onoff(get(dmode,'dname),t);
  135. return a end;
  136. symbolic procedure solvequartic(a4,a3,a2,a1,a0);
  137. % Solve the quartic equation a4*x**4+a3*x**3+a2*x**2+a1*x+a0 = 0,
  138. % where the ai are standard quotients, using technique described in
  139. % Section 3.8.3 of Abramowitz and Stegun;
  140. begin scalar x,y,yy,cx,z,s,l,zz1,zz2,dmode,neg,!*msg,!*numval;
  141. % Convert equation to monomial form.
  142. dmode := dmode!*;
  143. a3 := quotsq(a3,a4);
  144. a2 := quotsq(a2,a4);
  145. a1 := quotsq(a1,a4);
  146. a0 := quotsq(a0,a4);
  147. % Build and solve the resultant cubic equation. We select the
  148. % real root if there is only one; or if there are three, we choose
  149. % one that yields real coefficients for the quadratics. If no
  150. % roots are known to be real, we use an arbitrary one.
  151. yy := subtrsq(exptsq(a3,2),multfq(4,a2));
  152. x := solvecubic(!*f2q 1,
  153. negsq a2,
  154. subs2!* subtrsq(multsq(a1,a3),multfq(4,a0)),
  155. subs2!* negsq addsq(exptsq(a1,2),
  156. multsq(a0,yy)));
  157. cx := car x;
  158. % Now check for real roots of the cubic.
  159. for each rr in x do if one_real rr then s := append(s,list rr);
  160. x := if (l := length s)=1 then car s else cx;
  161. % Now solve the two equivalent quadratic equations.
  162. a3 := quotsqf(a3,2); yy := quotsqf(yy,4);
  163. % select real coefficient for quadratic if possible.
  164. y := addsq(yy,x);
  165. if l<2 then go to zz;
  166. loop: if not pos_num negsq y then go to zz else if l=1 then
  167. <<x := cx; y := addsq(yy,x); go to zz>>;
  168. l := l-1; s := cdr s; x := car s;
  169. y := addsq(yy,x); go to loop;
  170. zz: y := sqrtq y;
  171. x := quotsqf(x,2);
  172. z := sqrtq subtrsq(exptsq(x,2),a0);
  173. % the following test is needed, according to some editions of
  174. % Abramowitz and Stegun, to select the correct signs
  175. % (for the terms z) in the quadratics to produce correct roots.
  176. % Unfortunately, this test may fail for coefficients which are not
  177. % numeric because of the inability to recognize zero.
  178. !*numval := t;
  179. on rounded,complex;
  180. if null numr
  181. (zz1 :=
  182. resimp subtrsq(a1,addsq(multsq(subtrsq(a3,y),addsq(x,z)),
  183. multsq(addsq(a3,y),subtrsq(x,z))))) then go to rst;
  184. if null numr
  185. (zz2 :=
  186. resimp subtrsq(a1,addsq(multsq(subtrsq(a3,y),subtrsq(x,z)),
  187. multsq(addsq(a3,y),addsq(x,z)))))
  188. then <<neg := t; go to rst>>;
  189. if domainp numr zz1 and domainp numr zz2
  190. and numberp denr zz1 and numberp denr zz2 and
  191. numr simp list('sign,list('difference,list('norm,mk!*sq zz1),
  192. list('norm,mk!*sq zz2)))=1 then neg := t;
  193. rst: off rounded,complex;
  194. if dmode then onoff(get(dmode,'dname),t);
  195. if neg then z := negsq z;
  196. return append(solvequadratic(!*f2q 1,subtrsq(a3,y),subtrsq(x,z)),
  197. solvequadratic(!*f2q 1,addsq(a3,y),addsq(x,z)))
  198. end;
  199. endmodule;
  200. end;