123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- module polydiv; % Enhanced polynomial division.
- % F.J.Wright@Maths.QMW.ac.uk, 6 Nov 1995.
- % Defines (or redefines) the following polynomial division operators:
- % divide, div and remainder (mod),
- % pseudo_divide, pseudo_quotient (pseudo_div) and pseudo_remainder.
- % However, for now, div has been commented out, since it conflicts with
- % other packages (avector and fide).
- % ===================================================================
- % Enhanced algebraic-mode operators for performing polynomial division
- % over the current coefficient domain, based on the operator REMAINDER
- % currently defined in poly.red by put('remainder,'polyfn,'remf);
- % divide(p,q) or p divide q returns an algebraic list {quotient,
- % remainder} of p divided by q as polynomials over the current domain.
- % div(p,q) or p div q returns only the quotient.
- % remainder(p,q) or p mod q returns only the remainder.
- % div and mod are the operator names used in Pascal for Euclidean
- % (integer) division.
- % An optional third argument (for the prefix forms) specifies the
- % variable to treat as the leading variable for the (effectively
- % univariate) polynomial division.
- % Interface code
- % ==============
- % Regular division:
- % ----------------
- put('divide, 'psopfn, 'poly!-divide);
- symbolic procedure poly!-divide u;
- poly!-divide!*(u, nil, nil);
- remprop('remainder, 'polyfn);
- put('remainder, 'psopfn, 'poly!-remainder);
- put('mod, 'psopfn, 'poly!-remainder); % name from Pascal
- symbolic procedure poly!-remainder u;
- poly!-divide!*(u, 'remainder, nil);
- % put('div, 'psopfn, 'poly!-quotient); % name from Pascal
- symbolic procedure poly!-quotient u;
- poly!-divide!*(u, 'quotient, nil);
- infix divide, mod;
- % infix div;
- % Set a relatively low precedence:
- precedence divide, freeof; % higher than freeof, lower than +
- % precedence div, divide;
- % precedence mod, div;
- % Pseudo-division:
- % ---------------
- put('pseudo_divide, 'psopfn, 'pseudo!-divide);
- symbolic procedure pseudo!-divide u;
- poly!-divide!*(u, nil, t);
- put('pseudo_remainder, 'psopfn, 'pseudo!-remainder);
- symbolic procedure pseudo!-remainder u;
- poly!-divide!*(u, 'remainder, t);
- put('pseudo_div, 'psopfn, 'pseudo!-quotient);
- put('pseudo_quotient, 'psopfn, 'pseudo!-quotient);
- symbolic procedure pseudo!-quotient u;
- poly!-divide!*(u, 'quotient, t);
- fluid '(kord!*);
- symbolic procedure poly!-divide!*(u, fn, pseudo); % u = (p, q, x)
- % Returns the quotient and remainder of p (pseudo-)divided by q.
- % If specified, x is made the leading variable before dividing,
- % otherwise the first variable found is used.
- begin scalar p, q, x, new_kord;
- if null cdr u then RedErr "Divisor required";
- if length u > 3 then
- RedErr "Division operators take 2 or 3 arguments.";
- if null (q := !*a2f cadr u) then RedErr "Zero divisor";
- p := !*a2f car u;
- if cddr u and (x := !*a2k caddr u) and
- not(kord!* and x eq car kord!*) then <<
- new_kord := t; updkorder x;
- p := reorder p; q := reorder q
- >> where kord!* = kord!*; % preserve environment
- u := if pseudo then pseudo!-qremf(p, q, x) else qremf(p, q);
- p := car u; q := cdr u;
- if new_kord then <<
- if not(fn eq 'remainder) then p := reorder p;
- if not(fn eq 'quotient) then q := reorder q
- >>;
- return
- if fn eq 'remainder then mk!*sq (q ./ 1)
- else if fn eq 'quotient then mk!*sq (p ./ 1)
- else {'list, mk!*sq (p ./ 1), mk!*sq (q ./ 1)}
- end;
- % Pseudo-division code
- % ====================
- symbolic procedure pseudo!-qremf(u, v, var);
- % Returns quotient and remainder of u pseudo-divided by v wrt var.
- % u and v are standard forms, var is a kernel or nil.
- % If var = nil then var := first kernel found.
- % Internally, polynomials are represented as coeff lists wrt var,
- % i.e. as lists of forms.
- % (Knuth 1981, Seminumerical Algorithms, Algorithm R, page 407.)
- begin scalar no_var, m, n, k, q0, q, car_v, car_u, vv;
- no_var := null var;
- m := if domainp u or (var and not(mvar u eq var)) then 0
- else << if not var then var := mvar u; ldeg u >>;
- n := if domainp v or (var and not(mvar v eq var)) then 0
- else << if not var then var := mvar v; ldeg v >>;
- %% The following special-case code for n = 0 and m < n is not
- %% necessary, but could be a cheap efficiency measure.
- %% if zerop n then return multf(exptf(v,m), u) . nil;
- %% if minusp(k := m - n) then return nil . u;
- u := if zerop m then {u} else coeffs u;
- v := if zerop n then {v} else coeffs v;
- if no_var and not(domainp_list v and domainp_list u) then
- msgpri("Main division variable selected is", var,
- nil, nil, nil);
- k := m - n; car_v := car v;
- while k >= 0 do <<
- %% Compute the quotient q EFFICIENTLY.
- %% q0 = (q_0 ... q_k) without powers of v_n
- q0 := (car_u := car u) . q0;
- vv := cdr v;
- u := for each c in cdr u collect <<
- c := multf(c, car_v);
- if vv then <<
- c := subtrf(c, multf(car_u, car vv));
- vv := cdr vv
- >>;
- c
- >>;
- k := k - 1
- >>;
- if q0 then <<
- %% Reverse q0 and multiply in powers of v_n:
- q := car q0 . nil; vv := 1; % v_n^0
- while (q0 := cdr q0) do
- q := multf(car q0, (vv := multf(vv, car_v))) . q
- >>;
- return coeffs!-to!-form(q, var) . coeffs!-to!-form(u, var)
- end;
- symbolic procedure coeffs!-to!-form(coeff_list, var);
- % Convert a coefficient list in DESCENDING power order to a
- % standard form wrt the specified variable var:
- coeff_list and
- coeffs!-to!-form1(coeff_list, var, length coeff_list - 1);
- symbolic procedure coeffs!-to!-form1(coeff_list, var, d);
- if d > 0 then
- ( if car coeff_list then
- ((var .^ d) .* (car coeff_list)) .+ reductum
- else reductum )
- where reductum =
- coeffs!-to!-form1(cdr coeff_list, var, d - 1)
- else car coeff_list;
- symbolic procedure domainp_list coeff_list;
- % Returns true if argument is a list of domain elements:
- null coeff_list or
- (domainp car coeff_list and domainp_list cdr coeff_list);
- endmodule;
- end;
|