|
|
| #| simple-header |
|
|
| $Id: graph-container.lisp,v 1.12 2005/07/20 20:39:09 moody Exp $ |
|
|
| Author: Gary King |
|
|
| DISCUSSION |
|
|
| |# |
|
|
| (in-package #:metabang.graph) |
|
|
| |
|
|
| (defclass* graph-container (iteratable-container-mixin |
| non-associative-container-mixin |
| initial-contents-mixin |
| basic-graph |
| container-uses-nodes-mixin) |
| ((vertex-pair->edge (make-container 'simple-associative-container |
| :test #'equal) r)) |
| (:default-initargs |
| :vertex-class 'graph-container-vertex |
| :directed-edge-class 'graph-container-directed-edge |
| :undirected-edge-class 'graph-container-undirected-edge) |
| (:export-p t) |
| (:documentation "A graph container is essentially an adjacency list graph representation [?? The Bad name comes from it being implemented with containers... ugh]")) |
|
|
|
|
| (defclass* graph-container-edge (basic-edge) |
| ((vertex-1 nil ir "`Vertex-1` is one of the two vertexes that an edge connects. In a directed-edge, `vertex-1` is also the `source-vertex`.") |
| (vertex-2 nil ir "`Vertex-2` is one of the two vertexes that an edge connects. In a directed edge, `vertex-2` is also the `target-vertex`.") |
| |
| ) |
| (:export-slots vertex-1 vertex-2 |
| ) |
| (:export-p t) |
| (:documentation "This is the root class for edges in graph-containers. It adds vertex-1 and vertex-2 slots.")) |
|
|
|
|
| (defmethod print-object ((object graph-container-edge) stream) |
| (print-unreadable-object (object stream :type t) |
| (format stream "<~A ~A ~A>" (vertex-1 object) (vertex-2 object) |
| (value object)))) |
|
|
|
|
| (defclass* weighted-edge (weighted-edge-mixin graph-container-edge) |
| () |
| (:export-p t) |
| (:documentation "A weighted edge is both a weighted-edge-mixin and a graph-container-edge.")) |
|
|
| (defclass* weighted-directed-edge (weighted-edge-mixin graph-container-directed-edge) |
| () |
| (:export-p t) |
| (:documentation "A weighted edge is a weighted-edge-mixin, a directed-edge-mixin, and a graph-container-edge.")) |
|
|
| (defclass* weighted-undirected-edge (weighted-edge-mixin graph-container-undirected-edge) |
| () |
| (:export-p t) |
| (:documentation "A weighted undirected edge is a weighted-edge-mixin, an undirected-edge-mixin, and a graph-container-edge.")) |
|
|
|
|
| (defclass* graph-container-vertex (basic-vertex) |
| ((vertex-edges nil r)) |
| (:export-p t) |
| (:default-initargs |
| :vertex-edges-container-class 'vector-container) |
| (:documentation "A graph container vertex keeps track of its edges in the the vertex-edges slot. The storage for this defaults to a vector-container but can be changed using the vertex-edges-container-class initarg.")) |
|
|
|
|
| (defmethod make-vertex-edges-container ((vertex graph-container-vertex) |
| container-class &rest args) |
| (apply #'make-container container-class args)) |
|
|
|
|
| (defmethod initialize-instance :after ((object graph-container-vertex) &key |
| vertex-edges-container-class) |
| (setf (slot-value object 'vertex-edges) |
| (make-vertex-edges-container object vertex-edges-container-class))) |
|
|
|
|
| (defmethod make-vertex-container ((graph graph-container) initial-size) |
| (make-container 'simple-associative-container |
| :initial-size initial-size |
| :test (vertex-test graph))) |
|
|
|
|
| (defmethod make-edge-container ((graph graph-container) initial-size) |
| (make-container 'vector-container :initial-size initial-size |
| :fill-pointer 0)) |
|
|
| |
|
|
| (defclass* graph-container-undirected-edge (undirected-edge-mixin |
| graph-container-edge) |
| () |
| (:export-p t) |
| (:documentation "A graph-container-undirected-edge is both an undirected-edge-mixin and a graph-container-edge.")) |
|
|
|
|
| |
|
|
| (defclass* graph-container-directed-edge (directed-edge-mixin |
| graph-container-edge) |
| () |
| (:export-p t) |
| (:documentation "A graph-container-directed-edge is both a directed-edge-mixin and a graph-container-edge.")) |
|
|
|
|
| (defmethod initialize-instance :after ((object graph-container-directed-edge) |
| &key source-vertex target-vertex) |
| (when (and source-vertex (vertex-1 object)) |
| (error "Specify source-vertex or vertex-1, but not both")) |
| (when (and target-vertex (vertex-2 object)) |
| (error "Specify target-vertex or vertex-2, but not both")) |
| (when source-vertex |
| (setf (slot-value object 'vertex-1) source-vertex)) |
| (when target-vertex |
| (setf (slot-value object 'vertex-2) target-vertex))) |
|
|
| |
|
|
| (defmethod source-vertex ((edge graph-container-edge)) |
| (vertex-1 edge)) |
|
|
| |
|
|
| (defmethod target-vertex ((edge graph-container-edge)) |
| (vertex-2 edge)) |
|
|
|
|
| (defmethod other-vertex ((edge graph-container-edge) |
| (v graph-container-vertex)) |
| (cond ((eq v (vertex-1 edge)) |
| (values (vertex-2 edge))) |
| |
| ((eq v (vertex-2 edge)) |
| (values (vertex-1 edge))) |
| |
| (t (error "Vertex ~A not part of Edge ~A" v edge)))) |
|
|
|
|
| (defmethod other-vertex ((edge graph-container-edge) |
| (value t)) |
| (other-vertex edge (find-vertex edge value))) |
|
|
|
|
| |
| (defmethod associate-edge-with-pair ((graph graph-container) (edge graph-container-edge)) |
| (push edge (item-at-1 (vertex-pair->edge graph) |
| (cons (vertex-1 edge) (vertex-2 edge))))) |
|
|
| (defmethod associate-edge-with-pair :after ((graph graph-container) (edge undirected-edge-mixin)) |
| |
| (push edge (item-at-1 (vertex-pair->edge graph) |
| (cons (vertex-2 edge) (vertex-1 edge))))) |
|
|
| (defun dissoc-val-from-key-or-delete (container index val) |
| (let* ((l (delete val (item-at-1 container index) :test #'eq))) |
| (if (null l) |
| (delete-item-at container index) |
| (setf (item-at-1 container index) l)))) |
|
|
| (defmethod dissociate-edge-from-pair ((graph graph-container) (edge graph-container-edge)) |
| (dissoc-val-from-key-or-delete (vertex-pair->edge graph) |
| (cons (vertex-1 edge) (vertex-2 edge)) edge)) |
|
|
| (defmethod dissociate-edge-from-pair :after ((graph graph-container) (edge undirected-edge-mixin)) |
| (dissoc-val-from-key-or-delete (vertex-pair->edge graph) |
| (cons (vertex-2 edge) (vertex-1 edge)) edge)) |
|
|
|
|
| |
| |
|
|
| |
| (defmethod replace-vertex :before ((graph graph-container) (old basic-vertex) (new basic-vertex)) |
| (iterate-edges old (lambda (e) (dissociate-edge-from-pair graph e)))) |
|
|
| |
| (defmethod replace-vertex :after ((graph graph-container) (old basic-vertex) (new basic-vertex)) |
| (iterate-edges new (lambda (e) (associate-edge-with-pair graph e)))) |
|
|
|
|
| (defmethod add-edge ((graph graph-container) (edge graph-container-edge) |
| &key force-new?) |
| (declare (ignore force-new?)) |
| |
| (let ((vertex-1 (vertex-1 edge)) |
| (vertex-2 (vertex-2 edge))) |
| |
| (cond ((eq vertex-1 vertex-2) |
| (add-edge-to-vertex edge vertex-1)) |
| (t |
| (add-edge-to-vertex edge vertex-1) |
| (add-edge-to-vertex edge vertex-2))) |
| (associate-edge-with-pair graph edge)) |
| edge) |
|
|
|
|
| (defmethod add-edge-to-vertex :around ((edge graph-container-edge) |
| (vertex graph-container-vertex)) |
| (insert-item (vertex-edges vertex) edge)) |
|
|
|
|
| (defmethod make-node-for-container ((graph graph-container) (node t) &key) |
| (make-vertex-for-graph graph :element node)) |
|
|
|
|
| (defmethod find-edge-between-vertexes ((graph graph-container) |
| (vertex-1 graph-container-vertex) |
| (vertex-2 graph-container-vertex) |
| &key error-if-not-found?) |
| (multiple-value-bind (value found?) |
| (item-at-1 (vertex-pair->edge graph) |
| (cons vertex-1 vertex-2)) |
| (when (and error-if-not-found? |
| (not found?)) |
| (error 'graph-edge-not-found-error |
| :vertex-1 vertex-1 :vertex-2 vertex-1)) |
| (first value))) |
|
|
|
|
| (defmethod find-edge-between-vertexes-if ((graph graph-container) |
| (vertex-1 graph-container-vertex) |
| (vertex-2 graph-container-vertex) |
| fn |
| &key error-if-not-found?) |
| (let ((it (search-for-match (vertex-edges vertex-1) |
| (lambda (edge) |
| (and (eq vertex-2 (other-vertex edge vertex-1)) |
| (funcall fn edge)))))) |
| (when (and error-if-not-found? (not it)) |
| (error 'graph-edge-not-found-error |
| :vertex-1 vertex-1 :vertex-2 vertex-1)) |
| it)) |
|
|
|
|
| (defmethod find-edge-between-vertexes-if ((graph graph-container) |
| (value-1 t) |
| (value-2 t) |
| fn |
| &key error-if-not-found?) |
| (let ((v1 (find-vertex graph value-1 error-if-not-found?)) |
| (v2 (find-vertex graph value-2 error-if-not-found?))) |
| (or (and v1 v2 (find-edge-between-vertexes-if graph v1 v2 fn)) |
| (when error-if-not-found? |
| (error 'graph-edge-not-found-error :vertex-1 v1 :vertex-2 v2))))) |
|
|
|
|
| (defmethod find-edge ((graph graph-container) (edge graph-container-edge) |
| &optional error-if-not-found?) |
| (find-edge-between-vertexes |
| graph (vertex-1 edge) (vertex-2 edge) |
| :error-if-not-found? error-if-not-found?)) |
|
|
|
|
| (defmethod delete-edge ((graph graph-container) (edge graph-container-edge)) |
| (let ((vertex-1 (vertex-1 edge)) |
| (vertex-2 (vertex-2 edge))) |
| (delete-item (vertex-edges vertex-1) edge) |
| (delete-item (vertex-edges vertex-2) edge) |
| (dissociate-edge-from-pair graph edge)) |
| edge) |
|
|
| (defmethod delete-all-edges ((graph graph-container)) |
| (iterate-vertexes |
| graph |
| (lambda (vertex) |
| (empty! (vertex-edges vertex)))) |
| (empty! (vertex-pair->edge graph)) |
| graph) |
|
|
|
|
| (defmethod empty! :after ((graph graph-container)) |
| (empty! (vertex-pair->edge graph))) |
|
|
|
|
| |
|
|
| (defmethod iterate-edges ((graph graph-container) fn) |
| (iterate-elements (graph-edges graph) fn)) |
|
|
|
|
| (defmethod iterate-edges ((vertex graph-container-vertex) fn) |
| (iterate-elements (vertex-edges vertex) fn)) |
|
|
|
|
| (defmethod iterate-source-edges ((vertex graph-container-vertex) fn) |
| (iterate-elements (vertex-edges vertex) |
| (lambda (edge) |
| (when (or (undirected-edge-p edge) |
| (eq vertex (source-vertex edge))) |
| (funcall fn edge))))) |
|
|
|
|
| (defmethod iterate-target-edges ((vertex graph-container-vertex) fn) |
| (iterate-elements (vertex-edges vertex) |
| (lambda (edge) |
| (when (or (undirected-edge-p edge) |
| (eq vertex (target-vertex edge))) |
| (funcall fn edge))))) |
|
|
|
|
| (defmethod iterate-children ((vertex graph-container-vertex) fn) |
| (iterate-source-edges vertex |
| (lambda (edge) |
| (funcall fn (other-vertex edge vertex))))) |
|
|
|
|
| (defmethod iterate-parents ((vertex graph-container-vertex) fn) |
| (iterate-target-edges vertex |
| (lambda (edge) |
| (funcall fn (other-vertex edge vertex))))) |
|
|
|
|
| (defmethod iterate-neighbors ((vertex graph-container-vertex) fn) |
| (iterate-edges vertex |
| (lambda (edge) |
| (funcall fn (other-vertex edge vertex))))) |
|
|
|
|
| (defmethod vertexes ((edge graph-container-edge)) |
| (collect-using #'iterate-vertexes nil edge)) |
|
|
|
|
| (defmethod has-children-p ((vertex graph-container-vertex)) |
| (iterate-source-edges vertex |
| (lambda (edge) |
| (declare (ignore edge)) |
| (return-from has-children-p t))) |
| (values nil)) |
|
|
|
|
| (defmethod has-parent-p ((vertex graph-container-vertex)) |
| (iterate-target-edges vertex |
| (lambda (edge) |
| (declare (ignore edge)) |
| (return-from has-parent-p t))) |
| (values nil)) |
|
|
|
|
| (defmethod vertices-share-edge-p ((vertex-1 graph-container-vertex) |
| (vertex-2 graph-container-vertex)) |
| (iterate-target-edges vertex-1 |
| (lambda (e) |
| (when (or (eq (target-vertex e) vertex-2) |
| (eq (source-vertex e) vertex-2)) |
| (return-from vertices-share-edge-p t)))) |
| |
| (iterate-source-edges vertex-1 |
| (lambda (e) |
| (when (or (eq (target-vertex e) vertex-2) |
| (eq (source-vertex e) vertex-2)) |
| (return-from vertices-share-edge-p t)))) |
| |
| (values nil)) |
|
|
|
|
| (defmethod edge-count ((graph graph-container)) |
| (size (graph-edges graph))) |
|
|
|
|