; .EnTete "Le-Lisp (c) version 15.2" " " "Les objets circulaires"
; .EnPied " " "%" " "
; .SuperTitre "Les objets circulaires"
;
; .Centre "*****************************************************************"
; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA.  "
; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly "
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA                 "
; .Centre "*****************************************************************"

; .Centre "$Header: libcir.ll,v 4.3 88/11/24 15:00:55 gallou Exp $"

(unless (>= (version) 15.2)
        (error 'load 'erricf 'libcir))

; Tous les symboles pre'ce'de's de : seront dans le package libcir

(defvar #:sys-package:colon 'libcir)

(add-feature 'libcir)

; le drapeau du traitement particulier des packages

(ifn (boundp ':package-parano) (defvar :package-parano ()))

; Ajout de DEFVAR supple'mentaires pour indiquer que l'utilisation de
; variables dynamiques nest pas fortuite.

(defvar :old)				; le nouveau label lu.
(defvar :new)				; l'objet e'tiquete' par ce label.
(defvar :seen)				; pour imprimer ou copier, me'morise
					; les labels de'ja` visite's

; .Section "Les impressions : cirprint, cirprin et cirprinflush"

(de cirprint (:x) (cirprin :x) (print))

(de cirprinflush (:x) (cirprin :x) (prinflush))

; une passe pour repe'rer les boucles, une autre pour imprimer
; avec la syntaxe #<n>= et #<n>#

(de cirprin (:x)
    (let ((:shared))
         (let ((:seen)) (:sweep :x))
         (setq :shared (nreverse :shared))
         (if :shared (let ((:seen)) (:prin :x)) (prin :x))))

(de :sweep (:x)
    (cond
        ((and (consp :x) (:unshared :x)) 
         (:sweep (car :x)) (:sweep (cdr :x)))
        ((and (stringp :x) (:unshared :x))
         (:sweep (typestring :x)))
        ((and (vectorp :x) (:unshared :x))
         (mapvector ':sweep :x)
         (if (neq (typevector :x) 'vector) (:sweep (typevector :x))))
        ((and :package-parano (symbolp :x) (packagecell :x) (:unshared :x))
         (:sweep (packagecell :x)))))

(de :unshared (:x)
    (let ((:is-seen (memq :x :seen)))
        (ifn :is-seen (newl :seen :x))
        (if (and :is-seen (not (memq :x :shared))) (newl :shared :x))
        (not :is-seen)))

(de :prin (:x)
    (let ((:is-shared (memq :x :shared)))
        (if :is-shared
            (:prin-sharp :x (length :is-shared) (memq :x :seen))
            (:prin1 :x))))

(de :prin-sharp (:x :n :not-first)
    (if :not-first
        (:prin-one (cons '#/# (nconc1 (pname :n) '#/#)))
        (:prin-one (cons '#/# (nconc1 (pname :n) '#/=)))
        (newl :seen :x)
        (:prin1 :x)))

(de :prin1 (:x)
    (cond
        ((tconsp :x)
         (:prin-one '(#/# #/())
         (:prin (car :x))
         (:prin-cdr (cdr :x)))
        ((consp :x)
         (princn #/() (:prin (car :x)) (:prin-cdr (cdr :x)))
        ((vectorp :x)
         (when (neq (typevector :x) 'vector)
            (:prin-one '(#/# #/:))
            (:prin (typevector :x))
            (:prin-one '(#/:)))
         (:prin-one '(#/# #/[))
         (mapvector (lambda (:x) (:prin :x) (princn #\sp))  ; pas parfait
                     :x)
         (princn #/]))
        ((and (stringp :x) (neq (typestring :x) 'string))
         (:prin-one '(#/# #/:))
         (:prin (typestring :x))
         (:prin-one '(#/:))
         (prin (catenate :x "")))
        ((and :package-parano (symbolp :x) (packagecell :x))
         (:prin-one '(#/# #/:))
         (:prin (packagecell :x))
         (princn #/:)
         (prin (symbol () :x)))
        (t (prin :x))))

(de :prin-cdr (:x)
    (cond
        ((null :x) (princn #/)))        ; (
        ((or (atomp :x) (tconsp :x) (memq :x :shared))
         (:prin-one '(#\sp #/. #\sp))
         (:prin :x)
         (princn #/)))                        ; (
        (t (princn #\sp) (:prin (car :x)) (:prin-cdr (cdr :x)))))

(de :prin-one (:l)
    ;; print a list of character codes.
    ;; Ensure that all the charcodes are printed on the same line.
    (when (ge (length :l) (sub (rmargin) (outpos)))
	  (terpri))
    (mapc 'princn :l))

; .Section "Lecture de structures circulaires: read"
; on se contente de #<n>= et #<n>#
; a` la lecture, on alloue un cons unique qu'on remplace
; attention, la structure traite'e peut avoir des boucles

(defvar :shared)
(defvar :shared-length)

(defsharp |=| (:n)
    (ifn (and (boundp ':shared) (vectorp :shared))
         (setq :shared #[()] :shared-length 1))
    (while (>= :n :shared-length) (:double))
    (let ((:old (ncons :n)))
        (vset :shared :n :old)
        (let ((:new (read))(:seen))
            (vset :shared :n :new)
            (:replace :new)
            (list :new))))

(defsharp |#| (:n)
    (list (vref :shared :n)))

(de :double ()
    (let ((:dbnew (makevector (+ :shared-length :shared-length) ())))
         (bltvector :dbnew 0 :shared 0 :shared-length)
        (setq :shared :dbnew :shared-length
                           (+ :shared-length :shared-length))))

(de :replace (:x)
    (cond
        ((memq :x :seen))
        ((consp :x)
         (newl :seen :x)
         (if (eq (car :x) :old) (rplaca :x :new) (:replace (car :x)))
         (if (eq (cdr :x) :old) (rplacd :x :new) (:replace (cdr :x))))
        ((and :package-parano (symbolp :x) (packagecell :x))
         (newl :seen :x)
         (if (eq (packagecell :x) :old)
             (packagecell :x :new)
             (:replace (packagecell :x))))
        ((and (stringp :x) (neq (typestring :x) 'string))
         (newl :seen :x)
         (if (eq (typestring :x) :old)
             (typestring :x :new)
             (:replace (typestring :x))))
        ((vectorp :x)
         (newl :seen :x)
         (if (neq (typevector :x) 'vector)
             (if (eq (typevector :x) :old)
                 (typevector :x :new)
                 (:replace (typevector :x))))
         (for (:i 0 1 (1- (vlength :x)))
            (if (eq (vref :x :i) :old)
                (vset :x :i :new)
                (:replace (vref :x :i)))))))

; equal (pour les structures, on prend eq pour comparer les types ?)

(de cirnequal (:x :y) (not (cirequal :x :y)))

(de cirequal (:x :y)
    (tag :no
        (let ((:seen1)(:seen2))
            (:equal :x :y) t)))

(de :equal (:x :y)
    (cond
        ((eq :x :y))
        ((memq :x :seen1)
         (ifn (eq (length (memq :x :seen1)) (length (memq :y :seen2)))
              (exit :no ())))
        ((tconsp :x)
         (ifn (tconsp :y) (exit :no ()))
         (newl :seen1 :x) (newl :seen2 :y)
         (:equal (car :x) (car :y))
         (:equal (cdr :x) (cdr :y)))
        ((consp :x)
         (ifn (and (consp :y) (not (tconsp :y))) (exit :no ()))
         (newl :seen1 :x) (newl :seen2 :y)
         (:equal (car :x) (car :y))
         (:equal (cdr :x) (cdr :y)))
        ((vectorp :x)           ; le cas des chaines est couvert par equal
         (ifn (and (vectorp :y) (eq (vlength :x) (vlength :y)))
              (exit :no ()))
         (ifn (eq (typevector :x) (typevector :y)) (exit :no ()))
         (newl :seen1 :x) (newl :seen2 :y)
         (for (:i 0 1 (1- (vlength :x)))
            (:equal (vref :x :i) (vref :y :i))))
        ((and :package-parano (symbolp :x) (packagecell :x))
         (ifn (and (symbolp :y) (packagecell :y)) (exit :no ()))
         (newl :seen1 :x) (newl :seen2 :y)
         (:equal (packagecell :x) (packagecell :y)))
        (t (ifn (equal :x :y) (exit :no ())))))

; copy
; on procede par modification physique a partir du squelette
; on ne recopie pas les symboles aux packages pathologiques

(de circopy (:x)
    (let ((:seen)(:copied))
        (:copy :x)))

(de :copy (:x)
    (cond
        ((memq :x :seen)
         (car (lastn (length (memq :x :seen)) :copied)))
        ((tconsp :x)
         (newl :seen :x)
         (let ((:cpnew (tcons () ())))
            (newl :copied :cpnew)
            (rplaca :cpnew (:copy (car :x)))
            (rplacd :cpnew (:copy (cdr :x)))
            :cpnew))
        ((consp :x)
         (newl :seen :x)
         (let ((:cpnew (ncons ())))
            (newl :copied :cpnew)
            (rplaca :cpnew (:copy (car :x)))
            (rplacd :cpnew (:copy (cdr :x)))
            :cpnew))
        ((vectorp :x)
         (newl :seen :x)
         (let ((:cpnew (makevector (vlength :x) ())))
            (newl :copied :cpnew)
            (for (:i 0 1 (1- (vlength :x)))
                (vset :cpnew :i (:copy (vref :x :i))))
            (if (neq (typevector :x) 'vector)
                (typevector :cpnew (typevector :x)))
            :cpnew))
        (t (copy :x))))

; pour les tests

#|
    (setq a '(1 2 3))
    (rplaca (cdr a) (cdr a))
    (rplacd (cddr a) a)
    (cirprint a)                ; -> #1=(1 . #2=(#2# #1#))
    (cirequal a (read))
    '#1=(1 . #2=(#2# #1#))
    (cirequal a (circopy a))
|#