#lang racket ;; This is the complete symbolic algebra program from section 2.5.3, including ;; polynomials in more than one variable, a hierarchy of variables and coercion, ;; and rational functions, as outlined in the exercises. ;;;;;;;;;;;;;;;;;;;;;; ;; GLOBAL VARIABLES ;; ;;;;;;;;;;;;;;;;;;;;;; ;; Specify the output representation for polynomial term lists here. (define TERMLIST-OUTPUT-TYPE 'dense) ; 'dense or 'sparse ;; Only allow these variables in polynomials. (define VARIABLE-LIST '(x y z)) (define HIGHEST-VARIABLE (first VARIABLE-LIST)) (define LOWEST-VARIABLE (last VARIABLE-LIST)) ;; Only simplify the results of these operations. (define SIMPLIFY-LIST '(add sub mul div expo sqroot square arctan cosine sine re-part im-part mag-part ang-part first-term constant-term )) (define (can-simplify? op) (member op SIMPLIFY-LIST)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROCEDURE TABLES AND VARIABLE DICTIONARIES ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; procedure table for generic operations (define op-table (make-hash)) (define (put op tag-list proc) (hash-set! op-table (cons op tag-list) proc)) (define (can-apply? op tag-list) (member (cons op tag-list) (hash-keys op-table))) (define (get op tag-list) (hash-ref op-table (cons op tag-list))) ;; procedure table for type coercion operations (define coercion-table (make-hash)) (define (put-project type proc) (hash-set! coercion-table (cons 'project type) proc)) (define (can-project? arg) (member (cons 'project (type-tag arg)) (hash-keys coercion-table))) (define (project arg) ((hash-ref coercion-table (cons 'project (type-tag arg))) arg)) ;; raising the type (define (put-raise type proc) (hash-set! coercion-table (cons 'raise type) proc)) (define (can-raise? arg) (member (cons 'raise (type-tag arg)) (hash-keys coercion-table))) (define (raise arg) ((hash-ref coercion-table (cons 'raise (type-tag arg))) arg)) ;; variable dictionaries (define next-higher-variable (make-hash)) (define next-lower-variable (make-hash)) (define (install-variable-dictionaries) (define (loop var) (cond [(eq? var LOWEST-VARIABLE) 'done] [else (define next-lower (second (member var VARIABLE-LIST))) (hash-set! next-lower-variable var next-lower) (hash-set! next-higher-variable next-lower var) (loop next-lower)])) (loop HIGHEST-VARIABLE)) (install-variable-dictionaries) (define (next-higher var) (hash-ref next-higher-variable var)) (define (next-lower var) (hash-ref next-lower-variable var)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; APPLY-GENERIC AND HELPER FUNCTIONS ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; procedures for handling type tags (define (attach-tag type-tag datum) (cond [(exact-integer? datum) datum] [(inexact-real? datum) datum] [else (cons type-tag datum)])) (define (type-tag datum) (cond [(exact-integer? datum) 'integer] [(inexact-real? datum) 'real] [(pair? datum) (first datum)] [else (error "Bad tagged datum -- TYPE-TAG:" datum)])) (define (contents datum) (cond [(exact-integer? datum) datum] [(inexact-real? datum) datum] [(pair? datum) (cdr datum)] [else (error "Bad tagged datum -- CONTENTS:" datum)])) ;; type coercion and simplification (define (simplify arg) ; Simplify arg to the lowest possible correct type. (cond [(can-project? arg) (define projected-arg (project arg)) (if (equ? arg (raise projected-arg)) (simplify projected-arg) arg)] [else arg])) (define (highest-type tag-list) ; Return the highest type in tag-list. (define (loop type arg) (cond [(can-raise? arg) (define raised-arg (raise arg)) (define raised-type (type-tag raised-arg)) (if (member raised-type tag-list) (loop raised-type raised-arg) (loop type raised-arg))] [else type])) (loop 'integer 1)) (define (coerce arg target-type) ; Raise arg until it has type target-type. Assume type of arg <= target-type. (if (equal? (type-tag arg) target-type) arg (coerce (raise arg) target-type))) (define (coerce-all args target-type) ; Coerce all arguments to the target type. (map (lambda (arg) (coerce arg target-type)) args)) ;; the generic application procedure (define (apply-generic op . args) ; Dispatch by type. (define (simplify-result datum) (if (can-simplify? op) (simplify datum) datum)) (define type-tags (map type-tag args)) (cond [(can-apply? op type-tags) (simplify-result (apply (get op type-tags) (map contents args)))] [(andmap (lambda (type) (equal? type (first type-tags))) type-tags) (error "No method available -- APPLY-GENERIC:" op type-tags)] [else (define coerced-args (coerce-all args (highest-type type-tags))) (simplify-result (apply (get op (map type-tag coerced-args)) (map contents coerced-args)))])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; THE GENERIC OPERATIONS ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; constructors and selectors (define (make-integer a) ((get 'make 'integer) a)) (define (make-rational-number n d) ((get 'make 'rational-number) n d)) (define (make-rational-function n d) ((get 'make 'rational-function) n d)) (define (make-real x) ((get 'make 'real) x)) ;; complex numbers (define (make-complex-from-real-imag x y) ((get 'make-from-real-imag 'complex) x y)) (define (make-complex-from-mag-ang r a) ((get 'make-from-mag-ang 'complex) r a)) (define (re-part z) (apply-generic 're-part z)) (define (im-part z) (apply-generic 'im-part z)) (define (mag-part z) (apply-generic 'mag-part z)) (define (ang-part z) (apply-generic 'ang-part z)) ;; polynomials (define (make-poly-from-dense-termlist var term-list) ((get 'make-from-dense-termlist `(polynomial ,var)) term-list)) (define (make-poly-from-sparse-termlist var term-list) ((get 'make-from-sparse-termlist `(polynomial ,var)) term-list)) ;; generic operations (define (=zero? x) (apply-generic '=zero? x)) (define (absolute x) (apply-generic 'absolute x)) (define (add x y) (apply-generic 'add x y)) (define (arctan y x) (apply-generic 'arctan y x)) (define (constant-term p) (apply-generic 'constant-term p)) (define (cosine x) (apply-generic 'cosine x)) (define (div x y) (apply-generic 'div x y)) (define (equ? x y) (apply-generic 'equ? x y)) (define (expo x y) (apply-generic 'expo x y)) (define (first-term p) (apply-generic 'first-term p)) (define (greatest-common-divisor x y) (apply-generic 'greatest-common-divisor x y)) (define (mul x y) (apply-generic 'mul x y)) (define (polynomial-division p1 p2) (apply-generic 'polynomial-division p1 p2)) (define (reduce x y) (apply-generic 'reduce x y)) (define (sine x) (apply-generic 'sine x)) (define (sqroot x) (apply-generic 'sqroot x)) (define (square x) (apply-generic 'square x)) (define (sub x y) (apply-generic 'sub x y)) ;;;;;;;;;;;;;;; ;; INTEGERS ;; ;;;;;;;;;;;;;;; (define (install-integer-package) ;; internal procedures (define (make-integer a) (define int (floor a)) (if (exact? int) int (inexact->exact int))) (define (=zero-integer? a) (= a 0)) (define (gcd-integer a b) (if (= b 0) a (gcd-integer b (remainder a b)))) (define (reduce-integer n d) (define g (gcd-integer n d)) (list (/ n g) (/ d g))) (define arctan (compose make-real atan)) (define cosine (compose make-real cos)) (define sine (compose make-real sin)) (define sqroot (compose make-real sqrt)) ;; coercion procedures (define (raise a) (make-rational-number a 1)) (put-raise 'integer raise) ;; interface to the rest of the system (put 'make 'integer make-integer) (put '=zero? '(integer) =zero-integer?) (put 'absolute '(integer) abs) (put 'add '(integer integer) +) (put 'arctan '(integer integer) arctan) (put 'cosine '(integer) cosine) (put 'div '(integer integer) make-rational-number) (put 'equ? '(integer integer) =) (put 'expo '(integer integer) expt) (put 'greatest-common-divisor '(integer integer) gcd-integer) (put 'mul '(integer integer) *) (put 'reduce '(integer integer) reduce-integer) (put 'sine '(integer) sine) (put 'sqroot '(integer) sqroot) (put 'square '(integer) sqr) (put 'sub '(integer integer) -) ) (install-integer-package) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; RATIONAL NUMBERS AND RATIONAL FUNCTIONS ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (install-rational-package) ;; internal procedures (define (make-rat n d) (reduce n d)) (define (numer x) (first x)) (define (denom x) (second x)) (define (=zero-rat? x) (=zero? (numer x))) (define (equ-rat? x y) (equ? (mul (numer x) (denom y)) (mul (denom x) (numer y)))) (define (add-rat x y) (make-rat (add (mul (numer x) (denom y)) (mul (numer y) (denom x))) (mul (denom x) (denom y)))) (define (sub-rat x y) (make-rat (sub (mul (numer x) (denom y)) (mul (numer y) (denom x))) (mul (denom x) (denom y)))) (define (mul-rat x y) (make-rat (mul (numer x) (numer y)) (mul (denom x) (denom y)))) (define (div-rat x y) (make-rat (mul (numer x) (denom y)) (mul (denom x) (numer y)))) ;; procedures for rational numbers only (define (absolute x) (make-real (abs (/ (numer x) (denom x))))) (define (arctan y x) (make-real (atan (/ (numer y) (denom y))))) (define (cosine x) (make-real (cos (/ (numer x) (denom x))))) (define (expo-ratnum x y) (make-real (expt (/ (numer x) (denom x)) (/ (numer y) (denom y))))) (define (sine x) (make-real (sin (/ (numer x) (denom x))))) (define (square x) (make-rat (sqr (numer x)) (sqr (denom x)))) (define (sqroot x) (make-real (sqrt (/ (numer x) (denom x))))) ;; procedures for rational functions only (define (expo-ratfunc x n) (cond [(and (integer? n) (positive? n)) (if (= n 1) x (mul-rat x (expo-ratfunc x (sub1 n))))] [else (error "Exponent must be a positive integer -- EXPO-RATFUNC:" x n)])) ;; coercion procedures for rational numbers (define (project-rat x) (make-integer (floor (/ (numer x) (denom x))))) (define (raise-rat x) (make-real (/ (numer x) (denom x)))) (put-project 'rational-number (compose project-rat contents)) (put-raise 'rational-number (compose raise-rat contents)) ;; interface to the rest of the system for rational numbers (define (tag-as-number x) (attach-tag 'rational-number x)) (put 'make 'rational-number (compose tag-as-number make-rat)) (put '=zero? '(rational-number) =zero-rat?) (put 'absolute '(rational-number) absolute) (put 'add '(rational-number rational-number) (compose tag-as-number add-rat)) (put 'arctan '(rational-number rational-number) arctan) (put 'cosine '(rational-number) cosine) (put 'div '(rational-number rational-number) (compose tag-as-number div-rat)) (put 'equ? '(rational-number rational-number) equ-rat?) (put 'expo '(rational-number rational-number) expo-ratnum) (put 'mul '(rational-number rational-number) (compose tag-as-number mul-rat)) (put 'sine '(rational-number) sine) (put 'sqroot '(rational-number) sqroot) (put 'square '(rational-number) (compose tag-as-number square)) (put 'sub '(rational-number rational-number) (compose tag-as-number sub-rat)) ;; interface to the rest of the system for rational functions (define (tag-as-function x) (attach-tag 'rational-function x)) (put 'make 'rational-function (compose tag-as-function make-rat)) (put '=zero? '(rational-function) =zero-rat?) (put 'add '(rational-function rational-function) (compose tag-as-function add-rat)) (put 'div '(rational-function rational-function) (compose tag-as-function div-rat)) (put 'equ? '(rational-function rational-function) equ-rat?) (put 'expo '(rational-function integer) (compose tag-as-function expo-ratfunc)) (put 'mul '(rational-function rational-function) (compose tag-as-function mul-rat)) (put 'sub '(rational-function rational-function) (compose tag-as-function sub-rat)) ) (install-rational-package) ;;;;;;;;;;;;;;;;;; ;; REAL NUMBERS ;; ;;;;;;;;;;;;;;;;;; (define (install-real-package) ;; internal procedures (define (make-real x) ; x is a racket number or has lower type (cond [(pair? x) (raise x)] [(exact? x) (exact->inexact x)] [else x])) ; Account for floating-point error. (define (=zero-real? x) (< (abs x) 0.000000001)) (define (equ-real? x y) (< (abs (- x y)) 0.000000001)) ;; coercion procedures (define (project-real x) (make-rational-number (make-integer (floor (* x 1000000000))) 1000000000)) (define (raise-real x) (make-complex-from-real-imag x 0)) (put-project 'real project-real) (put-raise 'real raise-real) ;; interface to the rest of the system (put 'make 'real make-real) (put '=zero? '(real) =zero-real?) (put 'absolute '(real) abs) (put 'add '(real real) +) (put 'arctan '(real real) atan) (put 'cosine '(real) cos) (put 'div '(real real) /) (put 'equ? '(real real) =) (put 'expo '(real real) expt) (put 'mul '(real real) *) (put 'sine '(real) sin) (put 'sqroot '(real) sqrt) (put 'square '(real) sqr) (put 'sub '(real real) -) ) (install-real-package) ;;;;;;;;;;;;;;;;;;;;; ;; COMPLEX NUMBERS ;; ;;;;;;;;;;;;;;;;;;;;; (define (install-complex-package) ;; rectangular coordinates (define (install-rectangular-package) ;; internal procedures (define (make-from-real-imag x y) (list x y)) (define (re-part z) (first z)) (define (im-part z) (second z)) (define (make-from-mag-ang r a) (list (mul r (cosine a)) (mul r (sine a)))) (define (mag-part z) (sqroot (add (square (re-part z)) (square (im-part z))))) (define (ang-part z) (arctan (im-part z) (re-part z))) ;; interface to the rest of the system (define (tag z) (attach-tag 'rectangular z)) (put 'make-from-mag-ang 'rectangular (compose tag make-from-mag-ang)) (put 'make-from-real-imag 'rectangular (compose tag make-from-real-imag)) (put 're-part '(rectangular) re-part) (put 'im-part '(rectangular) im-part) (put 'mag-part '(rectangular) mag-part) (put 'ang-part '(rectangular) ang-part) ) (install-rectangular-package) ;; polar coordinates (define (install-polar-package) ;; internal procedures (define (make-from-mag-ang r a) (list r a)) (define (mag-part z) (first z)) (define (ang-part z) (second z)) (define (make-from-real-imag x y) (list (sqroot (add (square x) (square y))) (arctan y x))) (define (re-part z) (mul (mag-part z) (cosine (ang-part z)))) (define (im-part z) (mul (mag-part z) (sine (ang-part z)))) ;; interface to the rest of the system (define (tag x) (attach-tag 'polar x)) (put 'make-from-mag-ang 'polar (compose tag make-from-mag-ang)) (put 'make-from-real-imag 'polar (compose tag make-from-real-imag)) (put 're-part '(polar) re-part) (put 'im-part '(polar) im-part) (put 'mag-part '(polar) mag-part) (put 'ang-part '(polar) ang-part) ) (install-polar-package) ;; imported procedures from rectangular and polar subpackages (define make-from-real-imag (get 'make-from-real-imag 'rectangular)) (define make-from-mag-ang (get 'make-from-mag-ang 'polar)) ;; internal complex number procedures (define (=zero-complex? z) ; account for floating-point error (and (< (absolute (re-part z)) 0.000000001) (< (absolute (im-part z)) 0.000000001))) (define (equ? z1 z2) ; account for floating-point error (and (< (absolute (sub (re-part z1) (re-part z2))) 0.000000001) (< (absolute (sub (im-part z1) (im-part z2))) 0.000000001))) (define (add-complex z1 z2) (make-from-real-imag (add (re-part z1) (re-part z2)) (add (im-part z1) (im-part z2)))) (define (sub-complex z1 z2) (make-from-real-imag (sub (re-part z1) (re-part z2)) (sub (im-part z1) (im-part z2)))) (define (mul-complex z1 z2) (make-from-mag-ang (mul (mag-part z1) (mag-part z2)) (add (ang-part z1) (ang-part z2)))) (define (div-complex z1 z2) (make-from-mag-ang (div (mag-part z1) (mag-part z2)) (sub (ang-part z1) (ang-part z2)))) (define (expo-complex z1 z2) (define w (expt (+ (make-real (re-part z1)) (* (make-real (im-part z1)) 0+i)) (+ (make-real (re-part z2)) (* (make-real (im-part z2)) 0+i)))) (make-from-real-imag (real-part w) (imag-part w))) ;; coercion procedures (define (project-complex z) (make-real (re-part z))) (define (raise-complex z) (make-poly-from-dense-termlist LOWEST-VARIABLE (list (simplify z)))) (put-project 'complex project-complex) (put-raise 'complex raise-complex) ;; interface to the rest of the system (define (tag z) (attach-tag 'complex z)) (put 'make-from-mag-ang 'complex (compose tag make-from-mag-ang)) (put 'make-from-real-imag 'complex (compose tag make-from-real-imag)) (put '=zero? '(complex) =zero-complex?) (put 'add '(complex complex) (compose tag add-complex)) (put 'ang-part '(complex) ang-part) (put 'div '(complex complex) (compose tag div-complex)) (put 'equ? '(complex complex) equ?) (put 'expo '(complex complex) (compose tag expo-complex)) (put 'im-part '(complex) im-part) (put 'mag-part '(complex) mag-part) (put 'mul '(complex complex) (compose tag mul-complex)) (put 're-part '(complex) re-part) (put 'sub '(complex complex) (compose tag sub-complex)) ) (install-complex-package) ;;;;;;;;;;;;;;;;; ;; POLYNOMIALS ;; ;;;;;;;;;;;;;;;;; (define (install-polynomial-package) ;; term list representations ;; sparse term lists (define (install-sparse-termlist-package) ;; constructors and selectors (define (make-term order coeff) (list order coeff)) (define (order term) (first term)) (define (coeff term) (second term)) (define (the-empty-termlist) empty) (define empty-termlist? null?) (define (first-term L) (if (empty-termlist? L) (make-term 0 0) (first L))) (define (rest-terms L) (cdr L)) (define (dense->sparse DL) (cond [(null? DL) (the-empty-termlist)] [(=zero? (first DL)) (dense->sparse (cdr DL))] [else (cons (make-term (- (length DL) 1) (first DL)) (dense->sparse (cdr DL)))])) ;; procedures for generic operations (define (adjoin-term term L) (if (=zero? (coeff term)) L (cons term L))) (define (constant-term L) (cond [(empty-termlist? L) (make-term 0 0)] [else (define last-term (last L)) (if (zero? (order last-term)) last-term (make-term 0 0))])) (define (negate-terms L) (map (lambda (term) (make-term (order term) (mul -1 (coeff term)))) L)) (define (reduce-coefficients L) ; divide all coeffs by their gcd (define (list-of-coefficients term-list) ; Return a list of coefficients in no particular order. (foldr (lambda (term a-list) (cons (second term) a-list)) empty term-list)) (define gcf (foldr greatest-common-divisor 0 (list-of-coefficients L))) (map (lambda (term) (make-term (order term) (div (coeff term) gcf))) L)) ;; interface to the rest of the system (define (tag-as-sparse x) (attach-tag 'sparse x)) (define (tag-as-term x) (attach-tag 'term x)) (put 'make-from-dense-termlist 'sparse (compose tag-as-sparse dense->sparse)) (put 'make-from-sparse-termlist 'sparse tag-as-sparse) (put 'adjoin-term '(term sparse) (compose tag-as-sparse adjoin-term)) (put 'constant-term '(sparse) (compose tag-as-term constant-term)) (put 'first-term '(sparse) (compose tag-as-term first-term)) (put 'negate-terms '(sparse) (compose tag-as-sparse negate-terms)) (put 'reduce-coefficients '(sparse) (compose tag-as-sparse reduce-coefficients)) (put 'rest-terms '(sparse) (compose tag-as-sparse rest-terms)) ) (install-sparse-termlist-package) ;; dense term lists (define (install-dense-termlist-package) ;; internal procedures (define (make-term order coeff) (list order coeff)) (define (order term) (first term)) (define (coeff term) (second term)) (define (the-empty-termlist) empty) (define empty-termlist? null?) (define (first-term L) (if (empty-termlist? L) (make-term 0 0) (make-term (- (length L) 1) (first L)))) (define (rest-terms L) (cdr L)) (define (sparse->dense SL) (define (list-of-zeroes n) (for/list ([i (in-range n)]) 0)) (cond [(empty? SL) (the-empty-termlist)] [else (define first-term (first SL)) (cond [(=zero? (coeff first-term)) (sparse->dense (rest SL))] [(empty? (rest SL)) ; only one term in SL (cons (coeff first-term) (list-of-zeroes (order first-term)))] [else (define second-term (second SL)) (append (cons (coeff first-term) (list-of-zeroes (- (order first-term) (order second-term) 1))) (sparse->dense (rest SL)))])])) (define (adjoin-term term L) ; order >= length L (define (loop term-list) (if (> (order term) (length term-list)) (loop (cons 0 term-list)) (cons (coeff term) term-list))) (if (=zero? (coeff term)) L (loop L))) (define (constant-term L) (if (empty-termlist? L) (make-term 0 0) (make-term 0 (last L)))) (define (negate-terms L) (map (lambda (coefficient) (mul -1 coefficient)) L)) (define (reduce-coefficients L) ; divide all coeffs by their gcd (define gcf (foldr greatest-common-divisor 0 L)) (map (lambda (coeff) (div coeff gcf)) L)) ;; interface to the rest of the system (define (tag-as-dense x) (attach-tag 'dense x)) (define (tag-as-term x) (attach-tag 'term x)) (put 'make-from-dense-termlist 'dense tag-as-dense) (put 'make-from-sparse-termlist 'dense (compose tag-as-dense sparse->dense)) (put 'adjoin-term '(term dense) (compose tag-as-dense adjoin-term)) (put 'constant-term '(dense) (compose tag-as-term constant-term)) (put 'first-term '(dense) (compose tag-as-term first-term)) (put 'negate-terms '(dense) (compose tag-as-dense negate-terms)) (put 'reduce-coefficients '(dense) (compose tag-as-dense reduce-coefficients)) (put 'rest-terms '(dense) (compose tag-as-dense rest-terms)) ) (install-dense-termlist-package) ;; imported procedures from the term list subpackages (define make-from-dense-termlist (get 'make-from-dense-termlist TERMLIST-OUTPUT-TYPE)) (define make-from-sparse-termlist (get 'make-from-sparse-termlist TERMLIST-OUTPUT-TYPE)) ;; term and term list operations (define (make-term order coeff) (list 'term order coeff)) (define (order term) (second term)) (define (coeff term) (third term)) (define (rest-terms L) (apply-generic 'rest-terms L)) (define (the-empty-termlist) (list TERMLIST-OUTPUT-TYPE)) (define (empty-termlist? L) (null? (contents L))) (define (=zero-termlist? L) (or (empty-termlist? L) (and (=zero? (coeff (first-term L))) (=zero-termlist? (rest-terms L))))) (define (reduce-coefficients L) (apply-generic 'reduce-coefficients L)) (define (adjoin-term term L) (apply-generic 'adjoin-term term L)) (define (negate-terms L) (apply-generic 'negate-terms L)) (define (add-terms L1 L2) (cond [(empty-termlist? L1) L2] [(empty-termlist? L2) L1] [else (define t1 (first-term L1)) (define 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 (sub-terms L1 L2) (add-terms L1 (negate-terms L2))) (define (equ-termlist? L1 L2) (=zero-termlist? (sub-terms L1 L2))) (define (mul-term-by-all-terms t1 L) (cond [(empty-termlist? L) (the-empty-termlist)] [else (define 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 (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 (div-terms L1 L2) (cond [(empty-termlist? L1) (list (the-empty-termlist) (the-empty-termlist))] [else (define t1 (first-term L1)) (define t2 (first-term L2)) (cond [(> (order t2) (order t1)) (list (the-empty-termlist) L1)] [else (define divisor-term (make-term (- (order t1) (order t2)) (div (coeff t1) (coeff t2)))) (define rest-of-result (div-terms (sub-terms L1 (mul-term-by-all-terms divisor-term L2)) L2)) (list (adjoin-term divisor-term (first rest-of-result)) (second rest-of-result))])])) (define (divisor-termlist L1 L2) (first (div-terms L1 L2))) (define (remainder-termlist L1 L2) (second (div-terms L1 L2))) (define (expo-terms L n) (cond [(positive? n) (if (= n 1) L (mul-terms L (expo-terms L (sub1 n))))] [else (error "Exponent must be a positive integer -- EXPO-TERMS:" L n)])) (define (gcd-terms a b) (if (empty-termlist? b) a (gcd-terms b (remainder-termlist a b)))) (define (pseudo-remainder-termlist L1 L2) ; Clear denominators from remainder-termlist. (remainder-termlist (mul-term-by-all-terms (make-term 0 (expt (coeff (first-term L2)) (+ 1 (order (first-term L1)) (- (order (first-term L2)))))) L1) L2)) (define (pseudo-gcd-terms a b) ; Return a gcd term list with integer coefficients. (if (empty-termlist? b) a (pseudo-gcd-terms b (pseudo-remainder-termlist a b)))) (define (reduce-terms a b) (define (same-sign? L1 L2) (positive? (make-real (re-part (coerce (mul (coeff (first-term L1)) (coeff (first-term L2))) 'complex))))) (define (fix-signs L1 L2) ; Ensure the leading coefficient of L1/L2 is the same as that of a/b. (cond [(and (same-sign? a L1) (not (same-sign? b L2))) (list L1 (negate-terms L2))] [(and (not (same-sign? a L1)) (same-sign? b L2)) (list (negate-terms L1) L2)] [(and (not (same-sign? a L1)) (not (same-sign? b L2))) (list (negate-terms L1) (negate-terms L2))] [else (list L1 L2)])) (define pseudo-gcf (pseudo-gcd-terms a b)) (define (integerizing-factor L1 L2) (expt (coeff (first-term pseudo-gcf)) (+ 1 (max (order (first-term L1)) (order (first-term L2))) (- (order (first-term pseudo-gcf)))))) (define (clear-denominators L) (mul-term-by-all-terms (make-term 0 (integerizing-factor a b)) L)) (apply fix-signs (map (lambda (term-list) (reduce-coefficients (divisor-termlist (clear-denominators term-list) pseudo-gcf))) (list a b)))) ;; polynomial operations (define (make-poly var term-list) (attach-tag `(polynomial ,var) term-list)) (define (variable poly) (second (first poly))) (define (term-list poly) (rest poly)) ;; coercion operations ; Coercion procedures act on tagged polynomials. (define (project-poly-to-complex p) (coerce (coeff (constant-term (term-list p))) 'complex)) (define (project-poly-to-poly p) (coerce (coeff (constant-term (term-list p))) `(polynomial ,(next-lower (variable p))))) (define (raise-poly p) (make-poly-from-dense-termlist (next-higher (variable p)) (list p))) (define (install-coercion-procedures) (define (loop var) ; assume there is more than one variable (cond [(eq? var HIGHEST-VARIABLE) ; start of the loop (put-project `(polynomial ,var) project-poly-to-poly) (loop (next-lower var))] [(eq? var LOWEST-VARIABLE) ; end of the loop (put-project `(polynomial ,var) project-poly-to-complex) (put-raise `(polynomial ,var) raise-poly) 'done] [else ; middle of the loop (put-project `(polynomial ,var) project-poly-to-poly) (put-raise `(polynomial ,var) raise-poly) (loop (next-lower var))])) (if (eq? HIGHEST-VARIABLE LOWEST-VARIABLE) ; only one variable (put-project `(polynomial ,LOWEST-VARIABLE) project-poly-to-complex) (loop HIGHEST-VARIABLE))) (install-coercion-procedures) ;; interface to the rest of the system ; Generic procedures act on the contents of polynomials, ie term lists. (define (install-generic-procedures) (define (loop var) (put 'make-from-dense-termlist `(polynomial ,var) (compose (curry make-poly var) make-from-dense-termlist)) (put 'make-from-sparse-termlist `(polynomial ,var) (compose (curry make-poly var) make-from-sparse-termlist)) (put '=zero? `((polynomial ,var)) =zero-termlist?) (put 'equ? `((polynomial ,var) (polynomial ,var)) equ-termlist?) (put 'add `((polynomial ,var) (polynomial ,var)) (compose (curry make-poly var) add-terms)) (put 'sub `((polynomial ,var) (polynomial ,var)) (compose (curry make-poly var) sub-terms)) (put 'mul `((polynomial ,var) (polynomial ,var)) (compose (curry make-poly var) mul-terms)) (put 'expo `((polynomial ,var) integer) (compose (curry make-poly var) expo-terms)) (put 'first-term `((polynomial ,var)) first-term) (put 'constant-term `((polynomial ,var)) constant-term) (if (eq? var LOWEST-VARIABLE) (begin (put 'polynomial-division `((polynomial ,LOWEST-VARIABLE) (polynomial ,LOWEST-VARIABLE)) (lambda (L1 L2) (map (curry make-poly LOWEST-VARIABLE) (div-terms L1 L2)))) (put 'greatest-common-divisor `((polynomial ,LOWEST-VARIABLE) (polynomial ,LOWEST-VARIABLE)) ; return a gcd with reduced integer coefficients (compose (curry make-poly LOWEST-VARIABLE) reduce-coefficients pseudo-gcd-terms)) (put 'reduce `((polynomial ,LOWEST-VARIABLE) (polynomial ,LOWEST-VARIABLE)) (lambda (L1 L2) (map (curry make-poly LOWEST-VARIABLE) (reduce-terms L1 L2)))) ) (loop (next-lower var)))) (loop HIGHEST-VARIABLE)) (install-generic-procedures) ) (install-polynomial-package) ;;;;;;;;;;; ;; TESTS ;; ;;;;;;;;;;; ;; NUMBERS (=zero? (sub (make-complex-from-real-imag (make-rational-number 1 2) 0.75) (make-complex-from-real-imag 0.5 (make-rational-number 3 4)))) ;; #t (equ? (expo (make-complex-from-mag-ang 1 1) pi) -1) ; e ^ (i * pi) = -1 ;; #t (mul 0.25 (make-complex-from-real-imag 4 0)) ;; 1 ;; POLYNOMIALS IN DIFFERENT VARIABLES (define p (make-poly-from-dense-termlist 'x '(1 2 3))) (define q (make-poly-from-dense-termlist 'y '(4 5 6))) (add p q) ;; '((polynomial x) dense 1 2 ((polynomial y) dense 4 5 9)) (sub p q) ;; '((polynomial x) dense 1 2 ((polynomial y) dense -4 -5 -3)) (mul p q) ;; '((polynomial x) ;; dense ;; ((polynomial y) dense 4 5 6) ;; ((polynomial y) dense 8 10 12) ;; ((polynomial y) dense 12 15 18)) ;; POLYNOMIALS IN THE SAME VARIABLE (define p1 (make-poly-from-dense-termlist 'z '(1 1))) (define p2 (make-poly-from-dense-termlist 'z '(1 0 1))) (mul p1 p2) ;; '((polynomial z) dense 1 1 1 1) (expo p1 3) ;; '((polynomial z) dense 1 3 3 1) (reduce (mul p1 p2) (expo p1 3)) ;; '(((polynomial z) dense 1 0 1) ((polynomial z) dense 1 2 1)) (polynomial-division (make-poly-from-dense-termlist 'z '(32 0 0 0 0 -243)) (make-poly-from-dense-termlist 'z '(2 -3))) ;; '(((polynomial z) dense 16 24 36 54 81) ((polynomial z) dense)) ;; RATIONAL FUNCTIONS (define r1 (make-rational-function (make-poly-from-dense-termlist 'z '(1 3 3 1)) (make-poly-from-dense-termlist 'z '(1 -2 1)))) ; (x+1)^3/(x-1)^2 r1 ;; '(rational-function ;; ((polynomial z) dense 1 3 3 1) ;; ((polynomial z) dense 1 -2 1)) (define r2 (make-rational-function (make-poly-from-dense-termlist 'z '(1 -3 3 -1)) (make-poly-from-dense-termlist 'z '(1 2 1)))) ; (x-1)^3/(x+1)^2 r2 ;; '(rational-function ;; ((polynomial z) dense 1 -3 3 -1) ;; ((polynomial z) dense 1 2 1)) (add r1 r2) ;; '(rational-function ;; ((polynomial z) dense 1 0 10 0 5 0) ;; ((polynomial z) dense 1 0 -2 0 1)) (mul r1 r2) ;; '(rational-function ((polynomial z) dense 1 0 -1) ((polynomial z) dense 1)) (div r1 r2) ;; '(rational-function ;; ((polynomial z) dense 1 5 10 10 5 1) ;; ((polynomial z) dense 1 -5 10 -10 5 -1)) ;; WITH SPARSE OUTPUT (define s (make-poly-from-dense-termlist 'y `(1 2.0 ,(make-rational-number 6 2) ,(make-complex-from-real-imag 4 0) ,(make-complex-from-mag-ang -5 pi)))) (expo s 6) ; (y^4 + 2*y^3 + 3*y^2 + 4*y + 5) ^ 6 ;; '((polynomial y) ;; sparse ;; (24 1) ;; (23 12) ;; (22 78) ;; (21 364) ;; (20 1365) ;; (19 4332) ;; (18 11974) ;; (17 29376) ;; (16 64818) ;; (15 129740) ;; (14 236958) ;; (13 396516) ;; (12 609389) ;; (11 860772) ;; (10 1117050) ;; (9 1329584) ;; (8 1446498) ;; (7 1430532) ;; (6 1276546) ;; (5 1016220) ;; (4 709125) ;; (3 422500) ;; (2 206250) ;; (1 75000) ;; (0 15625))