; takes an expression and a variable and
; returns the derivitive of expr wrt var
(define (deriv expr var)
  (cond ((number? expr) 0)
        ((variable? expr)
         (if (same-variable? expr var) 
              1 0))
        ((sum? expr)
         (make-sum
           (deriv (sum-arg1 expr) var)
           (deriv (sum-arg2 expr) var)))
        ((product? expr)
         (make-sum
          (make-product
           (product-arg1 expr)
           (deriv (product-arg2 expr)
                  var))
          (make-product
           (product-arg2 expr)
           (deriv (product-arg1 expr)
                  var))))
        (else (error "Unknown type" expr))))

;; Implementation of lower level

; takes an expression and returns #t 
; if it is a variable
(define (variable? x) (symbol? x))

; takes two expressions and is #t if
; they are both the same variable
(define (same-variable? x y)
  (and (variable? x)
       (variable? y)
       (eq? x y)))

; takes two expressions and creates a sum
; with them as the arguments -- sums are
; simply represented as lists
(define (make-sum x y)
  (list '+ x y))

; returns #t if the argument is a sum
; a sum is a list whose first element is 
; the symbol +
(define (sum? x)
  (and (pair? x)
       (eq? (car x) '+)))

; selectors for a sum retrieve
; the first and second arguments
; to be added
(define (sum-arg1 x) (cadr x))
(define (sum-arg2 x) (caddr x))

; takes two expressions and creates a product
; with them as the arguments -- products are
; simply represented as lists
(define (make-product x y)
  (list '* x y))

; returns #t if the argument is a product
; a product is a list whose first element is 
; the symbol *
(define (product? x)
  (and (pair? x)
       (eq? (car x) '*)))

; selectors for a product retrieve
; the first and second arguments
; to be multiplied
(define (product-arg1 x) (cadr x))
(define (product-arg2 x) (caddr x))

;; some definitions
(define expr+ (make-sum 'x 3))
(define expr* (make-product 'x 'y))
(define expr-complicated (make-product expr* expr+))

; a new constructor that simplifies the sum a bit
(define (simp-make-sum x y)
  (cond ((and (number? x) (= x 0)) y)
        ((and (number? y) (= y 0)) x)
        ((and (number? x) (number? y))
         (+ x y))
        (else (list '+ x y))))

; a new constructor that simplifies the product a bit
(define (simp-make-product x y)
  (cond ((or (and (number? x) (= x 0))
             (and (number? y) (= y 0)))
         0)
        ((and (number? x) (= x 1)) y)
        ((and (number? y) (= y 1)) x)
        ((and (number? x) (number? y))
         (* x y))
        (else (list '* x y))))











