diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4094210..5d32dda 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -4,23 +4,38 @@ "tools": { "dotnet-format": { "version": "5.1.250801", - "commands": ["dotnet-format"], + "commands": [ + "dotnet-format" + ], "rollForward": false }, "dotnet-script": { "version": "1.4.0", - "commands": ["dotnet-script"], + "commands": [ + "dotnet-script" + ], "rollForward": false }, "csharpier": { "version": "0.29.2", - "commands": ["dotnet-csharpier"], + "commands": [ + "dotnet-csharpier" + ], "rollForward": false }, "husky": { "version": "0.6.1", - "commands": ["husky"], + "commands": [ + "husky" + ], + "rollForward": false + }, + "fantomas": { + "version": "6.3.16", + "commands": [ + "fantomas" + ], "rollForward": false } } -} +} \ No newline at end of file diff --git a/SharpGraph.Builder/Compiler.fs b/SharpGraph.Builder/Compiler.fs index 81030ae..63bd2b5 100644 --- a/SharpGraph.Builder/Compiler.fs +++ b/SharpGraph.Builder/Compiler.fs @@ -1,8 +1,9 @@ namespace SharpGraph.Builder -/// -/// -/// +/// +/// Compiler module to compiel expressions representing graphs into +/// graph objects. +/// module Compiler = open SharpGraph open Microsoft.FSharp.Collections @@ -121,28 +122,19 @@ module Compiler = | UnaryExp(e, op) -> HandleUnaryExp e op | BinaryOperatorExp(op, e1, e2) -> BinaryOperatorExp op e1 e2 - /// - /// - /// + let Parse code = - System.Diagnostics.Debug.WriteLine("BEGIN PARSING") - match runParserOnString (pExpression .>> eof) () "Tokamak reaction stream" code with + match runParserOnString (pExpression .>> eof) () "Graph compile stream" code with | Success(result, _, _) -> - System.Diagnostics.Debug.WriteLine("FINISH PARSING") result | Failure(msg, _, _) -> - System.Diagnostics.Debug.WriteLine("Error " + msg + "Reactor core containment failed!") failwith msg + /// + /// Compiles the given string into a Graph object. + /// + ///The input string to build the graph + ///Graph object let Compile (text: string) = - let result = HandleExpression(Parse text) - - System.Console.WriteLine( - "graph number of nodes " - + string (result.GetNodes().Count) - + " and edges: " - + string (result.GetEdges().Count) - ) - - result + HandleExpression(Parse text) diff --git a/SharpGraph.Builder/SharpGraph.Builder.fsproj b/SharpGraph.Builder/SharpGraph.Builder.fsproj index 7865310..ce2985b 100644 --- a/SharpGraph.Builder/SharpGraph.Builder.fsproj +++ b/SharpGraph.Builder/SharpGraph.Builder.fsproj @@ -3,7 +3,7 @@ SharpGraphLib.Builder net8.0 README.md - 1.0.3 + 1.0.4 Jonathan Hough Jonathan Hough true diff --git a/SharpGraph.Tests/test/CycleTest.cs b/SharpGraph.Tests/test/CycleTest.cs index d9d57fc..aed00a5 100644 --- a/SharpGraph.Tests/test/CycleTest.cs +++ b/SharpGraph.Tests/test/CycleTest.cs @@ -3,13 +3,6 @@ // Copyright Licensed under the MIT license. // See LICENSE file in the samples root for full license information. // - -using System; -// -// Copyright (C) 2023 Jonathan Hough. -// Copyright Licensed under the MIT license. -// See LICENSE file in the samples root for full license information. -// using Xunit; namespace SharpGraph diff --git a/SharpGraph.Tests/test/GraphContractionTest.cs b/SharpGraph.Tests/test/GraphContractionTest.cs new file mode 100644 index 0000000..33e4630 --- /dev/null +++ b/SharpGraph.Tests/test/GraphContractionTest.cs @@ -0,0 +1,60 @@ +// +// Copyright (C) 2023 Jonathan Hough. +// Copyright Licensed under the MIT license. +// See LICENSE file in the samples root for full license information. +// + +using System; +using System.Collections.Generic; +using Xunit; + +namespace SharpGraph +{ + public class GraphContractionTest + { + [Theory] + [InlineData(6, "1", "2", 5, 10, 4)] + [InlineData(10, "2", "6", 9, 36, 8)] + public void TestContractEdgeFromComplete( + uint totalNodeCount, + string nodeToKeep, + string nodeToRemove, + int expectedNumberOfNodes, + int expectedNumberOfEdges, + int expectedAdjacentToKeptNode + ) + { + var edgeToRemove = new Edge(nodeToKeep, nodeToRemove); + var g = GraphGenerator.CreateComplete(totalNodeCount); + var h = g.ContractEdge(edgeToRemove, new Node(nodeToKeep)); + + Assert.Equal(expectedNumberOfNodes, h.GetNodes().Count); + Assert.Equal(expectedNumberOfEdges, h.GetEdges().Count); + Assert.Equal(expectedAdjacentToKeptNode, h.GetAdjacent(new Node(nodeToKeep)).Count); + } + + [Fact] + public void TestContractionKeepsComponents() + { + var e1 = new Edge("1", "2"); + var e2 = new Edge("2", "3"); + var e3 = new Edge("2", "4"); + var e4 = new Edge("5", "8"); + var edges = new List { e1, e2, e3, e4 }; + var g = new Graph(edges); + foreach (var e in g.GetEdges()) + { + g.AddComponent(e); + } + + var h = g.ContractEdge(e1, new Node("1")); + + Assert.Equal(5, h.GetNodes().Count); + Assert.Equal(3, h.GetEdges().Count); + Assert.Equal(2, h.GetAdjacent(new Node("1")).Count); + + // edge that was not updated should have kept the component + Assert.True(h.HasComponent(e4)); + } + } +} diff --git a/SharpGraph/SharpGraph.csproj b/SharpGraph/SharpGraph.csproj index 101a937..9786562 100644 --- a/SharpGraph/SharpGraph.csproj +++ b/SharpGraph/SharpGraph.csproj @@ -4,7 +4,7 @@ net8.0 SharpGraphLib README.md - 1.0.3 + 1.0.4 Jonathan Hough Jonathan Hough diff --git a/SharpGraph/src/core/Graph.Contraction.cs b/SharpGraph/src/core/Graph.Contraction.cs new file mode 100644 index 0000000..feec98d --- /dev/null +++ b/SharpGraph/src/core/Graph.Contraction.cs @@ -0,0 +1,84 @@ +// +// Copyright (C) 2023 Jonathan Hough. +// Copyright Licensed under the MIT license. +// See LICENSE file in the samples root for full license information. +// + +using System; +using System.Collections.Generic; + +namespace SharpGraph +{ + public partial class Graph + { + /// + /// Contracts the edge from the graph. This essentially removes the edge form the graph. + /// Incident edges will be reassigned to connect to the "keep" node, which is one of the + /// two nodes of the to-be-deleted edge. + /// Edges that previously connected to the to-be-deleted node, will be deleted, and new edges to + /// the to-be-kept node will be created. + /// For edges not incident with the edge that is to be deleted, the edges in the returned graph will + /// be copies, including any components. + /// + /// Edge to be deleted. + /// Node of deleted edge to keep in output graph. + /// Graph with edge removed. + public Graph ContractEdge(Edge edge, Node keep) + { + if (this.GetEdges().Contains(edge) == false) + { + throw new Exception("Edge does not exist on this graph."); + } + + if (!edge.Nodes().Contains(keep)) + { + throw new Exception( + "Node to keep does not exist in the edge ot remove. Cannot remove edge." + ); + } + + var removeNode = keep == edge.To() ? edge.From() : edge.To(); + var incident = this.GetIncidentEdges(removeNode); + var toRemove = new HashSet(); + var newEdges = new HashSet(); + toRemove.Add(edge); + foreach (var e in incident) + { + if (toRemove.Contains(e)) + { + continue; + } + + if (!e.Nodes().Contains(keep)) + { + if (removeNode == e.To()) + { + toRemove.Add(e); + var rex = new Edge(keep, e.From()); + newEdges.Add(rex); + } + else if (removeNode == e.From()) + { + toRemove.Add(e); + var rex = new Edge(keep, e.To()); + newEdges.Add(rex); + } + } + } + + var g = this.Copy(); + foreach (var deadEdge in toRemove) + { + g.RemoveEdge(deadEdge); + } + + g.RemoveNode(removeNode); + foreach (var newEdge in newEdges) + { + g.AddEdge(newEdge); + } + + return g; + } + } +} diff --git a/SharpGraph/src/core/Graph.cs b/SharpGraph/src/core/Graph.cs index 1d1e996..8da3a8b 100644 --- a/SharpGraph/src/core/Graph.cs +++ b/SharpGraph/src/core/Graph.cs @@ -353,7 +353,7 @@ public bool RemoveNode(Node node) if (incidentEdges != null) { this.edges.RemoveAll(e => incidentEdges.Contains(e)); - incidentEdges.RemoveWhere(e => incidentEdges.Contains(e)); + incidentEdges.RemoveWhere(e => e.Equals(e)); } this.nodes.Remove(node); @@ -407,13 +407,13 @@ public bool RemoveEdge(Edge edge) var fromIncidentEdges = this.incidenceMap[f]; if (fromIncidentEdges != null) { - fromIncidentEdges.RemoveWhere(e => e.Equals(edge)); + int i = fromIncidentEdges.RemoveWhere(e => e.Equals(edge)); } var toIncidentEdges = this.incidenceMap[t]; if (toIncidentEdges != null) { - toIncidentEdges.RemoveWhere(e => e.Equals(edge)); + int i = toIncidentEdges.RemoveWhere(e => e.Equals(edge)); } this.edgeComponents.Remove(edge); diff --git a/SharpGraphProj.code-workspace b/SharpGraphProj.code-workspace index 876a149..dda1fcf 100644 --- a/SharpGraphProj.code-workspace +++ b/SharpGraphProj.code-workspace @@ -4,5 +4,9 @@ "path": "." } ], - "settings": {} + "settings": { + "files.associations": { + ".fantomasignore": "ignore" + } + } } \ No newline at end of file