;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: INTERACTORS; Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;         The Garnet User Interface Development Environment.      ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code was written as part of the Garnet project at          ;;;
;;; Carnegie Mellon University, and has been placed in the public   ;;;
;;; domain.  If you are using this code or any part of Garnet,      ;;;
;;; please contact garnet@cs.cmu.edu to be put on the mailing list. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;; fileio.lisp
;;;
;;; Routines for loading and saving gesture classifiers.   
;;;
;;; Designed and implemented by James A. Landay

#|
============================================================
Change log:
    06/10/93 James Landay - read/write also read & write class examples
     4/07/92 Pedro Szekely - Let *print-array* be T in gest-classifier-write
		so that arrays will not be printed as #<...> in Lucid.
    12/31/92 James Landay - changed function names
    12/21/92 James Landay - started
============================================================
|#

(in-package "INTERACTORS" :use '("LISP") :nicknames '("INTER"))


;; gest-classifier-read returns the classifier and class examples 
;; (if present -- in items lists format) read from the given file.
;;
;; Parameters:
;;    filename   - file name to read from 
;;
(defun gest-classifier-read (filename)
  (let ((*print-level* nil)
	(*print-length* nil))
    (with-open-file (str filename :direction :input)
		    (values (read str)             ;; read classifier
			    (read str NIL NIL))))) ;; read examples, if present


;; gest-classifier-write writes the given classifier and the optional
;; class examples to disk with the given file name.
;;
;; Parameters:
;;    classifier    - classifier to write
;;    filename      - file name to save under
;;    class-exampes - optional set of examples
;;
(defun gest-classifier-write (classifier filename &optional class-examples)
    (let ((*print-level* nil)
          (*print-length* nil)
	  (*print-array* T))
        (with-open-file (str filename :direction :output 
                                      :if-exists :supersede)
                        (write classifier :stream str :pretty nil)
			(if class-examples
			    (write class-examples :stream str :pretty nil)))))


;; gest-classifier-convert returns the classifier read from the
;; given file that is in Dean Rubine's format and converts it
;; to our format.
;;
;; Parameters:
;;    filename - file name to read from 
;;
(defun gest-classifier-convert (filename)
    (let ((classifier (gest-new-classifier))
          (classes nil))
        
        (with-open-file (stream filename :direction :input) 

            ; get the number of classes
            (setf (classifier-num-classes classifier) (read stream))
            (read stream)     ; throw away string 'classes'

            ; read in and add the new class names
            (do ((cur (read stream))
                 (i (classifier-num-classes classifier) (decf i)))
                ((zerop i))

                (setf classes (append classes (list cur)))
                (when (not (= i 1))
                      (setf cur (read stream)))
            )
           
            ;; initialize the classifier fields
            (setf (classifier-cnst classifier) 
                  (make-array `(,(classifier-num-classes classifier))))
            (setf (classifier-weights classifier) 
                  (make-array `(,(classifier-num-classes classifier))))

            ; read average vector and weight vector for each class
            (do ((new-class (make-g-class) (make-g-class))
                 (cur-class (car classes) (car classes))
                 (ignore 0)
                 (i 0 (incf i)))
                ((equalp i (classifier-num-classes classifier)))

                (setf (g-class-name new-class) cur-class)
                (setf (g-class-num-examples new-class) 0)
                
                ; drop the "V" and find out # of rows to skip at end
                (read stream)
                (setf ignore (- (read stream) NUM-FEATURES))

                ; read the average vector 
                (setf (g-class-average new-class) 
                      (read-vector stream NUM-FEATURES))
                (skip-input-items stream ignore)

                ; read the weight vector
                (read stream)               ; skip the "V"
                (read stream)               ;   and the size
                (setf (aref (classifier-weights classifier) i)
                      (read-vector stream NUM-FEATURES))
                (skip-input-items stream ignore)

                (setf (classifier-classes classifier)
                      (append (classifier-classes classifier) 
                              (list new-class)))
                (setf classes (cdr classes))
            )

            ; read in the constant vector 
            (read stream)               ; skip the "V"
            (read stream)               ;   and the size
            (setf (classifier-cnst classifier)
                  (read-vector stream 
                               (classifier-num-classes classifier)))
        
            ; read in the inverse convariance matrix
            (read stream)               ; skip the "M"
            (read stream)               ;   and the first size
            (setf (classifier-invavgcov classifier)     
                  (read-square-matrix stream NUM-FEATURES 
                      (- (read stream) NUM-FEATURES)))
        )
        (print classifier)
        classifier              ;; return the new classifier
    )
)


;; gest-read-example reads the next example in Dean Rubine's format
;; from the given open stream and returns a list with the name
;; of the class and the name of the example at the head followed by 
;; the points.
;;
;; Parameters:
;;    stream - the open stream to read from 
;;
(defun gest-read-example (stream)
    (let ((g-points nil))
        (read stream)                       ; ignore the leading x
        ; get the class name and the example name
        (setf g-points (list (read stream) (read stream)))

        (do ()
            ((or (equal #\x (peek-char nil stream nil 'EOF))
                 (equal 'EOF (peek-char nil stream nil 'EOF))))

            (read stream)           ; ignore the zero

            ; add the next x,y coordinate to the list
            (setf g-points (append g-points (list (read stream))
                                            (list (read stream))))
            (read stream)           ; ignore time 
        )
        g-points                ; return the example
    )
)


;; read-vector reads and returns a vector of the given
;; size from the given open stream. 
;;
;; Parameters:
;;    stream - the open stream to read from
;;    size   - number of elements in the vector to read
(defun read-vector (stream size)
    (let ((vector (make-array `(,size):initial-element 0)))
        (do ((cur (read stream))
             (j 0 (incf j)))
            ((equal j size) vector)         ; return the vector 

            (setf (aref vector j) cur) 
            (when (not (= (1- size) j))
                  (setf cur (read stream)))
        )
    )
)


;; read-square-matrix reads and returns a square matrix of the given
;; size from the given open stream. 
;;
;; Parameters:
;;    stream - the open stream to read from
;;    size   - number of elements in a row or column of the matrix 
;;    skip   - number of elements to skip at the end of each column
(defun read-square-matrix (stream size skip)
    (do ((j 0 (incf j))
         (matrix (make-array `(,size):initial-element 0)))
        ((equal j size) matrix)         ; return the matrix

        (setf (aref matrix j) (read-vector stream size))
        (when (not (= (1- size) j))
              (skip-input-items stream skip))
    )
)


;; skip-input-items will do enough reads of the input
;; to skip the give number of items. NOTE: the file must
;; already be open into the given stream.
;;
;; Parameters:
;;    stream    - the stream we are reading from
;;    num-items - number of items to skip
;;
(defun skip-input-items (stream num-items)
    (do ((i num-items (decf i)))
        ((< i 1))
        (read stream)
    )
)
