stoe/core/graph.lisp
Renaud Casenave-Péré 08706de1f4 Add a graph utility class
Remove tree functionality from utils.lisp and generalize it as graph
2017-06-04 23:21:44 +02:00

68 lines
2.4 KiB
Common Lisp

#|
This file is a part of stoe project.
Copyright (c) 2014 Renaud Casenave-Péré (renaud@casenave-pere.fr)
|#
(uiop:define-package :stoe/core/graph
(:use :cl :stoe/core/utils)
(:export #:graph-node #:prior-nodes #:next-nodes
#:attach-node #:detach-node #:insert-node
#:cyclic-graph-p #:make-graph-node))
(in-package :stoe/core/graph)
(defclass graph-node ()
((prior-nodes :initform nil :accessor prior-nodes)
(next-nodes :initform nil :accessor next-nodes)))
(defgeneric attach-node (node &key prior next))
(defmethod attach-node ((node graph-node) &key prior next)
(let ((prior-list (safe-list prior))
(next-list (safe-list next)))
(with-slots (prior-nodes next-nodes) node
(setf prior-nodes (append prior-nodes prior-list))
(setf next-nodes (append next-nodes next-list)))
(mapc (lambda (pnode)
(with-slots (next-nodes) pnode
(setf next-nodes (append next-nodes (list node)))))
prior-list)
(mapc (lambda (nnode)
(with-slots (prior-nodes) nnode
(setf prior-nodes (append prior-nodes (list node)))))
next-list))
node)
(defgeneric detach-node (node &key prior next))
(defmethod detach-node ((node graph-node) &key prior next)
(let ((prior-list (if (eq prior t) (prior-nodes node) (safe-list prior)))
(next-list (if (eq next t) (next-nodes node) (safe-list next))))
(mapc (lambda (pnode)
(with-slots (next-nodes) pnode
(setf next-nodes (remove node next-nodes))))
prior-list)
(mapc (lambda (nnode)
(with-slots (prior-nodes) nnode
(setf prior-nodes (remove node prior-nodes))))
next-list))
(values))
(defgeneric insert-node (node prior next))
(defmethod insert-node ((node graph-node) prior next)
(detach-node prior :next next)
(attach-node node :prior prior :next next))
(defun cyclic-graph-p (node)
(let ((visited-nodes (list node))
(visit-stack (next-nodes node)))
(loop for node = (pop visit-stack)
do (cond
((null node) (return nil))
((member node visited-nodes) (return t))
(t
(push node visited-nodes)
(setf visit-stack (append (next-nodes node) visit-stack)))))))
(defun make-graph-node (&key prior next)
(let ((node (make-instance 'graph-node)))
(when (or prior next)
(attach-node node :prior prior :next next))
node))