vix-ui
Version:
280 lines • 33.6 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
import { Component, EventEmitter, Input, Output } from '@angular/core';
import * as _ from 'lodash';
export class VixConfusionMatrixComponent {
constructor() {
this.pageSize = 0;
this.data = {};
this.tableExport = new EventEmitter();
this.concepts = [];
this.selectedConcepts = [];
this.nPages = 1;
this.pageNums = [];
this.summaries = {};
this.overallSummaries = {};
this.summaryRows = ['TP', 'FP', 'FN', 'Support'];
this.sortCriteria = {};
this.rowOrdering = [];
this.taskIds = [];
this.page = 1;
this.conceptsFiltered = [];
}
/**
* @param {?=} init
* @return {?}
*/
getRowsToShow(init = false) {
this.conceptsFiltered = this.concepts.slice();
if (this.selectedConcepts.length) {
this.conceptsFiltered = this.conceptsFiltered.filter(concept => this.selectedConcepts.indexOf(concept) !== -1);
}
this.nPages = this.pageSize ? Math.ceil(this.conceptsFiltered.length / this.pageSize) : 1;
if (!init && this.conceptsFiltered.length === this.concepts.length) {
// Use overall summary which is cached in-memory
this.summaries = _.cloneDeep(this.overallSummaries);
}
else {
this.computeSummary(init);
}
this.toggleSort();
}
/**
* @return {?}
*/
toggleSort() {
if (this.sortCriteria.criteria === 'alpha') {
this.conceptsFiltered.sort();
if (this.sortCriteria.direction === 'desc') {
this.conceptsFiltered.reverse();
}
}
else {
/** @type {?} */
const direction = this.sortCriteria.direction || 'desc';
this.conceptsFiltered.sort((a, b) => {
/** @type {?} */
const first = this.summaries[this.sortCriteria.taskId][this.sortCriteria.criteria][a] || 0;
/** @type {?} */
const second = this.summaries[this.sortCriteria.taskId][this.sortCriteria.criteria][b] || 0;
return (first - second) * (direction === 'desc' ? -1 : 1);
});
}
this.page = 1;
this.changePage();
}
/**
* @param {?} init
* @return {?}
*/
computeSummary(init) {
this.summaries = {};
for (const taskId of this.taskIds) {
this.summaries[taskId] = {
TP: {},
FP: {},
FN: {},
Support: {},
};
for (const concept of this.conceptsFiltered) {
this.summaries[taskId].TP[concept] = 0;
this.summaries[taskId].FP[concept] = 0;
this.summaries[taskId].FN[concept] = 0;
this.summaries[taskId].Support[concept] = 0;
}
for (const concept1 of this.conceptsFiltered) {
for (const concept2 of this.conceptsFiltered) {
/** @type {?} */
let count;
if (this.data[taskId].matrix[concept1]) {
count = this.data[taskId].matrix[concept1][concept2] || 0;
}
else {
count = 0;
}
if (concept1 === concept2) {
this.summaries[taskId].TP[concept1] += count;
}
else {
this.summaries[taskId].FN[concept1] += count;
this.summaries[taskId].FP[concept2] += count;
}
this.summaries[taskId].Support[concept1] += count;
}
}
if (init) {
this.overallSummaries[taskId] = {
TP: {},
FP: {},
FN: {},
Support: {},
};
for (const concept of this.conceptsFiltered) {
this.overallSummaries[taskId].TP[concept] = this.summaries[taskId].TP[concept];
this.overallSummaries[taskId].FP[concept] = this.summaries[taskId].FP[concept];
this.overallSummaries[taskId].FN[concept] = this.summaries[taskId].FN[concept];
this.overallSummaries[taskId].Support[concept] = this.summaries[taskId].Support[concept];
}
}
}
}
/**
* @param {?=} page
* @return {?}
*/
changePage(page) {
if (page) {
this.page = page;
}
this.page = Math.min(Math.max(+this.page, 1), this.nPages);
this.pageNums = [];
/** @type {?} */
let lowLimit;
/** @type {?} */
let uppLimit;
if (this.page <= 2) {
lowLimit = 1;
uppLimit = Math.min(5, this.nPages);
}
else if (Math.abs(this.page - this.nPages) <= 1) {
lowLimit = Math.max(1, this.nPages - 4);
uppLimit = this.nPages;
}
else {
lowLimit = this.page - 2;
uppLimit = this.page + 2;
}
for (let i = lowLimit; i <= uppLimit; i++) {
this.pageNums = this.pageNums.concat([i]);
}
this.rowOrdering = this.conceptsFiltered.slice();
if (!this.pageSize) {
// No paging required
return;
}
this.rowOrdering = this.rowOrdering.slice((this.page - 1) * this.pageSize, this.page * this.pageSize);
}
/**
* @param {?} event
* @return {?}
*/
onChange(event) {
this.selectedConcepts = event;
this.getRowsToShow();
}
/**
* @param {?} i
* @return {?}
*/
counter(i) {
return new Array(i);
}
/**
* @return {?}
*/
getTableData() {
/** @type {?} */
const tableHeader = ['concept'].concat(this.conceptsFiltered);
/** @type {?} */
const tableData = [];
for (const concept of this.conceptsFiltered) {
/** @type {?} */
const row = { concept };
for (const concept1 of this.conceptsFiltered) {
row[concept1] = this.taskIds.map(taskId => {
/** @type {?} */
const count = this.data[taskId].matrix[concept][concept1];
return count == null ? '-' : count;
}).join(' / ');
}
tableData.push(row);
}
this.tableExport.emit({ tableHeader, tableData });
}
/**
* @return {?}
*/
ngOnInit() {
// Page size must be non-zero; page size = 0 means all to be displayed without paging
this.pageSize = Math.max(this.pageSize, 0);
this.initialize();
}
/**
* @return {?}
*/
ngOnChanges() {
this.initialize();
}
/**
* @return {?}
*/
initialize() {
this.taskIds = Object.keys(this.data);
this.sortCriteria = {
criteria: 'alpha',
direction: 'asc',
taskId: this.taskIds[0],
};
this.selectedConcepts = [];
/** @type {?} */
let conceptsCombined = [];
for (const taskId of this.taskIds) {
conceptsCombined = conceptsCombined.concat(this.data[taskId].concepts);
}
this.concepts = Array.from(new Set(conceptsCombined));
for (const taskId of this.taskIds) {
for (const concept of this.concepts) {
this.data[taskId].matrix[concept] = this.data[taskId].matrix[concept] || {};
}
}
this.getRowsToShow(true);
}
}
VixConfusionMatrixComponent.decorators = [
{ type: Component, args: [{
selector: 'vix-confusion-matrix',
template: "<ng-select\n style=\"margin-bottom: 15px; max-width: 600px;\"\n placeholder=\"Select concepts to display\"\n [dropdownPosition]=\"'top'\"\n [items]=\"concepts\"\n [multiple]=\"true\"\n [closeOnSelect]=\"false\"\n (ngModelChange)=\"onChange($event)\"\n [(ngModel)]=\"selectedConcepts\">\n</ng-select>\n<div style=\"margin-bottom: 15px;\">\n Sort by\n <select *ngIf=\"taskIds.length > 1\" [disabled]=\"sortCriteria.criteria == 'alpha'\" [(ngModel)]=\"sortCriteria.taskId\" (ngModelChange)=\"toggleSort()\">\n <option *ngFor=\"let taskId of taskIds\" value=\"{{ taskId }}\">Task {{ taskId }}</option>\n </select>\n <select [(ngModel)]=\"sortCriteria.criteria\" (ngModelChange)=\"toggleSort()\">\n <option value=\"alpha\">Alphabetical</option>\n <option *ngFor=\"let row of summaryRows\" value=\"{{ row }}\">{{ row }}</option>\n </select>\n <select [(ngModel)]=\"sortCriteria.direction\" (ngModelChange)=\"toggleSort()\">\n <option value=\"asc\">ascending</option>\n <option value=\"desc\">descending</option>\n </select>\n</div>\n<div class=\"table-container\">\n <table class=\"confusion-matrix-table\">\n <thead>\n <tr class=\"first-row\">\n <th class=\"first-col\">\n Concept\n <span *ngIf=\"taskIds.length > 1\">\n <br>\n ( {{ taskIds[0] }}\n <span *ngFor=\"let taskId of taskIds; let i = index\"><span *ngIf=\"i !== 0\">/ {{ taskId }}</span></span>\n )\n </span>\n </th>\n <th *ngFor=\"let concept of rowOrdering\">\n <div class=\"concept-header\" [ngbTooltip]=\"concept\">{{ concept }}</div>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let concept1 of rowOrdering\">\n <td class=\"first-col\">{{ concept1 }}</td>\n <td *ngFor=\"let concept2 of rowOrdering\" [ngClass]=\"{ 'single-number': taskIds.length === 1 }\">\n <div>\n <span *ngFor=\"let taskId of taskIds; let i = index\">\n <span *ngIf=\"i !== 0\"> / </span>{{ data[taskId].matrix[concept1][concept2] | noEntry }}\n </span>\n </div>\n <div>\n <ngb-progressbar *ngIf=\"taskIds.length === 1\"\n type=\"{{ concept1 === concept2 ? 'success' : 'warning' }}\"\n [value]=\"data[taskIds[0]].matrix[concept1][concept2] / summaries[taskIds[0]].Support[concept1] * 100\"\n height=\"0.5rem\">\n </ngb-progressbar>\n </div>\n </td>\n </tr>\n <!-- Summary row -->\n <tr style=\"height: 15px;\"></tr>\n <tr class=\"summary-row\" *ngFor=\"let row of summaryRows\">\n <td class=\"first-col\">{{ row }}</td>\n <td *ngFor=\"let concept of rowOrdering\">\n <span *ngFor=\"let taskId of taskIds; let i = index\">\n <span *ngIf=\"i !== 0\"> / </span>{{ summaries[taskId][row][concept] }}\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n</div>\n<div *ngIf=\"nPages > 1\" style=\"margin-top: 15px\">\n <span class=\"paginator\" (click)=\"changePage(1)\">\n <i class=\"ion-ios-rewind\"></i>\n </span>\n <span class=\"paginator\" (click)=\"changePage(page - 1)\">\n <i class=\"ion-android-arrow-dropleft\"></i>\n </span>\n <span *ngFor=\"let pageNum of pageNums\">\n <span class=\"paginator\" [ngClass]=\"{'active': page === pageNum}\" (click)=\"changePage(pageNum)\">{{ pageNum }}</span>\n </span>\n <span class=\"paginator\" (click)=\"changePage(page + 1)\">\n <i class=\"ion-android-arrow-dropright\"></i>\n </span>\n <span class=\"paginator\" (click)=\"changePage(nPages)\">\n <i class=\"ion-ios-fastforward\"></i>\n </span>\n Jump to page:\n <select [(ngModel)]=\"page\" (ngModelChange)=\"changePage()\">\n <option *ngFor=\"let in of counter(nPages); let i = index\" value=\"{{ i + 1 }}\">{{ i + 1 }}</option>\n </select>\n</div>\n<div style=\"margin-top: 15px;\">\n <a href=\"javascript:;\" (click)=\"getTableData()\">\n Download Table as CSV\n </a>\n</div>\n",
styles: [".table-container{max-width:100%;overflow-x:auto}.confusion-matrix-table{border:1px solid gray}.confusion-matrix-table td,.confusion-matrix-table th{border:1px solid #d7d7d7;font-size:12px;min-width:60px;padding:6px}.confusion-matrix-table .single-number{padding:0}.confusion-matrix-table .single-number div:nth-child(1){padding:6px}.confusion-matrix-table th{text-align:center}.confusion-matrix-table .first-col{background-color:#f5f6f7}.confusion-matrix-table .summary-row .first-col{font-weight:600}.confusion-matrix-table .first-row{background-color:#f5f6f7;font-size:13px;font-weight:600}.confusion-matrix-table .concept-header{max-width:60px;overflow-x:hidden;text-overflow:ellipsis}.paginator{padding-left:7px;padding-right:7px}.paginator:hover{background-color:#eee;cursor:pointer}.paginator.active{background-color:#ddd}:host /deep/ .progress{background-color:#fff}:host /deep/ .ng-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option{font-size:12px}:host /deep/ .ng-select .ng-select-container .ng-value-container .ng-input,:host /deep/ .ng-select .ng-select-container .ng-value-container .ng-value-label{font-size:12px}:host /deep/ .ng-select .ng-select-container .ng-value-container .ng-placeholder{font-size:13px}"]
}] }
];
VixConfusionMatrixComponent.ctorParameters = () => [];
VixConfusionMatrixComponent.propDecorators = {
pageSize: [{ type: Input }],
data: [{ type: Input }],
tableExport: [{ type: Output }]
};
if (false) {
/** @type {?} */
VixConfusionMatrixComponent.prototype.pageSize;
/** @type {?} */
VixConfusionMatrixComponent.prototype.data;
/** @type {?} */
VixConfusionMatrixComponent.prototype.tableExport;
/** @type {?} */
VixConfusionMatrixComponent.prototype.concepts;
/** @type {?} */
VixConfusionMatrixComponent.prototype.selectedConcepts;
/** @type {?} */
VixConfusionMatrixComponent.prototype.nPages;
/** @type {?} */
VixConfusionMatrixComponent.prototype.pageNums;
/** @type {?} */
VixConfusionMatrixComponent.prototype.summaries;
/** @type {?} */
VixConfusionMatrixComponent.prototype.overallSummaries;
/** @type {?} */
VixConfusionMatrixComponent.prototype.summaryRows;
/** @type {?} */
VixConfusionMatrixComponent.prototype.sortCriteria;
/** @type {?} */
VixConfusionMatrixComponent.prototype.rowOrdering;
/** @type {?} */
VixConfusionMatrixComponent.prototype.taskIds;
/** @type {?} */
VixConfusionMatrixComponent.prototype.page;
/** @type {?} */
VixConfusionMatrixComponent.prototype.conceptsFiltered;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml4LWNvbmZ1c2lvbi1tYXRyaXguY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6Im5nOi8vdml4LXVpLyIsInNvdXJjZXMiOlsidml4LWNvbmZ1c2lvbi1tYXRyaXgvdml4LWNvbmZ1c2lvbi1tYXRyaXguY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQXFCLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMxRixPQUFPLEtBQUssQ0FBQyxNQUFNLFFBQVEsQ0FBQztBQU81QixNQUFNO0lBcUJKO1FBbkJTLGFBQVEsR0FBRyxDQUFDLENBQUM7UUFDYixTQUFJLEdBQVEsRUFBRSxDQUFDO1FBRWQsZ0JBQVcsR0FBc0IsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUVuRSxhQUFRLEdBQWEsRUFBRSxDQUFDO1FBQ3hCLHFCQUFnQixHQUFhLEVBQUUsQ0FBQztRQUNoQyxXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ1gsYUFBUSxHQUFHLEVBQUUsQ0FBQztRQUNkLGNBQVMsR0FBUSxFQUFFLENBQUM7UUFDcEIscUJBQWdCLEdBQVEsRUFBRSxDQUFDO1FBQzNCLGdCQUFXLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQztRQUM1QyxpQkFBWSxHQUFRLEVBQUUsQ0FBQztRQUN2QixnQkFBVyxHQUFhLEVBQUUsQ0FBQztRQUMzQixZQUFPLEdBQVUsRUFBRSxDQUFDO1FBQ3BCLFNBQUksR0FBRyxDQUFDLENBQUM7UUFFRCxxQkFBZ0IsR0FBYSxFQUFFLENBQUM7SUFFekIsQ0FBQzs7Ozs7SUFFUixhQUFhLENBQUMsSUFBSSxHQUFHLEtBQUs7UUFDaEMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFOUMsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxFQUFF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QUcsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO2FBQzdFO1NBQ0Y7UUFFRCxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzNCLENBQUM7OztZQXpNRixTQUFTLFNBQUM7Z0JBQ1QsUUFBUSxFQUFFLHNCQUFzQjtnQkFDaEMsaWdJQUFvRDs7YUFFckQ7Ozs7dUJBR0UsS0FBSzttQkFDTCxLQUFLOzBCQUVMLE1BQU07Ozs7SUFIUCwrQ0FBc0I7O0lBQ3RCLDJDQUF3Qjs7SUFFeEIsa0RBQW1FOztJQUVuRSwrQ0FBd0I7O0lBQ3hCLHVEQUFnQzs7SUFDaEMsNkNBQVc7O0lBQ1gsK0NBQWM7O0lBQ2QsZ0RBQW9COztJQUNwQix1REFBMkI7O0lBQzNCLGtEQUE0Qzs7SUFDNUMsbURBQXVCOztJQUN2QixrREFBMkI7O0lBQzNCLDhDQUFvQjs7SUFDcEIsMkNBQVM7O0lBRVQsdURBQXdDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkNoYW5nZXMsIE9uSW5pdCwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgKiBhcyBfIGZyb20gJ2xvZGFzaCc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3ZpeC1jb25mdXNpb24tbWF0cml4JyxcbiAgdGVtcGxhdGVVcmw6ICcuL3ZpeC1jb25mdXNpb24tbWF0cml4LmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdml4LWNvbmZ1c2lvbi1tYXRyaXguY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVml4Q29uZnVzaW9uTWF0cml4Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMge1xuXG4gIEBJbnB1dCgpIHBhZ2VTaXplID0gMDtcbiAgQElucHV0KCkgZGF0YTogYW55ID0ge307XG5cbiAgQE91dHB1dCgpIHRhYmxlRXhwb3J0OiBFdmVudEVtaXR0ZXI8YW55PiA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuXG4gIGNvbmNlcHRzOiBzdHJpbmdbXSA9IFtdO1xuICBzZWxlY3RlZENvbmNlcHRzOiBzdHJpbmdbXSA9IFtdO1xuICBuUGFnZXMgPSAxO1xuICBwYWdlTnVtcyA9IFtdO1xuICBzdW1tYXJpZXM6IGFueSA9IHt9O1xuICBvdmVyYWxsU3VtbWFyaWVzOiBhbnkgPSB7fTtcbiAgc3VtbWFyeVJvd3MgPSBbJ1RQJywgJ0ZQJywgJ0ZOJywgJ1N1cHBvcnQnXTtcbiAgc29ydENyaXRlcmlhOiBhbnkgPSB7fTtcbiAgcm93T3JkZXJpbmc6IHN0cmluZ1tdID0gW107XG4gIHRhc2tJZHM6IGFueVtdID0gW107XG4gIHBhZ2UgPSAxO1xuXG4gIHByaXZhdGUgY29uY2VwdHNGaWx0ZXJlZDogc3RyaW5nW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcigpIHt9XG5cbiAgcHJpdmF0ZSBnZXRSb3dzVG9TaG93KGluaXQgPSBmYWxzZSk6IHZvaWQge1xuICAgIHRoaXMuY29uY2VwdHNGaWx0ZXJlZCA9IHRoaXMuY29uY2VwdHMuc2xpY2UoKTtcblxuICAgIGlmICh0aGlzLnNlbGVjdGVkQ29uY2VwdHMubGVuZ3RoKSB7XG4gICAgICB0aGlzLmNvbmNlcHRzRmlsdGVyZWQgPSB0aGlzLmNvbmNlcHRzRmlsdGVyZWQuZmlsdGVyKFxuICAgICAgICAgIGNvbmNlcHQgPT4gdGhpcy5zZWxlY3RlZENvbmNlcHRzLmluZGV4T2YoY29uY2VwdCkgIT09IC0xKTtcbiAgICB9XG5cbiAgICB0aGlzLm5QYWdlcyA9IHRoaXMucGFnZVNpemUgPyBNYXRoLmNlaWwodGhpcy5jb25jZXB0c0ZpbHRlcmVkLmxlbmd0aCAvIHRoaXMucGFnZVNpemUpIDogMTtcbiAgICBpZiAoIWluaXQgJiYgdGhpcy5jb25jZXB0c0ZpbHRlcmVkLmxlbmd0aCA9PT0gdGhpcy5jb25jZXB0cy5sZW5ndGgpIHtcbiAgICAgIC8vIFVzZSBvdmVyYWxsIHN1bW1hcnkgd2hpY2ggaXMgY2FjaGVkIGluLW1lbW9yeVxuICAgICAgdGhpcy5zdW1tYXJpZXMgPSBfLmNsb25lRGVlcCh0aGlzLm92ZXJhbGxTdW1tYXJpZXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNvbXB1dGVTdW1tYXJ5KGluaXQpO1xuICAgIH1cbiAgICB0aGlzLnRvZ2dsZVNvcnQoKTtcbiAgfVxuXG4gIHRvZ2dsZVNvcnQoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuc29ydENyaXRlcmlhLmNyaXRlcmlhID09PSAnYWxwaGEnKSB7XG4gICAgICB0aGlzLmNvbmNlcHRzRmlsdGVyZWQuc29ydCgpO1xuICAgICAgaWYgKHRoaXMuc29ydENyaXRlcmlhLmRpcmVjdGlvbiA9PT0gJ2Rlc2MnKSB7XG4gICAgICAgIHRoaXMuY29uY2VwdHNGaWx0ZXJlZC5yZXZlcnNlKCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGRpcmVjdGlvbiA9IHRoaXMuc29ydENyaXRlcmlhLmRpcmVjdGlvbiB8fCAnZGVzYyc7XG4gICAgICB0aGlzLmNvbmNlcHRzRmlsdGVyZWQuc29ydCgoYSwgYikgPT4ge1xuICAgICAgICBjb25zdCBmaXJzdCA9IHRoaXMuc3VtbWFyaWVzW3RoaXMuc29ydENyaXRlcmlhLnRhc2tJZF1bdGhpcy5zb3J0Q3JpdGVyaWEuY3JpdGVyaWFdW2FdIHx8IDA7XG4gICAgICAgIGNvbnN0IHNlY29uZCA9IHRoaXMuc3VtbWFyaWVzW3RoaXMuc29ydENyaXRlcmlhLnRhc2tJZF1bdGhpcy5zb3J0Q3JpdGVyaWEuY3JpdGVyaWFdW2JdIHx8IDA7XG4gICAgICAgIHJldHVybiAoZmlyc3QgLSBzZWNvbmQpICogKGRpcmVjdGlvbiA9PT0gJ2Rlc2MnID8gLTEgOiAxKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMucGFnZSA9IDE7XG4gICAgdGhpcy5jaGFuZ2VQYWdlKCk7XG4gIH1cblxuICBjb21wdXRlU3VtbWFyeShpbml0OiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5zdW1tYXJpZXMgPSB7fTtcbiAgICBmb3IgKGNvbnN0IHRhc2tJZCBvZiB0aGlzLnRhc2tJZHMpIHtcbiAgICAgIHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0gPSB7XG4gICAgICAgIFRQOiB7fSxcbiAgICAgICAgRlA6IHt9LFxuICAgICAgICBGTjoge30sXG4gICAgICAgIFN1cHBvcnQ6IHt9LFxuICAgICAgfTtcbiAgICAgIGZvciAoY29uc3QgY29uY2VwdCBvZiB0aGlzLmNvbmNlcHRzRmlsdGVyZWQpIHtcbiAgICAgICAgdGhpcy5zdW1tYXJpZXNbdGFza0lkXS5UUFtjb25jZXB0XSA9IDA7XG4gICAgICAgIHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0uRlBbY29uY2VwdF0gPSAwO1xuICAgICAgICB0aGlzLnN1bW1hcmllc1t0YXNrSWRdLkZOW2NvbmNlcHRdID0gMDtcbiAgICAgICAgdGhpcy5zdW1tYXJpZXNbdGFza0lkXS5TdXBwb3J0W2NvbmNlcHRdID0gMDtcbiAgICAgIH1cbiAgICAgIGZvciAoY29uc3QgY29uY2VwdDEgb2YgdGhpcy5jb25jZXB0c0ZpbHRlcmVkKSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uY2VwdDIgb2YgdGhpcy5jb25jZXB0c0ZpbHRlcmVkKSB7XG4gICAgICAgICAgbGV0IGNvdW50O1xuICAgICAgICAgIGlmICh0aGlzLmRhdGFbdGFza0lkXS5tYXRyaXhbY29uY2VwdDFdKSB7XG4gICAgICAgICAgICBjb3VudCA9IHRoaXMuZGF0YVt0YXNrSWRdLm1hdHJpeFtjb25jZXB0MV1bY29uY2VwdDJdIHx8IDA7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvdW50ID0gMDtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGNvbmNlcHQxID09PSBjb25jZXB0Mikge1xuICAgICAgICAgICAgdGhpcy5zdW1tYXJpZXNbdGFza0lkXS5UUFtjb25jZXB0MV0gKz0gY291bnQ7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0uRk5bY29uY2VwdDFdICs9IGNvdW50O1xuICAgICAgICAgICAgdGhpcy5zdW1tYXJpZXNbdGFza0lkXS5GUFtjb25jZXB0Ml0gKz0gY291bnQ7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0uU3VwcG9ydFtjb25jZXB0MV0gKz0gY291bnQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChpbml0KSB7XG4gICAgICAgIHRoaXMub3ZlcmFsbFN1bW1hcmllc1t0YXNrSWRdID0ge1xuICAgICAgICAgIFRQOiB7fSxcbiAgICAgICAgICBGUDoge30sXG4gICAgICAgICAgRk46IHt9LFxuICAgICAgICAgIFN1cHBvcnQ6IHt9LFxuICAgICAgICB9O1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmNlcHQgb2YgdGhpcy5jb25jZXB0c0ZpbHRlcmVkKSB7XG4gICAgICAgICAgdGhpcy5vdmVyYWxsU3VtbWFyaWVzW3Rhc2tJZF0uVFBbY29uY2VwdF0gPSB0aGlzLnN1bW1hcmllc1t0YXNrSWRdLlRQW2NvbmNlcHRdO1xuICAgICAgICAgIHRoaXMub3ZlcmFsbFN1bW1hcmllc1t0YXNrSWRdLkZQW2NvbmNlcHRdID0gdGhpcy5zdW1tYXJpZXNbdGFza0lkXS5GUFtjb25jZXB0XTtcbiAgICAgICAgICB0aGlzLm92ZXJhbGxTdW1tYXJpZXNbdGFza0lkXS5GTltjb25jZXB0XSA9IHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0uRk5bY29uY2VwdF07XG4gICAgICAgICAgdGhpcy5vdmVyYWxsU3VtbWFyaWVzW3Rhc2tJZF0uU3VwcG9ydFtjb25jZXB0XSA9IHRoaXMuc3VtbWFyaWVzW3Rhc2tJZF0uU3VwcG9ydFtjb25jZXB0XTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNoYW5nZVBhZ2UocGFnZT86IG51bWJlcik6IHZvaWQge1xuICAgIGlmIChwYWdlKSB7XG4gICAgICB0aGlzLnBhZ2UgPSBwYWdlO1xuICAgIH1cbiAgICB0aGlzLnBhZ2UgPSBNYXRoLm1pbihNYXRoLm1heCgrdGhpcy5wYWdlLCAxKSwgdGhpcy5uUGFnZXMpO1xuICAgIHRoaXMucGFnZU51bXMgPSBbXTtcbiAgICBsZXQgbG93TGltaXQ7XG4gICAgbGV0IHVwcExpbWl0O1xuICAgIGlmICh0aGlzLnBhZ2UgPD0gMikge1xuICAgICAgbG93TGltaXQgPSAxO1xuICAgICAgdXBwTGltaXQgPSBNYXRoLm1pbig1LCB0aGlzLm5QYWdlcyk7XG4gICAgfSBlbHNlIGlmIChNYXRoLmFicyh0aGlzLnBhZ2UgLSB0aGlzLm5QYWdlcykgPD0gMSkge1xuICAgICAgbG93TGltaXQgPSBNYXRoLm1heCgxLCB0aGlzLm5QYWdlcyAtIDQpO1xuICAgICAgdXBwTGltaXQgPSB0aGlzLm5QYWdlcztcbiAgICB9IGVsc2Uge1xuICAgICAgbG93TGltaXQgPSB0aGlzLnBhZ2UgLSAyO1xuICAgICAgdXBwTGltaXQgPSB0aGlzLnBhZ2UgKyAyO1xuICAgIH1cbiAgICBmb3IgKGxldCBpID0gbG93TGltaXQ7IGkgPD0gdXBwTGltaXQ7IGkrKykge1xuICAgICAgdGhpcy5wYWdlTnVtcyA9IHRoaXMucGFnZU51bXMuY29uY2F0KFtpXSk7XG4gICAgfVxuICAgIHRoaXMucm93T3JkZXJpbmcgPSB0aGlzLmNvbmNlcHRzRmlsdGVyZWQuc2xpY2UoKTtcbiAgICBpZiAoIXRoaXMucGFnZVNpemUpIHtcbiAgICAgIC8vIE5vIHBhZ2luZyByZXF1aXJlZFxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnJvd09yZGVyaW5nID0gdGhpcy5yb3dPcmRlcmluZy5zbGljZSgodGhpcy5wYWdlIC0gMSkgKiB0aGlzLnBhZ2VTaXplLCB0aGlzLnBhZ2UgKiB0aGlzLnBhZ2VTaXplKTtcbiAgfVxuXG4gIG9uQ2hhbmdlKGV2ZW50OiBhbnkpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkQ29uY2VwdHMgPSBldmVudDtcbiAgICB0aGlzLmdldFJvd3NUb1Nob3coKTtcbiAgfVxuXG4gIGNvdW50ZXIoaTogbnVtYmVyKTogQXJyYXk8YW55PiB7XG4gICAgcmV0dXJuIG5ldyBBcnJheShpKTtcbiAgfVxuXG4gIGdldFRhYmxlRGF0YSgpIHtcbiAgICBjb25zdCB0YWJsZUhlYWRlciA9IFsnY29uY2VwdCddLmNvbmNhdCh0aGlzLmNvbmNlcHRzRmlsdGVyZWQpO1xuICAgIGNvbnN0IHRhYmxlRGF0YSA9IFtdO1xuICAgIGZvciAoY29uc3QgY29uY2VwdCBvZiB0aGlzLmNvbmNlcHRzRmlsdGVyZWQpIHtcbiAgICAgIGNvbnN0IHJvdyA9IHsgY29uY2VwdCB9O1xuICAgICAgZm9yIChjb25zdCBjb25jZXB0MSBvZiB0aGlzLmNvbmNlcHRzRmlsdGVyZWQpIHtcbiAgICAgICAgcm93W2NvbmNlcHQxXSA9IHRoaXMudGFza0lkcy5tYXAodGFza0lkID0+IHtcbiAgICAgICAgICBjb25zdCBjb3VudCA9IHRoaXMuZGF0YVt0YXNrSWRdLm1hdHJpeFtjb25jZXB0XVtjb25jZXB0MV07XG4gICAgICAgICAgcmV0dXJuIGNvdW50ID09IG51bGwgPyAnLScgOiBjb3VudDtcbiAgICAgICAgfSkuam9pbignIC8gJyk7XG4gICAgICB9XG4gICAgICB0YWJsZURhdGEucHVzaChyb3cpO1xuICAgIH1cbiAgICB0aGlzLnRhYmxlRXhwb3J0LmVtaXQoeyB0YWJsZUhlYWRlciwgdGFibGVEYXRhIH0pO1xuICB9XG5cbiAgbmdPbkluaXQoKSB7XG4gICAgLy8gUGFnZSBzaXplIG11c3QgYmUgbm9uLXplcm87IHBhZ2Ugc2l6ZSA9IDAgbWVhbnMgYWxsIHRvIGJlIGRpc3BsYXllZCB3aXRob3V0IHBhZ2luZ1xuICAgIHRoaXMucGFnZVNpemUgPSBNYXRoLm1heCh0aGlzLnBhZ2VTaXplLCAwKTtcblxuICAgIHRoaXMuaW5pdGlhbGl6ZSgpO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoKTogdm9pZCB7XG4gICAgdGhpcy5pbml0aWFsaXplKCk7XG4gIH1cblxuICBpbml0aWFsaXplKCk6IHZvaWQge1xuICAgIHRoaXMudGFza0lkcyA9IE9iamVjdC5rZXlzKHRoaXMuZGF0YSk7XG4gICAgdGhpcy5zb3J0Q3JpdGVyaWEgPSB7XG4gICAgICBjcml0ZXJpYTogJ2FscGhhJyxcbiAgICAgIGRpcmVjdGlvbjogJ2FzYycsXG4gICAgICB0YXNrSWQ6IHRoaXMudGFza0lkc1swXSxcbiAgICB9O1xuICAgIHRoaXMuc2VsZWN0ZWRDb25jZXB0cyA9IFtdO1xuXG4gICAgbGV0IGNvbmNlcHRzQ29tYmluZWQgPSBbXTtcbiAgICBmb3IgKGNvbnN0IHRhc2tJZCBvZiB0aGlzLnRhc2tJZHMpIHtcbiAgICAgIGNvbmNlcHRzQ29tYmluZWQgPSBjb25jZXB0c0NvbWJpbmVkLmNvbmNhdCh0aGlzLmRhdGFbdGFza0lkXS5jb25jZXB0cyk7XG4gICAgfVxuICAgIHRoaXMuY29uY2VwdHMgPSBBcnJheS5mcm9tKG5ldyBTZXQoY29uY2VwdHNDb21iaW5lZCkpO1xuXG4gICAgZm9yIChjb25zdCB0YXNrSWQgb2YgdGhpcy50YXNrSWRzKSB7XG4gICAgICBmb3IgKGNvbnN0IGNvbmNlcHQgb2YgdGhpcy5jb25jZXB0cykge1xuICAgICAgICB0aGlzLmRhdGFbdGFza0lkXS5tYXRyaXhbY29uY2VwdF0gPSB0aGlzLmRhdGFbdGFza0lkXS5tYXRyaXhbY29uY2VwdF0gfHwge307XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5nZXRSb3dzVG9TaG93KHRydWUpO1xuICB9XG5cbn1cbiJdfQ==