home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / maths / b116_1 / jacal / poly < prev    next >
Text File  |  1993-06-11  |  16KB  |  508 lines

  1. ;;; JACAL: Symbolic Mathematics System.        -*-scheme-*-
  2. ;;; Copyright 1989, 1990, 1991, 1992, 1993 Aubrey Jaffer.
  3. ;;; See the file "COPYING" for terms applying to this program.
  4.  
  5. ;;;Functions which operate on polynomials as polynomials
  6. ;;;have prefix POLY:
  7. ;;;Functions which operate on polynomials with the same major variable
  8. ;;;have prefix UNIV:
  9. ;;;Functions which operate on coefficients (without major varible)
  10. ;;;have prefix COES:
  11.  
  12. ;(proclaim '(optimize (speed 3) (compilation-speed 0)))
  13.  
  14. (define (one? n) (eqv? 1 n))
  15. ;;; POLY:0? should do those normalizations which could lead to colapse
  16. ;;; of an expression to 0.
  17. (define (poly:0? p) (eqv? 0 p))
  18. (define (divides? a b) (zero? (remainder b a)))
  19.  
  20. ;;; poly is the internal workhorse data type in the form
  21. ;;; of numeric or list (var coeff0 coeff1 ...)
  22. ;;; where var is a variable and coeffn is the coefficient of var^n.
  23. ;;; coeffn is poly.  The variables currently are arranged
  24. ;;; with the reverse alphabetically with z higher order than A.
  25.  
  26. (define (poly:find-var? poly var)
  27.   (poly:find-var-if? poly (lambda (x) (eqv? var x))))
  28.  
  29. (define (poly:find-var-if? poly proc)
  30.   (cond ((number? poly) #f)
  31.     ((proc (car poly)))
  32.     (else (some (lambda (x) (poly:find-var-if? x proc)) (cdr poly)))))
  33.  
  34. ;;; This can call proc more than once per var
  35. (define (poly:for-each-var proc poly)
  36.   (cond ((number? poly))
  37.     (else
  38.      (proc (car poly))
  39.      (for-each (lambda (b) (poly:for-each-var proc b))
  40.            (cdr poly)))))
  41.  
  42. ;;;POLY:VARS returns a list of all vars used in POLY
  43. (define (poly:vars poly)
  44.   (let ((elts '()))
  45.     (poly:for-each-var (lambda (v) (set! elts (adjoin v elts))) poly)
  46.     elts))
  47.  
  48. ;;;; the following functions are for internal use on the poly data type
  49.  
  50. ;;; this normalizes short polys.
  51. (define (univ:norm0 var col)
  52.   (cond ((null? col) 0)
  53.     ((null? (cdr col)) (car col))
  54.     (else (cons var col))))
  55.  
  56. (define (map-no-end-0s proc l)
  57.   (if (null? l)
  58.       l
  59.     (let ((res (proc (car l)))
  60.       (rest (map-no-end-0s proc (cdr l))))
  61.       (if (and (null? rest) (eqv? 0 res))
  62.       rest
  63.     (cons res rest)))))
  64. (define (map2c-no-end-0s proc l1 l2)
  65.   (cond ((null? l1) l2)
  66.     ((null? l2) l1)
  67.     (else
  68.      (let ((res (proc (car l1) (car l2)))
  69.       (rest (map2c-no-end-0s proc (cdr l1) (cdr l2))))
  70.        (if (and (null? rest) (eqv? 0 res))
  71.            rest
  72.          (cons res rest))))))
  73. (define (univ-w/o-lt p)
  74.   (univ:norm0 (car p) (map-no-end-0s identity (butlast (cdr p) 1))))
  75. (define (make-list-length lst len fill)
  76.   (let ((ld (- len (length lst))))
  77.     (cond ((negative? ld) (butlast lst (- ld)))
  78.       (else (append lst (make-list ld fill))))))
  79.  
  80. (define (ipow-by-squaring x n acc proc)
  81.   (cond ((zero? n) acc)
  82.     ((one? n) (proc acc x))
  83.     (else (ipow-by-squaring (proc x x)
  84.                 (quotient n 2)
  85.                 (if (even? n) acc (proc acc x))
  86.                 proc))))
  87.  
  88. (define (poly:add-const term p2)
  89.   (cons (car p2) (cons (poly:+ term (cadr p2)) (cddr p2))))
  90. (define (poly:+ p1 p2)
  91.   (cond ((and (number? p1) (number? p2)) (+ p1 p2))
  92.     ((number? p1) (if (zero? p1) p2 (poly:add-const p1 p2)))
  93.     ((number? p2) (if (zero? p2) p1 (poly:add-const p2 p1)))
  94.     ((eq? (car p1) (car p2))
  95.      (univ:norm0 (car p1) (map2c-no-end-0s poly:+ (cdr p1) (cdr p2))))
  96.     ((var:> (car p2) (car p1)) (poly:add-const p1 p2))
  97.     (else (poly:add-const p2 p1))))
  98.  
  99. (define (univ:* p1 p2)
  100.   (let ((res (make-list (+ (length (cdr p1)) (length (cdr p2)) -1) 0)))
  101.     (do ((rpl res (cdr rpl))
  102.      (a (cdr p1) (cdr a)))
  103.     ((null? a) (cons (car p1) res))
  104.     (do ((b (cdr p2) (cdr b))
  105.          (rp rpl (cdr rp)))
  106.         ((null? b))
  107.         (set-car! rp (poly:+ (poly:* (car a) (car b)) (car rp)))))))
  108. (define (poly:times-const term p2)
  109.   (cons (car p2) (map (lambda (x) (poly:* term x)) (cdr p2))))
  110. (define (poly:* p1 p2)
  111.   (cond ((and (number? p1) (number? p2)) (* p1 p2))
  112.     ((number? p1) (cond ((zero? p1) 0)
  113.                 ((one? p1) p2)
  114.                 (else (poly:times-const p1 p2))))
  115.     ((number? p2) (cond ((zero? p2) 0)
  116.                 ((one? p2) p1)
  117.                 (else (poly:times-const p2 p1))))
  118.     ((eq? (car p1) (car p2)) (univ:* p1 p2))
  119.     ((var:> (car p2) (car p1)) (poly:times-const p1 p2))
  120.     (else (poly:times-const p2 p1))))
  121.  
  122. (define (poly:negate p) (poly:* -1 p))
  123. (define (poly:- p1 p2) (poly:+ p1 (poly:negate p2)))
  124.  
  125. (define (univ:/? u v)
  126.   (let ((r (list->vector (cdr u)))
  127.     (m (length (cddr u)))
  128.     (n (length (cddr v)))
  129.     (vn (car (last-pair v)))
  130.     (q '()))
  131.     (do ((k (- m n) (- k 1))
  132.      (qk (poly:/? (vector-ref r m) vn)
  133.          (and (> k 0) (poly:/? (vector-ref r (+ n k -1)) vn))))
  134.     ((not qk)
  135.      (and (< k 0)
  136.           (do ((k (- n 2) (- k 1)))
  137.           ((or (< k 0) (not (poly:0? (vector-ref r k))))
  138.            (< k 0)))
  139.           (univ:norm0 (car u) q)))
  140.       (set! q (cons qk q))
  141.       (let ((qk- (poly:negate qk)))
  142.     (do ((j (+ n k -1) (- j 1)))
  143.         ((< j k))
  144.       (vector-set! r j (poly:+ (vector-ref r j)
  145.                    (poly:* (list-ref v (+ (- j k) 1)) qk-))))))))
  146.  
  147. ;;; POLY:/? returns U / V if V divides U, otherwise returns #f
  148. (define (poly:/? u v)
  149.   (cond ((equal? u v) 1)
  150.     ((eqv? 0 u) 0)
  151.     ((eqv? 0 v) #f)
  152.     ((unit? v) (poly:* v u))
  153.     ((and (number? u) (number? v)) (and (divides? v u) (quotient u v)))
  154.     ((number? v) (univ:/? u (const:promote (car u) v)))
  155.     ((number? u) #f)
  156.     ((eq? (car u) (car v)) (univ:/? u v))
  157.     ((var:> (car u) (car v))
  158.      (univ:/? u (const:promote (car u) v)))
  159.     (else #f)))
  160.  
  161. (define (univ:/ dividend divisor)
  162.   (or (univ:/? dividend divisor)
  163.       (math:error divisor 'does-not-udivide- dividend)))
  164.  
  165. (define (poly:/ dividend divisor)
  166.   (or (poly:/? dividend divisor)
  167.       (math:error divisor 'does-not-divide- dividend)))
  168.  
  169. (define (univ:monomial coeff n var)
  170.   (cond ((eq? 0 coeff) 0)
  171.     ((>= 0 n) coeff)
  172.     (else
  173.      (cons var
  174.            ((lambda (x) (set-car! (last-pair x) coeff) x)
  175.         (make-list (+ 1 n) 0))))))
  176.  
  177. (define (poly:degree p var)
  178.    (cond ((number? p) 0)
  179.      ((eq? var (car p)) (length (cddr p)))
  180.      ((var:> var (car p)) 0)
  181.      (else (reduce-init (lambda (m c) (max m (poly:degree c var)))
  182.                 0
  183.                 (cdr p)))))
  184.  
  185. ;(define (leading-coeff p var) (poly:coeff p var (poly:degree p var)))
  186. ;(define (monic? u var) (one? (leading-coeff u var)))
  187.  
  188. (define (poly:^ x n)
  189.   (if (number? x)
  190. ;      (expt x n)
  191.     (ipow-by-squaring x n 1 *)
  192.     (ipow-by-squaring x n 1 poly:*)))
  193.  
  194. ;;;; Routines used in normalizing IMPL polynomials
  195.  
  196. (define (leading-number p)
  197.   (if (number? p) p (leading-number (car (last-pair p)))))
  198.  
  199. ;;; This canonicalizes polys with respect to sign by forcing the
  200. ;;; numerical coefficient of the a certain term to always be positive.
  201. (define (signcan p)
  202.   (if (negative? (leading-number p)) (poly:negate p) p))
  203.  
  204. (define (shorter? x y) (< (length x) (length y)))
  205.  
  206. (define (univ:degree p var)
  207.   (if (or (number? p) (not (eq? (car p) var))) 0 (length (cddr p))))
  208.  
  209. ;;; THE NEXT 3 ROUTINES FOR SUBRESULTANT GCD ASSUME THAT THE ARGUMENTS
  210. ;;; ARE POLYNOMIALS WITH THE SAME MAJOR VARIABLE.
  211. ;;;  THESE TWO ROUTINES ASSUME THAT THE FIRST ARGUMENT IS OF GREATER
  212. ;;;  OR EQUAL ORDER THAN THE SECOND.
  213. ;;; These algorithms taken from:
  214. ;;; Knuth, D. E.,
  215. ;;; The Art Of Computer Programming, Vol. 2: Seminumerical Algorithms,
  216. ;;; Addison Wesley, Reading, MA 1969.
  217. ;;; Pseudo Remainder
  218.  
  219. ;;; This returns a list of the pseudo quotient and pseudo remainder.
  220. (define (univ:pdiv u v)
  221.   (let* ((r (list->vector (cdr u)))
  222.      (m (length (cddr u)))
  223.      (n (length (cddr v)))
  224.      (vn (car (last-pair v)))
  225.      (q (make-vector (+ (- m n) 1) 1)))
  226.     (do ((tt (- (- m n) 1) (- tt 1))
  227.      (k 1 (+ 1 k))
  228.      (vnp 1))
  229.     ((< tt 0))
  230.     (set! vnp (poly:* vnp vn))
  231.     (vector-set! q k vnp)
  232.     (vector-set! r tt (poly:* (vector-ref r tt) vnp)))
  233.     (do ((k (- m n) (- k 1))
  234.      (rnk 0))
  235.     ((< k 0))
  236.       (set! rnk (poly:negate (vector-ref r (+ n k))))
  237.       (do ((j (+ n k -1) (- j 1)))
  238.       ((< j k))
  239.     (vector-set! r j (poly:+ (poly:* (vector-ref r j) vn)
  240.                  (poly:* (list-ref v (+ (- j k) 1)) rnk)))))
  241.     (list
  242.      (do ((k (- m n) (+ -1 k))
  243.       (end '() (cons (poly:* (vector-ref r (+ n k))
  244.                  (vector-ref q k)) end)))
  245.      ((zero? k) (univ:norm0 (car u) (cons (vector-ref r n) end))))
  246.      (do ((j (- n 1) (- j 1))
  247.       (end '()))
  248.      ((< j 0) (univ:norm0 (car u) end))
  249.      (if (not (and (null? end) (eqv? 0 (vector-ref r j))))
  250.          (set! end (cons (vector-ref r j) end)))))))
  251. (define (poly:pdiv dividend divisor var)
  252.   (let ((pd1 (poly:degree dividend var))
  253.     (pd2 (poly:degree divisor var)))
  254.     (cond ((< pd1 pd2) (list 0 dividend))
  255.       ((zero? (+ pd1 pd2))
  256.        (list (quotient dividend divisor) (remainder dividend divisor)))
  257.       ((zero? pd1) (list 0 dividend))
  258.       ((zero? pd2)
  259. ;;; This should work but doesn't.
  260. ;;;       (map univ:demote (univ:pdiv (promote var dividend)
  261. ;;;                       (const:promote var divisor)))
  262.        (list 0 dividend))
  263.       (else
  264.        (map univ:demote (univ:pdiv (promote var dividend)
  265.                        (promote var divisor)))))))
  266. (define (univ:prem u v)
  267.   (let* ((r (list->vector (cdr u)))
  268.      (m (length (cddr u)))
  269.      (n (length (cddr v)))
  270.      (vn (car (last-pair v))))
  271.     (do ((k (- (- m n) 1) (- k 1))
  272.      (vnp 1))
  273.     ((< k 0))
  274.       (set! vnp (poly:* vnp vn))
  275.       (vector-set! r k (poly:* (vector-ref r k) vnp)))
  276.     (do ((k (- m n) (- k 1))
  277.      (rnk 0))
  278.     ((< k 0))
  279.       (set! rnk (poly:negate (vector-ref r (+ n k))))
  280.       (do ((j (+ n k -1) (- j 1)))
  281.       ((< j k))
  282.     (vector-set! r j (poly:+ (poly:* (vector-ref r j) vn)
  283.                  (poly:* (list-ref v (+ (- j k) 1)) rnk)))))
  284.     (do ((j (- n 1) (- j 1))
  285.      (end '()))
  286.     ((< j 0) (univ:norm0 (car u) end))
  287.       (if (and (null? end) (eqv? 0 (vector-ref r j)))
  288.       #f
  289.       (set! end (cons (vector-ref r j) end))))))
  290.  
  291. ;;; Pseudo Remainder Sequence
  292. (define (univ:prs u v)
  293.   (let ((var (car u))
  294.     (g 1)
  295.     (h 1)
  296.     (delta 0))
  297.     (do ((r (univ:prem u v) (univ:prem u v)))
  298.     ((eqv? 0 (univ:degree r var))
  299.      (if (eqv? 0 r) v r))
  300.       (set! delta (- (univ:degree u var) (univ:degree v var)))
  301.       (set! u v)
  302.       (set! v (univ:/ r (const:promote (car r) (poly:* g (poly:^ h delta)))))
  303.       (set! g (car (last-pair u)))
  304.       (set! h (cond ((one? delta) g)
  305.             ((zero? delta) h)
  306.             (else (poly:/ (poly:^ g delta)
  307.                   (poly:^ h (+ -1 delta)))))))))
  308.  
  309. (define (univ:gcd u v)
  310.   (let* ((cu (univ:cont u))
  311.      (cv (univ:cont v))
  312.      (c (poly:gcd cu cv))
  313.      (ppu (poly:/ u cu))
  314.      (ppv (poly:/ v cv))
  315.      (ans (if (shorter? ppv ppu)
  316.           (univ:prs ppu ppv)
  317.         (univ:prs ppv ppu))))
  318.     (if (zero? (univ:degree ans (car u)))
  319.     c
  320.       (poly:* c (univ:primpart ans)))))
  321.  
  322. (define (poly:gcd p1 p2)
  323.   (cond ((equal? p1 p2) p1)
  324.     ((and (number? p1) (number? p2)) (gcd p1 p2))
  325.     ((number? p1) (if (zero? p1)
  326.               p2
  327.             (apply poly:gcd* p1 (cdr p2))))
  328.     ((number? p2) (if (zero? p2)
  329.               p1
  330.             (apply poly:gcd* p2 (cdr p1))))
  331.     ((eq? (car p1) (car p2)) (univ:gcd p1 p2))
  332.     ((var:> (car p2) (car p1)) (apply poly:gcd* p1 (cdr p2)))
  333.     (else (apply poly:gcd* p2 (cdr p1)))))
  334.  
  335. (define (poly:gcd* . li)
  336.   (let ((nums (remove-if-not number? li)))
  337.     (if (null? nums)
  338.     (reduce poly:gcd li)
  339.     (let ((gnum (reduce gcd nums)))
  340.       (if (= 1 gnum) 1
  341.           (reduce-init poly:gcd gnum (remove-if number? li)))))))
  342.  
  343. (define (univ:cont p) (apply poly:gcd* (cdr p)))
  344. (define (univ:primpart p) (poly:/ p (univ:cont p)))
  345. (define (poly:num-cont p)
  346.   (if (number? p) p
  347.       (do ((l (cdr p) (cdr l))
  348.        (n (poly:num-cont (cadr p))
  349.           (gcd n (poly:num-cont (cadr l)))))
  350.       ((or (= 1 n) (null? (cdr l))) n))))
  351.  
  352. (define (list-ref? l n)
  353.   (cond ((null? l) #f)
  354.     ((zero? n) (car l))
  355.     (else (list-ref? (cdr l) (- n 1)))))
  356.  
  357. (define (univ:coeff p ord) (or (list-ref? (cdr p) ord) 0))
  358. (define (poly:coeff p var ord)
  359.   (cond ((or (number? p) (var:> var (car p)))
  360.      (if (zero? ord) p 0))
  361.     ((eq? var (car p)) (univ:coeff p ord))
  362.     (else
  363.      (univ:norm0 (car p)
  364.              (map-no-end-0s (lambda (c) (poly:coeff c var ord))
  365.                     (cdr p))))))
  366.  
  367. (define (poly:subst0 old e) (poly:coeff e old 0))
  368.  
  369. (define const:promote list)
  370.  
  371. (define (promote var p)
  372.   (if (eq? var (car p))
  373.       p
  374.       (let ((dgr (poly:degree p var)))
  375.     (do ((i dgr (+ -1 i))
  376.          (ol (list (poly:coeff p var dgr))
  377.          (cons (poly:coeff p var (+ -1 i)) ol)))
  378.         ((zero? i) (cons var ol))))))
  379.  
  380. ;;;this is bummed if v has higher priority than any variable in (cdr p)
  381. (define (univ:demote p)
  382.   (if (number? p)
  383.       p
  384.     (let ((v (car p)))
  385.       (if (every (lambda (cof) (or (number? cof) (var:> v (car cof))))
  386.          (cdr p))
  387.       p
  388.     (poly:+ (cadr p)
  389.         (do ((trms (cddr p) (cdr trms))
  390.              (sum 0)
  391.              (mon (list v 0 1) (cons v (cons 0 (cdr mon)))))
  392.             ((null? trms) sum)
  393.             (set! sum (poly:+ sum (poly:* mon (car trms))))))))))
  394.  
  395. (define (sylvester p1 p2 var)
  396.   (set! p1 (promote var p1))
  397.   (set! p2 (promote var p2))
  398.   (let ((d1 (univ:degree p1 var))
  399.     (d2 (univ:degree p2 var))
  400.     (m (list)))
  401.     (do ((i d1 (+ -1 i))
  402.      (row (nconc (make-list (+ -1 d1) 0) (reverse (cdr p2)))
  403.           (append (cdr row) (list 0))))
  404.     ((<= i 1) (set! m (cons row m)))
  405.     (set! m (cons row m)))
  406.     (do ((i d2 (+ -1 i))
  407.      (row (nconc (make-list (+ -1 d2) 0) (reverse (cdr p1)))
  408.           (append (cdr row) (list 0))))
  409.     ((<= i 1) (set! m (cons row m)))
  410.     (set! m (cons row m)))
  411.     m))
  412.  
  413. ;;; Bareiss's integer preserving gaussian elimination.
  414. ;;; Bareiss, E.H.: Sylvester's identity and multistep
  415. ;;; integer-preserving Gaussian elimination. Mathematics of
  416. ;;; Computation 22, 565-578, 1968.
  417. ;;; as related by:
  418. ;;; Akritas, A.G.: Exact Algorithms for the Matrix-Triangulation
  419. ;;; Subresultant PRS Method.  Computers and Mathematics, 145-155.
  420. ;;; Springer Verlag, 1989.
  421. (define (bareiss m)
  422.   4)
  423.  
  424. (define (poly:resultant p1 p2 var)
  425.   (let ((u1 (promote var p1))
  426.     (u2 (promote var p2)))
  427.     (or (not (zero? (univ:degree u1 var)))
  428.     (not (zero? (univ:degree u2 var)))
  429.     (math:error var 'does-not-appear-in- p1 'or- p2))
  430.     (let ((res (cond ((zero? (univ:degree u1 var)) p1)
  431.              ((zero? (univ:degree u2 var)) p2)
  432.              ((shorter? u1 u2) (univ:prs u2 u1))
  433.              (else (univ:prs u1 u2)))))
  434.       (if (zero? (univ:degree res var)) res
  435.       0))))
  436.  
  437. (define (poly:elim2 p1 p2 var)
  438.   (let* ((u1 (promote var p1))
  439.      (u2 (promote var p2))
  440.      (pg (poly:gcd (car (last-pair u1)) (car (last-pair u2)))))
  441.     (or (not (zero? (univ:degree u1 var)))
  442.     (not (zero? (univ:degree u2 var)))
  443.     (math:error var 'does-not-appear-in- p1 'or- p2))
  444.     (let* ((res (cond ((zero? (univ:degree u1 var)) p1)
  445.               ((zero? (univ:degree u2 var)) p2)
  446.               ((shorter? u1 u2) (univ:prs u2 u1))
  447.               (else (univ:prs u1 u2))))
  448.        (e (if (zero? (univ:degree res var)) res
  449.           0)))
  450.       (if (or (number? pg)) e
  451.       (let ((q (poly:/ e pg)))
  452.         (if (number? q) e (univ:primpart q)))))))
  453.  
  454. (define (poly-mod-number poly modulus)
  455.   (if (number? poly)
  456.       (modulo poly modulus)
  457.     (cons (car poly)
  458.       (map-no-end-0s (lambda (x) (poly-mod-number x modulus))
  459.              (cdr poly)))))
  460.  
  461. (define (poly:prem dividend divisor)
  462.   (if (number? divisor)
  463.       (poly-mod-number dividend divisor)
  464.       (let ((var (car divisor)))
  465.     (if (> (poly:degree divisor var) (poly:degree dividend var))
  466.         dividend
  467.         (univ:demote (univ:prem (promote var dividend)
  468.                     (promote var divisor)))))))
  469.  
  470. ;;;; VERIFICATION TESTS
  471. (define (poly:test)
  472.   (define a (sexp->var 'a))
  473.   (define b (sexp->var 'b))
  474.   (define c (sexp->var 'c))
  475.   (define x (sexp->var 'x))
  476.   (define y (sexp->var 'y))
  477.   (test (list a 0 -2)
  478.     poly:gcd
  479.     (list a 0 -2)
  480.     (list a 0 0 -2))
  481.   (test (list x (list a 0 1) 1)
  482.     poly:gcd
  483.     (list x (list a 0 0 -1) 0 1)
  484.     (list x (list a 0 0 1) (list a 0 2) 1))
  485.   (test (list x 0 (list a 0 1))
  486.     poly:gcd
  487.     (list x 0 (list a 0 0 1))
  488.     (list x 0 0 (list a 0 1)))
  489.   (test (list x (list b 0 0 1) 0 (list b 1 2) (list a 0 1) 1)
  490.     poly:resultant
  491.     (list y (list x (list b 0 1) 0 1) (list x 0 1))
  492.     (list y (list x 1 (list a 0 1)) 0 1)
  493.     y)
  494.   (test (list y (list b 0 0 1) 0 (list b 1 2) (list a 0 1) 1)
  495.     poly:resultant
  496.     (list y (list b 0 1) (list x 0 1) 1)
  497.     (list y (list x 1 0 1) (list a 0 1))
  498.     x)
  499.   (test 1
  500.      poly:gcd
  501.      (list x -5 2 8 -3 -3 1 1)
  502.      (list x 21 -9 -4 5 3))
  503.   (test 1
  504.      poly:gcd
  505.      (list x -5 2 8 -3 -3 0 1 0 1)
  506.      (list x 21 -9 -4 0 5 0 3))
  507.   'done)
  508.