@push.rocks/smartproxy 13.1.2 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -3
  3. package/dist_ts/proxies/smart-proxy/index.js +9 -5
  4. package/dist_ts/proxies/smart-proxy/models/index.d.ts +2 -0
  5. package/dist_ts/proxies/smart-proxy/models/index.js +2 -1
  6. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +82 -15
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.js +10 -1
  8. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +133 -0
  9. package/dist_ts/proxies/smart-proxy/models/route-types.js +2 -0
  10. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +55 -0
  11. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +804 -0
  12. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +127 -0
  13. package/dist_ts/proxies/smart-proxy/route-helpers.js +196 -0
  14. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +103 -0
  15. package/dist_ts/proxies/smart-proxy/route-manager.js +483 -0
  16. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -8
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.js +239 -46
  18. package/package.json +2 -2
  19. package/readme.md +863 -423
  20. package/readme.plan.md +311 -250
  21. package/ts/00_commitinfo_data.ts +2 -2
  22. package/ts/proxies/smart-proxy/index.ts +20 -4
  23. package/ts/proxies/smart-proxy/models/index.ts +4 -0
  24. package/ts/proxies/smart-proxy/models/interfaces.ts +91 -13
  25. package/ts/proxies/smart-proxy/models/route-types.ts +184 -0
  26. package/ts/proxies/smart-proxy/route-connection-handler.ts +1117 -0
  27. package/ts/proxies/smart-proxy/route-helpers.ts +344 -0
  28. package/ts/proxies/smart-proxy/route-manager.ts +587 -0
  29. package/ts/proxies/smart-proxy/smart-proxy.ts +300 -69
