; Evaluate an infix polynomial in variable x for a given value of x.
; Assume that coefficients a, b, c, etc. have been previously bound.
; Examples:
;   (define a 1)
;   (define b 1)
;   (define f '(a x ^ 5 + b x ^ 4 + 2 x ^ 3 + 6 x ^ 2 + 3 x + 7))
;
;   (evaluate f 0) ==> 7
;   (evaluate f 1) ==> 20
(define evaluate
  (lambda (f x)
    ((make-proc (infix-to-prefix f 'x)) x)
))

; Evaluate the derivative of an infix polynomial with respect variable x
; for a given value of x. Assume that coefficients a, b, c, etc. have been
; previously bound.
; Examples:
;   (define a 1)
;   (define b 1)
;   (define f '(a x ^ 5 + b x ^ 4 + 2 x ^ 3 + 6 x ^ 2 + 3 x + 7))
;
;   (evaluate-deriv f 0) ==> 3
;   (evaluate-deriv f 1) ==> 30
(define evaluate-deriv
  (lambda (f x)
    ((make-proc (infix-to-prefix (deriv f 'x) 'x)) x)
))

; Create a procedure (lambda expression) from a prefix expression.
; Example:
;  (make-proc '(+ (* a b) (* c d e)))
;    ==> (eval '(lambda (x) (+ (* a b) (* c d e))))
;          ==> #<procedure> 
(define make-proc
  (lambda (f)
    (eval (list 'lambda '(x) f))
))

; Convert a polynomial expression in infix notation to prefix notation.
; Example:
;  (infix-to-prefix '(a x + 2 b x + 3 c x) 'x) 
;     ==> (+ (* a x) (* 2 b x) (* 3 c x))
(define infix-to-prefix
  (lambda (infix var)
    (let* ((ti (terminize infix))
           (imti (insert* ti))
           (cimti (convert^ imti var)))
      (insert+ cimti)
)))

; Given a list of terms, where each term is a sublist, 
; insert + at the head of the list. The list of sublists
; can be created by procedure terminize.
; Example:
;  (insert+ '((* a b) (* c d e))) ==> (+ (* a b) (* c d e))
(define insert+
  (lambda (lst)
    (cons '+ lst)
))

; Given a list of terms, where each term is a sublist, 
; insert * at the head of each term. The list of sublists
; can be created by procedure terminize.
; Example:
;  (insert* '((a b) (c d e))) ==> ((* a b) (* c d e))
(define insert*
  (lambda (lst)
    (let ((inserter (lambda (term)
                      (if (null? (cdr term))
                                 (car term)
                                 (cons '* term)))))
      (map inserter lst))  
))

; Given a list of terms, convert each term containing 
; an exponentiation of a given variable from infix to prefix.
; Replace each ^ with a call to the expt procedure.
; Example:
;  (convert^ '((* a x ^ 3) (* b x ^ 2) (* c x)) 'x)
;    ==> ((* a (expt x 3)) (* b (expt x 2)) (* c x))
(define convert^
  (lambda (lst var)
    (let ((converter (lambda (term)
                       (if (and (pair? term) (member? '^ term))
                           (convert^term term var)
                           term))))
      (map converter lst))
))

; Convert a term containing an exponentiation of a given variable
; from infix to prefix. Replace each ^ with a call to the expt procedure.
; Example:
;  (convert^term '(* a x ^ 3) 'x) ==> (* a (expt x 3))
(define convert^term
  (lambda (term var)
    (let* ((leading (upto var term))
           (expo (car (reverse term))))
      (append leading (list (list 'expt var expo))))
))