Packages


library(spatstat)
library(sf)

Gerar dados de exemplo para árvores em dois grupos (agregado e disperso)


# Grupo 1: Árvores agregadas (clustered)
set.seed(1)
coords_agregado = data.frame(
  x = rnorm(50, mean = 10, sd = 1),  # coordenadas agregadas
  y = rnorm(50, mean = 10, sd = 1)
)

# Grupo 2: Árvores dispersas
set.seed(2)
coords_disperso = data.frame(
  x = runif(50, min = 5, max = 20),  # coordenadas mais espalhadas
  y = runif(50, min = 5, max = 20)
)

Definir os polígonos (áreas de pastagem) para cada grupo


# Polígono 1 para o grupo agregado
poligono1 = owin(xrange = c(8, 12), yrange = c(8, 12))

# Polígono 2 para o grupo disperso
poligono2 = owin(xrange = c(5, 20), yrange = c(5, 20))

Converter as coordenadas para objetos espaciais do spatstat


# Pontos agregados
pontos_agregado = ppp(coords_agregado$x, coords_agregado$y, window = poligono1)
Warning: 3 points were rejected as lying outside the specified window
str(pontos_agregado)
List of 5
 $ window    :List of 4
  ..$ type  : chr "rectangle"
  ..$ xrange: num [1:2] 8 12
  ..$ yrange: num [1:2] 8 12
  ..$ units :List of 3
  .. ..$ singular  : chr "unit"
  .. ..$ plural    : chr "units"
  .. ..$ multiplier: num 1
  .. ..- attr(*, "class")= chr "unitname"
  ..- attr(*, "class")= chr "owin"
 $ n         : int 47
 $ x         : num [1:47] 9.37 10.18 9.16 11.6 10.33 ...
 $ y         : num [1:47] 10.4 9.39 10.34 8.87 11.43 ...
 $ markformat: chr "none"
 - attr(*, "class")= chr "ppp"
 - attr(*, "rejects")=List of 5
  ..$ window    :List of 5
  .. ..$ type  : chr "polygonal"
  .. ..$ xrange: num [1:2] 7.64 12
  .. ..$ yrange: num [1:2] 8 12.5
  .. ..$ bdry  :List of 1
  .. .. ..$ :List of 2
  .. .. .. ..$ x: num [1:6] 11.62 9.13 7.64 7.88 9.99 ...
  .. .. .. ..$ y: num [1:6] 12.54 12.09 10.01 8.99 8.06 ...
  .. ..$ units :List of 3
  .. .. ..$ singular  : chr "unit"
  .. .. ..$ plural    : chr "units"
  .. .. ..$ multiplier: num 1
  .. .. ..- attr(*, "class")= chr "unitname"
  .. ..- attr(*, "class")= chr "owin"
  ..$ n         : int 3
  ..$ x         : num [1:3] 11.51 7.79 10.59
  ..$ y         : num [1:3] 12.4 10 12.2
  ..$ markformat: chr "none"
  ..- attr(*, "class")= chr "ppp"
# Pontos dispersos
pontos_disperso = ppp(coords_disperso$x, coords_disperso$y, window = poligono2)
str(pontos_disperso)
List of 5
 $ window    :List of 4
  ..$ type  : chr "rectangle"
  ..$ xrange: num [1:2] 5 20
  ..$ yrange: num [1:2] 5 20
  ..$ units :List of 3
  .. ..$ singular  : chr "unit"
  .. ..$ plural    : chr "units"
  .. ..$ multiplier: num 1
  .. ..- attr(*, "class")= chr "unitname"
  ..- attr(*, "class")= chr "owin"
 $ n         : int 50
 $ x         : num [1:50] 7.77 15.54 13.6 7.52 19.16 ...
 $ y         : num [1:50] 5.11 5.22 15.25 18.95 9.13 ...
 $ markformat: chr "none"
 - attr(*, "class")= chr "ppp"

Função para calcular o Índice de Morisita


