import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
class Tabela<L, C, X> {
private final Set<C> colunas;
private final Set<L> linhas;
private final Map<L, Map<C, X>> celulas;
private final Function<? super L, String> strLinhas;
private final Function<? super C, String> strColunas;
private final Function<? super X, String> strCelulas;
@FunctionalInterface
public static interface TabelaConsumer<L, C, X> {
public void accept(L linha, C coluna, X celula);
}
public Tabela(
Collection<L> linhas,
Collection<C> colunas,
Function<? super L, String> strLinhas,
Function<? super C, String> strColunas,
Function<? super X, String> strCelulas)
{
this.colunas = new LinkedHashSet<>(colunas);
this.linhas = new LinkedHashSet<>(linhas);
this.strLinhas = strLinhas;
this.strColunas = strColunas;
this.strCelulas = strCelulas;
this.celulas = new LinkedHashMap<>(this.linhas.size());
for (L linha : this.linhas) {
Map<C, X> valoresLinha = new LinkedHashMap<>(this.colunas.size());
for (C coluna : this.colunas) {
valoresLinha.put(coluna, null);
}
this.celulas.put(linha, valoresLinha);
}
}
public void put(L linha, C coluna, X valor) {
celulas.get(linha).put(coluna, valor);
}
public X get(L linha, C coluna) {
if (!linhas.contains(linha) || !colunas.contains(coluna)) return null;
return celulas.get(linha).get(coluna);
}
public void forEach(TabelaConsumer<? super L, ? super C, ? super X> consumer) {
for (L linha : linhas) {
for (C coluna : colunas) {
consumer.accept(linha, coluna, get(linha, coluna));
}
}
}
private List<Integer> calcularLargura() {
List<Integer> lista = new ArrayList<>(colunas.size() + 1);
int m = 0;
for (L linha : linhas) {
int x = strLinhas.apply(linha).length();
if (x > m) m = x;
}
lista.add(m);
for (C coluna : colunas) {
int n = strColunas.apply(coluna).length();
for (L linha : linhas) {
X celula = get(linha, coluna);
int x = celula == null ? 0 : strCelulas.apply(celula).length();
if (x > n) n = x;
}
lista.add(n);
}
return lista;
}
private void imprimirSeparador(StringBuilder sb, int tamanho) {
sb.append("+-");
for (int i = 0; i <= tamanho; i++) {
sb.append('-');
}
}
private void imprimirSeparador(StringBuilder sb, List<Integer> tamanhos) {
for (int i : tamanhos) {
imprimirSeparador(sb, i);
}
sb.append("+\n");
}
private void imprimirCelula
(StringBuilder sb,
int tamanho,
String conteudo
) { sb.append("| ").append(conteudo);
for (int i = conteudo.length(); i <= tamanho; i++) {
sb.append(' ');
}
}
private void imprimirCabecalho(StringBuilder sb, List<Integer> tamanhos) {
Iterator<Integer> it = tamanhos.iterator();
imprimirCelula(sb, it.next(), "");
for (C coluna : colunas) {
imprimirCelula(sb, it.next(), strColunas.apply(coluna));
}
sb.append("|\n");
}
private void imprimirLinha(StringBuilder sb, List<Integer> tamanhos, L linha) {
Iterator<Integer> it = tamanhos.iterator();
imprimirCelula(sb, it.next(), strLinhas.apply(linha));
for (C coluna : colunas) {
X celula = get(linha, coluna);
imprimirCelula(sb, it.next(), celula == null ? "" : strCelulas.apply(celula));
}
sb.append("|\n");
}
@Override
List<Integer> tamanhos = calcularLargura();
int larguraTotal
= tamanhos.
stream().
reduce(0,
Integer::sum
) + 3 * tamanhos.
size() + 2; StringBuilder sb = new StringBuilder(larguraTotal * (linhas.size() * 2 + 3));
imprimirSeparador(sb, tamanhos);
imprimirCabecalho(sb, tamanhos);
imprimirSeparador(sb, tamanhos);
for (L linha : linhas) {
imprimirLinha(sb, tamanhos, linha);
imprimirSeparador(sb, tamanhos);
}
return sb.toString();
}
}
class Local {
this.nome = nome;
}
@Override
return nome;
}
}
class Atividade {
public Atividade
(String nome
) { this.nome = nome;
}
@Override
return nome;
}
}
class Teste {
private static final DateTimeFormatter FMT = DateTimeFormatter
.ofPattern("dd/MM/uuuu")
.withResolverStyle(ResolverStyle.STRICT);
public static void main
(String[] args
) {
// Define os locais e atividades.
Local casa = new Local("Casa");
Local escola = new Local("Escola");
Local trabalho = new Local("Trabalho");
Atividade estudo = new Atividade("Estudo");
Atividade treino = new Atividade("Treino");
Atividade apresentacao = new Atividade("Apresentação");
// Define a estrutura da tabela 1.
Tabela<Atividade, Local, LocalDate> tabela1 = new Tabela<>(
Arrays.
asList(estudo, treino, apresentacao
),
Arrays.
asList(casa, escola, trabalho
),
ld -> ld.format(FMT)
);
// Preenche a tabela 1.
tabela1.put(estudo, casa, LocalDate.of(2017, 2, 1));
tabela1.put(estudo, escola, LocalDate.of(2017, 3, 10));
tabela1.put(estudo, trabalho, LocalDate.of(2017, 1, 1));
tabela1.put(treino, casa, LocalDate.of(2017, 5, 3));
tabela1.put(treino, escola, LocalDate.of(2017, 2, 4));
tabela1.put(treino, trabalho, LocalDate.of(2017, 5, 1));
tabela1.put(apresentacao, casa, LocalDate.of(2017, 2, 2));
tabela1.put(apresentacao, escola, LocalDate.of(2017, 3, 3));
tabela1.put(apresentacao, trabalho, LocalDate.of(2017, 12, 10));
// Mostra a tabela 1.
System.
out.
println("Tabela 1:");
// Descobre as datas que são linhas da tabela 2.
Set<LocalDate> datas = new TreeSet<>();
tabela1.forEach((atividade, local, data) -> datas.add(data));
// Define a estrutura da tabela 2.
Tabela<LocalDate, Atividade, Local> tabela2 = new Tabela<>(
datas,
Arrays.
asList(estudo, treino, apresentacao
),
ld -> ld.format(FMT),
);
// Preenche a tabela 2 com base nos dados da tabela 1.
tabela1.forEach((atividade, local, data) -> tabela2.put(data, atividade, local));
// Mostra a tabela 2.
System.
out.
println("Tabela 2:"); }
}
aW1wb3J0IGphdmEudGltZS5Mb2NhbERhdGU7CmltcG9ydCBqYXZhLnRpbWUuZm9ybWF0LkRhdGVUaW1lRm9ybWF0dGVyOwppbXBvcnQgamF2YS50aW1lLmZvcm1hdC5SZXNvbHZlclN0eWxlOwppbXBvcnQgamF2YS51dGlsLkFycmF5TGlzdDsKaW1wb3J0IGphdmEudXRpbC5BcnJheXM7CmltcG9ydCBqYXZhLnV0aWwuQ29sbGVjdGlvbjsKaW1wb3J0IGphdmEudXRpbC5JdGVyYXRvcjsKaW1wb3J0IGphdmEudXRpbC5MaW5rZWRIYXNoTWFwOwppbXBvcnQgamF2YS51dGlsLkxpbmtlZEhhc2hTZXQ7CmltcG9ydCBqYXZhLnV0aWwuTGlzdDsKaW1wb3J0IGphdmEudXRpbC5NYXA7CmltcG9ydCBqYXZhLnV0aWwuU2V0OwppbXBvcnQgamF2YS51dGlsLlRyZWVTZXQ7CmltcG9ydCBqYXZhLnV0aWwuZnVuY3Rpb24uRnVuY3Rpb247CgpjbGFzcyBUYWJlbGE8TCwgQywgWD4gewogICAgcHJpdmF0ZSBmaW5hbCBTZXQ8Qz4gY29sdW5hczsKICAgIHByaXZhdGUgZmluYWwgU2V0PEw+IGxpbmhhczsKICAgIHByaXZhdGUgZmluYWwgTWFwPEwsIE1hcDxDLCBYPj4gY2VsdWxhczsKICAgIHByaXZhdGUgZmluYWwgRnVuY3Rpb248PyBzdXBlciBMLCBTdHJpbmc+IHN0ckxpbmhhczsKICAgIHByaXZhdGUgZmluYWwgRnVuY3Rpb248PyBzdXBlciBDLCBTdHJpbmc+IHN0ckNvbHVuYXM7CiAgICBwcml2YXRlIGZpbmFsIEZ1bmN0aW9uPD8gc3VwZXIgWCwgU3RyaW5nPiBzdHJDZWx1bGFzOwoKICAgIEBGdW5jdGlvbmFsSW50ZXJmYWNlCiAgICBwdWJsaWMgc3RhdGljIGludGVyZmFjZSBUYWJlbGFDb25zdW1lcjxMLCBDLCBYPiB7CiAgICAgICAgcHVibGljIHZvaWQgYWNjZXB0KEwgbGluaGEsIEMgY29sdW5hLCBYIGNlbHVsYSk7CiAgICB9CgogICAgcHVibGljIFRhYmVsYSgKICAgICAgICAgICAgQ29sbGVjdGlvbjxMPiBsaW5oYXMsCiAgICAgICAgICAgIENvbGxlY3Rpb248Qz4gY29sdW5hcywKICAgICAgICAgICAgRnVuY3Rpb248PyBzdXBlciBMLCBTdHJpbmc+IHN0ckxpbmhhcywKICAgICAgICAgICAgRnVuY3Rpb248PyBzdXBlciBDLCBTdHJpbmc+IHN0ckNvbHVuYXMsCiAgICAgICAgICAgIEZ1bmN0aW9uPD8gc3VwZXIgWCwgU3RyaW5nPiBzdHJDZWx1bGFzKQogICAgewogICAgICAgIHRoaXMuY29sdW5hcyA9IG5ldyBMaW5rZWRIYXNoU2V0PD4oY29sdW5hcyk7CiAgICAgICAgdGhpcy5saW5oYXMgPSBuZXcgTGlua2VkSGFzaFNldDw+KGxpbmhhcyk7CiAgICAgICAgdGhpcy5zdHJMaW5oYXMgPSBzdHJMaW5oYXM7CiAgICAgICAgdGhpcy5zdHJDb2x1bmFzID0gc3RyQ29sdW5hczsKICAgICAgICB0aGlzLnN0ckNlbHVsYXMgPSBzdHJDZWx1bGFzOwogICAgICAgIHRoaXMuY2VsdWxhcyA9IG5ldyBMaW5rZWRIYXNoTWFwPD4odGhpcy5saW5oYXMuc2l6ZSgpKTsKICAgICAgICBmb3IgKEwgbGluaGEgOiB0aGlzLmxpbmhhcykgewogICAgICAgICAgICBNYXA8QywgWD4gdmFsb3Jlc0xpbmhhID0gbmV3IExpbmtlZEhhc2hNYXA8Pih0aGlzLmNvbHVuYXMuc2l6ZSgpKTsKICAgICAgICAgICAgZm9yIChDIGNvbHVuYSA6IHRoaXMuY29sdW5hcykgewogICAgICAgICAgICAgICAgdmFsb3Jlc0xpbmhhLnB1dChjb2x1bmEsIG51bGwpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY2VsdWxhcy5wdXQobGluaGEsIHZhbG9yZXNMaW5oYSk7CiAgICAgICAgfQogICAgfQoKICAgIHB1YmxpYyB2b2lkIHB1dChMIGxpbmhhLCBDIGNvbHVuYSwgWCB2YWxvcikgewogICAgICAgIGlmICghbGluaGFzLmNvbnRhaW5zKGxpbmhhKSB8fCAhY29sdW5hcy5jb250YWlucyhjb2x1bmEpKSB0aHJvdyBuZXcgSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uKCk7CiAgICAgICAgY2VsdWxhcy5nZXQobGluaGEpLnB1dChjb2x1bmEsIHZhbG9yKTsKICAgIH0KCiAgICBwdWJsaWMgWCBnZXQoTCBsaW5oYSwgQyBjb2x1bmEpIHsKICAgICAgICBpZiAoIWxpbmhhcy5jb250YWlucyhsaW5oYSkgfHwgIWNvbHVuYXMuY29udGFpbnMoY29sdW5hKSkgcmV0dXJuIG51bGw7CiAgICAgICAgcmV0dXJuIGNlbHVsYXMuZ2V0KGxpbmhhKS5nZXQoY29sdW5hKTsKICAgIH0KCiAgICBwdWJsaWMgdm9pZCBmb3JFYWNoKFRhYmVsYUNvbnN1bWVyPD8gc3VwZXIgTCwgPyBzdXBlciBDLCA/IHN1cGVyIFg+IGNvbnN1bWVyKSB7CiAgICAgICAgZm9yIChMIGxpbmhhIDogbGluaGFzKSB7CiAgICAgICAgICAgIGZvciAoQyBjb2x1bmEgOiBjb2x1bmFzKSB7CiAgICAgICAgICAgICAgICBjb25zdW1lci5hY2NlcHQobGluaGEsIGNvbHVuYSwgZ2V0KGxpbmhhLCBjb2x1bmEpKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICBwcml2YXRlIExpc3Q8SW50ZWdlcj4gY2FsY3VsYXJMYXJndXJhKCkgewogICAgICAgIExpc3Q8SW50ZWdlcj4gbGlzdGEgPSBuZXcgQXJyYXlMaXN0PD4oY29sdW5hcy5zaXplKCkgKyAxKTsKCiAgICAgICAgaW50IG0gPSAwOwogICAgICAgIGZvciAoTCBsaW5oYSA6IGxpbmhhcykgewogICAgICAgICAgICBpbnQgeCA9IHN0ckxpbmhhcy5hcHBseShsaW5oYSkubGVuZ3RoKCk7CiAgICAgICAgICAgIGlmICh4ID4gbSkgbSA9IHg7CiAgICAgICAgfQogICAgICAgIGxpc3RhLmFkZChtKTsKCiAgICAgICAgZm9yIChDIGNvbHVuYSA6IGNvbHVuYXMpIHsKICAgICAgICAgICAgaW50IG4gPSBzdHJDb2x1bmFzLmFwcGx5KGNvbHVuYSkubGVuZ3RoKCk7CiAgICAgICAgICAgIGZvciAoTCBsaW5oYSA6IGxpbmhhcykgewogICAgICAgICAgICAgICAgWCBjZWx1bGEgPSBnZXQobGluaGEsIGNvbHVuYSk7CiAgICAgICAgICAgICAgICBpbnQgeCA9IGNlbHVsYSA9PSBudWxsID8gMCA6IHN0ckNlbHVsYXMuYXBwbHkoY2VsdWxhKS5sZW5ndGgoKTsKICAgICAgICAgICAgICAgIGlmICh4ID4gbikgbiA9IHg7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbGlzdGEuYWRkKG4pOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGxpc3RhOwogICAgfQoKICAgIHByaXZhdGUgdm9pZCBpbXByaW1pclNlcGFyYWRvcihTdHJpbmdCdWlsZGVyIHNiLCBpbnQgdGFtYW5obykgewogICAgICAgIHNiLmFwcGVuZCgiKy0iKTsKICAgICAgICBmb3IgKGludCBpID0gMDsgaSA8PSB0YW1hbmhvOyBpKyspIHsKICAgICAgICAgICAgc2IuYXBwZW5kKCctJyk7CiAgICAgICAgfQogICAgfQoKICAgIHByaXZhdGUgdm9pZCBpbXByaW1pclNlcGFyYWRvcihTdHJpbmdCdWlsZGVyIHNiLCBMaXN0PEludGVnZXI+IHRhbWFuaG9zKSB7CiAgICAgICAgZm9yIChpbnQgaSA6IHRhbWFuaG9zKSB7CiAgICAgICAgICAgIGltcHJpbWlyU2VwYXJhZG9yKHNiLCBpKTsKICAgICAgICB9CiAgICAgICAgc2IuYXBwZW5kKCIrXG4iKTsKICAgIH0KCiAgICBwcml2YXRlIHZvaWQgaW1wcmltaXJDZWx1bGEoU3RyaW5nQnVpbGRlciBzYiwgaW50IHRhbWFuaG8sIFN0cmluZyBjb250ZXVkbykgewogICAgICAgIHNiLmFwcGVuZCgifCAiKS5hcHBlbmQoY29udGV1ZG8pOwogICAgICAgIGZvciAoaW50IGkgPSBjb250ZXVkby5sZW5ndGgoKTsgaSA8PSB0YW1hbmhvOyBpKyspIHsKICAgICAgICAgICAgc2IuYXBwZW5kKCcgJyk7CiAgICAgICAgfQogICAgfQoKICAgIHByaXZhdGUgdm9pZCBpbXByaW1pckNhYmVjYWxobyhTdHJpbmdCdWlsZGVyIHNiLCBMaXN0PEludGVnZXI+IHRhbWFuaG9zKSB7CiAgICAgICAgSXRlcmF0b3I8SW50ZWdlcj4gaXQgPSB0YW1hbmhvcy5pdGVyYXRvcigpOwogICAgICAgIGltcHJpbWlyQ2VsdWxhKHNiLCBpdC5uZXh0KCksICIiKTsKICAgICAgICBmb3IgKEMgY29sdW5hIDogY29sdW5hcykgewogICAgICAgICAgICBpbXByaW1pckNlbHVsYShzYiwgaXQubmV4dCgpLCBzdHJDb2x1bmFzLmFwcGx5KGNvbHVuYSkpOwogICAgICAgIH0KICAgICAgICBzYi5hcHBlbmQoInxcbiIpOwogICAgfQoKICAgIHByaXZhdGUgdm9pZCBpbXByaW1pckxpbmhhKFN0cmluZ0J1aWxkZXIgc2IsIExpc3Q8SW50ZWdlcj4gdGFtYW5ob3MsIEwgbGluaGEpIHsKICAgICAgICBJdGVyYXRvcjxJbnRlZ2VyPiBpdCA9IHRhbWFuaG9zLml0ZXJhdG9yKCk7CiAgICAgICAgaW1wcmltaXJDZWx1bGEoc2IsIGl0Lm5leHQoKSwgc3RyTGluaGFzLmFwcGx5KGxpbmhhKSk7CiAgICAgICAgZm9yIChDIGNvbHVuYSA6IGNvbHVuYXMpIHsKICAgICAgICAgICAgWCBjZWx1bGEgPSBnZXQobGluaGEsIGNvbHVuYSk7CiAgICAgICAgICAgIGltcHJpbWlyQ2VsdWxhKHNiLCBpdC5uZXh0KCksIGNlbHVsYSA9PSBudWxsID8gIiIgOiBzdHJDZWx1bGFzLmFwcGx5KGNlbHVsYSkpOwogICAgICAgIH0KICAgICAgICBzYi5hcHBlbmQoInxcbiIpOwogICAgfQoKICAgIEBPdmVycmlkZQogICAgcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKICAgICAgICBMaXN0PEludGVnZXI+IHRhbWFuaG9zID0gY2FsY3VsYXJMYXJndXJhKCk7CiAgICAgICAgaW50IGxhcmd1cmFUb3RhbCA9IHRhbWFuaG9zLnN0cmVhbSgpLnJlZHVjZSgwLCBJbnRlZ2VyOjpzdW0pICsgMyAqIHRhbWFuaG9zLnNpemUoKSArIDI7CiAgICAgICAgU3RyaW5nQnVpbGRlciBzYiA9IG5ldyBTdHJpbmdCdWlsZGVyKGxhcmd1cmFUb3RhbCAqIChsaW5oYXMuc2l6ZSgpICogMiArIDMpKTsKICAgICAgICBpbXByaW1pclNlcGFyYWRvcihzYiwgdGFtYW5ob3MpOwogICAgICAgIGltcHJpbWlyQ2FiZWNhbGhvKHNiLCB0YW1hbmhvcyk7CiAgICAgICAgaW1wcmltaXJTZXBhcmFkb3Ioc2IsIHRhbWFuaG9zKTsKICAgICAgICBmb3IgKEwgbGluaGEgOiBsaW5oYXMpIHsKICAgICAgICAgICAgaW1wcmltaXJMaW5oYShzYiwgdGFtYW5ob3MsIGxpbmhhKTsKICAgICAgICAgICAgaW1wcmltaXJTZXBhcmFkb3Ioc2IsIHRhbWFuaG9zKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHNiLnRvU3RyaW5nKCk7CiAgICB9Cn0KCmNsYXNzIExvY2FsIHsKICAgIHByaXZhdGUgZmluYWwgU3RyaW5nIG5vbWU7CgogICAgcHVibGljIExvY2FsKFN0cmluZyBub21lKSB7CiAgICAgICAgdGhpcy5ub21lID0gbm9tZTsKICAgIH0KCiAgICBAT3ZlcnJpZGUKICAgIHB1YmxpYyBTdHJpbmcgdG9TdHJpbmcoKSB7CiAgICAgICAgcmV0dXJuIG5vbWU7CiAgICB9Cn0KCmNsYXNzIEF0aXZpZGFkZSB7CiAgICBwcml2YXRlIGZpbmFsIFN0cmluZyBub21lOwoKICAgIHB1YmxpYyBBdGl2aWRhZGUoU3RyaW5nIG5vbWUpIHsKICAgICAgICB0aGlzLm5vbWUgPSBub21lOwogICAgfQoKICAgIEBPdmVycmlkZQogICAgcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKICAgICAgICByZXR1cm4gbm9tZTsKICAgIH0KfQoKY2xhc3MgVGVzdGUgewoKICAgIHByaXZhdGUgc3RhdGljIGZpbmFsIERhdGVUaW1lRm9ybWF0dGVyIEZNVCA9IERhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgIC5vZlBhdHRlcm4oImRkL01NL3V1dXUiKQogICAgICAgICAgICAud2l0aFJlc29sdmVyU3R5bGUoUmVzb2x2ZXJTdHlsZS5TVFJJQ1QpOwoKICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHsKCiAgICAgICAgLy8gRGVmaW5lIG9zIGxvY2FpcyBlIGF0aXZpZGFkZXMuCiAgICAgICAgTG9jYWwgY2FzYSA9IG5ldyBMb2NhbCgiQ2FzYSIpOwogICAgICAgIExvY2FsIGVzY29sYSA9IG5ldyBMb2NhbCgiRXNjb2xhIik7CiAgICAgICAgTG9jYWwgdHJhYmFsaG8gPSBuZXcgTG9jYWwoIlRyYWJhbGhvIik7CiAgICAgICAgQXRpdmlkYWRlIGVzdHVkbyA9IG5ldyBBdGl2aWRhZGUoIkVzdHVkbyIpOwogICAgICAgIEF0aXZpZGFkZSB0cmVpbm8gPSBuZXcgQXRpdmlkYWRlKCJUcmVpbm8iKTsKICAgICAgICBBdGl2aWRhZGUgYXByZXNlbnRhY2FvID0gbmV3IEF0aXZpZGFkZSgiQXByZXNlbnRhw6fDo28iKTsKCiAgICAgICAgLy8gRGVmaW5lIGEgZXN0cnV0dXJhIGRhIHRhYmVsYSAxLgogICAgICAgIFRhYmVsYTxBdGl2aWRhZGUsIExvY2FsLCBMb2NhbERhdGU+IHRhYmVsYTEgPSBuZXcgVGFiZWxhPD4oCiAgICAgICAgICAgICAgICBBcnJheXMuYXNMaXN0KGVzdHVkbywgdHJlaW5vLCBhcHJlc2VudGFjYW8pLAogICAgICAgICAgICAgICAgQXJyYXlzLmFzTGlzdChjYXNhLCBlc2NvbGEsIHRyYWJhbGhvKSwKICAgICAgICAgICAgICAgIE9iamVjdDo6dG9TdHJpbmcsCiAgICAgICAgICAgICAgICBPYmplY3Q6OnRvU3RyaW5nLAogICAgICAgICAgICAgICAgbGQgLT4gbGQuZm9ybWF0KEZNVCkKICAgICAgICApOwoKICAgICAgICAvLyBQcmVlbmNoZSBhIHRhYmVsYSAxLgogICAgICAgIHRhYmVsYTEucHV0KGVzdHVkbywgY2FzYSwgTG9jYWxEYXRlLm9mKDIwMTcsIDIsIDEpKTsKICAgICAgICB0YWJlbGExLnB1dChlc3R1ZG8sIGVzY29sYSwgTG9jYWxEYXRlLm9mKDIwMTcsIDMsIDEwKSk7CiAgICAgICAgdGFiZWxhMS5wdXQoZXN0dWRvLCB0cmFiYWxobywgTG9jYWxEYXRlLm9mKDIwMTcsIDEsIDEpKTsKICAgICAgICB0YWJlbGExLnB1dCh0cmVpbm8sIGNhc2EsIExvY2FsRGF0ZS5vZigyMDE3LCA1LCAzKSk7CiAgICAgICAgdGFiZWxhMS5wdXQodHJlaW5vLCBlc2NvbGEsIExvY2FsRGF0ZS5vZigyMDE3LCAyLCA0KSk7CiAgICAgICAgdGFiZWxhMS5wdXQodHJlaW5vLCB0cmFiYWxobywgTG9jYWxEYXRlLm9mKDIwMTcsIDUsIDEpKTsKICAgICAgICB0YWJlbGExLnB1dChhcHJlc2VudGFjYW8sIGNhc2EsIExvY2FsRGF0ZS5vZigyMDE3LCAyLCAyKSk7CiAgICAgICAgdGFiZWxhMS5wdXQoYXByZXNlbnRhY2FvLCBlc2NvbGEsIExvY2FsRGF0ZS5vZigyMDE3LCAzLCAzKSk7CiAgICAgICAgdGFiZWxhMS5wdXQoYXByZXNlbnRhY2FvLCB0cmFiYWxobywgTG9jYWxEYXRlLm9mKDIwMTcsIDEyLCAxMCkpOwoKICAgICAgICAvLyBNb3N0cmEgYSB0YWJlbGEgMS4KICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oIlRhYmVsYSAxOiIpOwogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbih0YWJlbGExKTsKCiAgICAgICAgLy8gRGVzY29icmUgYXMgZGF0YXMgcXVlIHPDo28gbGluaGFzIGRhIHRhYmVsYSAyLgogICAgICAgIFNldDxMb2NhbERhdGU+IGRhdGFzID0gbmV3IFRyZWVTZXQ8PigpOwogICAgICAgIHRhYmVsYTEuZm9yRWFjaCgoYXRpdmlkYWRlLCBsb2NhbCwgZGF0YSkgLT4gZGF0YXMuYWRkKGRhdGEpKTsKCiAgICAgICAgLy8gRGVmaW5lIGEgZXN0cnV0dXJhIGRhIHRhYmVsYSAyLgogICAgICAgIFRhYmVsYTxMb2NhbERhdGUsIEF0aXZpZGFkZSwgTG9jYWw+IHRhYmVsYTIgPSBuZXcgVGFiZWxhPD4oCiAgICAgICAgICAgICAgICBkYXRhcywKICAgICAgICAgICAgICAgIEFycmF5cy5hc0xpc3QoZXN0dWRvLCB0cmVpbm8sIGFwcmVzZW50YWNhbyksCiAgICAgICAgICAgICAgICBsZCAtPiBsZC5mb3JtYXQoRk1UKSwKICAgICAgICAgICAgICAgIE9iamVjdDo6dG9TdHJpbmcsCiAgICAgICAgICAgICAgICBPYmplY3Q6OnRvU3RyaW5nCiAgICAgICAgKTsKCiAgICAgICAgLy8gUHJlZW5jaGUgYSB0YWJlbGEgMiBjb20gYmFzZSBub3MgZGFkb3MgZGEgdGFiZWxhIDEuCiAgICAgICAgdGFiZWxhMS5mb3JFYWNoKChhdGl2aWRhZGUsIGxvY2FsLCBkYXRhKSAtPiB0YWJlbGEyLnB1dChkYXRhLCBhdGl2aWRhZGUsIGxvY2FsKSk7CgogICAgICAgIC8vIE1vc3RyYSBhIHRhYmVsYSAyLgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiVGFiZWxhIDI6Iik7CiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKHRhYmVsYTIpOwogICAgfQp9