dragonfly/pkg/dag/dag.go

223 lines
4.8 KiB
Go

/*
* Copyright 2022 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dag
import (
"errors"
"sync"
)
var (
// ErrVertexNotFound represents vertex not found.
ErrVertexNotFound = errors.New("vertex not found")
// ErrVertexAlreadyExists represents vertex already exists.
ErrVertexAlreadyExists = errors.New("vertex already exists")
// ErrParnetAlreadyExists represents parent of vertex already exists.
ErrParnetAlreadyExists = errors.New("parent of vertex already exists")
// ErrChildAlreadyExists represents child of vertex already exists.
ErrChildAlreadyExists = errors.New("child of vertex already exists")
// ErrCycleBetweenVertices represents cycle between vertices.
ErrCycleBetweenVertices = errors.New("cycle between vertices")
)
// DAG is the interface used for directed acyclic graph.
type DAG interface {
// AddVertex adds vertex to graph.
AddVertex(id string, value any) error
// DeleteVertex deletes vertex graph.
DeleteVertex(id string)
// GetVertex gets vertex from graph.
GetVertex(id string) (*Vertex, error)
// AddEdge adds edge between two vertices.
AddEdge(fromVertexID, toVertexID string) error
// DeleteEdge deletes edge between two vertices.
DeleteEdge(fromVertexID, toVertexID string) error
}
// dag provides directed acyclic graph function.
type dag struct {
mu sync.RWMutex
vertices map[string]*Vertex
}
// New returns a new DAG interface.
func NewDAG() DAG {
return &dag{
vertices: make(map[string]*Vertex),
}
}
// AddVertex adds vertex to graph.
func (d *dag) AddVertex(id string, value any) error {
d.mu.Lock()
defer d.mu.Unlock()
if _, ok := d.vertices[id]; ok {
return ErrVertexAlreadyExists
}
d.vertices[id] = NewVertex(id, value)
return nil
}
// DeleteVertex deletes vertex graph.
func (d *dag) DeleteVertex(id string) {
d.mu.Lock()
defer d.mu.Unlock()
vertex, ok := d.vertices[id]
if !ok {
return
}
vertex.Parents.Range(func(item any) bool {
parent, ok := item.(*Vertex)
if !ok {
return true
}
parent.Children.Delete(vertex)
return true
})
vertex.Children.Range(func(item any) bool {
child, ok := item.(*Vertex)
if !ok {
return true
}
child.Parents.Delete(vertex)
return true
})
delete(d.vertices, id)
}
// GetVertex gets vertex from graph.
func (d *dag) GetVertex(id string) (*Vertex, error) {
d.mu.RLock()
defer d.mu.RUnlock()
vertex, ok := d.vertices[id]
if !ok {
return nil, ErrVertexNotFound
}
return vertex, nil
}
// AddEdge adds edge between two vertices.
func (d *dag) AddEdge(fromVertexID, toVertexID string) error {
d.mu.Lock()
defer d.mu.Unlock()
if fromVertexID == toVertexID {
return ErrCycleBetweenVertices
}
fromVertex, ok := d.vertices[fromVertexID]
if !ok {
return ErrVertexNotFound
}
toVertex, ok := d.vertices[toVertexID]
if !ok {
return ErrVertexNotFound
}
for _, child := range fromVertex.Children.Values() {
vertex, ok := child.(*Vertex)
if !ok {
continue
}
if vertex.ID == toVertexID {
return ErrCycleBetweenVertices
}
}
if d.depthFirstSearch(toVertexID, fromVertexID) {
return ErrCycleBetweenVertices
}
if ok := fromVertex.Children.Add(toVertex); !ok {
return ErrChildAlreadyExists
}
if ok := toVertex.Parents.Add(fromVertex); !ok {
return ErrParnetAlreadyExists
}
return nil
}
// DeleteEdge deletes edge between two vertices.
func (d *dag) DeleteEdge(fromVertexID, toVertexID string) error {
d.mu.Lock()
defer d.mu.Unlock()
fromVertex, ok := d.vertices[fromVertexID]
if !ok {
return ErrVertexNotFound
}
toVertex, ok := d.vertices[toVertexID]
if !ok {
return ErrVertexNotFound
}
fromVertex.Children.Delete(toVertex)
toVertex.Parents.Delete(fromVertex)
return nil
}
// depthFirstSearch is a depth-first search of the directed acyclic graph.
func (d *dag) depthFirstSearch(fromVertexID, toVertexID string) bool {
successors := make(map[string]struct{})
d.search(fromVertexID, successors)
_, ok := successors[toVertexID]
return ok
}
// depthFirstSearch finds successors of vertex.
func (d *dag) search(vertexID string, successors map[string]struct{}) {
vertex, ok := d.vertices[vertexID]
if !ok {
return
}
for _, child := range vertex.Children.Values() {
vertex, ok := child.(*Vertex)
if !ok {
continue
}
if _, ok := successors[vertex.ID]; !ok {
successors[vertex.ID] = struct{}{}
d.search(vertex.ID, successors)
}
}
}