**Exercise 2.87**

A polynomial is zero if all the coefficients of all its terms are zeroes. To implement this logic we can add the following code to the *polynomial* package:

(define (zero-terms? L)
(if (empty-termlist? L)
#t
(and (=zero? (coeff (first-term L)))
(zero-terms? (rest-terms L)))))
(define (zero-poly? p)
(zero-terms? (term-list p)))
(put '=zero? '(polynomial) zero-poly?)

**Exercise 2.88**

To implement the subtraction operation for polynomials we will follow the given hint and reduce the operation to addition. In order to achieve this we have to define a generic *negate* operation and provide implementations of it for all the types in the arithmetic system. For all types, except polynomials, *negate* can be defined as multiplication by *-1*:

;;generic negate operation
(define (negate x) (apply-generic 'negate x))
;;add to the scheme number package
(put 'negate '(scheme-number)
(lambda (x) (tag (* -1 x))))
;;add to the rational number package
(put 'negate '(rational)
(lambda (x) (tag (mul-rat (make-rat -1 1) x))))
;;add to the complex number package
(put 'negate '(complex)
(lambda (x) (tag (mul-complex (make-from-real-imag -1.0 0.0) x))))

To negate a polynomial, however, we have to negate all the coefficients of its terms:

;;code to be added to the polynomial package
(define (negate-terms L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t (first-term L)))
(adjoin-term (make-term (order t) (negate (coeff t)))
(negate-terms (rest-terms L))))))
(define (negate-poly p)
(make-poly (variable p)
(negate-terms (term-list p))))
(put 'negate '(polynomial) (lambda (p) (tag (negate-poly p))))

Finally, we can reduce subtraction to addition with the negative of the subtrahend as addend:

;;code to be added to the polynomial package
(define (sub-poly p1 p2)
(add-poly p1 (negate-poly p2)))
(put 'sub '(polynomial polynomial)
(lambda (p1 p2) (tag (sub-poly p1 p2))))

**Exercise 2.89**

The following procedures implement the term-list representation as appropriate for dense polynomials and can replace the previous representation in *polynomial* package:

