(lambda (a b c x)
(+ (* a x x) (* b x) c)))

;; ax^2 + bx + c = 0
;; (-b +/- sqrt(b^2 - 4ac))/2a
(lambda (a b c)
(list (/ (+ (- b) (sqrt (- (* b b) (* 4 a c))))
(* 2 a))
(/ (- (- b) (sqrt (- (* b b) (* 4 a c))))
(* 2 a)))))

(lambda (a b c)
(let ((discriminant (sqrt (- (* b b) (* 4 a c))))
(denominator (* 2 a))
(-b (- b)))
(list (/ (+ -b discriminant) denominator)
(/ (- -b discriminant) denominator)))))

(lambda (a b c)
((lambda (discriminant denominator -b)
(list (/ (+ -b discriminant) denominator)
(/ (- -b discriminant) denominator)))
(sqrt (- (* b b) (* 4 a c)))
(* 2 a)
(- b))))

(define fact-helper
(lambda (x acc)
(if (= x 0)
acc
(fact-helper (- x 1) (* x acc)))))

(define fact
(lambda (x)
(fact-helper x 1)))

(define fact
(lambda (x)
(letrec
((loop
(lambda (x acc)
(if (= x 0)
acc
(loop (- x 1) (* x acc))))))
(loop x 1))))

(define append
(lambda (ls1 ls2)
(letrec
((loop
(lambda (ls acc)
(if (null? ls)
acc
(loop (cdr ls) (cons (car ls) acc))))))
(loop (loop ls1 ()) ls2))))

;; (let ((x 2) (y 3)) (+ x y)) =>
;; ((lambda (x y) (+ x y)) 2 3)

;; (let ((x 2) (y 3)) (+ x y)) => ((x 2) (y 3))

;; (let ((x 2) (y 3)) (+ x y)) => (+ x y)

;; (let ((x 2) (y 3)) (+ x y)) => (x y)
(define vars
(lambda (expr)

;; ((x 2) (y 3)) => (x y)
(define vars-helper
(lambda (bindings)
(if (null? bindings)
()
(cons (caar bindings)
(vars-helper (cdr bindings))))))

;; (let ((x 2) (y 3)) (+ x y)) => (2 3)
(define vals
(lambda (expr)

;; ((x 2) (y 3)) => (2 3)
(define vals-helper
(lambda (bindings)
(if (null? bindings)
()
(vals-helper (cdr bindings))))))

;; (let ((x 2) (y 3)) (+ x y)) =>
;; ((lambda (x y) (+ x y)) 2 3)
(define let->lambda
(lambda (expr)
(append (list (list 'lambda (vars expr) (body expr)))
(vals expr))))

;; (first-binding '(let* ((x 1) (y x)) (+ x y))) => (x 1)
(define first-binding
(lambda (expr)
(car (bindings expr))))

;; (rest-bindings '(let* ((x 1) (y x)) (+ x y))) => ((y x))
(define rest-bindings
(lambda (expr)
(cdr (bindings expr))))

;; (let*->let '(let* ((x 1) (y x)) (+ x y))) =>
;; (let ((x 1)) (let* ((y x)) (+ x y))) =>
;; (let ((x 1)) (let ((y x)) (let* () (+ x y)))) =>
;; (let ((x 1)) (let ((y x)) (+ x y)))

(define let*->let
(lambda (expr)
(if (null? (bindings expr))
(body expr)
(list 'let
(list (first-binding expr))
(let*->let
(list 'let*
(rest-bindings expr)
(body expr)))))))

;; (let*->lambda '(let* ((x 1) (y x)) (+ x y))) =>
;; ((lambda (x) ((lambda (y) (+ x y)) x)) 1)
(define let*->lambda
(lambda (expr)
(if (null? (bindings expr))
(body expr)
(let->lambda
(list 'let
(list (first-binding expr))
(let*->lambda
(list 'let*
(rest-bindings expr)
(body expr))))))))

;; for experts...
(define let->lambda
(lambda (expr)
`((lambda ,(vars expr) ,(body expr))
,@(vals expr))))

(define let*->let
(lambda (expr)
(if (null? (bindings expr))
(body expr)
`(let (,(first-binding expr))
,(let*->let
`(let* ,(rest-bindings expr)
,(body expr)))))))

;; (tel ((x 2)) (+ x x)) => 4
(define-macro tel
(lambda (bindings body)
`((lambda ,(vars-helper bindings) ,body)
,@(vals-helper bindings))))

;; (tel* ((x 1) (y x)) (+ x x)) => 2
(define-macro tel*
(lambda (bindings body)
(if (null? bindings)
body
`(tel (,(car bindings))
,(tel* ,(cdr bindings)
,body)))))