;;; -*-  Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;     The data in this file contains enhancments.                    ;;;;;
;;;                                                                    ;;;;;
;;;  Copyright (c) 1984,1987 by William Schelter,University of Texas   ;;;;;
;;;     All rights reserved                                            ;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;     (c) Copyright 1980 Massachusetts Institute of Technology         ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(in-package "MAXIMA")
(macsyma-module fortra)

(DECLARE-TOP (SPECIAL LB RB	        ;Used for communication with MSTRING.
		  $LOADPRINT	;If NIL, no load message gets printed.
		  1//2 -1//2)
	 (*LEXPR FORTRAN-PRINT $FORTMX))

(DEFMVAR $FORTSPACES NIL
   "If T, Fortran card images are filled out to 80 columns using spaces."
   BOOLEAN
   MODIFIED-COMMANDS '$FORTRAN)

(DEFMVAR $FORTINDENT 0
   "The number of spaces (beyond 6) to indent Fortran statements as they
   are printed."
   FIXNUM
   MODIFIED-COMMANDS '$FORTRAN)

(DEFMVAR $FORTFLOAT NIL "Something JPG is working on.")

;; This function is called from Macsyma toplevel.  If the argument is a
;; symbol, and the symbol is bound to a matrix, then the matrix is printed
;; using an array assignment notation.

(setq oldfortranstream *standard-output*)
(setq fortranstream *standard-output*)

(defun clean-atom (expr)
   (cond
    ((numberp expr) expr)
    (t (let* ((xx (exploden expr)) (xxx (car xx)))
         (cond
          ((or (char= #\$ xxx) (char= #\% xxx)) (implode (cdr xx)))
          ((char= #\& xxx) (concatenate 'string (cdr xx)))
          (t expr))))
   )
)

(defun $fortranfile (filename mode)
  (if mode
      (setq oldfortranstream fortranstream
	    fortranstream (open (clean-atom filename) :direction :output))
      (let () (close fortranstream) (setq fortranstream oldfortranstream))))

(DEFMSPEC $FORTRAN (L)
 (SETQ L (FEXPRCHECK L))
 (LET ((VALUE (STRMEVAL L)))
      (COND ((MSETQP L) (SETQ VALUE `((MEQUAL) ,(CADR L) ,(MEVAL L)))))
      (COND ((AND (SYMBOLP L) ($MATRIXP VALUE))
	     ($FORTMX L VALUE fortranstream))
	    ((AND (NOT (ATOM VALUE)) (EQ (CAAR VALUE) 'MEQUAL)
		  (SYMBOLP (CADR VALUE)) ($MATRIXP (CADDR VALUE)))
	     ($FORTMX (CADR VALUE) (CADDR VALUE) fortranstream))
	    (T (FORTRAN-PRINT VALUE fortranstream)))))

;; This function is called from Lisp programs.  It takes an expression and
;; a stream argument.  Default stream is NIL in MacLisp and *STANDARD-OUTPUT*
;; in LMLisp.  This should be canonicalized in Macsyma at some point.

;; TERPRI is a PDP10 MacLisp flag which, if set to T, will keep symbols and
;; bignums from being broken across page boundaries when printed.  $LOADPRINT
;; is NIL to keep a message from being printed when the file containing MSTRING
;; is loaded.  (MRG;GRIND)

(DEFPROP MEXPT (#\* #\*) DISSYM)

(DEFUN FORTRAN-PRINT (X &OPTIONAL (STREAM #+Maclisp NIL #-Maclisp *standard-output*)
			&AUX #+PDP10 (TERPRI T) #+PDP10 ($LOADPRINT NIL)
		        ;; This is a poor way of saying that array references
  		        ;; are to be printed with parens instead of brackets.
			(LB #. left-parentheses-char ) (RB #. right-parentheses-char ))
  ;; Restructure the expression for displaying.
  (SETQ X (FORTSCAN X))
  ;; Linearize the expression using MSTRING.  Some global state must be
  ;; modified for MSTRING to generate using Fortran syntax.  This must be
  ;; undone so as not to modifiy the toplevel behavior of MSTRING.
  (UNWIND-PROTECT
  
    (DEFPROP MEXPT MSIZE-INFIX GRIND)
    (DEFPROP MMINUS 100. LBP)
     
    (DEFPROP MSETQ (#\:) STRSYM)  
    (SETQ X (mstring x))
   ;; Make sure this gets done before exiting this frame.
   (DEFPROP MEXPT MSZ-MEXPT GRIND)
   (REMPROP 'MMINUS 'LBP)
   )
  
  ;; MSTRING returns a list of characters.   Now print them.
  (DO ((C #.(char-int #\0)
	  (f+ 1 (remainder (f- c #. (char-int #\0))
		16) #. (char-int #\0)))
       (COLUMN (f+ 6 $FORTINDENT) (f+ 9 $FORTINDENT)))
      ((NULL X))
      ;; Print five spaces, a continuation character if needed, and then
      ;; more spaces.  COLUMN points to the last column printed in.  When
      ;; it equals 80, we should quit.
      (COND ((= C #. (char-int #\0))
	     (PRINT-SPACES COLUMN STREAM))
	    (T (PRINT-SPACES 5 STREAM)
	       (TYO (code-char C) STREAM)
	       (PRINT-SPACES (f- COLUMN 6) STREAM)))
      ;; Print the expression.  Remember, Fortran ignores blanks and line
      ;; terminators, so we don't care where the expression is broken.
      (DO ()
	  ((= COLUMN 72.))
	  (IF (NULL X)
	      (IF $FORTSPACES (TYO #\SPACE STREAM) (RETURN NIL))
	      (progn (and (equal (car x) #. back-slash-char) (setq x (cdr x)))
		     (TYO (POP X) STREAM)))
	  (INCREMENT COLUMN))
      ;; Columns 73 to 80 contain spaces
      (IF $FORTSPACES (PRINT-SPACES 8 STREAM))
      (TERPRI STREAM))
  '$DONE)

(DEFUN PRINT-SPACES (N STREAM)
       (DOTIMES (I N) (TYO #\SPACE STREAM)))

;; This function is similar to NFORMAT.  Prepare an expression
;; for printing by converting x^(1/2) to sqrt(x), etc.  A better
;; way of doing this would be to have a programmable printer and
;; not cons any new expressions at all.  Some of this formatting, such
;; as E^X --> EXP(X) is specific to Fortran, but why isn't the standard
;; function used for the rest?

(DEFUN FORTSCAN (E)
 (COND ((ATOM E) (cond ((eq e '$%i) '((mprogn) 0.0 1.0))
		       (t E))) ;%I is (0,1)
       ((AND (EQ (CAAR E) 'MEXPT) (EQ (CADR E) '$%E))
	(LIST '($EXP SIMP) (FORTSCAN (CADDR E))))
       ((AND (EQ (CAAR E) 'MEXPT) (ALIKE1 (CADDR E) 1//2))
	(LIST '(%SQRT SIMP) (FORTSCAN (CADR E))))
       ((AND (EQ (CAAR E) 'MEXPT) (ALIKE1 (CADDR E) -1//2))
	(LIST '(MQUOTIENT SIMP) 1 (LIST '(%SQRT SIMP) (FORTSCAN (CADR E)))))
       ((AND (EQ (CAAR E) 'MTIMES) (RATNUMP (CADR E))
	     (zl-MEMBER (CADADR E) '(1 -1)))
	(COND ((EQUAL (CADADR E) 1) (FORTSCAN-MTIMES E))
	      (T (LIST '(MMINUS SIMP) (FORTSCAN-MTIMES E)))))
       ((EQ (CAAR E) 'RAT)
	(LIST '(MQUOTIENT SIMP) (FLOAT (CADR E)) (FLOAT (CADDR E))))
       ((EQ (CAAR E) 'MRAT) (FORTSCAN (RATDISREP E)))
       ;;  complex numbers to f77 syntax a+b%i ==> (a,b)
       ((and (memq (caar e) '(mtimes mplus))
	     ((lambda (a) 
		      (and (numberp (cadr a))
			   (numberp (caddr a))
			   (not (zerop1 (cadr a)))
			   (list '(mprogn) (caddr a) (cadr a))))
	      (simplify ($bothcoef e '$%i)))))
       (T (CONS (CAR E) (MAPCAR 'FORTSCAN (CDR E))))))

(DEFUN FORTSCAN-MTIMES (E)
       (LIST '(MQUOTIENT SIMP)
	     (COND ((NULL (CDDDR E)) (FORTSCAN (CADDR E)))
		   (T (CONS (CAR E) (MAPCAR 'FORTSCAN (CDDR E)))))
	     (FLOAT (CADDR (CADR E)))))

;; Takes a name and a matrix and prints a sequence of Fortran assignment
;; statements of the form
;;  NAME(I,J) = <corresponding matrix element>

(DEFMFUN $FORTMX (NAME MAT &OPTIONAL (STREAM #-CL NIL #+CL *standard-output*)
			 &AUX ($LOADPRINT NIL))
  (COND ((NOT (symbolp NAME))
	 (MERROR "~%First argument to FORTMX must be a symbol."))
	((NOT ($MATRIXP MAT))
	 (MERROR "Second argument to FORTMX not a matrix: ~M" MAT)))
  (DO ((MAT (CDR MAT) (CDR MAT)) (I 1 (f1+ I))) ((NULL MAT))
      (DECLARE (FIXNUM I))
      (DO ((M (CDAR MAT) (CDR M)) (J 1 (f1+ J))) ((NULL M))
	  (DECLARE (FIXNUM J))
	  (FORTRAN-PRINT `((MEQUAL) ((,NAME) ,I ,J) ,(CAR M)) STREAM)))
  '$DONE)


;; Local Modes:
;; Comment Column:26
;; End:
