routeros-api
Version:
Mikrotik Routerboard RouterOS API for NodeJS
511 lines • 36.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RouterOSAPICrud = void 0;
const lodash_1 = require("lodash");
const utils = require("./utils");
const RosException_1 = require("./RosException");
class RouterOSAPICrud {
/**
* Creates a CRUD set of operations and handle
* the raw query to input on the raw API
*
* @param rosApi the raw api
* @param path the menu path we are in
* @param snakeCase if should return routerboard properties in snake_case, defaults to camelCase
*/
constructor(rosApi, path, snakeCase) {
this.queryVal = [];
this.needsObjectTranslation = false;
this.rosApi = rosApi;
this.snakeCase = snakeCase;
this.pathVal = path
.replace(/ /g, "/")
.replace(/(print|enable|disable|add|set|remove|getall|move)$/, "")
.replace(/\/$/, "");
}
/**
* Get the current menu
*/
getCurrentMenu() {
return this.pathVal;
}
/**
* Adds an item on the menu
*
* @param data the params that will be used to add the item
*/
add(data) {
return this.exec("add", data).then((results) => {
if (results.length === 0)
return Promise.resolve(null);
return this.recoverDataFromChangedItems(results.shift().ret);
});
}
/**
* Alias of add
*
* @param data the params that will be used to add the item
*/
create(data) {
return this.add(data);
}
/**
* Disable one or more entries
*
* @param ids the id(s) or number(s) to disable
*/
disable(ids) {
if (ids) {
ids = this.stringfySearchQuery(ids);
this.queryVal.push("=numbers=" + ids);
}
let disabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.queryForIdsIfNeeded(disabledIds).then((ids) => {
disabledIds = ids;
return this.exec("disable");
}).then((response) => {
return this.recoverDataFromChangedItems(disabledIds);
});
}
/**
* Delete one or more entries
*
* @param ids the id(s) or number(s) to delete
*/
delete(ids) {
return this.remove(ids);
}
/**
* Enable one or more entries
*
* @param ids the id(s) or number(s) to enable
*/
enable(ids) {
if (ids) {
ids = this.stringfySearchQuery(ids);
this.queryVal.push("=numbers=" + ids);
}
let enabledIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.queryForIdsIfNeeded(enabledIds).then((ids) => {
enabledIds = ids;
return this.exec("enable");
}).then((response) => {
return this.recoverDataFromChangedItems(enabledIds);
});
}
/**
* Run a custom command over the api, for example "export"
*
* @param command the command to run
* @param data optional data that goes with the command
*/
exec(command, data) {
if (data)
this.makeQuery(data);
const query = this.fullQuery("/" + command);
return this.translateQueryIntoId(query).then((consultedQuery) => {
return this.write(consultedQuery);
}).then((results) => {
// Only runs when using the place-after feature
// otherwise it will return the response immediately
return this.prepareToPlaceAfter(results);
});
}
/**
* Move a queried rule above another.
*
* @param to where to move the queried rule
*/
moveAbove(to) {
let movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.queryForIdsIfNeeded(movedIds).then((ids) => {
movedIds = ids;
if (to)
this.queryVal.push("=destination=" + to);
return this.exec("move");
}).then(() => {
return this.recoverDataFromChangedItems(movedIds);
});
}
/**
* Update an entry or set of entries of the menu
*
* @param data the new data to update the item
* @param ids optional id(s) of the rules
*/
update(data, ids) {
if (ids) {
ids = this.stringfySearchQuery(ids);
this.queryVal.push("=numbers=" + ids);
}
let updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.queryForIdsIfNeeded(updatedIds).then((ids) => {
updatedIds = ids;
this.makeQuery(data);
return this.exec("set");
}).then((response) => {
return this.recoverDataFromChangedItems(updatedIds);
});
}
/**
* Unset a property or set of properties of one or more entries
*
* @param properties one or more properties to unset
* @param ids the id(s) of the entries to unset the property(ies)
*/
unset(properties, ids) {
if (ids) {
ids = this.stringfySearchQuery(ids);
this.queryVal.push("=numbers=" + ids);
}
let updatedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.queryForIdsIfNeeded(updatedIds).then((ids) => {
updatedIds = ids;
if (typeof properties === "string")
properties = [properties];
const $q = [];
// Saving current queryVal for reuse, since running exec will reset it
const curQueryVal = this.queryVal.slice();
// Cleaning current queryVal to prevent duplication
this.queryVal = [];
properties.forEach((property) => {
// Putting back queryVal after a cleanup
this.queryVal = curQueryVal.slice();
this.queryVal.push("=value-name=" + utils.camelCaseOrSnakeCaseToDashedCase(property));
$q.push(this.exec("unset"));
});
return Promise.all($q);
}).then(() => {
return this.recoverDataFromChangedItems(updatedIds);
});
}
/**
* Removes an entry or set of entries of the menu
*
* @param ids optional id(s) to be removed from the menu
*/
remove(ids) {
if (ids) {
ids = this.stringfySearchQuery(ids);
this.queryVal.push("=numbers=" + ids);
}
const idsForRemoval = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
let responseData;
return this.queryForIdsIfNeeded(idsForRemoval).then((ids) => {
return this.recoverDataFromChangedItems(ids);
}).then((response) => {
responseData = response;
return this.exec("remove");
}).then(() => {
return Promise.resolve(responseData);
});
}
/**
* Alias of update
*
* @param data the new data to update the item
* @param ids optional id(s) of the rules
*/
set(data, ids) {
return this.update(data, ids);
}
/**
* Alias of update
*
* @param data the new data to update the item
* @param ids optional id(s) of the rules
*/
edit(data, ids) {
return this.update(data, ids);
}
/**
* Moves a rule ABOVE the destination
*
* @param from the rule you want to move
* @param to the destination where you want to move
*/
moveEntry(from, to) {
if (!Array.isArray(from))
from = [from];
from = this.stringfySearchQuery(from);
this.queryVal.push("=numbers=" + from);
if (to) {
to = this.stringfySearchQuery(to);
this.queryVal.push("=destination=" + to);
}
const movedIds = utils.lookForIdParameterAndReturnItsValue(this.queryVal);
return this.exec("move").then(() => {
return this.recoverDataFromChangedItems(movedIds);
});
}
/**
* Creates the full array of sentences that will be
* compatible with the raw API to be sent to the
* routerboard using all the functions triggered
* up until now
*
* @param append action to add in front of the menu
*/
fullQuery(append) {
let val = [];
if (append) {
val.push(this.pathVal + append);
}
else {
val.push(this.pathVal);
}
if (this.proplistVal)
val.push(this.proplistVal);
val = val.concat(this.queryVal).slice();
if (!/(print|getall)$/.test(val[0])) {
for (let index = 0; index < val.length; index++) {
val[index] = val[index].replace(/^\?/, "=");
}
}
return val;
}
/**
* Make the query array to write on the API,
* adding a question mark if it needs to print
* filtered content
*
* @param searchParameters The key-value pair to add to the search
*/
makeQuery(searchParameters, addQuestionMark = false, addToLocalQuery = true) {
let tmpKey;
let tmpVal;
const tmpQuery = addToLocalQuery ? this.queryVal : [];
for (const key in searchParameters) {
if (searchParameters.hasOwnProperty(key)) {
tmpVal = searchParameters[key];
if (/[A-Z]/.test(tmpKey)) {
tmpKey = tmpKey.replace(/([A-Z])/g, "$1").toLowerCase();
}
tmpKey = key.replace(/_/, "-");
// if selecting for id, convert it to .id to match mikrotik standards
switch (tmpKey) {
case "id":
tmpKey = ".id";
break;
case "next":
tmpKey = ".nextid";
break;
case "dead":
tmpKey = ".dead";
break;
default:
break;
}
if (typeof tmpVal === "boolean") {
tmpVal = tmpVal ? "yes" : "no";
}
else if (tmpVal === null) {
tmpVal = "";
}
else if (typeof tmpVal === "object") {
tmpVal = this.stringfySearchQuery(tmpVal);
}
else if (tmpKey === "placeAfter") {
this.placeAfter = tmpVal;
tmpKey = "placeBefore";
}
tmpKey = (addQuestionMark ? "?" : "=") + tmpKey;
tmpKey = utils.camelCaseOrSnakeCaseToDashedCase(tmpKey);
tmpQuery.push(tmpKey + "=" + tmpVal);
}
}
return tmpQuery;
}
/**
* Write the query using the raw API
*
* @param query the raw array of sentences to write on the socket
*/
write(query) {
this.queryVal = [];
this.proplistVal = "";
return this.rosApi.write(query).then((results) => {
return Promise.resolve(this.treatMikrotikProperties(results));
});
}
/**
* Translates .id, place-before and number without using internal
* mikrotik id (something like *4A).
*
* This should check if one of those parameters are an object
* and use that object to search the real id of the item.
*
* @param queries query array
*/
translateQueryIntoId(queries) {
if (queries.length === 0 || !this.needsObjectTranslation)
return Promise.resolve(queries);
const promises = [];
const consultedIndexes = [];
for (const [index, element] of queries.entries()) {
const str = element.replace(/^\?/, "").replace(/^\=/, "");
if (str.includes(".id=") || str.includes("place-before=") || str.includes("place-after=") || str.includes("numbers=")) {
if (/\{.*\}/.test(str)) {
const key = str.split("=").shift();
const value = JSON.parse(str.split("=").pop());
const treatedQuery = [
this.pathVal + "/print",
"=.proplist=.id"
].concat(this.makeQuery(value, true, false));
const promise = this.rosApi.write(treatedQuery);
consultedIndexes.push({
index: index,
key: key
});
promises.push(promise);
}
}
}
return Promise.all(promises).then((results) => {
for (let result of results) {
if (Array.isArray(result))
result = result.shift();
const consulted = consultedIndexes.shift();
if (!result)
return Promise.reject(new RosException_1.RosException("REFNOTFND", { key: consulted.key }));
if (consulted.key === "place-after") {
this.placeAfter = result[".id"];
consulted.key = "place-before";
}
queries[consulted.index] = "=" + consulted.key + "=" + result[".id"];
}
this.needsObjectTranslation = false;
return Promise.resolve(queries);
});
}
/**
* If the place-after feature was used, the rule below
* will be moved above here.
*
* @param results
*/
prepareToPlaceAfter(results) {
if (!this.placeAfter || results.length !== 1)
return Promise.resolve(results);
if (!results[0].ret)
return Promise.resolve(results);
const from = this.placeAfter;
const to = results[0].ret;
this.placeAfter = null;
return this.moveEntry(from, to).then(() => {
return Promise.resolve(results);
});
}
/**
* Transform mikrotik properties to either camelCase or snake_case
* and casts values of true or false to boolean and
* integer strings to number
*
* @param results the result set of an operation
*/
treatMikrotikProperties(results) {
const treatedArr = [];
results.forEach((result) => {
const tmpItem = {
$$path: this.pathVal
};
for (const key in result) {
if (result.hasOwnProperty(key)) {
const tmpVal = result[key];
let tmpKey = this.snakeCase
? utils.dashedCaseToSnakeCase(key)
: utils.dashedCaseToCamelCase(key);
tmpKey = tmpKey.replace(/^\./, "");
tmpItem[tmpKey] = tmpVal;
if (tmpVal === "true" || tmpVal === "false") {
tmpItem[tmpKey] = tmpVal === "true";
}
else if (/^\d+(\.\d+)?$/.test(tmpVal)) {
tmpItem[tmpKey] = parseFloat(tmpVal);
}
}
}
treatedArr.push(tmpItem);
});
return treatedArr;
}
/**
* Stringify a json formated object to be used later
* for a translation
*
* @param items object items to stringfy
*/
stringfySearchQuery(items) {
let isArray = true;
const newItems = [];
if (!Array.isArray(items)) {
isArray = false;
items = [items];
}
for (const item of items) {
if (typeof item === "object") {
this.needsObjectTranslation = true;
newItems.push(JSON.stringify(item));
}
else
newItems.push(item);
}
return isArray ? newItems : newItems.shift();
}
/**
* Clean data print of provided ids, used only when
* creating, editting or unsetting properties
*
* @param data
* @param ids
*/
recoverDataFromChangedItems(ids) {
if (!ids) {
return this.rosApi.write([this.pathVal + "/print"])
.then((data) => Promise.resolve(this.treatMikrotikProperties(data).shift()));
}
const promises = [];
const splittedIds = ids.split(",");
for (const id of splittedIds) {
const promise = this.rosApi.write([
this.pathVal + "/print",
"?.id=" + id
]);
promises.push(promise);
}
return Promise.all(promises).then((data) => {
if (!data)
return Promise.resolve(data);
data = lodash_1.flatten(data);
data = this.treatMikrotikProperties(data);
if (!ids.includes(","))
return Promise.resolve(data.shift());
return Promise.resolve(data);
});
}
/**
* If trying do any action without providing any id, just
* a query. Find all their ids and return them
*
* @param ids
*/
queryForIdsIfNeeded(ids) {
if (ids)
return Promise.resolve(ids);
this.queryVal.push("=.proplist=.id");
const query = this.fullQuery("/print");
let queriedIds;
return this.write(query).then((data) => {
data = lodash_1.reduce(data, (result, value, key) => {
result.push(value.id);
return result;
}, []);
queriedIds = data + "";
if (queriedIds)
this.queryVal.push("=numbers=" + queriedIds);
return Promise.resolve(queriedIds);
});
}
}
exports.RouterOSAPICrud = RouterOSAPICrud;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm9zQXBpQ3J1ZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Sb3NBcGlDcnVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUF5QztBQUN6QyxpQ0FBaUM7QUFHakMsaURBQThDO0FBRTlDLE1BQXNCLGVBQWU7SUFnQmpDOzs7Ozs7O09BT0c7SUFDSCxZQUFZLE1BQW1CLEVBQUUsSUFBWSxFQUFFLFNBQWtCO1FBaEJ2RCxhQUFRLEdBQWEsRUFBRSxDQUFDO1FBSTFCLDJCQUFzQixHQUFZLEtBQUssQ0FBQztRQWE1QyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztRQUMzQixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUk7YUFDZCxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQzthQUNsQixPQUFPLENBQUMsb0RBQW9ELEVBQUUsRUFBRSxDQUFDO2FBQ2pFLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksY0FBYztRQUNqQixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxHQUFHLENBQUMsSUFBWTtRQUNuQixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQVksRUFBRSxFQUFFO1lBQ2hELElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDO2dCQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN2RCxPQUFPLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDakUsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxJQUFZO1FBQ3RCLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE9BQU8sQ0FBQyxHQUFjO1FBQ3pCLElBQUksR0FBRyxFQUFFO1lBQ0wsR0FBRyxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNwQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLEdBQUcsR0FBRyxDQUFDLENBQUM7U0FDekM7UUFDRCxJQUFJLFdBQVcsR0FBRyxLQUFLLENBQUMsbUNBQW1DLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzNFLE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLFdBQVcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQVcsRUFBRSxFQUFFO1lBQzlELFdBQVcsR0FBRyxHQUFHLENBQUM7WUFDbEIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ2hDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQWUsRUFBRSxFQUFFO1lBQ3hCLE9BQU8sSUFBSSxDQUFDLDJCQUEyQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3pELENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsR0FBUztRQUNuQixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDNUIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsR0FBYztRQUN4QixJQUFJLEdBQUcsRUFBRTtZQUNMLEdBQUcsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDcEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1NBQ3pDO1FBQ0QsSUFBSSxVQUFVLEdBQUcsS0FBSyxDQUFDLG1DQUFtQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMxRSxPQUFPLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFXLEVBQUUsRUFBRTtZQUM3RCxVQUFVLEdBQUcsR0FBRyxDQUFDO1lBQ2pCLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMvQixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFlLEVBQUUsRUFBRTtZQUN4QixPQUFPLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN4RCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLElBQUksQ0FBQyxPQUFlLEVBQUUsSUFBYTtRQUN0QyxJQUFJLElBQUk7WUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQy9CLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxHQUFHLE9BQU8sQ0FBQyxDQUFDO1FBQzVDLE9BQU8sSUFBSSxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLGNBQWMsRUFBRSxFQUFFO1lBQzVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUNoQiwrQ0FBK0M7WUFDL0Msb0RBQW9EO1lBQ3BELE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzdDLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxTQUFTLENBQUMsRUFBVztRQUN4QixJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsbUNBQW1DLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3hFLE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQVcsRUFBRSxFQUFFO1lBQzNELFFBQVEsR0FBRyxHQUFHLENBQUM7WUFDZixJQUFJLEVBQUU7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsZUFBZSxHQUFHLEVBQUUsQ0FBQyxDQUFDO1lBQ2pELE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM3QixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ1QsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdEQsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxNQUFNLENBQUMsSUFBWSxFQUFFLEdBQWM7UUFDdEMsSUFBSSxHQUFHLEVBQUU7WUFDTCxHQUFHLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxHQUFHLENBQUMsQ0FBQztTQUN6QztRQUVELElBQUksVUFBVSxHQUFHLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFMUUsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBVyxFQUFFLEVBQUU7WUFDN0QsVUFBVSxHQUFHLEdBQUcsQ0FBQztZQUNqQixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUM1QixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFlLEVBQUUsRUFBRTtZQUN4QixPQUFPLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN4RCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxVQUE2QixFQUFFLEdBQWM7UUFDdEQsSUFBSSxHQUFHLEVBQUU7WUFDTCxHQUFHLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxHQUFHLENBQUMsQ0FBQztTQUN6QztRQUVELElBQUksVUFBVSxHQUFHLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFMUUsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBVyxFQUFFLEVBQUU7WUFDN0QsVUFBVSxHQUFHLEdBQUcsQ0FBQztZQUVqQixJQUFJLE9BQU8sVUFBVSxLQUFLLFFBQVE7Z0JBQUUsVUFBVSxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7WUFFOUQsTUFBTSxFQUFFLEdBQXVCLEVBQUUsQ0FBQztZQUVsQyxzRUFBc0U7WUFDdEUsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUUxQyxtREFBbUQ7WUFDbkQsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFFLENBQUM7WUFDbkIsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUM1Qix3Q0FBd0M7Z0JBQ3hDLElBQUksQ0FBQyxRQUFRLEdBQUcsV0FBVyxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNwQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxjQUFjLEdBQUcsS0FBSyxDQUFDLGdDQUFnQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7Z0JBQ3RGLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2hDLENBQUMsQ0FBQyxDQUFDO1lBQ0gsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzNCLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDVCxPQUFPLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN4RCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLEdBQVM7UUFDbkIsSUFBSSxHQUFHLEVBQUU7WUFDTCxHQUFHLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxHQUFHLENBQUMsQ0FBQztTQUN6QztRQUVELE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFL0UsSUFBSSxZQUFZLENBQUM7UUFDakIsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBVyxFQUFFLEVBQUU7WUFDaEUsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDakQsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBYSxFQUFFLEVBQUU7WUFDdEIsWUFBWSxHQUFHLFFBQVEsQ0FBQztZQUN4QixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDL0IsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNULE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUN6QyxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEdBQUcsQ0FBQyxJQUFZLEVBQUUsR0FBYztRQUNuQyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLElBQUksQ0FBQyxJQUFZLEVBQUUsR0FBYztRQUNwQyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNPLFNBQVMsQ0FBQyxJQUFjLEVBQUUsRUFBb0I7UUFDcEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQUUsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEMsSUFBSSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN0QyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLENBQUM7UUFDdkMsSUFBSSxFQUFFLEVBQUU7WUFDSixFQUFFLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2xDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLGVBQWUsR0FBRyxFQUFFLENBQUMsQ0FBQztTQUM1QztRQUNELE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDMUUsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDL0IsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdEQsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNPLFNBQVMsQ0FBQyxNQUFlO1FBQy9CLElBQUksR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNiLElBQUksTUFBTSxFQUFFO1lBQ1IsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxDQUFDO1NBQ25DO2FBQU07WUFDSCxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUMxQjtRQUNELElBQUksSUFBSSxDQUFDLFdBQVc7WUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNqRCxHQUFHLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFeEMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRTtZQUNqQyxLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsR0FBRyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsRUFBRTtnQkFDN0MsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQy9DO1NBQ0o7UUFFRCxPQUFPLEdBQUcsQ0FBQztJQUNmLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDTyxTQUFTLENBQUMsZ0JBQXdCLEVBQUUsa0JBQTJCLEtBQUssRUFBRSxrQkFBMkIsSUFBSTtRQUMzRyxJQUFJLE1BQWMsQ0FBQztRQUNuQixJQUFJLE1BQXdDLENBQUM7UUFFN0MsTUFBTSxRQUFRLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFdEQsS0FBSyxNQUFNLEdBQUcsSUFBSSxnQkFBZ0IsRUFBRTtZQUNoQyxJQUFJLGdCQUFnQixDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDdEMsTUFBTSxHQUFHLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUMvQixJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUU7b0JBQ3RCLE1BQU0sR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztpQkFDM0Q7Z0JBQ0QsTUFBTSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUUvQixxRUFBcUU7Z0JBQ3JFLFFBQVEsTUFBTSxFQUFFO29CQUNaLEtBQUssSUFBSTt3QkFDTCxNQUFNLEdBQUcsS0FBSyxDQUFDO3dCQUNmLE1BQU07b0JBRVYsS0FBSyxNQUFNO3dCQUNQLE1BQU0sR0FBRyxTQUFTLENBQUM7d0JBQ25CLE1BQU07b0JBRVYsS0FBSyxNQUFNO3dCQUNQLE1BQU0sR0FBRyxPQUFPLENBQUM7d0JBQ2pCLE1BQU07b0JBRVY7d0JBQ0ksTUFBTTtpQkFDYjtnQkFFRCxJQUFJLE9BQU8sTUFBTSxLQUFLLFNBQVMsRUFBRTtvQkFDN0IsTUFBTSxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7aUJBQ2xDO3FCQUFNLElBQUksTUFBTSxLQUFLLElBQUksRUFBRTtvQkFDeEIsTUFBTSxHQUFHLEVBQUUsQ0FBQztpQkFDZjtxQkFBTSxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRTtvQkFDbkMsTUFBTSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztpQkFDN0M7cUJBQU0sSUFBSSxNQUFNLEtBQUssWUFBWSxFQUFFO29CQUNoQyxJQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQztvQkFDekIsTUFBTSxHQUFHLGFBQWEsQ0FBQztpQkFDMUI7Z0JBRUQsTUFBTSxHQUFHLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLE1BQU0sQ0FBQztnQkFFaEQsTUFBTSxHQUFHLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFFeEQsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsR0FBRyxHQUFHLE1BQU0sQ0FBQyxDQUFDO2FBQ3hDO1NBQ0o7UUFFRCxPQUFPLFFBQVEsQ0FBQztJQUNwQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNPLEtBQUssQ0FBQyxLQUFlO1FBQzNCLElBQUksQ0FBQyxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ25CLElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO1FBQ3RCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDN0MsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2xFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ08sb0JBQW9CLENBQUMsT0FBaUI7UUFDNUMsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0I7WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFMUYsTUFBTSxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQ3BCLE1BQU0sZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO1FBRTVCLEtBQUssTUFBTSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsSUFBSSxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDOUMsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztZQUMxRCxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxlQUFlLENBQUMsSUFBSSxHQUFHLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUU7Z0JBRW5ILElBQUksUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRTtvQkFDcEIsTUFBTSxHQUFHLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztvQkFDbkMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7b0JBQy9DLE1BQU0sWUFBWSxHQUFHO3dCQUNqQixJQUFJLENBQUMsT0FBTyxHQUFHLFFBQVE7d0JBQ3ZCLGdCQUFnQjtxQkFDbkIsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7b0JBQzdDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO29CQUNoRCxnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7d0JBQ2xCLEtBQUssRUFBRSxLQUFLO3dCQUNaLEdBQUcsRUFBRSxHQUFHO3FCQUNYLENBQUMsQ0FBQztvQkFDSCxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2lCQUMxQjthQUVKO1NBQ0o7UUFFRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDMUMsS0FBSyxJQUFJLE1BQU0sSUFBSSxPQUFPLEVBQUU7Z0JBQ3hCLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7b0JBQUUsTUFBTSxHQUFHLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDbkQsTUFBTSxTQUFTLEdBQUcsZ0JBQWdCLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQzNDLElBQUksQ0FBQyxNQUFNO29CQUFFLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLDJCQUFZLENBQUMsV0FBVyxFQUFFLEVBQUUsR0FBRyxFQUFFLFNBQVMsQ0FBQyxHQUFHLEVBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pGLElBQUksU0FBUyxDQUFDLEdBQUcsS0FBSyxhQUFhLEVBQUU7b0JBQ2pDLElBQUksQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO29CQUNoQyxTQUFTLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQztpQkFDbEM7Z0JBQ0QsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxHQUFHLEdBQUcsU0FBUyxDQUFDLEdBQUcsR0FBRyxHQUFHLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQ3hFO1lBQ0QsSUFBSSxDQUFDLHNCQUFzQixHQUFHLEtBQUssQ0FBQztZQUNwQyxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDcEMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDTyxtQkFBbUIsQ0FBQyxPQUFPO1FBQ2pDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQztZQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM5RSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUc7WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUM3QixNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQzFCLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUN0QyxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDcEMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ08sdUJBQXVCLENBQUMsT0FBaUI7UUFDL0MsTUFBTSxVQUFVLEdBQWEsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUN2QixNQUFNLE9BQU8sR0FBRztnQkFDWixNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU87YUFDdkIsQ0FBQztZQUNGLEtBQUssTUFBTSxHQUFHLElBQUksTUFBTSxFQUFFO2dCQUN0QixJQUFJLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLEVBQUU7b0JBQzVCLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDM0IsSUFBSSxNQUFNLEdBQUcsSUFBSSxDQUFDLFNBQVM7d0JBQ3ZCLENBQUMsQ0FBQyxLQUFLLENBQUMscUJBQXFCLENBQUMsR0FBRyxDQUFDO3dCQUNsQyxDQUFDLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUN2QyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQ25DLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUM7b0JBQ3pCLElBQUksTUFBTSxLQUFLLE1BQU0sSUFBSSxNQUFNLEtBQUssT0FBTyxFQUFFO3dCQUN6QyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBTSxLQUFLLE1BQU0sQ0FBQztxQkFDdkM7eUJBQU0sSUFBSSxlQUFlLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFO3dCQUNyQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO3FCQUN4QztpQkFDSjthQUNKO1lBQ0QsVUFBVSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM3QixDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sVUFBVSxDQUFDO0lBQ3RCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNLLG1CQUFtQixDQUFDLEtBQVU7UUFDbEMsSUFBSSxPQUFPLEdBQUcsSUFBSSxDQUFDO1FBQ25CLE1BQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQztRQUNwQixJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUN2QixPQUFPLEdBQUcsS0FBSyxDQUFDO1lBQ2hCLEtBQUssR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQ25CO1FBQ0QsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUU7WUFDdEIsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUU7Z0JBQzFCLElBQUksQ0FBQyxzQkFBc0IsR0FBRyxJQUFJLENBQUM7Z0JBQ25DLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2FBQ3ZDOztnQkFBTSxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQzlCO1FBQ0QsT0FBTyxPQUFPLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSywyQkFBMkIsQ0FBQyxHQUFZO1FBQzVDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDTixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQztpQkFDOUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUM7U0FDcEY7UUFFRCxNQUFNLFFBQVEsR0FBRyxFQUFFLENBQUM7UUFDcEIsTUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQyxLQUFLLE1BQU0sRUFBRSxJQUFJLFdBQVcsRUFBRTtZQUMxQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztnQkFDOUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxRQUFRO2dCQUN2QixPQUFPLEdBQUcsRUFBRTthQUNmLENBQUMsQ0FBQztZQUNILFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDMUI7UUFDRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUU7WUFDdkMsSUFBSSxDQUFDLElBQUk7Z0JBQUUsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3hDLElBQUksR0FBRyxnQkFBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLElBQUksR0FBRyxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUM3RCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxtQkFBbUIsQ0FBQyxHQUFZO1FBQ3BDLElBQUksR0FBRztZQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUVyQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkMsSUFBSSxVQUFVLENBQUM7UUFDZixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBVyxFQUFFLEVBQUU7WUFDMUMsSUFBSSxHQUFHLGVBQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxFQUFFO2dCQUN2QyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDdEIsT0FBTyxNQUFNLENBQUM7WUFDbEIsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ1AsVUFBVSxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7WUFDdkIsSUFBSSxVQUFVO2dCQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUMsQ0FBQztZQUM3RCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDdkMsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0NBRUo7QUFuaUJELDBDQW1pQkMifQ==