UNPKG

@tolokoban/tgd

Version:

ToloGameDev library for WebGL2

460 lines 38.2 kB
import { TgdEvent } from "./../event/index.js"; import { tgdLoadImage } from "./../loader/image.js"; import { TgdPainterFilter, TgdPainterFramebuffer, TgdPainterLogic } from "./../painter/index.js"; import { isWebglImage } from "./../types/index.js"; import { isString } from "./../types/guards.js"; import { ensureArrayNumber4, resolveErrorMessage, tgdCanvasCreate, webglLookup } from "./../utils/index.js"; import { webglTextureParametersSet } from "./../webgl/index.js"; import { isLoadBmpOptions } from "./types.js"; export class TgdTexture2D { constructor(context, options = {}) { this.context = context; this.options = options; this.eventChange = new TgdEvent(); this._texture = null; this._width = -1; this._height = -1; this.params = { magFilter: "LINEAR", minFilter: "LINEAR", wrapS: "REPEAT", wrapT: "REPEAT", wrapR: "REPEAT", }; this.videoUpdater = null; const { storage, params, load } = options; const { gl } = context; this.gl = gl; this.name = options.name ?? `Texture2D/${TgdTexture2D.counter++}`; this.storage = { width: 0, height: 0, format: storage?.format ?? "RGBA8 / RGBA / UNSIGNED_BYTE", levels: 1, flipY: false, premultipliedAlpha: false, ...storage, }; const width = storage?.width; const height = storage?.height; this.createTexture(options.color); if (typeof width === "number" && typeof height === "number") { this.resize(width, height); } if (params) this.setParams(params); if (isWebglImage(load) || isString(load)) { this.loadBitmap(load); } else if (isLoadBmpOptions(load)) { this.loadBitmap(load.bmp, load); } } clone(name) { return new TgdTexture2D(this.context, structuredClone({ ...this.options, name: name ?? this.options.name, })).loadBitmap(tgdCanvasCreate(this.width, this.height)); } get format() { return this.storage.format; } set format(format) { this.storage.format = format; } get internalFormat() { const [internalFormat] = this.splitFormat(this.context.gl); return internalFormat; } get sourceFormat() { const [, sourceFormat] = this.splitFormat(this.context.gl); return sourceFormat; } get sourceType() { const [, , sourceType] = this.splitFormat(this.context.gl); return sourceType; } delete() { const { videoUpdater } = this; if (videoUpdater) this.context.remove(videoUpdater); if (this._texture) this.gl.deleteTexture(this._texture); this._texture = null; } get width() { return this._width; } get height() { return this._height; } createTexture(color) { this.delete(); const { gl } = this; const texture = gl.createTexture(); if (!texture) throw new Error("Unable to create a WebGLTexture!"); this._texture = texture; this.bind(); this.setParams(this.params); const color4 = ensureArrayNumber4(color, [1, 0, 0.667, 1]); const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl); gl.texImage2D(gl.TEXTURE_2D, // target 0, // level of detail internalFormat, // internal format 1, // width 1, // height 0, // border sourceFormat, // source format sourceType, // source type sourceType === gl.FLOAT ? new Float32Array(color4) : new Uint8Array(color4)); this._width = 1; this._height = 1; } splitFormat(gl) { const [internalFormat, sourceFormat, sourceType] = this.storage.format.split(" / "); return [gl[internalFormat], gl[sourceFormat], gl[sourceType]]; } resize(w, h) { const width = Math.ceil(w); const height = Math.ceil(h); if (width === this.width && height === this.height) return; this.loadBitmap(tgdCanvasCreate(width, height)); } checkError(caption, action) { const { gl } = this; const error = gl.getError(); if (error !== gl.NO_ERROR) { this.context.console.error(`[TgdTexture2D::${this.name}] Error in ${caption}:`, webglLookup(error), this); action?.(); } } get glTexture() { if (this._texture) return this._texture; const error = `[TgdTexture2D] Texture "${this.name}" has been deleted!`; this.context.console.error(error); throw new Error(error); } bind() { this.gl.bindTexture(this.gl.TEXTURE_2D, this.glTexture); } unbind() { this.gl.bindTexture(this.gl.TEXTURE_2D, null); } loadVideo(url, options = {}) { let playing = false; let timeupdate = false; const video = document.createElement("video"); let initialized = false; const checkReady = () => { if (!playing || !timeupdate || initialized) return; initialized = true; this.loadBitmap(video, options); const videoUpdater = new TgdPainterLogic(() => { if (!video.paused && !video.ended) { this.loadBitmap(video); } }); if (this.videoUpdater) this.context.remove(this.videoUpdater); this.context.add(videoUpdater); this.videoUpdater = videoUpdater; }; video.playsInline = true; video.muted = true; video.loop = true; video.addEventListener("playing", () => { playing = true; checkReady(); }); video.addEventListener("timeupdate", () => { timeupdate = true; checkReady(); }); video.src = url; video.play().catch(() => { // We want to prevent this error: // The fetching process for the media resource was aborted by the user agent at the user's request. }); return this; } loadBitmap(bmp, options = {}) { if (!bmp) return this; if (typeof bmp === "string") { if (isImageURL(bmp)) return this.loadBitmap(tgdLoadImage(bmp), options); return this.loadVideo(bmp, options); } if (!isWebglImage(bmp)) { // This is a Promise bmp.then((data) => { this.loadBitmap(data); }).catch((error) => this.context.console.error("Unable to load texture BMP:", error)); return this; } if (bmp instanceof HTMLImageElement) { if (!bmp.complete) { bmp.addEventListener("load", () => this.loadBitmap(bmp, options)); return this; } } else if (bmp instanceof HTMLCanvasElement) { if (bmp.width <= 0 || bmp.height <= 0) { return this; } } const { storage, gl } = this; const { level = 0 } = options; this._width = bmp.width; this._height = bmp.height; this.bind(); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.storage.flipY); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.storage.premultipliedAlpha); const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl); gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, sourceFormat, sourceType, bmp); this.checkError(`loadBitmap(${JSON.stringify({ width: bmp.width, height: bmp.height, })})\ngl.texImage2D(gl.TEXTURE_2D, ${level}, gl.${"RGBA" // storage.internalFormat }, gl.${"RGBA"}, gl.UNSIGNED_BYTE, bmp)`, () => { this.context.console.log("bmp =", bmp); // @FIXME: Remove this line written on 2026-03-24 at 09:58 this.context.console.log("storage.flipY =", this.storage.flipY); this.context.console.log("storage.premultipliedAlpha =", this.storage.premultipliedAlpha); }); if (options.generateMipmap) { this.generateMipmap(); const { context } = this; this.checkError(`loadBitmap(${JSON.stringify({ width: bmp.width, height: bmp.height, })})\ngl.texImage2D(gl.TEXTURE_2D, ${level}, gl.${context.lookupWebglConstant(internalFormat)}, gl.${context.lookupWebglConstant(sourceFormat)}, gl.${context.lookupWebglConstant(sourceType)}, bmp)\ngenerateMipmap()`); } const { filter } = this.options; if (filter) this.applyFilter(filter); this.unbind(); options.onLoad?.(); this.eventChange.dispatch(this); return this; } applyFilter(filter) { if (this.width < 1 || this.height < 1) { throw new Error(`[TgdTexture2D.applyFilter] Current size is invalie (${this.width}, ${this.height})! Maybe the texture is not loaded yet.`); } const output = new TgdTexture2D(this.context, { params: { ...this.params }, storage: { ...this.storage, width: this.width, height: this.height, }, }); const painter = new TgdPainterFilter(this.context, { filters: [filter], flipY: true, texture: this, }); const framebuffer = new TgdPainterFramebuffer(this.context, { textureColor0: output, depthBuffer: false, children: [painter], fixedSize: true, }); framebuffer.paint(0, 0); this.gl.deleteTexture(this._texture); this._texture = output.glTexture; this.bind(); this.setParams(this.params); return this; } loadData(data, options) { try { const { level = 0, width, height, format, // offset = 0, } = options; const { gl, context } = this; if (format) { this.format = format; } checkIfDataHasMinimalRequestedSize(data, this.format, width, height); this._width = width; this._height = height; this.bind(); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.storage.flipY); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.storage.premultipliedAlpha); const [internalFormat, sourceFormat, sourceType] = this.splitFormat(gl); if (sourceType === gl.FLOAT && !(data instanceof Float32Array)) { throw new Error(`You must pass a Float32Array with this format: ${this.format}!`); } gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, 0, sourceFormat, sourceType, data); this.checkError(`loadData(${printArray(data)}, ${JSON.stringify(options)}) internalFormat: ${context.lookupWebglConstant(internalFormat)} sourceFormat: ${context.lookupWebglConstant(sourceFormat)} sourceType: ${context.lookupWebglConstant(sourceType)} `); this.unbind(); this.eventChange.dispatch(this); return this; } catch (error) { throw new Error(`[${this.name}::loadData()] ${resolveErrorMessage(error)}`); } } /** * * @param unit Unit to link the texture to * @param program The program or uniform block object that owns the uniform to update * @param uniformName The uniform that holds the texture */ activate(unit, program, uniformName) { const { gl } = this; gl.activeTexture(gl.TEXTURE0 + unit); this.bind(); if (program && uniformName) { program.uniform1i(uniformName, unit); } return this; } generateMipmap() { const { gl } = this; this.bind(); gl.generateMipmap(gl.TEXTURE_2D); return this; } setParams(parameters) { this.bind(); webglTextureParametersSet(this.gl, parameters); this.params = { ...this.params, ...parameters, }; return this; } set textureBaseLevel(value) { const { gl } = this; this.bind(); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, value); } get textureBaseLevel() { const { gl } = this; this.bind(); const value = gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL); return value; } set textureMaxLevel(value) { const { gl } = this; this.bind(); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, value); } get textureMaxLevel() { const { gl } = this; this.bind(); const value = gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL); return value; } getParameter(parameter) { const { gl } = this; this.bind(); const value = gl.getTexParameter(gl.TEXTURE_2D, gl[parameter]); return value; } debug(title) { console.debug(title ?? this.name ?? "TgdTexture2D", " ", this.width, "×", this.height); const parameters = [ "TEXTURE_MAG_FILTER", "TEXTURE_MIN_FILTER", "TEXTURE_WRAP_R", "TEXTURE_WRAP_S", "TEXTURE_WRAP_T", "TEXTURE_MAX_LEVEL", "TEXTURE_MAX_LOD", "TEXTURE_MIN_LOD", "TEXTURE_BASE_LEVEL", "TEXTURE_COMPARE_FUNC", "TEXTURE_COMPARE_MODE", "TEXTURE_IMMUTABLE_FORMAT", "TEXTURE_IMMUTABLE_LEVELS", ]; for (const name of parameters) { const value = this.getParameter(name); console.debug(">", name, "=", value, typeof value === "number" ? `(${webglLookup(value)})` : " "); } } } TgdTexture2D.counter = 0; function printArray(data) { if (data.length > 10) { return `[${data.slice(0, 10).join(", ")}, ...] (${data.length} elements)`; } return JSON.stringify(data); } function isImageURL(url) { const extensions = [".webp", ".jpg", ".jpeg", ".png", ".avif", ".gif"]; const urlLowercase = url.toLowerCase().trim(); for (const ext of extensions) { if (urlLowercase.endsWith(ext)) return true; } return false; } function computeDataSizePerRow(format, width) { const [, sourceFormat, sourceType] = format.split(" / "); const componentCount = { RED: 1, RED_INTEGER: 1, RG: 2, RG_INTEGER: 2, RGB: 3, RGB_INTEGER: 3, RGBA: 4, RGBA_INTEGER: 4, DEPTH_COMPONENT: 1, DEPTH_STENCIL: 1, }; const typeBytes = { UNSIGNED_BYTE: 1, BYTE: 1, UNSIGNED_SHORT: 2, SHORT: 2, HALF_FLOAT: 2, UNSIGNED_INT: 4, INT: 4, FLOAT: 4, UNSIGNED_SHORT_5_6_5: 2, UNSIGNED_SHORT_5_5_5_1: 2, UNSIGNED_SHORT_4_4_4_4: 2, UNSIGNED_INT_2_10_10_10_REV: 4, UNSIGNED_INT_10F_11F_11F_REV: 4, UNSIGNED_INT_5_9_9_9_REV: 4, UNSIGNED_INT_24_8: 4, FLOAT_32_UNSIGNED_INT_24_8_REV: 8, }; const packed = typeBytes[sourceType] !== undefined && sourceType !== "UNSIGNED_BYTE" && sourceType !== "BYTE" && sourceType !== "UNSIGNED_SHORT" && sourceType !== "SHORT" && sourceType !== "UNSIGNED_INT" && sourceType !== "INT" && sourceType !== "HALF_FLOAT" && sourceType !== "FLOAT"; const bytesPerPixel = packed ? typeBytes[sourceType] : (componentCount[sourceFormat] ?? 4) * (typeBytes[sourceType] ?? 1); const rowBytes = width * bytesPerPixel; return Math.ceil(rowBytes / 4) * 4; } function checkIfDataHasMinimalRequestedSize(data, format, width, height) { const expectedSizePerRow = computeDataSizePerRow(format, width); const expectedSize = expectedSizePerRow * height; if (data.byteLength < expectedSize) { throw new Error(`For a texture of ${width}×${height} with format "${format}", you need a storage of ${expectedSizePerRow}×${height} = ${expectedSize}. But your data has only ${data.byteLength} bytes. Did you specify the right format?`); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dHVyZTJkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RleHR1cmUvdGV4dHVyZTJkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFckMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBRWhELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxxQkFBcUIsRUFBRSxlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFFdkYsT0FBTyxFQUFnQixZQUFZLEVBQTJDLE1BQU0sWUFBWSxDQUFBO0FBQ2hHLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUM1QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUFFLFdBQVcsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUNsRyxPQUFPLEVBQStCLHlCQUF5QixFQUFFLE1BQU0sWUFBWSxDQUFBO0FBQ25GLE9BQU8sRUFBRSxnQkFBZ0IsRUFBdUIsTUFBTSxTQUFTLENBQUE7QUE4Ri9ELE1BQU0sT0FBTyxZQUFZO0lBbUJyQixZQUNvQixPQUFtQixFQUNsQixVQUErQixFQUFFO1FBRGxDLFlBQU8sR0FBUCxPQUFPLENBQVk7UUFDbEIsWUFBTyxHQUFQLE9BQU8sQ0FBMEI7UUFsQnRDLGdCQUFXLEdBQUcsSUFBSSxRQUFRLEVBQWdCLENBQUE7UUFFbEQsYUFBUSxHQUF3QixJQUFJLENBQUE7UUFDcEMsV0FBTSxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQ1gsWUFBTyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBRVosV0FBTSxHQUEyQjtZQUNyQyxTQUFTLEVBQUUsUUFBUTtZQUNuQixTQUFTLEVBQUUsUUFBUTtZQUNuQixLQUFLLEVBQUUsUUFBUTtZQUNmLEtBQUssRUFBRSxRQUFRO1lBQ2YsS0FBSyxFQUFFLFFBQVE7U0FDbEIsQ0FBQTtRQUNPLGlCQUFZLEdBQTJCLElBQUksQ0FBQTtRQU8vQyxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUE7UUFDekMsTUFBTSxFQUFFLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQTtRQUN0QixJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQTtRQUNaLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxhQUFhLFlBQVksQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFBO1FBQ2pFLElBQUksQ0FBQyxPQUFPLEdBQUc7WUFDWCxLQUFLLEVBQUUsQ0FBQztZQUNSLE1BQU0sRUFBRSxDQUFDO1lBQ1QsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLElBQUksOEJBQThCO1lBQ3pELE1BQU0sRUFBRSxDQUFDO1lBQ1QsS0FBSyxFQUFFLEtBQUs7WUFDWixrQkFBa0IsRUFBRSxLQUFLO1lBQ3pCLEdBQUcsT0FBTztTQUNiLENBQUE7UUFDRCxNQUFNLEtBQUssR0FBRyxPQUFPLEVBQUUsS0FBSyxDQUFBO1FBQzVCLE1BQU0sTUFBTSxHQUFHLE9BQU8sRUFBRSxNQUFNLENBQUE7UUFDOUIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDakMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDMUQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUE7UUFDOUIsQ0FBQztRQUNELElBQUksTUFBTTtZQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDbEMsSUFBSSxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDdkMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUN6QixDQUFDO2FBQU0sSUFBSSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUNuQyxDQUFDO0lBQ0wsQ0FBQztJQUVELEtBQUssQ0FBQyxJQUFhO1FBQ2YsT0FBTyxJQUFJLFlBQVksQ0FDbkIsSUFBSSxDQUFDLE9BQU8sRUFDWixlQUFlLENBQUM7WUFDWixHQUFHLElBQUksQ0FBQyxPQUFPO1lBQ2YsSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUk7U0FDbEMsQ0FBQyxDQUNMLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO0lBQzFELENBQUM7SUFFRCxJQUFJLE1BQU07UUFDTixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFBO0lBQzlCLENBQUM7SUFDRCxJQUFZLE1BQU0sQ0FBQyxNQUFxQztRQUNwRCxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7SUFDaEMsQ0FBQztJQUVELElBQUksY0FBYztRQUNkLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDMUQsT0FBTyxjQUFjLENBQUE7SUFDekIsQ0FBQztJQUVELElBQUksWUFBWTtRQUNaLE1BQU0sQ0FBQyxFQUFFLFlBQVksQ0FBQyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUMxRCxPQUFPLFlBQVksQ0FBQTtJQUN2QixDQUFDO0lBRUQsSUFBSSxVQUFVO1FBQ1YsTUFBTSxDQUFDLEVBQUUsQUFBRCxFQUFHLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUMxRCxPQUFPLFVBQVUsQ0FBQTtJQUNyQixDQUFDO0lBRUQsTUFBTTtRQUNGLE1BQU0sRUFBRSxZQUFZLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDN0IsSUFBSSxZQUFZO1lBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7UUFDbkQsSUFBSSxJQUFJLENBQUMsUUFBUTtZQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQTtRQUN2RCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQTtJQUN4QixDQUFDO0lBRUQsSUFBSSxLQUFLO1FBQ0wsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFBO0lBQ3RCLENBQUM7SUFFRCxJQUFJLE1BQU07UUFDTixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUE7SUFDdkIsQ0FBQztJQUVPLGFBQWEsQ0FBQyxLQUF5QztRQUMzRCxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUE7UUFDYixNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1FBQ25CLE1BQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQyxhQUFhLEVBQUUsQ0FBQTtRQUNsQyxJQUFJLENBQUMsT0FBTztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsa0NBQWtDLENBQUMsQ0FBQTtRQUVqRSxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQTtRQUN2QixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7UUFDWCxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUMzQixNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzFELE1BQU0sQ0FBQyxjQUFjLEVBQUUsWUFBWSxFQUFFLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDdkUsRUFBRSxDQUFDLFVBQVUsQ0FDVCxFQUFFLENBQUMsVUFBVSxFQUFFLFNBQVM7UUFDeEIsQ0FBQyxFQUFFLGtCQUFrQjtRQUNyQixjQUFjLEVBQUUsa0JBQWtCO1FBQ2xDLENBQUMsRUFBRSxRQUFRO1FBQ1gsQ0FBQyxFQUFFLFNBQVM7UUFDWixDQUFDLEVBQUUsU0FBUztRQUNaLFlBQVksRUFBRSxnQkFBZ0I7UUFDOUIsVUFBVSxFQUFFLGNBQWM7UUFDMUIsVUFBVSxLQUFLLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FDOUUsQ0FBQTtRQUNELElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFBO1FBQ2YsSUFBSSxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUE7SUFDcEIsQ0FBQztJQUVPLFdBQVcsQ0FDZixFQUEwQjtRQUUxQixNQUFNLENBQUMsY0FBYyxFQUFFLFlBQVksRUFBRSxVQUFVLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUlqRixDQUFBO1FBQ0QsT0FBTyxDQUFDLEVBQUUsQ0FBQyxjQUFjLENBQVcsRUFBRSxFQUFFLENBQUMsWUFBWSxDQUFXLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBVyxDQUFDLENBQUE7SUFDL0YsQ0FBQztJQUVELE1BQU0sQ0FBQyxDQUFTLEVBQUUsQ0FBUztRQUN2QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzFCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDM0IsSUFBSSxLQUFLLEtBQUssSUFBSSxDQUFDLEtBQUssSUFBSSxNQUFNLEtBQUssSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFNO1FBRTFELElBQUksQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFBO0lBQ25ELENBQUM7SUFFTyxVQUFVLENBQUMsT0FBZSxFQUFFLE1BQW1CO1FBQ25ELE1BQU0sRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDbkIsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFBO1FBQzNCLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0JBQWtCLElBQUksQ0FBQyxJQUFJLGNBQWMsT0FBTyxHQUFHLEVBQUUsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFBO1lBQ3pHLE1BQU0sRUFBRSxFQUFFLENBQUE7UUFDZCxDQUFDO0lBQ0wsQ0FBQztJQUVELElBQUksU0FBUztRQUNULElBQUksSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUE7UUFFdkMsTUFBTSxLQUFLLEdBQUcsMkJBQTJCLElBQUksQ0FBQyxJQUFJLHFCQUFxQixDQUFBO1FBQ3ZFLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUNqQyxNQUFNLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQzFCLENBQUM7SUFFRCxJQUFJO1FBQ0EsSUFBSSxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQzNELENBQUM7SUFFRCxNQUFNO1FBQ0YsSUFBSSxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUE7SUFDakQsQ0FBQztJQUVELFNBQVMsQ0FDTCxHQUFXLEVBQ1gsVUFFSSxFQUFFO1FBRU4sSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFBO1FBQ25CLElBQUksVUFBVSxHQUFHLEtBQUssQ0FBQTtRQUN0QixNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBQzdDLElBQUksV0FBVyxHQUFHLEtBQUssQ0FBQTtRQUN2QixNQUFNLFVBQVUsR0FBRyxHQUFHLEVBQUU7WUFDcEIsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLFVBQVUsSUFBSSxXQUFXO2dCQUFFLE9BQU07WUFFbEQsV0FBVyxHQUFHLElBQUksQ0FBQTtZQUNsQixJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQTtZQUMvQixNQUFNLFlBQVksR0FBRyxJQUFJLGVBQWUsQ0FBQyxHQUFHLEVBQUU7Z0JBQzFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDO29CQUNoQyxJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFBO2dCQUMxQixDQUFDO1lBQ0wsQ0FBQyxDQUFDLENBQUE7WUFDRixJQUFJLElBQUksQ0FBQyxZQUFZO2dCQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQTtZQUM3RCxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQTtZQUM5QixJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQTtRQUNwQyxDQUFDLENBQUE7UUFDRCxLQUFLLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQTtRQUN4QixLQUFLLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQTtRQUNsQixLQUFLLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQTtRQUNqQixLQUFLLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRTtZQUNuQyxPQUFPLEdBQUcsSUFBSSxDQUFBO1lBQ2QsVUFBVSxFQUFFLENBQUE7UUFDaEIsQ0FBQyxDQUFDLENBQUE7UUFDRixLQUFLLENBQUMsZ0JBQWdCLENBQUMsWUFBWSxFQUFFLEdBQUcsRUFBRTtZQUN0QyxVQUFVLEdBQUcsSUFBSSxDQUFBO1lBQ2pCLFVBQVUsRUFBRSxDQUFBO1FBQ2hCLENBQUMsQ0FBQyxDQUFBO1FBQ0YsS0FBSyxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUE7UUFDZixLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRTtZQUNwQixpQ0FBaUM7WUFDakMsbUdBQW1HO1FBQ3ZHLENBQUMsQ0FBQyxDQUFBO1FBQ0YsT0FBTyxJQUFJLENBQUE7SUFDZixDQUFDO0lBRUQsVUFBVSxDQUNOLEdBQXdFLEVBQ3hFLFVBSUksRUFBRTtRQUVOLElBQUksQ0FBQyxHQUFHO1lBQUUsT0FBTyxJQUFJLENBQUE7UUFFckIsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUMxQixJQUFJLFVBQVUsQ0FBQyxHQUFHLENBQUM7Z0JBQUUsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQTtZQUN2RSxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFBO1FBQ3ZDLENBQUM7UUFFRCxJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDckIsb0JBQW9CO1lBQ3BCLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtnQkFDZCxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFBO1lBQ3pCLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUE7WUFDckYsT0FBTyxJQUFJLENBQUE7UUFDZixDQUFDO1FBRUQsSUFBSSxHQUFHLFlBQVksZ0JBQWdCLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNoQixHQUFHLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUE7Z0JBQ2pFLE9BQU8sSUFBSSxDQUFBO1lBQ2YsQ0FBQztRQUNMLENBQUM7YUFBTSxJQUFJLEdBQUcsWUFBWSxpQkFBaUIsRUFBRSxDQUFDO1lBQzFDLElBQUksR0FBRyxDQUFDLEtBQUssSUFBSSxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDcEMsT0FBTyxJQUFJLENBQUE7WUFDZixDQUFDO1FBQ0wsQ0FBQztRQUNELE1BQU0sRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1FBQzVCLE1BQU0sRUFBRSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFBO1FBQzdCLElBQUksQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQTtRQUN2QixJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUE7UUFDekIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFBO1FBQ1gsRUFBRSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUMxRCxFQUFFLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyw4QkFBOEIsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUE7UUFDbEYsTUFBTSxDQUFDLGNBQWMsRUFBRSxZQUFZLEVBQUUsVUFBVSxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUN2RSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsS0FBSyxFQUFFLGNBQWMsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFBO1FBQ2xGLElBQUksQ0FBQyxVQUFVLENBQ1gsY0FBYyxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3pCLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSztZQUNoQixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07U0FDckIsQ0FBQyxtQ0FBbUMsS0FBSyxRQUN0QyxNQUFNLENBQUMseUJBQXlCO1FBQ3BDLFFBQVEsTUFBTSwwQkFBMEIsRUFDeEMsR0FBRyxFQUFFO1lBQ0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQSxDQUFDLDBEQUEwRDtZQUNqRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtZQUMvRCxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsOEJBQThCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFBO1FBQzdGLENBQUMsQ0FDSixDQUFBO1FBQ0QsSUFBSSxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFBO1lBQ3JCLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUE7WUFDeEIsSUFBSSxDQUFDLFVBQVUsQ0FDWCxjQUFjLElBQUksQ0FBQyxTQUFTLENBQUM7Z0JBQ3pCLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSztnQkFDaEIsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNO2FBQ3JCLENBQUMsbUNBQW1DLEtBQUssUUFBUSxPQUFPLENBQUMsbUJBQW1CLENBQ3pFLGNBQWMsQ0FDakIsUUFBUSxPQUFPLENBQUMsbUJBQW1CLENBQUMsWUFBWSxDQUFDLFFBQVEsT0FBTyxDQUFDLG1CQUFtQixDQUNqRixVQUFVLENBQ2IsMEJBQTBCLENBQzlCLENBQUE7UUFDTCxDQUFDO1FBQ0QsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUE7UUFDL0IsSUFBSSxNQUFNO1lBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUNwQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUE7UUFDYixPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQTtRQUNsQixJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUMvQixPQUFPLElBQUksQ0FBQTtJQUNmLENBQUM7SUFFRCxXQUFXLENBQUMsTUFBaUI7UUFDekIsSUFBSSxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3BDLE1BQU0sSUFBSSxLQUFLLENBQ1gsdURBQXVELElBQUksQ0FBQyxLQUFLLEtBQUssSUFBSSxDQUFDLE1BQU0seUNBQXlDLENBQzdILENBQUE7UUFDTCxDQUFDO1FBQ0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxZQUFZLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUMxQyxNQUFNLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDMUIsT0FBTyxFQUFFO2dCQUNMLEdBQUcsSUFBSSxDQUFDLE9BQU87Z0JBQ2YsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO2dCQUNqQixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07YUFDdEI7U0FDSixDQUFDLENBQUE7UUFDRixNQUFNLE9BQU8sR0FBRyxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDL0MsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDO1lBQ2pCLEtBQUssRUFBRSxJQUFJO1lBQ1gsT0FBTyxFQUFFLElBQUk7U0FDaEIsQ0FBQyxDQUFBO1FBQ0YsTUFBTSxXQUFXLEdBQUcsSUFBSSxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ3hELGFBQWEsRUFBRSxNQUFNO1lBQ3JCLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLFFBQVEsRUFBRSxDQUFDLE9BQU8sQ0FBQztZQUNuQixTQUFTLEVBQUUsSUFBSTtTQUNsQixDQUFDLENBQUE7UUFDRixXQUFXLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQTtRQUN2QixJQUFJLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUE7UUFDcEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFBO1FBQ2hDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQTtRQUNYLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQzNCLE9BQU8sSUFBSSxDQUFBO0lBQ2YsQ0FBQztJQUVELFFBQVEsQ0FDSixJQUFtRCxFQUNuRCxPQU1DO1FBRUQsSUFBSSxDQUFDO1lBQ0QsTUFBTSxFQUNGLEtBQUssR0FBRyxDQUFDLEVBQ1QsS0FBSyxFQUNMLE1BQU0sRUFDTixNQUFNO1lBQ04sY0FBYztjQUNqQixHQUFHLE9BQU8sQ0FBQTtZQUNYLE1BQU0sRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFBO1lBQzVCLElBQUksTUFBTSxFQUFFLENBQUM7Z0JBQ1QsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7WUFDeEIsQ0FBQztZQUNELGtDQUFrQyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQTtZQUNwRSxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQTtZQUNuQixJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQTtZQUNyQixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7WUFDWCxFQUFFLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQzFELEVBQUUsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLDhCQUE4QixFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtZQUNsRixNQUFNLENBQUMsY0FBYyxFQUFFLFlBQVksRUFBRSxVQUFVLENBQUMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3ZFLElBQUksVUFBVSxLQUFLLEVBQUUsQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFDLElBQUksWUFBWSxZQUFZLENBQUMsRUFBRSxDQUFDO2dCQUM3RCxNQUFNLElBQUksS0FBSyxDQUFDLGtEQUFrRCxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQTtZQUNyRixDQUFDO1lBQ0QsRUFBRSxDQUFDLFVBQVUsQ0FDVCxFQUFFLENBQUMsVUFBVSxFQUNiLEtBQUssRUFDTCxjQUFjLEVBQ2QsS0FBSyxFQUNMLE1BQU0sRUFDTixDQUFDLEVBQ0QsWUFBWSxFQUNaLFVBQVUsRUFDVixJQUFJLENBRVAsQ0FBQTtZQUNELElBQUksQ0FBQyxVQUFVLENBQUMsWUFBWSxVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUM7a0JBQ2xFLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLENBQUM7Z0JBQzdDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxZQUFZLENBQUM7Y0FDM0MsT0FBTyxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQztDQUNwRCxDQUFDLENBQUE7WUFDVSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUE7WUFDYixJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQTtZQUMvQixPQUFPLElBQUksQ0FBQTtRQUNmLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLGlCQUFpQixtQkFBbUIsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDL0UsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILFFBQVEsQ0FBQyxJQUFZLEVBQUUsT0FBb0IsRUFBRSxXQUFvQjtRQUM3RCxNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1FBQ25CLEVBQUUsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsQ0FBQTtRQUNwQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7UUFDWCxJQUFJLE9BQU8sSUFBSSxXQUFXLEVBQUUsQ0FBQztZQUN6QixPQUFPLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUN4QyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUE7SUFDZixDQUFDO0lBRUQsY0FBYztRQUNWLE1BQU0sRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDbkIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFBO1FBQ1gsRUFBRSxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUE7UUFDaEMsT0FBTyxJQUFJLENBQUE7SUFDZixDQUFDO0lBRUQsU0FBUyxDQUFDLFVBQWtDO1FBQ3hDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQTtRQUNYLHlCQUF5QixDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsVUFBVSxDQUFDLENBQUE7UUFDOUMsSUFBSSxDQUFDLE1BQU0sR0FBRztZQUNWLEdBQUcsSUFBSSxDQUFDLE1BQU07WUFDZCxHQUFHLFVBQVU7U0FDaEIsQ0FBQTtRQUNELE9BQU8sSUFBSSxDQUFBO0lBQ2YsQ0FBQztJQUVELElBQUksZ0JBQWdCLENBQUMsS0FBYTtRQUM5QixNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1FBQ25CLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQTtRQUNYLEVBQUUsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsa0JBQWtCLEVBQUUsS0FBSyxDQUFDLENBQUE7SUFDakUsQ0FBQztJQUVELElBQUksZ0JBQWdCO1FBQ2hCLE1BQU0sRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDbkIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFBO1FBQ1gsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBVyxDQUFBO1FBQ2hGLE9BQU8sS0FBSyxDQUFBO0lBQ2hCLENBQUM7SUFFRCxJQUFJLGVBQWUsQ0FBQyxLQUFhO1FBQzdCLE1BQU0sRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDbkIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFBO1FBQ1gsRUFBRSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxLQUFLLENBQUMsQ0FBQTtJQUNoRSxDQUFDO0lBRUQsSUFBSSxlQUFlO1FBQ2YsTUFBTSxFQUFFLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQTtRQUNuQixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7UUFDWCxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLGlCQUFpQixDQUFXLENBQUE7UUFDL0UsT0FBTyxLQUFLLENBQUE7SUFDaEIsQ0FBQztJQUVELFlBQVksQ0FBQyxTQUE0QjtRQUNyQyxNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFBO1FBQ25CLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQTtRQUNYLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLENBQTRCLENBQUE7UUFDekYsT0FBTyxLQUFLLENBQUE7SUFDaEIsQ0FBQztJQUVELEtBQUssQ0FBQyxLQUFjO1FBQ2hCLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxJQUFJLElBQUksY0FBYyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDdkYsTUFBTSxVQUFVLEdBQXdCO1lBQ3BDLG9CQUFvQjtZQUNwQixvQkFBb0I7WUFDcEIsZ0JBQWdCO1lBQ2hCLGdCQUFnQjtZQUNoQixnQkFBZ0I7WUFDaEIsbUJBQW1CO1lBQ25CLGlCQUFpQjtZQUNqQixpQkFBaUI7WUFDakIsb0JBQW9CO1lBQ3BCLHNCQUFzQjtZQUN0QixzQkFBc0I7WUFDdEIsMEJBQTBCO1lBQzFCLDBCQUEwQjtTQUM3QixDQUFBO1FBQ0QsS0FBSyxNQUFNLElBQUksSUFBSSxVQUFVLEVBQUUsQ0FBQztZQUM1QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFBO1lBQ3JDLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxXQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDckcsQ0FBQztJQUNMLENBQUM7O0FBL2JjLG9CQUFPLEdBQUcsQ0FBQyxBQUFKLENBQUk7QUFrYzlCLFNBQVMsVUFBVSxDQUFDLElBQXFGO0lBQ3JHLElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNuQixPQUFPLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLElBQUksQ0FBQyxNQUFNLFlBQVksQ0FBQTtJQUM3RSxDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFBO0FBQy9CLENBQUM7QUFFRCxTQUFTLFVBQVUsQ0FBQyxHQUFXO0lBQzNCLE1BQU0sVUFBVSxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUN0RSxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUE7SUFDN0MsS0FBSyxNQUFNLEdBQUcsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUMzQixJQUFJLFlBQVksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUE7SUFDL0MsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFBO0FBQ2hCLENBQUM7QUFFRCxTQUFTLHFCQUFxQixDQUFDLE1BQXFDLEVBQUUsS0FBYTtJQUMvRSxNQUFNLENBQUMsRUFBRSxZQUFZLEVBQUUsVUFBVSxDQUFDLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN4RCxNQUFNLGNBQWMsR0FBMkI7UUFDM0MsR0FBRyxFQUFFLENBQUM7UUFDTixXQUFXLEVBQUUsQ0FBQztRQUNkLEVBQUUsRUFBRSxDQUFDO1FBQ0wsVUFBVSxFQUFFLENBQUM7UUFDYixHQUFHLEVBQUUsQ0FBQztRQUNOLFdBQVcsRUFBRSxDQUFDO1FBQ2QsSUFBSSxFQUFFLENBQUM7UUFDUCxZQUFZLEVBQUUsQ0FBQztRQUNmLGVBQWUsRUFBRSxDQUFDO1FBQ2xCLGFBQWEsRUFBRSxDQUFDO0tBQ25CLENBQUE7SUFDRCxNQUFNLFNBQVMsR0FBMkI7UUFDdEMsYUFBYSxFQUFFLENBQUM7UUFDaEIsSUFBSSxFQUFFLENBQUM7UUFDUCxjQUFjLEVBQUUsQ0FBQztRQUNqQixLQUFLLEVBQUUsQ0FBQztRQUNSLFVBQVUsRUFBRSxDQUFDO1FBQ2IsWUFBWSxFQUFFLENBQUM7UUFDZixHQUFHLEVBQUUsQ0FBQztRQUNOLEtBQUssRUFBRSxDQUFDO1FBQ1Isb0JBQW9CLEVBQUUsQ0FBQztRQUN2QixzQkFBc0IsRUFBRSxDQUFDO1FBQ3pCLHNCQUFzQixFQUFFLENBQUM7UUFDekIsMkJBQTJCLEVBQUUsQ0FBQztRQUM5Qiw0QkFBNEIsRUFBRSxDQUFDO1FBQy9CLHdCQUF3QixFQUFFLENBQUM7UUFDM0IsaUJBQWlCLEVBQUUsQ0FBQztRQUNwQiw4QkFBOEIsRUFBRSxDQUFDO0tBQ3BDLENBQUE7SUFDRCxNQUFNLE1BQU0sR0FDUixTQUFTLENBQUMsVUFBVSxDQUFDLEtBQUssU0FBUztRQUNuQyxVQUFVLEtBQUssZUFBZTtRQUM5QixVQUFVLEtBQUssTUFBTTtRQUNyQixVQUFVLEtBQUssZ0JBQWdCO1FBQy9CLFVBQVUsS0FBSyxPQUFPO1FBQ3RCLFVBQVUsS0FBSyxjQUFjO1FBQzdCLFVBQVUsS0FBSyxLQUFLO1FBQ3BCLFVBQVUsS0FBSyxZQUFZO1FBQzNCLFVBQVUsS0FBSyxPQUFPLENBQUE7SUFDMUIsTUFBTSxhQUFhLEdBQUcsTUFBTTtRQUN4QixDQUFDLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQztRQUN2QixDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7SUFDeEUsTUFBTSxRQUFRLEdBQUcsS0FBSyxHQUFHLGFBQWEsQ0FBQTtJQUN0QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQTtBQUN0QyxDQUFDO0FBRUQsU0FBUyxrQ0FBa0MsQ0FDdkMsSUFBc0csRUFDdEcsTUFBcUMsRUFDckMsS0FBYSxFQUNiLE1BQWM7SUFFZCxNQUFNLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQTtJQUMvRCxNQUFNLFlBQVksR0FBRyxrQkFBa0IsR0FBRyxNQUFNLENBQUE7SUFDaEQsSUFBSSxJQUFJLENBQUMsVUFBVSxHQUFHLFlBQVksRUFBRSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSxLQUFLLENBQUMsb0JBQW9CLEtBQUssSUFBSSxNQUFNLGlCQUFpQixNQUFNLDRCQUE0QixrQkFBa0IsSUFBSSxNQUFNLE1BQU0sWUFBWTt5QkFDbkksSUFBSSxDQUFDLFVBQVU7a0NBQ04sQ0FBQyxDQUFBO0lBQy9CLENBQUM7QUFDTCxDQUFDIn0=