1 ;;;"cring.scm" Extend Scheme numerics to any commutative ring.
2 ;Copyright (C) 1997, 1998 Aubrey Jaffer
4 ;Permission to copy this software, to redistribute it, and to use it
5 ;for any purpose is granted, subject to the following restrictions and
8 ;1. Any copy made of this software must include this copyright notice
11 ;2. I have made no warrantee or representation that the operation of
12 ;this software will be error-free, and I am under no obligation to
13 ;provide any services, by way of maintenance, update, or otherwise.
15 ;3. In conjunction with products arising from the use of this
16 ;material, there shall be no use of my name in any advertising,
17 ;promotional, or sales literature without prior written consent in
20 (require 'common-list-functions)
21 (require 'relational-database)
22 (require 'database-utilities)
25 (define cring:db (create-database #f 'alist-table))
26 (define (make-ruleset . rules)
28 (cond ((and (not (null? rules)) (symbol? (car rules)))
29 (set! name (car rules))
30 (set! rules (cdr rules)))
31 (else (set! name (gentemp))))
32 (define-tables cring:db
37 '((reduction expression))
39 (let ((table ((cring:db 'open-table) name #t)))
41 (list (table 'get 'reduction)
44 (define *ruleset* (make-ruleset 'default))
45 (define (cring:define-rule . args)
47 ((cadr *ruleset*) args)
48 (slib:warn "No ruleset in *ruleset*")))
50 (define (combined-rulesets . rulesets)
52 (cond ((symbol? (car rulesets))
53 (set! name (car rulesets))
54 (set! rulesets (cdr rulesets)))
55 (else (set! name (gentemp))))
56 (apply make-ruleset name
58 (map (lambda (ruleset) (((caddr ruleset) 'row:retrieve*)))
61 ;;; Distribute * over + (and -)
67 ;;(print 'distributing '* '+ exp1 exp2 '==>)
68 (apply + (map (lambda (trm) (* trm exp2)) (cdr exp1)))))
71 ;;(print 'distributing '* '- exp1 exp2 '==>)
72 (apply - (map (lambda (trm) (* trm exp2)) (cdr exp1)))))))
74 ;;; Distribute / over + (and -)
80 ;;(print 'distributing '/ '+ exp1 exp2 '==>)
81 (apply + (map (lambda (trm) (/ trm exp2)) (cdr exp1)))))
84 ;;(print 'distributing '/ '- exp1 exp2 '==>)
85 (apply - (map (lambda (trm) (/ trm exp2)) (cdr exp1)))))))
87 (define (symbol-alpha? sym)
88 (char-alphabetic? (string-ref (symbol->string sym) 0)))
89 (define (expression-< x y)
90 (cond ((and (number? x) (number? y)) (> x y)) ;want negatives last
93 ((and (symbol? x) (symbol? y))
94 (cond ((eqv? (symbol-alpha? x) (symbol-alpha? y))
95 (string<? (symbol->string x) (symbol->string y)))
96 (else (symbol-alpha? x))))
101 ((expression-< (car x) (car y)) #t)
102 ((expression-< (car y) (car x)) #f)
103 (else (expression-< (cdr x) (cdr y)))))
104 (define (expression-sort seq) (sort! seq expression-<))
110 (define number^ integer-expt)
111 (define is-term-op? (lambda (term op) (and (pair? term) (eq? op (car term)))))
112 ;;(define (sign x) (if (positive? x) 1 (if (negative? x) -1 0)))
113 (define number0? zero?)
114 (define (zero? x) (and (number? x) (number0? x)))
116 ;; To convert to CR internal form, NUMBER-op all the `numbers' in the
117 ;; argument list and remove them from the argument list. Collect the
118 ;; remaining arguments into equivalence classes, keeping track of the
119 ;; number of arguments in each class. The returned list is thus:
120 ;; (<numeric> (<expression1> . <exp1>) ...)
122 ;;; Converts * argument list to CR internal form
123 (define (cr*-args->fcts args)
124 ;;(print (cons 'cr*-args->fcts args) '==>)
125 (let loop ((args args) (pow 1) (nums 1) (arg.exps '()))
126 ;;(print (list 'loop args pow nums denoms arg.exps) '==>)
127 (cond ((null? args) (cons nums arg.exps))
128 ((number? (car args))
129 (let ((num^pow (number^ (car args) (abs pow))))
131 (loop (cdr args) pow (number/ (number* num^pow nums))
133 (loop (cdr args) pow (number* num^pow nums) arg.exps))))
135 ((is-term-op? (car args) '*) (loop (append (cdar args) (cdr args))
138 ((and (is-term-op? (car args) '-) (= 2 (length (car args))))
139 ;;(print 'got-here (car args))
140 (set! arg.exps (loop (cdar args) pow (number- nums) arg.exps))
144 ((and (is-term-op? (car args) '/) (= 2 (length (car args))))
146 ;;(print 'got-here=cr+ (car args))
147 (set! arg.exps (loop (cdar args) (number- pow) nums arg.exps))
151 ((is-term-op? (car args) '/)
153 ;;(print 'doing '/ (cddar args) (number- pow))
155 (loop (cddar args) (number- pow) nums arg.exps))
156 ;;(print 'finishing '/ (cons (cadar args) (cdr args)) pow)
157 (loop (cons (cadar args) (cdr args))
161 ;; Pull out numeric exponents as powers
162 ((and (is-term-op? (car args) '^)
163 (= 3 (length (car args)))
164 (number? (caddar args)))
165 (set! arg.exps (loop (list (cadar args))
166 (number* pow (caddar args))
169 (loop (cdr args) pow (car arg.exps) (cdr arg.exps)))
170 ;; combine with same terms
171 ((assoc (car args) arg.exps)
172 => (lambda (pair) (set-cdr! pair (number+ pow (cdr pair)))
173 (loop (cdr args) pow nums arg.exps)))
174 ;; Add new term to arg.exps
175 (else (loop (cdr args) pow nums
176 (cons (cons (car args) pow) arg.exps))))))
178 ;;; Converts + argument list to CR internal form
179 (define (cr+-args->trms args)
180 (let loop ((args args) (cof 1) (numbers 0) (arg.exps '()))
181 (cond ((null? args) (cons numbers arg.exps))
182 ((number? (car args))
185 (number+ (number* (car args) cof) numbers)
188 ((is-term-op? (car args) '+) (loop (append (cdar args) (cdr args))
192 ;; Idempotent singlet *
193 ((and (is-term-op? (car args) '*) (= 2 (length (car args))))
194 (loop (cons (cadar args) (cdr args))
198 ((and (is-term-op? (car args) '-) (= 2 (length (car args))))
200 (set! arg.exps (loop (cdar args) (number- cof) numbers arg.exps))
201 (loop (cdr args) cof (car arg.exps) (cdr arg.exps)))
202 ;; Pull out numeric factors as coefficients
203 ((and (is-term-op? (car args) '*) (some number? (cdar args)))
204 ;;(print 'got-here (car args) '=> (cons '* (remove-if number? (cdar args))))
206 (loop (list (cons '* (remove-if number? (cdar args))))
207 (apply number* cof (remove-if-not number? (cdar args)))
210 (loop (cdr args) cof (car arg.exps) (cdr arg.exps)))
211 ((is-term-op? (car args) '-)
213 (set! arg.exps (loop (cddar args) (number- cof) numbers arg.exps))
214 (loop (cons (cadar args) (cdr args))
218 ;; combine with same terms
219 ((assoc (car args) arg.exps)
220 => (lambda (pair) (set-cdr! pair (number+ cof (cdr pair)))
221 (loop (cdr args) cof numbers arg.exps)))
222 ;; Add new term to arg.exps
223 (else (loop (cdr args) cof numbers
224 (cons (cons (car args) cof) arg.exps))))))
226 ;;; Converts + or * internal form to Scheme expression
227 (define (cr-terms->form op ident inv-op higher-op res.cofs)
228 (define (negative-cof? fct.cof)
229 (negative? (cdr fct.cof)))
230 (define (finish exprs)
231 (if (null? exprs) ident
232 (if (null? (cdr exprs))
235 (define (do-terms sign fct.cofs)
237 (map (lambda (fct.cof)
238 (define cof (number* sign (cdr fct.cof)))
239 (cond ((eqv? 1 cof) (car fct.cof))
240 ((number? (car fct.cof)) (number* cof (car fct.cof)))
241 ((is-term-op? (car fct.cof) higher-op)
242 (if (eq? higher-op '^)
243 (list '^ (cadar fct.cof) (* cof (caddar fct.cof)))
244 (cons higher-op (cons cof (cdar fct.cof)))))
245 ((eqv? -1 cof) (list inv-op (car fct.cof)))
246 (else (list higher-op (car fct.cof) cof))))
248 (let* ((all.cofs (remove-if (lambda (fct.cof)
249 (or (zero? (cdr fct.cof))
250 (eqv? ident (car fct.cof))))
252 (cofs (map cdr all.cofs))
253 (some-positive? (some positive? cofs)))
254 ;;(print op 'positive? some-positive? 'negative? (some negative? cofs) all.cofs)
255 (cond ((and some-positive? (some negative? cofs))
258 1 (remove-if negative-cof? all.cofs))))
259 (do-terms -1 (remove-if-not negative-cof? all.cofs))))
260 (some-positive? (finish (do-terms 1 all.cofs)))
261 ((not (some negative? cofs)) ident)
262 (else (list inv-op (finish (do-terms -1 all.cofs)))))))
267 ;;This next line is commented out so ^ will collapse numerical expressions.
268 ;;((null? (cdr args)) (car args))
270 (let ((in (cr*-args->fcts args)))
275 (set-cdr! in (list (cons 1 1))))
280 (lambda (numeric red.cofs res.cofs)
283 ;;(list (cons (abs numeric) 1))
286 (cr1 '* number* '^ '/ (car in) (cdr in))))))
287 (cond ((number0? (+ -1 num)) ans)
288 ((number? ans) (number* num ans))
289 ((number0? (+ 1 num))
290 (if (and (list? ans) (= 2 (length ans)) (eq? '- (car ans)))
293 ((not (pair? ans)) (list '* num ans))
296 ((*) (append (list '* num) (cdr ans)))
297 ((+) (apply + (map (lambda (mon) (* num mon)) (cdr ans))))
298 ((-) (apply - (map (lambda (mon) (* num mon)) (cdr ans))))
299 (else (list '* num ans))))))))))))
302 (cond ((null? args) 0)
303 ;;((null? (cdr args)) (car args))
305 (let ((in (cr+-args->trms args)))
310 (apply (lambda (numeric red.cofs res.cofs)
312 (list (if (and (number? numeric)
314 (cons (abs numeric) -1)
318 (cr1 '+ number+ '* '- (car in) (cdr in)))))))))
320 (define (- arg1 . args)
322 (if (number? arg1) (number- arg1)
323 (* -1 arg1) ;(list '- arg1)
325 (+ arg1 (* -1 (apply + args)))))
327 ;;(print `(/ ,arg1 ,@args) '=> )
328 (define (/ arg1 . args)
331 (* arg1 (^ (apply * args) -1))))
333 (define (^ arg1 arg2)
334 (cond ((and (number? arg2) (integer? arg2))
335 (* (list '^ arg1 arg2)))
336 (else (list '^ arg1 arg2))))
338 ;; TRY-EACH-PAIR-ONCE algorithm. I think this does the minimum
339 ;; number of rule lookups given no information about how to sort
342 ;; Pick equivalence classes one at a time and move them into the
343 ;; result set of equivalence classes by searching for rules to
344 ;; multiply an element of the chosen class by itself (if multiple) and
345 ;; the element of each class already in the result group. Each
346 ;; (multiplicative) term resulting from rule application would be put
347 ;; in the result class, if that class exists; or put in an argument
350 (define (cr1 op number-op hop inv-op numeric in)
351 (define red.pows '())
352 (define res.pows '())
353 (define (cring:apply-rule->terms exp1 exp2) ;(display op)
354 (let ((ans (cring:apply-rule op exp1 exp2)))
356 ((number? ans) (list ans))
357 (else (list (cons ans 1))))))
358 (define (cring:apply-inv-rule->terms exp1 exp2) ;(display inv-op)
359 (let ((ans (cring:apply-rule inv-op exp1 exp2)))
361 ((number? ans) (list ans))
362 (else (list (cons ans 1))))))
363 (let loop.arg.pow.s ((arg (caar in)) (pow (cdar in)) (arg.pows (cdr in)))
364 (define (arg-loop arg.pows)
365 (cond ((not (null? arg.pows))
366 (loop.arg.pow.s (caar arg.pows) (cdar arg.pows) (cdr arg.pows)))
367 (else (list numeric red.pows res.pows)))) ; Actually return!
368 (define (merge-res tmp.pows multiplicity)
369 (cond ((null? tmp.pows))
370 ((number? (car tmp.pows))
371 (do ((m (number+ -1 (abs multiplicity)) (number+ -1 m))
372 (n numeric (number-op n (abs (car tmp.pows)))))
373 ((negative? m) (set! numeric n)))
374 (merge-res (cdr tmp.pows) multiplicity))
375 ((or (assoc (car tmp.pows) res.pows)
376 (assoc (car tmp.pows) arg.pows))
378 (set-cdr! pair (number+
379 pow (number-op multiplicity (cdar tmp.pows))))
380 (merge-res (cdr tmp.pows) multiplicity)))
381 ((assoc (car tmp.pows) red.pows)
384 (cons (cons (caar tmp.pows)
387 (number* multiplicity (cdar tmp.pows))))
390 (merge-res (cdr tmp.pows) multiplicity)))
392 (cons (cons (caar tmp.pows)
393 (number* multiplicity (cdar tmp.pows)))
395 (merge-res (cdr tmp.pows) multiplicity))))
396 (define (try-fct.pow fct.pow)
397 ;;(print 'try-fct.pow fct.pow op 'arg arg 'pow pow)
398 (cond ((or (zero? (cdr fct.pow)) (number? (car fct.pow))) #f)
399 ((not (and (number? pow) (number? (cdr fct.pow))
400 (integer? pow) ;(integer? (cdr fct.pow))
403 ;;((zero? pow) (slib:error "Don't try exp-0 terms") #f)
404 ;;((or (number? arg) (number? (car fct.pow)))
405 ;; (slib:error 'found-number arg fct.pow) #f)
406 ((and (positive? pow) (positive? (cdr fct.pow))
407 (or (cring:apply-rule->terms arg (car fct.pow))
408 (cring:apply-rule->terms (car fct.pow) arg)))
410 ;;(print op op terms)
411 (let ((multiplicity (min pow (cdr fct.pow))))
412 (set-cdr! fct.pow (number- (cdr fct.pow) multiplicity))
413 (set! pow (number- pow multiplicity))
414 (merge-res terms multiplicity))))
415 ((and (negative? pow) (negative? (cdr fct.pow))
416 (or (cring:apply-rule->terms arg (car fct.pow))
417 (cring:apply-rule->terms (car fct.pow) arg)))
419 ;;(print inv-op inv-op terms)
420 (let ((multiplicity (max pow (cdr fct.pow))))
421 (set-cdr! fct.pow (number+ (cdr fct.pow) multiplicity))
422 (set! pow (number+ pow multiplicity))
423 (merge-res terms multiplicity))))
424 ((and (positive? pow) (negative? (cdr fct.pow))
425 (cring:apply-inv-rule->terms arg (car fct.pow)))
427 ;;(print op inv-op terms)
428 (let ((multiplicity (min pow (number- (cdr fct.pow)))))
429 (set-cdr! fct.pow (number+ (cdr fct.pow) multiplicity))
430 (set! pow (number- pow multiplicity))
431 (merge-res terms multiplicity))))
432 ((and (negative? pow) (positive? (cdr fct.pow))
433 (cring:apply-inv-rule->terms (car fct.pow) arg))
435 ;;(print inv-op op terms)
436 (let ((multiplicity (max (number- pow) (cdr fct.pow))))
437 (set-cdr! fct.pow (number- (cdr fct.pow) multiplicity))
438 (set! pow (number+ pow multiplicity))
439 (merge-res terms multiplicity))))
441 ;;(print op numeric 'arg arg 'pow pow 'arg.pows arg.pows 'red.pows red.pows 'res.pows res.pows)
442 ;;(trace arg-loop cring:apply-rule->terms merge-res try-fct.pow) (set! *qp-width* 333)
443 (cond ((or (zero? pow) (eqv? 1 arg)) ;(number? arg) arg seems to always be 1
445 ((assoc arg res.pows) => (lambda (pair)
446 (set-cdr! pair (number+ pow (cdr pair)))
447 (arg-loop arg.pows)))
448 ((and (> (abs pow) 1) (cring:apply-rule->terms arg arg))
450 (merge-res terms (quotient pow 2))
452 (loop.arg.pow.s arg 1 arg.pows)
453 (arg-loop arg.pows))))
454 ((or (some try-fct.pow res.pows) (some try-fct.pow arg.pows))
455 (loop.arg.pow.s arg pow arg.pows))
456 (else (set! res.pows (cons (cons arg pow) res.pows))
457 (arg-loop arg.pows)))))
459 (define (cring:try-rule op sop1 sop2 exp1 exp2)
461 (let ((rule ((car *ruleset*) op sop1 sop2)))
462 (and rule (rule exp1 exp2)))))
464 (define (cring:apply-rule op exp1 exp2)
466 (or (and (pair? exp2)
467 (cring:try-rule op (car exp1) (car exp2) exp1 exp2))
468 (cring:try-rule op (car exp1) 'identity exp1 exp2))))
470 ;;(begin (trace cr-terms->form) (set! *qp-width* 333))