polydiv.red 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. module polydiv; % Enhanced polynomial division.
  2. % F.J.Wright@Maths.QMW.ac.uk, 6 Nov 1995.
  3. % Defines (or redefines) the following polynomial division operators:
  4. % divide, div and remainder (mod),
  5. % pseudo_divide, pseudo_quotient (pseudo_div) and pseudo_remainder.
  6. % However, for now, div has been commented out, since it conflicts with
  7. % other packages (avector and fide).
  8. % ===================================================================
  9. % Enhanced algebraic-mode operators for performing polynomial division
  10. % over the current coefficient domain, based on the operator REMAINDER
  11. % currently defined in poly.red by put('remainder,'polyfn,'remf);
  12. % divide(p,q) or p divide q returns an algebraic list {quotient,
  13. % remainder} of p divided by q as polynomials over the current domain.
  14. % div(p,q) or p div q returns only the quotient.
  15. % remainder(p,q) or p mod q returns only the remainder.
  16. % div and mod are the operator names used in Pascal for Euclidean
  17. % (integer) division.
  18. % An optional third argument (for the prefix forms) specifies the
  19. % variable to treat as the leading variable for the (effectively
  20. % univariate) polynomial division.
  21. % Interface code
  22. % ==============
  23. % Regular division:
  24. % ----------------
  25. put('divide, 'psopfn, 'poly!-divide);
  26. symbolic procedure poly!-divide u;
  27. poly!-divide!*(u, nil, nil);
  28. remprop('remainder, 'polyfn);
  29. put('remainder, 'psopfn, 'poly!-remainder);
  30. put('mod, 'psopfn, 'poly!-remainder); % name from Pascal
  31. symbolic procedure poly!-remainder u;
  32. poly!-divide!*(u, 'remainder, nil);
  33. % put('div, 'psopfn, 'poly!-quotient); % name from Pascal
  34. symbolic procedure poly!-quotient u;
  35. poly!-divide!*(u, 'quotient, nil);
  36. infix divide, mod;
  37. % infix div;
  38. % Set a relatively low precedence:
  39. precedence divide, freeof; % higher than freeof, lower than +
  40. % precedence div, divide;
  41. % precedence mod, div;
  42. % Pseudo-division:
  43. % ---------------
  44. put('pseudo_divide, 'psopfn, 'pseudo!-divide);
  45. symbolic procedure pseudo!-divide u;
  46. poly!-divide!*(u, nil, t);
  47. put('pseudo_remainder, 'psopfn, 'pseudo!-remainder);
  48. symbolic procedure pseudo!-remainder u;
  49. poly!-divide!*(u, 'remainder, t);
  50. put('pseudo_div, 'psopfn, 'pseudo!-quotient);
  51. put('pseudo_quotient, 'psopfn, 'pseudo!-quotient);
  52. symbolic procedure pseudo!-quotient u;
  53. poly!-divide!*(u, 'quotient, t);
  54. fluid '(kord!*);
  55. symbolic procedure poly!-divide!*(u, fn, pseudo); % u = (p, q, x)
  56. % Returns the quotient and remainder of p (pseudo-)divided by q.
  57. % If specified, x is made the leading variable before dividing,
  58. % otherwise the first variable found is used.
  59. begin scalar p, q, x, new_kord;
  60. if null cdr u then RedErr "Divisor required";
  61. if length u > 3 then
  62. RedErr "Division operators take 2 or 3 arguments.";
  63. if null (q := !*a2f cadr u) then RedErr "Zero divisor";
  64. p := !*a2f car u;
  65. if cddr u and (x := !*a2k caddr u) and
  66. not(kord!* and x eq car kord!*) then <<
  67. new_kord := t; updkorder x;
  68. p := reorder p; q := reorder q
  69. >> where kord!* = kord!*; % preserve environment
  70. u := if pseudo then pseudo!-qremf(p, q, x) else qremf(p, q);
  71. p := car u; q := cdr u;
  72. if new_kord then <<
  73. if not(fn eq 'remainder) then p := reorder p;
  74. if not(fn eq 'quotient) then q := reorder q
  75. >>;
  76. return
  77. if fn eq 'remainder then mk!*sq (q ./ 1)
  78. else if fn eq 'quotient then mk!*sq (p ./ 1)
  79. else {'list, mk!*sq (p ./ 1), mk!*sq (q ./ 1)}
  80. end;
  81. % Pseudo-division code
  82. % ====================
  83. symbolic procedure pseudo!-qremf(u, v, var);
  84. % Returns quotient and remainder of u pseudo-divided by v wrt var.
  85. % u and v are standard forms, var is a kernel or nil.
  86. % If var = nil then var := first kernel found.
  87. % Internally, polynomials are represented as coeff lists wrt var,
  88. % i.e. as lists of forms.
  89. % (Knuth 1981, Seminumerical Algorithms, Algorithm R, page 407.)
  90. begin scalar no_var, m, n, k, q0, q, car_v, car_u, vv;
  91. no_var := null var;
  92. m := if domainp u or (var and not(mvar u eq var)) then 0
  93. else << if not var then var := mvar u; ldeg u >>;
  94. n := if domainp v or (var and not(mvar v eq var)) then 0
  95. else << if not var then var := mvar v; ldeg v >>;
  96. %% The following special-case code for n = 0 and m < n is not
  97. %% necessary, but could be a cheap efficiency measure.
  98. %% if zerop n then return multf(exptf(v,m), u) . nil;
  99. %% if minusp(k := m - n) then return nil . u;
  100. u := if zerop m then {u} else coeffs u;
  101. v := if zerop n then {v} else coeffs v;
  102. if no_var and not(domainp_list v and domainp_list u) then
  103. msgpri("Main division variable selected is", var,
  104. nil, nil, nil);
  105. k := m - n; car_v := car v;
  106. while k >= 0 do <<
  107. %% Compute the quotient q EFFICIENTLY.
  108. %% q0 = (q_0 ... q_k) without powers of v_n
  109. q0 := (car_u := car u) . q0;
  110. vv := cdr v;
  111. u := for each c in cdr u collect <<
  112. c := multf(c, car_v);
  113. if vv then <<
  114. c := subtrf(c, multf(car_u, car vv));
  115. vv := cdr vv
  116. >>;
  117. c
  118. >>;
  119. k := k - 1
  120. >>;
  121. if q0 then <<
  122. %% Reverse q0 and multiply in powers of v_n:
  123. q := car q0 . nil; vv := 1; % v_n^0
  124. while (q0 := cdr q0) do
  125. q := multf(car q0, (vv := multf(vv, car_v))) . q
  126. >>;
  127. return coeffs!-to!-form(q, var) . coeffs!-to!-form(u, var)
  128. end;
  129. symbolic procedure coeffs!-to!-form(coeff_list, var);
  130. % Convert a coefficient list in DESCENDING power order to a
  131. % standard form wrt the specified variable var:
  132. coeff_list and
  133. coeffs!-to!-form1(coeff_list, var, length coeff_list - 1);
  134. symbolic procedure coeffs!-to!-form1(coeff_list, var, d);
  135. if d > 0 then
  136. ( if car coeff_list then
  137. ((var .^ d) .* (car coeff_list)) .+ reductum
  138. else reductum )
  139. where reductum =
  140. coeffs!-to!-form1(cdr coeff_list, var, d - 1)
  141. else car coeff_list;
  142. symbolic procedure domainp_list coeff_list;
  143. % Returns true if argument is a list of domain elements:
  144. null coeff_list or
  145. (domainp car coeff_list and domainp_list cdr coeff_list);
  146. endmodule;
  147. end;