|
1 | 1 | package graph |
2 | 2 |
|
3 | | -import ( |
4 | | - "errors" |
5 | | - "fmt" |
6 | | -) |
7 | | - |
8 | 3 | type directed[K comparable, T any] struct { |
9 | | - hash Hash[K, T] |
10 | | - traits *Traits |
11 | | - store Store[K, T] |
| 4 | + graph[K, T] |
12 | 5 | } |
13 | 6 |
|
14 | 7 | func newDirected[K comparable, T any](hash Hash[K, T], traits *Traits, store Store[K, T]) *directed[K, T] { |
| 8 | + traits.IsDirected = true |
15 | 9 | return &directed[K, T]{ |
16 | | - hash: hash, |
17 | | - traits: traits, |
18 | | - store: store, |
19 | | - } |
20 | | -} |
21 | | - |
22 | | -func (d *directed[K, T]) Traits() *Traits { |
23 | | - return d.traits |
24 | | -} |
25 | | - |
26 | | -func (d *directed[K, T]) AddVertex(value T, options ...func(*VertexProperties)) error { |
27 | | - hash := d.hash(value) |
28 | | - properties := VertexProperties{ |
29 | | - Weight: 0, |
30 | | - Attributes: make(map[string]string), |
31 | | - } |
32 | | - |
33 | | - for _, option := range options { |
34 | | - option(&properties) |
| 10 | + graph: graph[K, T]{ |
| 11 | + hash: hash, |
| 12 | + traits: traits, |
| 13 | + store: store, |
| 14 | + }, |
35 | 15 | } |
36 | | - |
37 | | - return d.store.AddVertex(hash, value, properties) |
38 | 16 | } |
39 | 17 |
|
40 | 18 | func (d *directed[K, T]) AddVerticesFrom(g Graph[K, T]) error { |
41 | | - adjacencyMap, err := g.AdjacencyMap() |
42 | | - if err != nil { |
43 | | - return fmt.Errorf("failed to get adjacency map: %w", err) |
44 | | - } |
45 | | - |
46 | | - for hash := range adjacencyMap { |
47 | | - vertex, properties, err := g.VertexWithProperties(hash) |
48 | | - if err != nil { |
49 | | - return fmt.Errorf("failed to get vertex %v: %w", hash, err) |
50 | | - } |
51 | | - |
52 | | - if err = d.AddVertex(vertex, copyVertexProperties(properties)); err != nil { |
53 | | - return fmt.Errorf("failed to add vertex %v: %w", hash, err) |
54 | | - } |
55 | | - } |
56 | | - |
57 | | - return nil |
58 | | -} |
59 | | - |
60 | | -func (d *directed[K, T]) Vertex(hash K) (T, error) { |
61 | | - vertex, _, err := d.store.Vertex(hash) |
62 | | - return vertex, err |
63 | | -} |
64 | | - |
65 | | -func (d *directed[K, T]) VertexWithProperties(hash K) (T, VertexProperties, error) { |
66 | | - vertex, properties, err := d.store.Vertex(hash) |
67 | | - if err != nil { |
68 | | - return vertex, VertexProperties{}, err |
69 | | - } |
70 | | - |
71 | | - return vertex, properties, nil |
72 | | -} |
73 | | - |
74 | | -func (d *directed[K, T]) RemoveVertex(hash K) error { |
75 | | - return d.store.RemoveVertex(hash) |
76 | | -} |
77 | | - |
78 | | -func (d *directed[K, T]) AddEdge(sourceHash, targetHash K, options ...func(*EdgeProperties)) error { |
79 | | - _, _, err := d.store.Vertex(sourceHash) |
80 | | - if err != nil { |
81 | | - return fmt.Errorf("source vertex %v: %w", sourceHash, err) |
82 | | - } |
83 | | - |
84 | | - _, _, err = d.store.Vertex(targetHash) |
85 | | - if err != nil { |
86 | | - return fmt.Errorf("target vertex %v: %w", targetHash, err) |
87 | | - } |
88 | | - |
89 | | - if _, err := d.Edge(sourceHash, targetHash); !errors.Is(err, ErrEdgeNotFound) { |
90 | | - return ErrEdgeAlreadyExists |
91 | | - } |
| 19 | + other := tograph(g) |
| 20 | + return d.addVerticesFrom(&other) |
92 | 21 |
|
93 | | - // If the user opted in to preventing cycles, run a cycle check. |
94 | | - if d.traits.PreventCycles { |
95 | | - createsCycle, err := d.createsCycle(sourceHash, targetHash) |
96 | | - if err != nil { |
97 | | - return fmt.Errorf("check for cycles: %w", err) |
98 | | - } |
99 | | - if createsCycle { |
100 | | - return ErrEdgeCreatesCycle |
101 | | - } |
102 | | - } |
103 | | - |
104 | | - edge := Edge[K]{ |
105 | | - Source: sourceHash, |
106 | | - Target: targetHash, |
107 | | - Properties: EdgeProperties{ |
108 | | - Attributes: make(map[string]string), |
109 | | - }, |
110 | | - } |
111 | | - |
112 | | - for _, option := range options { |
113 | | - option(&edge.Properties) |
114 | | - } |
115 | | - |
116 | | - return d.addEdge(sourceHash, targetHash, edge) |
117 | 22 | } |
118 | 23 |
|
119 | 24 | func (d *directed[K, T]) AddEdgesFrom(g Graph[K, T]) error { |
120 | | - edges, err := g.Edges() |
121 | | - if err != nil { |
122 | | - return fmt.Errorf("failed to get edges: %w", err) |
123 | | - } |
124 | | - |
125 | | - for _, edge := range edges { |
126 | | - if err := d.AddEdge(copyEdge(edge)); err != nil { |
127 | | - return fmt.Errorf("failed to add (%v, %v): %w", edge.Source, edge.Target, err) |
128 | | - } |
129 | | - } |
130 | | - |
131 | | - return nil |
132 | | -} |
133 | | - |
134 | | -func (d *directed[K, T]) Edge(sourceHash, targetHash K) (Edge[T], error) { |
135 | | - edge, err := d.store.Edge(sourceHash, targetHash) |
136 | | - if err != nil { |
137 | | - return Edge[T]{}, err |
138 | | - } |
139 | | - |
140 | | - sourceVertex, _, err := d.store.Vertex(sourceHash) |
141 | | - if err != nil { |
142 | | - return Edge[T]{}, err |
143 | | - } |
144 | | - |
145 | | - targetVertex, _, err := d.store.Vertex(targetHash) |
146 | | - if err != nil { |
147 | | - return Edge[T]{}, err |
148 | | - } |
149 | | - |
150 | | - return Edge[T]{ |
151 | | - Source: sourceVertex, |
152 | | - Target: targetVertex, |
153 | | - Properties: EdgeProperties{ |
154 | | - Weight: edge.Properties.Weight, |
155 | | - Attributes: edge.Properties.Attributes, |
156 | | - Data: edge.Properties.Data, |
157 | | - }, |
158 | | - }, nil |
159 | | -} |
160 | | - |
161 | | -func (d *directed[K, T]) Edges() ([]Edge[K], error) { |
162 | | - return d.store.ListEdges() |
| 25 | + other := tograph(g) |
| 26 | + return d.addEdgesFrom(&other) |
163 | 27 | } |
164 | 28 |
|
165 | 29 | func (d *directed[K, T]) UpdateEdge(source, target K, options ...func(properties *EdgeProperties)) error { |
166 | | - existingEdge, err := d.store.Edge(source, target) |
167 | | - if err != nil { |
168 | | - return err |
169 | | - } |
170 | | - |
171 | | - for _, option := range options { |
172 | | - option(&existingEdge.Properties) |
173 | | - } |
174 | | - |
175 | | - return d.store.UpdateEdge(source, target, existingEdge) |
176 | | -} |
177 | | - |
178 | | -func (d *directed[K, T]) RemoveEdge(source, target K) error { |
179 | | - if _, err := d.Edge(source, target); err != nil { |
180 | | - return err |
181 | | - } |
182 | | - |
183 | | - if err := d.store.RemoveEdge(source, target); err != nil { |
184 | | - return fmt.Errorf("failed to remove edge from %v to %v: %w", source, target, err) |
185 | | - } |
186 | | - |
187 | | - return nil |
188 | | -} |
189 | | - |
190 | | -func (d *directed[K, T]) AdjacencyMap() (map[K]map[K]Edge[K], error) { |
191 | | - vertices, err := d.store.ListVertices() |
192 | | - if err != nil { |
193 | | - return nil, fmt.Errorf("failed to list vertices: %w", err) |
194 | | - } |
| 30 | + _, err := d.updateEdge(source, target, options...) |
195 | 31 |
|
196 | | - edges, err := d.store.ListEdges() |
197 | | - if err != nil { |
198 | | - return nil, fmt.Errorf("failed to list edges: %w", err) |
199 | | - } |
200 | | - |
201 | | - m := make(map[K]map[K]Edge[K], len(vertices)) |
202 | | - |
203 | | - for _, vertex := range vertices { |
204 | | - m[vertex] = make(map[K]Edge[K]) |
205 | | - } |
206 | | - |
207 | | - for _, edge := range edges { |
208 | | - m[edge.Source][edge.Target] = edge |
209 | | - } |
210 | | - |
211 | | - return m, nil |
212 | | -} |
213 | | - |
214 | | -func (d *directed[K, T]) PredecessorMap() (map[K]map[K]Edge[K], error) { |
215 | | - vertices, err := d.store.ListVertices() |
216 | | - if err != nil { |
217 | | - return nil, fmt.Errorf("failed to list vertices: %w", err) |
218 | | - } |
219 | | - |
220 | | - edges, err := d.store.ListEdges() |
221 | | - if err != nil { |
222 | | - return nil, fmt.Errorf("failed to list edges: %w", err) |
223 | | - } |
224 | | - |
225 | | - m := make(map[K]map[K]Edge[K], len(vertices)) |
226 | | - |
227 | | - for _, vertex := range vertices { |
228 | | - m[vertex] = make(map[K]Edge[K]) |
229 | | - } |
230 | | - |
231 | | - for _, edge := range edges { |
232 | | - if _, ok := m[edge.Target]; !ok { |
233 | | - m[edge.Target] = make(map[K]Edge[K]) |
234 | | - } |
235 | | - m[edge.Target][edge.Source] = edge |
236 | | - } |
237 | | - |
238 | | - return m, nil |
239 | | -} |
240 | | - |
241 | | -func (d *directed[K, T]) addEdge(sourceHash, targetHash K, edge Edge[K]) error { |
242 | | - return d.store.AddEdge(sourceHash, targetHash, edge) |
| 32 | + return err |
243 | 33 | } |
244 | 34 |
|
245 | 35 | func (d *directed[K, T]) Clone() (Graph[K, T], error) { |
246 | | - traits := &Traits{ |
247 | | - IsDirected: d.traits.IsDirected, |
248 | | - IsAcyclic: d.traits.IsAcyclic, |
249 | | - IsWeighted: d.traits.IsWeighted, |
250 | | - IsRooted: d.traits.IsRooted, |
251 | | - PreventCycles: d.traits.PreventCycles, |
252 | | - } |
253 | | - |
254 | | - clone := &directed[K, T]{ |
255 | | - hash: d.hash, |
256 | | - traits: traits, |
257 | | - store: newMemoryStore[K, T](), |
258 | | - } |
259 | | - |
260 | | - if err := clone.AddVerticesFrom(d); err != nil { |
261 | | - return nil, fmt.Errorf("failed to add vertices: %w", err) |
262 | | - } |
263 | | - |
264 | | - if err := clone.AddEdgesFrom(d); err != nil { |
265 | | - return nil, fmt.Errorf("failed to add edges: %w", err) |
266 | | - } |
267 | | - |
268 | | - return clone, nil |
269 | | -} |
270 | | - |
271 | | -func (d *directed[K, T]) Order() (int, error) { |
272 | | - return d.store.VertexCount() |
273 | | -} |
274 | | - |
275 | | -func (d *directed[K, T]) Size() (int, error) { |
276 | | - return d.store.EdgeCount() |
277 | | -} |
278 | | - |
279 | | -func (d *directed[K, T]) edgesAreEqual(a, b Edge[T]) bool { |
280 | | - aSourceHash := d.hash(a.Source) |
281 | | - aTargetHash := d.hash(a.Target) |
282 | | - bSourceHash := d.hash(b.Source) |
283 | | - bTargetHash := d.hash(b.Target) |
284 | | - |
285 | | - return aSourceHash == bSourceHash && aTargetHash == bTargetHash |
286 | | -} |
287 | | - |
288 | | -func (d *directed[K, T]) createsCycle(source, target K) (bool, error) { |
289 | | - // If the underlying store implements CreatesCycle, use that fast path. |
290 | | - if cc, ok := d.store.(interface { |
291 | | - CreatesCycle(source, target K) (bool, error) |
292 | | - }); ok { |
293 | | - return cc.CreatesCycle(source, target) |
| 36 | + gclone, err := d.clone() |
| 37 | + if err != nil { |
| 38 | + return nil, err |
294 | 39 | } |
295 | 40 |
|
296 | | - // Slow path. |
297 | | - return CreatesCycle(Graph[K, T](d), source, target) |
298 | | -} |
299 | | - |
300 | | -// copyEdge returns an argument list suitable for the Graph.AddEdge method. This |
301 | | -// argument list is derived from the given edge, hence the name copyEdge. |
302 | | -// |
303 | | -// The last argument is a custom functional option that sets the edge properties |
304 | | -// to the properties of the original edge. |
305 | | -func copyEdge[K comparable](edge Edge[K]) (K, K, func(properties *EdgeProperties)) { |
306 | | - copyProperties := func(p *EdgeProperties) { |
307 | | - for k, v := range edge.Properties.Attributes { |
308 | | - p.Attributes[k] = v |
309 | | - } |
310 | | - p.Weight = edge.Properties.Weight |
311 | | - p.Data = edge.Properties.Data |
| 41 | + dir := &directed[K, T]{ |
| 42 | + graph: *gclone, |
312 | 43 | } |
313 | | - |
314 | | - return edge.Source, edge.Target, copyProperties |
| 44 | + return dir, nil |
315 | 45 | } |
0 commit comments