calcular_morisita = function(pontos, num_celulas) {
  # Dividir o polígono em uma grade com `num_celulas` por `num_celulas`
  grid = quadratcount(pontos, nx = num_celulas, ny = num_celulas)
  # Número total de árvores
  N = sum(grid)
  # Número de células
  n = num_celulas^2
  # Contagem de árvores em cada célula
  X_i = as.vector(grid)
  # Calcular o Índice de Morisita
  I_M = (n * sum(X_i * (X_i - 1))) / (N * (N - 1))
  return(I_M)
}
calcular_morisita
function(pontos, num_celulas) {
  # Dividir o polígono em uma grade com `num_celulas` por `num_celulas`
  grid = quadratcount(pontos, nx = num_celulas, ny = num_celulas)
  # Número total de árvores
  N = sum(grid)
  # Número de células
  n = num_celulas^2
  # Contagem de árvores em cada célula
  X_i = as.vector(grid)
  # Calcular o Índice de Morisita
  I_M = (n * sum(X_i * (X_i - 1))) / (N * (N - 1))
  return(I_M)
}

Calcular o Índice de Morisita para cada grupo


indice_morisita_agregado = calcular_morisita(pontos_agregado, num_celulas = 5)
indice_morisita_agregado
[1] 1.480111
indice_morisita_disperso = calcular_morisita(pontos_disperso, num_celulas = 5)
indice_morisita_disperso
[1] 1.142857
# Exibir os resultados
cat("Índice de Morisita (Agregado):", indice_morisita_agregado, "\n")
Índice de Morisita (Agregado): 1.480111 
cat("Índice de Morisita (Disperso):", indice_morisita_disperso, "\n")
Índice de Morisita (Disperso): 1.142857 

Valores de Morisita maiores que 1 indicam agregação, aproximadamente 1 indicam aleatoriedade, e menores que 1 indicam dispersão.


Visualizar a distribuição espacial com a grade sobreposta


# Grupo Agregado
plot(pontos_agregado, main = "Grupo Agregado", pch = 20)

grid_agregado = quadratcount(pontos_agregado, nx = 5, ny = 5)  # Cria a grade
# Plotar a grade em um gráfico separado
plot(grid_agregado, main = "Contagem de Quadrantes (Agregado)", col = "red")


# Grupo Disperso
plot(pontos_disperso, main = "Grupo Disperso", pch = 20)

grid_disperso = quadratcount(pontos_disperso, nx = 5, ny = 5)  # Cria a grade
# Plotar a grade em um gráfico separado
plot(grid_disperso, main = "Contagem de Quadrantes (Disperso)", col = "red")


Tentando adaptar o indice de morisita usando o raio


Data

set.seed(1)
coords_agregado = data.frame(
  x = rnorm(50, mean = 10, sd = 1),
  y = rnorm(50, mean = 10, sd = 1),
  raio = runif(50, 0.1, 0.5)
)
coords_agregado$superficie_coberta = pi * (coords_agregado$raio^2)
coords_agregado

set.seed(2)
coords_disperso = data.frame(
  x = runif(50, min = 5, max = 20),
  y = runif(50, min = 5, max = 20),
  raio = runif(50, 0.1, 0.5)
)
coords_disperso$superficie_coberta = pi * (coords_disperso$raio^2)
coords_disperso

Definir limites dos polígonos


limites_agregado = owin(xrange = c(8, 12), yrange = c(8, 12))
limites_disperso = owin(xrange = c(5, 20), yrange = c(5, 20))

Função para calcular o Índice de Morisita considerando superfície coberta


