cnpmcore
Version:
323 lines • 33.1 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BinarySyncerService = void 0;
const promises_1 = require("fs/promises");
const tegg_1 = require("@eggjs/tegg");
const promises_2 = __importDefault(require("fs/promises"));
const binaries_1 = __importDefault(require("../../../config/binaries"));
const NFSAdapter_1 = require("../../common/adapter/NFSAdapter");
const Task_1 = require("../../common/enum/Task");
const FileUtil_1 = require("../../common/FileUtil");
const BinaryRepository_1 = require("../../repository/BinaryRepository");
const Task_2 = require("../entity/Task");
const Binary_1 = require("../entity/Binary");
const TaskService_1 = require("./TaskService");
const AbstractBinary_1 = require("../../common/adapter/binary/AbstractBinary");
const AbstractService_1 = require("../../common/AbstractService");
const Binary_2 = require("../../common/enum/Binary");
const lodash_1 = require("lodash");
function isoNow() {
return new Date().toISOString();
}
let BinarySyncerService = class BinarySyncerService extends AbstractService_1.AbstractService {
// canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz
// -> node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz
// canvas 历史版本的 targetName 可能是 category 需要兼容
async findBinary(targetName, parent, name) {
return await this.binaryRepository.findBinary(targetName, parent, name);
}
async listDirBinaries(binary) {
return await this.binaryRepository.listBinaries(binary.category, `${binary.parent}${binary.name}`);
}
async listRootBinaries(binaryName) {
// 通常 binaryName 和 category 是一样的,但是有些特殊的 binaryName 会有多个 category,比如 canvas
// 所以查询 canvas 的时候,需要将 binaryName 和 category 的数据都查出来
const { category, } = binaries_1.default[binaryName];
const reqs = [
this.binaryRepository.listBinaries(binaryName, '/'),
];
if (category && category !== binaryName) {
reqs.push(this.binaryRepository.listBinaries(category, '/'));
}
const [rootBinary, categoryBinary,] = await Promise.all(reqs);
const versions = rootBinary.map(b => b.name);
categoryBinary?.forEach(b => {
const version = b.name;
// 只将没有的版本添加进去
if (!versions.includes(version)) {
rootBinary.push(b);
}
});
return rootBinary;
}
async downloadBinary(binary) {
return await this.nfsAdapter.getDownloadUrlOrStream(binary.storePath);
}
async createTask(binaryName, lastData) {
try {
return await this.taskService.createTask(Task_2.Task.createSyncBinary(binaryName, lastData), false);
}
catch (e) {
this.logger.error('[BinarySyncerService.createTask] binaryName: %s, error: %s', binaryName, e);
}
}
async findTask(taskId) {
return await this.taskService.findTask(taskId);
}
async findTaskLog(task) {
return await this.taskService.findTaskLog(task);
}
async findExecuteTask() {
return await this.taskService.findExecuteTask(Task_1.TaskType.SyncBinary);
}
async executeTask(task) {
const binaryName = task.targetName;
const binaryAdapter = await this.getBinaryAdapter(binaryName);
const logUrl = `${this.config.cnpmcore.registry}/-/binary/${binaryName}/syncs/${task.taskId}/log`;
let logs = [];
logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Start sync binary "${binaryName}" 🚧🚧🚧🚧🚧`);
if (!binaryAdapter) {
task.error = 'unknow binaryName';
logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`);
this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', task.taskId, task.targetName, task.error);
await this.taskService.finishTask(task, Task_1.TaskState.Fail, logs.join('\n'));
return;
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
this.logger.info('[BinarySyncerService.executeTask:start] taskId: %s, targetName: %s, log: %s', task.taskId, task.targetName, logUrl);
try {
const [hasDownloadError] = await this.syncDir(binaryAdapter, task, '/');
logs.push(`[${isoNow()}] 🟢 log: ${logUrl}`);
logs.push(`[${isoNow()}] 🟢🟢🟢🟢🟢 "${binaryName}" 🟢🟢🟢🟢🟢`);
await this.taskService.finishTask(task, Task_1.TaskState.Success, logs.join('\n'));
// 确保没有下载异常才算 success
await binaryAdapter.finishFetch(!hasDownloadError, binaryName);
this.logger.info('[BinarySyncerService.executeTask:success] taskId: %s, targetName: %s, log: %s, hasDownloadError: %s', task.taskId, task.targetName, logUrl, hasDownloadError);
}
catch (err) {
task.error = err.message;
logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`);
logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`);
if (err.name === 'HttpClientRequestTimeoutError'
|| err.name === 'ConnectionError'
|| err.name === 'ConnectTimeoutError') {
this.logger.warn('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', task.taskId, task.targetName, task.error);
this.logger.warn(err);
}
else {
this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', task.taskId, task.targetName, task.error);
this.logger.error(err);
}
await binaryAdapter.finishFetch(false, binaryName);
await this.taskService.finishTask(task, Task_1.TaskState.Fail, logs.join('\n'));
}
}
async syncDir(binaryAdapter, task, dir, parentIndex = '', latestVersionParent = '/') {
const binaryName = task.targetName;
const result = await binaryAdapter.fetch(dir, binaryName);
let hasDownloadError = false;
let hasItems = false;
if (result && result.items.length > 0) {
hasItems = true;
let logs = [];
const { newItems, latestVersionDir } = await this.diff(binaryName, dir, result.items, latestVersionParent);
logs.push(`[${isoNow()}][${dir}] 🚧 Syncing diff: ${result.items.length} => ${newItems.length}, Binary class: ${binaryAdapter.constructor.name}`);
// re-check latest version
for (const [index, { item, reason }] of newItems.entries()) {
if (item.isDir) {
logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Start sync dir ${JSON.stringify(item)}, reason: ${reason}`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
const [hasError, hasSubItems] = await this.syncDir(binaryAdapter, task, `${dir}${item.name}`, `${parentIndex}${index}.`, latestVersionDir);
if (hasError) {
hasDownloadError = true;
}
else {
// if any file download error, let dir sync again next time
// if empty dir, don't save it
if (hasSubItems) {
await this.saveBinaryItem(item);
}
}
}
else {
// download to nfs
logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Downloading ${JSON.stringify(item)}, reason: ${reason}`);
// skip exists binary file
const existsBinary = await this.binaryRepository.findBinary(item.category, item.parent, item.name);
if (existsBinary && existsBinary.date === item.date) {
logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] binary file exists, skip download, binaryId: ${existsBinary.binaryId}`);
this.logger.info('[BinarySyncerService.syncDir:skipDownload] binaryId: %s exists, storePath: %s', existsBinary.binaryId, existsBinary.storePath);
continue;
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
let localFile = '';
try {
const { tmpfile, headers, timing } = await (0, FileUtil_1.downloadToTempfile)(this.httpclient, this.config.dataDir, item.sourceUrl, { ignoreDownloadStatuses: item.ignoreDownloadStatuses });
const log = `[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)}, ${item.sourceUrl} => ${tmpfile}`;
logs.push(log);
this.logger.info('[BinarySyncerService.syncDir:downloadToTempfile] %s', log);
localFile = tmpfile;
const binary = await this.saveBinaryItem(item, tmpfile);
logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] Synced file success, binaryId: ${binary.binaryId}`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
}
catch (err) {
if (err.name === 'DownloadNotFoundError') {
this.logger.info('Not found %s, skip it', item.sourceUrl);
logs.push(`[${isoNow()}][${dir}] 🧪️ [${parentIndex}${index}] Download ${item.sourceUrl} not found, skip it`);
}
else {
if (err.name === 'DownloadStatusInvalidError') {
this.logger.warn('Download binary %s %s', item.sourceUrl, err);
}
else {
this.logger.error('Download binary %s %s', item.sourceUrl, err);
}
hasDownloadError = true;
logs.push(`[${isoNow()}][${dir}] ❌ [${parentIndex}${index}] Download ${item.sourceUrl} error: ${err}`);
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
}
finally {
if (localFile) {
await (0, promises_1.rm)(localFile, { force: true });
}
}
}
}
if (hasDownloadError) {
logs.push(`[${isoNow()}][${dir}] ❌ Synced dir fail`);
}
else {
logs.push(`[${isoNow()}][${dir}] 🟢 Synced dir success, hasItems: ${hasItems}`);
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
}
return [hasDownloadError, hasItems];
}
// see https://github.com/cnpm/cnpmcore/issues/556
// 上游可能正在发布新版本、同步流程中断,导致同步的时候,文件列表不一致
// 如果的当前目录命中 latestVersionParent 父目录,那么就再校验一下当前目录
// 如果 existsItems 为空或者经过修改,那么就不需要 revalidate 了
async diff(binaryName, dir, fetchItems, latestVersionParent = '/') {
const existsItems = await this.binaryRepository.listBinaries(binaryName, dir);
const existsMap = new Map();
for (const item of existsItems) {
existsMap.set(item.name, item);
}
const diffItems = [];
let latestItem;
for (const item of fetchItems) {
const existsItem = existsMap.get(item.name);
if (!existsItem) {
diffItems.push({
item: Binary_1.Binary.create({
category: binaryName,
parent: dir,
name: item.name,
isDir: item.isDir,
size: 0,
date: item.date,
sourceUrl: item.url,
ignoreDownloadStatuses: item.ignoreDownloadStatuses,
}),
reason: 'new item',
});
}
else if (existsItem.date !== item.date) {
diffItems.push({
item: existsItem,
reason: `date diff, local: ${JSON.stringify(existsItem.date)}, remote: ${JSON.stringify(item.date)}`,
});
existsItem.sourceUrl = item.url;
existsItem.ignoreDownloadStatuses = item.ignoreDownloadStatuses;
existsItem.date = item.date;
}
else if (dir.endsWith(latestVersionParent)) {
if (!latestItem) {
latestItem = (0, lodash_1.sortBy)(fetchItems, ['date']).pop();
}
const isLatestItem = latestItem?.name === item.name;
if (isLatestItem && existsItem.isDir) {
diffItems.push({
item: existsItem,
reason: `revalidate latest version, latest parent dir is ${latestVersionParent}, current dir is ${dir}, current name is ${existsItem.name}`,
});
latestVersionParent = `${latestVersionParent}${existsItem.name}`;
}
}
}
return {
newItems: diffItems,
latestVersionDir: latestVersionParent,
};
}
async saveBinaryItem(binary, tmpfile) {
if (tmpfile) {
const stat = await promises_2.default.stat(tmpfile);
binary.size = stat.size;
await this.nfsAdapter.uploadFile(binary.storePath, tmpfile);
this.logger.info('[BinarySyncerService.saveBinaryItem:uploadFile] binaryId: %s, size: %d, %s => %s', binary.binaryId, stat.size, tmpfile, binary.storePath);
}
await this.binaryRepository.saveBinary(binary);
return binary;
}
async getBinaryAdapter(binaryName) {
const config = this.config.cnpmcore;
const binaryConfig = binaries_1.default[binaryName];
let binaryAdapter;
if (config.sourceRegistryIsCNpm) {
binaryAdapter = await this.eggObjectFactory.getEggObject(AbstractBinary_1.AbstractBinary, Binary_2.BinaryType.Api);
}
else {
binaryAdapter = await this.eggObjectFactory.getEggObject(AbstractBinary_1.AbstractBinary, binaryConfig.type);
}
await binaryAdapter.initFetch(binaryName);
return binaryAdapter;
}
};
exports.BinarySyncerService = BinarySyncerService;
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", BinaryRepository_1.BinaryRepository)
], BinarySyncerService.prototype, "binaryRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", TaskService_1.TaskService)
], BinarySyncerService.prototype, "taskService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", Object)
], BinarySyncerService.prototype, "httpclient", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", NFSAdapter_1.NFSAdapter)
], BinarySyncerService.prototype, "nfsAdapter", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", Object)
], BinarySyncerService.prototype, "eggObjectFactory", void 0);
exports.BinarySyncerService = BinarySyncerService = __decorate([
(0, tegg_1.SingletonProto)({
accessLevel: tegg_1.AccessLevel.PUBLIC,
})
], BinarySyncerService);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmluYXJ5U3luY2VyU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2FwcC9jb3JlL3NlcnZpY2UvQmluYXJ5U3luY2VyU2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFBQSwwQ0FBaUM7QUFDakMsc0NBS3FCO0FBSXJCLDJEQUE2QjtBQUM3Qix3RUFBOEU7QUFDOUUsZ0VBQTZEO0FBQzdELGlEQUE2RDtBQUM3RCxvREFBMkQ7QUFDM0Qsd0VBQXFFO0FBQ3JFLHlDQUFzQztBQUN0Qyw2Q0FBMEM7QUFDMUMsK0NBQTRDO0FBQzVDLCtFQUF3RjtBQUN4RixrRUFBK0Q7QUFDL0QscURBQXNEO0FBQ3RELG1DQUFnQztBQUVoQyxTQUFTLE1BQU07SUFDYixPQUFPLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDbEMsQ0FBQztBQUtNLElBQU0sbUJBQW1CLEdBQXpCLE1BQU0sbUJBQW9CLFNBQVEsaUNBQWU7SUFZdEQsOERBQThEO0lBQzlELDZGQUE2RjtJQUM3Riw0Q0FBNEM7SUFDckMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxVQUFxQyxFQUFFLE1BQWMsRUFBRSxJQUFZO1FBQ3pGLE9BQU8sTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDMUUsQ0FBQztJQUVNLEtBQUssQ0FBQyxlQUFlLENBQUMsTUFBYztRQUN6QyxPQUFPLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEdBQUcsTUFBTSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNyRyxDQUFDO0lBRU0sS0FBSyxDQUFDLGdCQUFnQixDQUFDLFVBQXNCO1FBQ2xELDJFQUEyRTtRQUMzRSxvREFBb0Q7UUFDcEQsTUFBTSxFQUNKLFFBQVEsR0FDVCxHQUFHLGtCQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDekIsTUFBTSxJQUFJLEdBQUc7WUFDWCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxHQUFHLENBQUM7U0FDcEQsQ0FBQztRQUNGLElBQUksUUFBUSxJQUFJLFFBQVEsS0FBSyxVQUFVLEVBQUU7WUFDdkMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzlEO1FBRUQsTUFBTSxDQUNKLFVBQVUsRUFDVixjQUFjLEVBQ2YsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFNUIsTUFBTSxRQUFRLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQzFCLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDdkIsY0FBYztZQUNkLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUMvQixVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ3BCO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCxPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDO0lBRU0sS0FBSyxDQUFDLGNBQWMsQ0FBQyxNQUFjO1FBQ3hDLE9BQU8sTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLHNCQUFzQixDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN4RSxDQUFDO0lBRU0sS0FBSyxDQUFDLFVBQVUsQ0FBQyxVQUFzQixFQUFFLFFBQWM7UUFDNUQsSUFBSTtZQUNGLE9BQU8sTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxXQUFJLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1NBQzlGO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyw0REFBNEQsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7U0FDaEc7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLFFBQVEsQ0FBQyxNQUFjO1FBQ2xDLE9BQU8sTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRU0sS0FBSyxDQUFDLFdBQVcsQ0FBQyxJQUFVO1FBQ2pDLE9BQU8sTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNsRCxDQUFDO0lBRU0sS0FBSyxDQUFDLGVBQWU7UUFDMUIsT0FBTyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUFDLGVBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRU0sS0FBSyxDQUFDLFdBQVcsQ0FBQyxJQUFVO1FBQ2pDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUF3QixDQUFDO1FBQ2pELE1BQU0sYUFBYSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzlELE1BQU0sTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxhQUFhLFVBQVUsVUFBVSxJQUFJLENBQUMsTUFBTSxNQUFNLENBQUM7UUFDbEcsSUFBSSxJQUFJLEdBQWEsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsbUNBQW1DLFVBQVUsY0FBYyxDQUFDLENBQUM7UUFDbkYsSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUNsQixJQUFJLENBQUMsS0FBSyxHQUFHLG1CQUFtQixDQUFDO1lBQ2pDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsZUFBZSxVQUFVLFdBQVcsSUFBSSxDQUFDLEtBQUssVUFBVSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ3hGLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsWUFBWSxVQUFVLFNBQVMsQ0FBQyxDQUFDO1lBQ3ZELElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLHVFQUF1RSxFQUN2RixJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzVDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLGdCQUFTLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUN6RSxPQUFPO1NBQ1I7UUFFRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDNUQsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNWLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDZFQUE2RSxFQUM1RixJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDeEMsSUFBSTtZQUNGLE1BQU0sQ0FBRSxnQkFBZ0IsQ0FBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQzFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsYUFBYSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQzdDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsaUJBQWlCLFVBQVUsY0FBYyxDQUFDLENBQUM7WUFDakUsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsZ0JBQVMsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQzVFLHFCQUFxQjtZQUNyQixNQUFNLGFBQWEsQ0FBQyxXQUFXLENBQUMsQ0FBQyxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUMvRCxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxxR0FBcUcsRUFDcEgsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1NBQzNEO1FBQUMsT0FBTyxHQUFRLEVBQUU7WUFDakIsSUFBSSxDQUFDLEtBQUssR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDO1lBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsZUFBZSxVQUFVLFdBQVcsSUFBSSxDQUFDLEtBQUssVUFBVSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ3hGLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsWUFBWSxVQUFVLFNBQVMsQ0FBQyxDQUFDO1lBQ3ZELElBQUksR0FBRyxDQUFDLElBQUksS0FBSywrQkFBK0I7bUJBQzNDLEdBQUcsQ0FBQyxJQUFJLEtBQUssaUJBQWlCO21CQUM5QixHQUFHLENBQUMsSUFBSSxLQUFLLHFCQUFxQixFQUFFO2dCQUN2QyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyx1RUFBdUUsRUFDdEYsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDNUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7YUFDdkI7aUJBQU07Z0JBQ0wsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsdUVBQXVFLEVBQ3ZGLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQzVDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2FBQ3hCO1lBQ0QsTUFBTSxhQUFhLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxVQUFVLENBQUMsQ0FBQztZQUNuRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxnQkFBUyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7U0FDMUU7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxhQUE2QixFQUFFLElBQVUsRUFBRSxHQUFXLEVBQUUsV0FBVyxHQUFHLEVBQUUsRUFBRSxtQkFBbUIsR0FBRyxHQUFHO1FBQ3ZILE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUF3QixDQUFDO1FBQ2pELE1BQU0sTUFBTSxHQUFHLE1BQU0sYUFBYSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDMUQsSUFBSSxnQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFDN0IsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDO1FBQ3JCLElBQUksTUFBTSxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUNyQyxRQUFRLEdBQUcsSUFBSSxDQUFDO1lBQ2hCLElBQUksSUFBSSxHQUFhLEVBQUUsQ0FBQztZQUN4QixNQUFNLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixFQUFFLEdBQUcsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLEtBQUssRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1lBQzNHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsS0FBSyxHQUFHLHNCQUFzQixNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sT0FBTyxRQUFRLENBQUMsTUFBTSxtQkFBbUIsYUFBYSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQ2xKLDBCQUEwQjtZQUMxQixLQUFLLE1BQU0sQ0FBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUMsSUFBSSxRQUFRLENBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQzNELElBQUksSUFBSSxDQUFDLEtBQUssRUFBRTtvQkFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxFQUFFLEtBQUssR0FBRyxTQUFTLFdBQVcsR0FBRyxLQUFLLG9CQUFvQixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxhQUFhLE1BQU0sRUFBRSxDQUFDLENBQUM7b0JBQ3pILE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztvQkFDNUQsSUFBSSxHQUFHLEVBQUUsQ0FBQztvQkFDVixNQUFNLENBQUUsUUFBUSxFQUFFLFdBQVcsQ0FBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsSUFBSSxFQUFFLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFHLFdBQVcsR0FBRyxLQUFLLEdBQUcsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO29CQUM3SSxJQUFJLFFBQVEsRUFBRTt3QkFDWixnQkFBZ0IsR0FBRyxJQUFJLENBQUM7cUJBQ3pCO3lCQUFNO3dCQUNMLDJEQUEyRDt3QkFDM0QsOEJBQThCO3dCQUM5QixJQUFJLFdBQVcsRUFBRTs0QkFDZixNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUM7eUJBQ2pDO3FCQUNGO2lCQUNGO3FCQUFNO29CQUNMLGtCQUFrQjtvQkFDbEIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sRUFBRSxLQUFLLEdBQUcsU0FBUyxXQUFXLEdBQUcsS0FBSyxpQkFBaUIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsYUFBYSxNQUFNLEVBQUUsQ0FBQyxDQUFDO29CQUN0SCwwQkFBMEI7b0JBQzFCLE1BQU0sWUFBWSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUNuRyxJQUFJLFlBQVksSUFBSSxZQUFZLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxJQUFJLEVBQUU7d0JBQ25ELElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsS0FBSyxHQUFHLFNBQVMsV0FBVyxHQUFHLEtBQUssa0RBQWtELFlBQVksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO3dCQUNySSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQywrRUFBK0UsRUFDOUYsWUFBWSxDQUFDLFFBQVEsRUFBRSxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7d0JBQ2pELFNBQVM7cUJBQ1Y7b0JBQ0QsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO29CQUM1RCxJQUFJLEdBQUcsRUFBRSxDQUFDO29CQUNWLElBQUksU0FBUyxHQUFHLEVBQUUsQ0FBQztvQkFDbkIsSUFBSTt3QkFDRixNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FDaEMsTUFBTSxJQUFBLDZCQUFrQixFQUN0QixJQUFJLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxTQUFVLEVBQUUsRUFBRSxzQkFBc0IsRUFBRSxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQyxDQUFDO3dCQUNwSCxNQUFNLEdBQUcsR0FBRyxJQUFJLE1BQU0sRUFBRSxLQUFLLEdBQUcsU0FBUyxXQUFXLEdBQUcsS0FBSywwQkFBMEIsT0FBTyxDQUFDLGdCQUFnQixDQUFDLGFBQWEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxJQUFJLENBQUMsU0FBUyxPQUFPLE9BQU8sRUFBRSxDQUFDO3dCQUN0TCxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO3dCQUNmLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLHFEQUFxRCxFQUFFLEdBQUcsQ0FBQyxDQUFDO3dCQUM3RSxTQUFTLEdBQUcsT0FBTyxDQUFDO3dCQUNwQixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO3dCQUN4RCxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxFQUFFLEtBQUssR0FBRyxTQUFTLFdBQVcsR0FBRyxLQUFLLG9DQUFvQyxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQzt3QkFDakgsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO3dCQUM1RCxJQUFJLEdBQUcsRUFBRSxDQUFDO3FCQUNYO29CQUFDLE9BQU8sR0FBUSxFQUFFO3dCQUNqQixJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssdUJBQXVCLEVBQUU7NEJBQ3hDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLHVCQUF1QixFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQzs0QkFDMUQsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sRUFBRSxLQUFLLEdBQUcsVUFBVSxXQUFXLEdBQUcsS0FBSyxjQUFjLElBQUksQ0FBQyxTQUFTLHFCQUFxQixDQUFDLENBQUM7eUJBQy9HOzZCQUFNOzRCQUNMLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyw0QkFBNEIsRUFBRTtnQ0FDN0MsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxHQUFHLENBQUMsQ0FBQzs2QkFDaEU7aUNBQU07Z0NBQ0wsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxHQUFHLENBQUMsQ0FBQzs2QkFDakU7NEJBQ0QsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDOzRCQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxFQUFFLEtBQUssR0FBRyxRQUFRLFdBQVcsR0FBRyxLQUFLLGNBQWMsSUFBSSxDQUFDLFNBQVMsV0FBVyxHQUFHLEVBQUUsQ0FBQyxDQUFDO3lCQUN4Rzt3QkFDRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7d0JBQzVELElBQUksR0FBRyxFQUFFLENBQUM7cUJBQ1g7NEJBQVM7d0JBQ1IsSUFBSSxTQUFTLEVBQUU7NEJBQ2IsTUFBTSxJQUFBLGFBQUUsRUFBQyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQzt5QkFDdEM7cUJBQ0Y7aUJBQ0Y7YUFDRjtZQUNELElBQUksZ0JBQWdCLEVBQUU7Z0JBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxNQUFNLEVBQUUsS0FBSyxHQUFHLHFCQUFxQixDQUFDLENBQUM7YUFDdEQ7aUJBQU07Z0JBQ0wsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sRUFBRSxLQUFLLEdBQUcsc0NBQXNDLFFBQVEsRUFBRSxDQUFDLENBQUM7YUFDakY7WUFDRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7U0FDN0Q7UUFDRCxPQUFPLENBQUUsZ0JBQWdCLEVBQUUsUUFBUSxDQUFFLENBQUM7SUFDeEMsQ0FBQztJQUdELGtEQUFrRDtJQUNsRCxxQ0FBcUM7SUFDckMsaURBQWlEO0lBQ2pELDhDQUE4QztJQUN0QyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQXNCLEVBQUUsR0FBVyxFQUFFLFVBQXdCLEVBQUUsbUJBQW1CLEdBQUcsR0FBRztRQUN6RyxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlFLE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBQzVDLEtBQUssTUFBTSxJQUFJLElBQUksV0FBVyxFQUFFO1lBQzlCLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztTQUNoQztRQUNELE1BQU0sU0FBUyxHQUF1QyxFQUFFLENBQUM7UUFDekQsSUFBSSxVQUFrQyxDQUFDO1FBQ3ZDLEtBQUssTUFBTSxJQUFJLElBQUksVUFBVSxFQUFFO1lBQzdCLE1BQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzVDLElBQUksQ0FBQyxVQUFVLEVBQUU7Z0JBQ2YsU0FBUyxDQUFDLElBQUksQ0FBQztvQkFDYixJQUFJLEVBQUUsZUFBTSxDQUFDLE1BQU0sQ0FBQzt3QkFDbEIsUUFBUSxFQUFFLFVBQVU7d0JBQ3BCLE1BQU0sRUFBRSxHQUFHO3dCQUNYLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTt3QkFDZixLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUs7d0JBQ2pCLElBQUksRUFBRSxDQUFDO3dCQUNQLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTt3QkFDZixTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUc7d0JBQ25CLHNCQUFzQixFQUFFLElBQUksQ0FBQyxzQkFBc0I7cUJBQ3BELENBQUM7b0JBQ0YsTUFBTSxFQUFFLFVBQVU7aUJBQ25CLENBQUMsQ0FBQzthQUNKO2lCQUFNLElBQUksVUFBVSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsSUFBSSxFQUFFO2dCQUN4QyxTQUFTLENBQUMsSUFBSSxDQUFDO29CQUNiLElBQUksRUFBRSxVQUFVO29CQUNoQixNQUFNLEVBQUUscUJBQXFCLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO2lCQUNyRyxDQUFDLENBQUM7Z0JBQ0gsVUFBVSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO2dCQUNoQyxVQUFVLENBQUMsc0JBQXNCLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDO2dCQUNoRSxVQUFVLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7YUFDN0I7aUJBQU0sSUFBSSxHQUFHLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFDLEVBQUU7Z0JBQzVDLElBQUksQ0FBQyxVQUFVLEVBQUU7b0JBQ2YsVUFBVSxHQUFHLElBQUEsZUFBTSxFQUFDLFVBQVUsRUFBRSxDQUFFLE1BQU0sQ0FBRSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7aUJBQ25EO2dCQUNELE1BQU0sWUFBWSxHQUFHLFVBQVUsRUFBRSxJQUFJLEtBQUssSUFBSSxDQUFDLElBQUksQ0FBQztnQkFDcEQsSUFBSSxZQUFZLElBQUksVUFBVSxDQUFDLEtBQUssRUFBRTtvQkFDcEMsU0FBUyxDQUFDLElBQUksQ0FBQzt3QkFDYixJQUFJLEVBQUUsVUFBVTt3QkFDaEIsTUFBTSxFQUFFLG1EQUFtRCxtQkFBbUIsb0JBQW9CLEdBQUcscUJBQXFCLFVBQVUsQ0FBQyxJQUFJLEVBQUU7cUJBQzVJLENBQUMsQ0FBQztvQkFDSCxtQkFBbUIsR0FBRyxHQUFHLG1CQUFtQixHQUFHLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztpQkFDbEU7YUFDRjtTQUNGO1FBR0QsT0FBTztZQUNMLFFBQVEsRUFBRSxTQUFTO1lBQ25CLGdCQUFnQixFQUFFLG1CQUFtQjtTQUN0QyxDQUFDO0lBQ0osQ0FBQztJQUVPLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBYyxFQUFFLE9BQWdCO1FBQzNELElBQUksT0FBTyxFQUFFO1lBQ1gsTUFBTSxJQUFJLEdBQUcsTUFBTSxrQkFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNwQyxNQUFNLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7WUFDeEIsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQzVELElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGtGQUFrRixFQUNqRyxNQUFNLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztTQUMxRDtRQUNELE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMvQyxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sS0FBSyxDQUFDLGdCQUFnQixDQUFDLFVBQXNCO1FBQ25ELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDO1FBQ3BDLE1BQU0sWUFBWSxHQUFHLGtCQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFMUMsSUFBSSxhQUE2QixDQUFDO1FBQ2xDLElBQUksTUFBTSxDQUFDLG9CQUFvQixFQUFFO1lBQy9CLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsK0JBQWMsRUFBRSxtQkFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQzFGO2FBQU07WUFDTCxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxDQUFDLCtCQUFjLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQzdGO1FBQ0QsTUFBTSxhQUFhLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzFDLE9BQU8sYUFBYSxDQUFDO0lBQ3ZCLENBQUM7Q0FDRixDQUFBO0FBdFNZLGtEQUFtQjtBQUViO0lBRGhCLElBQUEsYUFBTSxHQUFFOzhCQUMwQixtQ0FBZ0I7NkRBQUM7QUFFbkM7SUFEaEIsSUFBQSxhQUFNLEdBQUU7OEJBQ3FCLHlCQUFXO3dEQUFDO0FBRXpCO0lBRGhCLElBQUEsYUFBTSxHQUFFOzt1REFDa0M7QUFFMUI7SUFEaEIsSUFBQSxhQUFNLEdBQUU7OEJBQ29CLHVCQUFVO3VEQUFDO0FBRXZCO0lBRGhCLElBQUEsYUFBTSxHQUFFOzs2REFDMkM7OEJBVnpDLG1CQUFtQjtJQUgvQixJQUFBLHFCQUFjLEVBQUM7UUFDZCxXQUFXLEVBQUUsa0JBQVcsQ0FBQyxNQUFNO0tBQ2hDLENBQUM7R0FDVyxtQkFBbUIsQ0FzUy9CIn0=