1;;; -*-  Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3;;;     The data in this file contains enhancments.                    ;;;;;
4;;;                                                                    ;;;;;
5;;;  Copyright (c) 1984,1987 by William Schelter,University of Texas   ;;;;;
6;;;     All rights reserved                                            ;;;;;
7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8;;;     (c) Copyright 1981 Massachusetts Institute of Technology         ;;;
9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10
11(in-package :maxima)
12
13(macsyma-module nforma)
14
15(declare-top (special 1//2 -1//2 in-p))
16
17(defmvar $powerdisp nil)
18(defmvar $pfeformat nil)
19(defmvar $%edispflag nil)
20(defmvar $exptdispflag t)
21(defmvar $sqrtdispflag t)
22(defmvar $negsumdispflag t)
23
24(setq in-p nil)
25
26(defun nformat (form &aux (p nil))
27  (cond ((atom form)
28	 (cond ((and (realp form) (minusp form) (not (float-inf-p form))) (list '(mminus) (- form)))
29	       ((eq t form) (if in-p t '$true))
30	       ((eq nil form) (if in-p nil '$false))
31	       ;; revision, extension by Richard Fateman 3/2013.
32	       ;;  Perhaps some object is an atom, maybe a CLOS object or structure.
33	       ;; Either its type is a symbolp..
34	       ;; e.g. a structure like (defstruct (ri ...)) is type ri.
35	       ;; so we look for a formatter on the type or car of the type.
36	       ;; OR
37	       ;; if car of the type is also not a symbol, we look for formatter on nil
38	       ;; where it isn't.
39	       ;; depending on the lisp, type-of may be more or less sophisticated.
40	       ;; a "good" lisp
41	       ;;  may return a list, e.g. (type-of "abc") is (simple-array character (3))
42	       ;; in some lisps, e.g. GCL the type is just  string.
43
44	       ((and (setf p(type-of form))
45		     (if (not (symbolp p)) (setf p (car p)) p)
46		     (setf p (get (and (symbolp p) p) 'formatter))
47		     ;; form is an atom of a type with a formatter property
48		     (funcall p form)))
49	       ;; just display as a lisp symbol, number, or other atom.
50	       (t form)))
51	((atom (car form))  form) ;; probably an illegal form; just return it.
52
53    ;; Process FORM if it is simplified or has some other operator flag (e.g., TRUNC)
54    ;; (ignoring line number annotations). Otherwise, return FORM unchanged.
55    ((let ((foo (cdar form)))
56       (or (null foo) ;; no CAR flags at all
57           ;; only item in CAR is line number annotation
58           ;; (is it worth the trouble to find a faster way to test that?)
59           (and (eql (length foo) 1) (let ((bar (first foo))) (and (consp bar) (equal (last bar) '(src)))))))
60     form)
61
62	;; this next section is for the ordinary maxima objects that are tagged by
63	;; their main operator or CAAR,  e.g. ((mplus) a b) has CAAR mplus ...
64	((and (symbolp (caar form)) (setf p (get (caar form) 'formatter))) ;; find the formatter.  If there is one, call it.
65	 (funcall p form))
66	(t form)))			; if there is no formatter. Just return form unchanged.
67
68
69(defun form-mplus (form &aux args trunc simplified)
70  (setq args (mapcar #'nformat (cdr form)))
71  (setq trunc (member 'trunc (cdar form) :test #'eq))
72  (setq simplified (member 'simp (cdar form) :test #'eq))
73  (cons (if trunc '(mplus trunc) '(mplus))
74	(cond ((and (member 'ratsimp (cdar form) :test #'eq)
75		    (not (member 'simp (cdar form) :test #'eq)))
76	       (if $powerdisp (nreverse args) args))
77	      ((and trunc (not (member 'simp (cdar form) :test #'eq))) (nreverse args))
78	      ((or $powerdisp trunc (member 'cf (cdar form) :test #'eq)) args)
79	      ((and $negsumdispflag (null (cdddr form)))
80	       (if (and (not (mmminusp (car args)))
81			(mmminusp (cadr args)))
82		   args
83		   (if simplified (nreverse args) args)))
84	      (t (if simplified (nreverse args) args)))))
85
86(defun form-mtimes (form)
87  (cond ((null (cdr form)) '((mtimes)))
88	((equal -1 (cadr form)) (list '(mminus) (form-mtimes (cdr form))))
89        (t (prog (num den minus flag)
90	      (do ((l (cdr form) (cdr l)) (dummy)) ((null l))
91		(setq dummy (nformat (car l)))
92		(cond ((atom dummy) (setq num (cons dummy num)))
93		      ((eq 'mminus (caar dummy))
94		       (setq minus (not minus) l (append dummy (cdr l))))
95		      ((or (eq 'mquotient (caar dummy))
96			   (and (not $pfeformat) (eq 'rat (caar dummy))))
97		       (cond ((not (equal 1 (cadr dummy)))
98			      (setq num (cons (cadr dummy) num))))
99		       (setq den (cons (caddr dummy) den)))
100		      (t (setq num (cons dummy num)))))
101	      (setq num (cond ((null num) 1)
102			      ((null (cdr num)) (car num))
103			      (t (cons '(mtimes) (nreverse num))))
104		    den (cond ((null den) (setq flag t) nil)
105			      ((null (cdr den)) (car den))
106			      (t (cons '(mtimes) (nreverse den)))))
107	      (if (not flag) (setq num (list '(mquotient) num den)))
108	      (return (if minus (list '(mminus) num) num))))))
109
110(defun form-mexpt (form &aux exp)
111  (cond ((and $sqrtdispflag (alike1 1//2 (caddr form))) (list '(%sqrt) (cadr form)))
112	((and $sqrtdispflag (alike1 -1//2 (caddr form)))
113	 (list '(mquotient) 1 (list '(%sqrt) (cadr form))))
114	((and (or (and $%edispflag (eq '$%e (cadr form)))
115		  (and $exptdispflag (not (eq '$%e (cadr form)))))
116	      (not (atom (setq exp (nformat (caddr form)))))
117	      (eq 'mminus (caar exp)))
118	 (list '(mquotient) 1 (if (equal 1 (cadr exp)) (cadr form)
119				  (list '(mexpt) (cadr form) (cadr exp)))))
120	(t (cons '(mexpt) (cdr form)))))
121
122(defun form-mrat (form)
123  (let ((trunc (member 'trunc (cdar form) :test #'eq)) exact)
124    (if (and trunc (eq (cadr form) 'ps))
125	(setq exact (null (car (cadddr form)))))
126    (setq form (ratdisrepd form))
127    (rdis1 form)
128    (if (and trunc (or (atom form)
129		       ;; A constant, e.g. ((mplus) $a 1)
130		       (not (member (car form) '((mplus exact) (mplus trunc)) :test #'equal))))
131	(cons (if exact '(mplus exact) '(mplus trunc)) (ncons form))
132	(nformat form))))
133
134(defun rdis1 (form)
135  (cond ((or (atom form) (specrepp form)))
136	((null (cdar form)) (rplaca form (list (caar form) 'ratsimp)))
137	(t (mapc #'rdis1 (cdr form)))))
138
139;;(DEFMFUN NFORMAT-ALL (FORM)
140;;  (SETQ FORM (NFORMAT FORM))
141;;  (IF (OR (ATOM FORM) (EQ (CAAR FORM) 'BIGFLOAT))
142;;      FORM
143;;      (CONS (DELSIMP (CAR FORM)) (MAPCAR #'NFORMAT-ALL (CDR FORM)))))
144;;Update from F302
145;; used only in comm.lisp substitute, mpart.
146(defun nformat-all (form)
147  (setq form (nformat form))
148  (if (or (atom form) (eq (caar form) 'bigfloat))
149      form
150      (cons (delsimp (car form))
151	    (if (member (caar form) '(mdo mdoin) :test #'eq)
152		(mapcar #'(lambda (u) (if u (nformat-all u))) (cdr form))
153		(mapcar #'nformat-all (cdr form))))))
154
155
156;;; we should define all the formatters in the file after the helper functions like  form-mplus
157
158(setf (get 'rat 'formatter)
159  #'(lambda(form)(cond ((minusp (cadr form))
160			(list '(mminus) (list '(rat) (- (cadr form)) (caddr form))))
161		       (t (cons '(rat) (cdr form))))))
162
163(setf (get 'mmacroexpanded 'formatter)
164  #'(lambda(form)(nformat (caddr form))))
165
166(setf (get 'mplus 'formatter)  #'form-mplus)
167(setf (get 'mtimes 'formatter)  #'form-mtimes)
168(setf (get 'mexpt 'formatter)  #'form-mexpt)
169(setf (get 'mrat 'formatter)  #'form-mrat)
170(setf (get 'mpois 'formatter)  #'(lambda(form)(nformat ($outofpois form))))
171
172(setf (get 'bigfloat 'formatter)
173  #'(lambda(form)
174	 (if (minusp (cadr form))
175	     (list '(mminus) (list (car form) (- (cadr form)) (caddr form)))
176	   (cons (car form) (cdr form)))))
177
178(setf (get 'ratio 'formatter)  ;; in case a common lisp ratio is returned somehow.
179  #'(lambda (form)
180      (cond ((minusp form)
181		(list '(mminus) (list '(rat) (- (numerator form)) (denominator form))))
182	    (t (list '(rat) (numerator form)(denominator form))))))
183
184(setf (get 'complex 'formatter)  ;; in case a common lisp complex number is returned somehow.
185  #'(lambda(form)
186          (if (complexp form)
187            (nformat `((mplus) ,(realpart form)
188                     ((mtimes) ,(imagpart form) $%i)))
189            ;; some random form with caar COMPLEX
190            ;;not really a CL complex
191            form)))
192
193;; something I added for fun
194;; (defstruct (ri (:constructor $interval (lo hi) ))lo hi)
195;; (setf (get 'ri 'formatter) ;; in case a structure of type ri  [real interval] is computed
196;;   #'(lambda(r) (list '($interval simp) (ri-lo r)(ri-hi r)))) ;; this prints it.
197
198;;  so in maxima, we can construct ri structures by typing interval(1,2)
199;; and if we display it,  it  appear as  interval(1,2).
200;; but ?print(interval(1,2))  shows the lisp value which is the structure,
201;; #s(ri :lo 1 :hi 2).
202
203;; we could set up formatters for , say,  (simple-array single-float <dimensions>)
204;; or share the burden with display program .
205
206