github.com/gopherd/gonum@v0.0.4/graph/graphs/gen/gen.go (about)

     1  // Copyright ©2015 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 gen
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/gopherd/gonum/graph"
    11  )
    12  
    13  // GraphBuilder is a graph that can have nodes and edges added.
    14  type GraphBuilder interface {
    15  	HasEdgeBetween(xid, yid int64) bool
    16  	graph.Builder
    17  }
    18  
    19  func abs(a int) int {
    20  	if a < 0 {
    21  		return -a
    22  	}
    23  	return a
    24  }
    25  
    26  // NodeIDGraphBuild is a graph that can create new nodes with
    27  // specified IDs.
    28  type NodeIDGraphBuilder interface {
    29  	graph.Builder
    30  	graph.NodeWithIDer
    31  }
    32  
    33  // IDer is a mapping from an index to a node ID.
    34  type IDer interface {
    35  	// Len returns the length of the set of node IDs.
    36  	Len() int
    37  
    38  	// ID returns the ID of the indexed node.
    39  	// ID must be a bijective function. No check
    40  	// is made for this property.
    41  	ID(int) int64
    42  }
    43  
    44  // IDRange is an IDer that provides a set of IDs in [First, Last].
    45  type IDRange struct{ First, Last int64 }
    46  
    47  func (r IDRange) Len() int       { return int(r.Last - r.First + 1) }
    48  func (r IDRange) ID(i int) int64 { return r.First + int64(i) }
    49  
    50  // IDSet is an IDer providing an explicit set of IDs.
    51  type IDSet []int64
    52  
    53  func (s IDSet) Len() int       { return len(s) }
    54  func (s IDSet) ID(i int) int64 { return s[i] }
    55  
    56  // Complete constructs a complete graph in dst using nodes with the given IDs.
    57  // If any ID appears twice in ids, Complete will panic.
    58  func Complete(dst NodeIDGraphBuilder, ids IDer) {
    59  	switch ids.Len() {
    60  	case 0:
    61  		return
    62  	case 1:
    63  		u, new := dst.NodeWithID(ids.ID(0))
    64  		if new {
    65  			dst.AddNode(u)
    66  		}
    67  		return
    68  	}
    69  	for i := 0; i < ids.Len(); i++ {
    70  		uid := ids.ID(i)
    71  		u, _ := dst.NodeWithID(uid)
    72  		for j := i + 1; j < ids.Len(); j++ {
    73  			vid := ids.ID(j)
    74  			if uid == vid {
    75  				panic(fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", i, j, uid))
    76  			}
    77  			v, _ := dst.NodeWithID(vid)
    78  			dst.SetEdge(dst.NewEdge(u, v))
    79  		}
    80  	}
    81  }
    82  
    83  // Cycle constructs a cycle in dst using the node IDs in cycle.
    84  // If dst is a directed graph, edges are directed from earlier nodes to later
    85  // nodes in cycle. If any ID appears twice in cycle, Cycle will panic.
    86  func Cycle(dst NodeIDGraphBuilder, cycle IDer) {
    87  	switch cycle.Len() {
    88  	case 0:
    89  		return
    90  	case 1:
    91  		u, new := dst.NodeWithID(cycle.ID(0))
    92  		if new {
    93  			dst.AddNode(u)
    94  		}
    95  		return
    96  	}
    97  	err := check(cycle)
    98  	if err != nil {
    99  		panic(err)
   100  	}
   101  	cycleNoCheck(dst, cycle)
   102  }
   103  
   104  func cycleNoCheck(dst NodeIDGraphBuilder, cycle IDer) {
   105  	for i := 0; i < cycle.Len(); i++ {
   106  		uid := cycle.ID(i)
   107  		vid := cycle.ID((i + 1) % cycle.Len())
   108  		u, _ := dst.NodeWithID(uid)
   109  		v, _ := dst.NodeWithID(vid)
   110  		dst.SetEdge(dst.NewEdge(u, v))
   111  	}
   112  }
   113  
   114  // Path constructs a path graph in dst with
   115  // If dst is a directed graph, edges are directed from earlier nodes to later
   116  // nodes in path. If any ID appears twice in path, Path will panic.
   117  func Path(dst NodeIDGraphBuilder, path IDer) {
   118  	switch path.Len() {
   119  	case 0:
   120  		return
   121  	case 1:
   122  		u, new := dst.NodeWithID(path.ID(0))
   123  		if new {
   124  			dst.AddNode(u)
   125  		}
   126  		return
   127  	}
   128  	err := check(path)
   129  	if err != nil {
   130  		panic(err)
   131  	}
   132  	for i := 0; i < path.Len()-1; i++ {
   133  		uid := path.ID(i)
   134  		vid := path.ID(i + 1)
   135  		u, _ := dst.NodeWithID(uid)
   136  		v, _ := dst.NodeWithID(vid)
   137  		dst.SetEdge(dst.NewEdge(u, v))
   138  	}
   139  }
   140  
   141  // Star constructs a star graph in dst with edges between the center node ID to
   142  // node with IDs specified in leaves.
   143  // If dst is a directed graph, edges are directed from the center node to the
   144  // leaves. If any ID appears twice in leaves and center, Star will panic.
   145  func Star(dst NodeIDGraphBuilder, center int64, leaves IDer) {
   146  	c, new := dst.NodeWithID(center)
   147  	if new {
   148  		dst.AddNode(c)
   149  	}
   150  	if leaves.Len() == 0 {
   151  		return
   152  	}
   153  	err := check(leaves, center)
   154  	if err != nil {
   155  		panic(err)
   156  	}
   157  	for i := 0; i < leaves.Len(); i++ {
   158  		id := leaves.ID(i)
   159  		n, _ := dst.NodeWithID(id)
   160  		dst.SetEdge(dst.NewEdge(c, n))
   161  	}
   162  }
   163  
   164  // Wheel constructs a wheel graph in dst with edges from the center
   165  // node ID to node with IDs specified in cycle and between nodes with IDs
   166  // adjacent in the cycle.
   167  // If dst is a directed graph, edges are directed from the center node to the
   168  // cycle and from earlier nodes to later nodes in cycle. If any ID appears
   169  // twice in cycle and center, Wheel will panic.
   170  func Wheel(dst NodeIDGraphBuilder, center int64, cycle IDer) {
   171  	Star(dst, center, cycle)
   172  	if cycle.Len() <= 1 {
   173  		return
   174  	}
   175  	cycleNoCheck(dst, cycle)
   176  }
   177  
   178  // Tree constructs an n-ary tree breadth-first in dst with the given fan-out, n.
   179  // If the number of nodes does not equal \sum_{i=0}^h n^i, where h is an integer
   180  // indicating the height of the tree, a partial tree will be constructed with not
   181  // all nodes having zero or n children, and not all leaves being h from the root.
   182  // If the number of nodes is greater than one, n must be non-zero and
   183  // less than the number of nodes, otherwise Tree will panic.
   184  // If dst is a directed graph, edges are directed from the root to the leaves.
   185  // If any ID appears more than once in nodes, Tree will panic.
   186  func Tree(dst NodeIDGraphBuilder, n int, nodes IDer) {
   187  	switch nodes.Len() {
   188  	case 0:
   189  		return
   190  	case 1:
   191  		if u, new := dst.NodeWithID(nodes.ID(0)); new {
   192  			dst.AddNode(u)
   193  		}
   194  		return
   195  	}
   196  
   197  	if n < 1 || nodes.Len() <= n {
   198  		panic("gen: invalid fan-out")
   199  	}
   200  
   201  	err := check(nodes)
   202  	if err != nil {
   203  		panic(err)
   204  	}
   205  
   206  	j := 0
   207  	for i := 0; j < nodes.Len(); i++ {
   208  		u, _ := dst.NodeWithID(nodes.ID(i))
   209  		for j = n*i + 1; j <= n*i+n && j < nodes.Len(); j++ {
   210  			v, _ := dst.NodeWithID(nodes.ID(j))
   211  			dst.SetEdge(dst.NewEdge(u, v))
   212  		}
   213  	}
   214  }
   215  
   216  // check confirms that no node ID exists more than once in ids and extra.
   217  func check(ids IDer, extra ...int64) error {
   218  	seen := make(map[int64]int, ids.Len()+len(extra))
   219  	for j := 0; j < ids.Len(); j++ {
   220  		uid := ids.ID(j)
   221  		if prev, exists := seen[uid]; exists {
   222  			return fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", prev, j, uid)
   223  		}
   224  		seen[uid] = j
   225  	}
   226  	lenIDs := ids.Len()
   227  	for j, uid := range extra {
   228  		if prev, exists := seen[uid]; exists {
   229  			if prev < lenIDs {
   230  				if len(extra) == 1 {
   231  					return fmt.Errorf("gen: node ID collision i=%d with extra: id=%d", prev, uid)
   232  				}
   233  				return fmt.Errorf("gen: node ID collision i=%d with extra j=%d: id=%d", prev, j, uid)
   234  			}
   235  			prev -= lenIDs
   236  			return fmt.Errorf("gen: extra node ID collision i=%d j=%d: id=%d", prev, j, uid)
   237  		}
   238  		seen[uid] = j + lenIDs
   239  	}
   240  	return nil
   241  }