github.com/gopherd/gonum@v0.0.4/graph/multi/undirected.go (about)

     1  // Copyright ©2014 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package multi
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/gopherd/gonum/graph"
    11  	"github.com/gopherd/gonum/graph/iterator"
    12  	"github.com/gopherd/gonum/graph/set/uid"
    13  )
    14  
    15  var (
    16  	ug *UndirectedGraph
    17  
    18  	_ graph.Graph                = ug
    19  	_ graph.Undirected           = ug
    20  	_ graph.Multigraph           = ug
    21  	_ graph.UndirectedMultigraph = ug
    22  	_ graph.NodeAdder            = ug
    23  	_ graph.NodeRemover          = ug
    24  	_ graph.LineAdder            = ug
    25  	_ graph.LineRemover          = ug
    26  )
    27  
    28  // UndirectedGraph implements a generalized undirected graph.
    29  type UndirectedGraph struct {
    30  	nodes map[int64]graph.Node
    31  	lines map[int64]map[int64]map[int64]graph.Line
    32  
    33  	nodeIDs *uid.Set
    34  	lineIDs map[int64]map[int64]*uid.Set
    35  }
    36  
    37  // NewUndirectedGraph returns an UndirectedGraph.
    38  func NewUndirectedGraph() *UndirectedGraph {
    39  	return &UndirectedGraph{
    40  		nodes: make(map[int64]graph.Node),
    41  		lines: make(map[int64]map[int64]map[int64]graph.Line),
    42  
    43  		nodeIDs: uid.NewSet(),
    44  		lineIDs: make(map[int64]map[int64]*uid.Set),
    45  	}
    46  }
    47  
    48  // AddNode adds n to the graph. It panics if the added node ID matches an existing node ID.
    49  func (g *UndirectedGraph) AddNode(n graph.Node) {
    50  	if _, exists := g.nodes[n.ID()]; exists {
    51  		panic(fmt.Sprintf("simple: node ID collision: %d", n.ID()))
    52  	}
    53  	g.nodes[n.ID()] = n
    54  	g.nodeIDs.Use(n.ID())
    55  }
    56  
    57  // Edge returns the edge from u to v if such an edge exists and nil otherwise.
    58  // The node v must be directly reachable from u as defined by the From method.
    59  // The returned graph.Edge is a multi.Edge if an edge exists.
    60  func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
    61  	l := g.LinesBetween(uid, vid)
    62  	if l == graph.Empty {
    63  		return nil
    64  	}
    65  	return Edge{F: g.Node(uid), T: g.Node(vid), Lines: l}
    66  }
    67  
    68  // EdgeBetween returns the edge between nodes x and y.
    69  func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
    70  	return g.Edge(xid, yid)
    71  }
    72  
    73  // Edges returns all the edges in the graph. Each edge in the returned slice
    74  // is a multi.Edge.
    75  //
    76  // The returned graph.Edges is only valid until the next mutation of
    77  // the receiver.
    78  func (g *UndirectedGraph) Edges() graph.Edges {
    79  	if len(g.lines) == 0 {
    80  		return graph.Empty
    81  	}
    82  	var edges []graph.Edge
    83  	for xid, u := range g.lines {
    84  		for yid, lines := range u {
    85  			if yid < xid {
    86  				// Do not consider lines when the To node ID is
    87  				// before the From node ID. Both orientations
    88  				// are stored.
    89  				continue
    90  			}
    91  			if len(lines) == 0 {
    92  				continue
    93  			}
    94  			edges = append(edges, Edge{
    95  				F:     g.Node(xid),
    96  				T:     g.Node(yid),
    97  				Lines: iterator.NewLines(lines),
    98  			})
    99  		}
   100  	}
   101  	if len(edges) == 0 {
   102  		return graph.Empty
   103  	}
   104  	return iterator.NewOrderedEdges(edges)
   105  }
   106  
   107  // From returns all nodes in g that can be reached directly from n.
   108  //
   109  // The returned graph.Nodes is only valid until the next mutation of
   110  // the receiver.
   111  func (g *UndirectedGraph) From(id int64) graph.Nodes {
   112  	if len(g.lines[id]) == 0 {
   113  		return graph.Empty
   114  	}
   115  	return iterator.NewNodesByLines(g.nodes, g.lines[id])
   116  }
   117  
   118  // HasEdgeBetween returns whether an edge exists between nodes x and y.
   119  func (g *UndirectedGraph) HasEdgeBetween(xid, yid int64) bool {
   120  	_, ok := g.lines[xid][yid]
   121  	return ok
   122  }
   123  
   124  // Lines returns the lines from u to v if such an edge exists and nil otherwise.
   125  // The node v must be directly reachable from u as defined by the From method.
   126  func (g *UndirectedGraph) Lines(uid, vid int64) graph.Lines {
   127  	return g.LinesBetween(uid, vid)
   128  }
   129  
   130  // LinesBetween returns the lines between nodes x and y.
   131  func (g *UndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
   132  	if !g.HasEdgeBetween(xid, yid) {
   133  		return graph.Empty
   134  	}
   135  	var lines []graph.Line
   136  	for _, l := range g.lines[xid][yid] {
   137  		if l.From().ID() != xid {
   138  			l = l.ReversedLine()
   139  		}
   140  		lines = append(lines, l)
   141  	}
   142  	return iterator.NewOrderedLines(lines)
   143  }
   144  
   145  // NewLine returns a new Line from the source to the destination node.
   146  // The returned Line will have a graph-unique ID.
   147  // The Line's ID does not become valid in g until the Line is added to g.
   148  func (g *UndirectedGraph) NewLine(from, to graph.Node) graph.Line {
   149  	xid := from.ID()
   150  	yid := to.ID()
   151  	if yid < xid {
   152  		xid, yid = yid, xid
   153  	}
   154  	var lineID int64
   155  	switch {
   156  	case g.lineIDs[xid] == nil:
   157  		uids := uid.NewSet()
   158  		lineID = uids.NewID()
   159  		g.lineIDs[xid] = map[int64]*uid.Set{yid: uids}
   160  	case g.lineIDs[xid][yid] == nil:
   161  		uids := uid.NewSet()
   162  		lineID = uids.NewID()
   163  		g.lineIDs[xid][yid] = uids
   164  	default:
   165  		lineID = g.lineIDs[xid][yid].NewID()
   166  	}
   167  	return Line{F: from, T: to, UID: lineID}
   168  }
   169  
   170  // NewNode returns a new unique Node to be added to g. The Node's ID does
   171  // not become valid in g until the Node is added to g.
   172  func (g *UndirectedGraph) NewNode() graph.Node {
   173  	if len(g.nodes) == 0 {
   174  		return Node(0)
   175  	}
   176  	if int64(len(g.nodes)) == uid.Max {
   177  		panic("simple: cannot allocate node: no slot")
   178  	}
   179  	return Node(g.nodeIDs.NewID())
   180  }
   181  
   182  // Node returns the node with the given ID if it exists in the graph,
   183  // and nil otherwise.
   184  func (g *UndirectedGraph) Node(id int64) graph.Node {
   185  	return g.nodes[id]
   186  }
   187  
   188  // Nodes returns all the nodes in the graph.
   189  //
   190  // The returned graph.Nodes is only valid until the next mutation of
   191  // the receiver.
   192  func (g *UndirectedGraph) Nodes() graph.Nodes {
   193  	if len(g.nodes) == 0 {
   194  		return graph.Empty
   195  	}
   196  	return iterator.NewNodes(g.nodes)
   197  }
   198  
   199  // NodeWithID returns a Node with the given ID if possible. If a graph.Node
   200  // is returned that is not already in the graph NodeWithID will return true
   201  // for new and the graph.Node must be added to the graph before use.
   202  func (g *UndirectedGraph) NodeWithID(id int64) (n graph.Node, new bool) {
   203  	n, ok := g.nodes[id]
   204  	if ok {
   205  		return n, false
   206  	}
   207  	return Node(id), true
   208  }
   209  
   210  // RemoveLine removes the line with the given end point and line Ids from the graph, leaving
   211  // the terminal nodes. If the line does not exist it is a no-op.
   212  func (g *UndirectedGraph) RemoveLine(fid, tid, id int64) {
   213  	if _, ok := g.nodes[fid]; !ok {
   214  		return
   215  	}
   216  	if _, ok := g.nodes[tid]; !ok {
   217  		return
   218  	}
   219  
   220  	delete(g.lines[fid][tid], id)
   221  	if len(g.lines[fid][tid]) == 0 {
   222  		delete(g.lines[fid], tid)
   223  	}
   224  	delete(g.lines[tid][fid], id)
   225  	if len(g.lines[tid][fid]) == 0 {
   226  		delete(g.lines[tid], fid)
   227  	}
   228  
   229  	xid := fid
   230  	yid := tid
   231  	if yid < xid {
   232  		xid, yid = yid, xid
   233  	}
   234  	g.lineIDs[xid][yid].Release(id)
   235  }
   236  
   237  // RemoveNode removes the node with the given ID from the graph, as well as any edges attached
   238  // to it. If the node is not in the graph it is a no-op.
   239  func (g *UndirectedGraph) RemoveNode(id int64) {
   240  	if _, ok := g.nodes[id]; !ok {
   241  		return
   242  	}
   243  	delete(g.nodes, id)
   244  
   245  	for from := range g.lines[id] {
   246  		delete(g.lines[from], id)
   247  	}
   248  	delete(g.lines, id)
   249  
   250  	g.nodeIDs.Release(id)
   251  }
   252  
   253  // SetLine adds l, a line from one node to another. If the nodes do not exist, they are added
   254  // and are set to the nodes of the line otherwise.
   255  func (g *UndirectedGraph) SetLine(l graph.Line) {
   256  	var (
   257  		from = l.From()
   258  		fid  = from.ID()
   259  		to   = l.To()
   260  		tid  = to.ID()
   261  		lid  = l.ID()
   262  	)
   263  
   264  	if _, ok := g.nodes[fid]; !ok {
   265  		g.AddNode(from)
   266  	} else {
   267  		g.nodes[fid] = from
   268  	}
   269  	if _, ok := g.nodes[tid]; !ok {
   270  		g.AddNode(to)
   271  	} else {
   272  		g.nodes[tid] = to
   273  	}
   274  
   275  	switch {
   276  	case g.lines[fid] == nil:
   277  		g.lines[fid] = map[int64]map[int64]graph.Line{tid: {lid: l}}
   278  	case g.lines[fid][tid] == nil:
   279  		g.lines[fid][tid] = map[int64]graph.Line{lid: l}
   280  	default:
   281  		g.lines[fid][tid][lid] = l
   282  	}
   283  	switch {
   284  	case g.lines[tid] == nil:
   285  		g.lines[tid] = map[int64]map[int64]graph.Line{fid: {lid: l}}
   286  	case g.lines[tid][fid] == nil:
   287  		g.lines[tid][fid] = map[int64]graph.Line{lid: l}
   288  	default:
   289  		g.lines[tid][fid][lid] = l
   290  	}
   291  
   292  	xid := fid
   293  	yid := tid
   294  	if yid < xid {
   295  		xid, yid = yid, xid
   296  	}
   297  	switch {
   298  	case g.lineIDs[xid] == nil:
   299  		uids := uid.NewSet()
   300  		g.lineIDs[xid] = map[int64]*uid.Set{yid: uids}
   301  	case g.lineIDs[xid][yid] == nil:
   302  		uids := uid.NewSet()
   303  		g.lineIDs[xid][yid] = uids
   304  	}
   305  	g.lineIDs[xid][yid].Use(lid)
   306  }