This notebook is published under the Creative Commons Attribution 3.0 Unported license.

Introduction

We demonstrate a simple (?) function to generate a random tree (a connected, cycle-free undirected graph with a single root node).

Libraries used

The graph generation function is self-contained (uses only elements of base R). To display the graph, we import the DiagrammeR package.

library(DiagrammeR)

Function definition

The following function generates a random tree graph with a specified number of nodes and a specified number of layers. Every layer contains at least one node, with the first layer containing exactly one node (the root of the tree). The user can specify names to be assigned (randomly or in raster scan order) to the nodes. By default, the nodes are named with their index numbers.

The tree is output as an \((N-1)\times 2\) matrix of edges, where \(N\) is the number of nodes. The user can choose whether to order the edges (matrix rows) by the node names (which is the default choice), by layer, or randomly.

#
# This script defines a function `randomTree` that generates a random tree (connected acyclic graph)
# and outputs it in the form of a matrix of edges.
#
# Inputs:
#   nodes = the number of nodes in the tree (>= 1)
#   layers = the number of layers in the tree (>= 1, <= nodes)
#   node_labels = an optional vector of labels for the nodes (1 : nodes if omitted)
#   label_order = the order to apply node labels to nodes ("R" = random order (default),
#                "S" = raster scan order (top to bottom, left to right))
#   edge_order = the order of edges in the edge list ("N" = ordered by name (default),
#                "L" = ordered by layer, "R" = random)
#
# Output:
#   an (N-1) x 2 matrix of edges in the tree, where N is the number of nodes
#
randomTree <- function(nodes, layers, node_labels = NULL, label_order = "R", edge_order = "N") {
  ## Process the arguments.
  # Check the requested dimensions.
  if (nodes <= 1) {
    stop("The number of nodes must be at least 2.")
  } else if (layers > nodes) {
    stop("The tree cannot contain more layers than nodes.")
  }
  # Process the node labels argument.
  if (is.null(node_labels)) {
    node_labels <- 1:nodes
  } else if (length(node_labels) < nodes) {
    stop("An insufficient number of node labels was provided.")
  }
  # If the edge order argument is not one of the choices, default it to "N".
  edge_order <- toupper(edge_order)
  if (!edge_order %in% c("N", "L", "R")) {
    edge_order <- "N"
  }
  # If the label order argument is not one of the choices, default it to "R".
  label_order <- toupper(label_order)
  if (!label_order %in% c("S", "R")) {
    label_order <- "R"
  }
  ## Create a data frame of nodes.
  # Randomly generate a layer for every node, ensuring that no layer is empty and that the first
  # layer is a singleton.
  layer_no <- c(1:layers, sample(2:layers, size = nodes - layers, replace = TRUE)) |> sort()
  # Initialize a data frame with a row for every node, containing the nodel index and layer number
  # and a placeholder for the predecessor.
  df <- data.frame(
    Label = 1:nodes,
    Layer = layer_no,
    Predecessor = NA
  )
  # Compute the layer sizes.
  layer_size <- tabulate(layer_no)
  # Proceed layer by layer, assigning predecessors randomly from the previous layer.
  previous <- df[df$Layer == 1, "Label"]  # labels of nodes in the previous layer
  for (i in 2:layers) {
    parents <- unique(df[df$Layer == i - 1, "Label"])
    df[df$Layer == i, "Predecessor"] <- 
      parents |> sample(size = layer_size[i], replace = TRUE) |> sort()
  }
  # Randomize the node labels if desired.
  if (label_order == "R") {
    node_labels <- sample(node_labels, nodes, replace = FALSE)
  }
  # Replace label and predecessor numbers with the corresponding labels.
  df$Label <- node_labels[df$Label]
  df$Predecessor <- node_labels[df$Predecessor]
  # Drop the first row (root node) and sort the rest of the data frame according to the `edge_order`
  # argument.
  df <- df[-1, ]
  if (edge_order == "L") {
    # Sort by layer.
    df <- df[order(df$Layer), ]
  } else if (edge_order == "R") {
    # Randomize the order of the rows.
    df <- df[sample.int(nodes - 1, size = nodes - 1, replace = FALSE), ]
  } else {
    # Order the rows by the predecessor label first, node label second.
    df <- df[with(df, order(df$Predecessor, df$Label)), ]
  }
  ## Produce a matrix of edges.
  edges <- as.matrix(df[, c("Predecessor", "Label")])
  colnames(edges) <- c("Tail", "Head")
  # Get rid of row names (indices).
  rownames(edges) <- NULL
  # Return the result
  edges
}

