;;; -*-  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 1982 Massachusetts Institute of Technology         ;;;
;;; Original code by CFFK.  Modified to interface correctly with TRANSL  ;;;
;;; and the rest of macsyma by GJC                                       ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(in-package :maxima)

(macsyma-module rombrg)

(load-macsyma-macros transm numerm)

(declare-top(special user-timesofar))

(defmvar  $rombergit 11 "the maximum number of iterations")
(defmvar  $rombergmin 0 "the minimum number of iterations")
(defmvar  $rombergtol 1d-4 "the relative tolerance of error")	
(defmvar  $rombergabs 0d0 "the absolute tolerance of error")
(defmvar  $rombergit_used 0 "the number of iterations actually used.")

(defvar romb-print nil )		; " For ^]"

(defun $romberg_subr (function left right)
  (bind-tramp1$
   f function
   (let ((a (float left))
	 (b (float right))
	 (x 0d0)
	 (tt (make-array  $rombergit :element-type 'double-float))
	 (rr  (make-array  $rombergit :element-type 'double-float))
	 (user-timesofar (cons 'romb-timesofar user-timesofar))
	 (romb-print nil))
     (setq x (- b a))
     (setf (aref tt 0)
	   (* x (+ (funcall f b) (funcall f a)) 0.5d0))
     (setf	(aref rr 0)
		(* x (funcall f (* (+ b a) 0.5d0))))
     (do ((l 1 (1+ l))
	  (m 4 (* m 2))
	  (y 0d0)
	  (z 0d0)
	  (cerr 0d0))
	 ((= l $rombergit)
	  (merror "`romberg' failed to converge"))
       (declare	(fixnum l m))
       (setq y (float m) z (/ x y))
       (setf (aref tt l) (* (+ (aref tt (1- l))
			       (aref rr (1- l))) 0.5))
       (setf (aref rr l) 0.0)
       (do ((i 1 (+ i 2)))
	   ((> i m))
	 (when romb-print
	   (setq romb-print nil)	;^] magic.
	   (mtell "Romberg: ~A iterations; last error =~A;~
			    calculating F(~A)."
		  i cerr (+ (* z (float i)) a)))
	 (setf (aref rr l) (+ (funcall f (+ (* z (float i)) a))
			      (aref rr l))))
       (setf (aref rr l) (* z (aref rr l) 2))
       (setq y 0d0)
       (do ((k l (1- k))) ((= k 0))
	 (declare (fixnum k))
	 (setq y (+ (* y 4) 3))
	 (setf (aref  tt (1- k))
	       (+ (/ (- (aref tt k)
			(aref tt (1- k))) y)
		  (aref tt k)))
	 (setf (aref rr (1- k))
	       (+ (/ (- (aref rr k) (aref rr (1- k))) y)
		  (aref rr k))))
       (setq y (* (+ (aref tt 0) (aref rr 0)) 0.5d0))
;;; this is the WIN condition test.
       (cond ((and (or (not (< $rombergabs
			       (setq cerr (abs (- (aref tt 0) (aref rr 0))))))
		       (not (< $rombergtol
			       ;; cerr = "calculated error"; used for ^]
			       (setq cerr (/ cerr (if (zerop y) 1d0 (abs y)))))))
		   (> l $rombergmin))
	      (setq $rombergit_used l)
	      (return y)))))))

(defun romb-timesofar ()
  (setq romb-print t))			;^] function.
;;; Making the ^] scheme work through this special variable makes
;;; it possible to avoid various timing screws and having to have
;;; special variables for communication between the interrupt and MP
;;; function.  On the other hand, it may make it more difficult to
;;; have multiple reports (double integrals etc.).

;;; TRANSL SUPPORT.

(defprop $romberg_subr $float function-mode)

(defun romberg-macro (form translatep)
  (setq form (cdr form))
  (cond ((= (length form) 3)
	 (cond (translatep
		`(($romberg_subr) ,@form))
	       (t
		`((mprog) ((mlist) ((msetq) $numer t) ((msetq) $%enumer t))
		  (($romberg_subr) ,@form)))))
	((= (length form) 4)
	 (destructuring-let (((exp var . bnds) form))
			    (if translatep
				`(($romberg_subr)
				  ((lambda-i) ((mlist) ,var)
				   (($modedeclare) ,var $float)
				   ,exp)
				  ,@bnds)
				`((mprog) ((mlist) ((msetq) $numer t) ((msetq) $%enumer t))
				  (($romberg_subr)
				   ((lambda) ((mlist) ,var) ,exp)
				   ,@bnds)))))
	(t
	 (wna-err '$romberg))))

(defmspec $romberg (form)
  (meval (romberg-macro form nil)))

(def-translate-property $romberg (form)
  (let (($tr_numer t))
    (translate (romberg-macro form t))))

; Following stuff (from here to end of file) is copied from src/trdata.lisp

(eval-when
    #+gcl (load)
    #-gcl (:load-toplevel)
    (putprop '$romberg_subr '$float 'function-mode))