;;Term lists for dense polynomials
(define (make-term order coeff) (list order coeff))
(define (order term) (car term))
(define (coeff term) (cadr term))
(define (the-empty-termlist) '())
(define (first-term term-list)
(make-term (- (length term-list) 1)
(car term-list)))
(define (rest-terms term-list) (cdr term-list))
(define (empty-termlist? term-list) (null? term-list))
(define (adjoin-term term term-list)
(let ((term-order (order term))
(list-length (length term-list)))
(cond ((= term-order list-length)
(cons (coeff term) term-list))
((> term-order list-length)
(cons (coeff term)
(adjoin-term (make-term (- term-order 1) 0) term-list)))
(else (error "Term order to small to be adjoined" (list term term-list))))))

The most interesting parts are the *first-term* and *adjoin-term* procedures.

Since the dense representation is a list of coefficients, *first-term* has to construct a new term from the coefficient found at the *car* of its *term-list* argument and compute its order by taking the length of *term-list* and subtracting *1*.

*Adjoin-list* works by assuming that the order of its first argument – *term* (the term being adjoined) is greater than the order of its second argument – *term-list*. If this condition is not met an error is signaled. Otherwise the resulting term list is the coefficient of the *term*, followed by some number of zeroes, followed by the *term-list* we are adjoining to. This is done recursively. The base case is the situation where *term* “fits” directly in front of the *term-list* – no extra zeroes are required. In the recursive case we *cons* the coefficient of *term* with the result of adjoining a new *term* with a coefficient of zero and an order of one less than that of the current *term* with *term-list*.

**Exercise 2.90**

To make the system support both sparse and dense polynomial representations I have decided to split the *polynomial* package in two separate packages – one for sparse polynomials and one for dense polynomials, each of them containing a different representation of term-lists:

;;;Sparse polynomial package
(define (install-sparse-polynomial-package)
;;internal procedures
(define (make-poly variable term-list)
(cons variable term-list))
(define (variable p) (car p))
(define (term-list p) (cdr p))
(define (same-variable? v1 v2)
(and (symbol? v1) (symbol? v2) (eq? v1 v2)))
;;Term lists for sparse polynomials
(define (make-term order coeff) (list order coeff))
(define (order term) (car term))
(define (coeff term) (cadr term))
(define (the-empty-termlist) '())
(define (first-term term-list) (car term-list))
(define (rest-terms term-list) (cdr term-list))
(define (empty-termlist? term-list) (null? term-list))
(define (adjoin-term term term-list)
(if (=zero? (coeff term))
term-list
(cons term term-list)))
(define (zero-terms? L)
(if (empty-termlist? L)
#t
(and (=zero? (coeff (first-term L)))
(zero-terms? (rest-terms L)))))
(define (negate-terms L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t (first-term L)))
(adjoin-term (make-term (order t) (negate (coeff t)))
(negate-terms (rest-terms L))))))
(define (add-terms L1 L2)
(cond ((empty-termlist? L1) L2)
((empty-termlist? L2) L1)
(else
(let ((t1 (first-term L1))
(t2 (first-term L2)))
(cond ((> (order t1) (order t2))
(adjoin-term t1 (add-terms (rest-terms L1) L2)))
((< (order t1) (order t2))
(adjoin-term t2 (add-terms L1 (rest-terms L2))))
(else
(adjoin-term
(make-term (order t1)
(add (coeff t1) (coeff t2)))
(add-terms (rest-terms L1) (rest-terms L2)))))))))
(define (mul-terms L1 L2)
(if (empty-termlist? L1)
(the-empty-termlist)
(add-terms (mul-term-by-all-terms (first-term L1) L2)
(mul-terms (rest-terms L1) L2))))
(define (mul-term-by-all-terms t1 L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t2 (first-term L)))
(adjoin-term
(make-term (+ (order t1) (order t2))
(mul (coeff t1) (coeff t2)))
(mul-term-by-all-terms t1 (rest-terms L))))))
(define (add-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(add-terms (term-list p1)
(term-list p2)))
(error "Polys not in same variable -- ADD-POLY" (list p1 p2))))
(define (mul-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(mul-terms (term-list p1)
(term-list p2)))
(error "Polys not in same variable -- MUL-POLY" (list p1 p2))))
(define (sub-poly p1 p2)
(add-poly p1 (negate-poly p2)))
(define (zero-poly? p)
(zero-terms? (term-list p)))
(define (negate-poly p)
(make-poly (variable p)
(negate-terms (term-list p))))
;;interface to rest of system
(define (tag x)
(attach-tag 'sparse-polynomial x))
(put 'add '(sparse-polynomial sparse-polynomial)
(lambda (p1 p2) (tag (add-poly p1 p2))))
(put 'sub '(sparse-polynomial sparse-polynomial)
(lambda (p1 p2) (tag (sub-poly p1 p2))))
(put 'mul '(sparse-polynomial sparse-polynomial)
(lambda (p1 p2) (tag (mul-poly p1 p2))))
(put '=zero? '(sparse-polynomial) zero-poly?)
(put 'negate '(sparse-polynomial) (lambda (p) (tag (negate-poly p))))
(put 'make 'sparse-polynomial
(lambda (var terms) (tag (make-poly var terms))))
'done)
(define (make-sparse-polynomial var terms)
((get 'make 'sparse-polynomial) var terms))
;;;Dense polynomial package
(define (install-dense-polynomial-package)
;;internal procedures
(define (make-poly variable term-list)
(cons variable term-list))
(define (variable p) (car p))
(define (term-list p) (cdr p))
(define (same-variable? v1 v2)
(and (symbol? v1) (symbol? v2) (eq? v1 v2)))
;;Term lists for dense polynomials
(define (make-term order coeff) (list order coeff))
(define (order term) (car term))
(define (coeff term) (cadr term))
(define (the-empty-termlist) '())
(define (first-term term-list)
(make-term (- (length term-list) 1)
(car term-list)))
(define (rest-terms term-list) (cdr term-list))
(define (empty-termlist? term-list) (null? term-list))
(define (adjoin-term term term-list)
(let ((term-order (order term))
(list-length (length term-list)))
(cond ((= term-order list-length)
(cons (coeff term) term-list))
((> term-order list-length)
(cons (coeff term)
(adjoin-term (make-term (- term-order 1) 0) term-list)))
(else (error "Term order to small to be adjoined" (list term term-list))))))
(define (zero-terms? L)
(if (empty-termlist? L)
#t
(and (=zero? (coeff (first-term L)))
(zero-terms? (rest-terms L)))))
(define (negate-terms L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t (first-term L)))
(adjoin-term (make-term (order t) (negate (coeff t)))
(negate-terms (rest-terms L))))))
(define (add-terms L1 L2)
(cond ((empty-termlist? L1) L2)
((empty-termlist? L2) L1)
(else
(let ((t1 (first-term L1))
(t2 (first-term L2)))
(cond ((> (order t1) (order t2))
(adjoin-term t1 (add-terms (rest-terms L1) L2)))
((< (order t1) (order t2))
(adjoin-term t2 (add-terms L1 (rest-terms L2))))
(else
(adjoin-term
(make-term (order t1)
(add (coeff t1) (coeff t2)))
(add-terms (rest-terms L1) (rest-terms L2)))))))))
(define (mul-terms L1 L2)
(if (empty-termlist? L1)
(the-empty-termlist)
(add-terms (mul-term-by-all-terms (first-term L1) L2)
(mul-terms (rest-terms L1) L2))))
(define (mul-term-by-all-terms t1 L)
(if (empty-termlist? L)
(the-empty-termlist)
(let ((t2 (first-term L)))
(adjoin-term
(make-term (+ (order t1) (order t2))
(mul (coeff t1) (coeff t2)))
(mul-term-by-all-terms t1 (rest-terms L))))))
(define (add-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(add-terms (term-list p1)
(term-list p2)))
(error "Polys not in same variable -- ADD-POLY" (list p1 p2))))
(define (mul-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(make-poly (variable p1)
(mul-terms (term-list p1)
(term-list p2)))
(error "Polys not in same variable -- MUL-POLY" (list p1 p2))))
(define (sub-poly p1 p2)
(add-poly p1 (negate-poly p2)))
(define (zero-poly? p)
(zero-terms? (term-list p)))
(define (negate-poly p)
(make-poly (variable p)
(negate-terms (term-list p))))
;;interface to rest of system
(define (tag x)
(attach-tag 'dense-polynomial x))
(put 'add '(dense-polynomial dense-polynomial)
(lambda (p1 p2) (tag (add-poly p1 p2))))
(put 'sub '(dense-polynomial dense-polynomial)
(lambda (p1 p2) (tag (sub-poly p1 p2))))
(put 'mul '(dense-polynomial dense-polynomial)
(lambda (p1 p2) (tag (mul-poly p1 p2))))
(put '=zero? '(dense-polynomial) zero-poly?)
(put 'negate '(dense-polynomial) (lambda (p) (tag (negate-poly p))))
(put 'make 'dense-polynomial
(lambda (var terms) (tag (make-poly var terms))))
'done)
(define (make-dense-polynomial var terms)
((get 'make 'dense-polynomial) var terms))

**Exercise 2.91**

The completed implementation of *div-terms* follows:

(define (div-terms L1 L2)
(if (empty-termlist? L1)
(list (the-empty-termlist) (the-empty-termlist))
(let ((t1 (first-term L1))
(t2 (first-term L2)))
(if (> (order t2) (order t1))
(list (the-empty-termlist) L1)
(let ((new-c (div (coeff t1) (coeff t2)))
(new-o (- (order t1) (order t2))))
(let ((rest-of-result
(div-terms
(sub-terms
L1
(mul-term-by-all-terms
(make-term new-o new-c)
L2))
L2)))
(list (adjoin-term
(make-term new-o new-c)
(car rest-of-result))
(cadr rest-of-result))))))))
(define (sub-terms L1 L2)
(add-terms L1 (negate-terms L2)))

Note that we had to define *sub-terms* as a helper procedure so that we could subtract two term lists. To do so we rely on the *negate-terms* procedure that was defined earlier (see *Exercise 2.88*).

Having this dividing two polynomials becomes straight forward:

(define (div-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(let ((result (div-terms (term-list p1) (term-list p2))))
(list (make-poly (variable p1)
(car result))
(make-poly (variable p1)
(cadr result))))
(error "Polys not in same variable -- DIV-POLY" (list p1 p2))))

**Exercise 2.92**

Skipped.

**Exercise 2.93**

After modifying the *rational-number* package to use generic operations and changing the constructor so that no reduction of fractions takes place, we can create rational functions and apply generic arithmetic on them:

(define p1 (make-polynomial 'x '((2 1) (0 1))))
(define p2 (make-polynomial 'x '((3 1) (0 1))))
(define rf (make-rational p2 p1))
(add rf rf)
=> (rational (polynomial x (5 2) (3 2) (2 2) (0 2)) polynomial x (4 1) (2 2) (0 1))

As can be expected the result is not reduced to lowest terms.

** Exercise 2.94**

Definitions of *remainder-terms*, *gcd-terms*, and *gcd-poly* which can be added to the *polynomial* package:

(define (remainder-terms a b)
(cadr (div-terms a b)))
(define (gcd-terms a b)
(if (empty-termlist? b)
a
(gcd-terms b (remainder-terms a b))))
(define (gcd-poly a b)
(if (same-variable? (variable a) (variable b))
(make-poly (variable a)
(gcd-terms (term-list a)
(term-list b)))
(error "Polys not in same variable -- GCD-POLY" (list a b))))

Code for installing in the system *greatest-common-divisor* as a generic operation and registering concrete implementations for ordinary numbers and polynomials:

;;Generic operation - greatest-common-divisor
(define (greatest-common-divisor x y) (apply-generic 'greatest-common-divisor x y))
;;Should be added to the polynomial package
(put 'greatest-common-divisor '(polynomial polynomial)
(lambda (p1 p2) (tag (gcd-poly p1 p2))))
;;Should be added to the scheme-number package
(put 'greatest-common-divisor '(scheme-number scheme-number)
(lambda (x y) (tag (gcd x y))))

When testing *greatest-common-divisor* we get:

(p1 (make-polynomial 'x '((4 1) (3 -1) (2 -2) (1 2))))
(p2 (make-polynomial 'x '((3 1) (1 -1)))))
(greatest-common-divisor p1 p2)
=> (polynomial x (2 -1) (1 1))

**Exercise 2.95**

Let’s try the example:

(define p1 (make-polynomial 'x '((2 1) (1 -2) (0 1))))
(define p2 (make-polynomial 'x '((2 11) (0 7))))
(define p3 (make-polynomial 'x '((1 13) (0 5))))
(define q1 (mul p1 p2))
(define q2 (mul p1 p3))
(greatest-common-divisor q1 q2)
=> (polynomial x (2 1458/169) (1 -2916/169) (0 1458/169))

*Greatest-common-divisor* definitely does not return *p1*, but it does return a polynomial (of the same order as *p1*) that divides both *q1* and *q2*. So why does it not return *p1*? Apparently because it has found some other polynomial that it deems “greater” than *p1*, and that is a divisor of *q1* and *q2*.

**Exercise 2.96**

When defining *pseudoremainder-terms* it is convenient to have a helper procedure that computes the integerizing factor when given two term lists:

(define (integerizing-factor a b)
(if (or (empty-termlist? a) (empty-termlist? b))
0
(let ((t1 (first-term a))
(t2 (first-term b)))
(expt (coeff t2)
(+ (order t1)
(- (order t2))
1)))))
(define (pseudoremainder-terms a b)
(remainder-terms
(mul-term-by-all-terms
(make-term 0 (integerizing-factor a b))
a)
b))

After modifying the *gcd-terms* procedure we can test again the example from the previous exercise and confirm that we do not get fractions:

(define (gcd-terms a b)
(if (empty-termlist? b)
a
(gcd-terms b (pseudoremainder-terms a b))))
(greatest-common-divisor q1 q2)
=> (polynomial x (2 1458) (1 -2916) (0 1458))

The rest of the exercise requires *gcd-terms* to remove the common factors from the coefficients of its result. To simplify the implementation we can define another helper procedure *reduce-termlist*:

(define (reduce-termlist term-list)
(mul-term-by-all-terms (make-term 0
(div 1 (apply gcd (map coeff term-list))))
term-list))
(define (gcd-terms a b)
(if (empty-termlist? b)
(reduce-termlist a)
(gcd-terms b (pseudoremainder-terms a b))))

**Exercise 2.97**

Definitions of *reduce-terms* and *reduce-poly*. To simplify the implementation two helper procedures – *mul-terms-by-scalar* and *quotient-terms* are introduced:

(define (mul-terms-by-scalar s term-list)
(mul-term-by-all-terms (make-term 0 s)
term-list))
(define (quotient-terms a b)
(car (div-terms a b)))
(define (reduce-terms n d)
(let* ((g (gcd-terms n d))
(factor
(integerizing-factor
(if (> (order (first-term n))
(order (first-term d)))
n
d)
g))
(n1 (mul-terms-by-scalar factor n))
(d1 (mul-terms-by-scalar factor d))
(n2 (quotient-terms n1 g))
(d2 (quotient-terms d1 g))
(coeff-gcd (gcd (apply gcd (map coeff n2))
(apply gcd (map coeff d2)))))
(list (mul-terms-by-scalar (div 1 coeff-gcd) n2)
(mul-terms-by-scalar (div 1 coeff-gcd) d2))))
(define (reduce-poly p1 p2)
(if (same-variable? (variable p1) (variable p2))
(let ((result (reduce-terms (term-list p1) (term-list p2))))
(list (make-poly (variable p1) (car result))
(make-poly (variable p2) (cadr result))))
(error "Polys not in same variable -- REDUCE-POLY" (list p1 p2))))

Code that defines the generic *reduce* operation, provides implementations of it for polynomials and scheme-numbers and reimplements the *make-rat* constructor:

;;Generic operation - reduce
(define (reduce x y) (apply-generic 'reduce x y))
;;Should be added to the scheme-number package
(put 'reduce '(scheme-number scheme-number)
(lambda (x y)
(let ((result (reduce-integers x y)))
(list (tag (car result)) (tag (cadr result))))))
;;Should be added to the polynomial package
(put 'reduce '(polynomial polynomial)
(lambda (p1 p2)
(let ((result (reduce-poly p1 p2)))
(list (tag (car result)) (tag (cadr result))))))
;;Should be added to the rational package in place of the old constructor
(define (make-rat n d)
(let ((result (reduce n d)))
(cons (car result) (cadr result))))

Now if we test the code with the rational functions given at the beginning of the extended exercise we get:

(p1 (make-polynomial 'x '((1 1) (0 1))))
(p2 (make-polynomial 'x '((3 1) (0 -1))))
(p3 (make-polynomial 'x '((1 1))))
(p4 (make-polynomial 'x '((2 1) (0 -1))))
(rf1 (make-rational p1 p2))
(rf2 (make-rational p3 p4))
(add rf1 rf2)
=> (rational (polynomial x (3 -1) (2 -2) (1 -3) (0 -1)) polynomial x (4 -1) (3 -1) (1 1) (0 1))

Note that the result is almost correct, the only difference is that the numerator and denominator are both multiplied by *-1*, but otherwise the rational function is simplified as expected.

Read Full Post »