calcular_morisita_superficie = function(coords, num_celulas, poligono) {
  largura_celula = (poligono$xrange[2] - poligono$xrange[1]) / num_celulas
  altura_celula = (poligono$yrange[2] - poligono$yrange[1]) / num_celulas
  # Inicializar a grade
  grid_superficie = matrix(0, nrow = num_celulas, ncol = num_celulas)
  # Preencher a grade com a superfície coberta
  for (i in 1:nrow(coords)) {
    x_pos = min(num_celulas, max(1, floor((coords$x[i] - poligono$xrange[1]) / largura_celula) + 1))
    y_pos = min(num_celulas, max(1, floor((coords$y[i] - poligono$yrange[1]) / altura_celula) + 1))
    grid_superficie[x_pos, y_pos] <- grid_superficie[x_pos, y_pos] + coords$superficie_coberta[i]
  }
  # Calcular o Índice de Morisita modificado
  N = sum(grid_superficie) # Total de área coberta
  n = num_celulas^2        # Número de células
  X_i = as.vector(grid_superficie)
  
  I_M = if (N > 1) {
    (n * sum(X_i * (X_i - 1))) / (N * (N - 1))
  } else {
    0  # Caso especial onde N é muito pequeno
  }
  return(I_M)
}

Calcular o Índice de Morisita para ambos os grupos


indice_morisita_agregado = calcular_morisita_superficie(coords_agregado, num_celulas = 5, limites_agregado)
indice_morisita_disperso = calcular_morisita_superficie(coords_disperso, num_celulas = 5, limites_disperso)
cat("Índice de Morisita (Agregado com Superfície Coberta):", indice_morisita_agregado, "\n")
Índice de Morisita (Agregado com Superfície Coberta): 0.2192138 
cat("Índice de Morisita (Disperso com Superfície Coberta):", indice_morisita_disperso, "\n")
Índice de Morisita (Disperso com Superfície Coberta): 0.2984291 

Plot


plot_com_grade = function(coords, poligono, num_celulas, titulo) {
  # Configuração da janela gráfica
  plot(NA, xlim = poligono$xrange, ylim = poligono$yrange, xlab = "X", ylab = "Y", main = titulo)
  largura_celula <- (poligono$xrange[2] - poligono$xrange[1]) / num_celulas
  altura_celula <- (poligono$yrange[2] - poligono$yrange[1]) / num_celulas
  # Adicionar a grade
  for (i in 0:num_celulas) {
    abline(v = poligono$xrange[1] + i * largura_celula, col = "gray", lty = 2)
    abline(h = poligono$yrange[1] + i * altura_celula, col = "gray", lty = 2)
  }
  # Plotar cada ponto e o raio (raiz da superfície coberta)
  points(coords$x, coords$y, pch = 16, col = "blue")
  symbols(coords$x, coords$y, circles = sqrt(coords$superficie_coberta / pi), add = TRUE, inches = FALSE, fg = "red")
}

# Plotar grupo agregado e disperso com grade e raios
plot_com_grade(coords_agregado, limites_agregado, 5, "Grupo Agregado com Superfície Coberta")

plot_com_grade(coords_disperso, limites_disperso, 5, "Grupo Disperso com Superfície Coberta")