@@ -0,0 +1,804 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { isRoutedOptions, isLegacyOptions } from './models/interfaces.js';
3
+ import { ConnectionManager } from './connection-manager.js';
4
+ import { SecurityManager } from './security-manager.js';
5
+ import { DomainConfigManager } from './domain-config-manager.js';
6
+ import { TlsManager } from './tls-manager.js';
7
+ import { NetworkProxyBridge } from './network-proxy-bridge.js';
8
+ import { TimeoutManager } from './timeout-manager.js';
9
+ import { RouteManager } from './route-manager.js';
10
+ /**
11
+ * Handles new connection processing and setup logic with support for route-based configuration
12
+ */
13
+ export class RouteConnectionHandler {
14
+ constructor(settings, connectionManager, securityManager, domainConfigManager, tlsManager, networkProxyBridge, timeoutManager, routeManager) {
15
+ this.connectionManager = connectionManager;
16
+ this.securityManager = securityManager;
17
+ this.domainConfigManager = domainConfigManager;
18
+ this.tlsManager = tlsManager;
19
+ this.networkProxyBridge = networkProxyBridge;
20
+ this.timeoutManager = timeoutManager;
21
+ this.routeManager = routeManager;
22
+ this.settings = settings;
23
+ }
24
+ /**
25
+ * Handle a new incoming connection
26
+ */
27
+ handleConnection(socket) {
28
+ const remoteIP = socket.remoteAddress || '';
29
+ const localPort = socket.localPort || 0;
30
+ // Validate IP against rate limits and connection limits
31
+ const ipValidation = this.securityManager.validateIP(remoteIP);
32
+ if (!ipValidation.allowed) {
33
+ console.log(`Connection rejected from ${remoteIP}: ${ipValidation.reason}`);
34
+ socket.end();
35
+ socket.destroy();
36
+ return;
37
+ }
38
+ // Create a new connection record
39
+ const record = this.connectionManager.createConnection(socket);
40
+ const connectionId = record.id;
41
+ // Apply socket optimizations
42
+ socket.setNoDelay(this.settings.noDelay);
43
+ // Apply keep-alive settings if enabled
44
+ if (this.settings.keepAlive) {
45
+ socket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
46
+ record.hasKeepAlive = true;
47
+ // Apply enhanced TCP keep-alive options if enabled
48
+ if (this.settings.enableKeepAliveProbes) {
49
+ try {
50
+ // These are platform-specific and may not be available
51
+ if ('setKeepAliveProbes' in socket) {
52
+ socket.setKeepAliveProbes(10);
53
+ }
54
+ if ('setKeepAliveInterval' in socket) {
55
+ socket.setKeepAliveInterval(1000);
56
+ }
57
+ }
58
+ catch (err) {
59
+ // Ignore errors - these are optional enhancements
60
+ if (this.settings.enableDetailedLogging) {
61
+ console.log(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ if (this.settings.enableDetailedLogging) {
67
+ console.log(`[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` +
68
+ `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
69
+ `Active connections: ${this.connectionManager.getConnectionCount()}`);
70
+ }
71
+ else {
72
+ console.log(`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`);
73
+ }
74
+ // Start TLS SNI handling
75
+ this.handleTlsConnection(socket, record);
76
+ }
77
+ /**
78
+ * Handle a connection and wait for TLS handshake for SNI extraction if needed
79
+ */
80
+ handleTlsConnection(socket, record) {
81
+ const connectionId = record.id;
82
+ const localPort = record.localPort;
83
+ let initialDataReceived = false;
84
+ // Set an initial timeout for handshake data
85
+ let initialTimeout = setTimeout(() => {
86
+ if (!initialDataReceived) {
87
+ console.log(`[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}`);
88
+ // Add a grace period
89
+ setTimeout(() => {
90
+ if (!initialDataReceived) {
91
+ console.log(`[${connectionId}] Final initial data timeout after grace period`);
92
+ if (record.incomingTerminationReason === null) {
93
+ record.incomingTerminationReason = 'initial_timeout';
94
+ this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
95
+ }
96
+ socket.end();
97
+ this.connectionManager.cleanupConnection(record, 'initial_timeout');
98
+ }
99
+ }, 30000);
100
+ }
101
+ }, this.settings.initialDataTimeout);
102
+ // Make sure timeout doesn't keep the process alive
103
+ if (initialTimeout.unref) {
104
+ initialTimeout.unref();
105
+ }
106
+ // Set up error handler
107
+ socket.on('error', this.connectionManager.handleError('incoming', record));
108
+ // First data handler to capture initial TLS handshake
109
+ socket.once('data', (chunk) => {
110
+ // Clear the initial timeout since we've received data
111
+ if (initialTimeout) {
112
+ clearTimeout(initialTimeout);
113
+ initialTimeout = null;
114
+ }
115
+ initialDataReceived = true;
116
+ record.hasReceivedInitialData = true;
117
+ // Block non-TLS connections on port 443
118
+ if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
119
+ console.log(`[${connectionId}] Non-TLS connection detected on port 443. ` +
120
+ `Terminating connection - only TLS traffic is allowed on standard HTTPS port.`);
121
+ if (record.incomingTerminationReason === null) {
122
+ record.incomingTerminationReason = 'non_tls_blocked';
123
+ this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
124
+ }
125
+ socket.end();
126
+ this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
127
+ return;
128
+ }
129
+ // Check if this looks like a TLS handshake
130
+ let serverName = '';
131
+ if (this.tlsManager.isTlsHandshake(chunk)) {
132
+ record.isTLS = true;
133
+ // Check for ClientHello to extract SNI
134
+ if (this.tlsManager.isClientHello(chunk)) {
135
+ // Create connection info for SNI extraction
136
+ const connInfo = {
137
+ sourceIp: record.remoteIP,
138
+ sourcePort: socket.remotePort || 0,
139
+ destIp: socket.localAddress || '',
140
+ destPort: socket.localPort || 0,
141
+ };
142
+ // Extract SNI
143
+ serverName = this.tlsManager.extractSNI(chunk, connInfo) || '';
144
+ // Lock the connection to the negotiated SNI
145
+ record.lockedDomain = serverName;
146
+ // Check if we should reject connections without SNI
147
+ if (!serverName && this.settings.allowSessionTicket === false) {
148
+ console.log(`[${connectionId}] No SNI detected in TLS ClientHello; sending TLS alert.`);
149
+ if (record.incomingTerminationReason === null) {
150
+ record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
151
+ this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
152
+ }
153
+ const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
154
+ try {
155
+ socket.cork();
156
+ socket.write(alert);
157
+ socket.uncork();
158
+ socket.end();
159
+ }
160
+ catch {
161
+ socket.end();
162
+ }
163
+ this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
164
+ return;
165
+ }
166
+ if (this.settings.enableDetailedLogging) {
167
+ console.log(`[${connectionId}] TLS connection with SNI: ${serverName || '(empty)'}`);
168
+ }
169
+ }
170
+ }
171
+ // Find the appropriate route for this connection
172
+ this.routeConnection(socket, record, serverName, chunk);
173
+ });
174
+ }
175
+ /**
176
+ * Route the connection based on match criteria
177
+ */
178
+ routeConnection(socket, record, serverName, initialChunk) {
179
+ const connectionId = record.id;
180
+ const localPort = record.localPort;
181
+ const remoteIP = record.remoteIP;
182
+ // Find matching route
183
+ const routeMatch = this.routeManager.findMatchingRoute({
184
+ port: localPort,
185
+ domain: serverName,
186
+ clientIp: remoteIP,
187
+ path: undefined, // We don't have path info at this point
188
+ tlsVersion: undefined // We don't extract TLS version yet
189
+ });
190
+ if (!routeMatch) {
191
+ console.log(`[${connectionId}] No route found for ${serverName || 'connection'} on port ${localPort}`);
192
+ // Fall back to legacy matching if we're using a hybrid configuration
193
+ const domainConfig = serverName
194
+ ? this.domainConfigManager.findDomainConfig(serverName)
195
+ : this.domainConfigManager.findDomainConfigForPort(localPort);
196
+ if (domainConfig) {
197
+ if (this.settings.enableDetailedLogging) {
198
+ console.log(`[${connectionId}] Using legacy domain configuration for ${serverName || 'port ' + localPort}`);
199
+ }
200
+ // Associate this domain config with the connection
201
+ record.domainConfig = domainConfig;
202
+ // Handle the connection using the legacy setup
203
+ return this.handleLegacyConnection(socket, record, serverName, domainConfig, initialChunk);
204
+ }
205
+ // No matching route or domain config, use default/fallback handling
206
+ console.log(`[${connectionId}] Using default route handling for connection`);
207
+ // Check default security settings
208
+ const defaultSecuritySettings = this.settings.defaults?.security;
209
+ if (defaultSecuritySettings) {
210
+ if (defaultSecuritySettings.allowedIPs && defaultSecuritySettings.allowedIPs.length > 0) {
211
+ const isAllowed = this.securityManager.isIPAuthorized(remoteIP, defaultSecuritySettings.allowedIPs, defaultSecuritySettings.blockedIPs || []);
212
+ if (!isAllowed) {
213
+ console.log(`[${connectionId}] IP ${remoteIP} not in default allowed list`);
214
+ socket.end();
215
+ this.connectionManager.cleanupConnection(record, 'ip_blocked');
216
+ return;
217
+ }
218
+ }
219
+ }
220
+ else if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
221
+ // Legacy default IP restrictions
222
+ const isAllowed = this.securityManager.isIPAuthorized(remoteIP, this.settings.defaultAllowedIPs, this.settings.defaultBlockedIPs || []);
223
+ if (!isAllowed) {
224
+ console.log(`[${connectionId}] IP ${remoteIP} not in default allowed list`);
225
+ socket.end();
226
+ this.connectionManager.cleanupConnection(record, 'ip_blocked');
227
+ return;
228
+ }
229
+ }
230
+ // Setup direct connection with default settings
231
+ let targetHost;
232
+ let targetPort;
233
+ if (isRoutedOptions(this.settings) && this.settings.defaults?.target) {
234
+ // Use defaults from routed configuration
235
+ targetHost = this.settings.defaults.target.host;
236
+ targetPort = this.settings.defaults.target.port;
237
+ }
238
+ else {
239
+ // Fall back to legacy settings
240
+ targetHost = this.settings.targetIP || 'localhost';
241
+ targetPort = this.settings.toPort;
242
+ }
243
+ return this.setupDirectConnection(socket, record, undefined, serverName, initialChunk, undefined, targetHost, targetPort);
244
+ }
245
+ // A matching route was found
246
+ const route = routeMatch.route;
247
+ if (this.settings.enableDetailedLogging) {
248
+ console.log(`[${connectionId}] Route matched: "${route.name || 'unnamed'}" for ${serverName || 'connection'} on port ${localPort}`);
249
+ }
250
+ // Handle the route based on its action type
251
+ switch (route.action.type) {
252
+ case 'forward':
253
+ return this.handleForwardAction(socket, record, route, initialChunk);
254
+ case 'redirect':
255
+ return this.handleRedirectAction(socket, record, route);
256
+ case 'block':
257
+ return this.handleBlockAction(socket, record, route);
258
+ default:
259
+ console.log(`[${connectionId}] Unknown action type: ${route.action.type}`);
260
+ socket.end();
261
+ this.connectionManager.cleanupConnection(record, 'unknown_action');
262
+ }
263
+ }
264
+ /**
265
+ * Handle a forward action for a route
266
+ */
267
+ handleForwardAction(socket, record, route, initialChunk) {
268
+ const connectionId = record.id;
269
+ const action = route.action;
270
+ // We should have a target configuration for forwarding
271
+ if (!action.target) {
272
+ console.log(`[${connectionId}] Forward action missing target configuration`);
273
+ socket.end();
274
+ this.connectionManager.cleanupConnection(record, 'missing_target');
275
+ return;
276
+ }
277
+ // Determine if this needs TLS handling
278
+ if (action.tls) {
279
+ switch (action.tls.mode) {
280
+ case 'passthrough':
281
+ // For TLS passthrough, just forward directly
282
+ if (this.settings.enableDetailedLogging) {
283
+ console.log(`[${connectionId}] Using TLS passthrough to ${action.target.host}`);
284
+ }
285
+ // Allow for array of hosts
286
+ const targetHost = Array.isArray(action.target.host)
287
+ ? action.target.host[Math.floor(Math.random() * action.target.host.length)]
288
+ : action.target.host;
289
+ // Determine target port - either target port or preserve incoming port
290
+ const targetPort = action.target.preservePort ? record.localPort : action.target.port;
291
+ return this.setupDirectConnection(socket, record, undefined, record.lockedDomain, initialChunk, undefined, targetHost, targetPort);
292
+ case 'terminate':
293
+ case 'terminate-and-reencrypt':
294
+ // For TLS termination, use NetworkProxy
295
+ if (this.networkProxyBridge.getNetworkProxy()) {
296
+ if (this.settings.enableDetailedLogging) {
297
+ console.log(`[${connectionId}] Using NetworkProxy for TLS termination to ${action.target.host}`);
298
+ }
299
+ // If we have an initial chunk with TLS data, start processing it
300
+ if (initialChunk && record.isTLS) {
301
+ return this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, initialChunk, this.settings.networkProxyPort, (reason) => this.connectionManager.initiateCleanupOnce(record, reason));
302
+ }
303
+ // This shouldn't normally happen - we should have TLS data at this point
304
+ console.log(`[${connectionId}] TLS termination route without TLS data`);
305
+ socket.end();
306
+ this.connectionManager.cleanupConnection(record, 'tls_error');
307
+ return;
308
+ }
309
+ else {
310
+ console.log(`[${connectionId}] NetworkProxy not available for TLS termination`);
311
+ socket.end();
312
+ this.connectionManager.cleanupConnection(record, 'no_network_proxy');
313
+ return;
314
+ }
315
+ }
316
+ }
317
+ else {
318
+ // No TLS settings - basic forwarding
319
+ if (this.settings.enableDetailedLogging) {
320
+ console.log(`[${connectionId}] Using basic forwarding to ${action.target.host}:${action.target.port}`);
321
+ }
322
+ // Allow for array of hosts
323
+ const targetHost = Array.isArray(action.target.host)
324
+ ? action.target.host[Math.floor(Math.random() * action.target.host.length)]
325
+ : action.target.host;
326
+ // Determine target port - either target port or preserve incoming port
327
+ const targetPort = action.target.preservePort ? record.localPort : action.target.port;
328
+ return this.setupDirectConnection(socket, record, undefined, record.lockedDomain, initialChunk, undefined, targetHost, targetPort);
329
+ }
330
+ }
331
+ /**
332
+ * Handle a redirect action for a route
333
+ */
334
+ handleRedirectAction(socket, record, route) {
335
+ const connectionId = record.id;
336
+ const action = route.action;
337
+ // We should have a redirect configuration
338
+ if (!action.redirect) {
339
+ console.log(`[${connectionId}] Redirect action missing redirect configuration`);
340
+ socket.end();
341
+ this.connectionManager.cleanupConnection(record, 'missing_redirect');
342
+ return;
343
+ }
344
+ // For TLS connections, we can't do redirects at the TCP level
345
+ if (record.isTLS) {
346
+ console.log(`[${connectionId}] Cannot redirect TLS connection at TCP level`);
347
+ socket.end();
348
+ this.connectionManager.cleanupConnection(record, 'tls_redirect_error');
349
+ return;
350
+ }
351
+ // Wait for the first HTTP request to perform the redirect
352
+ const dataListeners = [];
353
+ const httpDataHandler = (chunk) => {
354
+ // Remove all data listeners to avoid duplicated processing
355
+ for (const listener of dataListeners) {
356
+ socket.removeListener('data', listener);
357
+ }
358
+ // Parse HTTP request to get path
359
+ try {
360
+ const headersEnd = chunk.indexOf('\r\n\r\n');
361
+ if (headersEnd === -1) {
362
+ // Not a complete HTTP request, need more data
363
+ socket.once('data', httpDataHandler);
364
+ dataListeners.push(httpDataHandler);
365
+ return;
366
+ }
367
+ const httpHeaders = chunk.slice(0, headersEnd).toString();
368
+ const requestLine = httpHeaders.split('\r\n')[0];
369
+ const [method, path] = requestLine.split(' ');
370
+ // Extract Host header
371
+ const hostMatch = httpHeaders.match(/Host: (.+?)(\r\n|\r|\n|$)/i);
372
+ const host = hostMatch ? hostMatch[1].trim() : record.lockedDomain || '';
373
+ // Process the redirect URL with template variables
374
+ let redirectUrl = action.redirect.to;
375
+ redirectUrl = redirectUrl.replace(/\{domain\}/g, host);
376
+ redirectUrl = redirectUrl.replace(/\{path\}/g, path || '');
377
+ redirectUrl = redirectUrl.replace(/\{port\}/g, record.localPort.toString());
378
+ // Prepare the HTTP redirect response
379
+ const redirectResponse = [
380
+ `HTTP/1.1 ${action.redirect.status} Moved`,
381
+ `Location: ${redirectUrl}`,
382
+ 'Connection: close',
383
+ 'Content-Length: 0',
384
+ '',
385
+ ''
386
+ ].join('\r\n');
387
+ if (this.settings.enableDetailedLogging) {
388
+ console.log(`[${connectionId}] Redirecting to ${redirectUrl} with status ${action.redirect.status}`);
389
+ }
390
+ // Send the redirect response
391
+ socket.end(redirectResponse);
392
+ this.connectionManager.initiateCleanupOnce(record, 'redirect_complete');
393
+ }
394
+ catch (err) {
395
+ console.log(`[${connectionId}] Error processing HTTP redirect: ${err}`);
396
+ socket.end();
397
+ this.connectionManager.initiateCleanupOnce(record, 'redirect_error');
398
+ }
399
+ };
400
+ // Setup the HTTP data handler
401
+ socket.once('data', httpDataHandler);
402
+ dataListeners.push(httpDataHandler);
403
+ }
404
+ /**
405
+ * Handle a block action for a route
406
+ */
407
+ handleBlockAction(socket, record, route) {
408
+ const connectionId = record.id;
409
+ if (this.settings.enableDetailedLogging) {
410
+ console.log(`[${connectionId}] Blocking connection based on route "${route.name || 'unnamed'}"`);
411
+ }
412
+ // Simply close the connection
413
+ socket.end();
414
+ this.connectionManager.initiateCleanupOnce(record, 'route_blocked');
415
+ }
416
+ /**
417
+ * Handle a connection using legacy domain configuration
418
+ */
419
+ handleLegacyConnection(socket, record, serverName, domainConfig, initialChunk) {
420
+ const connectionId = record.id;
421
+ // Get the forwarding type for this domain
422
+ const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
423
+ // IP validation
424
+ const ipRules = this.domainConfigManager.getEffectiveIPRules(domainConfig);
425
+ if (!this.securityManager.isIPAuthorized(record.remoteIP, ipRules.allowedIPs, ipRules.blockedIPs)) {
426
+ console.log(`[${connectionId}] Connection rejected: IP ${record.remoteIP} not allowed for domain ${domainConfig.domains.join(', ')}`);
427
+ socket.end();
428
+ this.connectionManager.initiateCleanupOnce(record, 'ip_blocked');
429
+ return;
430
+ }
431
+ // Handle based on forwarding type
432
+ switch (forwardingType) {
433
+ case 'http-only':
434
+ // For HTTP-only configs with TLS traffic
435
+ if (record.isTLS) {
436
+ console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName}`);
437
+ socket.end();
438
+ this.connectionManager.initiateCleanupOnce(record, 'wrong_protocol');
439
+ return;
440
+ }
441
+ break;
442
+ case 'https-passthrough':
443
+ // For TLS passthrough with TLS traffic
444
+ if (record.isTLS) {
445
+ try {
446
+ const handler = this.domainConfigManager.getForwardingHandler(domainConfig);
447
+ if (this.settings.enableDetailedLogging) {
448
+ console.log(`[${connectionId}] Using forwarding handler for SNI passthrough to ${serverName}`);
449
+ }
450
+ // Handle the connection using the handler
451
+ return handler.handleConnection(socket);
452
+ }
453
+ catch (err) {
454
+ console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
455
+ }
456
+ }
457
+ break;
458
+ case 'https-terminate-to-http':
459
+ case 'https-terminate-to-https':
460
+ // For TLS termination with TLS traffic
461
+ if (record.isTLS) {
462
+ const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
463
+ if (this.settings.enableDetailedLogging) {
464
+ console.log(`[${connectionId}] Using TLS termination (${forwardingType}) for ${serverName} on port ${networkProxyPort}`);
465
+ }
466
+ // Forward to NetworkProxy with domain-specific port
467
+ return this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, initialChunk, networkProxyPort, (reason) => this.connectionManager.initiateCleanupOnce(record, reason));
468
+ }
469
+ break;
470
+ }
471
+ // If we're still here, use the forwarding handler if available
472
+ try {
473
+ const handler = this.domainConfigManager.getForwardingHandler(domainConfig);
474
+ if (this.settings.enableDetailedLogging) {
475
+ console.log(`[${connectionId}] Using general forwarding handler for domain ${serverName || 'unknown'}`);
476
+ }
477
+ // Handle the connection using the handler
478
+ return handler.handleConnection(socket);
479
+ }
480
+ catch (err) {
481
+ console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
482
+ }
483
+ // Fallback: set up direct connection
484
+ const targetIp = this.domainConfigManager.getTargetIP(domainConfig);
485
+ const targetPort = this.domainConfigManager.getTargetPort(domainConfig, this.settings.toPort);
486
+ return this.setupDirectConnection(socket, record, domainConfig, serverName, initialChunk, undefined, targetIp, targetPort);
487
+ }
488
+ /**
489
+ * Sets up a direct connection to the target
490
+ */
491
+ setupDirectConnection(socket, record, domainConfig, serverName, initialChunk, overridePort, targetHost, targetPort) {
492
+ const connectionId = record.id;
493
+ // Determine target host and port if not provided
494
+ const finalTargetHost = targetHost || (domainConfig
495
+ ? this.domainConfigManager.getTargetIP(domainConfig)
496
+ : this.settings.defaults?.target?.host
497
+ ? this.settings.defaults.target.host
498
+ : this.settings.targetIP);
499
+ // Determine target port - first try explicit port, then forwarding config, then fallback
500
+ const finalTargetPort = targetPort || (overridePort !== undefined
501
+ ? overridePort
502
+ : domainConfig
503
+ ? this.domainConfigManager.getTargetPort(domainConfig, this.settings.toPort)
504
+ : this.settings.defaults?.target?.port
505
+ ? this.settings.defaults.target.port
506
+ : this.settings.toPort);
507
+ // Setup connection options
508
+ const connectionOptions = {
509
+ host: finalTargetHost,
510
+ port: finalTargetPort,
511
+ };
512
+ // Preserve source IP if configured
513
+ if (this.settings.defaults?.preserveSourceIP || this.settings.preserveSourceIP) {
514
+ connectionOptions.localAddress = record.remoteIP.replace('::ffff:', '');
515
+ }
516
+ // Create a safe queue for incoming data
517
+ const dataQueue = [];
518
+ let queueSize = 0;
519
+ let processingQueue = false;
520
+ let drainPending = false;
521
+ let pipingEstablished = false;
522
+ // Pause the incoming socket to prevent buffer overflows
523
+ socket.pause();
524
+ // Function to safely process the data queue without losing events
525
+ const processDataQueue = () => {
526
+ if (processingQueue || dataQueue.length === 0 || pipingEstablished)
527
+ return;
528
+ processingQueue = true;
529
+ try {
530
+ // Process all queued chunks with the current active handler
531
+ while (dataQueue.length > 0) {
532
+ const chunk = dataQueue.shift();
533
+ queueSize -= chunk.length;
534
+ // Once piping is established, we shouldn't get here,
535
+ // but just in case, pass to the outgoing socket directly
536
+ if (pipingEstablished && record.outgoing) {
537
+ record.outgoing.write(chunk);
538
+ continue;
539
+ }
540
+ // Track bytes received
541
+ record.bytesReceived += chunk.length;
542
+ // Check for TLS handshake
543
+ if (!record.isTLS && this.tlsManager.isTlsHandshake(chunk)) {
544
+ record.isTLS = true;
545
+ if (this.settings.enableTlsDebugLogging) {
546
+ console.log(`[${connectionId}] TLS handshake detected in tempDataHandler, ${chunk.length} bytes`);
547
+ }
548
+ }
549
+ // Check if adding this chunk would exceed the buffer limit
550
+ const newSize = record.pendingDataSize + chunk.length;
551
+ if (this.settings.maxPendingDataSize && newSize > this.settings.maxPendingDataSize) {
552
+ console.log(`[${connectionId}] Buffer limit exceeded for connection from ${record.remoteIP}: ${newSize} bytes > ${this.settings.maxPendingDataSize} bytes`);
553
+ socket.end(); // Gracefully close the socket
554
+ this.connectionManager.initiateCleanupOnce(record, 'buffer_limit_exceeded');
555
+ return;
556
+ }
557
+ // Buffer the chunk and update the size counter
558
+ record.pendingData.push(Buffer.from(chunk));
559
+ record.pendingDataSize = newSize;
560
+ this.timeoutManager.updateActivity(record);
561
+ }
562
+ }
563
+ finally {
564
+ processingQueue = false;
565
+ // If there's a pending drain and we've processed everything,
566
+ // signal we're ready for more data if we haven't established piping yet
567
+ if (drainPending && dataQueue.length === 0 && !pipingEstablished) {
568
+ drainPending = false;
569
+ socket.resume();
570
+ }
571
+ }
572
+ };
573
+ // Unified data handler that safely queues incoming data
574
+ const safeDataHandler = (chunk) => {
575
+ // If piping is already established, just let the pipe handle it
576
+ if (pipingEstablished)
577
+ return;
578
+ // Add to our queue for orderly processing
579
+ dataQueue.push(Buffer.from(chunk)); // Make a copy to be safe
580
+ queueSize += chunk.length;
581
+ // If queue is getting large, pause socket until we catch up
582
+ if (this.settings.maxPendingDataSize && queueSize > this.settings.maxPendingDataSize * 0.8) {
583
+ socket.pause();
584
+ drainPending = true;
585
+ }
586
+ // Process the queue
587
+ processDataQueue();
588
+ };
589
+ // Add our safe data handler
590
+ socket.on('data', safeDataHandler);
591
+ // Add initial chunk to pending data if present
592
+ if (initialChunk) {
593
+ record.bytesReceived += initialChunk.length;
594
+ record.pendingData.push(Buffer.from(initialChunk));
595
+ record.pendingDataSize = initialChunk.length;
596
+ }
597
+ // Create the target socket but don't set up piping immediately
598
+ const targetSocket = plugins.net.connect(connectionOptions);
599
+ record.outgoing = targetSocket;
600
+ record.outgoingStartTime = Date.now();
601
+ // Apply socket optimizations
602
+ targetSocket.setNoDelay(this.settings.noDelay);
603
+ // Apply keep-alive settings to the outgoing connection as well
604
+ if (this.settings.keepAlive) {
605
+ targetSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
606
+ // Apply enhanced TCP keep-alive options if enabled
607
+ if (this.settings.enableKeepAliveProbes) {
608
+ try {
609
+ if ('setKeepAliveProbes' in targetSocket) {
610
+ targetSocket.setKeepAliveProbes(10);
611
+ }
612
+ if ('setKeepAliveInterval' in targetSocket) {
613
+ targetSocket.setKeepAliveInterval(1000);
614
+ }
615
+ }
616
+ catch (err) {
617
+ // Ignore errors - these are optional enhancements
618
+ if (this.settings.enableDetailedLogging) {
619
+ console.log(`[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}`);
620
+ }
621
+ }
622
+ }
623
+ }
624
+ // Setup specific error handler for connection phase
625
+ targetSocket.once('error', (err) => {
626
+ // This handler runs only once during the initial connection phase
627
+ const code = err.code;
628
+ console.log(`[${connectionId}] Connection setup error to ${finalTargetHost}:${connectionOptions.port}: ${err.message} (${code})`);
629
+ // Resume the incoming socket to prevent it from hanging
630
+ socket.resume();
631
+ if (code === 'ECONNREFUSED') {
632
+ console.log(`[${connectionId}] Target ${finalTargetHost}:${connectionOptions.port} refused connection`);
633
+ }
634
+ else if (code === 'ETIMEDOUT') {
635
+ console.log(`[${connectionId}] Connection to ${finalTargetHost}:${connectionOptions.port} timed out`);
636
+ }
637
+ else if (code === 'ECONNRESET') {
638
+ console.log(`[${connectionId}] Connection to ${finalTargetHost}:${connectionOptions.port} was reset`);
639
+ }
640
+ else if (code === 'EHOSTUNREACH') {
641
+ console.log(`[${connectionId}] Host ${finalTargetHost} is unreachable`);
642
+ }
643
+ // Clear any existing error handler after connection phase
644
+ targetSocket.removeAllListeners('error');
645
+ // Re-add the normal error handler for established connections
646
+ targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
647
+ if (record.outgoingTerminationReason === null) {
648
+ record.outgoingTerminationReason = 'connection_failed';
649
+ this.connectionManager.incrementTerminationStat('outgoing', 'connection_failed');
650
+ }
651
+ // If we have a forwarding handler for this domain, let it handle the error
652
+ if (domainConfig) {
653
+ try {
654
+ const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
655
+ forwardingHandler.emit('connection_error', {
656
+ socket,
657
+ error: err,
658
+ connectionId
659
+ });
660
+ }
661
+ catch (handlerErr) {
662
+ // If getting the handler fails, just log and continue with normal cleanup
663
+ console.log(`Error getting forwarding handler for error handling: ${handlerErr}`);
664
+ }
665
+ }
666
+ // Clean up the connection
667
+ this.connectionManager.initiateCleanupOnce(record, `connection_failed_${code}`);
668
+ });
669
+ // Setup close handler
670
+ targetSocket.on('close', this.connectionManager.handleClose('outgoing', record));
671
+ socket.on('close', this.connectionManager.handleClose('incoming', record));
672
+ // Handle timeouts with keep-alive awareness
673
+ socket.on('timeout', () => {
674
+ // For keep-alive connections, just log a warning instead of closing
675
+ if (record.hasKeepAlive) {
676
+ console.log(`[${connectionId}] Timeout event on incoming keep-alive connection from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`);
677
+ return;
678
+ }
679
+ // For non-keep-alive connections, proceed with normal cleanup
680
+ console.log(`[${connectionId}] Timeout on incoming side from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`);
681
+ if (record.incomingTerminationReason === null) {
682
+ record.incomingTerminationReason = 'timeout';
683
+ this.connectionManager.incrementTerminationStat('incoming', 'timeout');
684
+ }
685
+ this.connectionManager.initiateCleanupOnce(record, 'timeout_incoming');
686
+ });
687
+ targetSocket.on('timeout', () => {
688
+ // For keep-alive connections, just log a warning instead of closing
689
+ if (record.hasKeepAlive) {
690
+ console.log(`[${connectionId}] Timeout event on outgoing keep-alive connection from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`);
691
+ return;
692
+ }
693
+ // For non-keep-alive connections, proceed with normal cleanup
694
+ console.log(`[${connectionId}] Timeout on outgoing side from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`);
695
+ if (record.outgoingTerminationReason === null) {
696
+ record.outgoingTerminationReason = 'timeout';
697
+ this.connectionManager.incrementTerminationStat('outgoing', 'timeout');
698
+ }
699
+ this.connectionManager.initiateCleanupOnce(record, 'timeout_outgoing');
700
+ });
701
+ // Apply socket timeouts
702
+ this.timeoutManager.applySocketTimeouts(record);
703
+ // Track outgoing data for bytes counting
704
+ targetSocket.on('data', (chunk) => {
705
+ record.bytesSent += chunk.length;
706
+ this.timeoutManager.updateActivity(record);
707
+ });
708
+ // Wait for the outgoing connection to be ready before setting up piping
709
+ targetSocket.once('connect', () => {
710
+ // Clear the initial connection error handler
711
+ targetSocket.removeAllListeners('error');
712
+ // Add the normal error handler for established connections
713
+ targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
714
+ // Process any remaining data in the queue before switching to piping
715
+ processDataQueue();
716
+ // Set up piping immediately
717
+ pipingEstablished = true;
718
+ // Flush all pending data to target
719
+ if (record.pendingData.length > 0) {
720
+ const combinedData = Buffer.concat(record.pendingData);
721
+ if (this.settings.enableDetailedLogging) {
722
+ console.log(`[${connectionId}] Forwarding ${combinedData.length} bytes of initial data to target`);
723
+ }
724
+ // Write pending data immediately
725
+ targetSocket.write(combinedData, (err) => {
726
+ if (err) {
727
+ console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`);
728
+ return this.connectionManager.initiateCleanupOnce(record, 'write_error');
729
+ }
730
+ });
731
+ // Clear the buffer now that we've processed it
732
+ record.pendingData = [];
733
+ record.pendingDataSize = 0;
734
+ }
735
+ // Setup piping in both directions without any delays
736
+ socket.pipe(targetSocket);
737
+ targetSocket.pipe(socket);
738
+ // Resume the socket to ensure data flows
739
+ socket.resume();
740
+ // Process any data that might be queued in the interim
741
+ if (dataQueue.length > 0) {
742
+ // Write any remaining queued data directly to the target socket
743
+ for (const chunk of dataQueue) {
744
+ targetSocket.write(chunk);
745
+ }
746
+ // Clear the queue
747
+ dataQueue.length = 0;
748
+ queueSize = 0;
749
+ }
750
+ if (this.settings.enableDetailedLogging) {
751
+ console.log(`[${connectionId}] Connection established: ${record.remoteIP} -> ${finalTargetHost}:${connectionOptions.port}` +
752
+ `${serverName
753
+ ? ` (SNI: ${serverName})`
754
+ : domainConfig
755
+ ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
756
+ : ''}` +
757
+ ` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}`);
758
+ }
759
+ else {
760
+ console.log(`Connection established: ${record.remoteIP} -> ${finalTargetHost}:${connectionOptions.port}` +
761
+ `${serverName
762
+ ? ` (SNI: ${serverName})`
763
+ : domainConfig
764
+ ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
765
+ : ''}`);
766
+ }
767
+ // Add the renegotiation handler for SNI validation
768
+ if (serverName) {
769
+ // Create connection info object for the existing connection
770
+ const connInfo = {
771
+ sourceIp: record.remoteIP,
772
+ sourcePort: record.incoming.remotePort || 0,
773
+ destIp: record.incoming.localAddress || '',
774
+ destPort: record.incoming.localPort || 0,
775
+ };
776
+ // Create a renegotiation handler function
777
+ const renegotiationHandler = this.tlsManager.createRenegotiationHandler(connectionId, serverName, connInfo, (connectionId, reason) => this.connectionManager.initiateCleanupOnce(record, reason));
778
+ // Store the handler in the connection record so we can remove it during cleanup
779
+ record.renegotiationHandler = renegotiationHandler;
780
+ // Add the handler to the socket
781
+ socket.on('data', renegotiationHandler);
782
+ if (this.settings.enableDetailedLogging) {
783
+ console.log(`[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`);
784
+ if (this.settings.allowSessionTicket === false) {
785
+ console.log(`[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`);
786
+ }
787
+ }
788
+ }
789
+ // Set connection timeout
790
+ record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
791
+ console.log(`[${connectionId}] Connection from ${record.remoteIP} exceeded max lifetime, forcing cleanup.`);
792
+ this.connectionManager.initiateCleanupOnce(record, reason);
793
+ });
794
+ // Mark TLS handshake as complete for TLS connections
795
+ if (record.isTLS) {
796
+ record.tlsHandshakeComplete = true;
797
+ if (this.settings.enableTlsDebugLogging) {
798
+ console.log(`[${connectionId}] TLS handshake complete for connection from ${record.remoteIP}`);
799
+ }
800
+ }
801
+ });
802
+ }
803
+ }
804
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtY29ubmVjdGlvbi1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS9yb3V0ZS1jb25uZWN0aW9uLWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQU01QyxPQUFPLEVBQ0wsZUFBZSxFQUNmLGVBQWUsRUFDaEIsTUFBTSx3QkFBd0IsQ0FBQztBQUtoQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDakUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFJbEQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sc0JBQXNCO0lBR2pDLFlBQ0UsUUFBNEIsRUFDcEIsaUJBQW9DLEVBQ3BDLGVBQWdDLEVBQ2hDLG1CQUF3QyxFQUN4QyxVQUFzQixFQUN0QixrQkFBc0MsRUFDdEMsY0FBOEIsRUFDOUIsWUFBMEI7UUFOMUIsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUFtQjtRQUNwQyxvQkFBZSxHQUFmLGVBQWUsQ0FBaUI7UUFDaEMsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFxQjtRQUN4QyxlQUFVLEdBQVYsVUFBVSxDQUFZO1FBQ3RCLHVCQUFrQixHQUFsQixrQkFBa0IsQ0FBb0I7UUFDdEMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLGlCQUFZLEdBQVosWUFBWSxDQUFjO1FBRWxDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO0lBQzNCLENBQUM7SUFFRDs7T0FFRztJQUNJLGdCQUFnQixDQUFDLE1BQTBCO1FBQ2hELE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxhQUFhLElBQUksRUFBRSxDQUFDO1FBQzVDLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxTQUFTLElBQUksQ0FBQyxDQUFDO1FBRXhDLHdEQUF3RDtRQUN4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLFFBQVEsS0FBSyxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUM1RSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDYixNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsT0FBTztRQUNULENBQUM7UUFFRCxpQ0FBaUM7UUFDakMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9ELE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFFL0IsNkJBQTZCO1FBQzdCLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUV6Qyx1Q0FBdUM7UUFDdkMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQzVCLE1BQU0sQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLENBQUMsQ0FBQztZQUMvRCxNQUFNLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQztZQUUzQixtREFBbUQ7WUFDbkQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7Z0JBQ3hDLElBQUksQ0FBQztvQkFDSCx1REFBdUQ7b0JBQ3ZELElBQUksb0JBQW9CLElBQUksTUFBTSxFQUFFLENBQUM7d0JBQ2xDLE1BQWMsQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFDekMsQ0FBQztvQkFDRCxJQUFJLHNCQUFzQixJQUFJLE1BQU0sRUFBRSxDQUFDO3dCQUNwQyxNQUFjLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQzdDLENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO29CQUNiLGtEQUFrRDtvQkFDbEQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7d0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLHFEQUFxRCxHQUFHLEVBQUUsQ0FBQyxDQUFDO29CQUMxRixDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLHlCQUF5QixRQUFRLFlBQVksU0FBUyxJQUFJO2dCQUN4RSxlQUFlLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsVUFBVSxJQUFJO2dCQUMvRCx1QkFBdUIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGtCQUFrQixFQUFFLEVBQUUsQ0FDdkUsQ0FBQztRQUNKLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxDQUFDLEdBQUcsQ0FDVCx1QkFBdUIsUUFBUSxZQUFZLFNBQVMseUJBQXlCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQzNILENBQUM7UUFDSixDQUFDO1FBRUQseUJBQXlCO1FBQ3pCLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssbUJBQW1CLENBQUMsTUFBMEIsRUFBRSxNQUF5QjtRQUMvRSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQy9CLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDbkMsSUFBSSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7UUFFaEMsNENBQTRDO1FBQzVDLElBQUksY0FBYyxHQUEwQixVQUFVLENBQUMsR0FBRyxFQUFFO1lBQzFELElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO2dCQUN6QixPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSwyQkFBMkIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsMkJBQTJCLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FDeEgsQ0FBQztnQkFFRixxQkFBcUI7Z0JBQ3JCLFVBQVUsQ0FBQyxHQUFHLEVBQUU7b0JBQ2QsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7d0JBQ3pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLGlEQUFpRCxDQUFDLENBQUM7d0JBQy9FLElBQUksTUFBTSxDQUFDLHlCQUF5QixLQUFLLElBQUksRUFBRSxDQUFDOzRCQUM5QyxNQUFNLENBQUMseUJBQXlCLEdBQUcsaUJBQWlCLENBQUM7NEJBQ3JELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxVQUFVLEVBQUUsaUJBQWlCLENBQUMsQ0FBQzt3QkFDakYsQ0FBQzt3QkFDRCxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7d0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO29CQUN0RSxDQUFDO2dCQUNILENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNaLENBQUM7UUFDSCxDQUFDLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBbUIsQ0FBQyxDQUFDO1FBRXRDLG1EQUFtRDtRQUNuRCxJQUFJLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUN6QixjQUFjLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsQ0FBQztRQUVELHVCQUF1QjtRQUN2QixNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBRTNFLHNEQUFzRDtRQUN0RCxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQWEsRUFBRSxFQUFFO1lBQ3BDLHNEQUFzRDtZQUN0RCxJQUFJLGNBQWMsRUFBRSxDQUFDO2dCQUNuQixZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7Z0JBQzdCLGNBQWMsR0FBRyxJQUFJLENBQUM7WUFDeEIsQ0FBQztZQUVELG1CQUFtQixHQUFHLElBQUksQ0FBQztZQUMzQixNQUFNLENBQUMsc0JBQXNCLEdBQUcsSUFBSSxDQUFDO1lBRXJDLHdDQUF3QztZQUN4QyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLElBQUksU0FBUyxLQUFLLEdBQUcsRUFBRSxDQUFDO2dCQUNoRSxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSw2Q0FBNkM7b0JBQzNELDhFQUE4RSxDQUNqRixDQUFDO2dCQUNGLElBQUksTUFBTSxDQUFDLHlCQUF5QixLQUFLLElBQUksRUFBRSxDQUFDO29CQUM5QyxNQUFNLENBQUMseUJBQXlCLEdBQUcsaUJBQWlCLENBQUM7b0JBQ3JELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxVQUFVLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztnQkFDakYsQ0FBQztnQkFDRCxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO2dCQUNwRSxPQUFPO1lBQ1QsQ0FBQztZQUVELDJDQUEyQztZQUMzQyxJQUFJLFVBQVUsR0FBRyxFQUFFLENBQUM7WUFDcEIsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQyxNQUFNLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztnQkFFcEIsdUNBQXVDO2dCQUN2QyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQ3pDLDRDQUE0QztvQkFDNUMsTUFBTSxRQUFRLEdBQUc7d0JBQ2YsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO3dCQUN6QixVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsSUFBSSxDQUFDO3dCQUNsQyxNQUFNLEVBQUUsTUFBTSxDQUFDLFlBQVksSUFBSSxFQUFFO3dCQUNqQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFNBQVMsSUFBSSxDQUFDO3FCQUNoQyxDQUFDO29CQUVGLGNBQWM7b0JBQ2QsVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7b0JBRS9ELDRDQUE0QztvQkFDNUMsTUFBTSxDQUFDLFlBQVksR0FBRyxVQUFVLENBQUM7b0JBRWpDLG9EQUFvRDtvQkFDcEQsSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixLQUFLLEtBQUssRUFBRSxDQUFDO3dCQUM5RCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSwwREFBMEQsQ0FBQyxDQUFDO3dCQUN4RixJQUFJLE1BQU0sQ0FBQyx5QkFBeUIsS0FBSyxJQUFJLEVBQUUsQ0FBQzs0QkFDOUMsTUFBTSxDQUFDLHlCQUF5QixHQUFHLCtCQUErQixDQUFDOzRCQUNuRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsd0JBQXdCLENBQUMsVUFBVSxFQUFFLCtCQUErQixDQUFDLENBQUM7d0JBQy9GLENBQUM7d0JBQ0QsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7d0JBQ3RFLElBQUksQ0FBQzs0QkFDSCxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7NEJBQ2QsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQzs0QkFDcEIsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDOzRCQUNoQixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7d0JBQ2YsQ0FBQzt3QkFBQyxNQUFNLENBQUM7NEJBQ1AsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO3dCQUNmLENBQUM7d0JBQ0QsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSwrQkFBK0IsQ0FBQyxDQUFDO3dCQUNsRixPQUFPO29CQUNULENBQUM7b0JBRUQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7d0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLDhCQUE4QixVQUFVLElBQUksU0FBUyxFQUFFLENBQUMsQ0FBQztvQkFDdkYsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztZQUVELGlEQUFpRDtZQUNqRCxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzFELENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ssZUFBZSxDQUNyQixNQUEwQixFQUMxQixNQUF5QixFQUN6QixVQUFrQixFQUNsQixZQUFxQjtRQUVyQixNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQy9CLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDbkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQztRQUVqQyxzQkFBc0I7UUFDdEIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxpQkFBaUIsQ0FBQztZQUNyRCxJQUFJLEVBQUUsU0FBUztZQUNmLE1BQU0sRUFBRSxVQUFVO1lBQ2xCLFFBQVEsRUFBRSxRQUFRO1lBQ2xCLElBQUksRUFBRSxTQUFTLEVBQUUsd0NBQXdDO1lBQ3pELFVBQVUsRUFBRSxTQUFTLENBQUMsbUNBQW1DO1NBQzFELENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNoQixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSx3QkFBd0IsVUFBVSxJQUFJLFlBQVksWUFBWSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1lBRXZHLHFFQUFxRTtZQUNyRSxNQUFNLFlBQVksR0FBRyxVQUFVO2dCQUM3QixDQUFDLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDLFVBQVUsQ0FBQztnQkFDdkQsQ0FBQyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyx1QkFBdUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUVoRSxJQUFJLFlBQVksRUFBRSxDQUFDO2dCQUNqQixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztvQkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVksMkNBQTJDLFVBQVUsSUFBSSxPQUFPLEdBQUcsU0FBUyxFQUFFLENBQUMsQ0FBQztnQkFDOUcsQ0FBQztnQkFFRCxtREFBbUQ7Z0JBQ25ELE1BQU0sQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO2dCQUVuQywrQ0FBK0M7Z0JBQy9DLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxZQUFZLENBQUMsQ0FBQztZQUM3RixDQUFDO1lBRUQsb0VBQW9FO1lBQ3BFLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLCtDQUErQyxDQUFDLENBQUM7WUFFN0Usa0NBQWtDO1lBQ2xDLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDO1lBQ2pFLElBQUksdUJBQXVCLEVBQUUsQ0FBQztnQkFDNUIsSUFBSSx1QkFBdUIsQ0FBQyxVQUFVLElBQUksdUJBQXVCLENBQUMsVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDeEYsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQ25ELFFBQVEsRUFDUix1QkFBdUIsQ0FBQyxVQUFVLEVBQ2xDLHVCQUF1QixDQUFDLFVBQVUsSUFBSSxFQUFFLENBQ3pDLENBQUM7b0JBRUYsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO3dCQUNmLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLFFBQVEsUUFBUSw4QkFBOEIsQ0FBQyxDQUFDO3dCQUM1RSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7d0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxZQUFZLENBQUMsQ0FBQzt3QkFDL0QsT0FBTztvQkFDVCxDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDekYsaUNBQWlDO2dCQUNqQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FDbkQsUUFBUSxFQUNSLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLEVBQy9CLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLElBQUksRUFBRSxDQUN0QyxDQUFDO2dCQUVGLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztvQkFDZixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSxRQUFRLFFBQVEsOEJBQThCLENBQUMsQ0FBQztvQkFDNUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO29CQUNiLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUM7b0JBQy9ELE9BQU87Z0JBQ1QsQ0FBQztZQUNILENBQUM7WUFFRCxnREFBZ0Q7WUFDaEQsSUFBSSxVQUFrQixDQUFDO1lBQ3ZCLElBQUksVUFBa0IsQ0FBQztZQUV2QixJQUFJLGVBQWUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLENBQUM7Z0JBQ3JFLHlDQUF5QztnQkFDekMsVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ2hELFVBQVUsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO1lBQ2xELENBQUM7aUJBQU0sQ0FBQztnQkFDTiwrQkFBK0I7Z0JBQy9CLFVBQVUsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsSUFBSSxXQUFXLENBQUM7Z0JBQ25ELFVBQVUsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQztZQUNwQyxDQUFDO1lBRUQsT0FBTyxJQUFJLENBQUMscUJBQXFCLENBQy9CLE1BQU0sRUFDTixNQUFNLEVBQ04sU0FBUyxFQUNULFVBQVUsRUFDVixZQUFZLEVBQ1osU0FBUyxFQUNULFVBQVUsRUFDVixVQUFVLENBQ1gsQ0FBQztRQUNKLENBQUM7UUFFRCw2QkFBNkI7UUFDN0IsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQztRQUUvQixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUN4QyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSxxQkFBcUIsS0FBSyxDQUFDLElBQUksSUFBSSxTQUFTLFNBQVMsVUFBVSxJQUFJLFlBQVksWUFBWSxTQUFTLEVBQUUsQ0FDdkgsQ0FBQztRQUNKLENBQUM7UUFFRCw0Q0FBNEM7UUFDNUMsUUFBUSxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzFCLEtBQUssU0FBUztnQkFDWixPQUFPLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQztZQUV2RSxLQUFLLFVBQVU7Z0JBQ2IsT0FBTyxJQUFJLENBQUMsb0JBQW9CLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUUxRCxLQUFLLE9BQU87Z0JBQ1YsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUV2RDtnQkFDRSxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSwwQkFBMkIsS0FBSyxDQUFDLE1BQWMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3ZFLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxtQkFBbUIsQ0FDekIsTUFBMEIsRUFDMUIsTUFBeUIsRUFDekIsS0FBbUIsRUFDbkIsWUFBcUI7UUFFckIsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUMvQixNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDO1FBRTVCLHVEQUF1RDtRQUN2RCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ25CLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLCtDQUErQyxDQUFDLENBQUM7WUFDN0UsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ25FLE9BQU87UUFDVCxDQUFDO1FBRUQsdUNBQXVDO1FBQ3ZDLElBQUksTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2YsUUFBUSxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUN4QixLQUFLLGFBQWE7b0JBQ2hCLDZDQUE2QztvQkFDN0MsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7d0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLDhCQUE4QixNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7b0JBQ2xGLENBQUM7b0JBRUQsMkJBQTJCO29CQUMzQixNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO3dCQUNsRCxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7d0JBQzNFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztvQkFFdkIsdUVBQXVFO29CQUN2RSxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7b0JBRXRGLE9BQU8sSUFBSSxDQUFDLHFCQUFxQixDQUMvQixNQUFNLEVBQ04sTUFBTSxFQUNOLFNBQVMsRUFDVCxNQUFNLENBQUMsWUFBWSxFQUNuQixZQUFZLEVBQ1osU0FBUyxFQUNULFVBQVUsRUFDVixVQUFVLENBQ1gsQ0FBQztnQkFFSixLQUFLLFdBQVcsQ0FBQztnQkFDakIsS0FBSyx5QkFBeUI7b0JBQzVCLHdDQUF3QztvQkFDeEMsSUFBSSxJQUFJLENBQUMsa0JBQWtCLENBQUMsZUFBZSxFQUFFLEVBQUUsQ0FBQzt3QkFDOUMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7NEJBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLCtDQUErQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUNwRixDQUFDO3dCQUNKLENBQUM7d0JBRUQsaUVBQWlFO3dCQUNqRSxJQUFJLFlBQVksSUFBSSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7NEJBQ2pDLE9BQU8sSUFBSSxDQUFDLGtCQUFrQixDQUFDLHFCQUFxQixDQUNsRCxZQUFZLEVBQ1osTUFBTSxFQUNOLE1BQU0sRUFDTixZQUFZLEVBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFDOUIsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQ3ZFLENBQUM7d0JBQ0osQ0FBQzt3QkFFRCx5RUFBeUU7d0JBQ3pFLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLDBDQUEwQyxDQUFDLENBQUM7d0JBQ3hFLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQzt3QkFDYixJQUFJLENBQUMsaUJBQWlCLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDO3dCQUM5RCxPQUFPO29CQUNULENBQUM7eUJBQU0sQ0FBQzt3QkFDTixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSxrREFBa0QsQ0FBQyxDQUFDO3dCQUNoRixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7d0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO3dCQUNyRSxPQUFPO29CQUNULENBQUM7WUFDTCxDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTixxQ0FBcUM7WUFDckMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7Z0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLCtCQUErQixNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7WUFDekcsQ0FBQztZQUVELDJCQUEyQjtZQUMzQixNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO2dCQUNsRCxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQzNFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztZQUV2Qix1RUFBdUU7WUFDdkUsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO1lBRXRGLE9BQU8sSUFBSSxDQUFDLHFCQUFxQixDQUMvQixNQUFNLEVBQ04sTUFBTSxFQUNOLFNBQVMsRUFDVCxNQUFNLENBQUMsWUFBWSxFQUNuQixZQUFZLEVBQ1osU0FBUyxFQUNULFVBQVUsRUFDVixVQUFVLENBQ1gsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxvQkFBb0IsQ0FDMUIsTUFBMEIsRUFDMUIsTUFBeUIsRUFDekIsS0FBbUI7UUFFbkIsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUMvQixNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDO1FBRTVCLDBDQUEwQztRQUMxQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3JCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLGtEQUFrRCxDQUFDLENBQUM7WUFDaEYsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1lBQ3JFLE9BQU87UUFDVCxDQUFDO1FBRUQsOERBQThEO1FBQzlELElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLCtDQUErQyxDQUFDLENBQUM7WUFDN0UsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO1lBQ3ZFLE9BQU87UUFDVCxDQUFDO1FBRUQsMERBQTBEO1FBQzFELE1BQU0sYUFBYSxHQUFnQyxFQUFFLENBQUM7UUFFdEQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxLQUFhLEVBQUUsRUFBRTtZQUN4QywyREFBMkQ7WUFDM0QsS0FBSyxNQUFNLFFBQVEsSUFBSSxhQUFhLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDMUMsQ0FBQztZQUVELGlDQUFpQztZQUNqQyxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDN0MsSUFBSSxVQUFVLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztvQkFDdEIsOENBQThDO29CQUM5QyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxlQUFlLENBQUMsQ0FBQztvQkFDckMsYUFBYSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztvQkFDcEMsT0FBTztnQkFDVCxDQUFDO2dCQUVELE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUMxRCxNQUFNLFdBQVcsR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRCxNQUFNLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxHQUFHLFdBQVcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBRTlDLHNCQUFzQjtnQkFDdEIsTUFBTSxTQUFTLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO2dCQUNsRSxNQUFNLElBQUksR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFlBQVksSUFBSSxFQUFFLENBQUM7Z0JBRXpFLG1EQUFtRDtnQkFDbkQsSUFBSSxXQUFXLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7Z0JBQ3JDLFdBQVcsR0FBRyxXQUFXLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsQ0FBQztnQkFDdkQsV0FBVyxHQUFHLFdBQVcsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDM0QsV0FBVyxHQUFHLFdBQVcsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFFNUUscUNBQXFDO2dCQUNyQyxNQUFNLGdCQUFnQixHQUFHO29CQUN2QixZQUFZLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxRQUFRO29CQUMxQyxhQUFhLFdBQVcsRUFBRTtvQkFDMUIsbUJBQW1CO29CQUNuQixtQkFBbUI7b0JBQ25CLEVBQUU7b0JBQ0YsRUFBRTtpQkFDSCxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFFZixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztvQkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVksb0JBQW9CLFdBQVcsZ0JBQWdCLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDdkcsQ0FBQztnQkFFRCw2QkFBNkI7Z0JBQzdCLE1BQU0sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztnQkFDN0IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1lBQzFFLENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLHFDQUFxQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2dCQUN4RSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ3ZFLENBQUM7UUFDSCxDQUFDLENBQUM7UUFFRiw4QkFBOEI7UUFDOUIsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDckMsYUFBYSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQ7O09BRUc7SUFDSyxpQkFBaUIsQ0FDdkIsTUFBMEIsRUFDMUIsTUFBeUIsRUFDekIsS0FBbUI7UUFFbkIsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUUvQixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUN4QyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksWUFBWSx5Q0FBeUMsS0FBSyxDQUFDLElBQUksSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO1FBQ25HLENBQUM7UUFFRCw4QkFBOEI7UUFDOUIsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxlQUFlLENBQUMsQ0FBQztJQUN0RSxDQUFDO0lBRUQ7O09BRUc7SUFDSyxzQkFBc0IsQ0FDNUIsTUFBMEIsRUFDMUIsTUFBeUIsRUFDekIsVUFBa0IsRUFDbEIsWUFBMkIsRUFDM0IsWUFBcUI7UUFFckIsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUUvQiwwQ0FBMEM7UUFDMUMsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRWhGLGdCQUFnQjtRQUNoQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFM0UsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztZQUNsRyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSw2QkFBNkIsTUFBTSxDQUFDLFFBQVEsMkJBQTJCLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQ3pILENBQUM7WUFDRixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBQ2pFLE9BQU87UUFDVCxDQUFDO1FBRUQsa0NBQWtDO1FBQ2xDLFFBQVEsY0FBYyxFQUFFLENBQUM7WUFDdkIsS0FBSyxXQUFXO2dCQUNkLHlDQUF5QztnQkFDekMsSUFBSSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7b0JBQ2pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLGtEQUFrRCxVQUFVLEVBQUUsQ0FBQyxDQUFDO29CQUM1RixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7b0JBQ2IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO29CQUNyRSxPQUFPO2dCQUNULENBQUM7Z0JBQ0QsTUFBTTtZQUVSLEtBQUssbUJBQW1CO2dCQUN0Qix1Q0FBdUM7Z0JBQ3ZDLElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO29CQUNqQixJQUFJLENBQUM7d0JBQ0gsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLG9CQUFvQixDQUFDLFlBQVksQ0FBQyxDQUFDO3dCQUU1RSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQzs0QkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVkscURBQXFELFVBQVUsRUFBRSxDQUFDLENBQUM7d0JBQ2pHLENBQUM7d0JBRUQsMENBQTBDO3dCQUMxQyxPQUFPLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDMUMsQ0FBQztvQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO3dCQUNiLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLHFDQUFxQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO29CQUMxRSxDQUFDO2dCQUNILENBQUM7Z0JBQ0QsTUFBTTtZQUVSLEtBQUsseUJBQXlCLENBQUM7WUFDL0IsS0FBSywwQkFBMEI7Z0JBQzdCLHVDQUF1QztnQkFDdkMsSUFBSSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7b0JBQ2pCLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLG1CQUFtQixDQUFDLFlBQVksQ0FBQyxDQUFDO29CQUVwRixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQzt3QkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVksNEJBQTRCLGNBQWMsU0FBUyxVQUFVLFlBQVksZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDO29CQUMzSCxDQUFDO29CQUVELG9EQUFvRDtvQkFDcEQsT0FBTyxJQUFJLENBQUMsa0JBQWtCLENBQUMscUJBQXFCLENBQ2xELFlBQVksRUFDWixNQUFNLEVBQ04sTUFBTSxFQUNOLFlBQWEsRUFDYixnQkFBZ0IsRUFDaEIsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQ3ZFLENBQUM7Z0JBQ0osQ0FBQztnQkFDRCxNQUFNO1FBQ1YsQ0FBQztRQUVELCtEQUErRDtRQUMvRCxJQUFJLENBQUM7WUFDSCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsb0JBQW9CLENBQUMsWUFBWSxDQUFDLENBQUM7WUFFNUUsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7Z0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLGlEQUFpRCxVQUFVLElBQUksU0FBUyxFQUFFLENBQUMsQ0FBQztZQUMxRyxDQUFDO1lBRUQsMENBQTBDO1lBQzFDLE9BQU8sT0FBTyxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzFDLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVkscUNBQXFDLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDMUUsQ0FBQztRQUVELHFDQUFxQztRQUNyQyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ3BFLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFOUYsT0FBTyxJQUFJLENBQUMscUJBQXFCLENBQy9CLE1BQU0sRUFDTixNQUFNLEVBQ04sWUFBWSxFQUNaLFVBQVUsRUFDVixZQUFZLEVBQ1osU0FBUyxFQUNULFFBQVEsRUFDUixVQUFVLENBQ1gsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNLLHFCQUFxQixDQUMzQixNQUEwQixFQUMxQixNQUF5QixFQUN6QixZQUE0QixFQUM1QixVQUFtQixFQUNuQixZQUFxQixFQUNyQixZQUFxQixFQUNyQixVQUFtQixFQUNuQixVQUFtQjtRQUVuQixNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBRS9CLGlEQUFpRDtRQUNqRCxNQUFNLGVBQWUsR0FBRyxVQUFVLElBQUksQ0FBQyxZQUFZO1lBQ2pELENBQUMsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQztZQUNwRCxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLElBQUk7Z0JBQ3BDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSTtnQkFDcEMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUyxDQUFDLENBQUM7UUFFL0IseUZBQXlGO1FBQ3pGLE1BQU0sZUFBZSxHQUFHLFVBQVUsSUFBSSxDQUFDLFlBQVksS0FBSyxTQUFTO1lBQy9ELENBQUMsQ0FBQyxZQUFZO1lBQ2QsQ0FBQyxDQUFDLFlBQVk7Z0JBQ1osQ0FBQyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDO2dCQUM1RSxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLElBQUk7b0JBQ3BDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSTtvQkFDcEMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFOUIsMkJBQTJCO1FBQzNCLE1BQU0saUJBQWlCLEdBQStCO1lBQ3BELElBQUksRUFBRSxlQUFlO1lBQ3JCLElBQUksRUFBRSxlQUFlO1NBQ3RCLENBQUM7UUFFRixtQ0FBbUM7UUFDbkMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxnQkFBZ0IsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDL0UsaUJBQWlCLENBQUMsWUFBWSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMxRSxDQUFDO1FBRUQsd0NBQXdDO1FBQ3hDLE1BQU0sU0FBUyxHQUFhLEVBQUUsQ0FBQztRQUMvQixJQUFJLFNBQVMsR0FBRyxDQUFDLENBQUM7UUFDbEIsSUFBSSxlQUFlLEdBQUcsS0FBSyxDQUFDO1FBQzVCLElBQUksWUFBWSxHQUFHLEtBQUssQ0FBQztRQUN6QixJQUFJLGlCQUFpQixHQUFHLEtBQUssQ0FBQztRQUU5Qix3REFBd0Q7UUFDeEQsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRWYsa0VBQWtFO1FBQ2xFLE1BQU0sZ0JBQWdCLEdBQUcsR0FBRyxFQUFFO1lBQzVCLElBQUksZUFBZSxJQUFJLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLGlCQUFpQjtnQkFBRSxPQUFPO1lBRTNFLGVBQWUsR0FBRyxJQUFJLENBQUM7WUFFdkIsSUFBSSxDQUFDO2dCQUNILDREQUE0RDtnQkFDNUQsT0FBTyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUM1QixNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsS0FBSyxFQUFHLENBQUM7b0JBQ2pDLFNBQVMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDO29CQUUxQixxREFBcUQ7b0JBQ3JELHlEQUF5RDtvQkFDekQsSUFBSSxpQkFBaUIsSUFBSSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7d0JBQ3pDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO3dCQUM3QixTQUFTO29CQUNYLENBQUM7b0JBRUQsdUJBQXVCO29CQUN2QixNQUFNLENBQUMsYUFBYSxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUM7b0JBRXJDLDBCQUEwQjtvQkFDMUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQzt3QkFDM0QsTUFBTSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUM7d0JBRXBCLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDOzRCQUN4QyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sUUFBUSxDQUNyRixDQUFDO3dCQUNKLENBQUM7b0JBQ0gsQ0FBQztvQkFFRCwyREFBMkQ7b0JBQzNELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxlQUFlLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQztvQkFFdEQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixFQUFFLENBQUM7d0JBQ25GLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLCtDQUErQyxNQUFNLENBQUMsUUFBUSxLQUFLLE9BQU8sWUFBWSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixRQUFRLENBQy9JLENBQUM7d0JBQ0YsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsOEJBQThCO3dCQUM1QyxJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLHVCQUF1QixDQUFDLENBQUM7d0JBQzVFLE9BQU87b0JBQ1QsQ0FBQztvQkFFRCwrQ0FBK0M7b0JBQy9DLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztvQkFDNUMsTUFBTSxDQUFDLGVBQWUsR0FBRyxPQUFPLENBQUM7b0JBQ2pDLElBQUksQ0FBQyxjQUFjLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUM3QyxDQUFDO1lBQ0gsQ0FBQztvQkFBUyxDQUFDO2dCQUNULGVBQWUsR0FBRyxLQUFLLENBQUM7Z0JBRXhCLDZEQUE2RDtnQkFDN0Qsd0VBQXdFO2dCQUN4RSxJQUFJLFlBQVksSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7b0JBQ2pFLFlBQVksR0FBRyxLQUFLLENBQUM7b0JBQ3JCLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDbEIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDLENBQUM7UUFFRix3REFBd0Q7UUFDeEQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxLQUFhLEVBQUUsRUFBRTtZQUN4QyxnRUFBZ0U7WUFDaEUsSUFBSSxpQkFBaUI7Z0JBQUUsT0FBTztZQUU5QiwwQ0FBMEM7WUFDMUMsU0FBUyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyx5QkFBeUI7WUFDN0QsU0FBUyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFFMUIsNERBQTREO1lBQzVELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsSUFBSSxTQUFTLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsR0FBRyxHQUFHLEVBQUUsQ0FBQztnQkFDM0YsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNmLFlBQVksR0FBRyxJQUFJLENBQUM7WUFDdEIsQ0FBQztZQUVELG9CQUFvQjtZQUNwQixnQkFBZ0IsRUFBRSxDQUFDO1FBQ3JCLENBQUMsQ0FBQztRQUVGLDRCQUE0QjtRQUM1QixNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVuQywrQ0FBK0M7UUFDL0MsSUFBSSxZQUFZLEVBQUUsQ0FBQztZQUNqQixNQUFNLENBQUMsYUFBYSxJQUFJLFlBQVksQ0FBQyxNQUFNLENBQUM7WUFDNUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO1lBQ25ELE1BQU0sQ0FBQyxlQUFlLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQztRQUMvQyxDQUFDO1FBRUQsK0RBQStEO1FBQy9ELE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDNUQsTUFBTSxDQUFDLFFBQVEsR0FBRyxZQUFZLENBQUM7UUFDL0IsTUFBTSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUV0Qyw2QkFBNkI7UUFDN0IsWUFBWSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRS9DLCtEQUErRDtRQUMvRCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDNUIsWUFBWSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1lBRXJFLG1EQUFtRDtZQUNuRCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztnQkFDeEMsSUFBSSxDQUFDO29CQUNILElBQUksb0JBQW9CLElBQUksWUFBWSxFQUFFLENBQUM7d0JBQ3hDLFlBQW9CLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQy9DLENBQUM7b0JBQ0QsSUFBSSxzQkFBc0IsSUFBSSxZQUFZLEVBQUUsQ0FBQzt3QkFDMUMsWUFBb0IsQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDbkQsQ0FBQztnQkFDSCxDQUFDO2dCQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7b0JBQ2Isa0RBQWtEO29CQUNsRCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQzt3QkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJLFlBQVksZ0VBQWdFLEdBQUcsRUFBRSxDQUN0RixDQUFDO29CQUNKLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsb0RBQW9EO1FBQ3BELFlBQVksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDakMsa0VBQWtFO1lBQ2xFLE1BQU0sSUFBSSxHQUFJLEdBQVcsQ0FBQyxJQUFJLENBQUM7WUFDL0IsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJLFlBQVksK0JBQStCLGVBQWUsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLEtBQUssR0FBRyxDQUFDLE9BQU8sS0FBSyxJQUFJLEdBQUcsQ0FDckgsQ0FBQztZQUVGLHdEQUF3RDtZQUN4RCxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUM7WUFFaEIsSUFBSSxJQUFJLEtBQUssY0FBYyxFQUFFLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLFlBQVksZUFBZSxJQUFJLGlCQUFpQixDQUFDLElBQUkscUJBQXFCLENBQzNGLENBQUM7WUFDSixDQUFDO2lCQUFNLElBQUksSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO2dCQUNoQyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSxtQkFBbUIsZUFBZSxJQUFJLGlCQUFpQixDQUFDLElBQUksWUFBWSxDQUN6RixDQUFDO1lBQ0osQ0FBQztpQkFBTSxJQUFJLElBQUksS0FBSyxZQUFZLEVBQUUsQ0FBQztnQkFDakMsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJLFlBQVksbUJBQW1CLGVBQWUsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLFlBQVksQ0FDekYsQ0FBQztZQUNKLENBQUM7aUJBQU0sSUFBSSxJQUFJLEtBQUssY0FBYyxFQUFFLENBQUM7Z0JBQ25DLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxZQUFZLFVBQVUsZUFBZSxpQkFBaUIsQ0FBQyxDQUFDO1lBQzFFLENBQUM7WUFFRCwwREFBMEQ7WUFDMUQsWUFBWSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRXpDLDhEQUE4RDtZQUM5RCxZQUFZLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRWpGLElBQUksTUFBTSxDQUFDLHlCQUF5QixLQUFLLElBQUksRUFBRSxDQUFDO2dCQUM5QyxNQUFNLENBQUMseUJBQXlCLEdBQUcsbUJBQW1CLENBQUM7Z0JBQ3ZELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxVQUFVLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztZQUNuRixDQUFDO1lBRUQsMkVBQTJFO1lBQzNFLElBQUksWUFBWSxFQUFFLENBQUM7Z0JBQ2pCLElBQUksQ0FBQztvQkFDSCxNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxvQkFBb0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztvQkFDdEYsaUJBQWlCLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO3dCQUN6QyxNQUFNO3dCQUNOLEtBQUssRUFBRSxHQUFHO3dCQUNWLFlBQVk7cUJBQ2IsQ0FBQyxDQUFDO2dCQUNMLENBQUM7Z0JBQUMsT0FBTyxVQUFVLEVBQUUsQ0FBQztvQkFDcEIsMEVBQTBFO29CQUMxRSxPQUFPLENBQUMsR0FBRyxDQUFDLHdEQUF3RCxVQUFVLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRixDQUFDO1lBQ0gsQ0FBQztZQUVELDBCQUEwQjtZQUMxQixJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLHFCQUFxQixJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ2xGLENBQUMsQ0FBQyxDQUFDO1FBRUgsc0JBQXNCO1FBQ3RCLFlBQVksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDakYsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUUzRSw0Q0FBNEM7UUFDNUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsR0FBRyxFQUFFO1lBQ3hCLG9FQUFvRTtZQUNwRSxJQUFJLE1BQU0sQ0FBQyxZQUFZLEVBQUUsQ0FBQztnQkFDeEIsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJLFlBQVksMERBQ2QsTUFBTSxDQUFDLFFBQ1QsVUFBVSxPQUFPLENBQUMsUUFBUSxDQUN4QixJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQ3ZDLHlCQUF5QixDQUMzQixDQUFDO2dCQUNGLE9BQU87WUFDVCxDQUFDO1lBRUQsOERBQThEO1lBQzlELE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLG1DQUNkLE1BQU0sQ0FBQyxRQUNULFVBQVUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUNyRSxDQUFDO1lBQ0YsSUFBSSxNQUFNLENBQUMseUJBQXlCLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQzlDLE1BQU0sQ0FBQyx5QkFBeUIsR0FBRyxTQUFTLENBQUM7Z0JBQzdDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDekUsQ0FBQztZQUNELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUN6RSxDQUFDLENBQUMsQ0FBQztRQUVILFlBQVksQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRTtZQUM5QixvRUFBb0U7WUFDcEUsSUFBSSxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLDBEQUNkLE1BQU0sQ0FBQyxRQUNULFVBQVUsT0FBTyxDQUFDLFFBQVEsQ0FDeEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLElBQUksT0FBTyxDQUN2Qyx5QkFBeUIsQ0FDM0IsQ0FBQztnQkFDRixPQUFPO1lBQ1QsQ0FBQztZQUVELDhEQUE4RDtZQUM5RCxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSxtQ0FDZCxNQUFNLENBQUMsUUFDVCxVQUFVLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLElBQUksT0FBTyxDQUFDLEVBQUUsQ0FDckUsQ0FBQztZQUNGLElBQUksTUFBTSxDQUFDLHlCQUF5QixLQUFLLElBQUksRUFBRSxDQUFDO2dCQUM5QyxNQUFNLENBQUMseUJBQXlCLEdBQUcsU0FBUyxDQUFDO2dCQUM3QyxJQUFJLENBQUMsaUJBQWlCLENBQUMsd0JBQXdCLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1lBQ3pFLENBQUM7WUFDRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDekUsQ0FBQyxDQUFDLENBQUM7UUFFSCx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUVoRCx5Q0FBeUM7UUFDekMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFhLEVBQUUsRUFBRTtZQUN4QyxNQUFNLENBQUMsU0FBUyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDakMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0MsQ0FBQyxDQUFDLENBQUM7UUFFSCx3RUFBd0U7UUFDeEUsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsR0FBRyxFQUFFO1lBQ2hDLDZDQUE2QztZQUM3QyxZQUFZLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFekMsMkRBQTJEO1lBQzNELFlBQVksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFFakYscUVBQXFFO1lBQ3JFLGdCQUFnQixFQUFFLENBQUM7WUFFbkIsNEJBQTRCO1lBQzVCLGlCQUFpQixHQUFHLElBQUksQ0FBQztZQUV6QixtQ0FBbUM7WUFDbkMsSUFBSSxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDbEMsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUM7Z0JBRXZELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO29CQUN4QyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSxnQkFBZ0IsWUFBWSxDQUFDLE1BQU0sa0NBQWtDLENBQ3RGLENBQUM7Z0JBQ0osQ0FBQztnQkFFRCxpQ0FBaUM7Z0JBQ2pDLFlBQVksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7b0JBQ3ZDLElBQUksR0FBRyxFQUFFLENBQUM7d0JBQ1IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFlBQVksMkNBQTJDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO3dCQUN0RixPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsYUFBYSxDQUFDLENBQUM7b0JBQzNFLENBQUM7Z0JBQ0gsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsK0NBQStDO2dCQUMvQyxNQUFNLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztnQkFDeEIsTUFBTSxDQUFDLGVBQWUsR0FBRyxDQUFDLENBQUM7WUFDN0IsQ0FBQztZQUVELHFEQUFxRDtZQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzFCLFlBQVksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFMUIseUNBQXlDO1lBQ3pDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUVoQix1REFBdUQ7WUFDdkQsSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUN6QixnRUFBZ0U7Z0JBQ2hFLEtBQUssTUFBTSxLQUFLLElBQUksU0FBUyxFQUFFLENBQUM7b0JBQzlCLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQzVCLENBQUM7Z0JBQ0Qsa0JBQWtCO2dCQUNsQixTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztnQkFDckIsU0FBUyxHQUFHLENBQUMsQ0FBQztZQUNoQixDQUFDO1lBRUQsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLENBQUM7Z0JBQ3hDLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLDZCQUE2QixNQUFNLENBQUMsUUFBUSxPQUFPLGVBQWUsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLEVBQUU7b0JBQzVHLEdBQ0UsVUFBVTt3QkFDUixDQUFDLENBQUMsVUFBVSxVQUFVLEdBQUc7d0JBQ3pCLENBQUMsQ0FBQyxZQUFZOzRCQUNkLENBQUMsQ0FBQyw0QkFBNEIsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUc7NEJBQ2hFLENBQUMsQ0FBQyxFQUNOLEVBQUU7b0JBQ0YsU0FBUyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksaUJBQ2xDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFDaEMsRUFBRSxDQUNMLENBQUM7WUFDSixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sT0FBTyxDQUFDLEdBQUcsQ0FDVCwyQkFBMkIsTUFBTSxDQUFDLFFBQVEsT0FBTyxlQUFlLElBQUksaUJBQWlCLENBQUMsSUFBSSxFQUFFO29CQUMxRixHQUNFLFVBQVU7d0JBQ1IsQ0FBQyxDQUFDLFVBQVUsVUFBVSxHQUFHO3dCQUN6QixDQUFDLENBQUMsWUFBWTs0QkFDZCxDQUFDLENBQUMsNEJBQTRCLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHOzRCQUNoRSxDQUFDLENBQUMsRUFDTixFQUFFLENBQ0wsQ0FBQztZQUNKLENBQUM7WUFFRCxtREFBbUQ7WUFDbkQsSUFBSSxVQUFVLEVBQUUsQ0FBQztnQkFDZiw0REFBNEQ7Z0JBQzVELE1BQU0sUUFBUSxHQUFHO29CQUNmLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtvQkFDekIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsVUFBVSxJQUFJLENBQUM7b0JBQzNDLE1BQU0sRUFBRSxNQUFNLENBQUMsUUFBUSxDQUFDLFlBQVksSUFBSSxFQUFFO29CQUMxQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxTQUFTLElBQUksQ0FBQztpQkFDekMsQ0FBQztnQkFFRiwwQ0FBMEM7Z0JBQzFDLE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQywwQkFBMEIsQ0FDckUsWUFBWSxFQUNaLFVBQVUsRUFDVixRQUFRLEVBQ1IsQ0FBQyxZQUFZLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUNyRixDQUFDO2dCQUVGLGdGQUFnRjtnQkFDaEYsTUFBTSxDQUFDLG9CQUFvQixHQUFHLG9CQUFvQixDQUFDO2dCQUVuRCxnQ0FBZ0M7Z0JBQ2hDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLG9CQUFvQixDQUFDLENBQUM7Z0JBRXhDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO29CQUN4QyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSx5REFBeUQsVUFBVSxFQUFFLENBQ3RGLENBQUM7b0JBQ0YsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixLQUFLLEtBQUssRUFBRSxDQUFDO3dCQUMvQyxPQUFPLENBQUMsR0FBRyxDQUNULElBQUksWUFBWSx3RkFBd0YsQ0FDekcsQ0FBQztvQkFDSixDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1lBRUQseUJBQXlCO1lBQ3pCLE1BQU0sQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUU7Z0JBQzFGLE9BQU8sQ0FBQyxHQUFHLENBQ1QsSUFBSSxZQUFZLHFCQUFxQixNQUFNLENBQUMsUUFBUSwwQ0FBMEMsQ0FDL0YsQ0FBQztnQkFDRixJQUFJLENBQUMsaUJBQWlCLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzdELENBQUMsQ0FBQyxDQUFDO1lBRUgscURBQXFEO1lBQ3JELElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNqQixNQUFNLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxDQUFDO2dCQUVuQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQztvQkFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJLFlBQVksZ0RBQWdELE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FDbEYsQ0FBQztnQkFDSixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztDQUNGIn0=