Demonstration

We demonstrate the use of the function by generating an example tree, naming the nodes with capital letters.

# Set the random seed (for reproducibility).
set.seed(12345)
# Set the tree size (number of nodes, number of layers) and node labels.
nNodes <- 15
nLayers <- 4
node_names <- LETTERS[1:nNodes]
# Generate the tree as a matrix of edges, with node labels randomized and edges in name order.
tree <- randomTree(nNodes, nLayers, node_names, "R", "N")

This is the edge matrix (ordered by node name).

tree
      Tail Head
 [1,] "B"  "E" 
 [2,] "B"  "K" 
 [3,] "B"  "O" 
 [4,] "G"  "F" 
 [5,] "I"  "M" 
 [6,] "I"  "N" 
 [7,] "J"  "A" 
 [8,] "M"  "B" 
 [9,] "M"  "C" 
[10,] "M"  "H" 
[11,] "M"  "L" 
[12,] "N"  "D" 
[13,] "N"  "G" 
[14,] "N"  "J" 

We can plot the graph to verify that it is a rooted tree.

# Create a data frame of nodes.
nodes <- create_node_df(nNodes, label = node_names)
# Convert the edge tails and heads from character to integer.
nodeID <- function(x) nodes[nodes$label == x, "id"]
arcs <- create_edge_df(from = sapply(tree[, "Tail"], nodeID),
                       to = sapply(tree[, "Head"], nodeID))
# Create a graph object.
graph <- create_graph(nodes_df = nodes, edges_df = arcs, directed = TRUE)
render_graph(graph, layout = "tree")

We can produce the same graph but with node labels in raster scan order and the edge matrix in random order.

# Reset the random seed so as to get the same graph structure.
set.seed(12345)
# Generate the tree as a matrix of edges, with node labels in raster scan order and edges
# in random order.
tree <- randomTree(nNodes, nLayers, node_names, "S", "R")
# Display the edge matrix.
tree
      Tail Head
 [1,] "C"  "J" 
 [2,] "G"  "N" 
 [3,] "B"  "E" 
 [4,] "E"  "K" 
 [5,] "C"  "H" 
 [6,] "A"  "C" 
 [7,] "G"  "M" 
 [8,] "B"  "D" 
 [9,] "C"  "I" 
[10,] "A"  "B" 
[11,] "C"  "G" 
[12,] "B"  "F" 
[13,] "G"  "O" 
[14,] "F"  "L" 

We again plot the graph to verify the changes we made. Unfortunately, the rendering process may not preserve the order of nodes within a layer.

# Create a data frame of nodes.
nodes <- create_node_df(nNodes, label = node_names)
# Convert the edge tails and heads from character to integer.
nodeID <- function(x) nodes[nodes$label == x, "id"]
arcs <- create_edge_df(from = sapply(tree[, "Tail"], nodeID),
                       to = sapply(tree[, "Head"], nodeID))
