1#   IGraph R package
2#   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
3#   334 Harvard street, Cambridge, MA 02139 USA
4#
5#   This program is free software; you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation; either version 2 of the License, or
8#   (at your option) any later version.
9#
10#   This program is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14#
15#   You should have received a copy of the GNU General Public License
16#   along with this program; if not, write to the Free Software
17#   Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
18#   02110-1301 USA
19#
20###################################################################
21
22
23
24#' Calculate Cohesive Blocks
25#'
26#' Calculates cohesive blocks for objects of class \code{igraph}.
27#'
28#' Cohesive blocking is a method of determining hierarchical subsets of graph
29#' vertices based on their structural cohesion (or vertex connectivity). For a
30#' given graph \eqn{G}, a subset of its vertices \eqn{S\subset V(G)}{S} is said
31#' to be maximally \eqn{k}-cohesive if there is no superset of \eqn{S} with
32#' vertex connectivity greater than or equal to \eqn{k}. Cohesive blocking is a
33#' process through which, given a \eqn{k}-cohesive set of vertices, maximally
34#' \eqn{l}-cohesive subsets are recursively identified with \eqn{l>k}. Thus a
35#' hierarchy of vertex subsets is found, with the entire graph \eqn{G} at its
36#' root.
37#'
38#' The function \code{cohesive_blocks} implements cohesive blocking.  It
39#' returns a \code{cohesiveBlocks} object. \code{cohesiveBlocks} should be
40#' handled as an opaque class, i.e. its internal structure should not be
41#' accessed directly, but through the functions listed here.
42#'
43#' The function \code{length} can be used on \code{cohesiveBlocks} objects and
44#' it gives the number of blocks.
45#'
46#' The function \code{blocks} returns the actual blocks stored in the
47#' \code{cohesiveBlocks} object. They are returned in a list of numeric
48#' vectors, each containing vertex ids.
49#'
50#' The function \code{graphs_from_cohesive_blocks} is similar, but returns the blocks as
51#' (induced) subgraphs of the input graph. The various (graph, vertex and edge)
52#' attributes are kept in the subgraph.
53#'
54#' The function \code{cohesion} returns a numeric vector, the cohesion of the
55#' different blocks. The order of the blocks is the same as for the
56#' \code{blocks} and \code{graphs_from_cohesive_blocks} functions.
57#'
58#' The block hierarchy can be queried using the \code{hierarchy} function. It
59#' returns an igraph graph, its vertex ids are ordered according the order of
60#' the blocks in the \code{blocks} and \code{graphs_from_cohesive_blocks}, \code{cohesion},
61#' etc. functions.
62#'
63#' \code{parent} gives the parent vertex of each block, in the block hierarchy,
64#' for the root vertex it gives 0.
65#'
66#' \code{plot_hierarchy} plots the hierarchy tree of the cohesive blocks on the
67#' active graphics device, by calling \code{igraph.plot}.
68#'
69#' The \code{export_pajek} function can be used to export the graph and its
70#' cohesive blocks in Pajek format. It can either export a single Pajek project
71#' file with all the information, or a set of files, depending on its
72#' \code{project.file} argument. If \code{project.file} is \code{TRUE}, then
73#' the following information is written to the file (or connection) given in
74#' the \code{file} argument: (1) the input graph, together with its attributes,
75#' see \code{\link{write_graph}} for details; (2) the hierarchy graph; and (3)
76#' one binary partition for each cohesive block. If \code{project.file} is
77#' \code{FALSE}, then the \code{file} argument must be a character scalar and
78#' it is used as the base name for the generated files. If \code{file} is
79#' \sQuote{basename}, then the following files are created: (1)
80#' \sQuote{basename.net} for the original graph; (2)
81#' \sQuote{basename_hierarchy.net} for the hierarchy graph; (3)
82#' \sQuote{basename_block_x.net} for each cohesive block, where \sQuote{x} is
83#' the number of the block, starting with one.
84#'
85#' \code{max_cohesion} returns the maximal cohesion of each vertex, i.e. the
86#' cohesion of the most cohesive block of the vertex.
87#'
88#' The generic function \code{summary} works on \code{cohesiveBlocks} objects
89#' and it prints a one line summary to the terminal.
90#'
91#' The generic function \code{print} is also defined on \code{cohesiveBlocks}
92#' objects and it is invoked automatically if the name of the
93#' \code{cohesiveBlocks} object is typed in. It produces an output like this:
94#' \preformatted{ Cohesive block structure:
95#' B-1 c 1, n 23
96#' '- B-2 c 2, n 14 oooooooo.. .o......oo ooo
97#' '- B-4 c 5, n  7 ooooooo... .......... ...
98#' '- B-3 c 2, n 10 ......o.oo o.oooooo.. ...
99#' '- B-5 c 3, n  4 ......o.oo o......... ...  }
100#' The left part shows the block structure, in this case for five
101#' blocks. The first block always corresponds to the whole graph, even if its
102#' cohesion is zero. Then cohesion of the block and the number of vertices in
103#' the block are shown. The last part is only printed if the display is wide
104#' enough and shows the vertices in the blocks, ordered by vertex ids.
105#' \sQuote{o} means that the vertex is included, a dot means that it is not,
106#' and the vertices are shown in groups of ten.
107#'
108#' The generic function \code{plot} plots the graph, showing one or more
109#' cohesive blocks in it.
110#'
111#' @aliases cohesive.blocks cohesiveBlocks blocks graphs_from_cohesive_blocks blockGraphs
112#' hierarchy parent plotHierarchy export_pajek maxcohesion plot.cohesiveBlocks
113#' summary.cohesiveBlocks length.cohesiveBlocks print.cohesiveBlocks
114#' plot_hierarchy max_cohesion exportPajek
115#' @param graph For \code{cohesive_blocks} a graph object of class
116#' \code{igraph}. It must be undirected and simple. (See
117#' \code{\link{is_simple}}.)
118#'
119#' For \code{graphs_from_cohesive_blocks} and \code{export_pajek} the same graph must be
120#' supplied whose cohesive block structure is given in the \code{blocks}
121#' argument.
122#' @param labels Logical scalar, whether to add the vertex labels to the result
123#' object. These labels can be then used when reporting and plotting the
124#' cohesive blocks.
125#' @param blocks,x,object A \code{cohesiveBlocks} object, created with the
126#' \code{cohesive_blocks} function.
127#' @param file Defines the file (or connection) the Pajek file is written to.
128#'
129#' If the \code{project.file} argument is \code{TRUE}, then it can be a
130#' filename (with extension), a file object, or in general any king of
131#' connection object. The file/connection will be opened if it wasn't already.
132#'
133#' If the \code{project.file} argument is \code{FALSE}, then several files are
134#' created and \code{file} must be a character scalar containing the base name
135#' of the files, without extension. (But it can contain the path to the files.)
136#'
137#' See also details below.
138#' @param project.file Logical scalar, whether to create a single Pajek project
139#' file containing all the data, or to create separated files for each item.
140#' See details below.
141#' @param y The graph whose cohesive blocks are supplied in the \code{x}
142#' argument.
143#' @param colbar Color bar for the vertex colors. Its length should be at least
144#' \eqn{m+1}, where \eqn{m} is the maximum cohesion in the graph.
145#' Alternatively, the vertex colors can also be directly specified via the
146#' \code{col} argument.
147#' @param col A vector of vertex colors, in any of the usual formats. (Symbolic
148#' color names (e.g. \sQuote{red}, \sQuote{blue}, etc.) , RGB colors (e.g.
149#' \sQuote{#FF9900FF}), integer numbers referring to the current palette. By
150#' default the given \code{colbar} is used and vertices with the same maximal
151#' cohesion will have the same color.
152#' @param mark.groups A list of vertex sets to mark on the plot by circling
153#' them. By default all cohesive blocks are marked, except the one
154#' corresponding to the all vertices.
155#' @param layout The layout of a plot, it is simply passed on to
156#' \code{plot.igraph}, see the possible formats there. By default the
157#' Reingold-Tilford layout generator is used.
158#' @param \dots Additional arguments. \code{plot_hierarchy} and \code{plot} pass
159#' them to \code{plot.igraph}.  \code{print} and \code{summary} ignore them.
160#' @return \code{cohesive_blocks} returns a \code{cohesiveBlocks} object.
161#'
162#' \code{blocks} returns a list of numeric vectors, containing vertex ids.
163#'
164#' \code{graphs_from_cohesive_blocks} returns a list of igraph graphs, corresponding to the
165#' cohesive blocks.
166#'
167#' \code{cohesion} returns a numeric vector, the cohesion of each block.
168#'
169#' \code{hierarchy} returns an igraph graph, the representation of the cohesive
170#' block hierarchy.
171#'
172#' \code{parent} returns a numeric vector giving the parent block of each
173#' cohesive block, in the block hierarchy. The block at the root of the
174#' hierarchy has no parent and \code{0} is returned for it.
175#'
176#' \code{plot_hierarchy}, \code{plot} and \code{export_pajek} return \code{NULL},
177#' invisibly.
178#'
179#' \code{max_cohesion} returns a numeric vector with one entry for each vertex,
180#' giving the cohesion of its most cohesive block.
181#'
182#' \code{print} and \code{summary} return the \code{cohesiveBlocks} object
183#' itself, invisibly.
184#'
185#' \code{length} returns a numeric scalar, the number of blocks.
186#' @author Gabor Csardi \email{csardi.gabor@gmail.com} for the current
187#' implementation, Peter McMahan (\url{https://socialsciences.uchicago.edu/news/alumni-profile-peter-mcmahan-phd17-sociology})
188#' wrote the first version in R.
189#' @seealso \code{\link{cohesion}}
190#' @references J. Moody and D. R. White. Structural cohesion and embeddedness:
191#' A hierarchical concept of social groups. \emph{American Sociological
192#' Review}, 68(1):103--127, Feb 2003.
193#' @export
194#' @keywords graphs
195#' @examples
196#'
197#' ## The graph from the Moody-White paper
198#' mw <- graph_from_literal(1-2:3:4:5:6, 2-3:4:5:7, 3-4:6:7, 4-5:6:7,
199#'                 5-6:7:21, 6-7, 7-8:11:14:19, 8-9:11:14, 9-10,
200#'                 10-12:13, 11-12:14, 12-16, 13-16, 14-15, 15-16,
201#'                 17-18:19:20, 18-20:21, 19-20:22:23, 20-21,
202#'                 21-22:23, 22-23)
203#'
204#' mwBlocks <- cohesive_blocks(mw)
205#'
206#' # Inspect block membership and cohesion
207#' mwBlocks
208#' blocks(mwBlocks)
209#' cohesion(mwBlocks)
210#'
211#' # Save results in a Pajek file
212#' \dontrun{
213#' export_pajek(mwBlocks, mw, file="/tmp/mwBlocks.paj")
214#' }
215#'
216#' # Plot the results
217#' plot(mwBlocks, mw)
218#'
219#' ## The science camp network
220#' camp <- graph_from_literal(Harry:Steve:Don:Bert - Harry:Steve:Don:Bert,
221#'                   Pam:Brazey:Carol:Pat - Pam:Brazey:Carol:Pat,
222#'                   Holly   - Carol:Pat:Pam:Jennie:Bill,
223#'                   Bill    - Pauline:Michael:Lee:Holly,
224#'                   Pauline - Bill:Jennie:Ann,
225#'                   Jennie  - Holly:Michael:Lee:Ann:Pauline,
226#'                   Michael - Bill:Jennie:Ann:Lee:John,
227#'                   Ann     - Michael:Jennie:Pauline,
228#'                   Lee     - Michael:Bill:Jennie,
229#'                   Gery    - Pat:Steve:Russ:John,
230#'                   Russ    - Steve:Bert:Gery:John,
231#'                   John    - Gery:Russ:Michael)
232#' campBlocks <- cohesive_blocks(camp)
233#' campBlocks
234#'
235#' plot(campBlocks, camp, vertex.label=V(camp)$name, margin=-0.2,
236#'      vertex.shape="rectangle", vertex.size=24, vertex.size2=8,
237#'      mark.border=1, colbar=c(NA, NA,"cyan","orange") )
238#'
239cohesive_blocks <- function(graph, labels=TRUE) {
240
241  # Argument checks
242  if (!is_igraph(graph)) { stop("Not a graph object") }
243
244  on.exit( .Call(C_R_igraph_finalizer) )
245  # Function call
246  res <- .Call(C_R_igraph_cohesive_blocks, graph)
247  class(res) <- "cohesiveBlocks"
248  if (labels && "name" %in% vertex_attr_names(graph)) {
249    res$labels <- V(graph)$name
250  }
251  if (igraph_opt("return.vs.es")) {
252    res$blocks <- lapply(res$blocks, create_vs, graph = graph)
253  }
254
255  res$vcount <- vcount(graph)
256  res
257}
258
259#' @rdname cohesive_blocks
260#' @method length cohesiveBlocks
261#' @export
262
263length.cohesiveBlocks <- function(x) {
264  length(x$blocks)
265}
266
267#' @rdname cohesive_blocks
268#' @export
269
270blocks <- function(blocks) {
271  blocks$blocks
272}
273
274#' @rdname cohesive_blocks
275#' @export
276
277graphs_from_cohesive_blocks <- function(blocks, graph) {
278  lapply(blocks(blocks), induced_subgraph, graph=graph)
279}
280
281#' @export
282
283cohesion <- function(x, ...)
284  UseMethod("cohesion")
285
286#' @rdname cohesive_blocks
287#' @method cohesion cohesiveBlocks
288#' @export
289
290cohesion.cohesiveBlocks <- function(x, ...) {
291  x$cohesion
292}
293
294#' @rdname cohesive_blocks
295#' @export
296
297hierarchy <- function(blocks) {
298  blocks$blockTree
299}
300
301#' @rdname cohesive_blocks
302#' @export
303
304parent <- function(blocks) {
305  blocks$parent
306}
307
308#' @rdname cohesive_blocks
309#' @method print cohesiveBlocks
310#' @export
311
312print.cohesiveBlocks <- function(x, ...) {
313  cat("Cohesive block structure:\n")
314  myb <- blocks(x)
315  ch <- cohesion(x)
316  pp <- parent(x)
317  si <- sapply(myb, length)
318
319  cs <- 3 + 2 + nchar(length(x)) +
320    max(distances(hierarchy(x), mode="out", v=1)) * 3
321
322  .plot <- function(b, ind="") {
323    if (b!=1) {
324      he <- format(paste(sep="", ind, "'- B-", b), width=cs)
325      ind <- paste("  ", ind)
326    } else {
327      he <- format(paste(sep="", "B-", b), width=cs)
328    }
329    cat(sep="", he,
330        "c ", format(ch[b], width=nchar(max(ch)), justify="right"),
331        ", n ", format(si[b], width=nchar(x$vcount), justify="right"))
332
333    if (x$vcount <= options("width")$width-40 && b != 1) {
334      o <- rep(".", x$vcount)
335      o[ myb[[b]] ] <- "o"
336      oo <- character()
337      for (i in 1:floor(x$vcount/10)) {
338        oo <- c(oo, o[((i-1)*10+1):(i*10)], " ")
339      }
340      if (x$vcount %% 10) { oo <- c(oo, o[(i*10+1):length(o)]) }
341      cat("  ", paste(oo, collapse=""), "\n")
342    } else {
343      cat("\n")
344    }
345
346    wc <- which(pp==b)
347    sapply(wc, .plot, ind=ind)
348  }
349  if (length(x) >0) .plot(1) else cat("No cohesive blocks found.")
350
351  invisible(x)
352}
353
354#' @rdname cohesive_blocks
355#' @method summary cohesiveBlocks
356#' @export
357
358summary.cohesiveBlocks <- function(object, ...) {
359  cat("Structurally cohesive block structure, with",
360      length(blocks(object)), "blocks.\n")
361  invisible(object)
362}
363
364#' @rdname cohesive_blocks
365#' @method plot cohesiveBlocks
366#' @export
367#' @importFrom grDevices rainbow
368#' @importFrom graphics plot
369
370plot.cohesiveBlocks <- function(x, y,
371                                colbar=rainbow(max(cohesion(x))+1),
372                                col=colbar[max_cohesion(x)+1],
373                                mark.groups=blocks(x)[-1],
374                                ...) {
375  plot(y, mark.groups=mark.groups,
376       vertex.color=col, ...)
377}
378
379#' @rdname cohesive_blocks
380#' @export
381#' @importFrom graphics plot
382
383plot_hierarchy <- function(blocks,
384                          layout=layout_as_tree(hierarchy(blocks),
385                            root=1), ...) {
386  plot(hierarchy(blocks), layout=layout, ...)
387}
388
389exportPajek.cohesiveblocks.pf <- function(blocks, graph, file) {
390
391  closeit <- FALSE
392  if (is.character(file)) {
393    file <- file(file, open = "w+b")
394    closeit <- TRUE
395  }
396  if (!isOpen(file)) {
397    file <- open(file)
398    closeit <- TRUE
399  }
400
401  ## The original graph
402  cat(file=file, sep="", "*Network cohesive_blocks_input.net\r\n")
403  write_graph(graph, file=file, format="pajek")
404
405  ## The hierarchy graph
406  cat(file=file, sep="", "\r\n*Network hierarchy.net\r\n")
407  write_graph(hierarchy(blocks), file=file, format="pajek")
408
409  ## The blocks
410  myb <- blocks(blocks)
411  for (b in seq_along(myb)) {
412    thisb <- rep(0, vcount(graph))
413    thisb[ myb[[b]] ] <- 1
414    cat(file=file, sep="", "\r\n*Partition block_", b, ".clu\r\n",
415        "*Vertices ", vcount(graph), "\r\n   ")
416    cat(thisb, sep="\r\n   ", file=file)
417  }
418
419  if (closeit) {
420    close(file)
421  }
422  invisible(NULL)
423}
424
425exportPajek.cohesiveblocks.nopf <- function(blocks, graph, file) {
426
427  ## The original graph
428  write_graph(graph, file=paste(sep="", file, ".net"), format="pajek")
429
430  ## The hierarchy graph
431  write_graph(hierarchy(blocks), file=paste(sep="", file, "_hierarchy.net"),
432              format="pajek")
433
434  ## The blocks
435  myb <- blocks(blocks)
436  for (b in seq_along(myb)) {
437    thisb <- rep(0, vcount(graph))
438    thisb[ myb[[b]] ] <- 1
439    cat(file=paste(sep="", file, "_block_", b, ".clu"), sep="\r\n",
440        paste("*Vertices", vcount(graph)), thisb)
441  }
442
443  invisible(NULL)
444}
445
446#' @rdname cohesive_blocks
447#' @export
448
449export_pajek <- function(blocks, graph, file,
450                        project.file=TRUE) {
451
452  if (!project.file && !is.character(file)) {
453    stop(paste("`file' must be a filename (without extension) when writing",
454               "to separate files"))
455  }
456
457  if (project.file) {
458    return(exportPajek.cohesiveblocks.pf(blocks, graph, file))
459  } else {
460    return(exportPajek.cohesiveblocks.nopf(blocks, graph, file))
461  }
462}
463
464#' @rdname cohesive_blocks
465#' @export
466
467max_cohesion <- function(blocks) {
468  res <- numeric(blocks$vcount)
469  myb <- blocks(blocks)
470  coh <- cohesion(blocks)
471  oo <- order(coh)
472  myb <- myb[oo]
473  coh <- coh[oo]
474  for (b in seq_along(myb)) {
475    res[ myb[[b]] ] <- coh[b]
476  }
477  res
478}
479
480#########################################################
481## Various designs to print the cohesive blocks
482
483## Cohesive block structure:
484## B-1          c. 1, n. 34
485## '- B-2       c. 2, n. 28    1,2,3,4,8,9,10,13,14,15,16,18,19,20,21,22,
486##    |                        23,24,25,26,27,28,29,30,31,32,33,34
487##    '- B-4    c. 4, n.  5    1,2,3,4,8
488##    '- B-5    c. 3, n.  7    1,2,3,9,31,33,34
489##    '- B-7    c. 4, n.  5    1,2,3,4,14
490##    '- B-8    c. 3, n. 10    3,24,25,26,28,29,30,32,33,34
491## '- B-3       c. 2, n.  6    1,5,6,7,11,17
492##    '- B-6    c. 3, n.  5    1,5,6,7,11
493
494## Cohesive block structure:
495## B-1          c. 1, n. 23
496## '- B-2       c. 2, n. 14    1,2,3,4,5,6,7,8,12,19,20,21,22,23
497##    '- B-4    c. 5, n.  7    1,2,3,4,5,6,7
498## '- B-3       c. 2, n. 10    7,9,10,11,13,14,15,16,17,18
499##    '- B-5    c. 3, n.  4    7,9,10,11
500
501## #########################################################
502
503## Cohesive block structure:
504## B-1        c 1, n 34
505## '- B-2     c 2, n 28    oooo...ooo ..oooo.ooo oooooooooo oooo
506##    '- B-4  c 4, n  5    oooo...o.. .......... .......... ....
507##    '- B-5  c 3, n  7    ooo.....o. .......... .......... o.oo
508##    '- B-7  c 4, n  5    oooo...... ...o...... .......... ....
509##    '- B-8  c 3, n 10    ..o....... .......... ...ooo.ooo .ooo
510## '- B-3     c 2, n  6    o...ooo... o.....o... .......... ....
511##    '- B-6  c 3, n  5    o...ooo... o......... .......... ....
512
513## Cohesive block structure:
514## B-1        c 1, n 23    oooooooooo oooooooooo ooo
515## '- B-2     c 2, n 14    oooooooo.. .o......oo ooo
516##    '- B-4  c 5, n  7    ooooooo... .......... ...
517## '- B-3     c 2, n 10    ......o.oo o.oooooo.. ...
518##    '- B-5  c 3, n  4    ......o.oo o......... ...
519
520## #########################################################
521
522## Cohesive block structure:
523## B-1          c. 1, n. 34
524## '- B-2       c. 2, n. 28     1, 2, 3, 4, 8, 9,10,13,14,15,16,18,19,20,21,
525##    |                        22,23,24,25,26,27,28,29,30,31,32,33,34
526##    '- B-4    c. 4, n.  5     1, 2, 3, 4, 8
527##    '- B-5    c. 3, n.  7     1, 2, 3, 9,31,33,34
528##    '- B-7    c. 4, n.  5     1, 2, 3, 4,14
529##    '- B-8    c. 3, n. 10     3,24,25,26,28,29,30,32,33,34
530## '- B-3       c. 2, n.  6     1, 5, 6, 7,11,17
531##    '- B-6    c. 3, n.  5     1, 5, 6, 7,11
532
533## Cohesive block structure:
534## B-1          c. 1, n. 23
535## '- B-2       c. 2, n. 14     1, 2, 3, 4, 5, 6, 7, 8,12,19,20,21,22,23
536##    '- B-4    c. 5, n.  7     1, 2, 3, 4, 5, 6, 7
537## '- B-3       c. 2, n. 10     7, 9,10,11,13,14,15,16,17,18
538##    '- B-5    c. 3, n.  4     7, 9,10,11
539
540## #########################################################
541
542## Cohesive block structure:
543## B-1          c. 1, n. 34
544## '- B-2       c. 2, n. 28    1-4, 8-10, 13-16, 18-34
545##    '- B-4    c. 4, n.  5    1-4, 8
546##    '- B-5    c. 3, n.  7    1-3, 9, 31, 33-34
547##    '- B-7    c. 4, n.  5    1-4, 14
548##    '- B-8    c. 3, n. 10    3, 24-26, 28-30, 32-34
549## '- B-3       c. 2, n.  6    1, 5-7, 11, 17
550##    '- B-6    c. 3, n.  5    1, 5-7, 11
551
552## Cohesive block structure:
553## B-1          c. 1, n. 23
554## '- B-2       c. 2, n. 14    1-8, 12, 19-23
555##    '- B-4    c. 5, n.  7    1-7
556## '- B-3       c. 2, n. 10    7, 9-11, 13-18
557##    '- B-5    c. 3, n.  4    7, 9-11
558
559## ##########################################################
560
561## Cohesive block structure:
562## B-1        c. 1, n. 34
563## |- B-2     c. 2, n. 28  [ 1] oooo...ooo ..oooo.ooo
564## |  |                    [21] oooooooooo oooo
565## |  |- B-4  c. 4, n.  5  [ 1] oooo...o.. ..........
566## |  |                    [21] .......... ....
567## |  |- B-5  c. 3, n.  7  [ 1] ooo.....o. ..........
568## |  |                    [21] .......... o.oo
569## |  |- B-7  c. 4, n.  5  [ 1] oooo...... ...o......
570## |  |                    [21] .......... ....
571## |  |- B-8  c. 3, n. 10  [ 1] ..o....... ..........
572## |                       [21] ...ooo.ooo .ooo
573## '- B-3     c. 2, n.  6  [ 1] o...ooo... o.....o...
574##    |                    [21] .......... ....
575##    '- B-6  c. 3, n.  5  [ 1] o...ooo... o.........
576##                         [21] .......... ....
577
578## Cohesive block structure:
579## B-1          c. 1, n. 23  [ 1] oooooooooo oooooooooo
580## |                         [21] ooo
581## |- B-2       c. 2, n. 14  [ 1] oooooooo.. .o......oo
582## |  |                      [21] ooo
583## |  '- B-4    c. 5, n.  7  [ 1] ooooooo... ..........
584## |                         [21] ...
585## '- B-3       c. 2, n. 10  [ 1] ......o.oo o.oooooo..
586##    |                      [21] ...
587##    '- B-5    c. 3, n.  4  [ 1] ......o.oo o.........
588##                           [21] ...
589