vibetunnel
Version:
Terminal sharing server with web interface - supports macOS, Linux, and headless environments
551 lines • 80.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamWatcher = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs"));
const logger_js_1 = require("../utils/logger.js");
const pruning_detector_js_1 = require("../utils/pruning-detector.js");
const git_watcher_js_1 = require("./git-watcher.js");
const logger = (0, logger_js_1.createLogger)('stream-watcher');
// Constants
const HEADER_READ_BUFFER_SIZE = 4096;
// Type guard functions
function isOutputEvent(event) {
return (Array.isArray(event) && event.length === 3 && event[1] === 'o' && typeof event[0] === 'number');
}
function isResizeEvent(event) {
return (Array.isArray(event) && event.length === 3 && event[1] === 'r' && typeof event[0] === 'number');
}
function isExitEvent(event) {
return Array.isArray(event) && event[0] === 'exit';
}
class StreamWatcher {
constructor(sessionManager) {
this.activeWatchers = new Map();
this.sessionManager = sessionManager;
// Clean up notification listeners on exit
process.on('beforeExit', () => {
this.cleanup();
});
logger.debug('stream watcher initialized');
}
/**
* Process a clear sequence event and update tracking variables
*/
processClearSequence(event, eventIndex, fileOffset, currentResize, eventLine) {
const prunePoint = (0, pruning_detector_js_1.findLastPrunePoint)(event[2]);
if (!prunePoint)
return null;
// Calculate precise offset using shared utility
const lastClearOffset = (0, pruning_detector_js_1.calculatePruningPositionInFile)(fileOffset, eventLine, prunePoint.position);
// Use shared logging function
(0, pruning_detector_js_1.logPruningDetection)(prunePoint.sequence, lastClearOffset, '(retroactive scan)');
logger.debug(`found at event index ${eventIndex}, ` +
`current resize: ${currentResize ? currentResize[2] : 'none'}`);
return {
lastClearIndex: eventIndex,
lastClearOffset,
lastResizeBeforeClear: currentResize,
};
}
/**
* Parse a line of asciinema data and return the parsed event
*/
parseAsciinemaLine(line) {
if (!line.trim())
return null;
try {
const parsed = JSON.parse(line);
// Check if it's a header
if (parsed.version && parsed.width && parsed.height) {
return parsed;
}
// Check if it's an event
if (Array.isArray(parsed)) {
if (parsed[0] === 'exit') {
return parsed;
}
else if (parsed.length >= 3 && typeof parsed[0] === 'number') {
return parsed;
}
}
return null;
}
catch (e) {
logger.debug(`skipping invalid JSON line: ${e}`);
return null;
}
}
/**
* Send an event to the client with proper formatting
*/
sendEventToClient(client, event, makeInstant = false) {
try {
let dataToSend = event;
// For existing content, set timestamp to 0
if (makeInstant &&
Array.isArray(event) &&
event.length >= 3 &&
typeof event[0] === 'number') {
dataToSend = [0, event[1], event[2]];
}
client.response.write(`data: ${JSON.stringify(dataToSend)}\n\n`);
// Handle exit events
if (Array.isArray(event) && isExitEvent(event)) {
logger.log(chalk_1.default.yellow(`session ${client.response.locals?.sessionId || 'unknown'} already ended, closing stream`));
client.response.end();
}
}
catch (error) {
logger.debug(`client write failed (likely disconnected): ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Add a client to watch a stream file
*/
addClient(sessionId, streamPath, response) {
logger.debug(`adding client to session ${sessionId}`);
const startTime = Date.now() / 1000;
const client = { response, startTime };
let watcherInfo = this.activeWatchers.get(sessionId);
if (!watcherInfo) {
// Create new watcher for this session
logger.log(chalk_1.default.green(`creating new stream watcher for session ${sessionId}`));
watcherInfo = {
clients: new Set(),
lastOffset: 0,
lastSize: 0,
lastMtime: 0,
lineBuffer: '',
};
this.activeWatchers.set(sessionId, watcherInfo);
// Send existing content first
this.sendExistingContent(sessionId, streamPath, client);
// Get current file size and stats
if (fs.existsSync(streamPath)) {
const stats = fs.statSync(streamPath);
watcherInfo.lastOffset = stats.size;
watcherInfo.lastSize = stats.size;
watcherInfo.lastMtime = stats.mtimeMs;
logger.debug(`initial file size: ${stats.size} bytes`);
}
else {
logger.debug(`stream file does not exist yet: ${streamPath}`);
}
// Start watching for new content
this.startWatching(sessionId, streamPath, watcherInfo);
// Start git watching if this is a git repository
this.startGitWatching(sessionId, response);
}
else {
// Send existing content to new client
this.sendExistingContent(sessionId, streamPath, client);
// Add this client to git watcher
git_watcher_js_1.gitWatcher.addClient(sessionId, response);
}
// Add client to set
watcherInfo.clients.add(client);
logger.log(chalk_1.default.blue(`client connected to stream ${sessionId} (${watcherInfo.clients.size} total)`));
}
/**
* Remove a client
*/
removeClient(sessionId, response) {
const watcherInfo = this.activeWatchers.get(sessionId);
if (!watcherInfo) {
logger.debug(`no watcher found for session ${sessionId}`);
return;
}
// Find and remove client
let clientToRemove;
for (const client of watcherInfo.clients) {
if (client.response === response) {
clientToRemove = client;
break;
}
}
if (clientToRemove) {
watcherInfo.clients.delete(clientToRemove);
logger.log(chalk_1.default.yellow(`client disconnected from stream ${sessionId} (${watcherInfo.clients.size} remaining)`));
// Remove client from git watcher
git_watcher_js_1.gitWatcher.removeClient(sessionId, response);
// If no more clients, stop watching
if (watcherInfo.clients.size === 0) {
logger.log(chalk_1.default.yellow(`stopping watcher for session ${sessionId} (no clients)`));
if (watcherInfo.watcher) {
watcherInfo.watcher.close();
}
this.activeWatchers.delete(sessionId);
// Stop git watching when no clients remain
git_watcher_js_1.gitWatcher.stopWatching(sessionId);
}
}
}
/**
* Send existing content to a client
*/
sendExistingContent(sessionId, streamPath, client) {
try {
// Load existing session info or use defaults, but don't save incomplete session data
const sessionInfo = this.sessionManager.loadSessionInfo(sessionId);
// Validate offset to ensure we don't read beyond file size
let startOffset = sessionInfo?.lastClearOffset ?? 0;
if (fs.existsSync(streamPath)) {
const stats = fs.statSync(streamPath);
startOffset = Math.min(startOffset, stats.size);
}
// Read header line separately (first line of file)
// We need to track byte position separately from string length due to UTF-8 encoding
let header = null;
let fd = null;
try {
fd = fs.openSync(streamPath, 'r');
const buf = Buffer.alloc(HEADER_READ_BUFFER_SIZE);
let data = '';
// Important: Use filePosition (bytes) not data.length (characters) for fs.readSync
// UTF-8 strings have character count != byte count for multi-byte characters
let filePosition = 0; // Track actual byte position in file
let bytesRead = fs.readSync(fd, buf, 0, buf.length, filePosition);
while (!data.includes('\n') && bytesRead > 0) {
data += buf.toString('utf8', 0, bytesRead);
// Increment by actual bytes read, not string characters
// This ensures correct file positioning for subsequent reads
filePosition += bytesRead;
if (!data.includes('\n')) {
// Use filePosition (byte offset) not data.length (character count)
bytesRead = fs.readSync(fd, buf, 0, buf.length, filePosition);
}
}
const idx = data.indexOf('\n');
if (idx !== -1) {
header = JSON.parse(data.slice(0, idx));
}
}
catch (e) {
logger.debug(`failed to read asciinema header for session ${sessionId}: ${e}`);
}
finally {
// Ensure file descriptor is always closed to prevent leaks
// This executes even if an exception occurs during read operations
if (fd !== null) {
try {
fs.closeSync(fd);
}
catch (closeError) {
logger.debug(`failed to close file descriptor: ${closeError}`);
}
}
}
// Analyze the stream starting from stored offset to find the most recent clear sequence
// This allows us to prune old terminal content and only send what's currently visible
const analysisStream = fs.createReadStream(streamPath, {
encoding: 'utf8',
start: startOffset,
});
let lineBuffer = '';
const events = [];
let lastClearIndex = -1;
let lastResizeBeforeClear = null;
let currentResize = null;
// Track byte offset in the file for accurate position tracking
// This is crucial for UTF-8 encoded files where character count != byte count
let fileOffset = startOffset;
let lastClearOffset = startOffset;
analysisStream.on('data', (chunk) => {
lineBuffer += chunk.toString();
let index = lineBuffer.indexOf('\n');
while (index !== -1) {
const line = lineBuffer.slice(0, index);
lineBuffer = lineBuffer.slice(index + 1);
// Calculate byte length of the line plus newline character
// Buffer.byteLength correctly handles multi-byte UTF-8 characters
fileOffset += Buffer.byteLength(line, 'utf8') + 1;
if (line.trim()) {
try {
const parsed = JSON.parse(line);
if (parsed.version && parsed.width && parsed.height) {
header = parsed;
}
else if (Array.isArray(parsed)) {
// Check if it's an exit event first
if (parsed[0] === 'exit') {
events.push(parsed);
}
else if (parsed.length >= 3 && typeof parsed[0] === 'number') {
const event = parsed;
// Track resize events
if (isResizeEvent(event)) {
currentResize = event;
}
// Check for clear sequence in output events
if (isOutputEvent(event) && (0, pruning_detector_js_1.containsPruningSequence)(event[2])) {
const clearResult = this.processClearSequence(event, events.length, fileOffset, currentResize, line);
if (clearResult) {
lastClearIndex = clearResult.lastClearIndex;
lastClearOffset = clearResult.lastClearOffset;
lastResizeBeforeClear = clearResult.lastResizeBeforeClear;
}
}
events.push(event);
}
}
}
catch (e) {
logger.debug(`skipping invalid JSON line during analysis: ${e}`);
}
}
index = lineBuffer.indexOf('\n');
}
});
analysisStream.on('end', () => {
// Process any remaining line in analysis
if (lineBuffer.trim()) {
try {
const parsed = JSON.parse(lineBuffer);
fileOffset += Buffer.byteLength(lineBuffer, 'utf8');
if (Array.isArray(parsed)) {
if (parsed[0] === 'exit') {
events.push(parsed);
}
else if (parsed.length >= 3 && typeof parsed[0] === 'number') {
const event = parsed;
if (isResizeEvent(event)) {
currentResize = event;
}
if (isOutputEvent(event) && (0, pruning_detector_js_1.containsPruningSequence)(event[2])) {
const clearResult = this.processClearSequence(event, events.length, fileOffset, currentResize, lineBuffer);
if (clearResult) {
lastClearIndex = clearResult.lastClearIndex;
lastClearOffset = clearResult.lastClearOffset;
lastResizeBeforeClear = clearResult.lastResizeBeforeClear;
}
}
events.push(event);
}
}
}
catch (e) {
logger.debug(`skipping invalid JSON in line buffer during analysis: ${e}`);
}
}
// Now replay the stream with pruning
let startIndex = 0;
if (lastClearIndex >= 0) {
// Start from after the last clear
startIndex = lastClearIndex + 1;
logger.log(chalk_1.default.green(`pruning stream: skipping ${lastClearIndex + 1} events before last clear at offset ${lastClearOffset}`));
// Persist new clear offset to session only if session already exists
if (sessionInfo) {
sessionInfo.lastClearOffset = lastClearOffset;
this.sessionManager.saveSessionInfo(sessionId, sessionInfo);
}
}
// Send header first - update dimensions if we have a resize
if (header) {
const headerToSend = { ...header };
if (lastClearIndex >= 0 && lastResizeBeforeClear) {
// Update header with last known dimensions before clear
const dimensions = lastResizeBeforeClear[2].split('x');
headerToSend.width = Number.parseInt(dimensions[0], 10);
headerToSend.height = Number.parseInt(dimensions[1], 10);
}
client.response.write(`data: ${JSON.stringify(headerToSend)}\n\n`);
}
// Send remaining events
let exitEventFound = false;
for (let i = startIndex; i < events.length; i++) {
const event = events[i];
if (isExitEvent(event)) {
exitEventFound = true;
client.response.write(`data: ${JSON.stringify(event)}\n\n`);
}
else if (isOutputEvent(event) || isResizeEvent(event)) {
// Set timestamp to 0 for existing content
const instantEvent = [0, event[1], event[2]];
client.response.write(`data: ${JSON.stringify(instantEvent)}\n\n`);
}
}
// If exit event found, close connection
if (exitEventFound) {
logger.log(chalk_1.default.yellow(`session ${client.response.locals?.sessionId || 'unknown'} already ended, closing stream`));
client.response.end();
}
});
analysisStream.on('error', (error) => {
logger.error('failed to analyze stream for pruning:', error);
// If stream fails, client will simply not receive existing content
// This is extremely rare and would indicate a serious filesystem issue
});
}
catch (error) {
logger.error('failed to create read stream:', error);
}
}
/**
* Start watching a file for changes
*/
startWatching(sessionId, streamPath, watcherInfo) {
logger.log(chalk_1.default.green(`started watching stream file for session ${sessionId}`));
// Use standard fs.watch with stat checking
watcherInfo.watcher = fs.watch(streamPath, { persistent: true }, (eventType) => {
if (eventType === 'change') {
try {
// Check if file actually changed by comparing stats
const stats = fs.statSync(streamPath);
// Only process if size increased (append-only file)
if (stats.size > watcherInfo.lastSize || stats.mtimeMs > watcherInfo.lastMtime) {
const sizeDiff = stats.size - watcherInfo.lastSize;
if (sizeDiff > 0) {
logger.debug(`file grew by ${sizeDiff} bytes`);
}
watcherInfo.lastSize = stats.size;
watcherInfo.lastMtime = stats.mtimeMs;
// Read only new data
if (stats.size > watcherInfo.lastOffset) {
const fd = fs.openSync(streamPath, 'r');
const buffer = Buffer.alloc(stats.size - watcherInfo.lastOffset);
fs.readSync(fd, buffer, 0, buffer.length, watcherInfo.lastOffset);
fs.closeSync(fd);
// Update offset
watcherInfo.lastOffset = stats.size;
// Process new data
const newData = buffer.toString('utf8');
watcherInfo.lineBuffer += newData;
// Process complete lines
const lines = watcherInfo.lineBuffer.split('\n');
watcherInfo.lineBuffer = lines.pop() || '';
for (const line of lines) {
if (line.trim()) {
this.broadcastLine(sessionId, line, watcherInfo);
}
}
}
}
}
catch (error) {
logger.error('failed to read file changes:', error);
}
}
});
watcherInfo.watcher.on('error', (error) => {
logger.error(`file watcher error for session ${sessionId}:`, error);
});
}
/**
* Broadcast a line to all clients
*/
broadcastLine(sessionId, line, watcherInfo) {
const parsed = this.parseAsciinemaLine(line);
if (!parsed) {
// Handle non-JSON as raw output
logger.debug(`broadcasting raw output line: ${line.substring(0, 50)}...`);
const currentTime = Date.now() / 1000;
for (const client of watcherInfo.clients) {
const castEvent = [currentTime - client.startTime, 'o', line];
this.sendEventToClient(client, castEvent);
}
return;
}
// Skip duplicate headers
if (!Array.isArray(parsed)) {
return;
}
// Handle exit events
if (isExitEvent(parsed)) {
logger.log(chalk_1.default.yellow(`session ${sessionId} ended with exit code ${parsed[1]}`));
for (const client of watcherInfo.clients) {
this.sendEventToClient(client, parsed);
}
return;
}
// Log resize broadcasts at debug level only
if (isResizeEvent(parsed)) {
logger.debug(`Broadcasting resize ${parsed[2]} to ${watcherInfo.clients.size} clients`);
}
// Calculate relative timestamp for each client
const currentTime = Date.now() / 1000;
for (const client of watcherInfo.clients) {
const relativeEvent = [currentTime - client.startTime, parsed[1], parsed[2]];
try {
client.response.write(`data: ${JSON.stringify(relativeEvent)}\n\n`);
if (client.response.flush)
client.response.flush();
}
catch (error) {
logger.debug(`client write failed (likely disconnected): ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Start git watching for a session if it's in a git repository
*/
async startGitWatching(sessionId, response) {
try {
const sessionInfo = this.sessionManager.loadSessionInfo(sessionId);
if (sessionInfo?.gitRepoPath && sessionInfo.workingDir) {
logger.debug(`Starting git watcher for session ${sessionId} at ${sessionInfo.gitRepoPath}`);
await git_watcher_js_1.gitWatcher.startWatching(sessionId, sessionInfo.workingDir, sessionInfo.gitRepoPath);
git_watcher_js_1.gitWatcher.addClient(sessionId, response);
}
}
catch (error) {
logger.error(`Failed to start git watching for session ${sessionId}:`, error);
}
}
/**
* Clean up all watchers and listeners
*/
cleanup() {
const watcherCount = this.activeWatchers.size;
if (watcherCount > 0) {
logger.log(chalk_1.default.yellow(`cleaning up ${watcherCount} active watchers`));
for (const [sessionId, watcherInfo] of this.activeWatchers) {
if (watcherInfo.watcher) {
watcherInfo.watcher.close();
}
logger.debug(`closed watcher for session ${sessionId}`);
}
this.activeWatchers.clear();
}
// Clean up git watchers
git_watcher_js_1.gitWatcher.cleanup();
}
}
exports.StreamWatcher = StreamWatcher;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RyZWFtLXdhdGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmVyL3NlcnZpY2VzL3N0cmVhbS13YXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGtEQUEwQjtBQUUxQix1Q0FBeUI7QUFHekIsa0RBQWtEO0FBQ2xELHNFQUtzQztBQUN0QyxxREFBOEM7QUFFOUMsTUFBTSxNQUFNLEdBQUcsSUFBQSx3QkFBWSxFQUFDLGdCQUFnQixDQUFDLENBQUM7QUFFOUMsWUFBWTtBQUNaLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxDQUFDO0FBa0JyQyx1QkFBdUI7QUFDdkIsU0FBUyxhQUFhLENBQUMsS0FBcUI7SUFDMUMsT0FBTyxDQUNMLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLENBQy9GLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsS0FBcUI7SUFDMUMsT0FBTyxDQUNMLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLENBQy9GLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsS0FBcUI7SUFDeEMsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxNQUFNLENBQUM7QUFDckQsQ0FBQztBQVdELE1BQWEsYUFBYTtJQUl4QixZQUFZLGNBQThCO1FBSGxDLG1CQUFjLEdBQTZCLElBQUksR0FBRyxFQUFFLENBQUM7UUFJM0QsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLENBQUM7UUFDckMsMENBQTBDO1FBQzFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsWUFBWSxFQUFFLEdBQUcsRUFBRTtZQUM1QixJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDakIsQ0FBQyxDQUFDLENBQUM7UUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssb0JBQW9CLENBQzFCLEtBQTJCLEVBQzNCLFVBQWtCLEVBQ2xCLFVBQWtCLEVBQ2xCLGFBQTBDLEVBQzFDLFNBQWlCO1FBTWpCLE1BQU0sVUFBVSxHQUFHLElBQUEsd0NBQWtCLEVBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDaEQsSUFBSSxDQUFDLFVBQVU7WUFBRSxPQUFPLElBQUksQ0FBQztRQUU3QixnREFBZ0Q7UUFDaEQsTUFBTSxlQUFlLEdBQUcsSUFBQSxvREFBOEIsRUFDcEQsVUFBVSxFQUNWLFNBQVMsRUFDVCxVQUFVLENBQUMsUUFBUSxDQUNwQixDQUFDO1FBRUYsOEJBQThCO1FBQzlCLElBQUEseUNBQW1CLEVBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxlQUFlLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztRQUVoRixNQUFNLENBQUMsS0FBSyxDQUNWLHdCQUF3QixVQUFVLElBQUk7WUFDcEMsbUJBQW1CLGFBQWEsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FDakUsQ0FBQztRQUVGLE9BQU87WUFDTCxjQUFjLEVBQUUsVUFBVTtZQUMxQixlQUFlO1lBQ2YscUJBQXFCLEVBQUUsYUFBYTtTQUNyQyxDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ssa0JBQWtCLENBQUMsSUFBWTtRQUNyQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRTtZQUFFLE9BQU8sSUFBSSxDQUFDO1FBRTlCLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFaEMseUJBQXlCO1lBQ3pCLElBQUksTUFBTSxDQUFDLE9BQU8sSUFBSSxNQUFNLENBQUMsS0FBSyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDcEQsT0FBTyxNQUF5QixDQUFDO1lBQ25DLENBQUM7WUFFRCx5QkFBeUI7WUFDekIsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7Z0JBQzFCLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLE1BQU0sRUFBRSxDQUFDO29CQUN6QixPQUFPLE1BQTRCLENBQUM7Z0JBQ3RDLENBQUM7cUJBQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxPQUFPLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLEVBQUUsQ0FBQztvQkFDL0QsT0FBTyxNQUF3QixDQUFDO2dCQUNsQyxDQUFDO1lBQ0gsQ0FBQztZQUVELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCxNQUFNLENBQUMsS0FBSyxDQUFDLCtCQUErQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2pELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLGlCQUFpQixDQUN2QixNQUFvQixFQUNwQixLQUF1QyxFQUN2QyxjQUF1QixLQUFLO1FBRTVCLElBQUksQ0FBQztZQUNILElBQUksVUFBVSxHQUFxQyxLQUFLLENBQUM7WUFFekQsMkNBQTJDO1lBQzNDLElBQ0UsV0FBVztnQkFDWCxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQztnQkFDcEIsS0FBSyxDQUFDLE1BQU0sSUFBSSxDQUFDO2dCQUNqQixPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLEVBQzVCLENBQUM7Z0JBQ0QsVUFBVSxHQUFHLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN2QyxDQUFDO1lBRUQsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUVqRSxxQkFBcUI7WUFDckIsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLFdBQVcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMvQyxNQUFNLENBQUMsR0FBRyxDQUNSLGVBQUssQ0FBQyxNQUFNLENBQ1YsV0FBVyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxTQUFTLElBQUksU0FBUyxnQ0FBZ0MsQ0FDMUYsQ0FDRixDQUFDO2dCQUNGLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDeEIsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FDViw4Q0FBOEMsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQ3ZHLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLFNBQWlCLEVBQUUsVUFBa0IsRUFBRSxRQUFrQjtRQUNqRSxNQUFNLENBQUMsS0FBSyxDQUFDLDRCQUE0QixTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFDcEMsTUFBTSxNQUFNLEdBQWlCLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBRXJELElBQUksV0FBVyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRXJELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNqQixzQ0FBc0M7WUFDdEMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFLLENBQUMsS0FBSyxDQUFDLDJDQUEyQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDaEYsV0FBVyxHQUFHO2dCQUNaLE9BQU8sRUFBRSxJQUFJLEdBQUcsRUFBRTtnQkFDbEIsVUFBVSxFQUFFLENBQUM7Z0JBQ2IsUUFBUSxFQUFFLENBQUM7Z0JBQ1gsU0FBUyxFQUFFLENBQUM7Z0JBQ1osVUFBVSxFQUFFLEVBQUU7YUFDZixDQUFDO1lBQ0YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBRWhELDhCQUE4QjtZQUM5QixJQUFJLENBQUMsbUJBQW1CLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUV4RCxrQ0FBa0M7WUFDbEMsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7Z0JBQzlCLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7Z0JBQ3RDLFdBQVcsQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQztnQkFDcEMsV0FBVyxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDO2dCQUNsQyxXQUFXLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUM7Z0JBQ3RDLE1BQU0sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEtBQUssQ0FBQyxJQUFJLFFBQVEsQ0FBQyxDQUFDO1lBQ3pELENBQUM7aUJBQU0sQ0FBQztnQkFDTixNQUFNLENBQUMsS0FBSyxDQUFDLG1DQUFtQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO1lBQ2hFLENBQUM7WUFFRCxpQ0FBaUM7WUFDakMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBRXZELGlEQUFpRDtZQUNqRCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzdDLENBQUM7YUFBTSxDQUFDO1lBQ04sc0NBQXNDO1lBQ3RDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLEVBQUUsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBRXhELGlDQUFpQztZQUNqQywyQkFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUVELG9CQUFvQjtRQUNwQixXQUFXLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoQyxNQUFNLENBQUMsR0FBRyxDQUNSLGVBQUssQ0FBQyxJQUFJLENBQUMsOEJBQThCLFNBQVMsS0FBSyxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksU0FBUyxDQUFDLENBQzFGLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSCxZQUFZLENBQUMsU0FBaUIsRUFBRSxRQUFrQjtRQUNoRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDakIsTUFBTSxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsU0FBUyxFQUFFLENBQUMsQ0FBQztZQUMxRCxPQUFPO1FBQ1QsQ0FBQztRQUVELHlCQUF5QjtRQUN6QixJQUFJLGNBQXdDLENBQUM7UUFDN0MsS0FBSyxNQUFNLE1BQU0sSUFBSSxXQUFXLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDekMsSUFBSSxNQUFNLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUNqQyxjQUFjLEdBQUcsTUFBTSxDQUFDO2dCQUN4QixNQUFNO1lBQ1IsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ25CLFdBQVcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQzNDLE1BQU0sQ0FBQyxHQUFHLENBQ1IsZUFBSyxDQUFDLE1BQU0sQ0FDVixtQ0FBbUMsU0FBUyxLQUFLLFdBQVcsQ0FBQyxPQUFPLENBQUMsSUFBSSxhQUFhLENBQ3ZGLENBQ0YsQ0FBQztZQUVGLGlDQUFpQztZQUNqQywyQkFBVSxDQUFDLFlBQVksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFFN0Msb0NBQW9DO1lBQ3BDLElBQUksV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ25DLE1BQU0sQ0FBQyxHQUFHLENBQUMsZUFBSyxDQUFDLE1BQU0sQ0FBQyxnQ0FBZ0MsU0FBUyxlQUFlLENBQUMsQ0FBQyxDQUFDO2dCQUNuRixJQUFJLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFDeEIsV0FBVyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDOUIsQ0FBQztnQkFDRCxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFFdEMsMkNBQTJDO2dCQUMzQywyQkFBVSxDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNyQyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLG1CQUFtQixDQUFDLFNBQWlCLEVBQUUsVUFBa0IsRUFBRSxNQUFvQjtRQUNyRixJQUFJLENBQUM7WUFDSCxxRkFBcUY7WUFDckYsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFbkUsMkRBQTJEO1lBQzNELElBQUksV0FBVyxHQUFHLFdBQVcsRUFBRSxlQUFlLElBQUksQ0FBQyxDQUFDO1lBQ3BELElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO2dCQUM5QixNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUN0QyxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2xELENBQUM7WUFFRCxtREFBbUQ7WUFDbkQscUZBQXFGO1lBQ3JGLElBQUksTUFBTSxHQUEyQixJQUFJLENBQUM7WUFDMUMsSUFBSSxFQUFFLEdBQWtCLElBQUksQ0FBQztZQUM3QixJQUFJLENBQUM7Z0JBQ0gsRUFBRSxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUNsQyxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLHVCQUF1QixDQUFDLENBQUM7Z0JBQ2xELElBQUksSUFBSSxHQUFHLEVBQUUsQ0FBQztnQkFFZCxtRkFBbUY7Z0JBQ25GLDZFQUE2RTtnQkFDN0UsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDLENBQUMscUNBQXFDO2dCQUMzRCxJQUFJLFNBQVMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUM7Z0JBRWxFLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLFNBQVMsR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDN0MsSUFBSSxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQztvQkFFM0Msd0RBQXdEO29CQUN4RCw2REFBNkQ7b0JBQzdELFlBQVksSUFBSSxTQUFTLENBQUM7b0JBRTFCLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7d0JBQ3pCLG1FQUFtRTt3QkFDbkUsU0FBUyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxZQUFZLENBQUMsQ0FBQztvQkFDaEUsQ0FBQztnQkFDSCxDQUFDO2dCQUVELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQy9CLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7b0JBQ2YsTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDMUMsQ0FBQztZQUNILENBQUM7WUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNYLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0NBQStDLFNBQVMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2pGLENBQUM7b0JBQVMsQ0FBQztnQkFDVCwyREFBMkQ7Z0JBQzNELG1FQUFtRTtnQkFDbkUsSUFBSSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUM7b0JBQ2hCLElBQUksQ0FBQzt3QkFDSCxFQUFFLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDO29CQUNuQixDQUFDO29CQUFDLE9BQU8sVUFBVSxFQUFFLENBQUM7d0JBQ3BCLE1BQU0sQ0FBQyxLQUFLLENBQUMsb0NBQW9DLFVBQVUsRUFBRSxDQUFDLENBQUM7b0JBQ2pFLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7WUFFRCx3RkFBd0Y7WUFDeEYsc0ZBQXNGO1lBQ3RGLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUU7Z0JBQ3JELFFBQVEsRUFBRSxNQUFNO2dCQUNoQixLQUFLLEVBQUUsV0FBVzthQUNuQixDQUFDLENBQUM7WUFDSCxJQUFJLFVBQVUsR0FBRyxFQUFFLENBQUM7WUFDcEIsTUFBTSxNQUFNLEdBQXFCLEVBQUUsQ0FBQztZQUNwQyxJQUFJLGNBQWMsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN4QixJQUFJLHFCQUFxQixHQUFnQyxJQUFJLENBQUM7WUFDOUQsSUFBSSxhQUFhLEdBQWdDLElBQUksQ0FBQztZQUV0RCwrREFBK0Q7WUFDL0QsOEVBQThFO1lBQzlFLElBQUksVUFBVSxHQUFHLFdBQVcsQ0FBQztZQUM3QixJQUFJLGVBQWUsR0FBRyxXQUFXLENBQUM7WUFFbEMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFzQixFQUFFLEVBQUU7Z0JBQ25ELFVBQVUsSUFBSSxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQy9CLElBQUksS0FBSyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3JDLE9BQU8sS0FBSyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7b0JBQ3BCLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO29CQUN4QyxVQUFVLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7b0JBRXpDLDJEQUEyRDtvQkFDM0Qsa0VBQWtFO29CQUNsRSxVQUFVLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUVsRCxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO3dCQUNoQixJQUFJLENBQUM7NEJBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQzs0QkFDaEMsSUFBSSxNQUFNLENBQUMsT0FBTyxJQUFJLE1BQU0sQ0FBQyxLQUFLLElBQUksTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dDQUNwRCxNQUFNLEdBQUcsTUFBTSxDQUFDOzRCQUNsQixDQUFDO2lDQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dDQUNqQyxvQ0FBb0M7Z0NBQ3BDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLE1BQU0sRUFBRSxDQUFDO29DQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLE1BQTRCLENBQUMsQ0FBQztnQ0FDNUMsQ0FBQztxQ0FBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVEsRUFBRSxDQUFDO29DQUMvRCxNQUFNLEtBQUssR0FBRyxNQUF3QixDQUFDO29DQUV2QyxzQkFBc0I7b0NBQ3RCLElBQUksYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7d0NBQ3pCLGFBQWEsR0FBRyxLQUFLLENBQUM7b0NBQ3hCLENBQUM7b0NBRUQsNENBQTRDO29DQUM1QyxJQUFJLGFBQWEsQ0FBQyxLQUFLLENBQUMsSUFBSSxJQUFBLDZDQUF1QixFQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7d0NBQzlELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FDM0MsS0FBNkIsRUFDN0IsTUFBTSxDQUFDLE1BQU0sRUFDYixVQUFVLEVBQ1YsYUFBYSxFQUNiLElBQUksQ0FDTCxDQUFDO3dDQUNGLElBQUksV0FBVyxFQUFFLENBQUM7NENBQ2hCLGNBQWMsR0FBRyxXQUFXLENBQUMsY0FBYyxDQUFDOzRDQUM1QyxlQUFlLEdBQUcsV0FBVyxDQUFDLGVBQWUsQ0FBQzs0Q0FDOUMscUJBQXFCLEdBQUcsV0FBVyxDQUFDLHFCQUFxQixDQUFDO3dDQUM1RCxDQUFDO29DQUNILENBQUM7b0NBRUQsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztnQ0FDckIsQ0FBQzs0QkFDSCxDQUFDO3dCQUNILENBQUM7d0JBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQzs0QkFDWCxNQUFNLENBQUMsS0FBSyxDQUFDLCtDQUErQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO3dCQUNuRSxDQUFDO29CQUNILENBQUM7b0JBQ0QsS0FBSyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ25DLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztZQUVILGNBQWMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDNUIseUNBQXlDO2dCQUN6QyxJQUFJLFVBQVUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO29CQUN0QixJQUFJLENBQUM7d0JBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQzt3QkFDdEMsVUFBVSxJQUFJLE1BQU0sQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO3dCQUNwRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQzs0QkFDMUIsSUFBSSxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssTUFBTSxFQUFFLENBQUM7Z0NBQ3pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBNEIsQ0FBQyxDQUFDOzRCQUM1QyxDQUFDO2lDQUFNLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssUUFBUSxFQUFFLENBQUM7Z0NBQy9ELE1BQU0sS0FBSyxHQUFHLE1BQXdCLENBQUM7Z0NBRXZDLElBQUksYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7b0NBQ3pCLGFBQWEsR0FBRyxLQUFLLENBQUM7Z0NBQ3hCLENBQUM7Z0NBQ0QsSUFBSSxhQUFhLENBQUMsS0FBSyxDQUFDLElBQUksSUFBQSw2Q0FBdUIsRUFBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO29DQUM5RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQzNDLEtBQTZCLEVBQzdCLE1BQU0sQ0FBQyxNQUFNLEVBQ2IsVUFBVSxFQUNWLGFBQWEsRUFDYixVQUFVLENBQ1gsQ0FBQztvQ0FDRixJQUFJLFdBQVcsRUFBRSxDQUFDO3dDQUNoQixjQUFjLEdBQUcsV0FBVyxDQUFDLGNBQWMsQ0FBQzt3Q0FDNUMsZUFBZSxHQUFHLFdBQVcsQ0FBQyxlQUFlLENBQUM7d0NBQzlDLHFCQUFxQixHQUFHLFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQztvQ0FDNUQsQ0FBQztnQ0FDSCxDQUFDO2dDQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7NEJBQ3JCLENBQUM7d0JBQ0gsQ0FBQztvQkFDSCxDQUFDO29CQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7d0JBQ1gsTUFBTSxDQUFDLEtBQUssQ0FBQyx5REFBeUQsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFDN0UsQ0FBQztnQkFDSCxDQUFDO2dCQUVELHFDQUFxQztnQkFDckMsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDO2dCQUVuQixJQUFJLGNBQWMsSUFBSSxDQUFDLEVBQUUsQ0FBQztvQkFDeEIsa0NBQWtDO29CQUNsQyxVQUFVLEdBQUcsY0FBYyxHQUFHLENBQUMsQ0FBQztvQkFDaEMsTUFBTSxDQUFDLEdBQUcsQ0FDUixlQUFLLENBQUMsS0FBSyxDQUNULDRCQUE0QixjQUFjLEdBQUcsQ0FBQyx1Q0FBdUMsZUFBZSxFQUFFLENBQ3ZHLENBQ0YsQ0FBQztvQkFFRixxRUFBcUU7b0JBQ3JFLElBQUksV0FBVyxFQUFFLENBQUM7d0JBQ2hCLFdBQVcsQ0FBQyxlQUFlLEdBQUcsZUFBZSxDQUFDO3dCQUM5QyxJQUFJLENBQUMsY0FBYyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLENBQUM7b0JBQzlELENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCw0REFBNEQ7Z0JBQzVELElBQUksTUFBTSxFQUFFLENBQUM7b0JBQ1gsTUFBTSxZQUFZLEdBQUcsRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDO29CQUNuQyxJQUFJLGNBQWMsSUFBSSxDQUFDLElBQUkscUJBQXFCLEVBQUUsQ0FBQzt3QkFDakQsd0RBQXdEO3dCQUN4RCxNQUFNLFVBQVUsR0FBRyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7d0JBQ3ZELFlBQVksQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7d0JBQ3hELFlBQVksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQzNELENBQUM7b0JBQ0QsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDckUsQ0FBQztnQkFFRCx3QkFBd0I7Z0JBQ3hCLElBQUksY0FBYyxHQUFHLEtBQUssQ0FBQztnQkFDM0IsS0FBSyxJQUFJLENBQUMsR0FBRyxVQUFVLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDaEQsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUN4QixJQUFJLFdBQVcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO3dCQUN2QixjQUFjLEdBQUcsSUFBSSxDQUFDO3dCQUN0QixNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUM5RCxDQUFDO3lCQUFNLElBQUksYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLGFBQWEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO3dCQUN4RCwwQ0FBMEM7d0JBQzFDLE1BQU0sWUFBWSxHQUFtQixDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQzdELE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLFNBQVMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBQ3JFLENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCx3Q0FBd0M7Z0JBQ3hDLElBQUksY0FBYyxFQUFFLENBQUM7b0JBQ25CLE1BQU0sQ0FBQyxHQUFHLENBQ1IsZUFBSyxDQUFDLE1BQU0sQ0FDVixXQUFXLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLFNBQVMsSUFBSSxTQUFTLGdDQUFnQyxDQUMxRixDQUNGLENBQUM7b0JBQ0YsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDeEIsQ0FBQztZQUNILENBQUMsQ0FBQyxDQUFDO1lBRUgsY0FBYyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDbkMsTUFBTSxDQUFDLEtBQUssQ0FBQyx1Q0FBdUMsRUFBRSxLQUFLLENBQUMsQ0FBQztnQkFDN0QsbUVBQW1FO2dCQUNuRSx1RUFBdUU7WUFDekUsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0JBQStCLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkQsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLGFBQWEsQ0FBQyxTQUFpQixFQUFFLFVBQWtCLEVBQUUsV0FBd0I7UUFDbkYsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFLLENBQUMsS0FBSyxDQUFDLDRDQUE0QyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFFakYsMkNBQTJDO1FBQzNDLFdBQVcsQ0FBQyxPQUFPLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FBQyxTQUFTLEVBQUUsRUFBRTtZQUM3RSxJQUFJLFNBQVMsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDM0IsSUFBSSxDQUFDO29CQUNILG9EQUFvRDtvQkFDcEQsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFFdEMsb0RBQW9EO29CQUNwRCxJQUFJLEtBQUssQ0FBQyxJQUFJLEdBQUcsV0FBVyxDQUFDLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxHQUFHLFdBQVcsQ0FBQyxTQUFTLEVBQUUsQ0FBQzt3QkFDL0UsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLElBQUksR0FBRyxXQUFXLENBQUMsUUFBUSxDQUFDO3dCQUNuRCxJQUFJLFFBQVEsR0FBRyxDQUFDLEVBQUUsQ0FBQzs0QkFDakIsTUFBTSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsUUFBUSxRQUFRLENBQUMsQ0FBQzt3QkFDakQsQ0FBQzt3QkFDRCxXQUFXLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUM7d0JBQ2xDLFdBQVcsQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQzt3QkFFdEMscUJBQXFCO3dCQUNyQixJQUFJLEtBQUssQ0FBQyxJQUFJLEdBQUcsV0FBVyxDQUFDLFVBQVUsRUFBRSxDQUFDOzRCQUN4QyxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxHQUFHLENBQUMsQ0FBQzs0QkFDeEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxHQUFHLFdBQVcsQ0FBQyxVQUFVLENBQUMsQ0FBQzs0QkFDakUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxVQUFVLENBQUMsQ0FBQzs0QkFDbEUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQzs0QkFFakIsZ0JBQWdCOzRCQUNoQixXQUFXLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUM7NEJBRXBDLG1CQUFtQjs0QkFDbkIsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQzs0QkFDeEMsV0FBVyxDQUFDLFVBQVUsSUFBSSxPQUFPLENBQUM7NEJBRWxDLHlCQUF5Qjs0QkFDekIsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7NEJBQ2pELFdBQVcsQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQzs0QkFFM0MsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztnQ0FDekIsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztvQ0FDaEIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO2dDQUNuRCxDQUFDOzRCQUNILENBQUM7d0JBQ0gsQ0FBQztvQkFDSCxDQUFDO2dCQUNILENBQUM7Z0JBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztvQkFDZixNQUFNLENBQUMsS0FBSyxDQUFDLDhCQUE4QixFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUN0RCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBRUgsV0FBVyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDeEMsTUFBTSxDQUFDLEtBQUssQ0FBQyxrQ0FBa0MsU0FBUyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdEUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxhQUFhLENBQUMsU0FBaUIsRUFBRSxJQUFZLEVBQUUsV0FBd0I7UUFDN0UsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBRTdDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLGdDQUFnQztZQUNoQyxNQUFNLENBQUMsS0FBSyxDQUFDLGlDQUFpQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDMUUsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztZQUN0QyxLQUFLLE1BQU0sTUFBTSxJQUFJLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDekMsTUFBTSxTQUFTLEdBQXlCLENBQUMsV0FBVyxHQUFHLE1BQU0sQ0FBQyxTQUFTLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUNwRixJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1lBQzVDLENBQUM7WUFDRCxPQUFPO1FBQ1QsQ0FBQztRQUVELHlCQUF5QjtRQUN6QixJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzNCLE9BQU87UUFDVCxDQUFDO1FBRUQscUJBQXFCO1FBQ3JCLElBQUksV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7WUFDeEIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFLLENBQUMsTUFBTSxDQUFDLFdBQVcsU0FBUyx5QkFBeUIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ25GLEtBQUssTUFBTSxNQUFNLElBQUksV0FBVyxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUN6QyxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ3pDLENBQUM7WUFDRCxPQUFPO1FBQ1QsQ0FBQztRQUVELDRDQUE0QztRQUM1QyxJQUFJLGFBQWEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzFCLE1BQU0sQ0FBQyxLQUFLLENBQUMsdUJBQXVCLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksVUFBVSxDQUFDLENBQUM7UUFDMUYsQ0FBQztRQUVELCtDQUErQztRQUMvQyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBQ3RDLEtBQUssTUFBTSxNQUFNLElBQUksV0FBVyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pDLE1BQU0sYUFBYSxHQUFtQixDQUFDLFdBQVcsR0FBRyxNQUFNLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3RixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDcEUsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUs7b0JBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNyRCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLENBQUMsS0FBSyxDQUNWLDhDQUE4QyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FDdkcsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGdCQUFnQixDQUFDLFNBQWlCLEVBQUUsUUFBa0I7UUFDbEUsSUFBSSxDQUFDO1lBQ0gsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDbkUsSUFBSSxXQUFXLEVBQUUsV0FBVyxJQUFJLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDdkQsTUFBTSxDQUFDLEtBQUssQ0FBQyxvQ0FBb0MsU0FBUyxPQUFPLFdBQVcsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO2dCQUM1RixNQUFNLDJCQUFVLENBQUMsYUFBYSxDQUFDLFNBQVMsRUFBRSxXQUFXLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDM0YsMkJBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzVDLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsNENBQTRDLFNBQVMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2hGLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxPQUFPO1FBQ2IsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUM7UUFDOUMsSUFBSSxZQUFZLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDckIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFLLENBQUMsTUFBTSxDQUFDLGVBQWUsWUFBWSxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7WUFDeEUsS0FBSyxNQUFNLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDM0QsSUFBSSxXQUFXLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQ3hCLFdBQVcsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQzlCLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsU0FBUyxFQUFFLENBQUMsQ0FBQztZQUMxRCxDQUFDO1lBQ0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM5QixDQUFDO1FBQ0Qsd0JBQXdCO1FBQ3hCLDJCQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkIsQ0FBQztDQUNGO0FBdmxCRCxzQ0F1bEJDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB0eXBlIHsgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCB0eXBlIHsgU2Vzc2lvbk1hbmFnZXIgfSBmcm9tICcuLi9wdHkvc2Vzc2lvbi1tYW5hZ2VyLmpzJztcbmltcG9ydCB0eXBlIHsgQXNjaWluZW1hSGVhZGVyIH0gZnJvbSAnLi4vcHR5L3R5cGVzLmpzJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQge1xuICBjYWxjdWxhdGVQcnVuaW5nUG9zaXRpb25JbkZpbGUsXG4gIGNvbnRhaW5zUHJ1bmluZ1NlcXVlbmNlLFxuICBmaW5kTGFzdFBydW5lUG9pbnQsXG4gIGxvZ1BydW5pbmdEZXRlY3Rpb24sXG59I