├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── breadth-first-search.lisp ├── connected-components.lisp ├── degrees.lisp ├── docs ├── bfs.dot ├── bfs.png ├── clique.dot ├── clique.png ├── sample.dot └── sample.png ├── graph-algorithms.asd ├── maximal-cliques.lisp ├── package.lisp ├── shortest-paths.lisp └── tests ├── coverage.lisp ├── main.lisp └── package.lisp /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | sbcl: 5 | docker: 6 | - image: circleci/buildpack-deps:buster-curl 7 | environment: 8 | LISP: sbcl-bin 9 | steps: 10 | - checkout 11 | - run: 12 | name: Setup Environment Variables 13 | command: | 14 | echo "export PATH=$HOME/.roswell/bin:$PATH" >> $BASH_ENV 15 | - run: curl -L https://raw.githubusercontent.com/roswell/roswell/release/scripts/install-for-ci.sh | bash 16 | - run: ros install ci-utils 17 | - run: run-fiveam -l graph-algorithms/tests graph-algorithms/tests:all-tests 18 | 19 | workflows: 20 | version: 2 21 | run_tests: 22 | jobs: 23 | - sbcl 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.FASL 2 | *.fasl 3 | *.lisp-temp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Fabricio Rosario 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![CircleCI](https://circleci.com/gh/fcbr/graph-algorithms.svg?style=shield)](https://circleci.com/gh/fcbr/graph-algorithms) 3 | 4 | # Installation 5 | 6 | This package uses [ASDF](https://common-lisp.net/project/asdf/). The easiest way to use this is via [Quicklisp](https://www.quicklisp.org/). 7 | 8 | To use, clone this repository and link it from your local `~/quicklisp/local-projects/`, for example: 9 | 10 | ``` 11 | $ cd ~/quicklisp/local-projects/ 12 | $ git clone https://github.com/fcbr/graph-algorithms.git 13 | [...] 14 | $ sbcl 15 | * (ql:quickload "graph-algorithms") 16 | To load "graph-algorithms": 17 | Load 1 ASDF system: 18 | graph-algorithms 19 | ; Loading "graph-algorithms" 20 | 21 | ("graph-algorithms") 22 | * 23 | ``` 24 | 25 | See the unit tests for sample usage of each of the defined methods. 26 | 27 | # Testing 28 | 29 | This package uses [FiveAM](https://github.com/lispci/fiveam/) for unit tests. 30 | 31 | You can use the package `graph-algorithms/tests` for unit tests. Sample usage: 32 | 33 | ``` 34 | $ sbcl 35 | * (require :graph-algorithms/tests) 36 | * (graph-algorithms/tests::test-graph-algorithms) 37 | 38 | Running test suite ALL-TESTS 39 | Running test BFS . 40 | Running test DEGREES ..... 41 | Running test DIJKSTRA ... 42 | Running test MAXIMAL-CLIQUES . 43 | Running test STRONGLY-CONNECTED-COMPONENTS . 44 | Running test CONNECTED-COMPONENTS . 45 | Did 12 checks. 46 | Pass: 12 (100%) 47 | Skip: 0 ( 0%) 48 | Fail: 0 ( 0%) 49 | ``` 50 | 51 | # Documentation 52 | 53 | ## Degrees of vertices 54 | 55 | ``` 56 | (degrees vertices neighbors-fn) 57 | ``` 58 | 59 | Given a list of `VERTICES` and a `NEIGHBOR-FN` function, returns two 60 | functions: one that gives the in degree of a vertex and another that 61 | gives the out degree of a vertex. 62 | 63 | ## Breadth first search 64 | 65 | ``` 66 | (breadth-first-search source neighbors-fn visitor-fn) 67 | ``` 68 | 69 | Performs a breadth-first-search on the graph. `SOURCE` is the vertex 70 | used as the start of the search. `NEIGHBORS-FN` should return a list of 71 | immediate neighbor vertices of a given vertex. `VISITOR-FN` is called 72 | on each new vertex found by the search. 73 | 74 | ## Connected components 75 | 76 | ``` 77 | (connected-components vertices neighbors-fn visitor-fn) 78 | ``` 79 | 80 | `VERTICES` is the list of vertices of the graph. `NEIGHBORS-FN` should 81 | return a list of immediate neighbor vertices of a given vertex. 82 | `VISITOR-FN` is called once for each representative vertex of found 83 | components. 84 | 85 | ## Shortest paths 86 | 87 | ``` 88 | (shortest-paths source vertices neighbors-fn) 89 | ``` 90 | 91 | Dijkstra's shortest patha algorithm. All reachable vertices from 92 | `SOURCE` are computed. Returns `DIST` and `PREV` hash tables. As in the 93 | other methods, `NEIGHBORS-FN` is a function that receives a vertex and 94 | returns its neighbors as a list of vertices. Note that this 95 | implementation does not consider weighted edges yet. 96 | 97 | ``` 98 | (reconstruct-path prev target) 99 | ``` 100 | 101 | Given the PREV hash table returned by DIJKSTRA, reconstruct the 102 | path from the original source vertex to TARGET. 103 | 104 | ## Strongly connected components 105 | 106 | ``` 107 | (strongly-connected-components vertices neighbors-fn visitor-fn) 108 | ``` 109 | 110 | Tarjan's strongly connected components algorithm. `VERTICES` is 111 | the list of vertices of the graph. `NEIGHBORS-FN` should return 112 | a list of immediate neighbor vertices of a given vertex. `VISITOR-FN` 113 | is called once for each SCC found. 114 | 115 | ## Maximal cliques 116 | 117 | ``` 118 | (maximal-cliques vertices neighbors-fn visitor-fn) 119 | ``` 120 | 121 | Implementation of the Bron–Kerbosch algorithm for finding maximal 122 | cliques in an undirected graph, without pivoting. 123 | 124 | # Sample usage 125 | 126 | Your graph can be in any format or data structure as you want, as long as you provide a function to access the neighbors of a vertex. For convenience sake, this sample uses [property lists](http://clhs.lisp.se/Body/26_glo_p.htm#property_list) as the data structure, where each vertex has an associated list of neighbours. This way, we can use Common Lisp's `GETF` accessor as the neighbor function. 127 | 128 | ```lisp 129 | (defun sample-bfs () 130 | (let ((graph 131 | '(:a (:b :c) 132 | :b (:d) 133 | :c (:f)))) 134 | (bfs :a 135 | (lambda (n) (getf graph n)) 136 | (lambda (n) (print n))))) 137 | ``` 138 | 139 | Sample call with output for the following graph: 140 | 141 | ![graph](docs/bfs.png) 142 | 143 | ``` 144 | * (sample-bfs) 145 | 146 | :A 147 | :B 148 | :C 149 | :D 150 | :F 151 | ``` 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /breadth-first-search.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms) 2 | 3 | (defun breadth-first-search (source neighbors-fn visitor-fn 4 | &key (queue-depth 500000)) 5 | "Performs a breadth-first-search on the graph. SOURCE is the vertex 6 | used as the start of the search. NEIGHBORS-FN should return a list of 7 | immediate neighbor vertices of a given vertex. VISITOR-FN is called 8 | on each new vertex found by the search. QUEUE-DEPH is the maximum 9 | queue depth used by CL-SPEEDY-QUEUE." 10 | (let ((Q (make-queue queue-depth)) 11 | (discovered (make-hash-table))) 12 | (enqueue source Q) 13 | (setf (gethash source discovered) t) 14 | (loop until (queue-empty-p Q) 15 | do (let ((p (dequeue Q))) 16 | (funcall visitor-fn p) 17 | (dolist (v (funcall neighbors-fn p)) 18 | (unless (gethash v discovered) 19 | (setf (gethash v discovered) t) 20 | (enqueue v Q))))))) 21 | -------------------------------------------------------------------------------- /connected-components.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms) 2 | 3 | (defun connected-components (vertices neighbors-fn visitor-fn) 4 | "VERTICES is the list of vertices of the graph. NEIGHBORS-FN should 5 | return a list of immediate neighbor vertices of a given vertex. 6 | VISITOR-FN is called once for each representative vertex of found 7 | components." 8 | (let ((discovered (make-hash-table))) 9 | (dolist (id vertices) 10 | (unless (gethash id discovered) 11 | (setf (gethash id discovered) t) 12 | (funcall visitor-fn id) 13 | (breadth-first-search id neighbors-fn 14 | (lambda (n) 15 | (setf (gethash n discovered) t))))))) 16 | 17 | (defun strongly-connected-components (vertices neighbors-fn visitor-fn) 18 | "Tarjan's algorithm. Performs a single pass of depth first 19 | search. It maintains a stack of vertices that have been explored by 20 | the search but not yet assigned to a component, and calculates low 21 | numbers of each vertex (an index number of the highest ancestor 22 | reachable in one step from a descendant of the vertex) which it uses 23 | to determine when a set of vertices should be popped off the stack 24 | into a new component. 25 | 26 | VERTICES is the list of vertices of the graph. NEIGHBORS-FN should 27 | return a list of immediate neighbor vertices of a given vertex. 28 | VISITOR-FN is called once for each SCC found. 29 | 30 | This implementation is a naive translation of the pseudocode in 31 | Wikipedia, not really optimized for any particular workflow." 32 | (let ((index 0) 33 | (stack nil) 34 | (on-stack (make-hash-table)) 35 | (indices (make-hash-table)) 36 | (low-links (make-hash-table))) 37 | (labels ((set-low-link (v n) (setf (gethash v low-links) n)) 38 | (get-low-link (v) (gethash v low-links)) 39 | (set-index (v n) (setf (gethash v indices) n)) 40 | (get-index (v) (gethash v indices)) 41 | (set-on-stack (v n) (setf (gethash v on-stack) n)) 42 | (get-on-stack (v) (gethash v on-stack)) 43 | (strong-connect (v) 44 | (set-index v index) 45 | (set-low-link v index) 46 | (incf index) 47 | (push v stack) 48 | (set-on-stack v t) 49 | 50 | ;; consider sucessors of v 51 | (dolist (w (funcall neighbors-fn v)) 52 | (if (not (get-index w)) 53 | ;; sucessor w has not yet been visited; recurse on it 54 | (progn 55 | (strong-connect w) 56 | (set-low-link v (min (get-low-link v) (get-low-link w)))) 57 | 58 | (if (get-on-stack w) 59 | ;; sucessor w is in stack and hence in the current 60 | ;; SCC 61 | (set-low-link v (min (get-low-link v) (get-index w)))))) 62 | 63 | ;; if v is a root node, pop the stack and generate a SCC 64 | (when (= (get-low-link v) (get-index v)) 65 | (let ((w nil) 66 | (connected-component nil)) 67 | (loop until (eql v w) 68 | do 69 | (setf w (pop stack)) 70 | (set-on-stack w nil) 71 | (push w connected-component)) 72 | 73 | ;; emit the SCC 74 | (funcall visitor-fn connected-component))))) 75 | (dolist (v vertices) 76 | (when (not (get-index v)) 77 | (strong-connect v)))))) 78 | -------------------------------------------------------------------------------- /degrees.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms) 2 | 3 | (defun degrees (vertices neighbors-fn) 4 | "Given a list of VERTICES and a NEIGHBOR-FN function, returns two 5 | functions: one that gives the in degree of a vertex and another that 6 | gives the out degree of a vertex." 7 | (let ((in-degree (make-hash-table)) 8 | (out-degree (make-hash-table))) 9 | (flet ((+in-degree (v n) 10 | (setf (gethash v in-degree) 11 | (+ n (gethash v in-degree)))) 12 | (+out-degree (v n) 13 | (setf (gethash v out-degree) 14 | (+ n (gethash v out-degree))))) 15 | (dolist (v vertices) 16 | (setf (gethash v in-degree) 0) 17 | (setf (gethash v out-degree) 0)) 18 | (dolist (v vertices) 19 | (let ((neighbors (funcall neighbors-fn v))) 20 | (+out-degree v (length neighbors)) 21 | (when neighbors 22 | (dolist (n neighbors) 23 | (+in-degree n 1))))) 24 | (values (lambda (n) 25 | (ensure-gethash n in-degree 0)) 26 | (lambda (n) 27 | (ensure-gethash n out-degree 0)))))) 28 | -------------------------------------------------------------------------------- /docs/bfs.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | a -> { b c } 3 | b -> d 4 | c -> f 5 | } 6 | 7 | -------------------------------------------------------------------------------- /docs/bfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcbr/graph-algorithms/3d9ea220344c2d10db83b99d0571a95904d5bc9a/docs/bfs.png -------------------------------------------------------------------------------- /docs/clique.dot: -------------------------------------------------------------------------------- 1 | graph { 2 | a -- { b e } 3 | b -- { c e } 4 | c -- { d } 5 | d -- { f } 6 | e -- { d } 7 | } 8 | -------------------------------------------------------------------------------- /docs/clique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcbr/graph-algorithms/3d9ea220344c2d10db83b99d0571a95904d5bc9a/docs/clique.png -------------------------------------------------------------------------------- /docs/sample.dot: -------------------------------------------------------------------------------- 1 | digraph "sample" { 2 | a -> b 3 | b -> { c e f } 4 | c -> { d g } 5 | d -> { c g h } 6 | e -> { a f } 7 | f -> g 8 | g -> f 9 | h -> { g d } 10 | } -------------------------------------------------------------------------------- /docs/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcbr/graph-algorithms/3d9ea220344c2d10db83b99d0571a95904d5bc9a/docs/sample.png -------------------------------------------------------------------------------- /graph-algorithms.asd: -------------------------------------------------------------------------------- 1 | ;;;; graph-algorithms.asd 2 | 3 | (asdf:defsystem #:graph-algorithms 4 | :description "Assorted graph algorithms." 5 | :author "Fabricio Chalub " 6 | :license "MIT" 7 | :depends-on (#:cl-speedy-queue 8 | #:minheap 9 | #:alexandria) 10 | :serial t 11 | :components ((:file "package") 12 | (:file "maximal-cliques") 13 | (:file "breadth-first-search") 14 | (:file "connected-components") 15 | (:file "degrees") 16 | (:file "shortest-paths"))) 17 | 18 | (asdf:defsystem #:graph-algorithms/tests 19 | :description "Unit tests for :graph-algorithms." 20 | :author "Fabricio Chalub " 21 | :license "MIT" 22 | :depends-on (#:graph-algorithms 23 | #:fiveam) 24 | :components ((:module "tests" 25 | :serial t 26 | :components ((:file "package") 27 | (:file "main"))))) 28 | 29 | 30 | ;;; :perform (test-op (o s) (uiop:symbol-call :fiveam :run! 'graph-algorithms/tests:all-tests))) 31 | -------------------------------------------------------------------------------- /maximal-cliques.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms) 2 | 3 | (defun maximal-cliques (vertices neighbors-fn visitor-fn) 4 | "Implementation of the Bron–Kerbosch algorithm for finding maximal 5 | cliques in an undirected graph, without pivoting." 6 | (bron-kerbosch nil vertices nil neighbors-fn visitor-fn)) 7 | 8 | (defun bron-kerbosch (R P X neighbors-fn visitor-fn) 9 | "The basic form of the Bron–Kerbosch algorithm is a recursive 10 | backtracking algorithm that searches for all maximal cliques in a 11 | given graph G. More generally, given three disjoint sets of vertices 12 | R, P, and X, it finds the maximal cliques that include all of the 13 | vertices in R, some of the vertices in P, and none of the vertices in 14 | X. In each call to the algorithm, P and X are disjoint sets whose 15 | union consists of those vertices that form cliques when added to R. In 16 | other words, P ∪ X is the set of vertices which are joined to every 17 | element of R. When P and X are both empty there are no further 18 | elements that can be added to R, so R is a maximal clique and the 19 | algorithm outputs R." 20 | (when (and (emptyp P) (emptyp X)) 21 | (funcall visitor-fn R)) 22 | (dolist (v P) 23 | (let ((nv (funcall neighbors-fn v))) 24 | (bron-kerbosch 25 | (union R (list v)) 26 | (intersection P nv) 27 | (intersection X nv) 28 | neighbors-fn visitor-fn) 29 | (removef P v) 30 | (push v X)))) 31 | -------------------------------------------------------------------------------- /package.lisp: -------------------------------------------------------------------------------- 1 | ;;;; package.lisp 2 | 3 | (defpackage #:graph-algorithms 4 | (:use #:cl #:cl-speedy-queue #:pairing-heap #:alexandria) 5 | (:export #:breadth-first-search 6 | #:connected-components 7 | #:strongly-connected-components 8 | #:maximal-cliques 9 | #:shortest-paths 10 | #:reconstruct-path 11 | #:degrees)) 12 | -------------------------------------------------------------------------------- /shortest-paths.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms) 2 | 3 | (defun shortest-paths (source vertices neighbors-fn) 4 | "Dijkstra's shortest path algorithm. All reachable vertices from 5 | SOURCE are computed. Returns DIST and PREV hash tables. As in the 6 | other methods, NEIGHBORS-FN is a function that receives a vertex and 7 | returns its neighbors as a list of vertices. Note that this 8 | implementation does not consider weighted edges yet." 9 | (let* ((dist (make-hash-table)) 10 | (prev (make-hash-table)) 11 | (nodes (make-hash-table)) 12 | (Q (make-instance 'pairing-heap))) 13 | (flet ((set-dist (v n) (setf (gethash v dist) n)) 14 | (get-dist (v) (gethash v dist)) 15 | (set-node (v n) (setf (gethash v nodes) n)) 16 | (get-node (v) (gethash v nodes)) 17 | (set-prev (v n) (setf (gethash v prev) n)) 18 | (get-prev (v) (gethash v prev))) 19 | (set-dist source 0) 20 | (set-prev source nil) 21 | (dolist (v vertices) 22 | (when (not (eql v source)) 23 | (set-dist v MOST-POSITIVE-FIXNUM) 24 | (set-prev v nil)) 25 | (set-node v (insert Q (get-dist v) v))) 26 | (loop until (empty-p Q) 27 | do 28 | (let ((u (extract-min Q)) 29 | (alt 0)) 30 | (dolist (v (funcall neighbors-fn u)) 31 | (setf alt (+ (get-dist u) 1)) 32 | (when (< alt (get-dist v)) 33 | (set-dist v alt) 34 | (set-prev v u) 35 | (decrease-key Q (get-node v) alt)))))) 36 | (values prev dist))) 37 | 38 | (defun reconstruct-path (prev target) 39 | "Given the PREV hash table returned by DIJKSTRA, reconstruct the 40 | path from the original source vertex to TARGET." 41 | (let ((S nil) 42 | (u target)) 43 | (loop while (gethash u prev) 44 | do 45 | (push u S) 46 | (setf u (gethash u prev))) 47 | S)) 48 | -------------------------------------------------------------------------------- /tests/coverage.lisp: -------------------------------------------------------------------------------- 1 | (require :sb-cover) 2 | 3 | ;;; Turn on generation of code coverage instrumentation in the compiler 4 | (declaim (optimize sb-cover:store-coverage-data)) 5 | 6 | (asdf:oos 'asdf:load-op :graph-algorithms :force t) 7 | (asdf:oos 'asdf:load-op :graph-algorithms/tests :force t) 8 | 9 | (require :graph-algorithms/tests) 10 | 11 | (graph-algorithms/tests::test-graph-algorithms) 12 | 13 | (sb-cover:report "/tmp/") 14 | -------------------------------------------------------------------------------- /tests/main.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:graph-algorithms/tests) 2 | 3 | (def-suite all-tests 4 | :description "The master suite of all tests.") 5 | 6 | (in-suite all-tests) 7 | 8 | (defun test-graph-algorithms () 9 | (run! 'all-tests)) 10 | 11 | (defvar *simple-graph* 12 | '(:a (:b :c) 13 | :b (:d) 14 | :c (:f :a))) 15 | 16 | (defvar *complex-graph* 17 | '(:a (:b) 18 | :b (:c :e :f) 19 | :c (:d :g) 20 | :d (:c :g :h) 21 | :e (:a :f) 22 | :f (:g) 23 | :g (:f) 24 | :h (:g :d))) 25 | 26 | (defvar *cliques* 27 | '(:a (:b :e) 28 | :b (:a :c :e) 29 | :c (:d :b) 30 | :d (:c :e :f) 31 | :e (:a :b :d) 32 | :f (:d))) 33 | 34 | (defun simple-bfs () 35 | (let ((path nil)) 36 | (breadth-first-search :a (lambda (n) (getf *simple-graph* n)) 37 | (lambda (n) (push n path))) 38 | (reverse path))) 39 | 40 | (defun simple-degrees (v) 41 | (multiple-value-bind (in-fn out-fn) 42 | (degrees '(:a :b :c :d :f) (lambda (n) (getf *simple-graph* n))) 43 | (list (funcall in-fn v) (funcall out-fn v)))) 44 | 45 | (defun complex-dijkstra (from to) 46 | (multiple-value-bind (prev dist) 47 | (shortest-paths from '(:a :b :c :d :e :f :g :h) 48 | (lambda (n) (getf *complex-graph* n))) 49 | (list 50 | (gethash to dist) 51 | (reconstruct-path prev to)))) 52 | 53 | (defun complex-strongly-connected-components () 54 | (let ((sccs nil)) 55 | (strongly-connected-components 56 | '(:a :b :c :d :e :f :g :h) 57 | (lambda (n) 58 | (getf *complex-graph* n)) 59 | (lambda (scc) 60 | (push scc sccs))) 61 | sccs)) 62 | 63 | (defun complex-connected-components () 64 | (let ((ccs nil)) 65 | (connected-components 66 | '(:a :b :c :d :e :f :g :h) 67 | (lambda (n) 68 | (getf *complex-graph* n)) 69 | (lambda (cc) 70 | (push cc ccs))) 71 | ccs)) 72 | 73 | (defun cliques-maximal-cliques () 74 | (let ((cliques nil)) 75 | (maximal-cliques '(:a :b :c :d :e :f) 76 | (lambda (n) (getf *cliques* n)) 77 | (lambda (n) (push n cliques))) 78 | cliques)) 79 | 80 | (test bfs 81 | (is (equal '(:a :b :c :d :f) (simple-bfs)))) 82 | 83 | (test degrees 84 | (is (equal '(1 2) (simple-degrees :a))) 85 | (is (equal '(1 1) (simple-degrees :b))) 86 | (is (equal '(1 2) (simple-degrees :c))) 87 | (is (equal '(1 0) (simple-degrees :d))) 88 | (is (equal '(1 0) (simple-degrees :f)))) 89 | 90 | (test dijkstra 91 | (is (equal '(2 (:b :f)) (complex-dijkstra :a :f))) 92 | (is (equal '(1 (:f)) (complex-dijkstra :b :f))) 93 | (is (equal '(4 (:b :c :d :h)) (complex-dijkstra :a :h)))) 94 | 95 | (test maximal-cliques 96 | (is (equal '((:d :e) (:d :f) (:c :d) (:b :c) 97 | (:e :a :b)) 98 | (cliques-maximal-cliques)))) 99 | 100 | (test strongly-connected-components 101 | (is (equal '((:a :b :e) 102 | (:c :d :h) 103 | (:g :f)) 104 | (complex-strongly-connected-components)))) 105 | 106 | (test connected-components 107 | (is (equal '(:a) (complex-connected-components)))) 108 | -------------------------------------------------------------------------------- /tests/package.lisp: -------------------------------------------------------------------------------- 1 | ;;;; package.lisp 2 | 3 | (defpackage #:graph-algorithms/tests 4 | (:use #:cl #:fiveam #:graph-algorithms) 5 | (:export #:test-graph-algorithms #:run! #:all-tests)) 6 | --------------------------------------------------------------------------------