From 110545f6de6b86379b900da642b2286e6d38f867 Mon Sep 17 00:00:00 2001 From: etienneINSA Date: Wed, 22 Jun 2022 17:33:07 +0200 Subject: [PATCH] human-readable output for simple graphs --- src/SimpleGraphs/SimpleGraphs.jl | 66 ++++++++++++++++++++++++++++++- test/simplegraphs/simplegraphs.jl | 16 ++++---- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/SimpleGraphs/SimpleGraphs.jl b/src/SimpleGraphs/SimpleGraphs.jl index ecc870af3..8c990f80c 100644 --- a/src/SimpleGraphs/SimpleGraphs.jl +++ b/src/SimpleGraphs/SimpleGraphs.jl @@ -6,7 +6,7 @@ using Graphs using SimpleTraits import Base: - eltype, show, ==, Pair, Tuple, copy, length, issubset, reverse, zero, in, iterate + eltype, show, summary, ==, Pair, Tuple, copy, length, issubset, reverse, zero, in, iterate import Graphs: _NI, AbstractGraph, AbstractEdge, AbstractEdgeIter, @@ -50,11 +50,73 @@ An abstract type representing a simple graph structure. """ abstract type AbstractSimpleGraph{T<:Integer} <: AbstractGraph{T} end -function show(io::IO, ::MIME"text/plain", g::AbstractSimpleGraph{T}) where T +function summary(io::IO, @nospecialize(g::AbstractSimpleGraph{T})) where T dir = is_directed(g) ? "directed" : "undirected" print(io, "{$(nv(g)), $(ne(g))} $dir simple $T graph") end +object_width(io, x) = textwidth(sprint(print, x, context=io, sizehint=0)) + +function _print_outneighbors(io, @nospecialize(X::AbstractVecOrMat)) + s = sprint(show, X, context=io, sizehint=0) + return match(r"(\[.*)", s).match # remove type when vertex type is different from Int64 +end + + +function _print_rows(io, g, rows, lastindx, arrow, max_vertex_size) + for i in rows + print(io, " "^(max_vertex_size - object_width(io, i))) + print(io, i) + print(io, arrow) + s = _print_outneighbors(io, outneighbors(g, i)) + if i != lastindx + println(io, s) + else + print(io, s) + end + end +end + +function _print_graph(io, @nospecialize(g::AbstractSimpleGraph{T})) where T + if !(get(io, :limit, false)::Bool) + screenheight = typemax(Int) + else + sz = displaysize(io)::Tuple{Int,Int} + screenheight = sz[1] - 4 + end + vdots = "\u22ee" + arrow = " => " + max_vertex_size = object_width(io, nv(g)) + halfheight = div(screenheight,2) + lastindx = nv(g) + if nv(g) > screenheight + rowsA, rowsB = 1:halfheight, lastindx-div(screenheight-1,2)+1:lastindx + else + rowsA, rowsB = 1:lastindx, 1:0 + end + + _print_rows(io, g, rowsA, nv(g), arrow, max_vertex_size) + if last(rowsA) != lastindx + print(io, " "^(max_vertex_size - length(vdots))) + print(io, vdots) + if !isempty(rowsB) + println(io) + end + _print_rows(io, g, rowsB, lastindx, arrow, max_vertex_size) + end +end + +function show(io::IO, ::MIME"text/plain", @nospecialize(g::AbstractSimpleGraph{T})) where T + summary(io, g) + if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 + return print(io, " …") + elseif nv(g) > 0 + println(io) + _print_graph(io, g) + end +end + + nv(g::AbstractSimpleGraph{T}) where T = T(length(fadj(g))) vertices(g::AbstractSimpleGraph) = Base.OneTo(nv(g)) diff --git a/test/simplegraphs/simplegraphs.jl b/test/simplegraphs/simplegraphs.jl index 2a1b3a12d..8e8c967b2 100644 --- a/test/simplegraphs/simplegraphs.jl +++ b/test/simplegraphs/simplegraphs.jl @@ -32,7 +32,7 @@ import Random T = eltype(g) @test repr("text/plain", g) == "{0, 0} undirected simple $T graph" @test @inferred(add_vertices!(g, 5) == 5) - @test repr("text/plain", g) == "{5, 0} undirected simple $T graph" + @test repr("text/plain", g) == "{5, 0} undirected simple $T graph\n1 => []\n2 => []\n3 => []\n4 => []\n5 => []" @test eval(Meta.parse(repr(g))) == g end gx = SimpleDiGraph() @@ -40,7 +40,7 @@ import Random T = eltype(g) @test repr("text/plain", g) == "{0, 0} directed simple $T graph" @test @inferred(add_vertices!(g, 5) == 5) - @test repr("text/plain", g) == "{5, 0} directed simple $T graph" + @test repr("text/plain", g) == "{5, 0} directed simple $T graph\n1 => []\n2 => []\n3 => []\n4 => []\n5 => []" @test eval(Meta.parse(repr(g))) == g end @@ -232,7 +232,7 @@ import Random edge_set_any = Set{Any}(edge_list) g1 = @inferred SimpleGraph(edge_list) - # we can't infer the return type of SimpleGraphFromIterator at the moment + # we can't infer the return type of SimpleGraphFromIterator at the moment g2 = SimpleGraphFromIterator(edge_list) g3 = SimpleGraphFromIterator(edge_iter) g4 = SimpleGraphFromIterator(edge_set) @@ -258,13 +258,13 @@ import Random # We create an edge list and shuffle it edge_list = [e for e in edges(g)] shuffle!(MersenneTwister(0), edge_list) - + edge_iter = (e for e in edge_list) edge_set = Set(edge_list) edge_set_any = Set{Any}(edge_list) g1 = @inferred SimpleDiGraph(edge_list) - # we can't infer the return type of SimpleDiGraphFromIterator at the moment + # we can't infer the return type of SimpleDiGraphFromIterator at the moment g2 = SimpleDiGraphFromIterator(edge_list) g3 = SimpleDiGraphFromIterator(edge_iter) g4 = SimpleDiGraphFromIterator(edge_set) @@ -302,7 +302,7 @@ import Random @test_throws DomainError SimpleDiGraphFromIterator( (SimpleDiGraphEdge(1,2), "a string") ) end - # check if multiple edges && multiple self-loops result in the + # check if multiple edges && multiple self-loops result in the # correct number of edges & vertices # edges using integers < 1 should be ignored g_undir = SimpleGraph(0) @@ -325,7 +325,7 @@ import Random @test nv(g3) == 4 @test nv(g4) == 4 @test nv(g5) == 4 - + @test ne(g1) == 2 @test ne(g2) == 2 @test ne(g3) == 2 @@ -352,7 +352,7 @@ import Random @test nv(g3) == 4 @test nv(g4) == 4 @test nv(g5) == 4 - + @test ne(g1) == 3 @test ne(g2) == 3 @test ne(g3) == 3