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