(in-package :maxima) ;; uh, for now

(eval-when (:compile-toplevel :load-toplevel)
  (proclaim (optimize (speed 3)(safety 1)(space 0)  )))

;;;; -*- Mode: Lisp; Syntax: Common-Lisp -*-
;;;; Code from Paradigms of AI Programming
;;;; Copyright (c) 1991 Peter Norvig
;;; ==============================

;;;; The Memoization facility:

(defmacro defun-memo (fn args &body body)
  "Define a memoized function."
  `(memoize (defun ,fn ,args . ,body)))

(defun memo (fn &key (key #'first) (test #'eql) name)
  "Return a memo-function of fn."
  (let ((table (make-hash-table :test test)))
    (setf (get name 'memo) table)
    #'(lambda (&rest args)
        (let ((k (funcall key args)))
          (multiple-value-bind (val found-p)
              (gethash k table)
            (if found-p val
                (setf (gethash k table) (apply fn args))))))))

(defun memoize (fn-name &key (key #'first) (test #'eql))
  "Replace fn-name's global definition with a memoized version."
  (clear-memoize fn-name)
  (setf (symbol-function fn-name)
        (memo (symbol-function fn-name)
              :name fn-name :key key :test test))
  (compile fn-name);; added; compile always?
  )

(defun clear-memoize (fn-name)
  "Clear the hash table from a memo function."
  (let ((table (get fn-name 'memo)))
    (when table (clrhash table))))

;; end of norvig code

;; debugging pgm to print out memo table
(defun dmpmemoht (h)(maphash #'(lambda (k v)(format t "~%key= ~s value=~s" k v)) (get h 'memo)))

;;; now to hack Maxima..

;; maybe (memoize 'great :key #'sxhash) ;; sxhash will be called on (a b)  for (great a b)
;; maybe (memoize 'great :key #'identity) ;; maybe slower but at least 100% the same.

;; first, save original simplifya definition unless it has already been saved.

(unless (fboundp 'simplifya_orig)   (setf (symbol-function 'simplifya_orig) (symbol-function 'simplifya)))

;; now memoize the separate kinds of simplify

(defun-memo simplifya_t (a)(simplifya_orig a t))
(defun-memo simplifya_nil (a)(simplifya_orig a nil))
(defun simplifya(a b)(if b (simplifya_t a)(simplifya_nil a)))

;;run_testsuite(tests=[rtest1,rtest2,rtest_rules]);

;; regular time is  4.812 sec run time

;; after loading this, 5.031 sec run time
;; BUT  if the test is run a second time, the
;; test takes 2.82 seconds
;; and uses half the bytes consed.

;; some of the other tests give answers that are simplified differently though.

;; To make this work "the same" we could require that any
;; change to the global environment that affects the
;; result of (simplifya ..) should also clear the memo table
;; for simplifya.  
;; As an obvious example, tellsimp(...) must also do (clear-memoize 'simplifya).
;; That's not all though.
;; Changes to variable settings like expon, expop,radexpand,negdistrib,
;; expandwrt_denom, exponentialize must also call clear-memoize.
;; Also changes or declarations of properties including
;; linear, additive, multiplicative, outative, evenfun, oddfun,
;; commutative, symmetric, antisymmetric, nary, lassociative, rassociative.

;; Maybe other things too.

;;RJF 9/5/2016