LS0tDQp0aXRsZTogIkRldGVybWluYW5kbyBhIGRpc3RyaWJ1acOnw6NvIGRhcyDDgXJ2b3JlcyBjb20gbyDDrW5kaWNlIGRlIE1vcmlzaXRhIg0KYXV0aG9yOiAiVmFnbmVyIE92YW5pIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19kZXB0aDogMg0KICAgIHRoZW1lOiB1bml0ZWQNCi0tLQ0KDQoqKioNCiMgKipQYWNrYWdlcyoqDQoqKioNCg0KYGBge3J9DQpsaWJyYXJ5KHNwYXRzdGF0KQ0KbGlicmFyeShzZikNCmBgYA0KDQoqKioNCiMgKipHZXJhciBkYWRvcyBkZSBleGVtcGxvIHBhcmEgw6Fydm9yZXMgZW0gZG9pcyBncnVwb3MgKGFncmVnYWRvIGUgZGlzcGVyc28pKioNCioqKg0KDQpgYGB7cn0NCiMgR3J1cG8gMTogw4Fydm9yZXMgYWdyZWdhZGFzIChjbHVzdGVyZWQpDQpzZXQuc2VlZCgxKQ0KY29vcmRzX2FncmVnYWRvID0gZGF0YS5mcmFtZSgNCiAgeCA9IHJub3JtKDUwLCBtZWFuID0gMTAsIHNkID0gMSksICAjIGNvb3JkZW5hZGFzIGFncmVnYWRhcw0KICB5ID0gcm5vcm0oNTAsIG1lYW4gPSAxMCwgc2QgPSAxKQ0KKQ0KDQojIEdydXBvIDI6IMOBcnZvcmVzIGRpc3BlcnNhcw0Kc2V0LnNlZWQoMikNCmNvb3Jkc19kaXNwZXJzbyA9IGRhdGEuZnJhbWUoDQogIHggPSBydW5pZig1MCwgbWluID0gNSwgbWF4ID0gMjApLCAgIyBjb29yZGVuYWRhcyBtYWlzIGVzcGFsaGFkYXMNCiAgeSA9IHJ1bmlmKDUwLCBtaW4gPSA1LCBtYXggPSAyMCkNCikNCmBgYA0KDQoqKioNCiMgKipEZWZpbmlyIG9zIHBvbMOtZ29ub3MgKMOhcmVhcyBkZSBwYXN0YWdlbSkgcGFyYSBjYWRhIGdydXBvKioNCioqKg0KDQpgYGB7cn0NCiMgUG9sw61nb25vIDEgcGFyYSBvIGdydXBvIGFncmVnYWRvDQpwb2xpZ29ubzEgPSBvd2luKHhyYW5nZSA9IGMoOCwgMTIpLCB5cmFuZ2UgPSBjKDgsIDEyKSkNCg0KIyBQb2zDrWdvbm8gMiBwYXJhIG8gZ3J1cG8gZGlzcGVyc28NCnBvbGlnb25vMiA9IG93aW4oeHJhbmdlID0gYyg1LCAyMCksIHlyYW5nZSA9IGMoNSwgMjApKQ0KYGBgDQoNCioqKg0KIyAqKkNvbnZlcnRlciBhcyBjb29yZGVuYWRhcyBwYXJhIG9iamV0b3MgZXNwYWNpYWlzIGRvIGBzcGF0c3RhdGAqKg0KKioqDQoNCmBgYHtyfQ0KIyBQb250b3MgYWdyZWdhZG9zDQpwb250b3NfYWdyZWdhZG8gPSBwcHAoY29vcmRzX2FncmVnYWRvJHgsIGNvb3Jkc19hZ3JlZ2FkbyR5LCB3aW5kb3cgPSBwb2xpZ29ubzEpDQpzdHIocG9udG9zX2FncmVnYWRvKQ0KDQojIFBvbnRvcyBkaXNwZXJzb3MNCnBvbnRvc19kaXNwZXJzbyA9IHBwcChjb29yZHNfZGlzcGVyc28keCwgY29vcmRzX2Rpc3BlcnNvJHksIHdpbmRvdyA9IHBvbGlnb25vMikNCnN0cihwb250b3NfZGlzcGVyc28pDQpgYGANCg0KKioqDQojICoqRnVuw6fDo28gcGFyYSBjYWxjdWxhciBvIMONbmRpY2UgZGUgTW9yaXNpdGEqKg0KKioqDQoNCmBgYHtyfQ0KY2FsY3VsYXJfbW9yaXNpdGEgPSBmdW5jdGlvbihwb250b3MsIG51bV9jZWx1bGFzKSB7DQogICMgRGl2aWRpciBvIHBvbMOtZ29ubyBlbSB1bWEgZ3JhZGUgY29tIGBudW1fY2VsdWxhc2AgcG9yIGBudW1fY2VsdWxhc2ANCiAgZ3JpZCA9IHF1YWRyYXRjb3VudChwb250b3MsIG54ID0gbnVtX2NlbHVsYXMsIG55ID0gbnVtX2NlbHVsYXMpDQogICMgTsO6bWVybyB0b3RhbCBkZSDDoXJ2b3Jlcw0KICBOID0gc3VtKGdyaWQpDQogICMgTsO6bWVybyBkZSBjw6lsdWxhcw0KICBuID0gbnVtX2NlbHVsYXNeMg0KICAjIENvbnRhZ2VtIGRlIMOhcnZvcmVzIGVtIGNhZGEgY8OpbHVsYQ0KICBYX2kgPSBhcy52ZWN0b3IoZ3JpZCkNCiAgIyBDYWxjdWxhciBvIMONbmRpY2UgZGUgTW9yaXNpdGENCiAgSV9NID0gKG4gKiBzdW0oWF9pICogKFhfaSAtIDEpKSkgLyAoTiAqIChOIC0gMSkpDQogIHJldHVybihJX00pDQp9DQpjYWxjdWxhcl9tb3Jpc2l0YQ0KYGBgDQoNCioqKg0KIyAqKkNhbGN1bGFyIG8gw41uZGljZSBkZSBNb3Jpc2l0YSBwYXJhIGNhZGEgZ3J1cG8qKg0KKioqDQoNCmBgYHtyfQ0KaW5kaWNlX21vcmlzaXRhX2FncmVnYWRvID0gY2FsY3VsYXJfbW9yaXNpdGEocG9udG9zX2FncmVnYWRvLCBudW1fY2VsdWxhcyA9IDUpDQppbmRpY2VfbW9yaXNpdGFfYWdyZWdhZG8NCmluZGljZV9tb3Jpc2l0YV9kaXNwZXJzbyA9IGNhbGN1bGFyX21vcmlzaXRhKHBvbnRvc19kaXNwZXJzbywgbnVtX2NlbHVsYXMgPSA1KQ0KaW5kaWNlX21vcmlzaXRhX2Rpc3BlcnNvDQoNCiMgRXhpYmlyIG9zIHJlc3VsdGFkb3MNCmNhdCgiw41uZGljZSBkZSBNb3Jpc2l0YSAoQWdyZWdhZG8pOiIsIGluZGljZV9tb3Jpc2l0YV9hZ3JlZ2FkbywgIlxuIikNCmNhdCgiw41uZGljZSBkZSBNb3Jpc2l0YSAoRGlzcGVyc28pOiIsIGluZGljZV9tb3Jpc2l0YV9kaXNwZXJzbywgIlxuIikNCmBgYA0KPlZhbG9yZXMgZGUgTW9yaXNpdGEgbWFpb3JlcyBxdWUgMSBpbmRpY2FtIGFncmVnYcOnw6NvLCBhcHJveGltYWRhbWVudGUgMSBpbmRpY2FtIGFsZWF0b3JpZWRhZGUsIGUgbWVub3JlcyBxdWUgMSBpbmRpY2FtIGRpc3BlcnPDo28uDQoNCioqKg0KIyAqKlZpc3VhbGl6YXIgYSBkaXN0cmlidWnDp8OjbyBlc3BhY2lhbCBjb20gYSBncmFkZSBzb2JyZXBvc3RhKioNCioqKg0KDQpgYGB7cn0NCiMgR3J1cG8gQWdyZWdhZG8NCnBsb3QocG9udG9zX2FncmVnYWRvLCBtYWluID0gIkdydXBvIEFncmVnYWRvIiwgcGNoID0gMjApDQpncmlkX2FncmVnYWRvID0gcXVhZHJhdGNvdW50KHBvbnRvc19hZ3JlZ2FkbywgbnggPSA1LCBueSA9IDUpICAjIENyaWEgYSBncmFkZQ0KIyBQbG90YXIgYSBncmFkZSBlbSB1bSBncsOhZmljbyBzZXBhcmFkbw0KcGxvdChncmlkX2FncmVnYWRvLCBtYWluID0gIkNvbnRhZ2VtIGRlIFF1YWRyYW50ZXMgKEFncmVnYWRvKSIsIGNvbCA9ICJyZWQiKQ0KDQojIEdydXBvIERpc3BlcnNvDQpwbG90KHBvbnRvc19kaXNwZXJzbywgbWFpbiA9ICJHcnVwbyBEaXNwZXJzbyIsIHBjaCA9IDIwKQ0KZ3JpZF9kaXNwZXJzbyA9IHF1YWRyYXRjb3VudChwb250b3NfZGlzcGVyc28sIG54ID0gNSwgbnkgPSA1KSAgIyBDcmlhIGEgZ3JhZGUNCiMgUGxvdGFyIGEgZ3JhZGUgZW0gdW0gZ3LDoWZpY28gc2VwYXJhZG8NCnBsb3QoZ3JpZF9kaXNwZXJzbywgbWFpbiA9ICJDb250YWdlbSBkZSBRdWFkcmFudGVzIChEaXNwZXJzbykiLCBjb2wgPSAicmVkIikNCmBgYA0KDQoqKioNCiMgKipUZW50YW5kbyBhZGFwdGFyIG8gaW5kaWNlIGRlIG1vcmlzaXRhIHVzYW5kbyBvIHJhaW8qKg0KKioqDQoNCiMgKkRhdGEqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCmNvb3Jkc19hZ3JlZ2FkbyA9IGRhdGEuZnJhbWUoDQogIHggPSBybm9ybSg1MCwgbWVhbiA9IDEwLCBzZCA9IDEpLA0KICB5ID0gcm5vcm0oNTAsIG1lYW4gPSAxMCwgc2QgPSAxKSwNCiAgcmFpbyA9IHJ1bmlmKDUwLCAwLjEsIDAuNSkNCikNCmNvb3Jkc19hZ3JlZ2FkbyRzdXBlcmZpY2llX2NvYmVydGEgPSBwaSAqIChjb29yZHNfYWdyZWdhZG8kcmFpb14yKQ0KY29vcmRzX2FncmVnYWRvDQoNCnNldC5zZWVkKDIpDQpjb29yZHNfZGlzcGVyc28gPSBkYXRhLmZyYW1lKA0KICB4ID0gcnVuaWYoNTAsIG1pbiA9IDUsIG1heCA9IDIwKSwNCiAgeSA9IHJ1bmlmKDUwLCBtaW4gPSA1LCBtYXggPSAyMCksDQogIHJhaW8gPSBydW5pZig1MCwgMC4xLCAwLjUpDQopDQpjb29yZHNfZGlzcGVyc28kc3VwZXJmaWNpZV9jb2JlcnRhID0gcGkgKiAoY29vcmRzX2Rpc3BlcnNvJHJhaW9eMikNCmNvb3Jkc19kaXNwZXJzbw0KYGBgDQoNCioqKg0KIyAqKkRlZmluaXIgbGltaXRlcyBkb3MgcG9sw61nb25vcyoqDQoqKioNCg0KYGBge3J9DQpsaW1pdGVzX2FncmVnYWRvID0gb3dpbih4cmFuZ2UgPSBjKDgsIDEyKSwgeXJhbmdlID0gYyg4LCAxMikpDQpsaW1pdGVzX2Rpc3BlcnNvID0gb3dpbih4cmFuZ2UgPSBjKDUsIDIwKSwgeXJhbmdlID0gYyg1LCAyMCkpDQpgYGANCioqKg0KIyAqKkZ1bsOnw6NvIHBhcmEgY2FsY3VsYXIgbyDDjW5kaWNlIGRlIE1vcmlzaXRhIGNvbnNpZGVyYW5kbyBzdXBlcmbDrWNpZSBjb2JlcnRhKioNCioqKg0KDQpgYGB7cn0NCmNhbGN1bGFyX21vcmlzaXRhX3N1cGVyZmljaWUgPSBmdW5jdGlvbihjb29yZHMsIG51bV9jZWx1bGFzLCBwb2xpZ29ubykgew0KICBsYXJndXJhX2NlbHVsYSA9IChwb2xpZ29ubyR4cmFuZ2VbMl0gLSBwb2xpZ29ubyR4cmFuZ2VbMV0pIC8gbnVtX2NlbHVsYXMNCiAgYWx0dXJhX2NlbHVsYSA9IChwb2xpZ29ubyR5cmFuZ2VbMl0gLSBwb2xpZ29ubyR5cmFuZ2VbMV0pIC8gbnVtX2NlbHVsYXMNCiAgIyBJbmljaWFsaXphciBhIGdyYWRlDQogIGdyaWRfc3VwZXJmaWNpZSA9IG1hdHJpeCgwLCBucm93ID0gbnVtX2NlbHVsYXMsIG5jb2wgPSBudW1fY2VsdWxhcykNCiAgIyBQcmVlbmNoZXIgYSBncmFkZSBjb20gYSBzdXBlcmbDrWNpZSBjb2JlcnRhDQogIGZvciAoaSBpbiAxOm5yb3coY29vcmRzKSkgew0KICAgIHhfcG9zID0gbWluKG51bV9jZWx1bGFzLCBtYXgoMSwgZmxvb3IoKGNvb3JkcyR4W2ldIC0gcG9saWdvbm8keHJhbmdlWzFdKSAvIGxhcmd1cmFfY2VsdWxhKSArIDEpKQ0KICAgIHlfcG9zID0gbWluKG51bV9jZWx1bGFzLCBtYXgoMSwgZmxvb3IoKGNvb3JkcyR5W2ldIC0gcG9saWdvbm8keXJhbmdlWzFdKSAvIGFsdHVyYV9jZWx1bGEpICsgMSkpDQogICAgZ3JpZF9zdXBlcmZpY2llW3hfcG9zLCB5X3Bvc10gPC0gZ3JpZF9zdXBlcmZpY2llW3hfcG9zLCB5X3Bvc10gKyBjb29yZHMkc3VwZXJmaWNpZV9jb2JlcnRhW2ldDQogIH0NCiAgIyBDYWxjdWxhciBvIMONbmRpY2UgZGUgTW9yaXNpdGEgbW9kaWZpY2Fkbw0KICBOID0gc3VtKGdyaWRfc3VwZXJmaWNpZSkgIyBUb3RhbCBkZSDDoXJlYSBjb2JlcnRhDQogIG4gPSBudW1fY2VsdWxhc14yICAgICAgICAjIE7Dum1lcm8gZGUgY8OpbHVsYXMNCiAgWF9pID0gYXMudmVjdG9yKGdyaWRfc3VwZXJmaWNpZSkNCiAgDQogIElfTSA9IGlmIChOID4gMSkgew0KICAgIChuICogc3VtKFhfaSAqIChYX2kgLSAxKSkpIC8gKE4gKiAoTiAtIDEpKQ0KICB9IGVsc2Ugew0KICAgIDAgICMgQ2FzbyBlc3BlY2lhbCBvbmRlIE4gw6kgbXVpdG8gcGVxdWVubw0KICB9DQogIHJldHVybihJX00pDQp9DQpgYGANCg0KKioqDQojICoqQ2FsY3VsYXIgbyDDjW5kaWNlIGRlIE1vcmlzaXRhIHBhcmEgYW1ib3Mgb3MgZ3J1cG9zKioNCioqKg0KDQpgYGB7cn0NCmluZGljZV9tb3Jpc2l0YV9hZ3JlZ2FkbyA9IGNhbGN1bGFyX21vcmlzaXRhX3N1cGVyZmljaWUoY29vcmRzX2FncmVnYWRvLCBudW1fY2VsdWxhcyA9IDUsIGxpbWl0ZXNfYWdyZWdhZG8pDQppbmRpY2VfbW9yaXNpdGFfZGlzcGVyc28gPSBjYWxjdWxhcl9tb3Jpc2l0YV9zdXBlcmZpY2llKGNvb3Jkc19kaXNwZXJzbywgbnVtX2NlbHVsYXMgPSA1LCBsaW1pdGVzX2Rpc3BlcnNvKQ0KY2F0KCLDjW5kaWNlIGRlIE1vcmlzaXRhIChBZ3JlZ2FkbyBjb20gU3VwZXJmw61jaWUgQ29iZXJ0YSk6IiwgaW5kaWNlX21vcmlzaXRhX2FncmVnYWRvLCAiXG4iKQ0KY2F0KCLDjW5kaWNlIGRlIE1vcmlzaXRhIChEaXNwZXJzbyBjb20gU3VwZXJmw61jaWUgQ29iZXJ0YSk6IiwgaW5kaWNlX21vcmlzaXRhX2Rpc3BlcnNvLCAiXG4iKQ0KYGBgDQoNCioqKg0KIyAqKlBsb3QqKg0KKioqDQoNCmBgYHtyfQ0KcGxvdF9jb21fZ3JhZGUgPSBmdW5jdGlvbihjb29yZHMsIHBvbGlnb25vLCBudW1fY2VsdWxhcywgdGl0dWxvKSB7DQogICMgQ29uZmlndXJhw6fDo28gZGEgamFuZWxhIGdyw6FmaWNhDQogIHBsb3QoTkEsIHhsaW0gPSBwb2xpZ29ubyR4cmFuZ2UsIHlsaW0gPSBwb2xpZ29ubyR5cmFuZ2UsIHhsYWIgPSAiWCIsIHlsYWIgPSAiWSIsIG1haW4gPSB0aXR1bG8pDQogIGxhcmd1cmFfY2VsdWxhIDwtIChwb2xpZ29ubyR4cmFuZ2VbMl0gLSBwb2xpZ29ubyR4cmFuZ2VbMV0pIC8gbnVtX2NlbHVsYXMNCiAgYWx0dXJhX2NlbHVsYSA8LSAocG9saWdvbm8keXJhbmdlWzJdIC0gcG9saWdvbm8keXJhbmdlWzFdKSAvIG51bV9jZWx1bGFzDQogICMgQWRpY2lvbmFyIGEgZ3JhZGUNCiAgZm9yIChpIGluIDA6bnVtX2NlbHVsYXMpIHsNCiAgICBhYmxpbmUodiA9IHBvbGlnb25vJHhyYW5nZVsxXSArIGkgKiBsYXJndXJhX2NlbHVsYSwgY29sID0gImdyYXkiLCBsdHkgPSAyKQ0KICAgIGFibGluZShoID0gcG9saWdvbm8keXJhbmdlWzFdICsgaSAqIGFsdHVyYV9jZWx1bGEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikNCiAgfQ0KICAjIFBsb3RhciBjYWRhIHBvbnRvIGUgbyByYWlvIChyYWl6IGRhIHN1cGVyZsOtY2llIGNvYmVydGEpDQogIHBvaW50cyhjb29yZHMkeCwgY29vcmRzJHksIHBjaCA9IDE2LCBjb2wgPSAiYmx1ZSIpDQogIHN5bWJvbHMoY29vcmRzJHgsIGNvb3JkcyR5LCBjaXJjbGVzID0gc3FydChjb29yZHMkc3VwZXJmaWNpZV9jb2JlcnRhIC8gcGkpLCBhZGQgPSBUUlVFLCBpbmNoZXMgPSBGQUxTRSwgZmcgPSAicmVkIikNCn0NCg0KIyBQbG90YXIgZ3J1cG8gYWdyZWdhZG8gZSBkaXNwZXJzbyBjb20gZ3JhZGUgZSByYWlvcw0KcGxvdF9jb21fZ3JhZGUoY29vcmRzX2FncmVnYWRvLCBsaW1pdGVzX2FncmVnYWRvLCA1LCAiR3J1cG8gQWdyZWdhZG8gY29tIFN1cGVyZsOtY2llIENvYmVydGEiKQ0KcGxvdF9jb21fZ3JhZGUoY29vcmRzX2Rpc3BlcnNvLCBsaW1pdGVzX2Rpc3BlcnNvLCA1LCAiR3J1cG8gRGlzcGVyc28gY29tIFN1cGVyZsOtY2llIENvYmVydGEiKQ0KYGBgDQoNCg==