From 50f7cda0fec7a92dd3b1ee9f3d60b5eeb96418a0 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Thu, 24 Oct 2019 01:10:49 -0700 Subject: [PATCH] structure for opting out of edge data --- Graph/DirectedGraph.cs | 2 +- Graph/DirectedMultiGraph.cs | 2 +- Graph/DirectedWeightedGraph.cs | 2 +- Graph/DirectedWeightedMultiGraph.cs | 2 +- Graph/Extensions/Unweighted.cs | 18 ++++ Graph/Extensions/Weighted.cs | 18 ++++ Graph/Extensions/WeightedMulti.cs | 18 ++++ Graph/GraphBuilder.cs | 35 +++++++ Graph/Interfaces/IUnweightedGraph.cs | 7 ++ Graph/Interfaces/IWeightedGraph.cs | 7 ++ Graph/Interfaces/IWeightedMultiGraph.cs | 7 ++ Graph/UndirectedGraph.cs | 2 +- Graph/UndirectedWeightedGraph.cs | 2 +- Graph/Unit.cs | 4 + README.md | 2 + test/GraphBuilder.cs | 126 ++++++++++++++++++++++++ 16 files changed, 248 insertions(+), 6 deletions(-) create mode 100644 Graph/Extensions/Unweighted.cs create mode 100644 Graph/Extensions/Weighted.cs create mode 100644 Graph/Extensions/WeightedMulti.cs create mode 100644 Graph/GraphBuilder.cs create mode 100644 Graph/Interfaces/IUnweightedGraph.cs create mode 100644 Graph/Interfaces/IWeightedGraph.cs create mode 100644 Graph/Interfaces/IWeightedMultiGraph.cs create mode 100644 Graph/Unit.cs create mode 100644 test/GraphBuilder.cs diff --git a/Graph/DirectedGraph.cs b/Graph/DirectedGraph.cs index 4519d96..bebde50 100644 --- a/Graph/DirectedGraph.cs +++ b/Graph/DirectedGraph.cs @@ -5,7 +5,7 @@ using Collections.Pooled; namespace MoonTools.Core.Graph { - public class DirectedGraph : SimpleGraph where TNode : IEquatable + public class DirectedGraph : SimpleGraph, IUnweightedGraph where TNode : IEquatable { private class SimpleCycleComparer : IEqualityComparer> { diff --git a/Graph/DirectedMultiGraph.cs b/Graph/DirectedMultiGraph.cs index 1890a83..89a4aa6 100644 --- a/Graph/DirectedMultiGraph.cs +++ b/Graph/DirectedMultiGraph.cs @@ -1,6 +1,6 @@ namespace MoonTools.Core.Graph { - public class DirectedMultiGraph : MultiGraph where TNode : System.IEquatable + public class DirectedMultiGraph : MultiGraph, IUnweightedGraph where TNode : System.IEquatable { public void AddEdge(TNode v, TNode u, TEdgeData edgeData) { diff --git a/Graph/DirectedWeightedGraph.cs b/Graph/DirectedWeightedGraph.cs index 812fc36..48c606f 100644 --- a/Graph/DirectedWeightedGraph.cs +++ b/Graph/DirectedWeightedGraph.cs @@ -6,7 +6,7 @@ using MoreLinq; namespace MoonTools.Core.Graph { - public class DirectedWeightedGraph : SimpleGraph where TNode : System.IEquatable + public class DirectedWeightedGraph : SimpleGraph, IWeightedGraph where TNode : System.IEquatable { protected Dictionary<(TNode, TNode), int> weights = new Dictionary<(TNode, TNode), int>(); diff --git a/Graph/DirectedWeightedMultiGraph.cs b/Graph/DirectedWeightedMultiGraph.cs index cbced37..3aa7b45 100644 --- a/Graph/DirectedWeightedMultiGraph.cs +++ b/Graph/DirectedWeightedMultiGraph.cs @@ -6,7 +6,7 @@ using MoreLinq; namespace MoonTools.Core.Graph { - public class DirectedWeightedMultiGraph : MultiGraph where TNode : IEquatable + public class DirectedWeightedMultiGraph : MultiGraph, IWeightedMultiGraph where TNode : IEquatable { protected Dictionary weights = new Dictionary(); diff --git a/Graph/Extensions/Unweighted.cs b/Graph/Extensions/Unweighted.cs new file mode 100644 index 0000000..1fe0e6d --- /dev/null +++ b/Graph/Extensions/Unweighted.cs @@ -0,0 +1,18 @@ +namespace MoonTools.Core.Graph.Extensions +{ + public static class UnweightedExtensions + { + public static void AddEdge(this TGraph g, TNode v, TNode u) where TGraph : Graph, IUnweightedGraph where TNode : System.IEquatable + { + g.AddEdge(v, u, default(Unit)); + } + + public static void AddEdges(this TGraph g, params (TNode, TNode)[] edges) where TGraph : Graph, IUnweightedGraph where TNode : System.IEquatable + { + foreach (var (v, u) in edges) + { + g.AddEdge(v, u, default(Unit)); + } + } + } +} \ No newline at end of file diff --git a/Graph/Extensions/Weighted.cs b/Graph/Extensions/Weighted.cs new file mode 100644 index 0000000..c7f576b --- /dev/null +++ b/Graph/Extensions/Weighted.cs @@ -0,0 +1,18 @@ +namespace MoonTools.Core.Graph.Extensions +{ + public static class WeightedExtensions + { + public static void AddEdge(this TGraph g, TNode v, TNode u, int weight) where TGraph : Graph, IWeightedGraph where TNode : System.IEquatable + { + g.AddEdge(v, u, weight, default(Unit)); + } + + public static void AddEdges(this TGraph g, params (TNode, TNode, int)[] edges) where TGraph : Graph, IWeightedGraph where TNode : System.IEquatable + { + foreach (var (v, u, weight) in edges) + { + g.AddEdge(v, u, weight, default(Unit)); + } + } + } +} \ No newline at end of file diff --git a/Graph/Extensions/WeightedMulti.cs b/Graph/Extensions/WeightedMulti.cs new file mode 100644 index 0000000..400b030 --- /dev/null +++ b/Graph/Extensions/WeightedMulti.cs @@ -0,0 +1,18 @@ +namespace MoonTools.Core.Graph.Extensions +{ + public static class WeightedMultiExtensions + { + public static void AddEdge(this TGraph g, TNode v, TNode u, int weight) where TGraph : Graph, IWeightedMultiGraph where TNode : System.IEquatable + { + g.AddEdge(v, u, weight, default(Unit)); + } + + public static void AddEdges(this TGraph g, params (TNode, TNode, int)[] edges) where TGraph : Graph, IWeightedMultiGraph where TNode : System.IEquatable + { + foreach (var (v, u, weight) in edges) + { + g.AddEdge(v, u, weight, default(Unit)); + } + } + } +} \ No newline at end of file diff --git a/Graph/GraphBuilder.cs b/Graph/GraphBuilder.cs new file mode 100644 index 0000000..d06e848 --- /dev/null +++ b/Graph/GraphBuilder.cs @@ -0,0 +1,35 @@ +namespace MoonTools.Core.Graph.Extensions +{ + public static class GraphBuilder + { + public static DirectedGraph DirectedGraph() where TNode : System.IEquatable + { + return new DirectedGraph(); + } + + public static DirectedMultiGraph DirectedMultiGraph() where TNode : System.IEquatable + { + return new DirectedMultiGraph(); + } + + public static DirectedWeightedGraph DirectedWeightedGraph() where TNode : System.IEquatable + { + return new DirectedWeightedGraph(); + } + + public static DirectedWeightedMultiGraph DirectedWeightedMultiGraph() where TNode : System.IEquatable + { + return new DirectedWeightedMultiGraph(); + } + + public static UndirectedGraph UndirectedGraph() where TNode : System.IEquatable + { + return new UndirectedGraph(); + } + + public static UndirectedWeightedGraph UndirectedWeightedGraph() where TNode : System.IEquatable + { + return new UndirectedWeightedGraph(); + } + } +} \ No newline at end of file diff --git a/Graph/Interfaces/IUnweightedGraph.cs b/Graph/Interfaces/IUnweightedGraph.cs new file mode 100644 index 0000000..768c808 --- /dev/null +++ b/Graph/Interfaces/IUnweightedGraph.cs @@ -0,0 +1,7 @@ +namespace MoonTools.Core.Graph +{ + public interface IUnweightedGraph + { + void AddEdge(TNode v, TNode u, TEdgeData edgeData); + } +} \ No newline at end of file diff --git a/Graph/Interfaces/IWeightedGraph.cs b/Graph/Interfaces/IWeightedGraph.cs new file mode 100644 index 0000000..b37cb73 --- /dev/null +++ b/Graph/Interfaces/IWeightedGraph.cs @@ -0,0 +1,7 @@ +namespace MoonTools.Core.Graph +{ + public interface IWeightedGraph + { + void AddEdge(TNode v, TNode u, int weight, TEdgeData edgeData); + } +} \ No newline at end of file diff --git a/Graph/Interfaces/IWeightedMultiGraph.cs b/Graph/Interfaces/IWeightedMultiGraph.cs new file mode 100644 index 0000000..8af2ceb --- /dev/null +++ b/Graph/Interfaces/IWeightedMultiGraph.cs @@ -0,0 +1,7 @@ +namespace MoonTools.Core.Graph +{ + public interface IWeightedMultiGraph + { + System.Guid AddEdge(TNode v, TNode u, int weight, TEdgeData edgeData); + } +} \ No newline at end of file diff --git a/Graph/UndirectedGraph.cs b/Graph/UndirectedGraph.cs index 41568a7..986be2d 100644 --- a/Graph/UndirectedGraph.cs +++ b/Graph/UndirectedGraph.cs @@ -4,7 +4,7 @@ using Collections.Pooled; namespace MoonTools.Core.Graph { - public class UndirectedGraph : DirectedGraph where TNode : System.IEquatable + public class UndirectedGraph : DirectedGraph, IUnweightedGraph where TNode : System.IEquatable { enum Color { White, Gray, Black } diff --git a/Graph/UndirectedWeightedGraph.cs b/Graph/UndirectedWeightedGraph.cs index a8f89e3..ddc1bed 100644 --- a/Graph/UndirectedWeightedGraph.cs +++ b/Graph/UndirectedWeightedGraph.cs @@ -1,6 +1,6 @@ namespace MoonTools.Core.Graph { - public class UndirectedWeightedGraph : DirectedWeightedGraph where TNode : System.IEquatable + public class UndirectedWeightedGraph : DirectedWeightedGraph, IWeightedGraph where TNode : System.IEquatable { public override void AddEdge(TNode v, TNode u, int weight, TEdgeData edgeData) { diff --git a/Graph/Unit.cs b/Graph/Unit.cs new file mode 100644 index 0000000..dc8c373 --- /dev/null +++ b/Graph/Unit.cs @@ -0,0 +1,4 @@ +namespace MoonTools.Core.Graph +{ + public struct Unit { } +} diff --git a/README.md b/README.md index ff6afcf..d4e593e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ A GC-friendly graph theory library for C# intended for use with games. ### TODO +* change Neighbors tests to use Equal instead of Contains +* change Edge id from a Guid to an integer index on the edge * Prim Minimum Spanning Tree * Kruskal Minimum Spanning Tree * Undirected Weighted Multigraph \ No newline at end of file diff --git a/test/GraphBuilder.cs b/test/GraphBuilder.cs new file mode 100644 index 0000000..ceeda74 --- /dev/null +++ b/test/GraphBuilder.cs @@ -0,0 +1,126 @@ +using NUnit.Framework; +using FluentAssertions; + +using MoonTools.Core.Graph.Extensions; + +namespace Tests +{ + public class GraphBuilderTests + { + [Test] + public void DirectedGraph() + { + var directedGraph = GraphBuilder.DirectedGraph(); + directedGraph.AddNodes(1, 2, 3); + directedGraph.AddEdge(1, 2); + directedGraph.AddEdges( + (2, 1), + (1, 3) + ); + + directedGraph.Neighbors(1).Should().Equal(2, 3); + directedGraph.Neighbors(2).Should().Equal(1); + } + + [Test] + public void DirectedMultiGraph() + { + var directedMultiGraph = GraphBuilder.DirectedMultiGraph(); + directedMultiGraph.AddNodes(1, 2, 3, 4); + directedMultiGraph.AddEdge(1, 2); + directedMultiGraph.AddEdges( + (2, 3), + (3, 4), + (1, 2) + ); + + directedMultiGraph.Neighbors(1).Should().Equal(2); + directedMultiGraph.Neighbors(2).Should().Equal(3); + directedMultiGraph.Neighbors(3).Should().Equal(4); + directedMultiGraph.EdgeIDs(1, 2).Should().HaveCount(2); + } + + [Test] + public void DirectedWeightedGraph() + { + var directedWeightedGraph = GraphBuilder.DirectedWeightedGraph(); + directedWeightedGraph.AddNodes(1, 2, 3, 4); + directedWeightedGraph.AddEdge(1, 2, 10); + directedWeightedGraph.AddEdges( + (2, 3, 5), + (3, 4, 1), + (4, 2, 2) + ); + + directedWeightedGraph.Neighbors(1).Should().Equal(2); + directedWeightedGraph.Neighbors(2).Should().Equal(3); + directedWeightedGraph.Neighbors(3).Should().Equal(4); + directedWeightedGraph.Neighbors(4).Should().Equal(2); + directedWeightedGraph.Weight(1, 2).Should().Be(10); + directedWeightedGraph.Weight(2, 3).Should().Be(5); + directedWeightedGraph.Weight(3, 4).Should().Be(1); + directedWeightedGraph.Weight(4, 2).Should().Be(2); + } + + [Test] + public void DirectedWeightedMultiGraph() + { + var directedWeightedMultiGraph = GraphBuilder.DirectedWeightedMultiGraph(); + directedWeightedMultiGraph.AddNodes(1, 2, 3, 4); + directedWeightedMultiGraph.AddEdge(1, 2, 10); + directedWeightedMultiGraph.AddEdges( + (1, 2, 4), + (2, 3, 5), + (3, 4, 1), + (4, 2, 2) + ); + + directedWeightedMultiGraph.Neighbors(1).Should().Equal(2); + directedWeightedMultiGraph.Neighbors(2).Should().Equal(3); + directedWeightedMultiGraph.Neighbors(3).Should().Equal(4); + directedWeightedMultiGraph.Neighbors(4).Should().Equal(2); + directedWeightedMultiGraph.Weights(1, 2).Should().Equal(10, 4); + directedWeightedMultiGraph.Weights(2, 3).Should().Equal(5); + directedWeightedMultiGraph.Weights(3, 4).Should().Equal(1); + directedWeightedMultiGraph.Weights(4, 2).Should().Equal(2); + } + + [Test] + public void UndirectedGraph() + { + var undirectedGraph = GraphBuilder.UndirectedGraph(); + undirectedGraph.AddNodes(1, 2, 3, 4); + undirectedGraph.AddEdge(1, 2); + undirectedGraph.AddEdges( + (2, 3), + (1, 4) + ); + + undirectedGraph.Neighbors(1).Should().Equal(2, 4); + undirectedGraph.Neighbors(2).Should().Equal(1, 3); + undirectedGraph.Neighbors(3).Should().Equal(2); + undirectedGraph.Neighbors(4).Should().Equal(1); + } + + [Test] + public void UndirectedWeightedGraph() + { + var undirectedWeightedGraph = GraphBuilder.UndirectedWeightedGraph(); + undirectedWeightedGraph.AddNodes(1, 2, 3, 4); + undirectedWeightedGraph.AddEdge(1, 2, 10); + undirectedWeightedGraph.AddEdges( + (2, 3, 4), + (1, 4, 2) + ); + + undirectedWeightedGraph.Neighbors(1).Should().Equal(2, 4); + undirectedWeightedGraph.Neighbors(2).Should().Equal(1, 3); + undirectedWeightedGraph.Neighbors(3).Should().Equal(2); + undirectedWeightedGraph.Neighbors(4).Should().Equal(1); + undirectedWeightedGraph.Weight(1, 2).Should().Be(10); + undirectedWeightedGraph.Weight(2, 3).Should().Be(4); + undirectedWeightedGraph.Weight(3, 2).Should().Be(4); + undirectedWeightedGraph.Weight(4, 1).Should().Be(2); + } + } +} \ No newline at end of file