diff --git a/matchers/support/goraph/bipartitegraph/bipartitegraph_test.go b/matchers/support/goraph/bipartitegraph/bipartitegraph_test.go index 776c6e26..d93438a5 100644 --- a/matchers/support/goraph/bipartitegraph/bipartitegraph_test.go +++ b/matchers/support/goraph/bipartitegraph/bipartitegraph_test.go @@ -1,6 +1,7 @@ package bipartitegraph_test import ( + "fmt" "reflect" . "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph" @@ -165,4 +166,44 @@ var _ = Describe("Bipartitegraph", func() { Ω(graph3.LargestMatching()).Should(HaveLen(79)) }) }) + + Describe("Edge case in Issue #765", func() { + It("is now resolved", func() { + knownEdges := map[string]bool{ + "1A": true, + "1B": true, + "1C": true, + "1D": true, + "1E": true, + "2A": true, + "2D": true, + "3B": true, + "3D": true, + "4B": true, + "4D": true, + "4E": true, + "5A": true, + } + + edgesFunc := func(l, r interface{}) (bool, error) { + return knownEdges[fmt.Sprintf("%v%v", l, r)], nil + } + + vertices := []interface{}{"1", "2", "3", "4", "5", "A", "B", "C", "D", "E"} + leftPart := vertices[:5] + rightPart := vertices[5:] + + bipartiteGraph, err := NewBipartiteGraph(leftPart, rightPart, edgesFunc) + Ω(err).ShouldNot(HaveOccurred()) + edgeSet := bipartiteGraph.LargestMatching() + Ω(edgeSet).Should(HaveLen(5)) + + result := []string{} + for _, edge := range edgeSet { + result = append(result, fmt.Sprintf("%v%v", vertices[edge.Node1], vertices[edge.Node2])) + } + Ω(result).Should(ConsistOf("1C", "2D", "3B", "4E", "5A")) + + }) + }) }) diff --git a/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go b/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go index 9cc448aa..44aa61d4 100644 --- a/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go +++ b/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go @@ -1,10 +1,11 @@ package bipartitegraph import ( + "slices" + . "github.com/onsi/gomega/matchers/support/goraph/edge" . "github.com/onsi/gomega/matchers/support/goraph/node" "github.com/onsi/gomega/matchers/support/goraph/util" - "slices" ) // LargestMatching implements the Hopcroft–Karp algorithm taking as input a bipartite graph @@ -159,7 +160,7 @@ func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers [ return []NodeOrderedSet{} } if done { // if last layer - into last layer must be only 'free' nodes - currentLayer = slices.DeleteFunc(currentLayer, func(in Node)bool{ + currentLayer = slices.DeleteFunc(currentLayer, func(in Node) bool { return !matching.Free(in) }) } diff --git a/matchers/support/goraph/bipartitegraph/bipartitegraphmatching_test.go b/matchers/support/goraph/bipartitegraph/bipartitegraphmatching_test.go deleted file mode 100644 index f0600fd9..00000000 --- a/matchers/support/goraph/bipartitegraph/bipartitegraphmatching_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package bipartitegraph - -import ( - "github.com/onsi/gomega/matchers/support/goraph/edge" - "slices" - "testing" -) - -func buildEdgesArr(l, r []interface{}, edges edge.EdgeSet) []string { - unpackArr := func(in []interface{}) []string { - result := make([]string, 0, len(in)) - for _, el := range in { - result = append(result, el.(string)) - } - return result - } - - vertexes := unpackArr(append(l, r...)) - - result := make([]string, 0) - for _, currEdge := range edges { - result = append(result, vertexes[currEdge.Node1]+"-"+vertexes[currEdge.Node2]) - } - return result -} - -func expectedContains(t *testing.T, expected string, edges []string) { - idx := slices.IndexFunc(edges, func(c string) bool { return c == expected }) - if idx == -1 { - t.Fatalf("edges %v not contains expected: %s", edges, expected) - } -} - -func TestMaximumCardinalityMatch(t *testing.T) { - edgesFunc := func(l, r interface{}) (bool, error) { - ll := l.(string) - rr := r.(string) - - type currEdge struct { - l string - r string - } - knownEdges := []currEdge{ - {"1", "A"}, - {"1", "B"}, - {"1", "C"}, - {"1", "D"}, - {"1", "E"}, - {"2", "A"}, - {"2", "D"}, - {"3", "B"}, - {"3", "D"}, - {"4", "B"}, - {"4", "D"}, - {"4", "E"}, - {"5", "A"}, - } - - for _, el := range knownEdges { - if el.l == ll && el.r == rr { - return true, nil - } - } - return false, nil - } - - leftPart := []interface{}{"1", "2", "3", "4", "5"} - rightPart := []interface{}{"A", "B", "C", "D", "E"} - - bipartiteGraph, err := NewBipartiteGraph( - leftPart, - rightPart, - edgesFunc, - ) - if err != nil { - t.Fatalf("NewBipartiteGraph returned error: %v", err) - } - if err != nil { - t.Fatal(err) - } - edgeSet := bipartiteGraph.LargestMatching() - if len(edgeSet) != 5 { - t.Fatalf("bipartiteGraph.LargestMatching() returned not 5 elements: %v", edgeSet) - } - edges := buildEdgesArr(leftPart, rightPart, edgeSet) - expectedContains(t, "1-C", edges) - expectedContains(t, "2-D", edges) - expectedContains(t, "3-B", edges) - expectedContains(t, "4-E", edges) - expectedContains(t, "5-A", edges) -}