;;; -*- Mode:LISP; Package:USER; Readtable:CL; Base:10 -*-


(defun make-garbage (mega-words &optional (sleep-amount 0.10))
  "Returns total realtime minus sleeping time"
  (let ((realtime)
	(mega-word/100 (floor (expt 2 20) 100))
	(loops (round (* mega-words 100)))
	(sleep-time 0))
    (format t "~&; Going to make ~D mega-word~p of garbage, in ~D loops, sleeping about ~S seconds."
	    mega-words mega-words loops
	    (* loops sleep-amount))
    (setq realtime (get-internal-real-time))
    (dotimes (j loops)
      ;; make 1/100'th of a megaword of potential garbage, assuming that the overead word for
      ;; an array or negligable.
      (make-array mega-word/100)
      (let ((off (/ (float sleep-amount) 6))
	    (x (get-internal-real-time)))
	(sleep (+ sleep-amount (- off) (random (* 2 off))))
	(incf sleep-time (- (get-internal-real-time) x))))
    (setq realtime (- (get-internal-real-time) realtime))
    (values (/ (float (- realtime sleep-time)) internal-time-units-per-second)
	    (/ (float sleep-time) internal-time-units-per-second))))


#+LISPM
(defun batch-process (expression)
  (let ((l (list :expression expression
		 :values nil
		 :output (make-string 250 :fill-pointer 0))))
    (process-run-function '(:name "batch process" :priority -1)
			  (closure '(*package* *readtable*)
				   #'(lambda (l)
				       (with-output-to-string (standard-output (getf l :output))
					 (format t ";; Batch Evaluating:")
					 (pprint (getf l :expression))
					 (format t "~%;; =>~%")
					 (setf (getf l :values) (multiple-value-list (eval (getf l :expression))))
					 (format t "~&~S~%" (car (getf l :values))))))

			  l)
    l))

#+lispm
(defun make-garbage-batch (total-mega-words &optional (processes 10) (sleep-amount 0.3))
  (do ((j 0 (1+ j))
       (l nil (cons (batch-process `(make-garbage  ,(/ total-mega-words processes) ,sleep-amount))
		    l)))
      ((= j processes) l)))


#+lispm
(defun result-garbage-batch (list)
  "Wait for a batch of garbage making processes to complete and describe the result"
  (let ((looking list))
    (process-wait "crunching"
		  #'(lambda ()
		      (cond ((null looking)
			     t)
			    ((not (getf (car looking) :values))
			     nil)
			    ('else
			     (null (setq looking (cdr looking))))))))
  (result-garbage-batch-report list)
  list)

#+lispm
(defun result-garbage-batch-report (list)
  (format t "~&Summary for ~D processes, each consing ~D megawords, ~D megawords total.~%"
	  (length list)
	  (cadr (getf (car list) :expression))
	  (* (cadr (getf (car list) :expression)) (length list)))
  (result-garbage-report (mapcar #'(lambda (x) (cadr (getf x :values))) list)
			 (mapcar #'(lambda (x) (car (getf x :values))) list)))


(defun result-garbage-report (sleep-times elapsed-times)
  (let ((sleep-stats (statistics sleep-times))
	(elapsed-stats (statistics elapsed-times)))
      (format t "Elapsed (non-sleep) time:~%Average: ~S~% Median: ~S~%Minimum: ~S~%Maximum: ~S~%Sum: ~S~%"
	      (getf elapsed-stats :average)
	      (getf elapsed-stats :median)
	      (getf elapsed-stats :min)
	      (getf elapsed-stats :max)
	      (getf elapsed-stats :sum))
      (format t "Sleep time:~%Average: ~S~% Median: ~S~%Minimum: ~S~%Maximum: ~S~%Sum: ~S~%"
	      (getf sleep-stats :average)
	      (getf sleep-stats :median)
	      (getf sleep-stats :min)
	      (getf sleep-stats :max)
	      (getf sleep-stats :sum))))



(defun statistics (list)
  (let ((sum (apply #'+ list))
	(sorted (sort (copy-list list) #'<))
	(length (length list)))
    (cond ((= length 0)
	   ())
	  ('else
	   (list :sum sum
		 :average (/ sum length)
		 :median (/ (+ (nth (floor length 2) sorted)
			       (nth (floor (1- length) 2) sorted))
			    2)
		 :min (car sorted)
		 :max (car (last sorted)))))))

			     
		      
		    
  

			  
			  
			  
			
			
	
			

