Subversion Repositories programming

Rev

Rev 100 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
72 irasnyd 1
;Written By: Ira Snyder (parts from Dr. Soroka)
2
;Due Date:   02-28-2005
3
;Homework #: HW11
108 ira 4
;License: Unknown, except for the code indicated as being
5
;         written by Ira Snyder, which is in the Public Domain
72 irasnyd 6
 
71 irasnyd 7
; POLY.LSP
8
;
9
; add code for (* i2 i3) &c
10
;
11
; on T 050118 at 1730
12
; It reads & prints polynomials.
13
;
14
; on T 050118 at 0830
15
; All new.
16
; Brought over the ARITH code & began altering it to handle polynomials.
17
;
18
 
72 irasnyd 19
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ASSUMPTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
22
;;;                                                                          ;;;
23
;;; Since it was not specified, I decided to make POLY- work in same way     ;;;
24
;;; as the lisp function -. (POLY- P1 P2) would be written in infix as       ;;;
25
;;; P1 - P2.                                                                 ;;;
26
;;;                                                                          ;;;
27
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 irasnyd 28
 
29
;
30
; Loop -- read:
31
; a polynomial 
32
; or a reference
33
; or an expression involving references.
34
;
35
(defun POLY nil
36
  (setf $HISTORY nil)
37
  (let ((I 0)
38
        (POLY))
39
    (loop
40
      (format t "~%(i~s) " I)
41
      (setf INPUT (read))
42
      (cond
43
        ((member INPUT '(exit halt quit stop)) (return nil))
44
        ((eq INPUT 'history) (SHOW-HISTORY)) 
45
        ((and (REFERENCEP INPUT) (not (VALID-REFERENCE-P INPUT)))
46
                 (format t "Reference out of range: ~s" INPUT)
47
                 )
48
        ((REFERENCEP INPUT)    ; this reference is valid
49
          (let ((OUTPUT (GET-REFERENCE INPUT)))
50
            (format t "(o~s) " I)
51
            (format t "~s~%" OUTPUT)
52
            (setf $HISTORY (append $HISTORY (list OUTPUT)))
53
            (setf I (1+ I))
54
            )
55
          )
56
        ((atom INPUT) (format t "Illegal expression: ~s" INPUT))
57
        ((VALID-OP-REF-REF INPUT) 
58
          (let ((OUTPUT (EXECUTE-EXPRESSION INPUT)))
59
            (format t "(o~s)~%" I)
60
            (WRITE-POLY OUTPUT)
61
            (setf $HISTORY (append $HISTORY (list OUTPUT)))
62
            (setf I (1+ I))
63
            ) ;end let
64
          )
65
        ((not (VALID-IN-EXP INPUT)))
66
        (t (let ((INTERMS (IE2INTERMS INPUT)))
67
             (setf POLY (mapcar #'(lambda (INTERM) (VALID-INTERM nil INTERM))
68
                                INTERMS))
69
             )
70
           (format t "(o~s)~%" I) 
71
           (WRITE-POLY POLY)
72
           (setf $HISTORY (append $HISTORY (list POLY)))
73
           (setf I (1+ I))
74
           )
75
        ) ;end cond
76
    ) ;end loop
77
  ) ;end let
78
) ;end defun
79
 
80
; VALID-OP-REF-REF recognizes forms like (* i2 i3)
81
; where the references are valid.
82
; Appropriate error messages are printed if needed.
83
(defun VALID-OP-REF-REF (EXP)
84
  (cond
85
    ((not (= 3 (length EXP))) nil)
86
    ((not (member (car EXP) '(+ - *))) nil)
87
    ((not (VALID-REFERENCE-P (cadr EXP)))
88
      (format t "Reference out of range: ~s" (cadr EXP))
89
      nil)
90
    ((not (VALID-REFERENCE-P (caddr EXP)))
91
      (format t "Reference out of range: ~s" (caddr EXP))
92
      nil)
93
    (t t)
94
  )
95
)
96
 
97
;
98
; (VALID-REFERENCE-P S) returns T iff S is of the form i8 or o8 and the number
99
;                       specifies a valid $HISTORY entry.
100
;
101
(defun VALID-REFERENCE-P (S)
102
  (and (REFERENCEP S)
103
       (let ((N (parse-integer (subseq (symbol-name S) 1))))
104
         (and (>= N 0) (< N (length $HISTORY)))
105
       )
106
  )
107
)
108
 
109
;
110
; (REFERENCEP S) returns T iff S is of the form i5 or o16 etc.
111
;
112
(defun REFERENCEP (S)
113
  (and (symbolp S)
114
       (or (char= #\I (char (symbol-name S) 0))
115
           (char= #\O (char (symbol-name S) 0)))
116
       (> (length (symbol-name S)) 1)
117
       (DIGIT-STRING-P (subseq (symbol-name S) 1))
118
  )
119
)
120
 
121
;
122
; (VALID-IN-EXP EXP)
123
;
124
; A valid IN-EXP is an append of valid INTERMs.
125
; E.g. (+ 3 - x ^ 2) is an append of (+ 3) and (- x ^ 2).
126
;
127
(defun VALID-IN-EXP (EXP)
128
  (cond
129
    ((atom EXP) (format t "Sorry, but ~s is not legal here.~%" EXP)
130
                nil)
131
    (t (let ((INTERMS (IE2INTERMS EXP)))
132
         (every #'(lambda (INTERM) (VALID-INTERM t INTERM))
133
                INTERMS)
134
       ))
135
  )
136
)
137
 
138
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
139
;;; SEE BELOW FOR IMPLEMENTATION OF WRITE-POLY                               ;;;
140
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141
 
142
;
143
; (IE2INTERMS EXP) returns a list of the INTERMS in EXP.
144
; For example, (3 - x ^ 2 + 7 x) --> ((3) (-  x ^ 2) (+ 7 x)).
145
; Note:  Some may not be legal!
146
;
147
(defun IE2INTERMS (EXP)
148
  (let ((RESULT nil)
149
        (REST EXP))
150
    (loop
151
      (if (null REST) (return (reverse RESULT)))
152
      (setf RESULT (cons (TAKE-INTERM REST) RESULT))
153
      (setf REST (DROP-INTERM REST))
154
    ) ;end-loop
155
  ) ;end-let
156
)
157
 
158
;
159
; (TAKE-INTERM IN-EXP) returns the first INTERM of IN-EXP
160
;
161
(defun TAKE-INTERM (EXP)
162
  (let ((INTERM (list (car EXP)))
163
        (REST (cdr EXP)))
164
    (loop
165
      (if (null REST) (return (reverse INTERM)))
166
      (if (member (car REST) '(+ -)) (return (reverse INTERM)))
167
      (setf INTERM (cons (car REST) INTERM))
168
      (setf REST (cdr REST))
169
    ) ;end loop
170
  ) ;end let
171
)
172
 
173
;
174
; (DROP-INTERM IN-EXP) returns ALL BUT the first INTERM of IN-EXP
175
;
176
(defun DROP-INTERM (EXP)
177
  (let ((INTERM (list (car EXP)))
178
        (REST (cdr EXP)))
179
    (loop
180
      (if (null REST) (return REST))
181
      (if (member (car REST) '(+ -)) (return REST))
182
      (setf INTERM (cons (car REST) INTERM))
183
      (setf REST (cdr REST))
184
    ) ;end loop
185
  ) ;end let
186
)
187
 
188
;
189
; (VALID-INTERM PFLAG EXP)
190
;
191
; Convert things like (- 2 x ^ 7) into (-2 7) if possible.
192
; If not possible, then return nil.
193
; If PFLAG is T, then error messages are printed.
194
;
195
(defun VALID-INTERM (PFLAG EXP) (VALID-INTERM0 PFLAG EXP EXP))
196
 
197
;
198
; Grab off the sign, if any.
199
;
200
(defun VALID-INTERM0 (PFLAG INTERM L)
201
  (cond
202
    ((atom L) (format PFLAG "Invalid INTERM: ~s" INTERM)
203
              nil)
204
    ((eq '- (car L)) (VALID-INTERM1 PFLAG INTERM -1 (cdr L)))
205
    ((eq '+ (car L)) (VALID-INTERM1 PFLAG INTERM 1 (cdr L)))
206
    (t               (VALID-INTERM1 PFLAG INTERM 1 L))
207
  )
208
)
209
 
210
;
211
; Grab off the coefficient, if any.
212
;
213
(defun VALID-INTERM1 (PFLAG INTERM SIGN L)
214
  (cond
215
    ((null L) (format PFLAG "Invalid INTERM: ~s" INTERM)
216
              nil)
217
    ((eq 'x (car L)) (VALID-INTERM2 PFLAG INTERM SIGN 1 L))
218
    ((numberp (car L)) (VALID-INTERM2 PFLAG INTERM SIGN (car L) (cdr L)))
219
    (t (format PFLAG "Invalid INTERM -- numeric coefficient expected: ")
220
       (format PFLAG "~s" INTERM)
221
       nil)
222
  )
223
)
224
 
225
;
226
; Handle constant terms here.
227
;
228
(defun VALID-INTERM2 (PFLAG INTERM SIGN COEF L)
229
  (cond
230
    ((null L) (list (* SIGN COEF) 0))
231
    ((eq 'x (car L)) (VALID-INTERM3 PFLAG INTERM SIGN COEF (cdr L)))
232
    (t (format PFLAG "Invalid INTERM -- X was expected: ")
233
       (format PFLAG "~s" INTERM)
234
       nil)
235
  )
236
)
237
 
238
;
239
; No exponent exits here:
240
;
241
(defun VALID-INTERM3 (PFLAG INTERM SIGN COEF L)
242
  (cond
243
    ((null L) (list (* SIGN COEF) 1))
244
    ((eq '^ (car L)) (VALID-INTERM4 PFLAG INTERM SIGN COEF (cdr L)))
245
    (t (format PFLAG "Invalid INTERM -- ^ was expected: ")
246
       (format PFLAG "~s" INTERM))
247
  )
248
)
249
 
250
;
251
; Grab the exponent.
252
;
253
(defun VALID-INTERM4 (PFLAG INTERM SIGN COEF L)
254
  (cond
255
    ((null L) (format PFLAG "Invalid INTERM -- exponent missing: ")
256
              (format PFLAG "~s" INTERM))
257
    ((cdr L) (format PFLAG "Invalid INTERM -- extra stuff at end: ")
258
             (format PFLAG "~s" INTERM))
259
    ((not (numberp (car L))) 
260
      (format PFLAG "Invalid INTERM -- exponent must be numeric: ")
261
      (format PFLAG "~s" INTERM))
262
    (t (list (* SIGN COEF) (car L)))
263
  )
264
)
265
 
266
;
267
; (EXECUTE-EXPRESSION EXP) processes things like (* I5 i6).
268
;
269
(defun EXECUTE-EXPRESSION (EXP)
270
  (let ((OP (car EXP))
271
        (ARG1 (GET-REFERENCE (cadr EXP)))
272
        (ARG2 (GET-REFERENCE (caddr EXP))))
273
    (cond
274
      ((eq OP '*) (POLY* ARG1 ARG2))
275
      ((eq OP '+) (POLY+ ARG1 ARG2))
276
      ((eq OP '-) (POLY- ARG1 ARG2))
277
      (t (format t "EXECUTE-EXPRESSION: Illegal operator: ~s" OP))
278
    ) ;end-cond
279
  ) ;end-let
280
)
281
 
282
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
108 ira 283
;;; The below 4 functions (POLY*, POLY+, and POLY-) were implemented         ;;;
71 irasnyd 284
;;; on 02-20-2005 by Ira Snyder for HW11                                     ;;;
285
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
286
 
287
;;; This function takes two polynomials represented in PNF,
288
;;; and returns the result of multiplying them together.
289
;;; The two do loops take each sublist of P1 and P2, and call
290
;;; POLY*1 on the two lists.
291
(defun POLY* (P1 P2)
292
  (do ((P1 P1 (cdr P1)) 
293
       (RESULT nil))
294
      ((null P1) RESULT)
295
    (do ((P2 P2 (cdr P2)))
296
        ((null P2) RESULT)
297
      (setf RESULT (cons (POLY*1 (car P2) (car P1)) RESULT))
298
    )
299
  )
300
)
301
 
302
;;; This function takes two pairs of numbers, each representing
303
;;; a coefficient and power of a variable. It returns the multiplication
304
;;; of these two pairs, as a new pair.
305
(defun POLY*1 (PAIR1 PAIR2)
306
  (list (* (car PAIR1) (car PAIR2)) (+ (cadr PAIR1) (cadr PAIR2)))
307
)
308
 
309
;;; This function takes two lists, each in PNF, and returns a new
310
;;; list which is the addition of P1 and P2.
311
(defun POLY+ (P1 P2)
312
  (do ((P1 P1 (cdr P1))
313
       (RESULT P2))
314
      ((null P1) RESULT)
315
    (setf RESULT (INSERT (car P1) RESULT))
316
  )
317
)
318
 
319
;;; This function takes two lists, each in PNF, and returns a new
320
;;; list which is the subtraction of P2 from P1.
321
(defun POLY- (P1 P2)
322
  (do ((P2 P2 (cdr P2))
323
       (RESULT P1))
324
      ((null P2) RESULT)
325
    (setf RESULT (INSERT (list (* -1 (caar P2)) (cadar P2)) RESULT))
326
  )
327
)
328
 
329
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
330
;;; End of things implemented by Ira Snyder for HW11                         ;;;
331
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
332
 
333
;
334
; (GET-REFERENCE REF) looks up things like I8 in $HISTORY.
335
;                     Assume that REF is legal.
336
;
337
(defun GET-REFERENCE (REF)
338
  (let ((N (parse-integer (subseq (symbol-name REF) 1))))
339
    (nth N $HISTORY)
340
  )
341
)
342
 
343
;
344
; (DIGIT-STRING-P string) returns T iff all chars in STRING are digits.
345
;
346
(defun DIGIT-STRING-P (STR)
347
  (let ((I 0))
348
    (loop 
349
      (if (>= I (length STR)) (return t))
350
      (if (not (digit-char-p (char STR I))) (return nil))
351
      (setf I (1+ I))
352
    ) ;end loop
353
  ) ;end let
354
)
355
 
356
(defun SHOW-HISTORY nil
357
  (let ((I 0))
358
    (mapc #'(lambda (ITEM) (format t ">(i~s) ~s~%" I ITEM)
359
                           (setf I (1+ I)))
360
          $HISTORY)
361
  )
362
) 
363
 
364
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
365
;;; Below is code added by Ira Snyder on 02-20-2005                          ;;;
366
;;; This code implements WRITE-POLY, PNF and all supporting                  ;;;
367
;;; functionsfrom HW08                                                       ;;;
368
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
369
 
370
;;; Given a list in unsorted, unreduced Polynomial
371
;;; Normal Form, and returns a list in perfect PNF
372
(defun PNF (L)
373
  (do ((L L (cdr L))
374
       (RET nil))
375
      ((null L) RET)
376
    (setf RET (INSERT (car L) RET))
377
    (setf RET (DEL-ZERO-COEF RET))
378
  )
379
)
380
 
381
;;; Given a pair of numbers (in a list) and a list of pairs
382
;;; this inserts the pair into the list in it's correct place
383
;;; including combining coefficients
384
(defun INSERT (PAIR LIST)
385
  (let ((FPTR (caar LIST))) ;this is needed so we don't change 
386
                            ;the list outside this function
387
    (cond
388
      ((null LIST)                  (cons PAIR LIST))
389
      ((> (cadr PAIR) (cadar LIST)) (cons PAIR LIST))
390
      ((= (cadr PAIR) (cadar LIST)) (progn
391
                                      (setf FPTR (+ (car PAIR) FPTR))
392
                                      (cons (list FPTR (cadar LIST))
393
                                            (cdr LIST))))
394
      (t                            (cons (car LIST) (INSERT PAIR (cdr LIST))))
395
    )
396
  )
397
)
398
 
399
;;; Deletes all of the pairs that have zero coefficients in
400
;;; a PNF list
401
(defun DEL-ZERO-COEF (L)
402
  (cond
403
    ((null L) nil)
404
    ((= 0 (caar L)) (DEL-ZERO-COEF (cdr L)))
405
    (t              (cons (car L) (DEL-ZERO-COEF (cdr L))))
406
  )
407
)
408
 
409
;;; This takes a non-negative integer, and returns a string
410
;;; containing the number of spaces specified by the integer
411
(defun SPACES (N)
412
  (do ((N N (1- N))
413
       (STR (format nil "")))
414
      ((zerop N) STR)
415
    (setf STR (concatenate 'string STR " "))
416
  )
417
)
418
 
419
;;; This checks if nil was passed. If it was, print a blank line, then
420
;;; a "0" line. Else pass the list off to the WRITE-POLY1 function.
421
(defun WRITE-POLY (L)
422
  (cond
423
    ((null L) (format t "~%0~%"))
424
    (t        (WRITE-POLY1 L))
425
  )
426
)
427
 
428
;;; Given a list in Polynomial Normal Form, this will print
429
;;; the human readable form of the list on two lines.
430
;;; Example:
431
;;;   INPUT:  ((1 1) (2 3) (-10 0) (3 2) (2 1))
432
;;;   OUTPUT:      3      2      
433
;;;   OUTPUT: + 2 x  + 3 x  + 3 x - 10
434
(defun WRITE-POLY1 (L)
435
  (do ((L (PNF L) (cdr L))
436
       (EXP  (format nil ""))
437
       (COEF (format nil "")))
438
      ((null L) (format t "~A~%~A~%" EXP COEF))
439
    (setf COEF (concatenate 'string COEF (WRITE-NUM (caar L) (cadar L))))
440
    (MAKE-EQUAL EXP COEF)
441
    (setf EXP  (concatenate 'string EXP  (WRITE-EXP (caar L) (cadar L))))
442
    (MAKE-EQUAL EXP COEF)
443
  )
444
)
445
 
446
;;; Given a coefficient and an exponent, output the correct expression
447
;;; to represent it.
448
;;; Examples:
449
;;; INPUT:  3 4
450
;;; OUTPUT: + 3 x
451
;;;
452
;;; INPUT:  3 0
453
;;; OUTPUT: + 3
454
;;;
455
;;; INPUT:  -3 0
456
;;; OUTPUT: - 3
457
(defun WRITE-NUM (NUM EXP)
458
  (cond
459
    ; don't output an x if we have a zero exponent
460
    ((zerop EXP) (if (plusp NUM) (format nil " + ~A" NUM) 
461
                                 (format nil " - ~A" (abs NUM))))
462
    ((plusp NUM) (format nil " + ~A x" NUM))
463
    (t           (format nil " - ~A x" (abs NUM)))
464
  )
465
)
466
 
467
;;; Given a coefficient and an exponent, output the exponent.
468
;;; NOTE: I chose to have the syntax of WRITE-EXP match the syntax of WRITE-NUM
469
;;;       even though I do not use the parameter NUM in the code. This was for
470
;;;       uniformity reasons.
471
(defun WRITE-EXP (NUM EXP)
472
  (cond
473
    ((zerop EXP) nil)
474
    ((= EXP 1)   nil)
475
    (t           (format nil "~A" EXP))
476
  )
477
)
478
 
479
;;; When called with two strings as arguments, this macro expands to
480
;;; make them the same length, regardless of which is longer.
481
(defmacro MAKE-EQUAL (S1 S2)
482
  `(cond
483
     ((> (length ,S1) (length ,S2)) (let ((DIFF (- (length ,S1) (length ,S2))))
484
                                    (setf ,S2 (concatenate 'string ,S2 (SPACES DIFF)))))
485
     ((< (length ,S1) (length ,S2)) (let ((DIFF (- (length ,S2) (length ,S1))))
486
                                    (setf ,S1 (concatenate 'string ,S1 (SPACES DIFF)))))
487
     (t                             nil)
488
   )
489
)
490