(define zero-poly?
  (lambda (poly)
    (and (zero? (degree poly))
	 (zero? (leading-coef poly)))))

(define make-term
  (lambda (deg coef)
    (poly-cons deg coef the-zero-poly)))

(define leading-term
  (lambda (poly)
    (make-term (degree poly) (leading-coef poly))))

(define p+
  (lambda (poly1 poly2)
    (cond ((zero-poly? poly1) poly2)
	  ((zero-poly? poly2) poly1)
	  (else
	   (let ((n1 (degree poly1))
		 (n2 (degree poly2))
		 (a1 (leading-coef poly1))
		 (a2 (leading-coef poly2))
		 (rest1 (rest-of-poly poly1))
		 (rest2 (rest-of-poly poly2)))
	     (cond 
	      ((> n1 n2) (poly-cons n1 a1 (p+ rest1 poly2)))
	      ((< n1 n2) (poly-cons n2 a2 (p+ poly1 rest2)))
	      (else
	       (poly-cons n1 (+ a1 a2) (p+ rest1 rest2)))))))))

(define p*
  (lambda (poly1 poly2)
    (letrec 
      ((t* (lambda (trm poly)
	     (if (zero-poly? poly)
		 the-zero-poly
		 (poly-cons
		  (+ (degree trm) (degree poly))
		  (* (leading-coef trm) (leading-coef poly))
		  (t* trm (rest-of-poly poly)))))))
      (if (zero-poly? poly1)
	  the-zero-poly
	  (p+ (t* (leading-term poly1) poly2)
	      (p* (rest-of-poly poly1) poly2))))))

(define negative-poly
  (lambda (poly)
    (let ((poly-negative-one (make-term 0 -1)))
      (p* poly-negative-one poly))))

(define p-
  (lambda (poly1 poly2)
    (p+ poly1 (negative-poly poly2))))

;; a_n x^n + a_n-1 x^n-1 + ... = (a_n x + a_n-1) x^n-1 + ...
(define poly-value
  (lambda (poly x)
    (let ((n (degree poly)))
      (if (zero? n)
	  (leading-coef poly)
	  (let ((rest (rest-of-poly poly))
		(a_n (leading-coef poly)))
	    (if (= (degree rest) (sub1 n))
		(poly-value (poly-cons (sub1 n)
				       (+ (* a_n x) (leading-coef rest))
				       (rest-of-poly rest))
			    x)
		(poly-value (poly-cons (sub1 n)
				       (* a_n x)
				       rest)
			    x)))))))

(define the-zero-poly '(0))

(define degree
  (lambda (poly)
    (sub1 (length poly))))

(define leading-coef
  (lambda (poly)
    (car poly)))

(define rest-of-poly
  (lambda (poly)
    (cond ((zero? (degree poly)) the-zero-poly)
	  ((zero? (leading-coef (cdr poly)))
	   (rest-of-poly (cdr poly)))
	  (else
	   (cdr poly)))))

(define poly-cons
  (lambda (deg coef poly)
    (let ((deg-p (degree poly)))
      (cond ((and (zero? deg) (equal? poly the-zero-poly))
	     (list coef))
	    ((>= deg-p deg)
	     (display "poly-cons: Degree too high in ")
	     (display-poly poly))
	    ((zero? coef) poly)
	    (else
	     (cons coef
		   (append (list-of-zeros (sub1 (- deg deg-p)))
			   poly)))))))

(define list-of-zeros
  (lambda (n)
    (if (= n 0)
	'()
	(cons 0 (list-of-zeros (sub1 n))))))

(define display-poly
  (lambda (poly)
    (letrec 
      ((loop 
	(lambda (poly)
	  (string-append 
	   (number->string (abs (leading-coef poly)))
	   (let ((rest (rest-of-poly poly))
		 (deg (degree poly)))
	     (string-append
	      (cond ((= deg 0) "")
		    ((= deg 1) "x")
		    (else
		     (string-append "x^" (number->string deg))))
	      (if (zero-poly? rest)
		  ""
		  (string-append
		   (if (> (leading-coef rest) 0) 
		       " + "
		       " - ")
		   (loop (rest-of-poly poly))))))))))
      (if (< (leading-coef poly) 0)
	  (string-append "-" (loop poly))
	  (loop poly)))))