diff --git a/gap/dot.gd b/gap/dot.gd index aa5b800..b91b7f8 100644 --- a/gap/dot.gd +++ b/gap/dot.gd @@ -104,6 +104,12 @@ DeclareOperation("GraphvizNodes", [IsGraphvizGraphDigraphOrContext]); #! @Description gets the subgraphs of a provided graphviz graph. DeclareOperation("GraphvizSubgraphs", [IsGraphvizGraphDigraphOrContext]); +#! @Arguments graph +#! @Returns the contexts of the provided graphviz graph, digraph or context. +#! @Description gets the contexts of a provided graphviz graph, digraph +#! or context. +DeclareOperation("GraphvizContexts", [IsGraphvizGraphDigraphOrContext]); + #! @Arguments graph, name #! @Returns a graph with the provided name. #! @Description diff --git a/gap/dot.gi b/gap/dot.gi index 0611abb..f3be316 100644 --- a/gap/dot.gi +++ b/gap/dot.gi @@ -18,6 +18,7 @@ function(name) rec( Name := name, Subgraphs := GV_Map(), + Contexts := GV_Map(), Nodes := GV_Map(), Edges := [], Attrs := [], @@ -34,6 +35,7 @@ function(name) rec( Name := name, Subgraphs := GV_Map(), + Contexts := GV_Map(), Nodes := GV_Map(), Edges := [], Attrs := [], @@ -139,6 +141,9 @@ end); InstallMethod(GraphvizSubgraphs, "for a graphviz (di)graph or context", [IsGraphvizGraphDigraphOrContext], x -> x!.Subgraphs); +InstallMethod(GraphvizContexts, "for a graphviz (di)graph or context", +[IsGraphvizGraphDigraphOrContext], x -> x!.Contexts); + InstallMethod(GraphvizTail, "for a graphviz edge", [IsGraphvizEdge], x -> x!.Tail); @@ -212,7 +217,8 @@ InstallMethod(\[\], "for a graphviz (di)graph or context and object", InstallMethod(GraphvizFindSubgraphRecursive, "for a graphviz (di)graph or context and a string", [IsGraphvizGraphDigraphOrContext, IsString], -{g, s} -> GV_GraphTreeSearch(g, v -> GraphvizName(v) = s)); +{g, s} -> GV_GraphTreeSearch(g, v -> GraphvizName(v) = s and + not IsGraphvizContext(v))); InstallMethod(GraphvizFindSubgraphRecursive, "for a graphviz (di)graph or context and a string", @@ -444,17 +450,17 @@ InstallMethod(GraphvizAddContext, "for a graphviz (di)graph or context and a string", [IsGraphvizGraphDigraphOrContext, IsString], function(graph, name) - local subgraphs, ctx; + local contexts, ctx; - subgraphs := GraphvizSubgraphs(graph); - if IsBound(subgraphs[name]) then + contexts := GraphvizContexts(graph); + if IsBound(contexts[name]) then ErrorFormatted("the 1st argument (a graphviz (di)graph/context) ", - "already has a context or subgraph with name \"{}\"", + "already has a context with name \"{}\"", name); fi; ctx := GV_Context(graph, name); - subgraphs[name] := ctx; + contexts[name] := ctx; return ctx; end); diff --git a/gap/gv.gi b/gap/gv.gi index a781a27..4d52941 100644 --- a/gap/gv.gi +++ b/gap/gv.gi @@ -198,6 +198,7 @@ function(parent, name) rec( Name := name, Subgraphs := GV_Map(), + Contexts := GV_Map(), Nodes := GV_Map(), Edges := [], Attrs := [], @@ -286,7 +287,7 @@ InstallMethod(GV_GraphTreeSearch, "for a graphviz graph and a predicate", [IsGraphvizGraphDigraphOrContext, IsFunction], function(graph, pred) - local seen, to_visit, g, key, subgraph, parent; + local seen, to_visit, g, key, subgraph, context, parent; seen := [graph]; to_visit := [graph]; @@ -307,6 +308,15 @@ function(graph, pred) fi; od; + # add contexts to list of to visit if not visited + for key in GV_MapNames(GraphvizContexts(g)) do + context := GraphvizContexts(g)[key]; + if not ForAny(seen, s -> IsIdenticalObj(s, context)) then + Add(seen, context); + Add(to_visit, context); + fi; + od; + # add parent if not visited parent := GV_GetParent(g); if not IsGraphvizGraphDigraphOrContext(parent) then @@ -326,7 +336,7 @@ InstallMethod(GV_GraphSearchChildren, "for a graphviz graph and a predicate", [IsGraphvizGraphDigraphOrContext, IsFunction], function(graph, pred) - local _, curr, queue, count, subs, key; + local _, curr, queue, count, nexts, key; queue := [graph]; while Length(queue) > 0 do @@ -342,9 +352,13 @@ function(graph, pred) fi; # Add children - subs := GraphvizSubgraphs(curr); - for key in GV_MapNames(subs) do - Add(queue, subs[key]); + nexts := GraphvizSubgraphs(curr); + for key in GV_MapNames(nexts) do + Add(queue, nexts[key]); + od; + nexts := GraphvizContexts(curr); + for key in GV_MapNames(nexts) do + Add(queue, nexts[key]); od; od; od; @@ -628,17 +642,20 @@ InstallMethod(GV_ConstructHistory, "for a graphviz graph", [IsGraphvizGraphDigraphOrContext], function(graph) - local nodes, edges, subs, node_hist, edge_hist, subs_hist, hist; + local ctxs, nodes, edges, subs, + ctxs_hist, node_hist, edge_hist, subs_hist, hist; nodes := GraphvizNodes(graph); edges := GraphvizEdges(graph); subs := GraphvizSubgraphs(graph); + ctxs := GraphvizContexts(graph); node_hist := List(GV_MapNames(nodes), n -> [GV_GetIdx(nodes[n]), nodes[n]]); subs_hist := List(GV_MapNames(subs), s -> [GV_GetIdx(subs[s]), subs[s]]); + ctxs_hist := List(GV_MapNames(ctxs), s -> [GV_GetIdx(ctxs[s]), ctxs[s]]); edge_hist := List(edges, e -> [GV_GetIdx(e), e]); - hist := Concatenation(node_hist, edge_hist, subs_hist); + hist := Concatenation(node_hist, edge_hist, subs_hist, ctxs_hist); SortBy(hist, v -> v[1]); Apply(hist, x -> x[2]); diff --git a/tst/subgraph.tst b/tst/subgraph.tst index 272e134..610e822 100644 --- a/tst/subgraph.tst +++ b/tst/subgraph.tst @@ -9,6 +9,7 @@ ## #@local a, a1, a2, a3, b, b1, b2, b3, c, child, ctx, g, gv, legend, main, n, o +#@local sub1, sub2, ctx1, ctx2 #@local parent, s, s1, s11, s2, sibling gap> START_TEST("graphviz package: subgraph.tst"); gap> LoadPackage("graphviz", false);; @@ -36,13 +37,13 @@ gap> GraphvizAddContext(g); #@if CompareVersionNumbers(GAPInfo.Version, "4.12") gap> GraphvizAddContext(g, "no_name_1"); -Error, the 1st argument (a graphviz (di)graph/context) already has a context o\ -r subgraph with name "no_name_1" +Error, the 1st argument (a graphviz (di)graph/context) already has a context w\ +ith name "no_name_1" #@else gap> GraphvizAddContext(g, "no_name_1"); -Error, the 1st argument (a graphviz (di)graph/context) already has a context o\ -r subgr\ -aph with name "no_name_1" +Error, the 1st argument (a graphviz (di)graph/context) already has a context w\ +ith nam\ +e "no_name_1" #@fi # Test no-name constructor graphs' names increment @@ -65,13 +66,14 @@ gap> GraphvizAddContext(g); gap> GraphvizAddContext(g); -# Test getting subgraphs +# Test getting subgraphs and contexts gap> g := GraphvizGraph();; gap> GraphvizAddSubgraph(g, "a");; gap> GraphvizAddContext(g, "b");; gap> GraphvizSubgraphs(g); -rec( a := , - b := ) +rec( a := ) +gap> GraphvizContexts(g); +rec( b := ) # Test adding a node to a subgraph (does or does not add to parent???) # TODO need to nail down expected behaviour! @@ -294,13 +296,15 @@ gap> GraphvizSubgraphs(g)["b"]; gap> GraphvizSubgraphs(g)["d"]; fail -# Test getting context (subgraph) by name +# Test getting context by name gap> g := GraphvizDigraph();; gap> s1 := GraphvizAddSubgraph(g, "a");; gap> s2 := GraphvizAddContext(g, "c");; gap> GraphvizSubgraphs(g)["a"]; gap> GraphvizSubgraphs(g)["c"]; +fail +gap> GraphvizContexts(g)["c"]; # Test adding a nested subgraph @@ -331,10 +335,13 @@ gap> g := GraphvizGraph();; gap> GraphvizAddContext(g, 11); -# Test getting subgraphs with non-string names +# Test getting subgraphs/contexts with non-string names gap> g := GraphvizGraph();; gap> GraphvizAddContext(g, ["a"]);; -gap> GraphvizSubgraphs(g)[["a"]]; +gap> GraphvizAddSubgraph(g, ["b"]);; +gap> GraphvizSubgraphs(g)[["b"]]; + +gap> GraphvizContexts(g)[["a"]]; # Test finding subgraph (parent) @@ -478,5 +485,54 @@ gap> GraphvizAddSubgraph(a, "c");; gap> GraphvizAddSubgraph(b, "c"); +# Test the parent is the object it was added to +gap> g := GraphvizGraph("g");; +gap> sub1 := GraphvizAddSubgraph(g, "sub1");; +gap> sub2 := GraphvizAddSubgraph(sub1, "sub2");; +gap> ctx2 := GraphvizAddContext(sub1, "ctx2");; +gap> GV_GetParent(sub1); + +gap> GV_GetParent(sub2); + +gap> GV_GetParent(ctx2); + +gap> g := GraphvizGraph("g");; +gap> ctx1 := GraphvizAddSubgraph(g, "ctx1");; +gap> sub2 := GraphvizAddSubgraph(ctx1, "sub2");; +gap> ctx2 := GraphvizAddContext(ctx1, "ctx2");; +gap> GV_GetParent(ctx1); + +gap> GV_GetParent(sub2); + +gap> GV_GetParent(ctx2); + + +# Test adding contexts with the same name +gap> g := GraphvizDigraph();; +gap> s1 := GraphvizAddContext(g, "a");; +#@if CompareVersionNumbers(GAPInfo.Version, "4.12") +gap> s2 := GraphvizAddContext(g, "a"); +Error, the 1st argument (a graphviz (di)graph/context) already has a context w\ +ith name "a" +#@else +gap> s2 := GraphvizAddContext(g, "a"); +Error, the 1st argument (a graphviz (di)graph/context) already has a context w\ +ith nam\ +e "a" +#@fi + +# Test adding contexts and subgraphs (different name spaces) +gap> g := GraphvizDigraph();; +gap> s1 := GraphvizAddContext(g, "a"); + +gap> s2 := GraphvizAddSubgraph(g, "a"); + + +# Test finding subgraphs will not return a context instead +gap> g := GraphvizDigraph();; +gap> s1 := GraphvizAddContext(g, "a");; +gap> GraphvizFindSubgraphRecursive(g, "a"); +fail + # gap> STOP_TEST("graphviz package: subgraph.tst", 0);