# Create a graph object.
graph <- create_graph(nodes_df = nodes, edges_df = arcs, directed = TRUE)
render_graph(graph, layout = "tree")
LS0tCnRpdGxlOiAiVHJlZSBHZW5lcmF0b3IiCmF1dGhvcjogIlBhdWwgQS4gUnViaW4gKHJ1YmluQG1zdS5lZHUpIgpkYXRlOiAiSmFudWFyeSAzMCwgMjAyMyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBub3RlYm9vayBpcyBwdWJsaXNoZWQgdW5kZXIgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIDMuMCBVbnBvcnRlZCBsaWNlbnNlXShodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvMy4wL2RlZWQuZW5fVVMpLgoKIyBJbnRyb2R1Y3Rpb24KCldlIGRlbW9uc3RyYXRlIGEgc2ltcGxlICg/KSBmdW5jdGlvbiB0byBnZW5lcmF0ZSBhIHJhbmRvbSB0cmVlIChhIGNvbm5lY3RlZCwgY3ljbGUtZnJlZSB1bmRpcmVjdGVkIGdyYXBoIHdpdGggYSBzaW5nbGUgcm9vdCBub2RlKS4KCiMgTGlicmFyaWVzIHVzZWQKClRoZSBncmFwaCBnZW5lcmF0aW9uIGZ1bmN0aW9uIGlzIHNlbGYtY29udGFpbmVkICh1c2VzIG9ubHkgZWxlbWVudHMgb2YgYmFzZSBSKS4gVG8gZGlzcGxheSB0aGUgZ3JhcGgsIHdlIGltcG9ydCB0aGUgRGlhZ3JhbW1lUiBwYWNrYWdlLgoKYGBge3J9CmxpYnJhcnkoRGlhZ3JhbW1lUikKYGBgCgojIEZ1bmN0aW9uIGRlZmluaXRpb24KClRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gZ2VuZXJhdGVzIGEgcmFuZG9tIHRyZWUgZ3JhcGggd2l0aCBhIHNwZWNpZmllZCBudW1iZXIgb2Ygbm9kZXMgYW5kIGEgc3BlY2lmaWVkIG51bWJlciBvZiBsYXllcnMuIEV2ZXJ5IGxheWVyIGNvbnRhaW5zIGF0IGxlYXN0IG9uZSBub2RlLCB3aXRoIHRoZSBmaXJzdCBsYXllciBjb250YWluaW5nIGV4YWN0bHkgb25lIG5vZGUgKHRoZSByb290IG9mIHRoZSB0cmVlKS4gVGhlIHVzZXIgY2FuIHNwZWNpZnkgbmFtZXMgdG8gYmUgYXNzaWduZWQgKHJhbmRvbWx5IG9yIGluIHJhc3RlciBzY2FuIG9yZGVyKSB0byB0aGUgbm9kZXMuIEJ5IGRlZmF1bHQsIHRoZSBub2RlcyBhcmUgbmFtZWQgd2l0aCB0aGVpciBpbmRleCBudW1iZXJzLgoKVGhlIHRyZWUgaXMgb3V0cHV0IGFzIGFuICQoTi0xKVx0aW1lcyAyJCBtYXRyaXggb2YgZWRnZXMsIHdoZXJlICROJCBpcyB0aGUgbnVtYmVyIG9mIG5vZGVzLiBUaGUgdXNlciBjYW4gY2hvb3NlIHdoZXRoZXIgdG8gb3JkZXIgdGhlIGVkZ2VzIChtYXRyaXggcm93cykgYnkgdGhlIG5vZGUgbmFtZXMgKHdoaWNoIGlzIHRoZSBkZWZhdWx0IGNob2ljZSksIGJ5IGxheWVyLCBvciByYW5kb21seS4KCmBgYHtyfQojCiMgVGhpcyBzY3JpcHQgZGVmaW5lcyBhIGZ1bmN0aW9uIGByYW5kb21UcmVlYCB0aGF0IGdlbmVyYXRlcyBhIHJhbmRvbSB0cmVlIChjb25uZWN0ZWQgYWN5Y2xpYyBncmFwaCkKIyBhbmQgb3V0cHV0cyBpdCBpbiB0aGUgZm9ybSBvZiBhIG1hdHJpeCBvZiBlZGdlcy4KIwojIElucHV0czoKIyAgIG5vZGVzID0gdGhlIG51bWJlciBvZiBub2RlcyBpbiB0aGUgdHJlZSAoPj0gMSkKIyAgIGxheWVycyA9IHRoZSBudW1iZXIgb2YgbGF5ZXJzIGluIHRoZSB0cmVlICg+PSAxLCA8PSBub2RlcykKIyAgIG5vZGVfbGFiZWxzID0gYW4gb3B0aW9uYWwgdmVjdG9yIG9mIGxhYmVscyBmb3IgdGhlIG5vZGVzICgxIDogbm9kZXMgaWYgb21pdHRlZCkKIyAgIGxhYmVsX29yZGVyID0gdGhlIG9yZGVyIHRvIGFwcGx5IG5vZGUgbGFiZWxzIHRvIG5vZGVzICgiUiIgPSByYW5kb20gb3JkZXIgKGRlZmF1bHQpLAojICAgICAgICAgICAgICAgICJTIiA9IHJhc3RlciBzY2FuIG9yZGVyICh0b3AgdG8gYm90dG9tLCBsZWZ0IHRvIHJpZ2h0KSkKIyAgIGVkZ2Vfb3JkZXIgPSB0aGUgb3JkZXIgb2YgZWRnZXMgaW4gdGhlIGVkZ2UgbGlzdCAoIk4iID0gb3JkZXJlZCBieSBuYW1lIChkZWZhdWx0KSwKIyAgICAgICAgICAgICAgICAiTCIgPSBvcmRlcmVkIGJ5IGxheWVyLCAiUiIgPSByYW5kb20pCiMKIyBPdXRwdXQ6CiMgICBhbiAoTi0xKSB4IDIgbWF0cml4IG9mIGVkZ2VzIGluIHRoZSB0cmVlLCB3aGVyZSBOIGlzIHRoZSBudW1iZXIgb2Ygbm9kZXMKIwpyYW5kb21UcmVlIDwtIGZ1bmN0aW9uKG5vZGVzLCBsYXllcnMsIG5vZGVfbGFiZWxzID0gTlVMTCwgbGFiZWxfb3JkZXIgPSAiUiIsIGVkZ2Vfb3JkZXIgPSAiTiIpIHsKICAjIyBQcm9jZXNzIHRoZSBhcmd1bWVudHMuCiAgIyBDaGVjayB0aGUgcmVxdWVzdGVkIGRpbWVuc2lvbnMuCiAgaWYgKG5vZGVzIDw9IDEpIHsKICAgIHN0b3AoIlRoZSBudW1iZXIgb2Ygbm9kZXMgbXVzdCBiZSBhdCBsZWFzdCAyLiIpCiAgfSBlbHNlIGlmIChsYXllcnMgPiBub2RlcykgewogICAgc3RvcCgiVGhlIHRyZWUgY2Fubm90IGNvbnRhaW4gbW9yZSBsYXllcnMgdGhhbiBub2Rlcy4iKQogIH0KICAjIFByb2Nlc3MgdGhlIG5vZGUgbGFiZWxzIGFyZ3VtZW50LgogIGlmIChpcy5udWxsKG5vZGVfbGFiZWxzKSkgewogICAgbm9kZV9sYWJlbHMgPC0gMTpub2RlcwogIH0gZWxzZSBpZiAobGVuZ3RoKG5vZGVfbGFiZWxzKSA8IG5vZGVzKSB7CiAgICBzdG9wKCJBbiBpbnN1ZmZpY2llbnQgbnVtYmVyIG9mIG5vZGUgbGFiZWxzIHdhcyBwcm92aWRlZC4iKQogIH0KICAjIElmIHRoZSBlZGdlIG9yZGVyIGFyZ3VtZW50IGlzIG5vdCBvbmUgb2YgdGhlIGNob2ljZXMsIGRlZmF1bHQgaXQgdG8gIk4iLgogIGVkZ2Vfb3JkZXIgPC0gdG91cHBlcihlZGdlX29yZGVyKQogIGlmICghZWRnZV9vcmRlciAlaW4lIGMoIk4iLCAiTCIsICJSIikpIHsKICAgIGVkZ2Vfb3JkZXIgPC0gIk4iCiAgfQogICMgSWYgdGhlIGxhYmVsIG9yZGVyIGFyZ3VtZW50IGlzIG5vdCBvbmUgb2YgdGhlIGNob2ljZXMsIGRlZmF1bHQgaXQgdG8gIlIiLgogIGxhYmVsX29yZGVyIDwtIHRvdXBwZXIobGFiZWxfb3JkZXIpCiAgaWYgKCFsYWJlbF9vcmRlciAlaW4lIGMoIlMiLCAiUiIpKSB7CiAgICBsYWJlbF9vcmRlciA8LSAiUiIKICB9CiAgIyMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBub2Rlcy4KICAjIFJhbmRvbWx5IGdlbmVyYXRlIGEgbGF5ZXIgZm9yIGV2ZXJ5IG5vZGUsIGVuc3VyaW5nIHRoYXQgbm8gbGF5ZXIgaXMgZW1wdHkgYW5kIHRoYXQgdGhlIGZpcnN0CiAgIyBsYXllciBpcyBhIHNpbmdsZXRvbi4KICBsYXllcl9ubyA8LSBjKDE6bGF5ZXJzLCBzYW1wbGUoMjpsYXllcnMsIHNpemUgPSBub2RlcyAtIGxheWVycywgcmVwbGFjZSA9IFRSVUUpKSB8PiBzb3J0KCkKICAjIEluaXRpYWxpemUgYSBkYXRhIGZyYW1lIHdpdGggYSByb3cgZm9yIGV2ZXJ5IG5vZGUsIGNvbnRhaW5pbmcgdGhlIG5vZGVsIGluZGV4IGFuZCBsYXllciBudW1iZXIKICAjIGFuZCBhIHBsYWNlaG9sZGVyIGZvciB0aGUgcHJlZGVjZXNzb3IuCiAgZGYgPC0gZGF0YS5mcmFtZSgKICAgIExhYmVsID0gMTpub2RlcywKICAgIExheWVyID0gbGF5ZXJfbm8sCiAgICBQcmVkZWNlc3NvciA9IE5BCiAgKQogICMgQ29tcHV0ZSB0aGUgbGF5ZXIgc2l6ZXMuCiAgbGF5ZXJfc2l6ZSA8LSB0YWJ1bGF0ZShsYXllcl9ubykKICAjIFByb2NlZWQgbGF5ZXIgYnkgbGF5ZXIsIGFzc2lnbmluZyBwcmVkZWNlc3NvcnMgcmFuZG9tbHkgZnJvbSB0aGUgcHJldmlvdXMgbGF5ZXIuCiAgcHJldmlvdXMgPC0gZGZbZGYkTGF5ZXIgPT0gMSwgIkxhYmVsIl0gICMgbGFiZWxzIG9mIG5vZGVzIGluIHRoZSBwcmV2aW91cyBsYXllcgogIGZvciAoaSBpbiAyOmxheWVycykgewogICAgcGFyZW50cyA8LSB1bmlxdWUoZGZbZGYkTGF5ZXIgPT0gaSAtIDEsICJMYWJlbCJdKQogICAgZGZbZGYkTGF5ZXIgPT0gaSwgIlByZWRlY2Vzc29yIl0gPC0gCiAgICAgIHBhcmVudHMgfD4gc2FtcGxlKHNpemUgPSBsYXllcl9zaXplW2ldLCByZXBsYWNlID0gVFJVRSkgfD4gc29ydCgpCiAgfQogICMgUmFuZG9taXplIHRoZSBub2RlIGxhYmVscyBpZiBkZXNpcmVkLgogIGlmIChsYWJlbF9vcmRlciA9PSAiUiIpIHsKICAgIG5vZGVfbGFiZWxzIDwtIHNhbXBsZShub2RlX2xhYmVscywgbm9kZXMsIHJlcGxhY2UgPSBGQUxTRSkKICB9CiAgIyBSZXBsYWNlIGxhYmVsIGFuZCBwcmVkZWNlc3NvciBudW1iZXJzIHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgbGFiZWxzLgogIGRmJExhYmVsIDwtIG5vZGVfbGFiZWxzW2RmJExhYmVsXQogIGRmJFByZWRlY2Vzc29yIDwtIG5vZGVfbGFiZWxzW2RmJFByZWRlY2Vzc29yXQogICMgRHJvcCB0aGUgZmlyc3Qgcm93IChyb290IG5vZGUpIGFuZCBzb3J0IHRoZSByZXN0IG9mIHRoZSBkYXRhIGZyYW1lIGFjY29yZGluZyB0byB0aGUgYGVkZ2Vfb3JkZXJgCiAgIyBhcmd1bWVudC4KICBkZiA8LSBkZlstMSwgXQogIGlmIChlZGdlX29yZGVyID09ICJMIikgewogICAgIyBTb3J0IGJ5IGxheWVyLgogICAgZGYgPC0gZGZbb3JkZXIoZGYkTGF5ZXIpLCBdCiAgfSBlbHNlIGlmIChlZGdlX29yZGVyID09ICJSIikgewogICAgIyBSYW5kb21pemUgdGhlIG9yZGVyIG9mIHRoZSByb3dzLgogICAgZGYgPC0gZGZbc2FtcGxlLmludChub2RlcyAtIDEsIHNpemUgPSBub2RlcyAtIDEsIHJlcGxhY2UgPSBGQUxTRSksIF0KICB9IGVsc2UgewogICAgIyBPcmRlciB0aGUgcm93cyBieSB0aGUgcHJlZGVjZXNzb3IgbGFiZWwgZmlyc3QsIG5vZGUgbGFiZWwgc2Vjb25kLgogICAgZGYgPC0gZGZbd2l0aChkZiwgb3JkZXIoZGYkUHJlZGVjZXNzb3IsIGRmJExhYmVsKSksIF0KICB9CiAgIyMgUHJvZHVjZSBhIG1hdHJpeCBvZiBlZGdlcy4KICBlZGdlcyA8LSBhcy5tYXRyaXgoZGZbLCBjKCJQcmVkZWNlc3NvciIsICJMYWJlbCIpXSkKICBjb2xuYW1lcyhlZGdlcykgPC0gYygiVGFpbCIsICJIZWFkIikKICAjIEdldCByaWQgb2Ygcm93IG5hbWVzIChpbmRpY2VzKS4KICByb3duYW1lcyhlZGdlcykgPC0gTlVMTAogICMgUmV0dXJuIHRoZSByZXN1bHQKICBlZGdlcwp9CmBgYAoKIyBEZW1vbnN0cmF0aW9uCgpXZSBkZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIHRoZSBmdW5jdGlvbiBieSBnZW5lcmF0aW5nIGFuIGV4YW1wbGUgdHJlZSwgbmFtaW5nIHRoZSBub2RlcyB3aXRoIGNhcGl0YWwgbGV0dGVycy4KCmBgYHtyfQojIFNldCB0aGUgcmFuZG9tIHNlZWQgKGZvciByZXByb2R1Y2liaWxpdHkpLgpzZXQuc2VlZCgxMjM0NSkKIyBTZXQgdGhlIHRyZWUgc2l6ZSAobnVtYmVyIG9mIG5vZGVzLCBudW1iZXIgb2YgbGF5ZXJzKSBhbmQgbm9kZSBsYWJlbHMuCm5Ob2RlcyA8LSAxNQpuTGF5ZXJzIDwtIDQKbm9kZV9uYW1lcyA8LSBMRVRURVJTWzE6bk5vZGVzXQojIEdlbmVyYXRlIHRoZSB0cmVlIGFzIGEgbWF0cml4IG9mIGVkZ2VzLCB3aXRoIG5vZGUgbGFiZWxzIHJhbmRvbWl6ZWQgYW5kIGVkZ2VzIGluIG5hbWUgb3JkZXIuCnRyZWUgPC0gcmFuZG9tVHJlZShuTm9kZXMsIG5MYXllcnMsIG5vZGVfbmFtZXMsICJSIiwgIk4iKQpgYGAKClRoaXMgaXMgdGhlIGVkZ2UgbWF0cml4IChvcmRlcmVkIGJ5IG5vZGUgbmFtZSkuCgpgYGB7cn0KdHJlZQpgYGAKCldlIGNhbiBwbG90IHRoZSBncmFwaCB0byB2ZXJpZnkgdGhhdCBpdCBpcyBhIHJvb3RlZCB0cmVlLgoKYGBge3J9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBub2Rlcy4Kbm9kZXMgPC0gY3JlYXRlX25vZGVfZGYobk5vZGVzLCBsYWJlbCA9IG5vZGVfbmFtZXMpCiMgQ29udmVydCB0aGUgZWRnZSB0YWlscyBhbmQgaGVhZHMgZnJvbSBjaGFyYWN0ZXIgdG8gaW50ZWdlci4Kbm9kZUlEIDwtIGZ1bmN0aW9uKHgpIG5vZGVzW25vZGVzJGxhYmVsID09IHgsICJpZCJdCmFyY3MgPC0gY3JlYXRlX2VkZ2VfZGYoZnJvbSA9IHNhcHBseSh0cmVlWywgIlRhaWwiXSwgbm9kZUlEKSwKICAgICAgICAgICAgICAgICAgICAgICB0byA9IHNhcHBseSh0cmVlWywgIkhlYWQiXSwgbm9kZUlEKSkKIyBDcmVhdGUgYSBncmFwaCBvYmplY3QuCmdyYXBoIDwtIGNyZWF0ZV9ncmFwaChub2Rlc19kZiA9IG5vZGVzLCBlZGdlc19kZiA9IGFyY3MsIGRpcmVjdGVkID0gVFJVRSkKcmVuZGVyX2dyYXBoKGdyYXBoLCBsYXlvdXQgPSAidHJlZSIpCmBgYAoKV2UgY2FuIHByb2R1Y2UgdGhlIHNhbWUgZ3JhcGggYnV0IHdpdGggbm9kZSBsYWJlbHMgaW4gcmFzdGVyIHNjYW4gb3JkZXIgYW5kIHRoZSBlZGdlIG1hdHJpeCBpbiByYW5kb20gb3JkZXIuCgpgYGB7cn0KIyBSZXNldCB0aGUgcmFuZG9tIHNlZWQgc28gYXMgdG8gZ2V0IHRoZSBzYW1lIGdyYXBoIHN0cnVjdHVyZS4Kc2V0LnNlZWQoMTIzNDUpCiMgR2VuZXJhdGUgdGhlIHRyZWUgYXMgYSBtYXRyaXggb2YgZWRnZXMsIHdpdGggbm9kZSBsYWJlbHMgaW4gcmFzdGVyIHNjYW4gb3JkZXIgYW5kIGVkZ2VzCiMgaW4gcmFuZG9tIG9yZGVyLgp0cmVlIDwtIHJhbmRvbVRyZWUobk5vZGVzLCBuTGF5ZXJzLCBub2RlX25hbWVzLCAiUyIsICJSIikKIyBEaXNwbGF5IHRoZSBlZGdlIG1hdHJpeC4KdHJlZQpgYGAKCldlIGFnYWluIHBsb3QgdGhlIGdyYXBoIHRvIHZlcmlmeSB0aGUgY2hhbmdlcyB3ZSBtYWRlLiBVbmZvcnR1bmF0ZWx5LCB0aGUgcmVuZGVyaW5nIHByb2Nlc3MgbWF5IG5vdCBwcmVzZXJ2ZSB0aGUgb3JkZXIgb2Ygbm9kZXMgd2l0aGluIGEgbGF5ZXIuCgpgYGB7cn0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIG9mIG5vZGVzLgpub2RlcyA8LSBjcmVhdGVfbm9kZV9kZihuTm9kZXMsIGxhYmVsID0gbm9kZV9uYW1lcykKIyBDb252ZXJ0IHRoZSBlZGdlIHRhaWxzIGFuZCBoZWFkcyBmcm9tIGNoYXJhY3RlciB0byBpbnRlZ2VyLgpub2RlSUQgPC0gZnVuY3Rpb24oeCkgbm9kZXNbbm9kZXMkbGFiZWwgPT0geCwgImlkIl0KYXJjcyA8LSBjcmVhdGVfZWRnZV9kZihmcm9tID0gc2FwcGx5KHRyZWVbLCAiVGFpbCJdLCBub2RlSUQpLAogICAgICAgICAgICAgICAgICAgICAgIHRvID0gc2FwcGx5KHRyZWVbLCAiSGVhZCJdLCBub2RlSUQpKQojIENyZWF0ZSBhIGdyYXBoIG9iamVjdC4KZ3JhcGggPC0gY3JlYXRlX2dyYXBoKG5vZGVzX2RmID0gbm9kZXMsIGVkZ2VzX2RmID0gYXJjcywgZGlyZWN0ZWQgPSBUUlVFKQpyZW5kZXJfZ3JhcGgoZ3JhcGgsIGxheW91dCA9ICJ0cmVlIikKYGBgCgo=