From f489ec6ceed5df4303ecca49e44d012fdfe015ec Mon Sep 17 00:00:00 2001 From: Stephan Oeste Date: Tue, 30 Aug 2022 15:40:13 +0200 Subject: [PATCH 01/31] Remove the mempool restart script in prod install --- production/install | 1 - production/mempool-restart-all | 8 -------- 2 files changed, 9 deletions(-) delete mode 100755 production/mempool-restart-all diff --git a/production/install b/production/install index 4eac1817b..40f89389f 100755 --- a/production/install +++ b/production/install @@ -1009,7 +1009,6 @@ osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_ osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-build-all upgrade osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-kill-all stop osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-start-all start -osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-restart-all restart case $OS in diff --git a/production/mempool-restart-all b/production/mempool-restart-all deleted file mode 100755 index 13e551066..000000000 --- a/production/mempool-restart-all +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env zsh -HOSTNAME=$(hostname) - -echo restarting mempool backends | wall -echo "${HOSTNAME} restarted mempool backends" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.ops -ps uaxw|grep 'dist/index'|grep -v grep|grep -v services|awk '{print $2}'|xargs -n 1 kill - -exit 0 From 88a36f4378f9de527650492a20ca2cc82c2ee263 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 2 Sep 2022 09:30:07 +0200 Subject: [PATCH 02/31] Renamed "City" to "Location" --- .../nodes-per-country/nodes-per-country.component.html | 2 +- .../app/lightning/nodes-per-isp/nodes-per-isp.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html index 16f4265a2..4b07214d7 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html @@ -12,7 +12,7 @@ Last update Capacity Channels - City + Location diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html index a8931d843..80c8a4a26 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html @@ -9,7 +9,7 @@ Last update Capacity Channels - City + Location From ee23d1695d65e826da1ddca6094307718fbc5e02 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 2 Sep 2022 10:08:25 +0200 Subject: [PATCH 03/31] Use shared component in node ranking list --- backend/src/api/explorer/nodes.api.ts | 15 ++++++++++++--- .../src/app/interfaces/node-api.interface.ts | 9 +++++++++ .../oldest-nodes/oldest-nodes.component.html | 2 +- .../oldest-nodes/oldest-nodes.component.ts | 17 +++++++++++++++-- .../top-nodes-per-capacity.component.html | 2 +- .../top-nodes-per-capacity.component.ts | 15 ++++++++++++++- .../top-nodes-per-channels.component.html | 4 ++-- .../top-nodes-per-channels.component.ts | 15 ++++++++++++++- 8 files changed, 68 insertions(+), 11 deletions(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 128405ffd..f9f0916fb 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -133,10 +133,13 @@ class NodesApi { CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' ORDER BY capacity DESC LIMIT 100 `; @@ -175,10 +178,13 @@ class NodesApi { CAST(COALESCE(nodes.channels, 0) as INT) as channels, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' ORDER BY channels DESC LIMIT 100 `; @@ -221,11 +227,14 @@ class NodesApi { CAST(COALESCE(node_stats.channels, 0) as INT) as channels, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM node_stats RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' WHERE added = FROM_UNIXTIME(${latestDate}) ORDER BY first_seen LIMIT 100 diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 5c071a357..d9670936d 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -161,6 +161,9 @@ export interface ITopNodesPerChannels { updatedAt?: number, city?: any, country?: any, + subdivision?: any, + iso_code?: string, + geolocation?: any; } export interface ITopNodesPerCapacity { @@ -172,6 +175,9 @@ export interface ITopNodesPerCapacity { updatedAt?: number, city?: any, country?: any, + subdivision?: any, + iso_code?: string, + geolocation?: any; } export interface INodesRanking { @@ -188,6 +194,9 @@ export interface IOldestNodes { updatedAt?: number, city?: any, country?: any, + subdivision?: any, + iso_code?: string, + geolocation?: any; } export interface IChannel { diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html index 5b96400c2..39e64a865 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html @@ -35,7 +35,7 @@ - {{ node?.city?.en ?? '-' }} + diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts index 23f248b0e..e97acd80b 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; +import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; import { IOldestNodes } from '../../../interfaces/node-api.interface'; import { LightningApiService } from '../../lightning-api.service'; @@ -23,11 +24,23 @@ export class OldestNodes implements OnInit { } if (this.widget === false) { - this.oldestNodes$ = this.apiService.getOldestNodes$(); + this.oldestNodes$ = this.apiService.getOldestNodes$().pipe( + map((ranking) => { + for (const i in ranking) { + ranking[i].geolocation = { + country: ranking[i].country?.en, + city: ranking[i].city?.en, + subdivision: ranking[i].subdivision?.en, + iso: ranking[i].iso_code, + }; + } + return ranking; + }) + ); } else { this.oldestNodes$ = this.apiService.getOldestNodes$().pipe( map((nodes: IOldestNodes[]) => { - return nodes.slice(0, 10); + return nodes.slice(0, 7); }) ); } diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html index 107609251..3e0293453 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html @@ -35,7 +35,7 @@ - {{ node?.city?.en ?? '-' }} + diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts index c79c396ee..595688690 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core import { map, Observable } from 'rxjs'; import { INodesRanking, ITopNodesPerCapacity } from 'src/app/interfaces/node-api.interface'; import { isMobile } from 'src/app/shared/common.utils'; +import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; import { LightningApiService } from '../../lightning-api.service'; @Component({ @@ -25,7 +26,19 @@ export class TopNodesPerCapacity implements OnInit { } if (this.widget === false) { - this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$(); + this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$().pipe( + map((ranking) => { + for (const i in ranking) { + ranking[i].geolocation = { + country: ranking[i].country?.en, + city: ranking[i].city?.en, + subdivision: ranking[i].subdivision?.en, + iso: ranking[i].iso_code, + }; + } + return ranking; + }) + ); } else { this.topNodesPerCapacity$ = this.nodes$.pipe( map((ranking) => { diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html index dd05e7a6a..91a15111b 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html @@ -35,9 +35,9 @@ - {{ node?.city?.en ?? '-' }} + - + diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts index 44e7aa518..ee4b159c0 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core import { map, Observable } from 'rxjs'; import { INodesRanking, ITopNodesPerChannels } from 'src/app/interfaces/node-api.interface'; import { isMobile } from 'src/app/shared/common.utils'; +import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; import { LightningApiService } from '../../lightning-api.service'; @Component({ @@ -25,7 +26,19 @@ export class TopNodesPerChannels implements OnInit { } if (this.widget === false) { - this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$(); + this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$().pipe( + map((ranking) => { + for (const i in ranking) { + ranking[i].geolocation = { + country: ranking[i].country?.en, + city: ranking[i].city?.en, + subdivision: ranking[i].subdivision?.en, + iso: ranking[i].iso_code, + }; + } + return ranking; + }) + ); } else { this.topNodesPerChannels$ = this.nodes$.pipe( map((ranking) => { From 83df23a9026458e40b5a4892932b999c32541e26 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 2 Sep 2022 10:23:02 +0200 Subject: [PATCH 04/31] Renamed capacity to liquidity --- .../nodes-ranking/oldest-nodes/oldest-nodes.component.html | 2 +- .../top-nodes-per-capacity.component.html | 2 +- .../top-nodes-per-channels.component.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html index 5b96400c2..8df85c7dd 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.html @@ -9,7 +9,7 @@ Alias First seen - Capacity + Liquidity Channels Last update Location diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html index 107609251..42eb6543e 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.html @@ -8,7 +8,7 @@ Alias - Capacity + Liquidity Channels First seen Last update diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html index dd05e7a6a..df9d79bac 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.html @@ -9,7 +9,7 @@ Alias Channels - Capacity + Liquidity First seen Last update Location From dcd55d9757b08be8c9426c899571fa040200c594 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 2 Sep 2022 10:28:54 +0200 Subject: [PATCH 05/31] Fixes #2495 --- .../nodes-per-country-chart.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html index d3e8686b0..a8cfdcfb4 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.html @@ -46,7 +46,7 @@ - {{ country.capacity | amountShortener: 1 }} + {{ country.capacity ?? 0 | amountShortener: 1 }} sats From 8f0fc3af57fab3c1953bfc58f6b3f5a1b07fcd0d Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:30 +0200 Subject: [PATCH 06/31] backend: Add config file env var --- backend/README.md | 5 +++++ backend/src/config.ts | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/README.md b/backend/README.md index 823998fdc..3d7c23eaa 100644 --- a/backend/README.md +++ b/backend/README.md @@ -110,6 +110,11 @@ Run the Mempool backend: ``` npm run start + +``` +You can also set env var `MEMPOOL_CONFIG_FILE` to specify a custom config file location: +``` +MEMPOOL_CONFIG_FILE=/path/to/mempool-config.json npm run start ``` When it's running, you should see output like this: diff --git a/backend/src/config.ts b/backend/src/config.ts index 8c91e104b..43ba16cb3 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -1,4 +1,6 @@ -const configFile = require('../mempool-config.json'); +const configFromFile = require( + process.env.MEMPOOL_CONFIG_FILE ? process.env.MEMPOOL_CONFIG_FILE : '../mempool-config.json' +); interface IConfig { MEMPOOL: { @@ -249,7 +251,7 @@ class Config implements IConfig { MAXMIND: IConfig['MAXMIND']; constructor() { - const configs = this.merge(configFile, defaults); + const configs = this.merge(configFromFile, defaults); this.MEMPOOL = configs.MEMPOOL; this.ESPLORA = configs.ESPLORA; this.ELECTRUM = configs.ELECTRUM; From 5683f639edf3f0b6c4fd80b1f7f8941b93270580 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:31 +0200 Subject: [PATCH 07/31] backend: Read `mtgox-weekly.json` from `dist` --- backend/package.json | 2 +- backend/src/tasks/price-updater.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index 082449dac..4af64a2a8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,7 +22,7 @@ "main": "index.ts", "scripts": { "tsc": "./node_modules/typescript/bin/tsc -p tsconfig.build.json", - "build": "npm run tsc", + "build": "npm run tsc && cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks/", "start": "node --max-old-space-size=2048 dist/index.js", "start-production": "node --max-old-space-size=4096 dist/index.js", "test": "./node_modules/.bin/jest --coverage", diff --git a/backend/src/tasks/price-updater.ts b/backend/src/tasks/price-updater.ts index 81066efb2..891e2bce3 100644 --- a/backend/src/tasks/price-updater.ts +++ b/backend/src/tasks/price-updater.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import path from "path"; import { Common } from '../api/common'; import config from '../config'; import logger from '../logger'; @@ -159,7 +160,7 @@ class PriceUpdater { const existingPriceTimes = await PricesRepository.$getPricesTimes(); // Insert MtGox weekly prices - const pricesJson: any[] = JSON.parse(fs.readFileSync('./src/tasks/price-feeds/mtgox-weekly.json').toString()); + const pricesJson: any[] = JSON.parse(fs.readFileSync(path.join(__dirname, 'mtgox-weekly.json')).toString()); const prices = this.getEmptyPricesObj(); let insertedCount: number = 0; for (const price of pricesJson) { From d591f7c456106f5052a01279073f0879dd5bc39b Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:32 +0200 Subject: [PATCH 08/31] backend: Fetch package version at build time Extract `fetch-version.ts` which is called at build time to create file `dist/api/version.json`. This file is read by `backend-info.ts` at runtime. This also fixes handing over the Git commit hash to the backend app in the Docker backend image, which was broken as of 2022-07-12. (Reason: The commit hash was previously required at runtime, but was only provided at build time.) --- backend/package.json | 3 +- backend/src/api/backend-info.ts | 73 +++++++++++--------------------- backend/src/api/fetch-version.ts | 37 ++++++++++++++++ 3 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 backend/src/api/fetch-version.ts diff --git a/backend/package.json b/backend/package.json index 4af64a2a8..1ba4a8554 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,7 +22,8 @@ "main": "index.ts", "scripts": { "tsc": "./node_modules/typescript/bin/tsc -p tsconfig.build.json", - "build": "npm run tsc && cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks/", + "build": "npm run tsc && npm run create-resources", + "create-resources": "cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks && node dist/api/fetch-version.js", "start": "node --max-old-space-size=2048 dist/index.js", "start-production": "node --max-old-space-size=4096 dist/index.js", "test": "./node_modules/.bin/jest --coverage", diff --git a/backend/src/api/backend-info.ts b/backend/src/api/backend-info.ts index d98675671..57bb5fe13 100644 --- a/backend/src/api/backend-info.ts +++ b/backend/src/api/backend-info.ts @@ -1,60 +1,37 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import logger from '../logger'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; import { IBackendInfo } from '../mempool.interfaces'; -const { spawnSync } = require('child_process'); class BackendInfo { - private gitCommitHash = ''; - private hostname = ''; - private version = ''; + private backendInfo: IBackendInfo; constructor() { - this.setLatestCommitHash(); - this.setVersion(); - this.hostname = os.hostname(); - } - - public getBackendInfo(): IBackendInfo { - return { - hostname: this.hostname, - gitCommit: this.gitCommitHash, - version: this.version, + // This file is created by ./fetch-version.ts during building + const versionFile = path.join(__dirname, 'version.json') + var versionInfo; + if (fs.existsSync(versionFile)) { + versionInfo = JSON.parse(fs.readFileSync(versionFile).toString()); + } else { + // Use dummy values if `versionFile` doesn't exist (e.g., during testing) + versionInfo = { + version: '?', + gitCommit: '?' + }; + } + this.backendInfo = { + hostname: os.hostname(), + version: versionInfo.version, + gitCommit: versionInfo.gitCommit }; } + public getBackendInfo(): IBackendInfo { + return this.backendInfo; + } + public getShortCommitHash() { - return this.gitCommitHash.slice(0, 7); - } - - private setLatestCommitHash(): void { - //TODO: share this logic with `generate-config.js` - if (process.env.DOCKER_COMMIT_HASH) { - this.gitCommitHash = process.env.DOCKER_COMMIT_HASH; - } else { - try { - const gitRevParse = spawnSync('git', ['rev-parse', '--short', 'HEAD']); - if (!gitRevParse.error) { - const output = gitRevParse.stdout.toString('utf-8').replace(/[\n\r\s]+$/, ''); - this.gitCommitHash = output ? output : '?'; - } else if (gitRevParse.error.code === 'ENOENT') { - console.log('git not found, cannot parse git hash'); - this.gitCommitHash = '?'; - } - } catch (e: any) { - console.log('Could not load git commit info: ' + e.message); - this.gitCommitHash = '?'; - } - } - } - - private setVersion(): void { - try { - const packageJson = fs.readFileSync('package.json').toString(); - this.version = JSON.parse(packageJson).version; - } catch (e) { - throw new Error(e instanceof Error ? e.message : 'Error'); - } + return this.backendInfo.gitCommit.slice(0, 7); } } diff --git a/backend/src/api/fetch-version.ts b/backend/src/api/fetch-version.ts new file mode 100644 index 000000000..e397be5ed --- /dev/null +++ b/backend/src/api/fetch-version.ts @@ -0,0 +1,37 @@ +import fs from 'fs'; +import path from "path"; +const { spawnSync } = require('child_process'); + +function getVersion(): string { + const packageJson = fs.readFileSync('package.json').toString(); + return JSON.parse(packageJson).version; +} + +function getGitCommit(): string { + if (process.env.DOCKER_COMMIT_HASH) { + return process.env.DOCKER_COMMIT_HASH; + } else { + const gitRevParse = spawnSync('git', ['rev-parse', '--short', 'HEAD']); + if (!gitRevParse.error) { + const output = gitRevParse.stdout.toString('utf-8').replace(/[\n\r\s]+$/, ''); + if (output) { + return output; + } else { + console.log('Could not fetch git commit: No repo available'); + } + } else if (gitRevParse.error.code === 'ENOENT') { + console.log('Could not fetch git commit: Command `git` is unavailable'); + } + } + return '?'; +} + +const versionInfo = { + version: getVersion(), + gitCommit: getGitCommit() +} + +fs.writeFileSync( + path.join(__dirname, 'version.json'), + JSON.stringify(versionInfo, null, 2) + "\n" +); From be72c5109ad0793e8d0f7cdece6002b716a57a35 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:33 +0200 Subject: [PATCH 09/31] backend: Create npm script `package` This script creates a directory `backend/package` which only contains the files required by the backend at runtime: - The contents of `dist` - `node_modules` minus `typescript` and `@typescript-eslint`. These packages are build-only and are larger than the remaining whole package. By using only `backend/package` in the Docker image, the backend content size in the image is decreased by 70% to 31M. This, along with the improved copying in the Dockerfile, reduces the backend image size by 44% to 200M. (Step `RUN chown -R 1000:1000 /backend ...` created a layer that effectively duplicated the backend.) --- backend/package.json | 2 ++ docker/backend/Dockerfile | 11 ++++------- docker/backend/start.sh | 2 +- docker/backend/wait-for-it.sh | 0 4 files changed, 7 insertions(+), 8 deletions(-) mode change 100644 => 100755 docker/backend/start.sh mode change 100644 => 100755 docker/backend/wait-for-it.sh diff --git a/backend/package.json b/backend/package.json index 1ba4a8554..3cd05e8ab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,6 +24,8 @@ "tsc": "./node_modules/typescript/bin/tsc -p tsconfig.build.json", "build": "npm run tsc && npm run create-resources", "create-resources": "cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks && node dist/api/fetch-version.js", + "package": "npm run build && rm -rf package && mv dist package && mv node_modules package && npm run package-rm-build-deps", + "package-rm-build-deps": "(cd package/node_modules; rm -r typescript @typescript-eslint)", "start": "node --max-old-space-size=2048 dist/index.js", "start-production": "node --max-old-space-size=4096 dist/index.js", "test": "./node_modules/.bin/jest --coverage", diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 8d13bc7f4..6b7e7ae19 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -9,18 +9,15 @@ COPY . . RUN apt-get update RUN apt-get install -y build-essential python3 pkg-config RUN npm install --omit=dev --omit=optional -RUN npm run build +RUN npm run package FROM node:16.16.0-buster-slim WORKDIR /backend -COPY --from=builder /build/ . - -RUN chmod +x /backend/start.sh -RUN chmod +x /backend/wait-for-it.sh - -RUN chown -R 1000:1000 /backend && chmod -R 755 /backend +RUN chown 1000:1000 ./ +COPY --from=builder --chown=1000:1000 /build/package ./package/ +COPY --from=builder --chown=1000:1000 /build/mempool-config.json /build/start.sh /build/wait-for-it.sh ./ USER 1000 diff --git a/docker/backend/start.sh b/docker/backend/start.sh old mode 100644 new mode 100755 index 8e6a34013..ade739f1f --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -205,4 +205,4 @@ sed -i "s!__LND_REST_API_URL__!${__LND_REST_API_URL__}!g" mempool-config.json # CLN sed -i "s!__CLN_SOCKET__!${__CLN_SOCKET__}!g" mempool-config.json -node /backend/dist/index.js +node /backend/package/index.js diff --git a/docker/backend/wait-for-it.sh b/docker/backend/wait-for-it.sh old mode 100644 new mode 100755 From 19bb8988f83c5da3c3b89e5a8481005927454bb7 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:34 +0200 Subject: [PATCH 10/31] docker/init.sh: Remove unused code This code has no effect because string `master` does not exist in `api/backend-info.ts`. --- docker/init.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker/init.sh b/docker/init.sh index 49b53d646..4eb06f0c1 100755 --- a/docker/init.sh +++ b/docker/init.sh @@ -1,10 +1,7 @@ #!/bin/sh #backend -gitMaster="\.\.\/\.git\/refs\/heads\/master" -git ls-remote https://github.com/mempool/mempool.git "$1^{}" | awk '{ print $1}' > ./backend/master cp ./docker/backend/* ./backend/ -sed -i "s/${gitMaster}/master/g" ./backend/src/api/backend-info.ts #frontend localhostIP="127.0.0.1" From 34c8ad614a014f3bf8ff5247f171154a4a60fe26 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 13 Jul 2022 12:44:35 +0200 Subject: [PATCH 11/31] backend: Rename build variable `DOCKER_COMMIT_HASH` -> `MEMPOOL_COMMIT_HASH` This var is useful for all build methods, not only Docker. This is an internal renaming that doesn't change the public Docker backend image API. --- backend/src/api/fetch-version.ts | 4 ++-- docker/backend/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/api/fetch-version.ts b/backend/src/api/fetch-version.ts index e397be5ed..cb0813c35 100644 --- a/backend/src/api/fetch-version.ts +++ b/backend/src/api/fetch-version.ts @@ -8,8 +8,8 @@ function getVersion(): string { } function getGitCommit(): string { - if (process.env.DOCKER_COMMIT_HASH) { - return process.env.DOCKER_COMMIT_HASH; + if (process.env.MEMPOOL_COMMIT_HASH) { + return process.env.MEMPOOL_COMMIT_HASH; } else { const gitRevParse = spawnSync('git', ['rev-parse', '--short', 'HEAD']); if (!gitRevParse.error) { diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 6b7e7ae19..9be457bb2 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,7 +1,7 @@ FROM node:16.16.0-buster-slim AS builder ARG commitHash -ENV DOCKER_COMMIT_HASH=${commitHash} +ENV MEMPOOL_COMMIT_HASH=${commitHash} WORKDIR /build COPY . . From 3801f988ba9e612c9619aeb65d0290a95c9647b0 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 2 Sep 2022 16:17:48 +0200 Subject: [PATCH 12/31] Show '-' when value is not defined in channel page --- .../channel/channel-box/channel-box.component.html | 10 +++++----- .../app/shared/components/sats/sats.component.html | 13 ++++++++----- .../app/shared/components/sats/sats.component.ts | 1 + 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.html b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html index 30d1a3585..8b486eff5 100644 --- a/frontend/src/app/lightning/channel/channel-box/channel-box.component.html +++ b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html @@ -19,31 +19,31 @@ Fee rate - {{ channel.fee_rate }} ppm ({{ channel.fee_rate / 10000 | number }}%) + {{ channel.fee_rate ?? '-' }} ppm ({{ channel.fee_rate / 10000 | number }}%) Base fee - + Min HTLC - + Max HTLC - + Timelock delta - + diff --git a/frontend/src/app/shared/components/sats/sats.component.html b/frontend/src/app/shared/components/sats/sats.component.html index a648cdfcb..dada43692 100644 --- a/frontend/src/app/shared/components/sats/sats.component.html +++ b/frontend/src/app/shared/components/sats/sats.component.html @@ -1,5 +1,8 @@ -‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number : digitsInfo }} -L- -tL- -t- -s-sats +{{ valueOverride }} +‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number : digitsInfo }} + + L- + tL- + t- + s-sats + \ No newline at end of file diff --git a/frontend/src/app/shared/components/sats/sats.component.ts b/frontend/src/app/shared/components/sats/sats.component.ts index d9801d249..39be66ecd 100644 --- a/frontend/src/app/shared/components/sats/sats.component.ts +++ b/frontend/src/app/shared/components/sats/sats.component.ts @@ -11,6 +11,7 @@ export class SatsComponent implements OnInit { @Input() satoshis: number; @Input() digitsInfo = '1.0-0'; @Input() addPlus = false; + @Input() valueOverride: string | undefined = undefined; network = ''; stateSubscription: Subscription; From a75262d79e51d60d5b92a658b1a40496c908cf07 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 4 Sep 2022 09:35:31 +0200 Subject: [PATCH 13/31] Only show active channels on world map --- backend/src/api/explorer/channels.api.ts | 3 ++- frontend/src/app/lightning/node/node.component.html | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/api/explorer/channels.api.ts b/backend/src/api/explorer/channels.api.ts index b396e4808..db6e92920 100644 --- a/backend/src/api/explorer/channels.api.ts +++ b/backend/src/api/explorer/channels.api.ts @@ -39,7 +39,8 @@ class ChannelsApi { FROM channels JOIN nodes AS nodes_1 on nodes_1.public_key = channels.node1_public_key JOIN nodes AS nodes_2 on nodes_2.public_key = channels.node2_public_key - WHERE nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL + WHERE channels.status = 1 + AND nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL AND nodes_2.latitude IS NOT NULL AND nodes_2.longitude IS NOT NULL `; diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index f0be2fe89..ded6b5821 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -120,7 +120,7 @@
-
+
@@ -128,7 +128,7 @@
-
+
From 5086f132f8dad3dfc0cf3eb3ba9dca17a0a86f45 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 4 Sep 2022 19:12:01 +0200 Subject: [PATCH 14/31] If a channel is closed, show closing date instead of last update --- backend/src/api/explorer/channels.api.ts | 1 + .../src/app/lightning/channel/channel.component.html | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/src/api/explorer/channels.api.ts b/backend/src/api/explorer/channels.api.ts index b396e4808..6b4a8d02c 100644 --- a/backend/src/api/explorer/channels.api.ts +++ b/backend/src/api/explorer/channels.api.ts @@ -374,6 +374,7 @@ class ChannelsApi { 'transaction_vout': channel.transaction_vout, 'closing_transaction_id': channel.closing_transaction_id, 'closing_reason': channel.closing_reason, + 'closing_date': channel.closing_date, 'updated_at': channel.updated_at, 'created': channel.created, 'status': channel.status, diff --git a/frontend/src/app/lightning/channel/channel.component.html b/frontend/src/app/lightning/channel/channel.component.html index 2824b8dba..9a0c424fb 100644 --- a/frontend/src/app/lightning/channel/channel.component.html +++ b/frontend/src/app/lightning/channel/channel.component.html @@ -25,13 +25,17 @@ - + - - + + + + + +
CreatedCreated
Last update
Last update
Closing date
From 5389928c49cb2998e829137b4b380c80e4f8336c Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sun, 4 Sep 2022 19:39:28 +0200 Subject: [PATCH 15/31] Show closing date in closed channel list --- .../channels-list/channels-list.component.html | 8 ++++++-- .../components/timestamp/timestamp.component.html | 11 +++++++---- .../components/timestamp/timestamp.component.ts | 6 ++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/lightning/channels-list/channels-list.component.html b/frontend/src/app/lightning/channels-list/channels-list.component.html index af87cefa4..a51e03ef8 100644 --- a/frontend/src/app/lightning/channels-list/channels-list.component.html +++ b/frontend/src/app/lightning/channels-list/channels-list.component.html @@ -35,7 +35,8 @@ Node Alias   Status - Fee Rate + Fee Rate + Closing date Capacity Channel ID @@ -71,9 +72,12 @@ - + {{ channel.fee_rate }} ppm ({{ channel.fee_rate / 10000 | number }}%) + + + diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.html b/frontend/src/app/shared/components/timestamp/timestamp.component.html index 769b292d4..69abce53f 100644 --- a/frontend/src/app/shared/components/timestamp/timestamp.component.html +++ b/frontend/src/app/shared/components/timestamp/timestamp.component.html @@ -1,4 +1,7 @@ -‎{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }} -
- () -
+- + + ‎{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }} +
+ () +
+
diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.ts b/frontend/src/app/shared/components/timestamp/timestamp.component.ts index dc577a185..120a5dfe4 100644 --- a/frontend/src/app/shared/components/timestamp/timestamp.component.ts +++ b/frontend/src/app/shared/components/timestamp/timestamp.component.ts @@ -11,15 +11,13 @@ export class TimestampComponent implements OnChanges { @Input() dateString: string; @Input() customFormat: string; - seconds: number; - - constructor() { } + seconds: number | undefined = undefined; ngOnChanges(): void { if (this.unixTime) { this.seconds = this.unixTime; } else if (this.dateString) { - this.seconds = new Date(this.dateString).getTime() / 1000 + this.seconds = new Date(this.dateString).getTime() / 1000; } } From d536d63d69a1b0534b669879d2ae20a24aecf7f0 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 6 Sep 2022 11:01:46 +0200 Subject: [PATCH 16/31] Add skeleton loader in node per isp/country lists --- .../nodes-per-country.component.html | 30 ++++++++++++++++++- .../nodes-per-country.component.ts | 8 ++++- .../nodes-per-isp.component.html | 30 ++++++++++++++++++- .../nodes-per-isp/nodes-per-isp.component.ts | 8 ++++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html index 16f4265a2..b4658a708 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html @@ -6,6 +6,7 @@
+ @@ -14,7 +15,8 @@ - + + + + + + + + + + + + + + + +
Alias First seenChannels City
{{ node.alias }} @@ -39,6 +41,32 @@
+ + + + + + + + + + + +
diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts index 644e6741a..e0bf5eb66 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -16,11 +16,17 @@ export class NodesPerCountry implements OnInit { nodes$: Observable; country: {name: string, flag: string}; + skeletonLines: number[] = []; + constructor( private apiService: ApiService, private seoService: SeoService, private route: ActivatedRoute, - ) { } + ) { + for (let i = 0; i < 20; ++i) { + this.skeletonLines.push(i); + } + } ngOnInit(): void { this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country) diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html index a8931d843..d27126fd2 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html @@ -3,6 +3,7 @@
+ @@ -11,7 +12,8 @@ - + + + + + + + + + + + + + + + +
Alias First seenChannels City
{{ node.alias }} @@ -36,6 +38,32 @@
+ + + + + + + + + + + +
diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts index cc57056fc..f7edf783a 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts @@ -15,11 +15,17 @@ export class NodesPerISP implements OnInit { nodes$: Observable; isp: {name: string, id: number}; + skeletonLines: number[] = []; + constructor( private apiService: ApiService, private seoService: SeoService, private route: ActivatedRoute, - ) { } + ) { + for (let i = 0; i < 20; ++i) { + this.skeletonLines.push(i); + } + } ngOnInit(): void { this.nodes$ = this.apiService.getNodeForISP$(this.route.snapshot.params.isp) From eb18625802995db26db115aa4755227a395af137 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 6 Sep 2022 11:42:19 +0200 Subject: [PATCH 17/31] Only scan for closed channels when there is a new block --- .../tasks/lightning/network-sync.service.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/src/tasks/lightning/network-sync.service.ts b/backend/src/tasks/lightning/network-sync.service.ts index b9b7df92c..8d8f1759f 100644 --- a/backend/src/tasks/lightning/network-sync.service.ts +++ b/backend/src/tasks/lightning/network-sync.service.ts @@ -12,9 +12,11 @@ import { ResultSetHeader } from 'mysql2'; import fundingTxFetcher from './sync-tasks/funding-tx-fetcher'; import NodesSocketsRepository from '../../repositories/NodesSocketsRepository'; import { Common } from '../../api/common'; +import blocks from '../../api/blocks'; class NetworkSyncService { loggerTimer = 0; + closedChannelsScanBlock = 0; constructor() {} @@ -240,10 +242,22 @@ class NetworkSyncService { } private async $scanForClosedChannels(): Promise { + if (this.closedChannelsScanBlock === blocks.getCurrentBlockHeight()) { + logger.debug(`We've already scan closed channels for this block, skipping.`); + return; + } + let progress = 0; try { - logger.info(`Starting closed channels scan`); + let log = `Starting closed channels scan`; + if (this.closedChannelsScanBlock > 0) { + log += `. Last scan was at block ${this.closedChannelsScanBlock}`; + } else { + log += ` for the first time`; + } + logger.info(log); + const channels = await channelsApi.$getChannelsByStatus([0, 1]); for (const channel of channels) { const spendingTx = await bitcoinApi.$getOutspend(channel.transaction_id, channel.transaction_vout); @@ -263,7 +277,9 @@ class NetworkSyncService { this.loggerTimer = new Date().getTime() / 1000; } } - logger.info(`Closed channels scan complete.`); + + this.closedChannelsScanBlock = blocks.getCurrentBlockHeight(); + logger.info(`Closed channels scan completed at block ${this.closedChannelsScanBlock}`); } catch (e) { logger.err('$scanForClosedChannels() error: ' + (e instanceof Error ? e.message : e)); } From 50b9644bd0a0af1604763ff012cc497e7dadc83d Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 30 Aug 2022 17:43:22 -0400 Subject: [PATCH 18/31] Add mainnet lightning api docs --- .../src/app/docs/api-docs/api-docs-data.ts | 1423 +++++++++++++++++ 1 file changed, 1423 insertions(+) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 1b02ceceb..f733e751d 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -5977,6 +5977,1429 @@ export const restApiDocsData = [ } } }, + { + type: "category", + category: "lightning", + fragment: "lightning", + title: "Lightning", + showConditions: bitcoinNetworks + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-lightning-network-stats", + title: "GET Network Stats", + description: { + default: "

Returns network-wide stats such as total number of channels and nodes, total capacity, and average/median fee figures.

Pass one of the following for :interval: latest, 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y.

" + }, + urlString: "/v1/lightning/statistics/:interval", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/statistics/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`latest`], + response: `{ + "latest": { + "id": 163, + "added": "2022-08-30T00:00:00.000Z", + "channel_count": 81690, + "node_count": 15851, + "total_capacity": 460820222344, + "tor_nodes": 11455, + "clearnet_nodes": 2305, + "unannounced_nodes": 974, + "avg_capacity": 5641085, + "avg_fee_rate": 497, + "avg_base_fee_mtokens": 915, + "med_capacity": 1500000, + "med_fee_rate": 40, + "med_base_fee_mtokens": 100, + "clearnet_tor_nodes": 1117 + } +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-lightning-nodes-channels", + title: "GET Nodes/Channels", + description: { + default: "

Returns Lightning nodes and channels that match a full-text, case-insensitive search :query across node aliases, node pubkeys, channel IDs, and short channel IDs.

" + }, + urlString: "/v1/lightning/search?searchText=:query", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/search?searchText=%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`ACINQ`], + response: `{ + "nodes": [ + { + "public_key": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "alias": "ACINQ", + "capacity": 35920090247, + "channels": 2907 + }, + { + "public_key": "03d3902b46d6ab9558a76cbf91b27d093c0a3c54e59f33c7eb4bd643dbb3b1b5b0", + "alias": "Acinq", + "capacity": null, + "channels": null + } + ], + "channels": [] +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-lightning-nodes-country", + title: "GET Nodes in Country", + description: { + default: "

Returns a list of Lightning nodes running on clearnet in the requested :country, where :country is an ISO Alpha-2 country code.

" + }, + urlString: "/v1/lightning/nodes/country/:country", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/country/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`ch`], + response: `{ + "country": { + "de": "Schweiz", + "en": "Switzerland", + "es": "Suiza", + "fr": "Suisse", + "ja": "スイス連邦", + "pt-BR": "Suíça", + "ru": "Швейцария", + "zh-CN": "瑞士" + }, + "nodes": [ + { + "public_key": "033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025", + "capacity": 54339697486, + "channels": 991, + "alias": "bfx-lnd0", + "first_seen": 1574813156, + "updated_at": 1661814056, + "city": { + "de": "Zürich", + "en": "Zurich", + "es": "Zúrich", + "fr": "Zurich", + "ja": "チューリッヒ", + "pt-BR": "Zurique", + "ru": "Цюрих", + "zh-CN": "苏黎世" + }, + "country": { + "de": "Schweiz", + "en": "Switzerland", + "es": "Suiza", + "fr": "Suisse", + "ja": "スイス連邦", + "pt-BR": "Suíça", + "ru": "Швейцария", + "zh-CN": "瑞士" + }, + "iso_code": "CH", + "subdivision": { + "de": "Zürich", + "en": "Zurich", + "fr": "Zurich" + } + }, + ... + ] +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-country-node-stats", + title: "GET Node Stats Per Country", + description: { + default: "

Returns aggregate capacity and number of clearnet nodes per country. Capacity figures are in satoshis.

" + }, + urlString: "/v1/lightning/nodes/countries", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/countries`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `[ + { + "name": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso": "US", + "count": 2775, + "share": 34.53, + "capacity": "372732844657" + }, + { + "name": { + "de": "Frankreich", + "en": "France", + "es": "Francia", + "fr": "France", + "ja": "フランス共和国", + "pt-BR": "França", + "ru": "Франция", + "zh-CN": "法国" + }, + "iso": "FR", + "count": 972, + "share": 12.09, + "capacity": "7740713270" + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-isp-nodes", + title: "GET ISP Nodes", + description: { + default: "

Returns a list of nodes hosted by a specified :isp, where :isp is an ISP's ASN.

" + }, + urlString: "/v1/lightning/nodes/isp/:isp", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/isp/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`16509`], + response: `{ + "isp": "Amazon.com", + "nodes": [ + { + "public_key": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "capacity": 36010390247, + "channels": 2907, + "alias": "ACINQ", + "first_seen": 1522941222, + "updated_at": 1661274935, + "city": null, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso_code": "US", + "subdivision": null + }, + ... + ] +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-isp-node-stats", + title: "GET Node Stats Per ISP", + description: { + default: "

Returns aggregate capacity, number of nodes, and number of channels per ISP. Capacity figures are in satoshis.

" + }, + urlString: "/v1/lightning/nodes/isp-ranking", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/isp-ranking`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `{ + "clearnetCapacity": 417154330493, + "torCapacity": 36605381932, + "unknownCapacity": 6678700534, + "ispRanking": [ + [ + "14061", //ASN + "DigitalOcean", //ISP name + 43681728521, //aggregate capacity, in sats + 5028, //total number of channels + 192 //number of nodes + ], + [ + "701", + "Verizon Internet Services", + 3047086363, + 507, + 55 + ], + [ + "396982,15169", + "Google Cloud", + 139554933568, + 2747, + 78 + ], + ... + ] +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-top-100-nodes", + title: "GET Top 100 Nodes", + description: { + default: "

Returns two lists of the top 100 nodes: one ordered by liquidity (aggregate channel capacity) and the other ordered by connectivity (number of open channels).

" + }, + urlString: "/v1/lightning/nodes/rankings", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/rankings`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `{ + "topByCapacity": [ + { + "publicKey": "033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025", + "alias": "bfx-lnd0", + "capacity": 54361697486 + }, + { + "publicKey": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "alias": "ACINQ", + "capacity": 36010516297 + }, + ... + ], + "topByChannels": [ + { + "publicKey": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "alias": "ACINQ", + "channels": 2908 + }, + { + "publicKey": "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226", + "alias": "WalletOfSatoshi.com", + "channels": 2771 + }, + ... + ] +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-top-100-nodes-liquidity", + title: "GET Top 100 Nodes by Liquidity", + description: { + default: "

Returns a list of the top 100 nodes by liquidity (aggregate channel capacity).

" + }, + urlString: "/v1/lightning/nodes/rankings/liquidity", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/rankings/liquidity`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `[ + { + "publicKey": "033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025", + "alias": "bfx-lnd0", + "capacity": 54361697486, + "channels": 993, + "firstSeen": 1574813156, + "updatedAt": 1661814056, + "city": { + "de": "Zürich", + "en": "Zurich", + "es": "Zúrich", + "fr": "Zurich", + "ja": "チューリッヒ", + "pt-BR": "Zurique", + "ru": "Цюрих", + "zh-CN": "苏黎世" + }, + "country": { + "de": "Schweiz", + "en": "Switzerland", + "es": "Suiza", + "fr": "Suisse", + "ja": "スイス連邦", + "pt-BR": "Suíça", + "ru": "Швейцария", + "zh-CN": "瑞士" + } + }, + { + "publicKey": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "alias": "ACINQ", + "capacity": 36010516297, + "channels": 2908, + "firstSeen": 1522941222, + "updatedAt": 1661274935, + "city": null, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + } + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-top-100-nodes-connectivity", + title: "GET Top 100 Nodes by Connectivity", + description: { + default: "

Returns a list of the top 100 nodes by connectivity (number of open channels).

" + }, + urlString: "/v1/lightning/nodes/rankings/connectivity", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/rankings/connectivity`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `[ + { + "publicKey": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "alias": "ACINQ", + "channels": 2908, + "capacity": 36010516297, + "firstSeen": 1522941222, + "updatedAt": 1661274935, + "city": null, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + } + }, + { + "publicKey": "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226", + "alias": "WalletOfSatoshi.com", + "channels": 2772, + "capacity": 15464503162, + "firstSeen": 1601429940, + "updatedAt": 1661812116, + "city": { + "de": "Vancouver", + "en": "Vancouver", + "es": "Vancouver", + "fr": "Vancouver", + "ja": "バンクーバー市", + "pt-BR": "Vancôver", + "ru": "Ванкувер" + }, + "country": { + "de": "Kanada", + "en": "Canada", + "es": "Canadá", + "fr": "Canada", + "ja": "カナダ", + "pt-BR": "Canadá", + "ru": "Канада", + "zh-CN": "加拿大" + } + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-top-100-oldest-nodes", + title: "GET Top 100 Oldest Nodes", + description: { + default: "

Returns a list of the top 100 oldest nodes.

" + }, + urlString: "/v1/lightning/nodes/rankings/age", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/rankings/age`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `[ + { + "publicKey": "02d4531a2f2e6e5a9033d37d548cff4834a3898e74c3abe1985b493c42ebbd707d", + "alias": "coinfinity.co", + "channels": 13, + "capacity": 35945717, + "firstSeen": 1518001533, + "updatedAt": 1661713804, + "city": { + "de": "Brüssel", + "en": "Brussels", + "es": "Bruselas", + "fr": "Bruxelles", + "ja": "ブリュッセル", + "pt-BR": "Bruxelas", + "ru": "Брюссель", + "zh-CN": "布鲁塞尔" + }, + "country": { + "de": "Belgien", + "en": "Belgium", + "es": "Bélgica", + "fr": "Belgique", + "ja": "ベルギー王国", + "pt-BR": "Bélgica", + "ru": "Бельгия", + "zh-CN": "比利时" + } + }, + { + "publicKey": "024bd94f0425590434538fd21d4e58982f7e9cfd8f339205a73deb9c0e0341f5bd", + "alias": "CL.rompert.com🔵 ", + "channels": 2, + "capacity": 600000, + "firstSeen": 1520596684, + "updatedAt": 1603261631, + "city": { + "de": "Clifton", + "en": "Clifton", + "ja": "クリフトン", + "pt-BR": "Clifton", + "ru": "Клифтон", + "zh-CN": "克利夫頓" + }, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + } + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-node-stats", + title: "GET Node Stats", + description: { + default: "

Returns details about a node with the given :pubKey.

" + }, + urlString: "/v1/lightning/nodes/:pubKey", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`033ac2f9f7ff643c235cc247c521663924aff73b26b38118a6c6821460afcde1b3`], + response: `{ + "public_key": "033ac2f9f7ff643c235cc247c521663924aff73b26b38118a6c6821460afcde1b3", + "alias": "Red.de.Rayos", + "first_seen": 1521504055, + "updated_at": 1661869523, + "color": "#68f442", + "sockets": "84.44.203.181:9735", + "as_number": 8422, + "city_id": 2886242, + "country_id": 2921044, + "subdivision_id": 2861876, + "longitude": 6.9489, + "latitude": 50.9298, + "iso_code": "DE", + "as_organization": "NetCologne GmbH", + "city": { + "de": "Köln", + "en": "Cologne", + "es": "Colonia", + "fr": "Cologne", + "ja": "ケルン", + "pt-BR": "Colônia", + "ru": "Кёльн", + "zh-CN": "科隆" + }, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + }, + "subdivision": { + "de": "Nordrhein-Westfalen", + "en": "North Rhine-Westphalia", + "es": "Renania del Norte-Westfalia", + "fr": "Rhénanie du Nord-Westphalie", + "ru": "Северный Рейн-Вестфалия" + }, + "active_channel_count": 55, + "capacity": "31505027", + "opened_channel_count": 55, + "closed_channel_count": 111 +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-historical-node-stats", + title: "GET Historical Node Stats", + description: { + default: "

Returns historical stats for a node with the given :pubKey.

" + }, + urlString: "/v1/lightning/nodes/:pubKey/statistics", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/nodes/%{1}/statistics`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`033ac2f9f7ff643c235cc247c521663924aff73b26b38118a6c6821460afcde1b3`], + response: `[ + { + "added": 1661817600, + "capacity": 31505027, + "channels": 55 + }, + { + "added": 1661731200, + "capacity": 31505027, + "channels": 55 + }, + { + "added": 1655078400, + "capacity": 26487523, + "channels": 43 + }, + { + "added": 1654992000, + "capacity": 32692287, + "channels": 57 + }, + { + "added": 1654905600, + "capacity": 32692287, + "channels": 57 + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-channel", + title: "GET Channel", + description: { + default: "

Returns info about a Lightning channel with the given :channelId.

" + }, + urlString: "/v1/lightning/channels/:channelId", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/channels/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`768457472831193088`], + response: `{ + "id": "768457472831193088", + "short_id": "698908x1305x0", + "capacity": 16777215, + "transaction_id": "9f248ff82f6ff4c112c218438cfde8260623663bc85a360d09a13b9a9b083564", + "transaction_vout": 0, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-08-25T23:05:40.000Z", + "created": "2021-09-04T00:10:42.000Z", + "status": 1, + "node_left": { + "alias": "CoinGate", + "public_key": "0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3", + "channels": 1, + "capacity": 20000, + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-25T23:05:40.000Z", + "longitude": 8.6843, + "latitude": 50.1188 + }, + "node_right": { + "alias": "Blixt Wallet 🟡", + "public_key": "0230a5bca558e6741460c13dd34e636da28e52afd91cf93db87ed1b0392a7466eb", + "channels": 3, + "capacity": 34754430, + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 180, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-25T18:44:00.000Z", + "longitude": 9.491, + "latitude": 51.2993 + } +}` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-channels-from-txid", + title: "GET Channels from TXID", + description: { + default: "

Returns channels that correspond to the given :txid (multiple transaction IDs can be specified).

" + }, + urlString: "/v1/lightning/channels/txids?txId[]=:txid", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/channels/txids?txId[]=%{1}&txId[]=%{2}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`c3173549f502ede6440d5c48ea74af5607d88484c7a912bbef73d430049f8af4`,`d78f0b41a263af3df91fa4171cc2f60c40196aaf8f4bde5d1c8ff4474cfe753b`], + response: `[ + { + "inputs": {}, + "outputs": { + "1": { + "id": "819296691409584129", + "short_id": "745146x287x1", + "capacity": 300000000, + "transaction_id": "c3173549f502ede6440d5c48ea74af5607d88484c7a912bbef73d430049f8af4", + "transaction_vout": 1, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-08-25T18:44:07.000Z", + "created": "2022-07-16T00:11:33.000Z", + "status": 1, + "node_left": { + "alias": "River Financial 1", + "public_key": "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a", + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 500, + "is_disabled": 0, + "max_htlc_mtokens": 297000000000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-23T17:53:43.000Z" + }, + "node_right": { + "alias": "0204a91bb5802ad0a799", + "public_key": "0204a91bb5802ad0a799acfd86ef566da03d80cc9e13acb01e680634bf64188a0d", + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 152, + "is_disabled": 0, + "max_htlc_mtokens": 297000000000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-25T18:44:07.000Z" + } + } + } + }, + { + "inputs": {}, + "outputs": { + "1": { + "id": "814662250034036737", + "short_id": "740931x2355x1", + "capacity": 300000000, + "transaction_id": "d78f0b41a263af3df91fa4171cc2f60c40196aaf8f4bde5d1c8ff4474cfe753b", + "transaction_vout": 1, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-08-28T18:54:40.000Z", + "created": "2022-06-15T16:18:33.000Z", + "status": 1, + "node_left": { + "alias": "bfx-lnd0", + "public_key": "033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025", + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 297000000000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-25T18:44:03.000Z" + }, + "node_right": { + "alias": "River Financial 1", + "public_key": "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a", + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 750, + "is_disabled": 0, + "max_htlc_mtokens": 297000000000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-28T18:54:40.000Z" + } + } + } + } +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-channels-from-pubkey", + title: "GET Channels from Node Pubkey", + description: { + default: "

Returns a list of a node's channels given its :pubKey. Ten channels are returned at a time. Use :index for paging. :channelStatus can be open, active, or closed.

" + }, + urlString: "/v1/lightning/channels?public_key=:pubKey&status=:channelStatus", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/channels?public_key=%{1}&status=%{2}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`026165850492521f4ac8abd9bd8088123446d126f648ca35e60f88177dc149ceb2`,`open`], + response: `[ + { + "status": 1, + "closing_reason": null, + "capacity": 59200000, + "short_id": "751361x1324x1", + "id": "826130156244172801", + "fee_rate": 1, + "node": { + "alias": "ipayblue.com", + "public_key": "032fe854a231aeb2357523ee6ca263ae04ce53eee8a13767ecbb911b69fefd8ace", + "channels": 65, + "capacity": "856675361" + } + }, + { + "status": 1, + "closing_reason": null, + "capacity": 51000000, + "short_id": "750792x1586x1", + "id": "825504534145138689", + "fee_rate": 1, + "node": { + "alias": "Escher", + "public_key": "02b515c74f334dee09821bee299fcbd9668182730c5719b25a8f262b28893198b0", + "channels": 50, + "capacity": "2202925844" + } + }, + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-channel-geodata", + title: "GET Channel Geodata", + description: { + default: "

Returns a list of channels with corresponding node geodata.

" + }, + urlString: "/v1/lightning/channels-geo", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/channels-geo`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [], + response: `[ + [ + "03120ac28af913889cbc3cb86d7aff12bc0abe939f1fa9fb1980bdff8483197092", + "LIGHTNING2", + -77.2278, + 38.9567, + "03baa70886d9200af0ffbd3f9e18d96008331c858456b16e3a9b41e735c6208fef", + "LIGHTNING", + -77.2278, + 38.9567 + ], + [ + "033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025", + "bfx-lnd0", + 8.5671, + 47.3682, + "028d98b9969fbed53784a36617eb489a59ab6dc9b9d77fcdca9ff55307cd98e3c4", + "OpenNode.com", + -83.0061, + 39.9625 + ], + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + }, + { + type: "endpoint", + category: "lightning", + httpRequestMethod: "GET", + fragment: "get-channel-geodata-node", + title: "GET Channel Geodata for Node", + description: { + default: "

Returns a list of channels with corresponding geodata for a node with the given :pubKey.

" + }, + urlString: "/v1/lightning/channels-geo/:pubKey", + showConditions: bitcoinNetworks, + showJsExamples: showJsExamplesDefaultFalse, + codeExample: { + default: { + codeTemplate: { + curl: `/api/v1/lightning/channels-geo/%{1}`, + commonJS: ``, + esModule: `` + }, + codeSampleMainnet: { + esModule: [], + commonJS: [], + curl: [`03d607f3e69fd032524a867b288216bfab263b6eaee4e07783799a6fe69bb84fac`], + response: `[ + [ + "03d607f3e69fd032524a867b288216bfab263b6eaee4e07783799a6fe69bb84fac", + "Bitrefill", + -77.4903, + 39.0469, + "024a2e265cd66066b78a788ae615acdc84b5b0dec9efac36d7ac87513015eaf6ed", + "Bitrefill", + -6.2591, + 53.3379 + ], + [ + "03d607f3e69fd032524a867b288216bfab263b6eaee4e07783799a6fe69bb84fac", + "Bitrefill", + -77.4903, + 39.0469, + "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f", + "Bitrefill Routing", + -6.2591, + 53.3379 + ], + ... +]` + }, + codeSampleTestnet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleSignet: { + esModule: [], + commonJS: [], + curl: [`3y`], + response: `{ + +}` + }, + codeSampleLiquid: emptyCodeSample, + codeSampleLiquidTestnet: emptyCodeSample, + codeSampleBisq: emptyCodeSample, + } + } + } ]; export const faqData = [ From 25ee1acde939337b3b704c736f458c50de4a92a7 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 6 Sep 2022 08:30:17 -0400 Subject: [PATCH 19/31] Add testnet lightning api docs --- .../src/app/docs/api-docs/api-docs-data.ts | 661 ++++++++++++++++-- 1 file changed, 609 insertions(+), 52 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index f733e751d..a08d058ea 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -6030,9 +6030,25 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`latest`], response: `{ - + "latest": { + "id": 13, + "added": "2022-08-30T00:00:00.000Z", + "channel_count": 5101, + "node_count": 1806, + "total_capacity": 43341092977, + "tor_nodes": 288, + "clearnet_nodes": 736, + "unannounced_nodes": 656, + "avg_capacity": 8496588, + "avg_fee_rate": 354, + "avg_base_fee_mtokens": 1183, + "med_capacity": 1148313, + "med_fee_rate": 1, + "med_base_fee_mtokens": 1000, + "clearnet_tor_nodes": 126 + } }` }, codeSampleSignet: { @@ -6093,9 +6109,23 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`lnd`], response: `{ - + "nodes": [ + { + "public_key": "02be8f360e57600486b93dd33ea0872a4e14a259924ba4084f27d693a77d151158", + "alias": "lndus1.dev.zaphq.io", + "capacity": 762968876, + "channels": 27 + }, + { + "public_key": "028c3640c57ffe47eb41db8225968833c5032f297aeba98672d6f7037090d59e3f", + "alias": "lndus0.next.zaphq.io", + "capacity": 641040063, + "channels": 26 + } + ], + "channels": [] }` }, codeSampleSignet: { @@ -6188,9 +6218,55 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`ch`], response: `{ - + "country": { + "de": "Schweiz", + "en": "Switzerland", + "es": "Suiza", + "fr": "Suisse", + "ja": "スイス連邦", + "pt-BR": "Suíça", + "ru": "Швейцария", + "zh-CN": "瑞士" + }, + "nodes": [ + { + "public_key": "0200a7f20e51049363cb7f2a0865fe072464d469dca0ac34c954bb3d4b552b6e95", + "capacity": 94802991, + "channels": 15, + "alias": "looptest", + "first_seen": 1601298108, + "updated_at": 1661857089, + "city": { + "de": "Thun", + "en": "Thun", + "es": "Thun", + "fr": "Thoune", + "ja": "トゥーン", + "pt-BR": "Tune", + "ru": "Тун", + "zh-CN": "图恩" + }, + "country": { + "de": "Schweiz", + "en": "Switzerland", + "es": "Suiza", + "fr": "Suisse", + "ja": "スイス連邦", + "pt-BR": "Suíça", + "ru": "Швейцария", + "zh-CN": "瑞士" + }, + "iso_code": "CH", + "subdivision": { + "de": "Bern", + "en": "Bern", + "fr": "Berne" + } + }, + ... + ] }` }, codeSampleSignet: { @@ -6269,10 +6345,42 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "name": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso": "US", + "count": 304, + "share": 37.95, + "capacity": "23906225936" + }, + { + "name": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + }, + "iso": "DE", + "count": 85, + "share": 10.61, + "capacity": "1878052329" + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -6342,9 +6450,33 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`16509`], response: `{ - + "isp": "Amazon.com", + "nodes": [ + { + "public_key": "03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134", + "capacity": 2041664924, + "channels": 70, + "alias": "endurance", + "first_seen": 1566809576, + "updated_at": 1660926529, + "city": null, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso_code": "US", + "subdivision": null + }, + ... + ] }` }, codeSampleSignet: { @@ -6417,9 +6549,28 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [], response: `{ - + "clearnetCapacity": 21714967205, + "torCapacity": 1183591190, + "unknownCapacity": 965032372, + "ispRanking": [ + [ + "14080", //ASN + "Telmex Colombia S.A.", //ISP Name + 220063321, //aggregate capacity, in sats + 98, //total number of channels + 1 //number of nodes + ], + [ + "16509,14618", + "Amazon.com", + 5590657952, + 445, + 41 + ], + ... + ] }` }, codeSampleSignet: { @@ -6491,9 +6642,34 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [], response: `{ - + "topByCapacity": [ + { + "publicKey": "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "alias": "aranguren.org", + "capacity": 17155095532 + }, + { + "publicKey": "0225ff2ae6a3d9722b625072503c2f64f6eddb78d739379d2ee55a16b3b0ed0a17", + "alias": "STRANGEIRON", + "capacity": 7038263480 + }, + ... + ], + "topByChannels": [ + { + "publicKey": "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "alias": "aranguren.org", + "channels": 489 + }, + { + "publicKey": "030425d8babe3ab6dfc065e69dd8b10ce6738c86ea7d634324c913e21620fa5eaf", + "alias": "MTest441", + "channels": 258 + }, + ... + ] }` }, codeSampleSignet: { @@ -6587,10 +6763,66 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "alias": "aranguren.org", + "capacity": 17155095532, + "channels": 489, + "firstSeen": 1521457251, + "updatedAt": 1662035238, + "city": { + "de": "Melbourne", + "en": "Melbourne", + "es": "Melbourne", + "fr": "Melbourne", + "ja": "メルボルン", + "pt-BR": "Melbourne", + "ru": "Мельбурн", + "zh-CN": "墨尔本" + }, + "country": { + "de": "Australien", + "en": "Australia", + "es": "Australia", + "fr": "Australie", + "ja": "オーストラリア", + "pt-BR": "Austrália", + "ru": "Австралия", + "zh-CN": "澳大利亚" + } + }, + { + "publicKey": "0225ff2ae6a3d9722b625072503c2f64f6eddb78d739379d2ee55a16b3b0ed0a17", + "alias": "STRANGEIRON", + "capacity": 7038263480, + "channels": 95, + "firstSeen": 1651725065, + "updatedAt": 1661958465, + "city": { + "de": "Melbourne", + "en": "Melbourne", + "es": "Melbourne", + "fr": "Melbourne", + "ja": "メルボルン", + "pt-BR": "Melbourne", + "ru": "Мельбурн", + "zh-CN": "墨尔本" + }, + "country": { + "de": "Australien", + "en": "Australia", + "es": "Australia", + "fr": "Australie", + "ja": "オーストラリア", + "pt-BR": "Austrália", + "ru": "Австралия", + "zh-CN": "澳大利亚" + } + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -6682,10 +6914,74 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "alias": "aranguren.org", + "channels": 489, + "capacity": 17155095532, + "firstSeen": 1521457251, + "updatedAt": 1662035238, + "city": { + "de": "Melbourne", + "en": "Melbourne", + "es": "Melbourne", + "fr": "Melbourne", + "ja": "メルボルン", + "pt-BR": "Melbourne", + "ru": "Мельбурн", + "zh-CN": "墨尔本" + }, + "country": { + "de": "Australien", + "en": "Australia", + "es": "Australia", + "fr": "Australie", + "ja": "オーストラリア", + "pt-BR": "Austrália", + "ru": "Австралия", + "zh-CN": "澳大利亚" + } + }, + { + "publicKey": "030425d8babe3ab6dfc065e69dd8b10ce6738c86ea7d634324c913e21620fa5eaf", + "alias": "MTest441", + "channels": 258, + "capacity": 4113430492, + "firstSeen": 1640955758, + "updatedAt": 1662035216, + "city": null, + "country": null + }, + { + "publicKey": "0270685ca81a8e4d4d01beec5781f4cc924684072ae52c507f8ebe9daf0caaab7b", + "alias": "0270685ca81a8e4d4d01", + "channels": 164, + "capacity": 638119030, + "firstSeen": 1535613050, + "updatedAt": 1662034882, + "city": { + "de": "Clifton", + "en": "Clifton", + "ja": "クリフトン", + "pt-BR": "Clifton", + "ru": "Клифтон", + "zh-CN": "克利夫頓" + }, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + } + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -6785,10 +7081,66 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "alias": "aranguren.org", + "channels": 489, + "capacity": 17155095532, + "firstSeen": 1521457251, + "updatedAt": 1662035238, + "city": { + "de": "Melbourne", + "en": "Melbourne", + "es": "Melbourne", + "fr": "Melbourne", + "ja": "メルボルン", + "pt-BR": "Melbourne", + "ru": "Мельбурн", + "zh-CN": "墨尔本" + }, + "country": { + "de": "Australien", + "en": "Australia", + "es": "Australia", + "fr": "Australie", + "ja": "オーストラリア", + "pt-BR": "Austrália", + "ru": "Австралия", + "zh-CN": "澳大利亚" + } + }, + { + "publicKey": "0277622bf4c497475960bf91bd3c673a4cb4e9b589cebfde9700c197b3989cc1b8", + "alias": "CoinGate", + "channels": 11, + "capacity": 91768515, + "firstSeen": 1525964963, + "updatedAt": 1661991683, + "city": { + "de": "Frankfurt am Main", + "en": "Frankfurt am Main", + "es": "Francfort", + "fr": "Francfort-sur-le-Main", + "ja": "フランクフルト・アム・マイン", + "pt-BR": "Frankfurt am Main", + "ru": "Франкфурт", + "zh-CN": "法兰克福" + }, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + } + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -6878,9 +7230,52 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`03f060953bef5b777dc77e44afa3859d022fc1a77c55138deb232ad7255e869c00`], response: `{ - + "public_key": "03f060953bef5b777dc77e44afa3859d022fc1a77c55138deb232ad7255e869c00", + "alias": "Boltz", + "first_seen": 1551006126, + "updated_at": 1662033208, + "color": "#ff9800", + "sockets": "35.237.24.136:9735,idz7qlezif6hgmjkpmuelnsssyxea2lwan562a5gla7jmlxsl5cb2cad.onion:9735", + "as_number": 396982, + "city_id": 4589387, + "country_id": 6252001, + "subdivision_id": 4597040, + "longitude": -79.9746, + "latitude": 32.8608, + "iso_code": "US", + "as_organization": "Google Cloud", + "city": { + "en": "North Charleston", + "ja": "ノースチャールストン", + "pt-BR": "North Charleston", + "ru": "Норт-Чарлстон", + "zh-CN": "北查尔斯顿" + }, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "subdivision": { + "en": "South Carolina", + "es": "Carolina del Sur", + "fr": "Caroline du Sud", + "ja": "サウスカロライナ州", + "pt-BR": "Carolina do Sul", + "ru": "Южная Каролина", + "zh-CN": "南卡罗来纳州" + }, + "active_channel_count": 46, + "capacity": "111724126", + "opened_channel_count": 165, + "closed_channel_count": 1 }` }, codeSampleSignet: { @@ -6952,10 +7347,25 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`0225ff2ae6a3d9722b625072503c2f64f6eddb78d739379d2ee55a16b3b0ed0a17`], + response: `[ + { + "added": 1662422400, + "capacity": 7038263480, + "channels": 95 + }, + { + "added": 1662336000, + "capacity": 7038263480, + "channels": 95 + }, + { + "added": 1662249600, + "capacity": 7038263480, + "channels": 95 + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -7040,9 +7450,48 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`2478509215728271360`], response: `{ - + "id": "2478509215728271360", + "short_id": "2254191x4x0", + "capacity": 16777215, + "transaction_id": "6b711b07b019d73ad432f401c01ac6ea253fbe2778388e5a686b5777678556c7", + "transaction_vout": 0, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-08-31T08:30:42.000Z", + "created": "2022-06-05T16:26:31.000Z", + "status": 1, + "node_left": { + "alias": "scarce-city-testnet", + "public_key": "0304fa1da67d441b382e3b2142a1980840276d89b6477812da8d26487b5ffa938c", + "channels": 15, + "capacity": 104876207, + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 16777215000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-31T08:30:42.000Z", + "longitude": -123.1236, + "latitude": 49.2526 + }, + "node_right": { + "alias": "STRANGEIRON", + "public_key": "0225ff2ae6a3d9722b625072503c2f64f6eddb78d739379d2ee55a16b3b0ed0a17", + "channels": 95, + "capacity": 7038263480, + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 10, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1, + "updated_at": "2022-08-27T20:22:06.000Z", + "longitude": 144.9669, + "latitude": -37.8159 + } }` }, codeSampleSignet: { @@ -7166,10 +7615,48 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`6b711b07b019d73ad432f401c01ac6ea253fbe2778388e5a686b5777678556c7`], + response: `[ + { + "inputs": {}, + "outputs": { + "0": { + "id": "2478509215728271360", + "short_id": "2254191x4x0", + "capacity": 16777215, + "transaction_id": "6b711b07b019d73ad432f401c01ac6ea253fbe2778388e5a686b5777678556c7", + "transaction_vout": 0, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-08-31T08:30:42.000Z", + "created": "2022-06-05T16:26:31.000Z", + "status": 1, + "node_left": { + "alias": "scarce-city-testnet", + "public_key": "0304fa1da67d441b382e3b2142a1980840276d89b6477812da8d26487b5ffa938c", + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 16777215000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-08-31T08:30:42.000Z" + }, + "node_right": { + "alias": "STRANGEIRON", + "public_key": "0225ff2ae6a3d9722b625072503c2f64f6eddb78d739379d2ee55a16b3b0ed0a17", + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 10, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1, + "updated_at": "2022-08-27T20:22:06.000Z" + } + } + } + } +]` }, codeSampleSignet: { esModule: [], @@ -7243,10 +7730,40 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`0200202c1f23899d03bf3f37c87d348e6847bbd91e407df91a713c7dcf3442738b`, `open`], + response: `[ + { + "status": 1, + "closing_reason": null, + "closing_date": null, + "capacity": 8000000, + "short_id": "2223130x18x0", + "id": "2444357285058838528", + "fee_rate": 10, + "node": { + "alias": "Gilgamesh Lightning Testnet", + "public_key": "034997a34858a25dc453a722efc1545d8c7749cbd4587a8d2ef149d257babd8357", + "channels": 121, + "capacity": "512199932" + } + }, + { + "status": 0, + "closing_reason": null, + "closing_date": null, + "capacity": 1000000, + "short_id": "2223130x19x0", + "id": "2444357285058904064", + "fee_rate": 0, + "node": { + "alias": "routing.testnet.lnmarkets.com", + "public_key": "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431", + "channels": 22, + "capacity": "246940161" + } + }, + ... +]` }, codeSampleSignet: { esModule: [], @@ -7312,10 +7829,30 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + [ + "038863cf8ab91046230f561cd5b386cbff8309fa02e3f0c3ed161a3aeb64a643b9", + "aranguren.org", + 144.9669, + -37.8159, + "028c3640c57ffe47eb41db8225968833c5032f297aeba98672d6f7037090d59e3f", + "lndus0.next.zaphq.io", + -79.9746, + 32.8608 + ], + [ + "02be8f360e57600486b93dd33ea0872a4e14a259924ba4084f27d693a77d151158", + "lndus1.dev.zaphq.io", + -79.9746, + 32.8608, + "0273ec4a4c80e767aca1477592649ad6e709ad31e7435668043a9dceccb3020f35", + "lndwr1.dev.zaphq.io", + -79.9746, + 32.8608 + ], + ... +]` }, codeSampleSignet: { esModule: [], @@ -7381,10 +7918,30 @@ export const restApiDocsData = [ codeSampleTestnet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`0273ec4a4c80e767aca1477592649ad6e709ad31e7435668043a9dceccb3020f35`], + response: `[ + [ + "039b1717db1193eb332d3c0bfdcce90a6aab60efa478b60963d3b406a8fc45134a", + "testnet.demo.btcpayserver.org", + -79.3503, + 43.7806, + "0273ec4a4c80e767aca1477592649ad6e709ad31e7435668043a9dceccb3020f35", + "lndwr1.dev.zaphq.io", + -79.9746, + 32.8608 + ], + [ + "0273ec4a4c80e767aca1477592649ad6e709ad31e7435668043a9dceccb3020f35", + "lndwr1.dev.zaphq.io", + -79.9746, + 32.8608, + "02c6fbedc6ca81d4db5883f1d01481c8187d5f85075729a658288a6d507f770ada", + "HAPPYTOLL", + -97.822, + 37.751 + ], + ... +]` }, codeSampleSignet: { esModule: [], From 5d986e86dea570d0666c0c45f5114a492ba1234a Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:22:12 -0400 Subject: [PATCH 20/31] Add signet lightning api docs --- .../src/app/docs/api-docs/api-docs-data.ts | 609 ++++++++++++++++-- 1 file changed, 557 insertions(+), 52 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index a08d058ea..7ea01584e 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -6054,9 +6054,25 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`latest`], response: `{ - + "latest": { + "id": 13, + "added": "2022-08-30T00:00:00.000Z", + "channel_count": 33, + "node_count": 24, + "total_capacity": 161821884, + "tor_nodes": 5, + "clearnet_nodes": 11, + "unannounced_nodes": 6, + "avg_capacity": 4903693, + "avg_fee_rate": 38, + "avg_base_fee_mtokens": 1061, + "med_capacity": 2000000, + "med_fee_rate": 1, + "med_base_fee_mtokens": 1000, + "clearnet_tor_nodes": 2 + } }` }, codeSampleLiquid: emptyCodeSample, @@ -6131,9 +6147,18 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`guggero`], response: `{ - + "nodes": [ + { + "public_key": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "capacity": 66577093, + "channels": 12, + "status": 1 + } + ], + "channels": [] }` }, codeSampleLiquid: emptyCodeSample, @@ -6272,9 +6297,91 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`us`], response: `{ - + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "nodes": [ + { + "public_key": "03f70ac4525c0014bbf380c069ce82d70946d37a56c027a2ed18609a3e60c3b353", + "capacity": 2000000, + "channels": 1, + "alias": "", + "first_seen": 1637708194, + "updated_at": 0, + "city": { + "en": "Oak Park", + "ru": "Оук-Парк" + }, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso_code": "US", + "subdivision": { + "en": "Illinois", + "es": "Illinois", + "fr": "Illinois", + "ja": "イリノイ州", + "pt-BR": "Ilinóis", + "ru": "Иллинойс", + "zh-CN": "伊利诺伊州" + } + }, + { + "public_key": "0397b15fd867541c53a3a5e27c021f7acad582684d05d120af572266c92c8a0313", + "capacity": 19802, + "channels": 1, + "alias": "pseudozach", + "first_seen": 1637620444, + "updated_at": 1637721257, + "city": { + "de": "Atlanta", + "en": "Atlanta", + "es": "Atlanta", + "fr": "Atlanta", + "ja": "アトランタ", + "pt-BR": "Atlanta", + "ru": "Атланта", + "zh-CN": "亚特兰大" + }, + "country": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso_code": "US", + "subdivision": { + "en": "Georgia", + "es": "Georgia", + "fr": "Géorgie", + "ja": "ジョージア州", + "pt-BR": "Geórgia", + "ru": "Джорджия", + "zh-CN": "乔治亚" + } + }, + ... + ] }` }, codeSampleLiquid: emptyCodeSample, @@ -6385,10 +6492,42 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "name": { + "de": "Vereinigte Staaten", + "en": "United States", + "es": "Estados Unidos", + "fr": "États Unis", + "ja": "アメリカ", + "pt-BR": "EUA", + "ru": "США", + "zh-CN": "美国" + }, + "iso": "US", + "count": 4, + "share": 36.36, + "capacity": "2059317" + }, + { + "name": { + "de": "Japan", + "en": "Japan", + "es": "Japón", + "fr": "Japon", + "ja": "日本", + "pt-BR": "Japão", + "ru": "Япония", + "zh-CN": "日本" + }, + "iso": "JP", + "count": 2, + "share": 18.18, + "capacity": "107710417" + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -6482,9 +6621,32 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`7684`], response: `{ - + "isp": "SAKURA Internet", + "nodes": [ + { + "public_key": "02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a", + "capacity": 51155987, + "channels": 15, + "alias": "珠美ノード⚡@wakiyamap", + "first_seen": 1612221581, + "updated_at": 1662382573, + "city": null, + "country": { + "de": "Japan", + "en": "Japan", + "es": "Japón", + "fr": "Japon", + "ja": "日本", + "pt-BR": "Japão", + "ru": "Япония", + "zh-CN": "日本" + }, + "iso_code": "JP", + "subdivision": null + } + ] }` }, codeSampleLiquid: emptyCodeSample, @@ -6576,9 +6738,28 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [], response: `{ - + "clearnetCapacity": 126914725, + "torCapacity": 1000000, + "unknownCapacity": 31150000, + "ispRanking": [ + [ + "1136", + "KPN", + 99878, + 1, + 1 + ], + [ + "24940", + "Hetzner Online GmbH", + 34877093, + 6, + 1 + ], + ... + ] }` }, codeSampleLiquid: emptyCodeSample, @@ -6675,9 +6856,34 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [], response: `{ - + "topByCapacity": [ + { + "publicKey": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "capacity": 66577093 + }, + { + "publicKey": "0271cf3881e6eadad960f47125434342e57e65b98a78afa99f9b4191c02dd7ab3b", + "alias": "eclair@wakiyamap", + "capacity": 56554430 + }, + ... + ], + "topByChannels": [ + { + "publicKey": "02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a", + "alias": "珠美ノード⚡@wakiyamap", + "channels": 15 + }, + { + "publicKey": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "channels": 12 + }, + ... + ] }` }, codeSampleLiquid: emptyCodeSample, @@ -6827,10 +7033,56 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "capacity": 66577093, + "channels": 12, + "firstSeen": 1608832520, + "updatedAt": 1662440260, + "city": null, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + } + }, + { + "publicKey": "0271cf3881e6eadad960f47125434342e57e65b98a78afa99f9b4191c02dd7ab3b", + "alias": "eclair@wakiyamap", + "capacity": 56554430, + "channels": 4, + "firstSeen": 1628031165, + "updatedAt": 1648064593, + "city": { + "de": "Ōsaka", + "en": "Osaka", + "es": "Osaka", + "fr": "Osaka", + "ja": "大阪市", + "pt-BR": "Osaka", + "ru": "Осака" + }, + "country": { + "de": "Japan", + "en": "Japan", + "es": "Japón", + "fr": "Japon", + "ja": "日本", + "pt-BR": "Japão", + "ru": "Япония", + "zh-CN": "日本" + } + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -6986,10 +7238,48 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a", + "alias": "珠美ノード⚡@wakiyamap", + "channels": 15, + "capacity": 51155987, + "firstSeen": 1612221581, + "updatedAt": 1662382573, + "city": null, + "country": { + "de": "Japan", + "en": "Japan", + "es": "Japón", + "fr": "Japon", + "ja": "日本", + "pt-BR": "Japão", + "ru": "Япония", + "zh-CN": "日本" + } + }, + { + "publicKey": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "channels": 12, + "capacity": 66577093, + "firstSeen": 1608832520, + "updatedAt": 1662440260, + "city": null, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + } + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7145,10 +7435,39 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + { + "publicKey": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "channels": 12, + "capacity": 66577093, + "firstSeen": 1608832520, + "updatedAt": 1662440260, + "city": null, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + } + }, + { + "publicKey": "03870a4c4c54a9b2e705023d706843ffbac5b0e95e2b80d4b02dc7a9efb5380322", + "alias": "03870a4c4c54a9b2e705", + "channels": 2, + "capacity": 30000000, + "firstSeen": 1608832520, + "updatedAt": 0, + "city": null, + "country": null + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7281,9 +7600,38 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120`], response: `{ - + "public_key": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "alias": "guggero", + "first_seen": 1608832520, + "updated_at": 1662440260, + "color": "#cccccc", + "sockets": "88.99.101.67:9735", + "as_number": 24940, + "city_id": null, + "country_id": 2921044, + "subdivision_id": null, + "longitude": 9.491, + "latitude": 51.2993, + "iso_code": "DE", + "as_organization": "Hetzner Online GmbH", + "city": null, + "country": { + "de": "Deutschland", + "en": "Germany", + "es": "Alemania", + "fr": "Allemagne", + "ja": "ドイツ連邦共和国", + "pt-BR": "Alemanha", + "ru": "Германия", + "zh-CN": "德国" + }, + "subdivision": null, + "active_channel_count": 12, + "capacity": "66577093", + "opened_channel_count": 16, + "closed_channel_count": 0 }` }, codeSampleLiquid: emptyCodeSample, @@ -7370,10 +7718,20 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120`], + response: `[ + { + "added": 1662422400, + "capacity": 66577093, + "channels": 12 + }, + { + "added": 1662336000, + "capacity": 63477093, + "channels": 9 + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7497,9 +7855,48 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], + curl: [`58998694435160064`], response: `{ - + "id": "58998694435160064", + "short_id": "53659x5x0", + "capacity": 16777215, + "transaction_id": "cbb18e4b23c2a27736fa5be559fee7efcc855f2dfb1f16b125f686c307513ef3", + "transaction_vout": 0, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-09-04T10:15:51.000Z", + "created": "2021-09-02T07:08:40.000Z", + "status": 1, + "node_left": { + "alias": "STRANGESUCKER-v0.12.0-11-gea4143", + "public_key": "03b9e6c1dec203f47efc95d003314d22cbb12a1324de4b091fe7d68f321a56322f", + "channels": 4, + "capacity": 55778192, + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 0, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1, + "updated_at": "2022-09-04T10:15:51.000Z", + "longitude": 19.1477, + "latitude": 48.7386 + }, + "node_right": { + "alias": "guggero", + "public_key": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "channels": 12, + "capacity": 66577093, + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 16777215000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-09-01T22:57:40.000Z", + "longitude": 9.491, + "latitude": 51.2993 + } }` }, codeSampleLiquid: emptyCodeSample, @@ -7661,10 +8058,48 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`cbb18e4b23c2a27736fa5be559fee7efcc855f2dfb1f16b125f686c307513ef3`], + response: `[ + { + "inputs": {}, + "outputs": { + "0": { + "id": "58998694435160064", + "short_id": "53659x5x0", + "capacity": 16777215, + "transaction_id": "cbb18e4b23c2a27736fa5be559fee7efcc855f2dfb1f16b125f686c307513ef3", + "transaction_vout": 0, + "closing_transaction_id": null, + "closing_reason": null, + "updated_at": "2022-09-04T10:15:51.000Z", + "created": "2021-09-02T07:08:40.000Z", + "status": 1, + "node_left": { + "alias": "STRANGESUCKER-v0.12.0-11-gea4143", + "public_key": "03b9e6c1dec203f47efc95d003314d22cbb12a1324de4b091fe7d68f321a56322f", + "base_fee_mtokens": 0, + "cltv_delta": 0, + "fee_rate": 0, + "is_disabled": 0, + "max_htlc_mtokens": 16609443000, + "min_htlc_mtokens": 1, + "updated_at": "2022-09-04T10:15:51.000Z" + }, + "node_right": { + "alias": "guggero", + "public_key": "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "base_fee_mtokens": 1000, + "cltv_delta": 0, + "fee_rate": 1, + "is_disabled": 0, + "max_htlc_mtokens": 16777215000, + "min_htlc_mtokens": 1000, + "updated_at": "2022-09-01T22:57:40.000Z" + } + } + } + } +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7768,10 +8203,40 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120`, `open`], + response: `[ + { + "status": 1, + "closing_reason": null, + "closing_date": null, + "capacity": 16777215, + "short_id": "53659x5x0", + "id": "58998694435160064", + "fee_rate": 1, + "node": { + "alias": "STRANGESUCKER-v0.12.0-11-gea4143", + "public_key": "03b9e6c1dec203f47efc95d003314d22cbb12a1324de4b091fe7d68f321a56322f", + "channels": 4, + "capacity": "55778192" + } + }, + { + "status": 1, + "closing_reason": null, + "closing_date": null, + "capacity": 15000000, + "short_id": "17498x2x1", + "id": "19239254462955521", + "fee_rate": 1, + "node": { + "alias": "03870a4c4c54a9b2e705", + "public_key": "03870a4c4c54a9b2e705023d706843ffbac5b0e95e2b80d4b02dc7a9efb5380322", + "channels": 2, + "capacity": "30000000" + } + }, + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7857,10 +8322,30 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [], + response: `[ + [ + "02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a", + "珠美ノード⚡@wakiyamap", + 139.6895, + 35.6897, + "0271cf3881e6eadad960f47125434342e57e65b98a78afa99f9b4191c02dd7ab3b", + "eclair@wakiyamap", + 135.4911, + 34.7135 + ], + [ + "03b9e6c1dec203f47efc95d003314d22cbb12a1324de4b091fe7d68f321a56322f", + "STRANGESUCKER-v0.12.0-11-gea4143", + 19.1477, + 48.7386, + "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "guggero", + 9.491, + 51.2993 + ], + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, @@ -7946,10 +8431,30 @@ export const restApiDocsData = [ codeSampleSignet: { esModule: [], commonJS: [], - curl: [`3y`], - response: `{ - -}` + curl: [`02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120`], + response: `[ + [ + "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "guggero", + 9.491, + 51.2993, + "0271cf3881e6eadad960f47125434342e57e65b98a78afa99f9b4191c02dd7ab3b", + "eclair@wakiyamap", + 135.4911, + 34.7135 + ], + [ + "02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a", + "珠美ノード⚡@wakiyamap", + 139.6895, + 35.6897, + "02ad48db0d1a7f7c3d186ddc57f8e62c49a1234fb829af9ccd3be1a4596bc39120", + "guggero", + 9.491, + 51.2993 + ], + ... +]` }, codeSampleLiquid: emptyCodeSample, codeSampleLiquidTestnet: emptyCodeSample, From 2d52dcd867028b352d25d710dee916e8be0bb823 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 6 Sep 2022 16:43:22 +0200 Subject: [PATCH 21/31] Fix wrong skeleton labels --- .../nodes-per-isp-chart.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html index ef1c71efd..093a8ad1a 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html @@ -10,14 +10,14 @@

-
Unknown capacity
+
Unknown capacity

-
Tor capacity
+
Tor capacity

@@ -80,19 +80,19 @@

-
Tagged ISPs
+
Clearnet capacity

-
Tagged capacity
+
Unknown capacity

-
Tagged nodes
+
Tor capacity

From 367c06dca68093cdefc8766cbee3ca38fb63409d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 6 Sep 2022 19:33:07 +0200 Subject: [PATCH 22/31] Show clearnet nodes on world map --- backend/src/api/explorer/nodes.api.ts | 43 ++++ backend/src/api/explorer/nodes.routes.ts | 14 +- .../asset-circulation.component.ts | 1 - .../nodes-map/nodes-map.component.html | 5 +- .../nodes-map/nodes-map.component.ts | 186 ++++++++++-------- .../nodes-per-country-chart.component.ts | 2 +- frontend/src/app/services/api.service.ts | 6 +- 7 files changed, 167 insertions(+), 90 deletions(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 128405ffd..b959d49a0 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -5,6 +5,49 @@ import { ILightningApi } from '../lightning/lightning-api.interface'; import { ITopNodesPerCapacity, ITopNodesPerChannels } from '../../mempool.interfaces'; class NodesApi { + public async $getWorldNodes(): Promise { + try { + let query = ` + SELECT nodes.public_key as publicKey, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias, + CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, + CAST(COALESCE(nodes.channels, 0) as INT) as channels, + nodes.longitude, nodes.latitude, + geo_names_country.names as country, geo_names_iso.names as isoCode + FROM nodes + LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + WHERE status = 1 AND nodes.as_number IS NOT NULL + ORDER BY capacity + `; + + const [nodes]: any[] = await DB.query(query); + + for (let i = 0; i < nodes.length; ++i) { + nodes[i].country = JSON.parse(nodes[i].country); + } + + query = ` + SELECT MAX(nodes.capacity) as maxLiquidity, MAX(nodes.channels) as maxChannels + FROM nodes + WHERE status = 1 AND nodes.as_number IS NOT NULL + `; + + const [maximums]: any[] = await DB.query(query); + + return { + maxLiquidity: maximums[0].maxLiquidity, + maxChannels: maximums[0].maxChannels, + nodes: nodes.map(node => [ + node.longitude, node.latitude, + node.publicKey, node.alias, node.capacity, node.channels, + node.country, node.isoCode + ]) + }; + } catch (e) { + logger.err(`Can't get world nodes list. Reason: ${e instanceof Error ? e.message : e}`); + } + } + public async $getNode(public_key: string): Promise { try { // General info diff --git a/backend/src/api/explorer/nodes.routes.ts b/backend/src/api/explorer/nodes.routes.ts index 7a5ff880a..cf3f75208 100644 --- a/backend/src/api/explorer/nodes.routes.ts +++ b/backend/src/api/explorer/nodes.routes.ts @@ -9,6 +9,7 @@ class NodesRoutes { public initRoutes(app: Application) { app + .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/world', this.$getWorldNodes) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/country/:country', this.$getNodesPerCountry) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/search/:search', this.$searchNode) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp-ranking', this.$getISPRanking) @@ -115,7 +116,6 @@ class NodesRoutes { private async $getISPRanking(req: Request, res: Response): Promise { try { const nodesPerAs = await nodesApi.$getNodesISPRanking(); - res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); @@ -125,6 +125,18 @@ class NodesRoutes { } } + private async $getWorldNodes(req: Request, res: Response) { + try { + const worldNodes = await nodesApi.$getWorldNodes(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); + res.json(worldNodes); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + private async $getNodesPerCountry(req: Request, res: Response) { try { const [country]: any[] = await DB.query( diff --git a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts index 4d65417e6..d64607fe1 100644 --- a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts +++ b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts @@ -4,7 +4,6 @@ import { map } from 'rxjs/operators'; import { moveDec } from 'src/app/bitcoin.utils'; import { AssetsService } from 'src/app/services/assets.service'; import { ElectrsApiService } from 'src/app/services/electrs-api.service'; -import { formatNumber } from '@angular/common'; import { environment } from 'src/environments/environment'; @Component({ diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.html b/frontend/src/app/lightning/nodes-map/nodes-map.component.html index b762b2d24..75f8aeb08 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.html +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.html @@ -2,10 +2,7 @@
- Lightning nodes world heat map - + Lightning nodes world map
(Tor nodes excluded)
diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index d2fcb31e0..6c809916e 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -1,14 +1,15 @@ -import { ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit } from '@angular/core'; -import { mempoolFeeColors } from 'src/app/app.constants'; +import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit } from '@angular/core'; import { SeoService } from 'src/app/services/seo.service'; import { ApiService } from 'src/app/services/api.service'; -import { combineLatest, Observable, tap } from 'rxjs'; +import { Observable, tap, zip } from 'rxjs'; import { AssetsService } from 'src/app/services/assets.service'; import { EChartsOption, registerMap } from 'echarts'; -import { download } from 'src/app/shared/graphs.utils'; +import { lerpColor } from 'src/app/shared/graphs.utils'; import { Router } from '@angular/router'; import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe'; import { StateService } from 'src/app/services/state.service'; +import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe'; +import { getFlagEmoji } from 'src/app/shared/common.utils'; @Component({ selector: 'app-nodes-map', @@ -16,7 +17,7 @@ import { StateService } from 'src/app/services/state.service'; styleUrls: ['./nodes-map.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class NodesMap implements OnInit, OnDestroy { +export class NodesMap implements OnInit { observable$: Observable; chartInstance = undefined; @@ -26,44 +27,52 @@ export class NodesMap implements OnInit, OnDestroy { }; constructor( + @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, private apiService: ApiService, private stateService: StateService, private assetsService: AssetsService, private router: Router, private zone: NgZone, + private amountShortenerPipe: AmountShortenerPipe ) { } - ngOnDestroy(): void {} - ngOnInit(): void { this.seoService.setTitle($localize`Lightning nodes world map`); - this.observable$ = combineLatest([ + this.observable$ = zip( this.assetsService.getWorldMapJson$, - this.apiService.getNodesPerCountry() - ]).pipe(tap((data) => { + this.apiService.getWorldNodes$() + ).pipe(tap((data) => { registerMap('world', data[0]); - const countries = []; - let max = 0; - for (const country of data[1]) { - countries.push({ - name: country.name.en, - value: country.count, - iso: country.iso.toLowerCase(), - }); - max = Math.max(max, country.count); + const nodes: any[] = []; + console.log(data[1].nodes[0]); + for (const node of data[1].nodes) { + // We add a bit of noise so nodes at the same location are not all + // on top of each other + const random = Math.random() * 2 * Math.PI; + const random2 = Math.random() * 0.01; + nodes.push([ + node[0] + random2 * Math.cos(random), + node[1] + random2 * Math.sin(random), + node[4], // Liquidity + node[3], // Alias + node[2], // Public key + node[5], // Channels + node[6].en, // Country + node[7], // ISO Code + ]); } - this.prepareChartOptions(countries, max); + this.prepareChartOptions(nodes, data[1].maxLiquidity); })); } - prepareChartOptions(countries, max) { + prepareChartOptions(nodes, maxLiquidity) { let title: object; - if (countries.length === 0) { + if (nodes.length === 0) { title = { textStyle: { color: 'grey', @@ -76,53 +85,80 @@ export class NodesMap implements OnInit, OnDestroy { } this.chartOptions = { - title: countries.length === 0 ? title : undefined, - tooltip: { - backgroundColor: 'rgba(17, 19, 31, 1)', - borderRadius: 4, - shadowColor: 'rgba(0, 0, 0, 0.5)', - textStyle: { - color: '#b1b1b1', + silent: false, + title: title ?? undefined, + tooltip: {}, + geo: { + animation: false, + silent: true, + center: [0, 5], + zoom: 1.3, + tooltip: { + show: false }, - borderColor: '#000', - formatter: function(country) { - if (country.data === undefined) { - return `${country.name}
0 nodes

`; - } else { - return `${country.data.name}
${country.data.value} nodes

`; - } + map: 'world', + roam: true, + itemStyle: { + borderColor: 'black', + color: '#272b3f' + }, + scaleLimit: { + min: 1.3, + max: 100000, + }, + emphasis: { + disabled: true, } }, - visualMap: { - left: 'right', - show: true, - min: 1, - max: max, - text: ['High', 'Low'], - calculable: true, - textStyle: { - color: 'white', - }, - inRange: { - color: mempoolFeeColors.map(color => `#${color}`), - }, - }, - series: { - type: 'map', - map: 'world', - emphasis: { - label: { - show: false, + series: [ + { + large: false, + type: 'scatter', + data: nodes, + coordinateSystem: 'geo', + geoIndex: 0, + progressive: 500, + symbolSize: function (params) { + return 10 * Math.pow(params[2] / maxLiquidity, 0.2) + 3; + }, + tooltip: { + trigger: 'item', + show: true, + backgroundColor: 'rgba(17, 19, 31, 1)', + borderRadius: 4, + shadowColor: 'rgba(0, 0, 0, 0.5)', + textStyle: { + color: '#b1b1b1', + align: 'left', + }, + borderColor: '#000', + formatter: (value) => { + const data = value.data; + const alias = data[3].length > 0 ? data[3] : data[4].slice(0, 20); + const liquidity = data[2] >= 100000000 ? + `${this.amountShortenerPipe.transform(data[2] / 100000000)} BTC` : + `${this.amountShortenerPipe.transform(data[2], 2)} sats`; + + return ` + ${alias}
+ ${liquidity}
+ ${data[5]} channels
+ ${getFlagEmoji(data[7])} ${data[6]} + `; + } }, itemStyle: { - areaColor: '#FDD835', - } + color: function (params) { + return `${lerpColor('#1E88E5', '#D81B60', Math.pow(params.data[2] / maxLiquidity, 0.2))}`; + }, + opacity: 1, + borderColor: 'black', + borderWidth: 0, + }, + blendMode: 'lighter', + zlevel: 2, }, - data: countries, - itemStyle: { - areaColor: '#5A6A6D' - }, - } + ] }; } @@ -134,30 +170,16 @@ export class NodesMap implements OnInit, OnDestroy { this.chartInstance = ec; this.chartInstance.on('click', (e) => { - if (e.data && e.data.value > 0) { + if (e.data) { this.zone.run(() => { - const url = new RelativeUrlPipe(this.stateService).transform(`/lightning/nodes/country/${e.data.iso}`); + const url = new RelativeUrlPipe(this.stateService).transform(`/lightning/node/${e.data[4]}`); this.router.navigate([url]); }); } }); - } - onSaveChart() { - // @ts-ignore - const prevBottom = this.chartOptions.grid.bottom; - const now = new Date(); - // @ts-ignore - this.chartOptions.grid.bottom = 30; - this.chartOptions.backgroundColor = '#11131f'; - this.chartInstance.setOption(this.chartOptions); - download(this.chartInstance.getDataURL({ - pixelRatio: 2, - excludeComponents: ['dataZoom'], - }), `lightning-nodes-heatmap-clearnet-${Math.round(now.getTime() / 1000)}.svg`); - // @ts-ignore - this.chartOptions.grid.bottom = prevBottom; - this.chartOptions.backgroundColor = 'none'; - this.chartInstance.setOption(this.chartOptions); + this.chartInstance.on('georoam', (e) => { + this.chartInstance.resize(); + }); } } diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts index 09b00e032..9f1b3fe88 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts @@ -45,7 +45,7 @@ export class NodesPerCountryChartComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`Lightning nodes per country`); - this.nodesPerCountryObservable$ = this.apiService.getNodesPerCountry() + this.nodesPerCountryObservable$ = this.apiService.getNodesPerCountry$() .pipe( map(data => { for (let i = 0; i < data.length; ++i) { diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 5f036c575..8c0f5ecd0 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -267,10 +267,14 @@ export class ApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/isp/' + isp); } - getNodesPerCountry(): Observable { + getNodesPerCountry$(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/countries'); } + getWorldNodes$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/world'); + } + getChannelsGeo$(publicKey?: string, style?: 'graph' | 'nodepage' | 'widget' | 'channelpage'): Observable { return this.httpClient.get( this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/channels-geo' + From d9483dbd7a454a9465aaf1b87cd48d84fd368d61 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 7 Sep 2022 10:10:25 +0200 Subject: [PATCH 23/31] Update ISP pie chart colors --- .../nodes-per-isp-chart/nodes-per-isp-chart.component.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index c60620f61..4f7656526 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -132,9 +132,6 @@ export class NodesPerISPChartComponent implements OnInit { return; } data.push({ - itemStyle: { - color: isp[0] === null ? '#7D4698' : undefined, - }, value: this.sortBy === 'capacity' ? isp[7] : isp[6], name: isp[1].replace('&', '') + (isMobile() || this.widget ? `` : ` (${this.sortBy === 'capacity' ? isp[7] : isp[6]}%)`), label: { @@ -204,7 +201,7 @@ export class NodesPerISPChartComponent implements OnInit { } this.chartOptions = { - color: chartColors.slice(3), + color: chartColors.filter((color) => color != '#5E35B1'), // Remove color that looks like Tor tooltip: { trigger: 'item', textStyle: { From 51bf4f769f10348da45d4e95de7f88c21ad56192 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 7 Sep 2022 10:05:22 +0200 Subject: [PATCH 24/31] Fix missing seo in lightning pages --- .../src/app/lightning/channel/channel.component.ts | 6 ++++-- .../nodes-per-isp-chart.component.ts | 4 +++- .../oldest-nodes/oldest-nodes.component.ts | 10 +++++++++- .../top-nodes-per-capacity.component.ts | 10 +++++++++- .../top-nodes-per-channels.component.ts | 10 +++++++++- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/lightning/channel/channel.component.ts b/frontend/src/app/lightning/channel/channel.component.ts index b1e39dda7..c6d06683a 100644 --- a/frontend/src/app/lightning/channel/channel.component.ts +++ b/frontend/src/app/lightning/channel/channel.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { Observable, of, zip } from 'rxjs'; -import { catchError, map, shareReplay, switchMap } from 'rxjs/operators'; +import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators'; import { IChannel } from 'src/app/interfaces/node-api.interface'; import { ElectrsApiService } from 'src/app/services/electrs-api.service'; import { SeoService } from 'src/app/services/seo.service'; @@ -31,9 +31,11 @@ export class ChannelComponent implements OnInit { .pipe( switchMap((params: ParamMap) => { this.error = null; - this.seoService.setTitle(`Channel: ${params.get('short_id')}`); return this.lightningApiService.getChannel$(params.get('short_id')) .pipe( + tap((value) => { + this.seoService.setTitle(`Channel: ${value.short_id}`); + }), catchError((err) => { this.error = err; return of(null); diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index c60620f61..5fe0d3bf3 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -47,7 +47,9 @@ export class NodesPerISPChartComponent implements OnInit { } ngOnInit(): void { - this.seoService.setTitle($localize`Lightning nodes per ISP`); + if (!this.widget) { + this.seoService.setTitle($localize`Lightning nodes per ISP`); + } this.nodesPerAsObservable$ = combineLatest([ this.sortBySubject.pipe(startWith(true)), diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts index e97acd80b..d1cec0780 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; +import { SeoService } from 'src/app/services/seo.service'; import { IOldestNodes } from '../../../interfaces/node-api.interface'; import { LightningApiService } from '../../lightning-api.service'; @@ -16,9 +17,16 @@ export class OldestNodes implements OnInit { oldestNodes$: Observable; skeletonRows: number[] = []; - constructor(private apiService: LightningApiService) {} + constructor( + private apiService: LightningApiService, + private seoService: SeoService + ) {} ngOnInit(): void { + if (!this.widget) { + this.seoService.setTitle($localize`Oldest lightning nodes`); + } + for (let i = 1; i <= (this.widget ? 10 : 100); ++i) { this.skeletonRows.push(i); } diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts index 595688690..ad396ee31 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; import { INodesRanking, ITopNodesPerCapacity } from 'src/app/interfaces/node-api.interface'; +import { SeoService } from 'src/app/services/seo.service'; import { isMobile } from 'src/app/shared/common.utils'; import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; import { LightningApiService } from '../../lightning-api.service'; @@ -18,9 +19,16 @@ export class TopNodesPerCapacity implements OnInit { topNodesPerCapacity$: Observable; skeletonRows: number[] = []; - constructor(private apiService: LightningApiService) {} + constructor( + private apiService: LightningApiService, + private seoService: SeoService + ) {} ngOnInit(): void { + if (!this.widget) { + this.seoService.setTitle($localize`Liquidity Ranking`); + } + for (let i = 1; i <= (this.widget ? (isMobile() ? 8 : 7) : 100); ++i) { this.skeletonRows.push(i); } diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts index ee4b159c0..f8a2ae52b 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; import { INodesRanking, ITopNodesPerChannels } from 'src/app/interfaces/node-api.interface'; +import { SeoService } from 'src/app/services/seo.service'; import { isMobile } from 'src/app/shared/common.utils'; import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; import { LightningApiService } from '../../lightning-api.service'; @@ -18,9 +19,16 @@ export class TopNodesPerChannels implements OnInit { topNodesPerChannels$: Observable; skeletonRows: number[] = []; - constructor(private apiService: LightningApiService) {} + constructor( + private apiService: LightningApiService, + private seoService: SeoService + ) {} ngOnInit(): void { + if (!this.widget) { + this.seoService.setTitle($localize`Connectivity Ranking`); + } + for (let i = 1; i <= (this.widget ? (isMobile() ? 8 : 7) : 100); ++i) { this.skeletonRows.push(i); } From 1a2c0b7843044e53d4f2acab4666d2f132641e19 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 7 Sep 2022 15:38:48 +0200 Subject: [PATCH 25/31] Lightning dashboard -> Lightning network --- .../lightning-dashboard/lightning-dashboard.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index d601606fd..063e2c6a5 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -21,7 +21,7 @@ export class LightningDashboardComponent implements OnInit { ) { } ngOnInit(): void { - this.seoService.setTitle($localize`Lightning Dashboard`); + this.seoService.setTitle($localize`Lightning Network`); this.nodesRanking$ = this.lightningApiService.getNodesRanking$().pipe(share()); this.statistics$ = this.lightningApiService.getLatestStatistics$().pipe(share()); From 3a1da0eb4a52095cb37a08342adde6df2114bf8d Mon Sep 17 00:00:00 2001 From: nymkappa Date: Wed, 7 Sep 2022 21:53:42 +0200 Subject: [PATCH 26/31] Change isp pie chart threshold from 0.5% to 0.4% --- .../nodes-per-isp-chart/nodes-per-isp-chart.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index 5fe0d3bf3..fc73ddb7a 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -107,7 +107,7 @@ export class NodesPerISPChartComponent implements OnInit { } generateChartSerieData(ispRanking): PieSeriesOption[] { - let shareThreshold = 0.5; + let shareThreshold = 0.4; if (this.widget && isMobile() || isMobile()) { shareThreshold = 1; } else if (this.widget) { From 5a0ffee58b619ee0e8be82089aad866f68e875a5 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 8 Sep 2022 14:33:46 +0200 Subject: [PATCH 27/31] Fix missing space between value and label --- frontend/src/app/shared/components/sats/sats.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/shared/components/sats/sats.component.html b/frontend/src/app/shared/components/sats/sats.component.html index dada43692..1b4ab9143 100644 --- a/frontend/src/app/shared/components/sats/sats.component.html +++ b/frontend/src/app/shared/components/sats/sats.component.html @@ -1,5 +1,5 @@ {{ valueOverride }} -‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number : digitsInfo }} +‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number : digitsInfo }} L- tL- From 2d2b7d3a9fba1922b776aefa86e4b673319e3b56 Mon Sep 17 00:00:00 2001 From: wiz Date: Fri, 9 Sep 2022 19:11:55 +0900 Subject: [PATCH 28/31] Add more Community Integrations on About page --- .../app/components/about/about.component.html | 36 +++++++++--------- .../app/components/about/about.component.scss | 4 +- .../src/resources/profile/btcpayserver.svg | 1 + frontend/src/resources/profile/electrum.jpg | Bin 19839 -> 0 bytes frontend/src/resources/profile/electrum.png | Bin 0 -> 174386 bytes frontend/src/resources/profile/lnbits.svg | 1 + 6 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 frontend/src/resources/profile/btcpayserver.svg delete mode 100644 frontend/src/resources/profile/electrum.jpg create mode 100644 frontend/src/resources/profile/electrum.png create mode 100644 frontend/src/resources/profile/lnbits.svg diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 36d765a17..5cfb2c905 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -187,8 +187,8 @@
-
-

Self-Hosted Integrations

+
+

Community Integrations

-
- -
-

Wallet Integrations

-
+ + + BTCPay + Bisq + + + BlueWallet + + + + Muun + - + Electrum @@ -244,18 +250,14 @@ Phoenix + + + LNBits + Mercury - - - Muun - - - - BlueWallet - Blixt diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss index 2cd755bde..2cc5d5102 100644 --- a/frontend/src/app/components/about/about.component.scss +++ b/frontend/src/app/components/about/about.component.scss @@ -43,7 +43,6 @@ .alliances, .enterprise-sponsor, .community-integrations-sponsor, - .selfhosted-integrations-sponsor, .maintainers { margin-top: 68px; margin-bottom: 68px; @@ -117,7 +116,6 @@ .community-sponsor, .project-translators, .community-integrations-sponsor, - .selfhosted-integrations-sponsor, .maintainers { .wrapper { display: inline-block; @@ -193,6 +191,6 @@ } .community-integrations-sponsor { - max-width: 830px; + max-width: 970px; margin: auto; } diff --git a/frontend/src/resources/profile/btcpayserver.svg b/frontend/src/resources/profile/btcpayserver.svg new file mode 100644 index 000000000..1c18a4cfe --- /dev/null +++ b/frontend/src/resources/profile/btcpayserver.svg @@ -0,0 +1 @@ +BTCPayServer diff --git a/frontend/src/resources/profile/electrum.jpg b/frontend/src/resources/profile/electrum.jpg deleted file mode 100644 index c1c063d758d32c458f0f50e0844656d909407a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19839 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 diff --git a/frontend/src/resources/profile/electrum.png b/frontend/src/resources/profile/electrum.png new file mode 100644 index 0000000000000000000000000000000000000000..ca24d5f45f6b2ff8e8374999327f1b7aa960b2c1 GIT binary patch literal 174386 zcmX_GWn2?p)E->|DoB?gAtET<0|W+2iqbH;Q#!^d1Bpp@NjFGJgEYvbySsbjws-vB z_xFCf+dka8d(Ly7^PF=}n1&jNl$eef0059GE6KkH0I)G1u>pkmnC0Ai{00CZmbHEJ zMnn0{8y1aE&Q`V#mH>bfX?lX!2dx>ZukaR;mVb{~S=i6%e2G{_C6u1vYKAEXYG8i} z1+KT-)M9I~M3GtGNOtNl38S6w&EJjh+rod4-LI2z#QVGNH4@6lAa7(#%%< z)Qt5JWU3LMaTko+>8^_vD44BFdAQeM_?Tj_>k^yhAr_e~R{M&T6)xyX9((+*MJhSD zP^qUHamX8J(KN@?rorJhiH1hAioAJ-4pCQXLpCBqZ^>c5l&^&ffOw{#%2?eR{}E8*h%d9TQmTH$0yxD3 z)GVf*F@13`j_F!`z)2Fmzwh9Nm1)oWVq4@kGuU~c?TgrD^5EgEwnRQPFo;e0iGZcT zMt(v5JNcoZ2V{wG3*_ZP^jf=^?ce@CJZYKj)F+>}KWc8r5ru}?G<}u~(*40}K^zL5 zSb@?Aa}v-7q}x;4cz^+r&te)4$^?DRQh+yXIM1AbzSxjGgNLt)`&yfA%AzaWVEn$Q zts7<%!|zTjDzdCvn}`Q6i%;x7d1!N%LLEA=AO9e5&mtBK;(dk9V?xRhDByy#$N~rr zRC$@2p;5?97QsIjL3|_@B=k`d_LwQ@G@veUrc#1|5Y#!iEbsj!jexp+ZslzQl{8s> zC-n;bF@WbYxHFberjHP3L{XQJ{zP8xhwd}1M^EjeS#z4=kuSTn zO<*Plm5Eh2)290p3}n6Sw5tp*0Sz>U{7ypUxb@^y5lw-6e@It2PQ07B7b%Z|$vfPR z&HSIX5?mhrEUGfno&`&l1=A%VJBS z?c?_n{n*vxrV_mp!MvHA;HS{HsQkMIY|Rdt4y~Y^O6_dLm6}ee&&moZKjY25nWdP8 znT4zhb!)OW#{|XHKUOc2*{K|5&wENIHcll^utjsojj-?F4U0B)kCbl>tu&3!>NWzV22whWR$! z*j@bDmD-J^OQCC}qow;umnhgHM4RH2(x0-CvL)DCW1~+}i>tq(zg27eDn9ve=z|q| zb+}k|S@}W1%Mt0^g%YVU^TKLf>m0S}VG5%dUIP)?dc%4xFFEyhe7bz#3eBTzY0b>C z%-l9mn*ns7{U6J$Nw{okM`8B&>^9R3)0`D^w&LiL5$+uQ9El!L^E%h>C(>&M9F<|| z$}!53{i<5isYf}dGlwsE5+W(9Mb#6Zq2-UIDcWYwyfyol4siiL?Xqg za)GKztqG1GL$Ka*+`cA|`s)3)h3s+6f*H>Z+oR-_#|SDfYXn&YD~FcDP3>) z`7r5l4#@-RW9spjG@nLXR~i^TMe|zlMu5Ftvd2Hp#jok_-3+NtMQ(X^Id&~fOU)n~ zZeNhUBaa4!_r?{(ZqdO>jK3vT=tS#O)W=+!9oZcfNQ+B{Nk>Xkw0gFdK=`gqk=RJj zn}xHfb7!OslGsj%hl-gthN zBocaH^yJ2khL=aMlw;qg;iGqew-@e9GX?oKT}?``zR^BVcqG+3Dlf%;P`APaNL9g4 zAyMI7qArhK^}1q&egsRt6csc56TfGkpNcL=7v6nc+g~MKJLDBGOsL+U{ZzYAGzc&M z0q)RqqPQP%InM=(MvDI0hSprwjLET^ey@CCBp}7v!uLlo z=hd6W0o#egj}$*;Mxs@?-1MxPlA=&57fKP&QrScFNUH6AEn0&tGY2$>9&Mhlr>r{- zGWMVK-v$U-33+)~w|b(!+;s>Y+LM)io-8+<`>p0>)X}(d(Ygn7WN}oQIUZCTjI^X* za{9hFu9>A-*=k}_eYDvl_ghYLrRC+Y0hvR$!xM*t1*_^ZySYSGg@9| zdrNu{o$G}mMCd_F9wP77YuS^~Ykpy;@k}?_OwV}cU-`08*~ZZ6&(r!Qo&8pAQv+8M za$A@MtcJhvSIgT9JJ3YIZtiXn9V}(DW!JC%AbqxXZmMm`5jutyiPKFW%|yx24DGwr zurHH!>^7ZWDg6WA`?=fLW(@^H~_>@SYNSttIWK2)hnrp8! za{1zoA4SQIhCt5mD_qg1VkfJ;SIkEl4C&* z{FZhC$I=;x8D9cZ(f;QVh~$*&0qb}0=;0@6sbZO40jF|3pHITWsK1KqxJx(ItdFLb zSC%gh1|?P)pZxw*G}m%__c5`=x?Idi0+J4CIrxH%oMdXsNC58#*zVLGIkxP$R3j7C z%A#8FAX_&^w|t9;M#x4KI-MRT9RKE!x781AJTJ8aSwxNy6pf9N{z%t~-)s$6H%lUrRlnK20n>Fl9!y7(y{B?!;A3S&w{DoqT;x4ER1dq6mSn#bn z?>-)kNE}=H-1Zz*cNsrYk{RH2+hT3j-*s$1vX=~eyI9yNb~A}^+_KnSAM>(3$}jl7 zSkeka1R=9;&Ch$IclOn<)Z;TFfnNSZccfv3tVd*CLj$hO5;qIb!S+US~ zajl)sL`H#lx|*HVUeF|t-yDvXmR9)GQ%5H!W1xV58zW+FPg7e~7b;c99``a(7>GL@ z(h6vlt)si*9++e>d=v2GI>3dLmAYzyAmg^(t%Aj&iM(#XkNNR{c43oh8G%0ez7e^K zDQKZ-vNoZXNpzX19ar3MwJVHZXn0uK+unX}a%X3Uz&x}0$=_Dmn!O+}=-b_>v|Bg( z^e`SaHO^%xqA74GNO&FZ8n<;53*KuVh%%llt?x1f8tI6qWKuCdlCW>ZYmW@)h z0CO|XKR5^2CL+zVyUiR@fo#{YN$t|46ek5((sXW9Iathi)(o_(KZfV$=g05v{IOL+ zJCUL4&9-t2!a)@LuDl=hfJQ$f2bR>o4m2b!=i_zW-1zDn7*wl@vT$BHq~ZFlC`{i$ zP2o*|B|OLxzQ4#YL)wm|z1x2#?Ycjj)7aj!+?L!#E=9`cbp6jtsQMpGK~5hvMA|v? zH2k&X`>C$-ej^{X8}YYCoO8JkgwIuuD+^N2Z+C1l$M%X^O2%e&1E>j@tTV#D1`v5d zaUIvpmW}?M@E$kc4kZgfVgAsdfVZ>1CUMFL-ZuekBxLu3=*UfewLOWwT6@(YNk#!T z$6_WDW$xCP)H5_p>r5bH6py2#qI$5q&3LXz@rfBJby{iulLBQt){ECZFz|NX#L9Y3 z4=3(~qvsNU5?hvddaK4meE3b;;q3li?o$7F0gbITRg*Yr8rBtUs2g~D^v0TtMY%FM zphJj}p<_T@8x}ymNLd5Oc zKX+^lCYE$#4bwO@r2(#Cf4+FhZKi+QkT`0&Tx?U&^Gje6Z~P0cX0o9MOgU`NeHp`P zkUbLR2!p?jMQ-7s9VnR*iz|=rMn~U^`(IVcGA*3dKo)SfJkJ00#>2!p%lLL-*RFf? zI*W6EK4VeiCGr}*9UL6Q_t45&TZ<)vhWR>b0`B-+a#qAZ&LfjJQ*i{dc zv!L2S@VX4DXs!m|Z_em9U$h5Z$TR^&E3+NxImj8rKCN@8tb&7sGGb9$0QE5Z<`A}< z=W6HT15-kNCxpTETXf9>c-c+jFWP_gEyeM+1gl;stGSF`ysI$LLV6rT8+VAKloHce zocY%+anjiVWU)<`6aXM8V;jkpHV@OH@5yR+6Z>4}Bo4s{27=2hiWhXw6!<5!^9`4} z3>(nrE^0S0tWw^ZGeYL@VE6s$*Qw`1D%Lov^?@cF7ZekyR z04&D%J-|VXHJD}v@Glcx<9ibnoEJnfN!d{VkA|HwRJI*7F)?BHI{nt5Z+e=i8Ri6M^4CDzGV10Mj+=_7_6s94l_8@_18mX3e~>{Mvw%vaiZ z)*iE$rc`?>u}@K;HR0+E*0yd3>vGxh^~W^YE63$T#fiDoTBhlD_W<&HPFMD_mZ@F7 zEj0D2^}!E}51zUd63%E!tM9q3Zeas(+&pGmGR^vQB$3-yrA4iMKqrW1s~R-77a7P(Zovg`axduu0W%?uNT(Zjpv=!87Z-0dTUw{C97gqnH0q2RspZ)QaaaGg#}r z7u2$Ue~rYPu~{pocA39?D<{4vot)(tl{>Fn$eiX=S99Ti)o&;r#IYgkWt-n-m6>OI(vIZ|R{i{yI)ji}clL*}dFaI6fXW!fO{ANJrS zEQhvlH0g_*m;V7cgBWBVAsN`=LkGH1$M&bHE6BdH$(}K~?S(O`&tIh&k*%wE-~>c3 zy*805UVA&PW4#?;xAk_ao_Q9IVI1uFs}EQXlgU9O+b^2I%cPy;Q%Mg0H% z4;DMUpx!HBvs*g=NH#ST9fHI6?)(Btz(tUHhCfJe@3cS!>V)4t1gepyBU!vn&a3%AvbjxMw%4Obh{ z;qnFK_M8!17M^cM9^2gI(vJdfa)O>q3V> z*GjO1@VjWH4fEC_e?};y)Fktn=Hgf_O}lC^BARWq9o|d06zqw{rowIpXF*E;NtAV0 zG~5IDiI{-NmhE*^~r^qaQe!$Q9W36Mteh zAJ^n$-qDnvte?B9Lg|8QI8S^1)BSTn*SbqiXT3M(kdcf&<};7e+%sG3*03KWSo0zp zMyxVM%wJo8HoF3)XA*%E4KV6o%=_VgiFPq6!uq%QstkSt9*|Qy z5tR{;B@tXj1yx(aXZ5gDZp8n3kPp+`@fhpf*`>W~xC|8f?KsSIjv=?{5Lq!Lj+LI4 zucoVLH5CM0v1vaU(i)}?y7eXHn;Qyw%J}&;Us@&D?mn6F*%wztwM8B7akOIW)tZ#) zj*OiYDR0q~-sE&V;<~x&mRl6;tH$=HFje-iZ{MqWctxJIkl#sF$COla@1R_%*y>!^-D#_<&pY|PkC67i%Dzm>|2^yXwvBy)l#Cg`L|#*PY*P>0vbk1D%;`{P411FtS!6Ydqu6gE&VK z7xmYZIBUKbXh!UHXt$mM@FfH6E@=b#dJtmI0G=*XCd3=hTSJ&@b_)}mT>+MQv4XY` z4-c5ZQp4#;tMXp>jd{TQO;3Dgjg%(XD8TTRp#SWVoXVA?K%fyTmy87~Yvm~LqW zS;h|2b20H1MyhJT;dqpLA!IwK=8dss!;SYC3m>V;=+QDyV0j4SA@T&ctkS`UUAM$C zt(;|$Jb=j%S)UZb1uxo3Mdn>7amKt%vetx9v2j}#@Uc68TlG5tIp=%Q@9R(0rul?6 zjtc%Hs_JJ>Nf9sBU^*Zk!YcY;BM8Qzm@oI9tZ6N5<6Wu=IN6iN%6QTa!O|wRDX2m| zLsVtnb#Y3&W|to(Ec#|;V!S8=tf;YkBNa%L3c71fMYFe8u$^VU`TAJ>?CgzX2*-K$ zTiNkIP@lXMcXjYF+bj!8iF~#8h7F2gDsWuIpvVNcmwBfOm5%^HR5#3#ua`bR88I|{ zlQPSEArue0z(ioc=(VLBFYfWIDFtW-B4ZpTfDOcHMn2SZw@YQ-#jx_LajBOhegK?B zR*vhfmBO_=u6oe5=*gOjjE8yV7R;OB)mk9+b%lFC{lT0ga_z?czjf;U?e(Xp4A(j& zST*UehbRdivH+&rkC#$iYvOtFz<>AgMIhcBYs-Lt=)-UVq@G0yx%ingFa3pwWjgQO z=c`E^on9ROPk|&@5I;aHaOu^Rv#a8AZTnPt)JMm%+wObq*e1WrJss8`Kn1#kbR*YJ&|Vm8)9H=Nt3Ye6)nXQ_8o z&r}b>E~ffHe!KsrtQ)o#Tj@#ai=)se5yIIf-1&E(r2$fWy|h7S?CF4*2HPKGqEL)5 zTE=pkl~g%}eH~IayLBJZD zk&wuD)u;m*yj;itTV-=i0`2S*ox zOM#3>%XwI>T7g8C&bTNu|F6pk<3nMum-lR77_RB%*uw(BwDXq;u8g>h0sAb@e8*w% z6N#!;E&>M98;ghksj2*@z_kH#O{quk(;*(`;%q(f6=Oi&mf==$UnAt&z|;>08k%bHL0AEq4E7{)rv>L|`2|#{MAGJ?9xG!Y=;%ZI%K;E{5?-MCnUkKezK?9qx-88?hPO zWFYQIe{5xjaqZO!lnFmIvlF3L8U#CE@Lr2V@H2zW7Xm4Ej1#;W{;@(CTZ&pS?118G zn?v;cmkX88K#;0d8})VAITyGW400~6cn&QE4)h`vP)e}>GI;)t;>MH&?tD%N^mAP^ zAxRK0TzvMf5~InMh+CgY7GjOeQdA%&jaPUxVZJgMm)<$Z*^?|1kn)(mSfBc z-oYH$Qwue`wt>eF!|c%wr>e7q-6Tug+ehSqchEl@!AIS4%WSIdwavFv_c4d=ak3bE zT{*P;MLM8xYhya$$5EQ3&uHtvsdI63UwrUurl;LlzPUQ+mDJPQ*ZHari{vL; zNG$)_SBH-Re|tg!V9q;!o(BPsuMs67s@SdMK64mR{6L=~_Vox9Kk6Jzk_^vf$Xwg< z2!LceCzUIcn_$x4kmqPm83xZbOSu?dyvE4RwE^|njKT`u|E>bM<@m3V;kY}b?RV=sfF}6YOug1eZy%`FGz`ZB5PV6`ZDhM@ z#IhbepZ=1MP`GaN*Fr|OIiimRuvL*1Yfq4jn0gH3{M20-##OF6Ba4N6qvpuyVM*{y zf4J1;w0X$#R&@w4>K!&8!D0Y7EAtP+M-KynEv$X(0H0*Iy^J4C9a+Xn=Bfaafkja1 zJeDG|5=>_aIQgUrPQS~&?w#10lWQJEVH7pe0|Uj1oT(Z59S?*&Mo(9iRH$KaGQ6l% zQO$V@Gk_XlFu1oJvTxbaECM(p{x^nQA9R^Imq@4pJ3#wmjzzyN)ejs)a=PTA-|n}= zyN7|@^;Zk-W2&Bs&~8E5+#P^GNAYE7aUHS+7Bs1<>&I$CA-I~=o2gDT54l`G2+MZW zg&YPAe~#SzeAs4P7O zpu32Q9HB>{kia+2s`hKZyO=?)lLB&|SQg=Q(&Z)`<8K1E&I}5z8#8nN6&w=MwUa9; zsK4G(=!vBH3|TAbF?db@6LKoA5$o4;sYxA?mB}60P0A_gI;mo8Dvs%*{(!l4x$w;S z+*%98g;9++DWf^45K44ytx%IldyB-fNFb0|gmm%A+`{H1?4H_CVH3jdT+7h5T>gx8 zd#|V6F@j|G>SXN+cnquXF+-p{!~@6T1I0C)6h`TAF<1Qy3CT zZ&iZbL+@Ux+*w>TQ{QvBY0-mQ-=@tcd7 zoeknQtFsYlQfwY-wFYV}hU1=EuhARNIqTRu z%sPuWDktDztWe&gANOs2pLB_D*y=x{Te*ai%%Hq-l4*UDrFz?UAC=5_;rXkIZN1Az z$hP2gFwM;wSwQ#cH;lCwf?`q?CH~%(*WAm(pwb zZ3D|Xg*p%u3stR+cMTU4{k{Ju7i-p<3pVOLx*v+x?^AEGT4l~w6E`x7&9US#%So}Ux zuW;E`)Pd`I5#uxT*Ll_tOUI};exkU*AlS|B>3yjxjvPUW2UKK^F%DPASkOKE+Vwl} zr^^$F`|Jmy3Ff`ai)ifvOyvZ54aO|*fEzAE?ZbpW2(E#+-c(fB%;Efw0nR>{tg~O{ z?NE(Rfz&Yr`H|LXiC!#qL_)Q`k~1&%)YIe5b`T|}kUh+qtr7EhRtBF3Iu1#*I1Vqy z)aAqS%ZhTsHi)k9m&eabNsA1LZM^ePFPlppeD|>H!fmbg3{wsb0g|x0--6XXUMUM# z-YE->1iuV&QnXHtiV!88FN`vG8F4FWq;wivWe>E)4rV%AoKgiuvZ0G+E^fQJvngUu zNH_Z%@7XkIGvpJ#xplHmYsYuMJgW8IN(Y82THBe`+o@XnC4tqcUU|tEu-xB&VR^Lw zk`ZRNweT_Nl~Cm4CDw2B*b)tt?LHfVbPz+X9SR1KmowAY zX*2v8v!eQ{cP#F`%uH@8zwo_V{53x&5Pzu2$giSF`eSBdwPqg3tai3npO@_3zsqo8 z{rG^nwp{rbQ!Fd*rOMNP-LbJdvJ#JIz1BD%G#Kj{LjDsu`r41;aOa&jv~LMR@?X{) zI-^k_x?6Jynw7*b!>(X_aK5xM$pFOh#79wXzpWgJ_LJm!*0j>yJU+&CK>)z8?$Ubm4Jmi+yP791n`Ii2dvFHAe z@vZvFDnf5)3i!6o7He)^5hd=N-o`3rlj$woGp5r{k9&Q}@@)7un)&dF_vZE4*3yxW z{@w0>eFr4Xxo+SdQXPaEUwsVTFIxG|d39Zi*IvnUQ%Uq70tGm<`?y3YffsgokS&>FtFiXxQbjSIpS&pKn0 z3iX;Q2d3C2Nknzd6gtS^P7HuUa@qrsoL}pA!{=_9;g3Z-=KltZ72{pB1L{5&rNSl0 z=)DT^2B9$t+9=Azx?wt?PY(1M@Hv+RVe;;7_+3d!38KN|x9qXxhvU}3C4S2Tc*gO) zs-rNilr^C%xC3Lk8GmQ&FF!krYja|u>mF*r5neFu-ONl;esPwW!OMKZ+76oW@T1oo z1iW8i*q2M*6=o7jkQ9EkiS>=GeAb3X>ip*zX>N~s_X5@ozI{oXMEko)mObTkAs$?B zTts~!^y^;DR!O5RPOJlmMU10FUTVzifZbhKG3Q@!K}8hPfqlwRLFUws&IsoWpAPjj zCv`DzH${4MRUQ{tfOONz!_7J5Rxpo9k>7Yh#w{Ku(Zuz{Xyfd_h?Xzs8* z+ndC{S!=4I@g}%gUy`#tg1r0{`R^**z*75RfP5+?rUmUl-NxH@ixW6jjV4RPMUnn9 zUJG$_L=1S}>w8)tc^O{8UB#d0Q0X}|<*V7}Q2!3+!XaZ;rxm-KjXciW@OSE}=`&j0 zg23;;KK3OB_0#aE^N?#V@Na;Ah`1iVt7jSfOragtm^YP3{Y8$ScbN_GbE( zbY|Y?_%?a_Ildfi-i44pD}9xc@XT0Vz5wIj>s`|CSH*@FAEW0v+1usVw@tT9-nYHy zJkf9m=c8;o6&BrU%2-P=%gH1+y=>3a^6Y;g_PeR(Z#VW3%3%DfQz<<13b zQNA?_S|X6HozVKqNb*#0p2x%JqkuYew}va5G=&EKK}`{+6O8vrRpZ=&-0eE<`J7o1 z9m8Oj-f55X{<68=l00L^=BYyZteJy>s6Zt`War<2gexV&>1u2cQD(BDXVdd=@D9I`fnl@WHiu&gJdrj7YC+;Q zVW#!u1_ipvkKJ5HcFHphH>x^+O5;jtCPVhU1Cnu@4(j{E}EbEw=9#75D%mu%=?=`!08@9Bt8XiG~JzFzzEL}}a zJET{~)_Ku|RZd^aaxiI<&u70q>8g#zNb(Lox;Y;yEPc|uEq|9}x8d_({7QGHTl7PZ zO)M?X5~Jvgncxp0&rX?+R$k!zDtQ@O%f&?!W(ZVD!Hswq`6##OI;L}cbEd}o8a|di z=-g{G?`3>~Ra+3*$0dzvo(5Lrr2hMn$;GUPDc_kk_O5dL+a#Ic)$i28Vcl!qS7i8| zxmOke-Kr1s*cXLeWBYr-H9Tohf(vF1NamS0Oq4&rZg>B)_$PElV&-B4Cu8eXYJ=o) z!N-Y_wzV7JfXVpU3UWyqF#X|!{8uA` z3B~3X;_QX+m{ssi=(+9F=$s*@wOjQ=rZ^E-IfsuzWaJa)?tDrt`hr-HcHZQ3K`p=)5;Db!r%t(RetP zPt#`lfGp1EVXoss(d)Gu#?RUr1f#}_0ha5Pvb7Il@~0HX46E_w6Rf4p+VwutDwoaU zPYtbf3AinR3(7v2wQ@R-7|6zM$;VjciyW%F8*GEwSBtWz?sN5udY-|CZRKa zMg-i&ss>R3+G&H;zYL8zj3`NGbrg5eY<*+F!E3CD`%Zv>g8XwwbJkVcjA&2Nzi&S*^^Etsqt@NVZdP+%vv_hoCAAvm_KSmQoD+XCtA8r&RxCO2yyE7; ztVI&RHd7;C?>{p)Y?^gV(pjeWi`!RK!qkV7T(=BE_i4oxRc1QR+3tF}{It_3N47RETwu;#zL(!9I0c z2Fl*|7pU>%2tn(qT7u0Z#`?Q0Y99S-cXz6HYCm*R5xG?Bmn7+!r?bh|MsJN{9DZq# zR^(t4+?N=tvWVjF#Xot^*T&w?M{+s}IQ25|;~vIuw*H}FAgy-~mi`;JEMXOdy3E$* zAVlrh7ab|Gm~?En#DsG7dUdEVD%;&j1X4z>^Sum^ZFITY8ynv@-On12A;2-q&(z3s z%3?{sX_wDkefd_Yd_XbbNC!HiS}mAV+%m~D+tK(vK0i2~t%zr9Xvolbeq*TKsv-Yf zF7^9BWmKKyp)wdnVVH?@Y)-kN`2BZvaDKvh%==?7-VQY|USTo7=j1Veq`i768>S@G zUFtc%*m>;qY9kV2>&sg`=yh796|$y_$BvGv z?KP&n+=FVrb`Ulu?hojv_(hXyv;J2vF6uX0WyPU=BIYR zJ~1_v|A!x!c!66yyAX~=jw{g5dY8+4w(*45$6FaLL8 zKmWTjhl`9!+yUueG!mw7CAAxYQKM&7tx6SxQxlxXy)lu5F!b3&9G;l6mTxKDx%8!W zod;V`nrf+lyoegs9NhuWnWQtZBq_`8OT~UhnV&*~kD_8InLE>SO)pM*%v$7VZmyZuw^mQv={?`8zYu7=ukN>7XCHA! z-9aA)17%Dlk1~(-!9O8d^RlaCI-sKxccm2;NUH7A^rk}v(G56#slS|BPVry&j7fqD zMFCJXza69Lza6&BZd(Em#MsEh!p2K~nuhUBhiF$B5*E~113uH;3~P39a#O^}XL44( zW_$eN!^sRw-+=7EBT8LE)LD)DN&WG9yOET|jAEaA?P?o6j(;0Mmu&%#Z`|#-^D62p zk%?sKl*D(2+-#@kS>lIr;-bwF{!H$ubWi<4eAgMQ{a(CM#k~$(pHx zT+JI8s00jE=pg4F>+HXm>?(@|`Hv)Uj?T9<(IQ#x#)p6K+Ktsyz2kO=JeJahshMI_ z%M>4p*c7S!6Ct(~)+}}as0(S$7QV1**IbWe46dVzF0F83u#<$Grn=~nRmjeS|586!WWd3WXy%~?WQ3~Q_+fUD`?-e++>Ts+Qx=`~eiuF8hYkHVPMLCWD4p?Zv z{h(|a`~>S;vCa%2!+|{Mms^*V{36{CD{W_W&Lh8gh*^2MT+gQ}n6?OYn(yg)KSe_;98<>pq;1X85zD1u}BXJ5YsE8WFjlFgw41e zD@R+03Bw-_a$IR2#diBBv`_S&p5Ij1fU-HNhbQgC{ddZ2cRHQTYYcYu0u6Vr)Ej|M zs7Y}RM8|6U&tJHVquCMPjbCez1d&^6<&P8{^V0}kYNJ3wCkdiIz$murtHP%DGR?WA zdSc|^Lhk`FyY;Q>110_wfL?oUx0mtF6+JwHd(&OTgNV~Zjf@LXiGs;P<$}XoSAXu? zVJ4DwdpU||^_~3}Km3vYwhyCFnik9yH*E8q$3p_v!-jJ&HjKW>VhY+yt$UH%kA6wx z&xAGI@i#!r`|pubtxY?t#i@im5HEHeoC>uOcSCxuSphThXe9rAFsE>=ab;EiswcOm z63v&=d_WQFg_Do3+~J7WI#KDKVW4#o)BBiZWAu5MyXL(j6-N&jhXnE8;T?S^R zc;AD07CW}S!wddUFD6m(As>~GS*7dnqpC=)SRW*iBDa5DoXG+M;=YO|0uu!cof^fpbewlaKXjBuod?9NJgKSIX3TKlsOr@fZlDp2WX(^H5Vw zEOI9rODq`_EKbxdsvDQb=F&#<9s>V?g@AOGu20h&END_OINwzZ9%ep*9oRfMb$GtC zLq_TKt>MqaNIfkL@w2wp<4X6NFGqvSZ#xR(2+AbC*=7y=`QWATgWEn;QHC-la=iV7 zbVHGEBTh5DE7_A39<6QeC0(~iR_pWl_1`N^vA5weLM*J#W{VKX#7Z-(GCi-DcQf^GX%1&mAN}2yD_($on0?Vi%<={Ce?}Wl|G39eT~s^Z zfmfTn_wA4I`){8n-hTZZE&r^Mqqk4BkB1MHsAj&YPV^(*AbUzXTK4&)R{`cVG3DrY zEWX`(LH$&=;ovE{_kK7@x6A{_Z3f6p-}q^#tr|nl7roGrWQYN1xA9nDH6{$N9~t0k zWbR+we1xlInOO=WG&OXXH?G)arJu@ta~D}otgRqFTpMDfswD|z6zBcE;)~wO{&U)g z7R@|xzF}`GJbJq!;?jsdSLIVD0-;>Lu9MkVTFR_sb+~qOl2Sc!j(!T`GY{bLnWvfW z@ER=u6o}J*_^Q*!)UjrIz5l(!K2^2Hk?p6cr8|$P%vVX0Dq@k+ zkMDf0ZG1O3ND(5(9h*ix{=~9bC*<=(h4EY>sWoo4pa%+^zrKVRP`qek607wY8F$Xp zJBH^!_lITSq*aMLy0aMi^}F;uz8D(lWq@YX$*BFcDvjVw^AY#B9CQ)OLdQNgdRM`e z&xw2dljUZUp^onl1PkhU!6PmzR8JB})b$v4jnvw2zZMO|!I zvVR=Q);Z+nxg!!13(9<$B3=ErnW_x12Kj>q-i{zlM|%?F4mRvKfVDT#f_~RXTi=5< zTRUWiV0F;eCRTyS@PqR$Z30->2_v5F0|&LW$nPYVy(1fcZN`vQ3o&(poc`De%AP5a zmRV1nLdom*j;A(@->k)pcU8-Jje&fv zMprM>1mWQsrIqN4RZw59IDR*@{@NCY^Up?(y80lfm4RZ_5J7RL&}RLj$ulOif1jhv zilS63U5tu_;+9qEldG$V_9Ce3Lo#c+sZ4$s`xuM7*aS}BtziDtk$hY8i%ZpWb;L;W z%jN;kGi(l&rD5F;T0#28+kVqxcW5a|*xFf%(O)9uc5X9UUCn@m++-s!-D+I;%vQ@X zyy}Ct)lvjcrv0y0Q&paDznXMMF`uo$(ke7Ry7i)7xX4nNpto#B^UZ^LDnWU52EU>=Y^Gs5z5MU?U)|4L2k%q&= z&hB0=WlmB7yTAPSZY3wYzy9fgfX_E>b8dX{{qO|AOv}QKpl3j(jgxoFI6CUbYgg(YOPn~yn~rAq6hAB4hq`@ps`_vVHOy*W=8%wqpzxuVG zp=rR{b`btv%9A&G>`wYT1QDU{ImHSmum9~5z{iO>F5@o?^c=pmihV7I+m56lY^IOJ2E_f411N`|4GIm z{sHtiyec48WP6Zb+V5Iw=gJ5eW0hZDO1ch1{2D4~L9PfU6n_fc;{#Zk4E2yN?O8{-& zejbL=W%^KL_W#1zI<&bb&^YzeoD)6V&4W)Do(*tp`2skxRA`VcYV27L?Ge` z00V(>)PyACAX{Z58~T|Cm9xUwsfD@^M+N-!swf3EX%X-~Nke1<_t$eh7J;~>(jbe0 zR6v&M4F{O=M=+uRyPlJ?Qu znL(YFoI80=rzK!wYabQ6#Hos3eG**o)_!>LfLV__O>^Ai#70wL`G#trd8S?KQ z)#BUjyAe2gpfWwCl*tmO(*9E$roQ< zgjNFWp0?|{&50tWYkZMYCQpx-=V8;013{SpqM^^j_Wa`;k4^Y=5svTglk8 z7jJuVx z^J$*z^A&lY&)Uozi*LBQlU3Um)e(*kbzx~hOoob?oSbf!vxuQ8x2c+yz zo{Q$&8Ru&^MyNvAw>j-CMf~f^P>~ygUMuidJ8e>`U|*3NvqLxi&{0LiAd}Q-MZ70; zoCz~u`75UnbpOVNduL{X>;kHmhybUYyoCzJ05h6m<>yak)EiygJaLdtxsO zZ+zfgLk=IzgNBraXrZ*44=dD`Hg?T^lFmMDOruQ|gpPY1e%UGzGBn>OBQ8z!lavwq zR{21}g~l*9eB6nfO|ibcawi;`m>`WBqfwGJjRg|uI=K>%nZ@&QZt%_UU}kLs5Zk2y z0&u^h>dX9#HFYI>&NfOBJE_Vkh=$#v+m8q1e<>X+Pwg~>6BaWiJw4-N-hMO?<0u{s z{hl#1ahb<uCEKJS?+89+Ec;#Pa0m6(j+HQXub%z2Wdhx5T?qNFztr9~Ea=b5bH00S4ToTQ4|qe~qBpO=BY zj}M~BN}k<)Q|uEgZfhWHCkY-8)LAAC<|8Sd+XvhiG+0q#-J+*ZJn{9*oaT^JbiQ{l zbiY!QW7GVr6E|i|u)l%_V%6v}_^vp8X)3)}KMMPqb7r%@l%z*t4i9-G0Vwy(tL^FE zrOP?tzEc}8NGBKk7Xf<@tx*S=1iY?nJ>RZz-7T_Zyd+Usaj#-5G*}bL9{_TV%M8|0 zGhNpcP|^MVw}e&j(mCUp@@`6t8O0)i#6s`a~B^!Th$UAhm9E=o+2A`RWAz zvTgAD^NJh@LSctfQ}pRr;Q=SJR$P7OQ=rGq*Kf0|f#F!Z8dDXw-)laNiNxLVe1gW7 zBGfmfj7LVaok31GMJ zEvDZDa2civH9JKcyDu{1wWQqj(@PvXd1B-`>5D`uf79U|g4J>Ket+jM7P*AvrH@GP zr*;!lCSu|j{0z*4N!E7?jW5)L;$eAzn6IO|*!ak5{3%#t9V&;OpfndkRbCtCq`VP( zi7yJ!V$R$#mbj>?2{`TFApKs-VjovAZcVI$m0wmVA)&#R>PjTyKteDF-*8 z48Yy)M~QM4_q^FUwp+P7#H~%f>m>4unLZ@-=h^KnqF&ZV7i><;X?}$606!AMEa2C> zR=Fi1R}+msHm5n6Us%2QK%!wxoGfwXXM5PX^verW={VkjR~UuR6AnYX0|6nWZZzrq zYtME!=}-1QJy~?Cx>H8aF8YmJfG5?!uj8GxWiJ>%cdw_ZS0BU;5$6lLJYW9POlbmJ z*zkU1`y?}1YdF{QH&VO*2XiYMKpj{2%tXsi%;Z~zyd5>*v}G~a3121bRgX_~tm)eqWJC?(TxI>;e>^{s^Imj- zL5b5c^J8nBEI)bG-1TYuyIyhEF=d5uQ`BWwK$>^cT(lorOTTL*Reh}vYWb+*qy=Sg zY=#}cC#T#K=U-1`WuO16vZh~L%Zc!y>Vw60{80aBxpA2M;_zmkAoQke`uJz=UpK|g z)mVB?R8o&4R0|SYu70uit9|pls^%_>xwGjdli`KHjRx}V|B&^aVNC^Hx1oa|s7Mnb zAkqZsy+uGN(nPv6rHayf2?3?oPz9ta(tGbkDWP|y*U)=_03m_g_`cuwo$GYdg8ZxvTNQe z;CQSZRxE>ePe@SznWdTcFV2ju4);CAiGgv>BD|z}ti{6~)3;FWu}8yvvjq?pBb^hS zq3TxAC~Hj~?K5sUD`6b#x^*fzMY)P8g)CWLMFfci;N) z5oKkkNZMeZgk3-JxQ14I`r0rA*lUNS)cn1k)PY&|30s?Z-qeiO$n9^qbn{WVD$LjY zr4sre^!I*t{NHjvsjSBmx*4%9;fmU6R7B62c`M$^?M>*8-ZvJD@8KBulRlknR73un zshEOf-+S?}j}!82xICb?*!tejX0VLdHi+A_@ixSc8mg>Mt9!VkUusi`bvHVF(zVAc zteIcr(DqkXj+Hv${Qj}MnI?I`N|Fr-p>*Dqym#KtaXSm*ntY1<+%Zt7+;|Bc_&&vE z)=xQMpj^PrhhLvhJCR4yF5FY(IP0p3!gU+00< zW&fFyy?JrUOmX6G%7o${R%Yt>$@hRiJn_b0{NOhJSgz%8N(mSYgUN+t`Dak>A)_N_ zrs%ecX%g#`&3;QBcYC9AsIZv z*hBQbJ#`fvwQ8jCycu<^saa17W?_hQw&|rN-1iPzt(YAQG8EzJxiFWz?y*B{%xpSw zzwuY?`IW$f+#0bsOkx(gS8=WNp^R1{C!L=4c1oB#MTd6B-eFy^eMOD@5_2gMa;$Lj z(Yyc^Va)WvWef(Rp?Yb3$>Ogqu!rO7p5qv*D_TJ7M$*~YKb+ryIO;1tvtogPTtj{1 zO$w&Tf3CsEHiBT|bvT94v&jDVmH;^>DQwuoinky?T_ujSVD)j3{_JW&kz50*AxsW6 zbW!(vA#;fj@KwusJ{x$khR&n&nHtT-s(P%xnXb2=q-@vt!qa&(wtHR%%A9yC_rgLL zKVrV^Y@|OyhCYt)5ecPYi!|h0)%t0Wyalqn_MB=Z&)z)do(N}+7=604DnBHMW@2}b5r3hDCiy|M9m%^n&TDzl*BOh_+eq!_! zCO7j!W7n4h4Hm|Qtx4FkdK$wGUR3~Z?TZHD@Qb^z$!m<7!2d$Cth5%1(@&PGMec8d z`;k3!a3npmorKTKw>B91-Zrt8i$QX6KrNtZ!A5nG*iO(+c^J6fx~oyM6}%zXfhT z$Nc33m~^95%^z!H_#&MvRzCQRL#6U8OWur=dL=Znsgi7l9P4H`d_YMsPc|bYjX25X ze|#u8>=c>n4YO~lUb-*$*>bY_BeS~qBhOu6JLhmkz^`ph?9kGIm-vE~+RxjMrkNeE z_=3_A(tF#maQ%}fDI;rI@rr}31~MxiwP|*F&P{VFn$5tX!Eb{?nFJo1HZ?z9RoE;& z0mVd|_nBguvjc8EuH4oOq66J;^N;_`?4N|F3{)zxylVY^&()!NbUHianRM>W1Sg7@ z>`q(!Zq_I%@AR#o$^nwe;7_)dEpZOOlhTL*hls@FcC z7pTB7_6#!bJA5Ry*$? z5zFlH7ZEiwy0RRWi^cSU76wJ{zD#D#_A&aHszfAi!wcC_p2VO)HIug+C3{vJPsd5q zes=8b$eZfT6kh)Xo8D-ka@)DIq~SUQj0NoyWsBjqjoSnJxzk=dY+X^4Ek6Y6@@k`k zmY)yW68)4-4rqBB>LF&}%&|)6Y&j!ck{VZ8eJ=4B6+nGt+UbWW%I5fyf!t8%wrYPm z(Vxe5Q_R3CCjaE6hdx;dZ^g5|M}^rJ1u~&p<$foO@Jn{rA54U|-}_@WA&d6j z_bq@Ioq}^Kk@8#ofy?eYW31_zxipKgNG6Gk%g*6$cSlxVF@&Ew(do}ey13%V}l=6InmXDL-F76~PU3J(joCNXmj`FZH& z_gAMLqpv@6ws$)dRu@19YUI+pqhh95hiO{2OFoI(z2()bf1!e=Cd=IPMTGsY7Ql%9 zcj+6kR+YV!!x4z-_BMCGso>qS3PJU&0y}@)Q*QD&^>E*_1i{k+lhd=4|2kbiEBP5* z8b!^+YI9Ibp`HJDk+0Mu0!sA3rQUr@BaI&uYhiwGxBqYd%(!1t2nG5c9bO9mf-&oH zZ~IkK-^%^j@}>jP4nXHd0DjO=_768%iw_@I*O&wSm&m+tF3uHC?r4G6>ur^7N(Oco znC^*?ZX>d!Q}6DFXem1cPo^wqe{)3hWn3Klx2!-s|4I-HXf6!<5j5U=KQD6*6?hXs z&POh@$f0wc*3Y5z$jmjVuA`Csx&@=24ZP4j$Cc$?X}+90k4I5fI07s-@C4_ z9J-6HcPu#kgj`f1kukNH%IwZdUgl6PF36^O43f;XIDjiw(u{Uc{fIczro)3ecXF2AUSs4`eU?};M6V@v3h44uzkrNuT6JKhG` zm$AfZ->>+O;GE0B6DAyxJ20Pt=x>Bu0C?}T6OSUOuRx5 z;p9trBnvXJCWs2j3&v~@kz`)1(xv;5-Z+8SNe#W?V*3)49pP4pB+0*UD97(>!r9un(whf#yM1Z3H( zt+-wCekJS6k0!cIb8?XodFIO_wVBxmq_ij1J|zz^JNKEDFisWU4oSMLRZOe}c(a;q zok`cPjMG`aufLwwmw!#hdC=gKWukWMq=V|i6#{PjtEA9`bfB(x290=&w~qXxB!6>w zpBii4f9Xu`#KQxkEr^avuj&Hr4QvL{0yGW_#QMEW98Rs7>0}i~6=gV1-QtaUuPMQ& zgd=$I$L|KrYf=Cw)*Zl?IRSvooS?~D9huWYwSyP)@z;n*B^=`@8H(|^o}mS?XLcZ0 zet!Bp+D0?xxmdomEVhuXmj01{zb3OyrN;h_`0ZoJv3Nm9+TG7B_AJji#=%Tkob|BD zaAi~;Wf1&+{)-%~FWCI(jQ8myesggGR^I>#vOHdzM3@=@PGv`_Y4h zN~0g@@E%Lc5S6C-ce}o7-zZ+TRJTe7#}8bm{SZmm_>t}q&OP({mguiWYtJ#fLc`7W zOn5V+(7hXYLx(H*S=i%7>cn!NiccF9fF}Mi4fp?3AcCtnSB7jEh}lf!DYp}gXJ!pl zc`9t>tqap8W>?jcey94fX72kukhc?Cwa~#oY-{EpO zcS~202PSI-{q#hIB~q&NAnb(bH_*MfoZ zCf*5-cwF)3^~eh5Pc39G!be#tWPL`{6p=27;(~IKKr|jL{mwjX)IJEhdP2WUas(YI zY%niAUsxT7TDA(+DI;#RS1UU zt=%%l4s|Yy$C>0LPj;MhFuy9*l7V5}=4XM_IK>?j8$(IAU@1NF{tzpnN!#TJWi&)i z!zrVVO%PdB{%6hb94m3lY^D?NsTAaTST9?3tG#EL-3&e_pO{x{hgpo*H7x|xA7W|G z@!(-S4T-cPw=Pni2FK#~N>v5M0dDr3hd}*7oZJSINPhN~zmlcTtDBNq>V55JN4!8b zEGNsXlcMc+xEcfl^S^j!pYDEDwyU^1)O9Ny`5d*$9)V=)Fy!3=BpVT-2zp+EaxlP?WzDf>B7^)EJo z1U+sGSMCsFOrj8%<*6^xb52(uSM$Pd>6af|aYc0?*$v{U?CP*|%}*YWQffGq=O;GE zl4#k_nj$q9HpWzIJ4PP019Mu1yqS>xf+K_&?aU$hpXNH3fK}g7wsF)UO-IJnK1aYI zS1_{y$q8;pwo&>rK-ET4?n4<--8HTU=Y&sJvQ4dMlE0j#0V3yLYw&T)|5<+;Z$RSj065cfH*_Pt!nQYZSK1-y^5`Gs< zcDpzH>N<3{BNjHcwgchnaqubQsGe3rLkVEjAY$7CoG4Nlm-4f;a3UL3ic6M83~!Jj zlrw`(HMfLI1`7Dwd6;~$aQ3Zdrt3xjurHcWf**< zOGv>{{VOx@r8wRtH8Sy+5Uiff9l4P>@Pa}b<+Np!amOzAu#fA(MzDpSn_c>Vk%T`g z$nJJxWv*ym{xFfBc-&HPN?!r0+@87BrU``>j}G1w&o&LYsxe!7DBk8`E_MDpAZBK* zojHZ%1fP};WW(#uwf{{xF>(Aoh`>z2UL{u8a`> z@zQL6L3dogj1WYo@`vXPZQvOQY#EYWlkWbxyL%?w*DhBo{cOGnGD4I=Jr+tmyFAon zp-gsr?SMl-9`nwWrrEO>%XwjBO`HTIb%K^l#oie3u^e7GA*nypzg^tE6l%AO(~} zyMoQMyQS{Pa%d3S_@N{xLi6f4=R&!|7wBUGZXbgGR90a$6#MkRX-fE$++StJG*acN znKN$_Bw7fXIP$9Vz_jDKxR zrc+t`Q- zbW=OCpp}@$u{m1m0QNs!kwD;rOX8u@#n=cMpjFf5bL*24g#58F$<$7X)vM<>Vhyyy9hR!oFG7O{D0)>Xf*N5Xa>~+erD5O-rRD_H)Hn?q@(^!z6d_DJV#3viS>zWeQ7Hi#*4pyAxt3>A|}eRa^LfaFCbw3q@Xq zw&!O8@#`f-t_`EZMz>aan#yH+J@)Vh>-ro` zp3OFd9bv`9Smyt_+$r@XL?m46f9C=kIg$CxYLa`~qgmZ)(654d!A4n)2VoH4@m*y>(<; zdMecL9~XKn->jp}lk)ZDf-94+g#KEe7Tl$fjN_eMQ za*?*^Dvz(~HLn0?J0_3)B#2F8HWm&Lnez%v8Enp4fsz1+Tgeet>FEaG2IT=hK)#hy zoT=@;yx|53q*1Ua*E_>{(cYP7OiCt|upgM{t}3T+8im{bXV%^O3GS*{^<_*x>Uv>^ zsk(nMsUuJy1G1=IaFUB{H6g;Bx6Y*}Fhlz6besLf4-ex_ziv=NVI<>uUiXst`X|NK zgR6C%ldiw-MR~sRnPcB^IJsi+odTXES)wK47V0k$wO8%OD#DjHju&n|BOSpYn5Y*~ zp+z9W2gkfhfp;COl4^!bVA)6)egCIAb_DcSPQEq*KX_ZAxr&?MSQT z>TN}8x{Pn&Ov<5)^tv7COKV;iPxXN(Df!uylvlF$jM|vuoH3f88TJg>~fV$Fvi+ zN30Eoz&WG8Ps^Clr6!!k;+YTl2;4Jv?DFp(6^81i&25OLJD;uWf<}BVRMFFm+Bx8o z#%p?SlXIyZ>u_PRiyCDS1;;6Vl?>I&>{kAoTuF!yAloLh>#0l~gu$G4NGvs<+0V79 zzveM^ql$Z}x@pLsd$Ur&fCTbw`xm96v`g69e4dEk@xsKxl+Air?MI!IhHwcMT)$lA zkEddz2O5IgkK`}Ho$j4Gu#7yN9pxG)ZSrCKck4u8Lv*tbtsN~|q6BVZGwEK_KmC6A zH*~y{{B8Xh>C!DhC=!nS^{HWgM?|miqe0D$NGN1=%W~Ca0;i5uoQ@CF-AW*kG+^#i zS~D6kV9C*vw>>_I4e<0OPF0PQAb37op%}M?ikZ84>64(lf-yibscCF z^N($W_lC|g&RLw3GDv^B|19?F&Bp!}H=No-xX5fx-*7sD@KwE(I)f}Y+(Hhk232NZlk=Rs8GP^b zFh)u>9+p^Q19~bn-A!Cw_iD436)@#C-ZA-E17i#EX3@ba2KeXIJ^oesF zTg%9W2ORcUPUewdc+|T$_5MCuu#X$8Qg-&c2FFT@d@YsF@+is}+fy>qPh@!2P%-H6 z6FbI5yFQ#aNaf9~NdOWXq(NyP6x_*fjfTl;QMcO3q@OAqw+=Hy!9k~Hz3`x`{Q;7? zFw1pbKICSq=Vid&es6ZuVwmAKy5OeK)Qn@0H}^f*6Wr_;*(Gh@`V(mn%91}kh7DHt zX@OiQS;-dE1{W3n5+qVi;R9wq$uY$H5J-(a)Lc_X)c(>E?EFL1r9#7VWBN1E{PZ?vP*FApE|WW?)I| zJqGwz#9zkr+fcW6Sl+p!*8Jj?U$JKkA{!V1H~B+&E1o17`jg}dQz{W)-HrbD`ez6f zQKs1%&qkaiXvM%P&LIzt;xqdL!9^jsM8~xdG-^aDf+s#KVho498Y77UcY7SUNO#Na z^=+h!rWO6v7`#5L1?%9>l6=5&B8fGW1F6fh^AKUrJ}6jiXW@^Si;i*LpOm7duM=xO zJ_a+>LHW-bcS=(lANZt_%JMa@4n!2>Z>DEcDaqL=GwkH>dgxX6Er51u`n`u~yS$%{ z%EfOIY8cSw7m|62xma?|rSf~e^Vi3hoxIw6Z2wLcwfUE$esGU_3=Xf)=7>(%FrME2 zXRUTzzWJE-EARNo@X48Pxr`fey0D)L-1jv(&&LHv*$uxhkjYSE-eLSHBc?d@WzKE^xZ6SzpYTimsXGeuZz4-X`w-C!sHx)qi5> zw^COCGK;gRvd^5~(*hcmx>9by&$JfaC;y#Us!|jH+2=m!Ki4zV;WC=m##Q^SOp4o7 zz+{WPEv=b_C4!CD=`FyEMuV$AnHZA(Y*s!2xg9U~d7Aa#(f@k)UjGAJ`s&Uo?kV7~ zZIl^F8wH{{=K85lK?~5dy0}2ooC&m>Qi0yXksjQd2@E2#r_E1f1Z)?cukVomiAk`2N^3A|Oh5fVaNyUP4lsJ$E=;MW1~NAkh4LJ4!iz_~W?c*;IbLyWOqZ2WKYKWHS?B=26)g|k4g=a>JWWE;*L z5FQss%vO`sJ0oFtmAxx^NzRDNNmIO$y$3Vh6v1THwa z`gKLJtKC#FiQ8ki6|32fRHPF0^i=0q@F8CwX5>CzC8Ft*hAg|F;?w5Tj%kX%wrfHS zDDK|>*W)Q~W+>Hq1<${ms*IfB%zS`p6K;m&UDwy@*3`sh`O7kW-`QB7G_o#7cl5}4 z0ix>SV?fubV)}0uHyb#~S;=uy*=49#lF+{qSjkekj6A=3^JaHTr($hU zSjDp=>3E+AKnx-d62Hk@AAZ!TpPM5H_(Rq-_ul9|tu^*;+rleN9QrW}n>lXT?U<3zP#qh*`zKfKv?#jK{k|((Od@vR#w<=u zQ!a#85<+1XksHa|%6<_oY+V!bCtlIWX3eR$lz~C-ehXpYvtNee)~~MCKw^`lK$}!T z@^;8N_inW@VPRP?v$zrF{Bs9J;0_3>5(YEkko|gduHDNys%TO@p=`FEn_mcZea6OT zI&r-JF#A0SV!nltziu2IH36H`e6Eo0V0iG3x7Ob8H-qIj7oAD9MNy6~6XL3%AFv*9 z-omJ_b(sCh8<9CRw6_z(_M@as{E8*#BkK#s_6jf0FWyh;DJ#(CyOiD6vy!CB zbF-3GxiEGfQ=EBTQe2p`-{v(Pac;=9IG(!gmz};Cx8M<0;&I+vTzSZzSTnltY_VYu z{u3?cheJ}hhaWY%{|_EQS5kbyadO{EC=nDvW(6>IHq+om`_qn1BDIa}Nr|Te>B)uWzK)L9E`;zeYK={>0^}x@WZH7;FX2 zW@|_bKZ8WQfxfMU6?suwxLYB#ch?yS+hUmMI{s^Ra1QUk36`Dxrj>(7Hf8XmCOmP9hZq~_W!U=pi=&s%yilRT>ID7fS;|v zz{9H#Bd>lQiZ}Ap0Z1_qj`}uAAls(;2s;22 zL*DnSKSz8>BV%!=m1S&n`>{c&QQOB@qxTXeMD~VrcNV7rntLPlAUQX>ZnIte7D=u3 zI;Dy<4%#PNc=NYh&OcDd%&hS$eWHKmsYY{q+Zf~s-AdB8-qX0AMX40)6D7DFXK4!7 zV)o>Ean>UFX1tf=nV{I+Nab^+*~frdJHusIwj1R(bM1%rp;s4(zz?T>@%6a5Y9u&Y z=|WI#TQW3qTUtPnco$dQ&qyP|Ow3gF3)TCr3-t#%_0p~d-Mu$L$9?xcWT^_+Mu!!0 z1nrVHtWnqOD6|`tDpCj=Kp)vX;eo z&5Z9(H?C~IBfj0nZK{{L6ZLO@$vh5F;qlTxZL-~H*Y%*~{vg{uxMA|}d+EW30*kOU z(=*OvVp=?&kLmM!KVW)G$)U@{(Bp4R?Uc}CegB(t*Olz1tJ}u`0s@c&DZK+Xjwsp; z%jjO9rBc&15SFi6)LtW^{vZ=&s81Xu_J(Lt-lPo|NfJfTG-SYJJ~{b{AaC_#^Y;aH z1Y)Gu&FtHQIAp*lP1w&V3Dt_k6<@SduB+RxJfsEmEKz7{E%Ig?9j0g}X6xD`Lhx^q z(HeZA)xQ-7ge#t!$iQ(2RRViDZC^j7pa}04JVtytwWeIZ0gS>IpPHhHj!K9C`X=IC zRTa5zCN2`BSQ>Mm4-tKE*({FJ1KmE1FhZpFAF#ehPbkq0^vMDdVU+QZ27~ zaqnUhH*4j-KOxhi;c|Vk@|)WKR!=eipO4w6!YLr#9@x+4bjeqOj*_h}XA9IPau(AZ zx>^8~UO=Mgi()zImET%Z+c=0f>$thUS3t?}2ZD1IC$6-Q%&N|tg_M&ImPu;-R=UeW zynBvI;#;z^uQvA~Y4oJ*YhRx^;fM8KQ>tWcSkDQh88&Rw@Nt-scJ`Kb|tiBQi*C5l{r0rv3RHXO{^MB3Yz=A#zcL1oz7faHlHei69=YvaNQW6iw% zjc6L%!zO0^l_kznU)Qhv#>u;kGqRGV%gq^-OE*&C)=LRBQrvI%=wuCM(ff9Pi2Nj# z2k#~(<5G6X!?|{&4i75!9we1>T(hfX{vp!SV#+W=sk~DRqz@^^CZ%rRlLJ5Z6?b;G zcel<85uuu>?LOau_B)i>ET8NJzxtI?KJsGL$x?Pb?PcafUi0}IU3H1M*k zb;i|7RM_5tuQ8cyo07M+*3M?Li$DW<(Z}~2a&cEz44?I{K>;?WJ76=kLTi7h^AEkJ z^3?G*isFs)QmzeIlxpZeR}B_O;TP$=#i!~82iHjRczwq!smX!t&!V@koztH{RQR(# zQw>ARbfB54=uwLhuI;Ps)O7p!D#6z=p8ys5Xp%Jg!w<_&TO>(Uht`_wd4aa8(Om{&b`MeXcci@M6`ekjAn6|?=oi$whjKc#mqh?3{ueAZvK%!=7Z({dh~ zWWkkXU`w#X5Tnp;-W6G?WXzB<_Vah_m=?S@Z@0VVBczjaL};Q<_+}i7uVF;zk?7c_ z3@Z1XaI5}KQRn@l*McCbmXsxaDehCD+f8qUD$j|0$A;zJf71i)D?gkPVbgs7f^6jw zG2+t95%j&FAgk+esz;zctJc44YW&02jBDutt0mP_hto#)e?A|=&dN`;$}YbpbH1Ow zmf^v^zhY;KUZ()WYC<00Z4FW82A^BJInC^Z8$<=XY0LfQ7HC!d+dz8jiBnLgrom|l zo=?{))yD+(Y_PmVmgb#>yoDI3JA%|EcG-MfY4bSQF-wOLjMn?BWvqktOz+5!X}3pq zr~Il*&tAG~9r0mWsW+7ou0w`z$w9Z>n5f+U(ig7vWtXahW7r>a*KEV-*dUBY zKNL+T)LUStbDdzMxL9~qv}e0H)KaaWL(XOCx;nmC4H>MV_P?Kqaee~_E-;=M6yKD3 z?M*O=(Wc&|$h8=d;CaLHXhdd^LG?6ZCK&M%fe$N0B-+pNBewGZM4d!+2>-$f<;*$Ff!QjB!#P(qLj6?fB$ZH?( zzrkAmaGx4vZZ8}|1fX*~=QHKyr@n{=g0)^~+nt6?UX_(o(gIK=fw+^$A41T9n!fGi0Xecg^ua!t4Hwjv0YO&n4#b z$)k-ii2PaW9a`4JuN+NM%`>lBal8~wreeQ`{u}+5fHBB|s2yQc&GX@MY60L)mrypYbnzihXEZ_Z119|! z;jS9`o06nJ`@UFvlA39R1^Nvrk-p+56gYdQb@TmDo57sLl6s?4plSzGNop!Tz;H z!Fg#OT1{3x=p2>m*IM#HLLbN9u@1|z*LfZv@bG$mqQLSe+L0m^Q!l}OP!P#U!m1Sh zGAgkuhXQLlV+-;z;`u_+(;o(}6hVJ6&`$(i{>Z;@eOq6HciAdA3^_@jmP^^6ca&E9 zn7EVNX8&N2#@wdF>gC`#XA1CYDMGH;n}^+}gERM##F4j9cbes2NV|oLK4ZY^i2sCa zB;)OA_u6X!VW~TWWcS<)pfxrp+gTE_I_ap38+}VzGFo!a z40gP5;L<1Pr6ZLra$=cDF&G z$lH4DJ>jn6>*=?WR98}}dJ5>nUg!IKZ%fIQRY7Fw}jV-UbB~fzI@l5V% zb}Pin0^qaCa9l!G4p*@h7KV#DqHN#z^_8gMt@;aYRgcCEnFaZ|;sHoT-jDe(t@1jS zrk)nuZn{gom0%T@GK-T)WI^=d*noWg_kEeyCp7vrJkc9;IIiKekTOcmG7wM^{9=#%!8 z2bZI98D~3}>Go%8ckK7MEkhYk3W0WMg6#Xy3KzTy>|OTCFh z8snj#B+TojM$$YKLlxgxWXdJ+ty~L%4Y#%i^qu^)Ek)L=r(uD(vAW)bb^= zV|1D00qZu~>-Bv4oae+01<-FIMWK>6ly7@wprz-`O>T{|-D?Vp*6v9CYpHIgE^?P!`< z2%}SaD#C?h^?NVq=l%@lo;6)|JQ>dL!FjdCfK;n&XCC`2%M^TPvB2i-Wo*}2#IbUY z;|1yWY}P|7yumA-XFtk%^SI5a^wO;DJIS<3Uc_$|1(v73UTAQAbQZS}5D`k1 zCzb~|65vPwNbCCIFz*oOa!=m!kC-EcLnj9&SFq7#LER#$ROzkVucyhO-?zn=SZg|t zjrXOSTn)Bl(~g(Xd-9)G(rwGyw(2gHw{Z&ar@zA0?Cql}*VPI~Xo|$_YZSH>`skph zO~fEssGZxPTuS%g5g{NuZ87pIhAZmpvEBuB9__q@b$APdd3Qs`2c8v5mQ5Ao`nrok z+xcp-;tUTm)Vz^2-z+StO0^uCi0oq$^n&Fmhs2TGhEVqi14=E>DSo%HlW}!#kBmg% zQCHl5gY<7%PJz(a^`!$U%P?=Y0ig6SeFuH|48l!;qOXPh5|7xV5?c#mTT?v2yg90M1BjGe7$#bM% zU5PeV6W%4hUffRw?{`(U9)y3QZ?_Y8mrhlbpVvnrfO}`A1?JGQ7m>iUFV#*v5pDg< zvFSD2ve^CuSFq9*J&O&~!E1VBfnXRQvH~^u2SIy!}@^*|F)nTT@H-I54!jIU6enr`t zVeOzLCXm+1;)dvvOXd;x%z78zJx9asBBjEBTAyD6KYU6S(XA0vwBFXq9RI0i-A|;& zX-_mi(!7+wg!Eu!<<@^`Lm~?r`)=E@8rYF0_~*?zT31L$?AIt^CxQeI%w*D&y*l2G zEi?`|@Ny5lkj669u}g_=M;c7uJ_WSz=o~*1*6+!iz0aJi;)RI_`GAya?Yv)I0Grq2nGQ1qn{0?V3Fo5>Xg zy>T)4G4ae1LSCZYW87qoo>uB9Gq1{ncXvTSgvq zOOLb`1N`Xz*Tzq+W(h{p%8M=Ysl!u=7tVu2Jvnj1aF* z72VOEGS7dtTZbED({MVSZ8`0DmR}!-E44|oAb<}k@zLH@sz9bWt+|a~?Z0vo?&P>~ z`tOVeD0rdI&@dSR>DVz-hyBI*)};c`!7)1j({$o!*7hSHiM?Np;p2)AQ(a+qH?{P) zGA0dfR>wJ*13jic>**|TxR(dhR0x%;kaS1PaQ=HqOl8LMNvO|{@# zqi&`?y>|`jlx6pa&O~3<962JS#pWNKxrvqwXaeQSMV>chMF$45@kI>v!h*sq%@!c8 zqsZE_M{J9HOZvkr5Q>y>r=Tjn-T%3WF35Ps-vvfTVi#Ibg_pJux=ivYRY`13t;$X2 zvPLDmd%|MBc)8_x_{048B$CsYqk(K!!?fQj%#9}Cd(FfjD9Mlf+%n~6MJntg_FR2Q;)BU8N5s3( zxM3g0cPV4w$(J9+2CqKr+BDa4dZEMWT_=OiUn%7J-YAnf`7Y;(iQ6( ze~|afoYnyFrkmRPhMszR^8u}gnQ&%Yg%p7KrQ%-0>0jYeq3$v@LzmOpXJzZZ{V(c~ zT4a;q28_6s=Qt?~m?AJ>$N%~6m8^~X_ecHw?xee)YpJOU-mzyrvh1`~1qJp~h#6~5y@=f6-SGf-`m zQn~({Y0G$|^184QHLd*rcbCn)NvqQQ^gz*dN0F84Ht7^Y^vvv&>lNkdoj^rQuD(2ftQ z;u3DVBnv5Q*?$_L$!3#E*)X|$9U)uJ!14-earnLRv|2&%*8E?UvL4P{RzN>*_ zE?5Y>oAw7eO;N1*)+^S?e!A(Y_}8A{TFpo_B3BNG^%C|%8yEjkOAf%&$amLXtl{du zb-u8C8Or7sVrqYJ(C~=-A^{j(${_uh>iz>=_#vBDLd(8yvo4A!PBQ&3_D!ps`cRyH z-ONCZxw(VP=UGl?Fou!(k9L4U`)q5#cw;!7o0tS^P@9^#QO8dpBRUBJno;yjc1RzM zYtvtLp{LxP5j96xTRPsH&Gu4z)~27PHk1|-B>IhL0W`Da{5d1h99D&lSd*QbzD6d% zyodt>`Wt)3<-6%+Avv$a@diL6?xh;ux)qD6#3OmBErt{D4)OG#S8tFCTE@X9jwt>} zxBG1|*{=D|CyLw$AB_0v`N3m8H*BF{|MZR`a9g`Q$bm zv1LSed&qqLUSTr5-;DK6Q+n{(!wcf<+-M&CwBFt{^NE?ao-7m82Y(2J!g|!M zp@{5R8>=vpcQddCCf-Z7z|oD!<-R(@5zToQX$7U#)Z6Rq)=vjSf|up^Ze^We{cJLi>|;vC;aS9QlZnq04=K3mu1h1?GydH>{?W$2N;X zir_({00{rhNA|~ZJUou}W2L~@LOoHL#@kfs-RT33v#^k?8x1!JU6lr&ov{y)4>FN64$tuMV z1@lg^bX=SS?ES@JvXLYK*D0?$Bcr&I1-86ZIU!ZU&Gp+DGSs@YrvKAG`WBJ^MZuY? z-IV)0gdu{%<}$N64g=17JWgG@cl^2F^1|Tibt{MKXutxC)n|B_2w7>P-C-T7g)&q` z>;LT{Lt~J?f+khS-Hgf#$zY$Ff|ml!wXc>54O(XDZM6m~@DjFcz;j?;+mFD(c{iME zYq%}0oM#znq+qfPIa}YP0Wj}(N+-bD^}1#aP}}E(fuNNC!_`}d#nD9FgFtX6xJz&i z?mB3KyN2Kp+}#u0HMj;e)m`VFbMCznZ~;tQ zncfdmh1Y|YWs|*OgI1MLz;r{K;!>g|xO>2sWM!A)-o?h_m?*l+>sHeO=&(upcle`0 zM(Ep$LW!bzoL6sxata50H0$(qziBDk9W3-_2MWkp+PMMpuFwmo_zZkoY|Gka>d9x7 z8pj>#Vy6Ny%55{EvNrpeHJrydTfknk_8VYPuOADd=xL^Uu9(tWYL+09yFR`;G9+~k z_WLS&c|*k~Qqia9b;NF$L(i2hu}ZbL32^hY2+E&qn`*n}X8GJljmce^VRAC#aIvd+ ze5hPq9i#MS{@E5G`;o^mQ`PWm~u<#E%>+blY|;PM6VQ2B7wUL!6Ht;(kONZ}rLGOqx;0~?XW|C~#)k$^68I zy!Umgwng=()RReLk9OYcQ(@KlN6K9jM(&=8ocny4ma~r?Sg4)m17iXTx@R@Nc2PV| z^368m%2OHEo0z`}-;h`=pVdM=+JMm&he1NH!SlVDyyy;y-$BeD+q3af=DOHX^fGoG zGOF`gRVT}OvkUOkB=+l4JWfToz)!KWWsX>EO3}xAhgVVYkZ0sw;@vo)pKzmSpEt+y z%Hhj#X>2$H%&G2rjQ91xYGFx&2Dfxy96ynoIOyxW{Lj&*0P_Ssa>|qV+Lv*M(=>kd zj#hpSPoRyit;yq$Pyq|{!x0_*)YRE*jMutlIQ?i$ex9P+EbG`2?r6+xbQg z5{tB~S@3H})>*|2E2CWetEJEM&}KFg{|r{vYcp7Y9WeUQle;@&^Z&i(IK0i^v^Lf8 zTy09IMXkX8hIYdk4gQ@$GH^1ke=5Ul4IaE8|2F3}l3<*uThbl`eQ#=_RRk_&1jjVj z8d7Du>_JAfJvsrWwdZD@gkQNC%G1IQSr|N=1fu~9BKK_xK0-uoNZ%PZK<^1J-yW-b zT8x2RJ88DionV~Os5r=`VKcC5X*A|&bc3*dk6}C;9|;6lSe^%4Bi9uulX8a-APJ8C zcqs#X@PWEV82zRt9Z3*-{7FE;Z3qcxiEA0 ztbQ}y5KxLy{+G>GX>x7TzU#)fqj^M-T-A0G{}LZ5Arml2Sgw^3#gXKAEQ0d=Y}~}{ zKER5jUTjt5x310{A$hLQOew`@c$&)jQ%#YvR_`%>TSYr7iYn+$b6IV)N}%u`EAtz4 zNynb51vS^6ir9OI2fi%6BG|4y2%_c|d^dl!00;nM{|f2vTzG5zcdms&?`>RcY{A?u zAAm0u?;Wr^W=8#OYJ}T6HyJmmvbOsPgArup_i*XSJBByKfC}Am4&=pAS;p@FcbLki zd3I1yp{@cJ=2SijLoba znK;_>6_Db8Yj!`dU(uGbG3;*w~>f_blFA(HbgLz-Zk1{VF@ini4M zx%U4*L&Q5T`c(13)}H&E^;&{gZ*=0Y64Y3`cdz4B{zobXR8U|p^xvnOIo4~n%Jxx< z`g|D?EW{L%Y$Y(|0sYF@w}`6n+u61bQ|+Wi&!F)%(R<<|gJSaR_+5xSGhhY8Zu^4f8H#(Sh zJE~F?3cmi8GDNY2GNg(d9k5&W@xC=Mwxfn)vQJw3eLC_2t|xYTbG%Nf?~vwQUoLE$ zD06zAxAsJ{Yr%2fAmFqpwYKG#Sza&amZqyeiq$>kQSyNxnXANXdB3DNC8bWEs(-MT z_u=M%ff>WbP|x^FE#JX$dL_&zmT&s~;wAPpbOL)1$sMH&TfyVumzahr(M)yj7VudG zu*69Wn|u+i)kC^SAc1|hxs%CL1CPXRGwMBCt^<4+-O&#{_V+Fupw8P*Dd^0e=z5JyY7NHcPG`F5s^CdYDVijTmXuYmQUl+Ic(+rljmk>1;>STD1;jcK zJ6y(7m#+6p!9}6Azt1>xajr!L#>#wb@UYGKx_FdX|72C(hSd|IYA#pO-LB*>yKA`O z=;Bg4&lgNX6bSnrQS=$B9tShI^c$+> zZH^Tcpr8VyYPfL@EPcDn3&i%siN$Sb{~ZmX=3S>~dYPYl`wjrN_|D$Iq|J`w%8s15+U#;B z+e%Io2|Zg->#R{A>Hq|Pcl=jf_VQ&!sLW5hS_j3|B1Kqxod^Ok=x7!X z=In5~NShS5u;O)rp0e}w$U#vwPxGbmzSsKqmNGL!#AJTWFNi0lRgUVF8btFi)?E)D$sNbrZbs37) zYEN2RE_9%%FK{|RtexEtALgx_BR&!7yrVS`IyqN@K>knvvf4jwWnd+H;zTqO-{aH- z%}Z86Q0Xx>n}+tfFrc&CCDAtB)YLZAw$e5Y59d=#V#uW)=TpV|WT(F-0NXZiU;BtP zxVlo>WdrB{_=yDuJeTyKaN=>^JNFp{V*d0?l!$}jo8grdLH;3PifnfFGY~>(nR`ju zH|ChbVltTC>QtAM9ooULwfkN`>JNDj;+`}mV2m30{VVdPQUZR;0S6~YkovLl$<%4i ze)EO@E(^9MG-3)oRFBQ9bCBfv?{wqJRLcH*F3e_Z=|<;4z>{2Vo^Zu~o%dDG>!SiF z+P|2Fq*W?1A|7oDw!_f0dh zyU9S6CU0;vLQ#wjK0|D+hm=;P1#=Z&1Qzc}gBB@1zls!-70LbzWY~Zya6`BMS1F)eH`#~r5HdjQy4f_S6|}~MuT9$dr`vT; z+fe(>A+@Gf+-#&{l^#KWr>d+3RoJdd5Sb$RGagscui?Gm%`=b~q!H{5ExbN|xUyQB zjXrmT$N!#xjpaCXZ`Bh9C62SEAdp!gEalVT@FRbr_BcY|UOOK*0(=hN5+hAS?AaHM zRE%$^sxfAKVm=1L^=}i=9(L&v)kZs8p#O8 zY3kZzUxKc(aX=)D`?{*}(N7egt_0^sP7fU;RBrxtGrRQ>KwsEl!mI+JiIN3|)uyOq zzNvsedL?~7LLsU}>;}zh{~Oh)Ikdj`;|BrdhmA}~kuW%xQDDS2A*=ESK0VK#Cg`@# zSVG4#_qOQn-(xTNf^O`5T%dGML$&yS!3*mie9W(+UkcgpEm(~HQ3h7 zw5nQHFNqA6mad@tg{+fyWbX*v>VA6pDhIw0fOZnvo^?3fzZAmek^>yt3UH|3AhS&R z2PwlPxIPRRXc2->HBCjWo*F3%158?1bn5>gZ4~UTgVx1k@=A;x0)5J_J?Z~<+<$&1 zn+jYN(W_jr(`NQ1xiqfg`cCZ zZOlAsqW3DrtC6bEnoe(YmotSE-sqs{ZWmL9=IPQBQDn|pZ8fB$?LcEgrlRe!@?=id zzQx{mFGG7jZLr$b7vQu2VGyZ2aJe-dC#NMW^Sc?Q%}ujoUGx5@5ytR{^~R@4z*NtyBguC*akkA)cz~7G_J{1(0Lr_B)ivJEm!hr*r?(qwQ#AD z%7a!XzhW(6=xp}fIt>uEddhmsPQFFrOQWlT&@|grr2}6M-@g0%Y4aYnW9tr$$+Gzp~SQe&7b#P)Q?qz@_A zdgi4q-O(6{?$7?(EQrN#zyH!&pDg{i4bpZI0UD|SF=XiGGU-cuwXom4>8;28K{n#~ zVlX)(Tj9SOmiCtn7zpKSw@P{zD<>1tm6kxI%Wtk4-9(wgKq-xdE(s%m3G^1zVtr* ziuWM@QGZh|z7dEyF*4Q@;4nWD(`Sb8`DmF}f_y0@?zRX-)_P9LnKso`x$1Qie>hb9 z{h`g|+wJ)altksM-5oQ(G?*`6mu+u8?K&f3{7L+k_nj-DKz zYkIQkU4cXTDyM|HAX;GP-J%AsApj{+($LCQbQb{8S}zy(QF8T+0$ccg54zUR8J z$3>5KJNJ1{bg(i<>*j2SjWRaqSddOU)@`d^B{@4{Sf}@55mA z@EK^oK{K{VH($QJ}TRf+#ayB=RxBJt*s;$?{X8yipPhIWG#)`ec>0q zlwC42m6+TkQl7D-Brpy>rN08e5-;SRJX(vi9$y7Ffj!Lflo?q1G2J zek6l{VDvSSkzw10yjU*NYd+`1SirwY*!TI^+&N$dYj;#>#UIG}hFQmXZ+vU-n`do1m79ND}q z6YA-O!{%F|Jn2Ig+@r^;4BE>te(G=Inq8_dmwdxSO)KMc&iy=3uG&H<2Og zqZFFLM=UTt^bAwhzq4^^&CNI!U3>auAYC--pcaBY=ngmt*ykW1Xy}nzkE!cdTwU4D9`&rK#&q&AynOC8ui6CCPFld%D*iszo&B(9cH;qAg9+Lu}lXVCmEytFH=I9yr%eFqso&uWVz z8r!Gs6l3X6{=;GxKS+}tclES#yX`_D4NocioHs9zH}j9mZh;w1q1H$qa6#PZI=eS9 zI#j_Zlh!;t>|xZn6_9VZO>F(t&uQFij*onl&zxk}izo3drFoSjvg|^adrenOZC*~2 z8Vk5qP{2JXTmUG2THzp#-$y?#+&Q}hvD(TnM_rb5qEEG;mvhVFw5Y6ybv`=cul)-S zkP~>GI+blxoan2{Sv^r+e@4C%Xsb*6v7uK`i=bF&U0)PksO-(^%>jfB64fW!)nDc2 zXd_532)4#932^H@hyyWL;ax3xBaPR8BJQ@B5wIVjqE3ApoG1bc%pz&)Flz(y}Hgc0!&<7-79w#pQHK6h?-^Wtzif9^$v-s9f~Nv!=cfN?%Z zd5Bb0Y_re7MP(=MrFZd^9KA_aeb`O!JY9v?N7x7=siAwOF-9lLr5vw!vvm!QZcGL; z+x3-#qV-8F&l5T{Y|gV5e&;`1-51klmzR7L!04+;EVomYKw2|3c;AgPz8)LD2ywjL z{5PBnTo*E}p1Pxz@~)U7p{Q%i?P!QStf+Hk7a41NeXa@VRK;7^ z-Z`-D4%qkutVw@9^yHuG*=v}cG7~8UmF^@5z8YGH|J&~_I1oT*G6om;=|2OmTkMyf zxcy_s?nQb}e|>lZX?dCf{Z$spg~DLwSn{!m3hPv)>3U^9|BMejLRm|l3};mn#Y#z$ znP4Kf|CV=l}c*-JDZ&G`J?cQ6VZW!)tKj*zh4XK z+ARPEfn;FlCbUgCq#`b~KwytoIi)B@9|Y^40qD|J>pDS_drc`y;gK1jKUc`&(gP-( z0)82Kt#@3@dtQJv*1^wtNmcvV{QJ_U=ZwOPV+BbWLm$vx&KhxQjF*mUAX~WW8J#s> zzBpyKZXaPgSauyCtUp#dJ0i<0{YI-Hu@i~I4F+y9cttQE#KVd|I1Td{EZ}qAdf&v^(F^k2^z9smNo`L9nG=jP`Rr(UNyGnwk8!X{Y;|WQZQ|2OyxOa5YviR`wZn%n{ zMEodX8vyrAjb9p}YL>R2R^Wg0IUEn`1lNliNF86UBKElVtQ{Fhy!@dZE-3UpD`9|S zOYvVA+z+(*(Za5HzyqdV-wvPhkfRXZJXMXn zZlZ3VKlZP`d;~qSoiyd#mA<2uI%9V-^{8Ph+fbWD*B4UqF5O}Bn5LOSifR-3wEvC7 zODTLCus68J0jv5}sNjw_f~VSWiiU6%ZQBBxie(?9HS)mIEvB&b3s|x~r4A&=`^kl8 zb24otP%Dklf_?V#38r`)XCanjFtn41qHq&aCvB31#K!dl zXzFg=FiwxHLe{9bh=K~~(?68M`@6ig^Q~XNjbgGk!rZ7z`1p-ga-Ti&^`ZXD0U!!x z<_UYRp%rj3s`=C=_l0I1L;$s7XpUUf=L40mYv`g?_0v@D-2F@NCz#WHVaywkIHV5u zH7u~W-cPJ>Hl94ch1tJUztD7+Ow9o~-I|)4d3QMRT{i8OUqhY);G9n8%Wc7_J+(4H zPiLDl-+PA$@ng7HLss{xE_IQ~vrP7JD5E?hR7e**ST+~Ne&~9uG1;}NE^`-CZ%fiE zcQz5m3VVw%hNZt;@d(YaX_(0)z|LW?#CwrC+B!z`_`k^d@lzl@+lrU3b{r%-ZM~a| z9EGVQmVjZT63|?A)-p)3bo@Wey@(XwvvL(Qro-{t+M%-^!a~ zSQxTyi!z*ivE9ROxjl0) zY6dz_++zssnZF@x#4yl&G0NG1LrjM=4>Q^~$5PENK7+YN?Z3zON{nxc0^5v~OC@#I=5a9$1AOn|lwa4B9AdQNeQ7(_ zN(}!EA+^oW1I?5pObo<{v6*#c-pRPqrhX*GeQ7R#$W5=OIj2!*#6vNKPkFfHiBeVU z*}^#ZVCbf=?m7Z%pMRh<_D9yWA8srz4D030;?C032NlsfJO|B{o|a1n7-Wb6_6Dc; zbUwYgA5OLoRf$jiwN|>5$l*ZQWFyxeXv83uM!uAn2vc2xvFiTiUO>Jg0Q*HK%+F9-xBU$0eP`1>))teXd z+Q%yXgBa%hy7L;f3VBxrR$7)Y!FjXR(^_zHiXbz`g`$KRn*YJ5@(^r*Bbtu5^4ddN zwu}UgiMX2x@};~0wKy9v#7K*c6G}D7sb$V+RqOe3eIOsb^|{UQ zIErxHE+^a$fFVASgr7dO&{3GShaavn#s0{}Nif3je1#$s2f<&Tyb-GuXF8e`3uA#7 z!0C-CL=T zDztBF{CyFV|d35zIuttj1}5oe^eY|HA1E=LBKyLTk?xZRYFtzH3xE zWkqXMS(~*tn(~zKVtXB>VF33Gi2p#^w`OY z-Eur9aEmMI<6y&exKmFjd{Dc-r^LkLKBg5;LvcVgS5oP^tRD_;$k3ceRFmlDD31PA zP?iTwH%&`sKdahh2?vT@(_RP;XPGf23vyu(QtP+HX6FP_j`DCIa>&7!+;Er`oSW%* z8nkwkOwoDqvVBs25q+Jxm<9x{ko#<@bjBiWuGov*;D86FsO&0dBRcOZ$e%JRZ|9Iy z${%B&L%IX>bgcd~=(C-!tX8$y1nv3-_sp^q!~QbsOI1LHcXo1TbGsHLHduG>fEetcN(*t zRhC0va#+YbN;P#ATu<6DVm3$q)1UN9#mY~d>#%Evz{Al_r`|xl)=(&p!r(ZkZ*z{X z)>dZ1dq$r%lDfsS-RmQ3v>-DVB3wpX#{(B9G}p6lA&RMpT@MQ`untE_cWk#J<4)v@ zP+Uh&%#)LixDWVVc(=j({h_qPZ843RWyhbhT$=SLsVQdktH9p=)LAw(P*o##)G+vg z3SovW|2M~Jsh`uoP(!a2BCW!V4KxBFzu$J^d$J@h^Z}Ywckm&EuBZe%;@8%<=TtlK z&P6Cs7F|`xk69`nK(YI#r#JwT2Qm|b!%=AaX93X9dfXjxJl}*q7;8Caj^6u`PY`+q zF?!+Q^0XF-A|^eU$crX~nS*{L!F<{TU3qA&05JnKgP6T+@5H8o5m@BVtqIY?h-a77A z@k~1s9w~UGqUTF(n;F9dU;0FC^X7?;_<(`;Ghb88UD%nP1CHJ6L~>>Kc|1@(PB4J# z$cN0V2id1bMCHNM`WZ@}ZEX%yLE2@M27(2t`@HQl;3no)IaVsb&DQ{M>$UwF;3?%0 z1?Ey`QEG5>Uj4RDMo&+VeMyhjTJ3I)*px$9&;wWL-hKhXI+caeTI&iS`0eeG=_?o}FbAff9+!B`By8WmOgs{X@{B$e4iwuuXqgE|?wuf#)J`j5aJaRbE8 z#deMS`3m>r@0_CTuP!#mzs_Em%o?@G8^y zOvoW;XtLF`yL%e4SZmZDVy~B^@Zo19g3f>I>;)4^o&7}L%@j&{5ic!!Gs+E73DzUxw~}uz2C|; zF-OA!d@USbPfj*ameb=EJjq;l%Xb&I5lfC$xW}|9E6+cZBOpi(a0plYiTM*sp-R1L znous=Ns`=@X!b8xI?gCOIhf;!@AAd3o!H3BPzMon`=s|NG;o&OXXn+e<&dvEPc z>E0?)i1Y)}zX24B;c1gR4(ELs_{XZ8K&O?`?|hdWT?C5!w<4$Z8P1Vg|IZ@ncOKuQ z!#$=-3||rXKIXs}x-V#XZYl1&BRhnA2hZ3k9`TKQam3Bl$!B(4bJBX1sb8Xagqr`t zpFq6X494D-Ln=nOF!ESKuExRnrMW&Ngsg8(05-Tn$p|OEGg&6kZ)UOPCp)`mh|)Sp zsrIbJ;mwW=B2J#?ykjYPPRqq!cH8}zKiUZK0q46=i>ZP4_fW`FDBNo5l}o z#sC6Iw)1J)r#+)!tBG3Rhl3FM-L&uoG$Pa)oo4F~Vl-XIOcwnN6bw#8>E)Nbi!5JX z8bpeUB#wsPQMeJ~IA10DChm80ILo)xB?8prCCraPDa=&V2Nc=(A_rll8XF|(=5yL#D=zjrDKGf=b}O?g zRP?_S-O9PkNsYJFjsE;BDm2VHjxm{Nb;Tn!$k3Sxvrx>O(>r&LH{D*`7VAuv>>ZZs zBqRI-S-?q8mMDtehRICEBi`^=JcgJK$B#HZuo{6fB4*&rL+4%m+c(cL?*}8>WRTBy zifznVn6i&^zl*q*r<$YzWr_5MvKW}&E-;fj%oc#wxi5>^q1lI+-cZMQYfXVg`a-J` zV3m{h?@pQlf&QXX?zjEwkK&t}hdOY^o=>;O!g*dhkT?T?Z`$aLx%^vWfdXg@_gJ_<3XxFH_|+(m9rGiTKob6#8EA_FPIDdB z_M+U^&mu?J|JF~EF*X#GP_UCOuR>BphKD_gzLHmPQrx(NBR431L^TO2IKoC=CbAm9 zubcYnz-@_WO4)?_UM`+GBOn1XJB5^P9DnjdMM0BHK^n?4LYD1Ds3=2^>4Wvn7!U2H z>7F*TaHj%JDm6VZOHyYRDCzh}<1O7T_BwF!#TaemP1>grEptmR9`-Ok@{-Ptfy zZBY04HYpLzlxB7U!w7Z{HpPU<`2hOQk~xoW#gdxsvCken&lC zd%_Gp#{=WPKNO#5+i*MJ_;=1{J$j+88Lf3b%%@d;)60;&KNitOY4P-y&k(&^N)m(oDST+h58fTaZ;3(cP zDKRI@i@j82HO&3C5s+pid%OC?H`V#EhozU)Sdz&@TpG$`a?{MZmV!Ll} zi{W5#05uMWL#Nyf7_d%HgJb$DVE1Lj1^|n$%y7ug*L?v?O|fEuA87}?s8+vW!cH}q zh`XR>^2~DV7Qklv8hTrKZqJji|049KqZo4SHVE8`cjm%ofIqboBXDtT!97Q_W-z5I zS^5;oq|D%k(Lu9C^o5OT=E836L+@{V7W;hSbfa5??w=mZ(3K*bd+sBNa)k0+@SBD= z@G$2XW$Re~C@xJmG*;7bVb&hsb#MkJ$W%A#R7q_dhAI&mviS+bWCo_dWjsJ=Q zjEy}S@OOVfdgG)l8vA`j6sx<`VuUS8RecCsgftSF_;&z60GBql;sRu$;7dgFqf_n} zcp^7HP<>H^vQDbuh;)&)apXYU%_SO2=LIBcDmwQfqiYF5pk2a9qFBiBA^aYn3gs!g zVW7-XuhE$PA9Q@2Q|x=*0_!kt@3{_2)RbqY&73Pwp(=|S$oA|0;(aiG9_!=Q$4gKd$QK%GfgG*X`c|3@80mET(#sDOElgUBeZ2i%Qy z^v3A+fn>(vlZf6)8j|;>!e(P%o%E6fd|dYM1~|>r8_r>TP-8@ocuQ5?0%-)ezlZiN zUMbNnC!y#^fM%`x>(%?(_SMe|C2f0ndG;K&{DJoJfj3Tb`p?B#prKK*`08?B?zr~% z5AyFOxogZ#{(`U)(~jof@7Bdbt#MskevVzyY4TOBjG4Au`#Yca{QibYe!WRToJ6@Z z)wr1BO?8S|12UagH{eiZO8O{F>E0Xxkn^_#UB$fQu;hj_ocVKgN8Prs-`{+T&5;#T zCcFmQ`L%=Jte--84Hu6&(gZw3Xib0sqis-X-JU2t#fJqLP=DB`=%oTGsUOfx6@~cs z0K4PQ0`-71jF5nrB1kr#I5RH40cS~7GzMEq7CH_ld$0f~+;NaF_>`X>Ya1->**uxZ=jZbDb2r&RQ zLwjz&+^&UEKc+c$LgffwIkW`OTf$%JN9q7l#QmFZFraJF-pw17-oC6m{ln-d0bFki zFG?vVTOC7Z8O8o=Rsg~edrW+DsGWb+Hc%_@zob=+}R zBTvzU=+`dtA!J4H(BKh2B!Jgj*w@C|CG7ILBWM+Xl7OINs zYINotL&|^R)n{mKz2Tg$McXK;&I>gQeMOh6kB$T=9z&~kt6bcw5uc~l0y^G*P}$t} z>-MQc;OM9p-wPs)7HEC-*=poj&5RP7l&{!d4L56f&2$z2Gn?w3!eLrJF+#75*a1j` z!(*V@GldT^a@MA3ylQqco(0BH;(MDA=il3@VAo2BYDgMyajZ8)gv28J zOrxZQ+WVQ)5Go#4ef6t-`pf1wR8qTq>|A;!Dc(Sdyl;wczBMK z+ehG75JjKh&0=!SSAH=#Py4`a9`$ly=LW)2p2CPfd-b~hRN056K)O>R+jH(+gr#W1 zyv|SwTl_GX#+h`KjeOV~npD~&AlN3wfb3`aw)fd7o9xIPPo+V`13worGK(IT0jOLQ z=LN1TItvx+tG$siQ{p{Ep4a@Mp26}q1jICBRd(0e+Ez|+hPHz7PWs1nE2zPZ^yTuc zUU;HreeY-+HnlC}_7=abmC5l6{arBqQ*lPMe?(b(uRswK;@8H0fw~Fw36vk7X-fA5 z_^YuGHc3lh%uu7>AOLzstFf&)oxLx}U;Q5PRCJ4Bfu;`?xMeS%6XwdY(z1k#va!A? zWS=aUFFJny-OA9msGR&y+<0nf{$Ihjm9k%J|9Grp;GsgrpefjlEf2jd?v3>O{pH;K zV2fxD4ApPmwHKi9M|T@jM&$ni>HlS}ucmD$_>U1^wEB_Oz7kpL|Gt}0R zj~lWpBH23vXAB8=gV0_W`~&=wM^7&}GyG@WAN=334@-P90 zYCQei_oYJia|F=?_np~{(XWiJsyd)j%YOD>73i7XuRbv&@&71l5e+q0 z=TKmBzuXGJJ0UTIJtNMD0%g7wOK4f3p zE!J~LX7^ZH-G+)sYUqXD&HTYO>1?^1dHuUlWsT?FD854FsnF7(MZCDD z!tc(OvU7%&8|&IE4kMdYDfdm+I8O$A1iuZhjIO)^QgV3q@Ka2TznKskg7M)I-4p!x z1XFadcMra8B#P$?e3EK?T@uW(ZgbFFxDEbM^I7jk6LT&BMjV957>DwiX4oCTIc=@7 zn;~F97Vk&5*`S_44a`YA8W7(NRfbe|OMEeCt6?>CPdajo)kpL!rr4rx#nCL|6Rbb{spG$M7)0ei#@x zrzj4Xv#+QVpUll@O4^OLY1VKH_g6tQ&nd=#mgA}2S-LBFsJ&PD>|Qgv@5jSCoG&n% z7$ZB=bYoKHc0d~@9ZrnNKd7;I7ov7yb6+xs9O9Di)(`*j1Vnsp;B)mI@0J-}@v7Ml@j#&jq5ID^B zvZ7ohV~hK))!)+-QtqjAqex0YDYbj+nEhDg1Jmlz%pETqVC+dp5}@=caKTbMY`(V5 z9yilUKNXjozt~`-`8R_tam7SG%LHYSB1M4bqUh6%!}YOvtU$fWsGySG^PS<=H+&LjZk5$(* z^$cFd=S0VOdy-GZuGxF+wx+GD_{)1El*BQlRA*{bIk06-su3&l0#~J%W>yZ3Ibv8u zoUy36NMX=288UvW`sR6j+BQ(D#d=ijiKF~R3x+{piR=2+?`UHba}g0l<69I75zWk8 zZ96AFyxJaFUc~;bA0ZU*{TAZ}X-*r_eE;R!X_J5pEDb=EAx&1~JLAh&>IdR|G;=gz zfsa~v?IX?-`7}3mi%912+?J$;p{FG&&tN(Xu||G+YB1q`$Frn~4iTDEtb z8m!uh2LuT2Z!=U zi^VIoyf27)G8;WI*~(fFfP!hqYHRJh7%Rt{bJ zy9lvd=Crb;v(ysj3dw6E@R7=33EPImLf+#W-FZ3F6&r&)GG|qtN)#NGq@4q%_?2{P zaA#ksv&`Ec7>&R4F8**~)!k{=IMhlM?{hVsmxsF&8bA8H@EEMaCda1T0CN}$R@}78 zxdr>s6esA?j1lWkPP>GqaI>3F4t4yz`F1a(3*B`3gA=7+cN|hoKCLNjyu8tFg}!&3 zQUuy&qL6-GTSd*{EaBvUdgFZX%xuu|Z4O`^vR8-9JSjOum{qy{(2cP_4Zt$Z-24h* z5W4QmliHs%sY*tQQ6$XO-BH26#Rkq z;icH=_Zc0mIBG7%NR`Q?6b9Q-(I!Y0Yv;`VyL!zW(8= zsV%i{v50GXpNvWk1gS(7iEr=wJxHwWm^hA-6z@^IJ0LS^8)Y6L!ZtgMhUz1MKWjROKl26LKBy;ox8GiDgE;s82Vc`}krxwj9 zrm)Bh(%TF$@Ln^+kh;5tnz`99WFw)AQxovH{54M;PM^$(_aLQw>*)YczVP}L?VG5J z&tT@my&wm1G`-(!hf@Z;4z{(GLCXl$>_XneDItC@M>9o5BDn2D?|IY2WxbPHUh-4x zwU;f9Z-)Lz2DYU1Bi_0|P9aWEo|K1QO>PyXR^=;JGyIMl7vu z6SL${0VXS95mVZwxagmB&xfz%B>=Y&@_CYgXVceh@lz1RQy|z! zuJ!Gx(<;}s=CZ8k7HYL0Asod#x5-N{7{Pu`U?k!D>A@8%kgcA^WGW;Gdl=>V1M!2$g7zWl66)ssq3DQVAn)@RY{ zErmt_$fe;;pM&44@v%qEvu3Z#@lfey2s*xABLc z=bW*QwT{^Lv6VccClb}kHN{D`MM>>%V;I69xVXX=XAZWjMBnC7$qvr}vN?~ggAV%{ zG=z@$&T_}#*RUTJwNIDK@*4Lel3+*C#q`V+OTl;|&UGD+!_Egf~Pm>4Z@D-iFWSp~oG!xYO2X|A{a* z`E9w$4EW^f^>-%)k)(fNzZ68BimiZsDnD#J8SybbCqIJfY1?me_p(8!V|n!CScACQ!*y^;&r2QJ8n3*@=nJh#J( z-%>S%^08(vJX5RXxu;RS`w3$w&D&yw^if~_=_fmEICo{g*is@}*drJ)5wI0EtKOZT z{Je;Za7ciZF5PwVY*ONZ@Dh?Ua^7mPn904vYyZ}dTz zM!^IMSN(5}v{u z>KYxnaKeGcCvC`azAGorATq~9~hL?;nPiXSI+ zR>?}5GfPg{KBh}S3lWuvg0p?1sIJOC_!({{P5MMFY-DUcfgtxzR3&QBjn|DOK!?g1 zm&bAlvWF2ENcOHPnZV?nr=_0MXUCM~MLYy66UKlIjT*Tk`MdM^rfW8f*U6^V03@a= zIu=;0V>;{@7IZx<*B7bg1v`{%UfRIz0`na93AK^kUS~}Mf8scWQG1bFEpB(U*nE}0 z;!puTV8u>TOGc%Tg)QTZ2`AE7?F!H`AC;Gc2GvJ!OR4^%+UEQKF!uNbKKtI;7YLbj zT89Ns?2u|O)Nv}Xzt!>q-0|rP%EDuc$C0V5|A1@lqb>)Rtf>HW6oi%`W@q||z0)?5 z;pFoi3h1I)hWOJ*LNwCd1;e8_lS*Lj~QNQ9Bu-Ck?sG_ z0{HinGVo(sASY|nceYIFJ934KyL6CU-OM8@LxW;o#SbUT7v%{;H-=yAzoJ-QhD_aw ziNnlb`H+cn2qN_irTp;&=DD&1iM98VzP8)MOw2>;LJoAv1r$)P@O;7BdH+PUp6*x8 z^Sc~~_6?ipiD233(dCZ2%ouL6weJ4=w0`|DduY_Fa73h9=+jf)7(|#Gh`=8zd%7KL?Fh-=txr4`u&8evv|tLLeRgNI0YMUBivW ziTC9&jNgrm^II^>85E^*4k{@c)lZGcQ%4JiG9moZym#xInEp?q@J;_Nvl z#CMq1mV+{l6|KQC`?<=aZT9hl0#^X<7q*ar7!vCqtatX!0$2S0>rCp}92Du)Ursk_HcIH~pNyO!H1|cAQ&_K|1EIr0Wsu%px0%iEJ5_nV%4RZUr z_}`uOK_D2ypUjQnE-q1%ox7^3GWg_e+m$4D+w855@TIvR>&EAQLf*-xQUW>|F*8MB z0>8tR{}8qtI~t1E>eu!>k5U)g?|=NrRJTe<3|nQJuRhX9le~JNm*ifE<%!RD+rW<2 z@}LMkW?xxU0I6XE;S~3Ke~FZ^9FIML_c{LLY@iuINp}w+zk#uhl7W}Sea_+I^@;5y zLLaR!bN~|?^?S91KpUa^e3v7_jr-HOMr8xepsIR`)W(D-x znWUXWCuZ54%KU2@)*iZs#3#(mOGYU7w!n|4mwym9*G+BD6G6%Y&QPDmhs3##$?(p* zt@0+(Z=@la#;af#xZ`qA;PH=a4dKlZ5$6iDxCJf}OY%rc*^3bt&E?@_Z+@#^Ug4h% zY5TI<;4CO4%l?Sw2v#YHyQLC)_})@0`1a}28TLI?;-#zYBY_XC$pg)%iKFp%A{tg z1l!({+sJ{_DV}J0^)P-~p=0ae(MTg0U+xpT{)KDz#ugEz!T?H`y~gDObP_J@SvsgW zHFN@1-sl6$pHrl^Tq+-sWSzA|KupmL;6UfGO|vEN4hby$DU|?#ba`mJ$8Cy^PM%`x zVIw)Ie6F&T^e0$y^0L1F$}CN*k=relGVJvH(>+Tsms&JAl}8N(|f<2VBq{ z&2vl=GErf6T3WWK9h0~ytIr^TkSD(m57;(BFnCrWQ~j+JvbVoo8`KYaD>dzMTz@g@ z0(>sr>`bVmC79vuOrn3! z1UEjee~^VEjgdjfUP{0qvJqJ)puEF5rDeO)$AJgVT_rrPLF_yHap$98QRJWKv>zk5 zB>!Yo_%kd{5d+Rwot1j}mx}_-6HQ0?kk9bhjp#V1h1F54pdSZ2I$kgXw-4V+(OI5! zzrHZj>&ftL$=Df0CmEjbeZ4d^^oj#miMKrd$W4Ko#N;PJMJ|!wd!*ys znEoOg8-)Q^V=drYIGrSoFP*~#vM_Nl+HDX}K3sBs(!08|^XCNn z8@RGd3q>5)p}FAzx08qEFAuB`adRQHLhwqQY%&o}jO;TgdD42WzgU{-pZhXUY zHuJG9SvKx>ZG(Nf!41yuh;V65$^N{ywm>n5dVgR4moZAdV{izA3=Q_|}>9T4vgmx7Ks@Pz!$)eLRyzEPP ztoNZ|s@|jC4laSRjST~_1sr%5u{*J0gVyYj!6KC$d@2h5mC^lOA5uelDk-AMLz55} zDBa}eQCHa?^s6XQcYkW7bWiZUR=5b*02~J}=yMT=EGTbFe{m26CA$5p+zGRgP4h?F zPj2${jX+Vu5M=O+y|AM13CVI`{wL|;7@+rGrEi09|FmznZ3pjiL&p{EY`gsU<8z@J z$~ur9fGUpWn{f(19GxHqHlk2>5`(@Z4o&LRL)I=DggQ=suFPJp&3oxwc1mThWo_jk zlC0G5T>VjOATA$`-=DkJCqgMzQJUI>vCcg@ZcqpY1HKo0QD=RdBRbHpTSp=TfO(8u zCqOWTx}L2YgAj-*aKk3lYcJAb>}~-#&UW%NqM@8<;2kvH$LI942^#mBYnyz;FG?p8 z8}y-gk>CYxveglDhj!m_ByY><&qqs4sRSWP&jflDY%YFtFBxCbP7?7@LX2kMK|@3f zw3{ZtoxJ9_z(BHZMHm(B2b3)nc^divULtqq@gJ{cRf!S>W(IQ7Ik$FOy2U3AZ4lK` zd&p6A;}_T?mjusNo5`Cov=28lm*i*}!~`Zh%N^VGD_b{X-7~0V)IhBim7&n6i_c-v zHDgadJ=bFln6JMX5FXV1*~L|7;GrqPMO$Ph;Oehp!Vl{hwE{2=aPW9e?OFJybOwO1 zOeo>9N+X?GhplTDvNsWT`~zk4X4!uHjO(O#IP3b-&&Fqo>BxuTdW$GM_uD&t7Y(uX zK@gFfLGCPobH0zgprTAegr3>g*r>O`8d9{bgcOJUUcUs!`?r~t?Y9m)R}aw#t~(2- zHx2{*cL@!V{LuEVo^#ereKqaTKKF=1gGVH2QT`U8^F;X2Z2a^5p|f&6V6^b#>$$du zL*u9JsWvKz{_C$7I)Pn>8W=A637qB=YkrSJ>+Ud@eGkO@2i&vNDzpdt_3O&RS;Sz1 zocD796jB`q&(KB zxvWe9VZv_n;$7g_rXFjpXRB?fFI)As;@W~!2MwToef^Z*P{mZ$GopMPeV28oOMm}U zl=20$)M2c(`-NqsDDljO^1~j9yIXNMeKv8GOS-A*E4fC(zMFPLgV@#5+Cn+#Ov?Mv zVtD~S03J;K@4*?8)?&Hmd#o$eacQ6x!ifSDgZHO*QF#m)Qi5$rUTIw9ieD`?rJo~m z2HY0TA)1);4&tQ;m2Z$QJ@UWV-8Ke^v&@yPyfvBrG>j~4D{fo*9U5@fgY3Mg^Yy@I z-u+;Q_zJ-3s~bGGr!aDtV}7X$Es*A6{SfKHH4)t3XAjbWtJcsnN8vE~{$Ef5qp*>b zHHqq6>a5ma?T7e*2*((jy6~!LDid9%mub>Rqyv+8VV3N!cubSzQ`eQ#9Vw5?+2CZ2 zbkyNAAg;+?n{)!U#qGnW~u!7 zE@in^6GmQi(x#0Z%3e6PBv3mE2yDC-fyA9sgQ!>kR)&(3QWHM-))fm+JVWz|Ol%Eb zOeA#@vbCZ}>P&6o;7-nkVC-e zmSW_MjIJBB-d?hFa4EqIoVU3{v`{HZ-uQnMb1tE+G53hlL4&cXJ|wI&^2i|aGTkG~ zLUQ$jV9Ky@K|J`~mtu`MoFd{(CuA@{`IIFntiaZInT}rT#NMH|Z*zN;T(y~!Ws@tu zJ9K?iFYyl+yL>t;_hE%B3*8IIbo<48YilB7`#0*9JbOm#o4NZ-fbWyt3t^7wiycEmLY zSg5spqpxe_T@m2b_$&gM2fGb`!W07JV{t1KN1{(&y{t8mknbI*OYLDz_j=V;EgQ;C z`FFcpjmMm53R8|BKQu%=hZM{ATtV`<1}fM=!-mpk7Nt7jJ(LR^dO1j#Mg28(L_Iqg zY-IpBN>FQS%3Qt*kNNSZTYOWqY-4DrSH~t}DGoyn$mOr|&4UIY-`I$9R9@6RBzrcyd~qb$}B%`vSqh35fYGFdd*|DfLhb znKp-nYeh6UEdjQp4Jv=yfEwi`1P>ek$65U`LW=S$IrSpVsd;7nD>7TPb-K&K8+4j$ zYW8jCrS@{PCbC-K!$+9L=NR@MU4N2$iTM$ktjqQ8V>>+&G=c9p?@oc})OK6IT@x}f zXf{HrA^%SOGrHp@q4NH z;-pHzW@&cHk1AqB7lYg+MJ@G4CQESo4E71Rb&Zw;49d!Pq>(0nYBtVsoM5)u_)HNq z`^r-5j68n=3R;Pc>AB(q*8tIaCIX+g?MnWZ9>sjnkoi)cQ3lpxQLf8qyTfQS!7OMR6I$?r^p>}tmuC7oO#Zb>v zZ{YVmdhvjukw!J)NPqVp3Nv%Vm|GnI>M?0Kv7zD`wJ_OG~=^Ph*aphN*4sHxL zY*?en^8dr-U_maT!pu&r4g5|3{h)h(KEre9Z5*2W+;-_A{WCWbeQWCnmzwH$bc~9e z=S>YJI{lGmCn>hN`QOxtLQ`rP94O(Z_^w8l-*r9oS{Ktly)Z2Zs6U<^@Z&~T32w5- z(?31Edp_^ArXmSgTAY1z!=~Wex%=0I4f+DNTKOD_4Y1UI4xgI3BV2;JrGya!?2+U3~$xps;_9(W7p=HE~J zpniY4iET@G(|zrp-OTgC={=o6=t28D6@A9s_os%*39{N{R?lka3ERKh{`e`N&W{D^ zXvz?wew|z{7B0p#{r%fYFGTI|jkPWAh@{tan4E$^&p)Xd^@D+Y!#8%yzv3KbR<oKRL?1r;cNsR<6?yWOfs1*rgUN0*?@gOu$mYv@W?UIRf@SSiv zGUF!8+m4|Tcl=J!6utV0Dh;JiFvV&p#j=zV`Pb$%CQ{o0xkenN%^NyJx|N=AC5zm zU*q=MPmfv*jCt-0j)K-6OgKXcSV9ax=D9X0Qgiv}pH>`<1P51hVBLxr5C)lVLY;eGa^5*cj*O z6Y&3CrAz4*<$3jjKUwMtze(L7bPyZG&2_I3L%`E?q;5yua4L<;qz{m`BxqC!iGhr) zzxcqlw^gPVo0_z#hY06MSX++ftM5%#0xu74?)x2Y5g)#HQvCFg0p5&|3M}jLfpZTpE4+CO>s8*bg?_uh=yPH3 z!FBMTN!TNum{nM9ZsGZfB}q4nyr1qrwcRdtx=J+M4z-XWbdBrjQ~+dOc$giD{uo`)CrqzDK8!JQpo`SSj)6Rein z#l0t(%d_|uh=!PYT$U|iK(i6kSA~|JCavX{XXR_U0roAD-_15#ED<}0t>f;;*lOS175vB4(HQCifKXX zLNLNWqFxmR!t2nA+se{HyNGHoIpSBrV*9#)=H~^ajtizhj4{SZwNLeT(vo>1Rr-n7wvvp z`EWS+sn$ZjzFi0YE(f^{;L<_viq@*RVxjJb|Fmx6e@MmU?`XPhrPaP}IRx>}fs?L& ze_<8H`A%TwruW5|;I|HggO>NwLtJi|wTkSecfo!6B$8k5o+W;Ke?F4xy?A=b7@LCo zUOl)BFG&3B;C-ZSR)8=zSc+I`mv*;+dsHP~Y%`^OB#IT;ZnVBAjNk>rEv)}gHOKm9 zw3vqo=N45s68k)rVrr`;S?80&%6o*1)N<~hS2$0W_>QCmO6p=-gD$|Kg1`8O*SP8` z;@*q>(OPQVPHjZ>JZ^w9H^I=q`QmQWsrv^xK0KHZlx)W0%jQQ#kW=z{Bc&q=6p)9q zremUwt-n0Se|YY*E5s@TgUnYQxI|NE3u=xgG4S%7ES0$>rcr0^K4oGveCRhPy*6B! ze9v#%W=KEw*&5*MB?O0oCc$erPAhjejfZq{`Pc2M*cAzlXq#3|RB02P_}D}dwA`;$ zl2QjSG_ch|yB5VCG5=CFJ(AcaytT#@FX{rs>tDtgj5bg%w(dE!PT;zYlUT+=vfq$F zmvg!minqq(?$KrVAq$~^J!_%oAcQfsIfGaq>4(z01`FW?US=o=U-s<3r{!YO83PNL z5dI31&7866^sBlPO&~so+Ubdz_z={>RWo%aZHf1b4d- zby{AR-z#@L9xd?#MwZ&WpaS3{8qF?SxK*m~4}(|(x9`gMsNM!+fSL(8Vpv<<%Uy<1 znXK4%UCoNS#Zp0jS+zl8l7<6s-xwdVH^QiYJ30kQW9YfzHelnDP1sA|7Xa+K`L-0w z^dPQZ7HWTcP4~qvu%Uf)^;$cz%K)ZtQ4N#EUk*do6J# zD~01b7qF$l?QbERRK)nmjv}a!J}OJOw5|$c?Q*RQCa3fO8@V*j(34Mjf6dKyHJ&p% z`x!GI6fMQyAG*Y$uH^IzV$9~N3UG4P8)WOys-);tJ)bA6+7#__%bnuSpxx}&M=^(z&^)_=REU0Vq>f$9{%wIe#e*JN_F`tyn#yJs%hi>P| z@Q2|2(rjd011Fu2yimZ&+tz*l4xSDU;>!2j)bY5w=dU)G`fmuz?gYE?m2JMdaI?!5SykKYuxWhaI#)?iz-x>w^i(c zlsr;Y$tGoCUFY;5hi+dIWF{| zy1u=d(-TEU^?X%L9IFELF)+WhkYKHUr{~8x z`ls2=P^jgp)t?_qLw%EA;qSS#?RRu|x4|i0Ol=B!ie;DYTfIxnjvGyGgo@a_E6yF$ z5PeM~F%GVT>tzqM+&*~<1za)M)g@i;k`da*@8{s6<=v0g+MLk4T>SVnRSt9=-M0I> zV_75Id=?8^Mhwxzww-_KJ8$w@E3(@4eRIG`uNK|J28-6T@YrGiNu7Sf0y z_@UlU#TQATHl+YX2l;BTn|gdH>?}or(IPazt~Pr|jK4L8@s7RU=?hl6w+WPgnq0H7 zH0;^Jq5nnJ?eaDQrk2br%PqgdqkMUC-rBNI4;ep>O?`VSnpMi={iZ`Z=;d1cJth2| zDo_jX2DMR+{2xtpr4&c8uVJNVnE1OOL2q$CKr|wF(<|NNYhZw zz2f@s1KSc2S{+;ysyR5{4Jtd{r#`nZyX_+={w&qK*zh1zYVq?aaV*QMe+ZFtxJhLK zsNmS{?4l8h)75>8YV%Q`m;IBgwEC2|)mDZQ)voxl?Oe%6My2B->aRnXMBm-CZ_TA0 zWD#5uZxFn8zx$hOsX0EPUkjK93DmQ0d@`eJt%eG;&IGv-q5NCuK*uI*D}LSl29?cT z*DG>>Gs9ral`Y+V>7x5C;-Q~u3Y?IDNtMvU^H8KO?38W`{uE)wYR;Ks$W}hV8;$Vi ztn(tP@Rw;0&v92&Yvp1tpIuja3zPsVJqbfydelzgW}J&ExM0}2Ok{bys1V^YC7^FX z{K>X0yepKNzU424Fm^Zx*I-PVdLWSUuYKmHRYj z;b9kcsVy-b*(1$m?^Kx~8t$Y}A&Kp*lVBHkY=>$;rpRHQ;*oy-cfcAv=VW>;uF7RY`FE6y=&(G-327^VAq;jf9&=GyXIr-5f4 z8$KpK+Y-%$31w}s2l9im-!VkRR|a?qwMrU;#qKVgAFg~ET?aA0#6~d!2Q{TJ+~N@QX0$8Pk|=l7 zCo}K}|88`NI6H=a%}#gL^}FLXwUB37iID~&g9&VnvaUR~{CwA@JGimwPC=k0S*4=S ztzX;D+deACjnQD{FOq+av>76|@R{q~1kEP}crE*}#^ffak3|?!>4u-8`#52an&lXN7uxxG@sZ z$@{do`q_T>>moJjod6R6xX50LD+KsEKUS$Tcc}6@dDSJke z<-J21_FGW2&<0U{6a1wjnEOoZuqyV*NC;&V7trFLLj}}yp(W*_0Hc_xYJIJ`1gpB= zmEC;>6=J6>;A&@*H^y6_axW(Sr@e_)am9kJAB{G8CyODJ@?W(}ep6l;rlnsXjB+B! zVGi}z%br&ih0vPZjXf@k!L^7r>}Vb14bqHDSydym{UIZ2{hl%^~EzR4^h%M*)J@ZYohauZ`)}&_vFG96Gs4PR%V9NX61| z^JEDU{B7yISmp5FFFKG`eOUzX4PPEO){h@-sJ=p#7S~_8xRA>5c8)}zcG@qh-B>NC za$$QL3p}w{&>@K;Nr`2-ZoPN!jrm`&*$?sWw;NWFk?Xvu&L59!Xq#lXv?ir=Cmm8A zO5f?pm0Z4E%{^=p0x8mAR1$n{lcU9!e>Mep`|#HlUkP@KS9D$_(0>^8^$3pogCgRa z^rvYcn(Ks8QevY_5?VK5Pu`c6zwz=iL8>*==t>}M-y$a$%@9`B_&}Cde8T%hlQ^^D z%VcgZ+fTOB$1mG5aIs#84*P{T%aO@CuV8XDZUM=fhxySr^SeqxILWD|n+Jex#>f;8 zFu@o3LR@vXU_p}f6#=5#WSZ&nq)ewddRzTRz%sSZ!^*;5IX;zaLHYgTs1y{NC;{Z z5XB0E?M*1FH#@A}y~wrXd-IP~NbZM4mP3KYCg4F~>OE|)>b^@_DAO|^*&}kediCl& zcsDl|cAft>)aYNW6n@VhENnMKP$rzxe}?UA5~<^^w&l%#^@ zlixgd)uuwMi&#Loz-w3NiNif}s}caEXLAl6g;uZNNp30vcXRE%EA&N~aNB-Y<)QqT zpVi#4*ehRk^Q!(uR-$#oH|udhW5}s8EP6+n&H)aEG57pVXHZB#AU2SOC$M^6VeaW` z+Zcc+@+Y3uA8(pbfxse-sqv+zOZq{m-H6}IPfv~Pd=zAwY>DeH9Zaaso&-CWvA#l` zMRCN@K7{U*H+t!_`9#m|LP1X~*Dud07DU}DI%9Ow*+@?< znEft_bQk&FTzEUxVD)2gNltW(M*VF6IPCDR$^>B0+ptGBq?n6Hf=^p?Y##Q#uN$it z@jn`M_FmzUxAIM#DWUlV&2$?vFw)J(U+_qf`-UUIU~?>%XEAbWjSov!6pK|vswn$a z#=t%vnT~jB4hm3%vl*B*i^mWQ+Xxx+Si&pc?8!n0mM=SU9)&60RgYH5Ab0n9cYfD) zZRRQxMpK)wJdk75`i6e+QS!1&pIl3gxaGpRU!R%0@EJq%HE94opM?T$5!k1 zU*|YhEx|QRr8kS~jxvC*YtmgQrFc{8J*J4-&1m7Nk)pM0>Pz zdn31JW8ad3el_1o-L9L!8aO0L{?T2Wvtoe@(VY2g2i#d-L3A84Ky+fiD|AV!H_p$) zEP-=c=Z@R?HR%qbgyF*f2E?A_TzM&1ai-`VK!^UEE>PPdKyT>W{tkG23K9LW4G={!r-{a(z+CL#{2)B1NZ`v+Nhze z)XAk?s^ZVuul<#cHud~&#N2GlR`o{<1n-9|*b5xgm(&-T@IN~GU{&edzE*R4s~um%fPrcbCkEM%1-FW%bK3A#EBzZ zN!{(2dbC-&+L3Y*6O1+; z%Jl`r4h+oF=Nr-9V*xGu1|}0=_)b7eMPLHNb#uAZ5JfJStvUjlK8FD}->Z!y`Z~ZsHvvFT@ za$SRAkzd|T27&z3Bl$Pu#)Zn8PL{hmz`I@C8gx62`+E45ITyu|)R%kGg+UmwzR%n z{Bn2jq!NML9bJ>%ABDqZh5wB6@_1KC$C)VAD4b=Y?ndVAL@l<;=V(M&$+H`}-Qo z{$c5$pE_XTy#E!SZFrj@|d$p)4v_wvsk2UyBK&~>(T z3=)00U}OqGmN;tNUS)aQiyi-V_Fk0f9n7L5NYF+=cYC?OFn(%o`aHWjT?}T)dKtF{ zQ?qgGFs2!CbJ6zegw_*y(-+0*3|-3JSbO?G5uDDCOV#~rk`8ozBOaXNAE)Lnn5{12 zbss73{^DBg6AUAfUkUnVM1c$3-VXxI3_-4vm2ZD{`$k@exaj}<=rd7_bn&saMj`#< zcg%XWGuTZ+5~iTD_U1o7g#K0kRi7Njy_eDSBTgfF)LAj@{Ue$EfV;A{%#(#j;YdZq zITbViby||G$e6D3y&?M?HC;pojSQ7g*bD>J`$8N+Ohu6Aqlu7J|KJA?=uS%)I=1#Q z@JE?N=I9d@hYVuSiu1g;~A^2iw+&nF_Z<=USi1UZdN8zH8i)<1gF$F~9N9SeOyp zf(gkI5{a#_7+Aa(3r5!8#^<=O+IwB*de%zCx==5li3$wkWf&ev=WYPA-XcwwM0s9T z1q;-dD4lsikDD;s+omuVcI!!L{@-Ml=Pt%)3kqD4t$bPWo7PgPz|f(xGxsD^>%dNy zEtL4rUaY8dwc8j56T3vihyH9q)g;rUVEMB+UbJi zYj*j!F&8nUg3lp;YN9BG+1yv&qf&gvrR+n8;pJx@Z zrUWz~7`5BBh7s%!a8d&^tcrKdPGkhyWBnv_6FVpSuct8`QHVs|u?LB)0IJavR zN1dO(QsGOtf8vMe&KvI;LhuTKFjfr-{2UZ_d}ucEvBXq|^b&-5?9 zA-@TS#kA9%i&*QP7|Fy5N52X?7oC5DB34`gDF*iqo7lS1iauSpLs6Ywsg`#g3Zr1{ z$-vp_4dRbP+hrM~d6+W6F5LufY_9=v*r3Q$jpvtc&*SKgAm18HlI}acpwC=ub4aHW zcx4gkm#O%d7%4_^Un*a2dOs*V`AdW0>fA-wVjm{^^`D}QjzBLr?P0@NrcUXQASuu> zx=~Rxm;96fKhZva6TP}{ssz1*4>YTVzZG$wH07!zW{BfPp%xW` zj0}&Dn?Vx#O8?roYMkY&?!?oO*rvYGhve8zDu$l)^Il``n1-kUVsPCR^Dx@5qK87`OY_Go)n#u0d;XJS4a5cyznGRr{%q zy+<*FFflP%-vFik_wHuo)0Vn8?5WWCA%*8u-UrHmMiqq7s?nw(2D%6DlH`l6QhUYd zg{35B0e@Krz0B=pB@dA5!zOb23zm7;j0L}Um^CVuZ3;?1FVH>8}=*; zh5wp@nB!BpS%$6ie$n{aYcK6D7;qPHH$Hp%7mnN?`9_`CY0{E6R(~#Sx|eObg+rHU zsA5HnO}->$HPj!|$oL%B5gX3^SZkOo#o4?2M=bn)fheIZ10_>5)E)9zCI`s^z>Iw<_|r7?>er4r&@?m%ex>p zQ;;}ur*r8V@r|JLEY+UJkt*yF@q&M6UT1~n^@2_Tm+_`1P=pON9ES^^y<2RJnulFw zr&@_Iw2PtJjh51_OVtfyg*Ok|`T0fOC8IRUXm#ydNL%Jvd9dQL7$$9b&iZqdt9T=i zZ?AWF@btL3%It8eZ-l zQJV!U5dj2B(cRj>$1~o5Zhit?YC`F}Bdv7OkQWj&J84rS$Q%%)n`R#YmU2Ae{s$9(el^We{=UiLc3m}Pv? zNEifde4evqAfwP8Q)J!wEOHaWi}5X<(zv8A2LXOwxe5udWLP-MS>Ib1IxJP+$0+gz zgN@fy{op)RYCm{cZQh69k0Sb-wo}Ry76Om`dUdeM^usj^vM6m_XKnxW{I$Tbt@E#6 z;Om@VB0nl2VsuY#D38)pX7<3yM>fTK0Ox3GWs1T#*H6w?8NpkEo91Z7waBahN!10&QJ@mrHV!W~zXtT*9dJx{nj3-*bfq z6YsI`F&0MfBprtItIUzje{b)Lfac3K&zBCNJ1>bV<6Evy(*iPMxF6BV69lRTjzO#7 zlRyQ;Bw~5gpN;DAonhgB#VTIIC59J2zFA)zOrvEa*zUz?kJ*b+XB@dC;3%M6W_N4z(xyOwexVb zU-mDtf@yC}D2YBhr-FvqBi3z1ik0?w#~NPai@zPm$UNTf>&l0=DO4Qk|nc-MXjd2KM2_a zkm7?^O5l1ce{RS7F^<4IfZfC{7WjV-ar@ZVv8{~UX&WccpikW%$rWO}B{%l6H!KBP zMdxw_T8sbm5kKdfMJxm&Xfk0RM@4jP;Bb(b&`Vkt#+7a!S=S+q#kRf+dMS9_X)sY? zi04=8oV)M{^+GMWRb%;j86xE36QU`?fa0o}BjJwsG11Rn2DFFD`VsHwGD`s!z%?Kg z_(eaE#O1p4E6!z!%5BzN#=S<)#L|(IMd>}NHt@vDN4Fle;)%v{jiB0aI~}_c(!g)? z-JV&Egzc<>wW> zyWIE|$rmDgPZR_{9D%d!UG&6X`ZYbe}@|0qEv4pjakykV!$n zLH*I8L!bVbJgOt&SAAZO-+o*Z$87Le3ci~N4^%hLuQSHL+MQ8y(CcDhzf|tCZ%>-& zz$(pJ)@y2Jyx<8Hm!GQq^K+B%c(PeKA(?{NDoac#)BhxlZ z0(PzLJ#4Rj1Tf+AMqo)G)ueddy>1X!WdlZTsRlh{KkTupBvs?hHaf^{NnTW@n4nmz zr{d1;eOs1=kU_diYSh&ts^Q(4{_&<*F_CxX>7CHZd&v!{n9B6~SM1PccKO{GNJXKZ zZ38AU$#QSy33OV%YQQ({h_$T1Tu3D%AtRVii=vGr^L-XwZ#kdI>VnlrJHswqbly#; zlmoid(tk-qA;3^kZki+-&sZsck$|lm=Zn0x1CxhSC&ENz@`b49!S1KqC^@rjy76De zcv@Ygej2l$>+zf2B87~Vii8&XaijPTBG8+1`@O{xC$9Qa(R{$j#E;kYlBODx>Ckj! zRPKmtKt{PO=e4ET$-9QqqnH|lM_E58CM#fe`Dnto5X^3VLZ|gep_jsaBcsXjD$7gx zQ8CewqK($%hNpF(Q*9*POeb2(S-B}QWKr&q`;yoHhiDHuJX%<>%>*V?0mQ4D` zvJyDFda2q>mmfo}oF2C{l#u(Fd~SO9Yq|~jFqfa0>Wk=sxtSRy9~)ICcbbLa&KJJGCf^|RU&cY$1xQ&ry{30>iD1{j#IE(| zOjd1?+sSJsc`?%e$J19hMA1fjgNUG{ARP;$bVws9Afb}dA>EBMERA#{-5}lFCEeZ4 z0!zb^3%ju2`rdoLKVasGnR(`%^NSO|2DFy@w1C;Or87Dr-t)|xkeKU|jF1ronnXg$ zif+2;!oiS#DY?uuTP!4@fDcs9+3IHv+_U#U!GGoxLo>ye`mIQ?#GTi@9I_1kV+Z;u zM!X3SW6_n0v?9ilpEG3XOesF>2|SA&!pZ2EZz5$xUvEE*THn|AQQ_Z-^EsGLh7ZrX zp!n8?7^5By@(LJyQQZs6d=XnQFa2wHwn%g2CGHTHGAd22Fzph0%P z;VvaHRVOkl=J8};uud+}-nB>d~ewZ%!EsD=9rL!D%ruMkU4}Jjr<+N3{ z*k`(%S(U1tPukNenq7UWH^$*w;j)u0OAl2|jKF~G2Dig|&xsO3)}UL9{p|K)5hsHA z``l4j1cR(}UgWZ*jgbz5A1^r2r@KShP;k25i#`If>kI{aL2rGqPeLQIpyDXtlU@kz z>F~JkZ%gJ>%()v^J2uzD5bVQJg;A?oP2RDZS4;H4W&jKs&b8 z1cH?iHWwnC?GkfeJ-pgacT%B>Q=R8UZHH~0T@;hJclu)D`@x~@*o7OyLl`*XH1A`bo~t%M=vwrZN9JT^5tKB z0q5u&7Q2Pvl+?YI)%J%$uk;BF3$fstZh1T2g~2K3sX8`;`38~I7hI)L;S6rD z&XFavUmfyRvSbbZO;PzD9d1~yuNX8nqkHRH7kX*4+Cs;0Je}}DUNy0hzhG@jPSGAJyUBc(|8ONlusyM zK8veb!nvlR?0`A|A>(RDj*QSw9 z4~3lkc8|1_`(WX0yEBugx84^w z@=jjxUiTWdm8aPqX$JlGtCux7{7ez!*T>X{dv;!Lsw#$u3D*$2ZcB{RKbSX`m+@OR?vl9!EM>wUy9JJ-%=zwewQU$S$%T@go>xQ*(7d5l;;uV z430xrdWY^^4*PV|{H?5-9$r zJ$^1BPJt;PdJ%8u3R!enm*1Xs4SC>f@&yY8Euf#7vd-WEbo5Y{E>URSWS!ES%sEt{ z=4kv&$Ox&Q%H(3QZ&D$(nn19q^sx%?DD|%r@c2Dm>cGoXUEoxGjwKMjZ1+$%-V7aF zl;uP0`-bbE!K&F=S#vsYVITSw`a@i|*~YsL)G3GnE359%M!TQ(!1ovBib?_TQJyAJ zwYYMc`gsjM2E=hXE&^URWa&Z)*0pTf$(qRZjIV3@Z53n&Ggox#=38F6u`nGr-Gn|d zRT39n#D?CzLxQ*^?tn&4@IaDL^q@nDf#Hetf>pvSo@>8XU9|tL$(*Be-+GaPCMl0U^6=Ss`hckhS&F+<;j@86Om*G@3vhJt;!U2x8{Gb z@#vqkZeriThS|8vjrFVw`54qTnrYt>aTF5gCGqMq^3YV$l|_ufd|N3psj@e~$3qaA zFvf}_V!p%EiN<2#{Grnrd;TBS4eKWl*+hd6!T2ofxl4y3mh~xBI_*~?0J*bP#VkClib1;SAcJ7!Oa? zo+l4}Irx@h#`=ftq*Yz6W$?cIxsQw+^t`i(!d~H{N60;?rB;XBl@l-&mIb=6A>!$+n zk%>?AE{NlKPM{cdc6TS8UgrIfS6fuu#-i;7^`(qhd}9E|H0qk|qv@ZMjF@e^(^K^g zpErMv-JBEf5O(~Xa3XK262hCDJiV*Z*NHonCRxwp3fO5WPFUr2+u5PDXBXpLfV-Zq zZ%7d6W7hEdY{IHjYL4mpU-Aoj198~Grr3v_Zp?`4DZ`6HP6;;k}d0FgZz4_^Gv+M z?#0INhKR6ZDTuRC_e;S!h5GAO53RL|@+f4WJ1*k}yg1HxgBa4!i+r>cJIz~!;WomI zC<5e@l@3m>YrJ;ecj*6knCWt{#$oxtqK`0A^Va^WL^sb*J2Ex->lbCy0GAPbgXFv` zW2P^UE8^Xc@dPg(*w_p`lv&+56>kQ!(S~IA0JQxHe@PP&dT)f}gEL55WMbvRAs#VB z&UF!@9qGadiZ;n?1mSwVJ|cNNAJJ>wDw+MjwtgKss-J=PFnNAX98diQMQ^_`D6UsQoSh zUJu|K6VdT^eB(Wmdf?dmX9w$xa27j%?cV>taW3Hb7Xq9=?Z7Xo!JZTMc?r%x`UZwm zS8`WPRA0my7fG1zU1|<>zQWrO=%gNMh_Kd-GS!0rHzkefm(yY2385x( zT6$n)phPEYx~*I4eR-jzFceUXGCb@-_{Kt{&gj#84Os#pT-H(t$3r5)`6UC#lv%^; z^!C7bpCi)~S~fl7AoqqXct+b}KAo6)a{ADf<>V=!*rV?mHKJ7PkQ!FX?-r5`Nrc=E zG;T7)CFj>T>2`+$a8{Q{S4`v;L@%(oCVw>maw-Pu!%&^09`-|-N*7#9`^r$jK8E}e z#+I3J90=b9_)@hb8K~KA0xkDjtT!AC!Pu-1<k53mO0e zk^?SeI)L5gaoXoA66%?Z8l>Y#yvG;!G3Hn8INimZs_mA2FoR73j_E6kQKhR1alXCp}(Zz-KB~LO(eWXH_=H=0NMo_^nDA4!vjA-k7$lPS!i;YEMVZfVj z3Z7hVX%`1w`o7w?-5RZa*j$yY+R&mdJ{7@Lz%Be}V47qyx-oLfFla`4CP!ds49DXw4W~omibTWw8MH4G(n(`8J z+Ma=R%)4od0Z&R0WUl+Y5_H#QW&4c2LjwzUg>xucgLB7p7BF^=GVd8WGsx9*dfy9B z!DQ#(u`-1%%U75S*vBX%SDzNr-2>L8N6ZAa*jP(Ozhdf$f||@EhWje47_#rZ*FpM^ zNp@ETm1Py*k0*6m4Eh%vlw7)hI2r6KEZw)~NZ5qhDq;S1aURd(H%zM9Iw>5)!J38WhBE zAOLO411g^D8;TsiU1Lo2ZPkGm-0sOK3t}jt0hB=ph1xlPt=<5eC>m|h);j%=84k;rSAmQ zxuYKVb#VR^#3qPd+GM#$d$YYDWis$Ki~VDy>oY@Cqs6!WjV^U)@Zi{ICUt%H%C}Gm z9uJ7TlLMvW<$hQTenb*g`g2q51nL`PI|uT&UI;mpM%bj%`_@Qnl^u<@+bmOH3y3oI zED{T}UexCG#@UNcsG^&e zfgEwt#~$0OAFHq`uuSmVa<4w9t{Ez9bFJ|0!4-Vc+GqKIa#R{W*#Fsc_=Cmn# z)C3A-yMizFmO&}#36_F&QJ{&?)g0est0Zkj;8Xv(IOq}0k4D!hj~3my*m!u zdn<2T_+`Ic>f*bJ`_`#D06?Fu zO=7O~_)6?F)v+sB@mNb+s+Q!BU-WEeLch<%?DOq+ES*eE@Okd#UEI4ytbgzGy2rmb z>5w(rvIg|_0+CP2h+IM%xFN+qX*Pq?5eD_SN|c+&hto0 z-rjp^@iQN{*6czPS8_d)!s--Op?NB1ohWfTh}Xq&MzVx$reEZK&ZlMHf9 z+9H(M5BuzXLPjrV5xLoe>VcN3ol7kn-oWR12DOp{5;nx8a>Ei8k-->0q^ojXP$!4dK50kyf%(e|pC zQ+*A-{ufEAz&g=2()LDiz|SLMQ_4*T@Bih-S!?Z<(n&tw2m_Hs85Bhhx)q`#zOE|9MU4k)TWVy}-eeZz0__sfV;v2Cc7 zZ*lfxz`M4W!+KkN#d>DK>23oIuIJw7M~yd$`%|l{4yV7pbodL|YMXr!I#82B;7{Jt z6Sk@ib%7DrAl3#;?ywRr@vtv;!vz#G{u{eQ_WI#lfp=He{?VP@g;me!^<9;cPSg>l zU6e(>ebw~^(&H$OW9#pOGmK>>Kbq^$l0Wmt9`|wEN@yMDcxZrbQ8dA_@L=fbK=vA; z@|9vJ7fUn%nH{XY5ME%mR!7G~W#eG@s!WRWoef%U4VYC z?PCIVO?}t(SH(4!USfcm#E|2~r7zXL)Sz(Sx#(JKU8IXET7`9+%q;c&1>TGYZvs;iy;?DYrFBug`UShy0VPe5qah@+*v>2{8SkwQ6Yfok92!8 zHa8lOT~P=ONnyDoe<=c5>E;cvMrYw$G^x}(`fD`ts^O~?i)E2Ie|s@q^9!29@1cib zU}s%W^guBZdi!OY*GjW97iD*ivQQ(KgN0ypcE z8`DqhlBqBAsXp<9L8ZQ|c3`&~4Cz5vb~Q)=;cZq8sOugSg7%W5qH(dBJg@MXE|pt* zRF`2yX*(C6Ux{pgv1{OpLY0kIImHQa9cC| z7@gxOA@QfYU>K%EDBCgTU4qH&u(c6WhHBaV$qCpwA^8brHh4?WdLDe%mWwUiBXqYJ z%R3r_zL$f`&PWtRAXRaY&tg(cc|fO=^o`NAx?@@oJy!G)M`-q&9AzQ&O}BZ^76fla*;YB@(5QPGe^orHJ0UGMTz!%F??Y)Kk7A zY*G`scH3NciP>QGwGgoDrNwzDtvK=nSUyR5jm4M<@Pp$x4jUra@B6cj3iaSk+A7jV zg|F+S&%iWG8N!bd4}uaD*-t0#w+N$#9>L|&`XmUcBE_)%Q+ZI$z8kUrd@Aq)+#~{% zF_|abKNlp;vqu*Gk@*jm!=j=ag9%v@(O3C~8p2?Ehe;qO=G*tL3Yq5%39v;5f47^_ zWSJJef-8lr9rKf6KWEdI$J9>v@-soaCD&3`w;zo~W@HFyeNi*9m{>0n=_yu?dB`Q}s?oe1qVM|zN& zA9P_PvTB-^h1PY8EWw45YgDWb50xIfr*E59O!Q*Mo#+$4Z6apd@N8G}Vo&3|cw~`J zsUKk*oUYsV6AYk#M-NHfJ8Q?OInwnbQjA%>zqJPpsd+7|vedJ!BI%@JQ127RiYZ=W zC{1yN;)2i*!>=8tWv7X4SYfJ|OvK`P*i>#X4g~IWtcz~#^vUSl>$6>Mo25>0$_=kR z9?FhsY2dtueh>|F>Hv?O=!>4R!0>$aO!B5k7k?19{+ewD1m5{uYvW=QsIousrl4sW zI`k`*@(m~bQh)b$nNRaEwR;ur(Dvw%yyT}#e^eH`)URj^FC+(5Xg$Y}Ow)n?Ul>ZF zc#sT6eRmECsX8%BKYby1%M`Z8G@~vFs$I9a9`Ck;EAD6wo&?Rpo~|~!yqQm9hXeDk z(KMjlpC{drK1I*ANOH$1^hw0}fh^iOR`siJs^na3T-SZ%m^H{Tr6un^T{s9kwIT}b zTqj+AxGiE_CN(z=7X7u4n*2~-(FS{{gdK<&(p8MA?;eXO*8pTMKNZVH;E33syiL08 z6uqQwUE;QM-TH_z8dL}rz#mR2X`&98u^APUr@%J)8A&Gg`j^h}$3|jb!E)(vpE40U z8>fd1IBvT&PPxEO8*E7ju+{xnG(fc4rDW$$gyxAa<{tmT*?v@&2CMg8P+#St8Rag` zck42S-NfTD$aj!;^1eCh1!q_r`(+a4&n4s+@6TMABILTx|5j?I>yHx{SO~d9Kzr?n zQ_@A=*+_acEcJwVctztjhajt@ZIvC!fqaL!Z8n=jv0RfDbxOw8(TWJwA6F)Hp}hQy=Kq-SY#}lZcwjUhyR54Fd(m!p zU>KPs+E|56`-m##$5Dex_e8CrE74`g-#27zE2_d9v9-5P z)6d-$?0oCk{i3VB6xzZ*)O;ER9K(MkxWE%H8gr4J2X9D`%_pUWG2I_XOl;Hr9<}wZ z)ED7%n%ClrENBDSU*!~cFs$77YtBWw=A#t6)SG&^6G(gFU8&J4IC+L1!|Rk1OWe6D zpC;%X%^R$JnW#CN3I)60PkB(V6O;*qkf7fp`p~Bdo>BE5SGVhMjr9YyY|A*R?dv}7 z)?rf(#}AZq5lYWG?mmEQI||?qRM|1kBwj2ZdP7wPC=UdIe6mxdcddB zVzC6j-@ke$YlRW~@^oj|^9ZAxD_@^52{pKa*hlVf3>+hhMd{*ooZ`XI2)Sw@Q!FR? zc1iY{JV*OmAH^-(9gfu&lHiD4W~6>pY2#6qvgh;v%&krxL?%kGG+aZI`7s?2r`dbrHE)?ol2awHc zLVGUgo0SEP197s!sNR7D0gcZm6J#Gj$Y6_OWRvHb42f^#np!`~eLKy(Ycy__ZM}@7 zz}>r7qFIa{N6X$gZQF{#6y^J)yI7ulP)cNi#&nT`58LV~;c}Q2Pi%vh?5EcSNBfyANliorfvq3!`j(pP>1?{aEHB zi1dFP*%3Trwbi~JHhCDbUEfAZfS0gF)+}CC)VNUx7b9V;zrww0mu>S+KYEd-*gnA1 z>^>&ax3tp;%PGx%_`+?=%L#JV_5HJ1B z)3yi1A+GOmj9dC7?A5%I%d-e9c7}81b{p9Ofp@j#TKo^On%ZEffzr>5LlEFp0lID< z{iS_ObRGNeyXGE0vQkrWz=jO@g6>g#RnF3#GT)X6=Ya{wLqhp@jEtqc9rXeJs!^}>nH=3{0TgTnJHJa{+ z^~qTmWG!fj`MiDvkWzPH@_Lx0a_U||Fc7j%U_;EB9Rg;dD{PHn61Cg~Zpx!{JAqxi zQ>v(sydv;U=yncw7D*S4J2iUjGApmUa|Kal)kjP`XR@fGr#{tFV!BRZ(#3X1U6|Qj z`;RwZQ%uL#ouLbIl{kL~-eVr4e(H8g5!NyW{jFMfyNhB_PcCz+d*W(n8Nv6Ah@ zFlR&g!{!bT!25Z1EX0_x8v1GLnu){>gkc|au{lRmG_E;HLE_sYS`0=DqEw^s|4v6h zLg-IgX+Blw$|NLu{L4|L5(?~VnN+J#t&3H**ZowL+=*vcRpp53ACcT>o*r`uLmZ)X zB7^|CLWPbvAA$3Du7e$nK7sE!S>{qbFL`|1K8fx5`BHn6czfYyBDQ9|O zvUmmwq!o#q!utc^7rQ=QzwUI4;Ly?}5OBReS?C0D=^_oQ?1e^Y9_ThUuhu&l&)b(u z1~LR&0Q*fJ_r|BIx9oMFY`?rpg-(j1%tsEqk{RO^X{uQld9zE;KTwAl$4IN%cT9d< z&7(|MUpUtsB?KHNCt%}|O3(JLKk*~*Rr^bQXy+~qi5`CYW=6zFhc+b7#+^f;N^G~w zCs;^ON)BVhW{J%Ay3!6*y66hihq0}{6nh%8+brv%w_|%G?*ILrAkkNpWh3e)9#e6F(1o{*1OUYBnq1 znkK&NQm5a3Bk9>O9lqmnS``LLk3hBTx8Z_nf_@a9f3+ke{l(UqbO9awI`%~);cAqutRl@jVQrn%8nEIxZJ)h<+I8?h5OY7!Kubmytb`Iv7mh|YQPrt5O=w` zZr{?OgmJ9PWHo{7S=F!|~NHqt#Lg<4wx5pn)@6@!#RlC6}jLoSlRx}f#c=$_H@80kT(zaZ&gF>}6h$^`| z(i*|ti|qCkv^|C4E54W{m#L=2LzVJ~R)NUs~NsFu-N6b!}vD@tVs7cQ+wKeO{8!n6qcxUO`=7)e(AqoQt zC+^!~S-&V-A2$16e$XeVu##M>`M9BWVFB)1b&qu$BXV0ye9>tU+Y5aVJ1&gruRHEe zoc$ClT1>*a(tU^~$&*7&dX#?_)JDz3Ih{h!AVohPFcy?~|4gk)s_{lZ+2_D*M@v=| z=4OZViXZ|dmGGqM)=(U+PH?%G!xWn-eqTK4s5X64a~d2D^5hHwulN_yeDL`Fdx8`+;VqiK!8Wt1xKgPJ~RK$J%C;M<(F;*YmPDj=lcD} z&1FjrSXTZal+w3=cT^}KxAT-WP-e#0c`zN%b=KYV8qCN~nbcFuuo2r*Vfd1@(ciCS&FKJ&Gz*kRO8CTcSTk% zG12d6Zna(76#idg>$Xzez&T-!HLsB!5Uj9}e1n%;x#uBfI4{n|E8z>DOYW1OpNMC( z7~T1x=qW$J;~a^lRfC1_oB;r;sU7wICa`W|m`V8059iTL|3X?>nIhTqAWH;emSLY) zLEi=mmt*F;JZZM7-;S*DlVNx%^{+AdPiy=Dth2`ZOuS1W1js$&53A=j&tlXXeU^3hYV#DnGQ%MYczBP(PUrg)B|22h$ zsZ}i15U+R+nGcWANlzR0YX|myL-e39Jg`+^_vxU_i#B6$e(*yU_+NjHHvb$SV_pY) zx>#*n<}EL{h^Y|GYPr1)e}QgGb$;z zHb+QWAPvfNqFbq2&-i!LQ0%0MleneYt=>}Yt?u}-_LP4fli&)SGNu$1@xA50`927i zw;&&v-gGs>3U-OLj2C2Zu+!v==JCC^5h-at4%COCxZ!p)NOV3t2jKQ!Y{A;bn_CDS zTceMRPjqE&x~=hA$(Us0TYjPKE%e*J&d9uLb$@$lFRJirlOb|a_)1QMpxU_C@bY-9 z0$J{y2aems)D-*&=}O)R}E!>JT7#kIQ71)q$U3q74xoD3J{o!NHWL~|KaXp z^hMC=#U?d!;qUZ3htQ+W=}&xn-CXN1>}G4^l&s1?e?QjX>tOezQ{!oKJtdJ44u_UL zC4?<}4JAAl=&bk`;IZBq*U_F z!m$Cdtq3I^3M0F3e7%E3YPcgrl7u2$*;RsWcUqNI(*pe5`Ibw4uS+Mrt--B)FH9eA zXNR2dT*ZuS{*SsWgY2g2T4!xVvUebB$p%fUqaASE;X|~Gs4i>}LIv!Ai(Poj74qgr z;~_Htw7o7B5E#2us%K@qoYL&>_Cdv{G>y;al z?H^a0KhBH`zWgwGGcxj?oc%_EvGp$@fCs#w5e(xO*pEOent;2r%rHk;dvMFxECu*1t0nUEPA6A#8CUWX5sz zNwU!eu9e=smNF=*>%U0zQ;Lo*9%@&JkDwy)?R<gC->nt>_D)N)sd&Hd)3;2Pm9WajpEQ;phe;_0Kc z&3IE~mq~u-;{FU{@yW5hs_6pF@3>Dn9jP-#_O9GM7jpR3$B2C#!>M~9h`CkL>9PGq z9J3^d{pYN^*6D>y7wc{952U_obXQ{t$USJ5A6}Uy9>>}!aM-WPC;1s?bR*RaG92Qoakrg>bh|LU zx9H52tlnEiw%k1EHKeXal6R0C6zTqo|L{SkNb(YiieN2ag}2Kwy9KTsBn1Q$fdQm{ zRX!i(@CJwT*Ze?R{p9F8P63(-b~`@B04~Z~`$Gd{o!Q#@XaZi=8|x79C&U^K<& zzL4IFH1+=nI{s7RA5Z*SP}fXkFG%kyy=I{#Si3yQ)c+Dn2fB9zc@`io>vOp;I}ycS zGri}SkC!~hpJU7^NrahW)KLu@euoNqEP<6s zbHv?1)h~2Ko!7I&e<+D*lE;t1{= z&4caepnUGGbOg<%|QdW>wbjDXV z_ic+ZKd^^yjsI}F+Pvl^%p*6i0y`PDU=g1`#;kD5CvnVFP)!%#f6-U}t7oaPz+n=Q zCZDLZb`v91U@KaV;ZTW(>sETl+<8ihdlh2$AB>IZ+w^qeMpl2C+vPJ)2(VMDbba@M z|3_j`_bYj4L(WC;i!{ojJeNxy;uz2%UvmhAdc!eMf>+-$jleKI_;%@n5)-ZNi#qj4 zTSZMF5gz$Ygni5J67!!+M`ZGz)D0Tk1_m(5y zOQCt0{>43=iRy=9Xe*o??IFb8^GErYv-05~31cihT>XxCd*(-GoCJD75aHzc0E2!Z zxp4^6ziUwEzYi>vyEZb#-WR=F!qp};z>%VnH5Tda1)=o?mEU{Mn6d;VR{b2nmF@1t z3=5a^*QuE`))hVrKUV;>>U2_p6rQ#@Da$~TBy0DyDyL-po3#s7d@P-jF=GnGiDJ;` z+>sS};Q=nh1ODQ?Z|u%cYmT&0CGjT`u~-47^Dd#RmOgz~roBoA8M4F zK5j^60S}sp%wHtH(p1Lh?^U%E8*m0qJpKjvaJAdg7-s87B++9OI>bsnXE6aOdf3x% zu;!Hx)`7nz_nvlqJ>_`o9csTKU@xf}Db$&kU`zVeo^mKSixvuvYcpc+@xjR{GKcAo zrBnaW615++)j)N$_n=X; z$D{q=>^*eOdz8pC%qPousw5B^HwrQzfFaVFvM|0qsr*@%3?u*2GL8Q`b=N<5Oke^v z11oC?uJcAHKE^z9h z6`1tK<)SqM@a#<~uT%}=+hJu!h#x1P{k@`h`P=&SDY2_ezPWNVpYlsL90>YZN@Tje zF^Zc_QWej|HYu{n2foKfTPBr{YN?id=rYa%LCvn8&~bDdIL^-anIxhD%^@HdQ|K52 z_Z6AwZGB*) zv$saetrH#=_C&r1vn|h9M1QYZcD_FEh-tZ(sf%mEEqb|xEnlJaBl>rn9h}f)4`057 z%LCSO#D=pw6P)GxUc77>S&!!fwN3XSOm_A2sdkVDXR%mDRjs+`%}9czW+=I3vF2Qa zG0hRKHA=y|7J5l;q^?E(0=jJ;oSZL4K#xW%GWAEuZYTr zkBM+NU&lS2+`7HnxOExV07gu`mP5+dHp_;s&@xiAV88-7Neq=T|BIYwb&;jxxo8Z> zV4B(tOUY-Uzsi#%u0N1FyIT1xy{^p?-(A`a6|!e1?{$ykw2gx^ry(uln)LqJjMJ8j zRQ*TpLSompc-aYUh|+Y?hq!VfkA9??qOy*2P<|}VH?;JRd;i0t2|W zQvk9|So?x(UVW1!d|(s3+tx;o+dW7STh`gU3j$T^PQaW1I=kPm@n{@p@nG-_nA z-AHrhWU+r}oZIiKW~j$X!d+8VFc3c?+@$6uj>vX}E70@a)^{KrRRu(88J zbjO(U5LCJI2L*?Opbfe8<6?96P-&c6Hn8u1mNt^+sR@i zSc9Oy6=lB;FE`@6cEIiq|K`!LJ`BXXc=>2_3jGZ&s5>zYC=dEO9uP}L(T4wK%!rS- zETYR}FuN?2^S+s)p@c{*Evz<&&(7V8-|JxF!m6Zo>*tf;z?8e)h1vwT?8SMo_xZaeI$ZH@M;%F{98;)}Q@ z@;&)f=UiT`*|I=dc&k21me_E6Oh?%6g3#%<>rcFD$|hXikaz zj8hTU(Al=Lyh-dPs2$`L&MQYoS#~SWB?3I`Qr#``k@CYyM-lhteO{`j5*J)~Lyc}E zN_;*tq)fa3!5{42Kh$w3m^+Ue}X~%`3LLNAJ@X!)DHyVo|*WViPQ5{ zSRe33NA9V@T2}JI&*#mM%(sq}I_gE5N-63&K@ATjM*HI;sJ0h#*Ml5*t!Z~~6@@{6 z<#Qc>pC``_)k^3+q|FmOhrGBsFY3p?b~{^|!=^@Wn6ZAh%8|_^hVI4pmM^~SKYY|U zv^C#NzE9|BbjrQ*lT9yFYcWN3P(lXA$Uy(!D~{w#iA}hqh+RS2^rt^W$u(J{&(PV zycNnr#(@>8|3W&lcf=j9{7}#@sGdB3L0_lM?sOtxpr(WOvsOh?&lGBOa8esr$n$y^ z-Zk77DIY`HeA^Hws?{3N$Q}e0n#eo+&)GBzpF`&Csqk1mZ`BM}d4Rk99Dgh$kMPkt z-W^@&A%Dx(-&l-X@km0B*P0DwoT|XojSt`ydvD}JD1mYXN_x8F?7n4@Tm^YWkNggT z{Q>`+%V{#kP{q(0Lo3LNrN8UasU5l#WarDn$B?hLBlL|D=`lV=tBet=_KE zReA2$_b*?5v5=N{=8vquYZ<)YpBg`SAs1-*qUTv)E~n&blx2EuPjIlQ1IMTG{&+8c zia<_c@}5F*zv@qQUzxwWPgE_a2`(QQ3niI`!S3$QSI>97kve;>Z`Z@^Zs&D(U4u?% z={q=y*?tP)R=5|?4R$m6=4F(2?oauTkEwsY#!Uf?w7;4QAu^uG{O4XxK$N{H&N7S(U12xm>ubH)7rd zsgGO#H$r$2qa{=cV{$ z#h0=3EK$TsdiSrAZw5<;c<|p^gMnsDQ<7T#PUofO^R>IM>&Jma)1Rw5AgAl&3>4>l zQd&^%8p)QxawPd6^3@RhF3PV(tf@tRe?* zMwuf6MF!(7Zn55>pOD#Ug;^iWfnn@5)4X4`b8yv;==AaC_V~JElUgw*w#1z0eelBq zwwJIImA`G~%(uO?ZH8!+KfJ$%WDD`H;j_ZVudt$HGUOvgMe#?Ot1`6LN-^r(X3ep$ zIGzd`0dK7dVq!}ZbdBBt5-w?ggh2+%qCt=^r=<@ORXRnQzj&V68o z#C?>x>+2@0sRA;GgQte~x4Zss5(rMx|1}j#Ic|67-hLmG{Ur6e_T3jEmD>Ir25jnG zjF02;?Y8^~D@aB91PS4NV3?s#qNs9&Q;gWx$7p5C@!U%yn?mE;>CyWwbN?`fd4f6P_E zz>V@hCzJ>oYuwK~8K^nV|J;D4*xdbPhR@vWvx-SZqc0o9Ex31M-G0VdP5AeOW4(}{ zqwVdYid>X`(|JuN?~Mip)Mv>Ju;_F#pt|9C>E|YDkU<9*M{8pf#dSZY@(84L;PDm( z_})z3md7QtEsj{HU>-^YDL{sY2b$YiHku%Di)`}&D-A7KS?Y3oQC>7RiuZN;v*G@3 z_PM-`5H`UjyR*^5L!U8Zf0kGE$lX=dy0l@X5PA8nh=>)O+IP+!kH@FN zlG^v#7Tq}ZkNlb!XO)-Q7C$$wZHc{PB`S25e%W0?aq`YgE7U4OXMv#rs0q-*9;jQi>k+<-8O4k&R&guueQ zbFL9{*DJ)QJ$U`GbUVqXeA`M8AngB<^Tu%{mU!%no&FGzHNElnpeA_L#NoOU!7>ba5cLL zC9*yl62nZa4z`1GJ5r}t4@P&&!A>b*1E_k?yg6X>w>OHB)dCB7Fd&53y%*y&`p z0Iib>tJ4@HU|*RW@SVdnMfcM!q1D{ISjtWfGd<;_>hjb-QKz@L{~1$g`DRDtXtCu{ zPUqN(kupy@4jE9m9*?s|s+?k+a~=wUC;|JaCksIDhf4(`D1dKje}>U2*Y%{%%@ z8VuKYQSgVqU1{AN{9t8x;@34?PHi#9BD&t@sT{ird@E|dANJ4iMVHtb#>)|J-Qbq* z8QH#E`M= z9i?wg;*;cJ(2#5IVBYAsR>~0t=`XJ8Qez#(bp00piD#Zsy4oz!wt_UETW0z}06LM(3#EITXZ-DY6mzkYNE8m%@#TYng5LM}A z@Nb$dDQb^rJVF3q<0uxumfIu6PZ( z=%p733H2%mDy~g7lEI-=m6KSjx>ac8Iv^||bbNyRxY5V&!;g5uw(NX?9b-43GFKUptzhQ~DTIq^}r^~b*Yd>#{a|hJc zR9iPN0m#N@ld&!jSt>T9i}j-q2*ayzsWGjX*Eh}37mK1#()MI0{+Z;4ySr8mmsAK=Z>6o{qSPaCE~*HiMpB_i+$STFL;iOWxSv(AZiR( z%ymb_r_T~+VSzE}8H(0NMCf|6{XgPwh24(t!U8O^2@-gJ;*O4Tbb{qmU;N z0_f^z2X$n4uL@uVPGLdYK~}KNrXJggs=|m^9jJkFHTRj>-yy`6>$rMMcUF6@g!;Y^ zbjpQl$GBAaXI5N}rqcD8-2AGO(|b~Suv+a3S|@$XaPCb|aS5co?)116vT1KLZ)XhT z(w&v-rD5GsXJO4|q>%U#@>r-uGw*wdZO8e~BGnV3D#kC3olBcoaeB1BdrE9T3`MY2 z^^EzOd`XkQ_>Tu*Xp_0`yWz~3pBbs1Xo)`!!YqIl-)|a*Z7hY za<@EL_`^$V_`y||=C#$ZNtu(5GE%*Yl8f?w6b?VI!#DtaU-#N@A0g|~&XYH~1)(Sd zU?RajA`br>QCZMVW?1FH$z)l8@xctMQmk28y4@Y}hO_dgb8oO;L#M5dn~4L}N(nty z@1QLcv>`Z`DmE0-YO#mm!<2dUb>#&9!ILjIr_hS)JAtRwwp!Ec0d~bNME|MmP!8$w zey*r0xBv9(*3yJ_EO`xE9z7&yA?3}PCv{I+ihiXTmYij$R1l0yxbl@j--i%T=_bW*LMxw`xMSn|H>#)S4uA~<6XroO4#C#Z=(+1poY z@tIsz*pNZ6VQSpQ;$dEQhV}~!x<}pirm9=)`BKyD7-YvBVap_|(?U3SqG5B?hG*P0 z^83!6GI6MqF2(Q|fIUvDAc}jtVe^{L=CJb3)Z{B~6p`PVuqOFRrBu+nXXh>3-SYw9 z;@wKZ2G>Bija>L0kO+_3pL2n1a`?9_>$0ipUW?~7bGpkBa7XN;hhxcHFF!>s7}b52 zImv9Y;{hDsMQ7I5rz>S1D>)U$<66hpWHpuMJatzejoEZua!Pk3 zq7G^`!76#5q*UNjN&dzaDkf!Lc$^a$uRQqRUea74Fdm!`@iI%6IK+L zen+rY&hCy-8r|?!X$d`sNrGp24LJ3#wZQugI+s6RtkTTZju5;YOnrl$-JGBDSnqd{_Q&XVp)e4-9HX@x)OGCjya3t(Blq`8~QLiJmod5=LBlb zSjG82x7elziH&krbyh(CyXMNzoec4XHs`ljf1kS$nR!9X>COhY`T3`*)qe%;bAo9; z3es`dX0LGQq^io<0M$zwI-3b9KH3Cb(^2KC4x>f${A&zY;C#%wYYMRXv`Nc}!Z=v* z;xv{o`d9Q_=C2h^4H++*ek;N9G8XvfGw^H3u7QIb%yem2|F#evGLS8nnxX1^%zl%jSL9lh=R-13U&|I;KXsR>z2)rjIDT# z!eJiSzXyJ3B{>J{0DNVoiD!(zlJ@K7o~D^95u z&PuD`S+siXPVm;q^ueKrQiJyafDWv_YBD^BB0s+HjEll8dJerj{V!Yf$ll~=QJ=E#>7{H*|V0PHGxf&WR zbkN@vI|sxFJ4(x#xGx)%qOftZ}zpTrNrwN>g zdVfC5rQB;aFV^pBgQyDRhA!2us((}M3NAw>W318!^fbGNFRJxZpUswt#cS*$vjYSq+K=cte zlJ4^ugKK!?+w5WPnMWvf7!?OU;^H(B+9u;0b&2U-?Vf3o&f{vFR68?DvE9($Fj6#{{sTpSjAMS;Nw(>i24 zxw?62CcCpslu`90*T$uG$GJo-Ib_bKZU+SUp>gP1{>$i=Z1wV6cKM*e49E)oFL834 zT94l8tDJT5m->Wd^CRtf&C5^r%?Ub>e`U_rkw(CL`&maBI|$!G`-|O&XSgF`XLY&9 zZz;cwpSk5{he;Z*GnHS6ji~t#l~%ueb}e*5+W#atN+y3nZQOVL>tsmQZlM$_Q2StV zL^{F5#|Tm#0ld#d z+wdLduMK6Hhg?r+3|P^QM@cDDy6yIYDw2->NQ>^g(reX=Z!s3f-_QU4{+7FV?2+T0 zqsQzWG9GinZ`p|r*i!7_K_&@~ z2{i5PW&~Tc^u`P?U-)Nf)9!wjx6Fj|6}VjLVezy?PS1k9+@bvO z=^*j)CZ?*hplJ`PxRj_rN71_h*)#TguS+AH!NoQT03P2pxx+fKA?RC?^BDkS(R|M( z$4;{%P8d(8)6hSv5VO<;{s%0edmk?i{FZA|-kyJLb$A@Z zrlD;bVR6O-9n|jlbwJTKUT)JDk$*`e?hBR1;UH9iN6V@6pKb*h?Wp!2|Dgnk`3|3P zEJ~BqR7;0I2f$?QF|@FO-?@Jg2to#Qhc1{WQs8QUlJ;k%(EI2<5_L5+K17kxX}1D= zuHz!W$%}F`DNnDT4^YvSWz4=RFV9Tw&4*&j&dU1PIfM90UCsOhwBhACGp#8fWxz@n z3rauxyFvG;CtpFbtm7TEqBZg(cXn}o0@&gS#4;wZy! zUkO%(e9x3GoR%3X(<#=vl*t$pr^w63>NX(DSK(jsVVYbea}ElTZT>3fl>#RB%X~Zo z;)86S_+*hw&|?ax?jyHqU3(oPv%XlJJ;B$4bXU)3@ZuLWCiNyC1hh1QV(Dn-_3K2?&cp%<>XDqu)Sf07Q(;X z$aISsU)vrTq}%bFA??v(DiE4Iz`*Dx{EWW;;yl4vMUrqjmldMkZ#C3;EOv8#nW<44 z`Tp8jgbBQqc~#wOJ;durth9!ePh#mPvzQ9`@F9YuLkO9S31afTO;^c>@=Zpa)mG#` z`w>&ttJN$W2i+a9|cz7EbW@{!EkgsxCWh@?)q@2(bA3c+})8-Rpb8=p(2SJ}vZk@sKal}eN z_k9A9nw^!^AO*WJ|8k-=vOun7;n}B*_$i}X%$8Lm%hIf~owi!xlf-CGYX!ka*{I-1J!|#RuqTB<=!OR2@qDfwVT=nV5&vIc$EF4JWbonHb0sqec;S5 z$sn8F#3{=FF0*o+MO)$WR$0+8S)3df-EM!`xl{Y06Ru`2=@LzualQ`*{K}AIR%ng(Xf6sF5(klL1s>+gGf}ETU`6$P+W~|D1 z5GTcrd9}G{CjM`map#O;ym+<>I-gDuMDMPx%Rj;SexrcLclsHWKpJ|P#;3I~bI(C5 z3_N{H-_Yy{`99<@+59-gCay-jqt@^5bv`Dtu&JD*3U)Gqgc>*rRPA)`lG<*XhR}Sk zV0c1o|EXhXjoanK=gmp(^lB?oI_!?I`I_H!BEyFoB zL1xW*Oe>lupB62C)lzDeQ#;(qp|Ka8;R}OhQ+XF!9e>Ss?~5eslZ5HNyP8*-w57Rq z@VdIXuJf&^Q)=o@%h*l)+-=-k``Hd=lv%;Yn^tEDSQ!HKh#cAsio$uHIZdeSWrx36 zdU0}8EFhrtcI)*cr+d9T$4oEXzOR*EzvL!UT*A$VI6FG@{w18WQ`z{JxFR~zj(6Er z#tiw^8w?<*9>84X{lfyz2MvXMVw7Lc#J|J*Z=tHXHdQ)%eY$xEX?{^(c+zs${y)y3CdO#Yy70hdI;>s}~HpdW1kACV|1F7*+AQXNLnnDyp ziDbIKpK{jocj^h}dp~Oo^D!CJ$xfaTmQ)wPVwAY+>uE82`ixkP)elA|Aq83d?0m?6 zt>#GHSsz+InPaJvny$Q1@c^_-vJ!#i++s-`!N-j27O&u#MzAeQVl^p7VMl`YqSn*c zcDl9Diy?nxJ1R3V&F;yd{vz37H07zBr~!3f>w2tQLR`S+=|R2lU!2?KhORP$TeRamnPHtsIx^DpP#>Lr@1) z^eO%nVMWaC7x-{?h*^9oGUo?ytajn94|!63xoLR~arvElA^1Sbxm?Tfqb8%YZNnSA5P(j;=yllDa zC|tSUS!wIDFQ$KnjG{{^)I+;g<*bp+fEJ^@EWZ19tJSu*9#*Yb9|I6Gk8K|OCuS8w zV%GV;%`mx=Bw9sY@N{Ldc||RYEPnDF3AX)R6}tVbBa3b;08y99rC6@+EP4)8h`IBZ zO;D{pxfGb8>gr&^W=N7M>^3|(nX6ri?_M`@PAxpP4G41l;&H3>iqb?0K2zwT(DV~w zaG2m!fJD^D%a}ux56&}6A68?#DhT)G^UmZjg!_}*7VyF7OeUZ82 zwPR*}_*WQdbEh}M#xqPYN^ZxHncAR{QkU_$kHK?{& zq?t6@Wip(!X=qO~0N(YmNRhVm2Mx0l)G&`|%54eHjbgdI)9?sZ6C79xLRvv-cZBOc zuJ^n9ySMOjZo`#5jom*$pmNy+Rl*Aq|u4vM&jAbf1{X?Z95I2@0c**!8;SG=bFF{Xy z%dC}x1-<2us1T-rt1-ib&cogyX#%;e7%Tp&t@-_#zlr$6WrkNogi)8l=&G5)dH}r7 z9^bGU>n9@L3w;v#;`Tm|O8c5o+7t0%_&|XD7Vioz_uS8-_$UH>@yy;2sRLUtdwxaQ ztxX}WJkWskiA>2(5pQyGU2q*<%!(Xp)f9CrvwcVpm2LdZWVkAbL9TJ}6{+I}BGC%< z1i!OMk}WUZ;jVz)ozjf0?nWIuL{55we%8L&Dx$Hv^U{mCMJ3KEN_vEUZ?(U{Tu>#h z%SGkvMKGV!);Rz2r~4C_|31)PyUP~q`>7)KtBdco`FgQ9xQoEJlp(L^5>Q?$-sax7 zeVa&N=zRzRSY8i~Y z+Op?0>c2}BO0`W;`@jRY!0VGtr0%o{1*q`mIcJyZnr_C2eXM_d{hs;1*Ohs?)1+c? zpE9Xo{0ebRu_2D;SD9zWr21o%SloEf zdH_=dts-lQq_w}xnteyy8=95`dppCVBtCs3S-yhLb85Hnf}pu+n)I;s<{b5Cf_-&t z%QsId)^8Q2-7>)qJlLU|M}HX5WhjcXRZ8^B3gi;6?%l$iJu6Z+s%-;_`U%r?2?;Y3Su_xdl9-GTjnWy4+BJTn4gN3oXe;`&I{X{@T7GdAJ`L zwsfn@B#nBPRNnr@W|<4V>$Rst(a$uKdB4%x$K$(y;zocDCBnPSB{RYL@zlNmyqM_stRTx$x;AtYjay9;dbwv#VJ>)H>iTp4XC>|GO5>MIpn7w4 zG@vvFOfUc@AYpbKXFCqIrL|+|0GgP!i8WGOE^7W`(jNYGv(@(xu4S-XM55$x%$|NK z45pr^UId-r<-8guUO@YZ84dedFyiq+C6R+p65KggoXb6VPl2N$+L43jSqpW58)MkF z6H8uZQUA!G32k}uFzxZ*@NYCdmMV-r{tx#lZBlK5S^asA=YZr%H0oXlF6Nxfqe)Xr zZQOxFL`s@0_)S@mLXp^M#O$ne-HvK-L)ZM>!0kIvvl6g`75U2txM$ayfRw~l$>~530Et0EZ&VFu7<*F#JcSx`vv+s>Oaz`Fv%_VL>dU48aw^w zpEFyo_{>Pb>WSYc2 zOoepYlV=n*;UZ9DZ$psfnhBOmf6_Iy|NCYp|Pad=^} z!i^F4)U5^H<^V^31^#L0pcGGLYKkx2O_0YTP{#UVx=q45qv-p!)&7&ILl+90jeus( zPz#{L1?oOadlJoRJ37k4MA~Qj>jR8OJt%6j***PiD##k3#O|_Z5m*D8EPwLNSuDkH zBWW;ZI*V+p(&Ma>fUpL*d%9_}Ly|Qqm8?l*pLQgrT7H6I>H4|Ts3bAj%w=8O%c1V# zTA-`^^1pMl=_7DOZE(9t&-fcQda{P>=IX65UVmui|TjfVgtR+LI1AB|I#8XSGm+r*( zg*43PZ=k~h%4vQ(bm!wk*zPGmpOb>FOF$F(QAx7$*vL$a0C;~uESkxkVz+9RZQe?RG6_6=b_jT_e>4x}eicFb9*KK!x4dtQMsluD`Aue$_UWN6OKl1zXku;Rq zIxV!nZ|XP4RYn<)1jx`gKo`Df?nC;DEA5`+b5kchI<6CCd~_S~%b#%Fu#)yemLs<$-;I^d`)^ZeO_ zuZOjqQVs07$3dEG?$P>(G^3BrlT{3o|HGnv`L!&iygOGMk2Q>SAb)Q8U5fmbyp2Ys zGl@=o1J^fWow znI@9SvCI2T@BeR-{y(XU#h;KTHY_vsK+v+}rKIF(k#mXNk=VoLL{@0`?Lb1=(`K(# z_*->Mn@p4S`Tm0cqj*`+S~@}CzFx{%2qJxJ+f(|e&1@rx_&2Kf%vqn+YNn6KiDa~V z`Dg3R4~0P`%^ODeR~zWl!yMH8Q~m*1fbt4{W1Vao26~OZ{B`b3PU%}im)B@n--mgT zBAUrVsUWlD@cIkrKI(ja*84QR>yx2wgSchIlsJF49^VKVi(iU8X=e@o#Ys&KWx-eA zxQV;yEqhqcJ(}LY5@P#Tfn^$1u|=^XdUZm*Hpx5upw4L6v=5zoaEnoJo#>v}Hc2bE zv!)~Nlfod6Q&F9)A8FCn6C$p@ATUia`*Y-X&9Y9#g1&(A^?dM;-zukK8?44l#Z$cc zng8Cq+cU^;aqj_aywy9ow1|lxSLPdpaNnRR^qo|mT0xEk0U|!*k!JR2=tV@Ee zO$i8cj!GkiN-{a9m@C^j4#(7_GFcu+T9H#%3%GZCkK5m?uU-7}^JtdI?1sehScCqd z1vq{Eh3T2^B>Xt;?1|D2O#flMX|+^_X?LZ(o1wA1tHt%)63#D_=2})-6Tfo`?8oj( z_x_tZb3>H_Y8pu}gQP_F!A%X%ADtrInRda10$5$@pCe^;vKaS%yEXNr>Oq>V3XV;U z$DWJts=ZYxwR2w|z26H8d!B=2%C?O#QZ_xX96|BR*_V3jpN*{tWI>hI9;PXYSnN3S zl~pU6YGG9^(;*dxqwmxmKCnMDEVJyl-~<1fSya7yMKblBRZy0+H77U3ba1Q^>@8?iHBNijtkyWq!#sB_X~ z^}V*-zIpJDBW^l#>$jRkb6I`tdU>^P)ilia^wqZY7-&d2tas)yrkl^`zXeA1|4#F= z8q78v)gvY&i5XqXRw`4bHs8ya@?kP}-z?(mA4bMJ?}u$4VB~6a_h>?f8ano`9#(C; zJ#zqt{lmz`p^QKr@F_S+XQJJoSFOk3$-YZ}^_#td$A=rV^oYcliXWHxt`7T*0J;fo`~0TU zHc6YV_%<3}bAR8F8$J{PK6P%rYq}dbJXSEJX8}7`>9Nyobvgsq;+y{RoR#8Owez;~ zx7_&2Ly5e%c!LFJ)@5F(cuCFZ>QhK{VUz1sg!1|%M0?+78y1XRtR%1fiiCVz%7EnD z!my@f-jFaRiMOVzE+_4@so<|L@eJQ@+B6u9>Dl2v|67iX{1zLl9Q z=fKLibYjj%(zW#h{0mOfnmYNX!z;t(qQDn+^9wJ_B@!5-L6c10th(Fy&~jSFM=2s~ zh6c^D9t0zmg%NH}w6BTrcEK=I-oU1mL2-s(l55J!TwXuCq24P;>{x1i=(wY9*L7h@!rFRg8Gx2-^_luNon8qjCj_Wx(SE^BAaf{ z4a7%%R%*Lm;y|7iJnWyS#}2$6Blu*JAEHm*!79_dI@A8r$>Qv$s7(Pb6F)p@T{et_ z5}IS2mI%f#mTyQ2PMk6HlTyonj;Z=#HST}Y)j1^8oC zVKwXxj`ka3MLR0P-bSZWe>|l=DrUQy_BmoZ^68`di~J{BR5*Sdem3MBmeGHhrT6tq z?4=6(S4l{f%fsUwN`v3v3%K@8pEp158;mhT-O*u@J0h5D;G*BEF!UaO6zx2+5C4;P z*f)3;O4H|ycUIFtL=e<6s~R$3YEa&8_f28{5IB(EHxV_J;wI|Md~c~Hmn<&eoWKSh zyLtWaa2h_tlI4RpqqATztJ*qw;}j&z2^$)MK1TZ)%8@S5n?613N4W zuiT^wm|R#IV+s7vfIuUof@@wbJE&Jp2OUE_C8pK2Ie{s`mcIx z?x7q@g}SmR14J}FvalxV@f z8dK47>Gtsvl%-fElkvT+5ol;0)0y5Nlx_L4;a;aQ; zxyhY#Dmqdpy#*9sFzi6e>CeIRP1Oms>(4gL)49e&+Y=+5Z&o-czpRd@`iD%GzQ2H1 zRvRJ8U+Ua;RE)TM(^2BfJ-q|e>iE@<$s8}K=c8o(muV$)Z_Mch_%EvT6SP#(u$KIFzF?2VVO>vgL zVRU5S$)!4{Cu$<>Wa~ve8eeeaB(c(ywp%*mW2(Pf;#gLTO? z>b@ymyElM_d1u4{8Ms8L%J@rHw(|Bxs* znY3HC{A#VazVdg%7~vECPAv)j7gsaQi*P|@tSv{$p#9H>(`PaTrF0!K+qO{4cR9cG z-Btiz#|gmo%F;pQsd7prJnzH#)X%)Tn$2$H=(Zmdd`LmDVi_bX&rQa8tG8FKy;@%D zg~K>KgqBL6u(2u8PX>vdOsXx%X>|Ai{yM*Q`AGK5IK=WRX13vn))n`>@*{O;=8f3S zE*k3Iyetf{@mYLBs^b`eT1~U8rh#eUL-+I<%9rkPplb6u?KJEBgts&n=pQQ+Kjvv> zO3E}WLn-49FOE%wSAxQ-^jUG>yy5j44Td-w0N->Hn^5L9s!K@RprikrUnxz>!A&{! zCNSBv{(ZgwnH!)Af;4jjP44*vRw2At5Df?e{jR<-9qXbzs+Kz2e&@y2$Lu3yM2i*A z-0_qZxpz}Tq>jCw?(*UGdfN<>^%J^jS(%`C7SP!+U>;5bUmY8oV>G(P2$Rq_P^vh6&ZDd`O{WF!0KLMoRDyd4WYJ?Kn}r^Kt5#yMTGa0dKt z_+HHa^!0o80FIRjPnVXvFo$&|q`-%I#iXf14lO@F&EKX4r(--#Tm#IMW6mrgVDxhDC*K%-t@n3de}l|_ z*UzciD=}Vbaox`cACG!@3Vi^AL>o_o<*-L+%>&A&>ol5)*)mMCDqrgU;i>y$ZT3*d zo^9jJ@mKh8wOYKVsK~ z-zG!!XsVG^!ASknA~2@7qT4hp-tL7VWZ;&}Pn|s$=nUsC1d-w5pzJ}V^Sj4JvA>E} zv?*S3_vNyV4n&*%CLm+9BF$HqfpT9JRHdGgb=krzY9O{b%IJQ;Cv%BqdJaRm_hK#U z?!?ksKz%07y7Ak_DP4xAzUyz|H4CiSaCQ|ilT%`>H-j?ud{I;LTzt#J)(tM#L;?-*r8oAg_t-+p2 zeHF2{l67;Crd%dA%{5b(cWS+lK^%Nwcy95MT41ug-ar*-Y2DU&E<}SVMeStJcfzmj z;P=@(B}N$&nhsQ4>S*p<#!vgs8ju=Ib3;~|4mrD4Tq8Jdy80L9RCjr?s=jcpcE2Xk zz1UY3W1?HK_k9$;m(Myab`;}F+1(+Oa0R_A?N*pD$bH28DrTu|qc3yHDBrFp^SAK~ zBZTmfB=|F+I<&wX;dc-B6scnUC2D`0knCwVr*mF1%CTw1SjKZT6+-IiJt+9_sLXr$EABpVukcG&sHy5kCh~;0GGT*8 z3m37{l5aa3^ZzI8HO2^MiStJtUtU=1GJlP%a*Iy*em8!=q|BwF#F%Y%^_3wji^36` z`ZCMAh`-wOb2dOWT&9WE0AO=-*6G4kTrmF#qkjI1;d)VjW)_@)Y7d}4ef(YR%ivv# zXNzljSFhBQ(La{_GHKJ%pP4d~sx%czSskH$#1y8+pDoHli^hyZSsLwbqi4LV+qhl* z09GADMDU`OIlecm%q_fe`E6`-10$Cr4sXw1`Ey_G(h%TP!3W~fY|+8L2yCw*^}V&u z$E14AHo*`F+mEk9jf{4FuFx&YoXb4AhTf`12zYsEu=(8-zuvttWw-~Fvq@+c(V zYZ@&C?1Wo?=R6T2?u&q$zdx1#Whg%mN#&rMptv0|1F(l?`dF)-p0Y_TvUX?yXRQ$W zGMtQsv>`i@!)7^V&auqw5Wj@mkCj&S&+SJ*AJn!YywW9)n>n=30wc?{W0{Izm^&SQ zgxT2%bY#Y~u;)`WZz$bl-I?kw)}6dJPt$Wh0|HQsp;^6@X7ov)iW@A(d1Sv>$&yI# z`@WTTgt|EX)SiPf^Zc_K-`Mz(2!k~&;k4km2d3^Eoj4TqqkpvkkMcOVh5ju>^{o zpYU^GN%!Xou@4Nvpcx_QSDraEgTn5x7?2=F(0WYamf7-hG zmK$DYe$=braaFyo-5BZpPl&g}9RHCThL3lC*NSF{hUCt4P@aJX{U#k0q(wvd|qF&K;5(hV!|9aA~p z1xBG#I%_?@ZwoYg_Re@paZ~WAOw*npHB#_i)B!;TuCtX|l&2XV!3?qEh+k3Eh>_^% zY)Y2ZPr*x919fQ$zP&`qDF-Xb)pax#BKI&b53~yfTG48(W~Y zyETpB_1JKdET?=CsPXtgT&Y|bag(2IU<+LVTbzDcILtrcabs6iSFeTd=g)K3UStj4rb0vxm2#*L9PMZ8|N~D^bwUkuILP;Hp+YvS|k0eL`CrJ~zFXx`pWad}M_jmIL6& z803vmHTZfN={uIc26Q-d$hRZ06Lj&3=XLWH=RW$^;bg;<-dHA%=z-DHy>GYM8-1FE zPtR|Uf20*K5L@<3ryVOu&*kb6PlEF9Z-xNFYYyc`GZ2=a6ni&HZeiZwil4fek>%ole-+`I}W6W2D zjz1dEY7^|A4w^Fl^%2eLFxt9<#2XhQcChl5{KEY5(zV7D@zQykz2>{;FZ0Y|I-nU@ zRQTec3qEXG@P)($t7VCK_Cyzsv}B0M_ssuQ*d8#> z)fAls73Lfu1S-%IVDkgHpcp1&D=zgqS2h=VPp)AhEVBZty*pM^noMCjySHU|9orJh zC%sFcp1Dr=dOlQlW9okYID0QjNLJh5A+RKZF3^-AM`C&!S?UK-$h{|z z7SFo*oK4q825FMvAFv`RZN#SROM>n9Twag-0z+n_9-c4gn)rREh4!t z=JZ?$Ybm9*fwz|^eOM~4Wc3}?_9|2Iwk~q&Nt=k zKw>c=kX#6dOUzY$IQeN+N63=}h`X4Zx0)WB>Y!}tX53CZr{!C zQC>1`TKV|$^kV&R?Qq}^jc)7e;is4VYkcHeUhSHT@!2(eWqag%u4eE6>#4hE4mF)H zl6kQ?U3CDW_JU!F`vfC(W-A42S@UDP8J%$NxxPU@LR}5dzExzjvuE9X7*5qZ__;Rk zjFGsL6~5XjNm7fnED)i|QF$2B8tZ$|@_%aqP&QFY=hAZuZB%bm!&d(0#W?{73d{R| z(xZk5B6OefU1fg_EKq|}{>xS$7=syVG{xumKh9^ICnUHAKw$oNAYJ~Whg)L~TM zi{6M-G9@QfdT!uTx=~XHzGflHrxkT@Q~t(3dvtVyosBqOJ$_uW=c5O~#z}!+#XD+t zM!B1C87)*SCQTb8>q5@GDhY48X%bcK5iItT&atg?)S=H7&}1g5f&uKYkw&wD2tj$!PCC`&Geu7ERe<@r;6x&nrtVaXcTZi^VF6Fv@`9tP4E2BM0qGqZSGNh-k{7= zRiFJ175P6fN>rkg^!4W|#S)u)e>jHZTE=r~F=IUVpQxBO0C^Y1|A(fx@N4pY--kgI zR2o!3Vkj!ojP8j_NQj7p)aagcv(Y6nkw!wKBu9tDq&uX$8%F1fXYbGV_xu67Uc2sV zyRY*+&Lgl@Yco>AZ4(Xb+=}Op9n7D|4lEEbY`OEoSomN+8^5^E1v4kg=+J%NG7YoX zJj%fT`JqU%2)ZXml4`ZMLL4Oi#m%f*n}LCv{MUt-tCruWnYnOFz9$cCfD889D&laM z(JHxfsT7_kOLeTmcEUEcd`blp=_n`7EPS@UWTzbG2ASywIonVNX^1P@@%vue75w#b zr1Rf`AcY(b77&+_(P5#-`YDeldf0MLXN%xDok6XWjH;-^fu-wzG;vO4$>>o%bDM%$ zh8~jzDb%}3-OcQK@6CnF3QOy2v7^A(n4lrh8F(#NvS2mbZnc$35duk(cwh7R5Amyi zudaTT4HnCZUbH73heYaH46}AglF<-|+WUr%A=VnT&^f=)I#2?;$T?pNSp3CfOqc49 zqZ6R*)v;xLz0bB*8N>B0{fp=x5?|8Ere54WnLj5iL`k5DS?J@G)NHk}M0aK%f{xLM z7pQeb>QxtbPW-p@iC@dZ5fYYYL32fUQ*_W*72et|_Hx9GgT2Igt-q8ZegF)poenS$ zR0%GtQwzE{c1r7m?*TIaYwb0eFJlLUs5>qs&^pG%KI>Is8Jy7La$NpWV(2Mh zHaR7Q-g(swh1X@+T9@s}|5NQ`q zW1f=5KNH`YZQ3DckcjS%${3;(cv>5hVjmHyb(H}A_QT+qM1B}P*Mk*~%1&gfzQJIN zSpAT47nZXG1)?!>$RB~YAZP)e${k!975N)R*2>NB{7J&S1LCnKRm#yguyV~@X6bYc+{fAEUc@j{ZI^%IMmh3`jtvmeb%KE}xh($rZn8bbe zCnJUJ$39t=a}BQbH%VDZ+;j8}rB#qm*EUr8fbOohA0S?Z?MZSEd!4Y(H!oY(SN6K* ze6lcF5#%AQumX5b3Hsvt+rInHkgUe|g^+g7w>Lbgn*Vf72HWZc(INGc;08wYZo5vn z4~QHtpfASVYs=flXM@xF$%9EB*5QVg%Dg9AVwnv~)!|7EUoe)0%yj#nM^ugZ$+DjH zgStTd)}w86Ez)~P_FJ5!A^M= z=e=WmV346|6`X%jg0y8gs{t<(j@X70nXJ#~&D6X+;Hy-tp0P=5-Wns?l4hZoa$lZG zDP)|SsqMZbh$X2T@pIxgxRv!aq`;DH_FI>U?cr&T8lcZ>4fADbT=2V8_cOl)QNZQW ziPmx44mD5MY5TBEdoJ^2Y1@CFFWBXL+p~oFutpLf_-CRmm}S{*;y<95FwI?@+xOev zXy1{>SDBO^4(5ubt3FdqR~{r6L#vcNfb85#?{B}JHYf05VdLWc0`9_iv2oBQ7;R*k zs5?r}IYAG?HF~Qz@ytO3n4y165A>yu2de*M9(GvAg3dVOnWtdt)`$qW@xeieBrv$g z^yEy=^*%p3_HnaG3CZ+Nnw^pE=|=SCiUamnnt4jT$MrmdQ@=kcPO^%sFVIn|8<rvA(l1u`S zN7h@F+{imFX$JoZ7=7JYNBVSG>Tum_ash$1p)Q41~nK9C;6%h;6evpN8yy@OBOw<)}PXh1thCx%v_#$b0h&4-}%fX;%`q1wS zc4VygYEIJ3&W6Af{jb9$5Yn9qEHWZ<8cn7IiRTGe#5EK5`=8`5n2&PI*}{8@p2;N0ZrwGNv;h1>dr<|lyO9&+!q zdzvFnYq64g9G`_#3k+*_dw8u!YI2&uhm%6VZ*U;n%G z*gtq*&a#2#cT|J-+~z@@1sm)Jljbs8-}wm+K!_+%XjjcT5;q6%*}4li^0V(M#{^Er z9>;apWjAz2QnL^_m~_1JoTqTz2a5Gm`3MP3*LK|0VBNU&MNZt z*Kc0S3j`ZSm5|q^C%#p;kXrQ7MKpKt)--HlxMQ@Fxn`Cp$@Ha-qse|0M^hW^+-VLc;O`BnE-z|X6Rnpr^R%FMfT$j*1nt6~^- zKFlMK_0R> z*s1jDK5baWnaXZIIc1^Za@Z;tGnCwEf-khRcXRo&x_O#DS$D|o-HWK~P z9frI08cIR1)2ZmneHzMJHsnmb$(J=)v1p3}$Sc=O*)9_Bu-`5pNXDUqa}J$O)puw% zq47RC0&4HFLHt(tZ3GDoY2-4`IDF9s=Vb=UXO;J|d%S3q71N-Qrx8TMPYd59cN1TWuB9PMk&yvok z3mXXVWg*($@0379@H~WH>tJD`3ZwD+@Wux^+?g$%Hc1MJo9v13e;$i3d*_I(JysckxF&D%)VfKGnM4)3k5CCEVhi0zTaaGNg|a=pRHi z)`_5!MTxP;1YMVSSp-3!C@m@~LZ{oP)VH_>jyHe1=FWRn-CGn&)ofWie8)j-jTO4Z zGhV!(p$qW$bNXi%dyyN@bth5eG<{#~KGmy3JZ6mor z`zfNRXvrz6DVpo?bZ$q^^}Byk-WHit z_;-2O*ZJD9?-k3Yg&iCjE}Pdl!<*|5UCM4H#=FM*Uqfkj;g~BAFhZ?Mm+0xeF6IpI zk(oLlVmy>E_KSx6&xna2QWj<7Wxx)xBE{2V;i$s%+@ziCp+Hm+WbZqBZ+GbZRQwz< zgB8VY%uQ8srd&F<`i6(C7xZz_j%^U9jK*r}kvbau$GnIRbqN4wv=vMclz|hIcrk?6 zN58}kQEtfmP{;Qu39U*@n4kfTWHsZj<=3>qhlWejps|FlO?c2W+mP+WZWims@R2#+ zEuzQHC%g@%kHfe~o!2|bvYuM}I9}s!dh$)4rCZ1ypdQD}W3~DTNN`P6Stoc&DfRZ> zoM|1Va5|&a?(pBX@w|A}WQ6 z$MuOr7l*&P3YKP@n5rUqjl^huxPSEX&99-eP7;baZYrpQ>CckZgK%Te{AC(qJmazK zMgMXTZQaV>U*e?%{epR?S~_7ULjNIhH|-GC3h&}Hu2pLIr9k^B1fk6Lf^L0PnMTy? zpBrC$#}kn2@ZXv>iHP0bl2*~tHIf|##B%d3+$j=88SIimEY@Wu(P?Sx6VkVdHT$~D zdI5J;M!YanmJXc(STN#Pk2+hj{0S!yXBaR0n3J%993#Z=7KB4lh)7(>wgs|y*Z-_b<5&8@j z-mtYFm1Hwfk2N3cm1ceiP}nEU%J|aht>(_r6g{yevACE8GuC^gs{%lfD(bG31HYpQ zzNFyPKNCPt@FVUSiP($B<=-ZCi48q9fVF&0u7Nsi3*)~DOWQBSl9RGYg(50vYO;Xo zUR{Xo+6^V;`9$DxPVhN~ujzuR>iR!xQWMdMO) zKdUJ4f#Z%K`BUF2XGU|sI|1)*>wSD$oFnm>n?f?2fkZ#P22P;VpUa|ve*SHR;x{AZ zuDi|EQf8w+tvVIUNmjc-q~yO$``)%a=rMNqp~^a}zZ$OgUJhp+HZuqYO`b%SWEqdM zO&NXy_cOEchTnK}Pr6^vA6_gAy5*)Jvh$aV2$i|f-E`Jg%Zp`(^yhWx=CF20u8yel5m^{=O^ zU3wkV#Mdj`E-7MUph`+k1HrW5L5?Mh2;x2kxm5Fr1v2)g`I~%y(fONju;6~vIl~&x zz8~{Ov<}A!-iM3rKK>77C)mIGrf+HO!QcA|zC`Po`89l8>&DGBNh|?T8|cS&)Ad_J zUPMQTF>8@D>^kyaS`@^P#C5SmdMI*dv9A6iPn7Z8{38T0_wwN7DwIg{g`fX83+r4$ z`u|O7K2bEedKfw^%lgQ;e5td7W6>)eTKUrzKlUXVO!k>>IxN+QCCrVU7vPkqCr_`R6ESP#SMTwBQlZ8x}khU7IetU zZXnvkIMRn`L&WO>j?i;Ui_PtY;xI%@hRR;~`m?0_rZfT+c8^Bf*Uenszvr2nuI|kX zfg?@2mEPjFRyxLDVr;SC$YK%#*l*HLC1RLe%~yTGv#gpDvnJj_&A!n+GF@LJfvupyOz18{z) z3~ld@R^08;|IMM`3-=-vyww7ZG!$_fm;E(wB>$%BEc*Dh0zB%ij+m1{iqgtVl58#s zSI=6mU+D6GcUIwT6#P+LA8%Y+H!Nt=kPmLK0gkX*2#_y0&plGvPai|n%jay>qfV05 zRy&B^5!~rs^i(Bnpj!_zd~TdJDM7&b%M%8|Xzi-*m$8e_<`v8-| z@sM9)O0e-lvc1nRihygh(up5SRPsD>s#L5F z$kotRkWsZ-@2`Nz-h+C3MdxT?;S3qQjf3Hm{_kP zo>`WO-A+6tK<^7SQfH4(bT5QmSSd%&Omt_-6o*F)Sh~BZk0noH3AOL4wRKw|HvI5W z6@py$#nmWf%)WE9gX1)=hdHdu5kJ)qMd15Z@X6i6cu$*u7Pw&zWJ{jRj_1- z;G~?M)|K|20l^KCY64?EoB{nuorfff!H7T3MU7p2nDqrcf4}#{aI&|hfRFKu-}2z` z@eDXQ)fG`OwuC+{yP*g$FcP<2lNiv~>HVo3Xs1$r`V~ZS6t>DZ)Y}xI{$=JDTCuZr)NA37wokn@$XUh-i>BXB9+8Ij>3?Dm8%*LZ;p9hzYmpR}tqn z29p0NGs*No9MjV0RI;u1)E%)iA={ zj~_ODw8ZifO1>-Qo4S!NwEKQb>&6rZF+62a$mz^2V&;^}ZhG^|6gc=??+m=E85>o- z(iO5ebuMpk`k%Lpv%zuYcEu8gHlL4kRl3TjJvzFcnBo@WFN?U)TQ`)V`$pbtP8Z%w zPr&!c)vq4r|C0l(sqN{rn}3=y#Y%SRU@*uv zZpVu3wg&k;4()-RJq+@ooX)DqSz2U!mH!mULABIIN_4~56+}uz!MQJ~7axI`?&K8n zJ$CRoTytdgSULr)8em~g66b&{P&y2?1n^kG^E~kdr8{&SuA^|UFIH`)WVgW8AaD3?lCiUgSRQgT@DgTN27w6Z+rSfbawC%uA8^k z`0l_rd~#rq6lT<7k}j_2H8nRzwk^QFMaWWwcf*x%d_=uZU&3kFT0?GgqrAELdwbs$ zkm94|{af&mVmMg~!SJs?lP)VhMxeyR{c{r#7HQ4&JTAkvHJL)|>(fnd`DtA<&Caab za@W#fpZ$0@&wkw4HmP2cR9^JfNG5IM$IcLHwEDieq3TOyAGnECvZZw|mAl{b_C@w? zwVa(!Bm1w94}g{84k`mV$;Xa|i%?Cif+TrU$#<6h!N$qh)^8AhG1Z@2& z%&CuK&GP$uQS;U08~w_w`BOX&5F>sBwU|a;)?T&X4Z^OC4rvzPm#8DC$N`x*n&ICr zWxg3gV&+kw6S(Pxtf=DhsRZk~BWy#xeQ2x0vs>v2MB9c0mB9OOW{E<*Q-s@>oyW_d zTMMXto}^1*;OMVy*k)j>Go)1Ne>mQ_ror>?QWfyI>95_yI8sfb(sqX{oR+oCg+wR{ z@Iacmj<14C@vS8qTSSEqEYm%_Ve#i=lS}tng?CVAb;21dk~#7({2OBv-o&>RuLLst zTMEBziPck@>DM@Y_W7icoZ(n_pkVO~ey-_$;{5((DWfD<$VcDumRmv1xhKrmpVo0_ z284EHBYOJ{Nwv)IorQQ$BI|aMY*SPic#B$%wKmk{h!lQpT9+Q=iGx-lu!l_< zM>gYVbiRH;cXrdg=o?@_$leIVblx}0XiU^peQ8~J`(*%%IQXvFt7pVrPZ=2n2Lgb> zL_f}e(+~6szJ{C-C zJPu8ctjqA-E?A=qod(}Eyi{G6Ha2v7l|NW6(Du?UsOrQ>dQ>bE-Htj!;EGNC&nhqt zry_YRC(St6o})D=qULzd34Xbd`XyZ?mG?$#sOXyg9vf&ZX5j$?1SK)Fo}S2wbUEa# zvxE(fnEi?8e1E(D)Fb_N9sGt>?w!Ak`^q!QC5A)+IdE_tZxX`S;V*?ZP_kaZhbfr< z%98;WP3E~VC+W-6vKmP)#|AOScKCR8CDEonvOjM38LA=u*62Oz-R?40hyzmR^hKMi z7;O~uT|ahQD+GmPsmeWWNRE0&)P?(oY3DJ-=rl%P7&#!F=Iz6uOKuh~w-L%kn3Z`o zd%5bod7)wN{W8!Ie%@BFXZ-KT`%{|m%=awgam(QK0ZBQ=W>{VeRXvn*lD@0SE2-aP zsH;J1!!VJ2Q`Y$K3wUf7>60a3vqydXC|#>;#3v?EBScSiUQs(STk5>M4Wz@N%c7OrN#OT9h;x*rZSLc0yjtQNnZA4xbKI!TDZGzgQjgBl;%CAVd6D;rtwr z4)Ua+l%%kb(*B#UD3LE3cFW@j8=Qe&mH-ViT&-_h*5**(z<7xkd*QtV15ql{1Vo3O z_3HwtI(=a^564ITuvW+>(Ot5gzb@IVFbUYV^{#?tDoaoZy~%t@yCmI+3>Vp6^}wl$ zZ|14ge&bzuZFEKS+|AeSF1e9yq!1JNCu)oS`E|?T+;Y?*?ht3WuW&suM(=+9#?h|$ zO6KnceaqK-Z$4^>q^NYcb;JIt1UFdY+jNZvSTybe&SxN)?3pubB zTM$`WR(IoTyElsxePpNJUK2=yDZ}RVrI`B{hcaMU4KcVu5Ls;n<&NwAbRnerB94`D zBCh$T-^K=VcJhn&=~18ZFG`Du0(n6oGPpxPW?Yn|~VP){mN z5oIc!G{n{m$J+{{NBDhe8k-{lGAc5?RmyWGE+<@)kCZPcVz9xoIuC>d`<&fF{?j=O&LZlZ_ttdG ztUehZWG+jrKB+BbOsrdU+mOOO;0k*rc&T|}cSpQrqEk7Ux$BN}?Jd`)l16t-aRU5Q z$dYRP-PVSdr7ZG-3>3|&Zv9j+NYP?cIc9wNN#gB~6kYVDkQ`1vF*&j&k?AeO9nuGJ zo77!fPR+8G>O&O>xNDi}Sf7^IVXIz(Z%SP$aQaDS%vqSOZ8&oLU;MIZz|GrS#~b!C zVDgF9DEbwrAU zOF!X7?4EndriF*fr5`RocPaxC-51tbswaJzHTV%eEnTKrBm6taYH@aIbQKam206bx zF1lrrT!lxiAtsH8Q@`O0J7<3snI-^%T#3P`#HZSlI!CwvUQ6m;CcRjf9S|5LlmLg~ z8%c2puXMyb#?|?t6Y*@7MJ6PM&TP<1V55If&_;x<@qsHSalo<=nrvUGzweOIx;5NF zvelXVvOr<4anry%aCoL(=Q8;PVHw86f-j3H=E8<^5dT*C0O^DJHTAoF@YNwd`T!GL zFgummUzLm|@?9>E~Z%=tz0;pQ>YmJ@$qaAdE?NzCi?YcwX1bk#^b2AF$3 zloZt0D!ngA?Ryb)oj=$Ilp%*c`XX{%1=;yZ#ziGgBIeY43AcNLH`~ihe8FG(LYT?+^^Fb-MMrl{(rh>ywJ(7K?J#oWl8-9n+Glv2U*i9eXer zF)_Pe*D!BADf`&LJIw(lRUaC=tuV5h7*5!j+Tk53>#;2*`u?8AS_0?3n9`ei_A1vz zA2(p2)JG2|~Ewb^j?G(KLY zyYV=-y=P0*m*3qMgD+~II8uC+`z>6PVSAqEu$VLZ?J&GzU$xnC9pfjlv>vWr(X~(S z`?S{Sr@>j2Ou;j{s>n^-ww8~N6zv6Yy|q4Qu}%w?DadYIl@ES3zBR;V|a9%D9k{4J;v)d_3g&#e*{Jylx9lo z)J)GhYq4b)x;ig=9_Dd!=tMl-;hV`=A?%v$60TBo9!RPW_~iAV(t=6o1{{{>#_LRxo;40&;h zV3i=`%KVdwG5~kwC`o_Y z5%j#>?-d77C}~%W^BwT2lZlpHz35@u`HY zX$|w6LPf;qNlr_L&drwPKUUB(=Z?SlB5B&Mt<191lKw0VH;$t?oHyPDc5zsEURGC7 zU8jC%;C@j|P_Qg%9s2i3E=FAI0JefSi9`ZcmZoW9-&B3Hp16}h4AHtKr(n2Sq_F`p z{_hAHT)K~Vf;f#a=T}^c_-H}b)%$E7c>|Jb?RG+yd90y+8i^c{Q~}>?vOCksUxYoP z!X_Qr2knVN6{N9OOMe%tGU-xmg%#}76YIeLxvGv@wqOZ^9CE5D30{YXyor4YG4@o+ zHi=$M2$%tHsKW8BUaxM`F*G@OAPrn(kO_=sQ z@U+GRR*=;IW|DinJfzPCQs%RkFSV%S2y(8hm`X1lD7PbAzROxgv1+yX*WT(vN&{V z_a+ml*6Zl^Xp(y}i90?sbFD&5M0HPz`?o7jA|kyZFkRW^Xg12foNrC<$fU&kKRGsP zN^5h73~t)_UEXOP(e;5&Gl6%Tp5(zzp2|IF5+bIKQA>noElvj}m|{YB);nFUT*HNg zr?zqQgM7Cxo+J-`!BBh$VOsS47yndu>bOV^Qhsb$Lb^UF-H)}5t6=e$eKhc?vI9g_ z|JtsK0ZiCy*8f0e%h)0Mi+0iG*+J+nB2L^)cNAB6AY+2e4vg-1V2^GVp-t?SV%td4 zhQ(#3Orp{|2ZM|;qL5SmCR8>n3;EO47u0tc67}KaVmcbeBWT*9S044??BTv8AFo)w_vf?Et=^v&7I#vv>a{&26-kb_mZN1 zTh3X3`O;Xhl*hstU!Q+^!ihJ|K{MKKB;MrkH6NZwL{U`61ser57|(vYq(N9biSylr z?_A`75D?kzcW<&#T01mkgc2aeobp(}6&Jl$zmD-DDA8DxOMYB`Xq}Z0s$;3j=I`00 z=zj4$y&_tPJA?nqkQLbRkGP#Nd&bv(F2XbW2DW#JHxa>n%W5l7XVtpsda(dIim%C^ zCwbdHt=~YwM(H-pa@S?;gUAsP-Ob`u-GuH6sd9v=(J-@}3i3A_Tf4ONkqWccfcZef zy75o$6Ea{d%lez=O5;MA=0y;_{Tfkz+YOX3qGiLJ`pX_1)9bNb0V;B}TYvDmc*}|H z^4^O+Ps8H2&6alzXhAXPODpN|?@Z}T9krp2KQsTOJiKp zzb+ZVl3?JV0U4%2U}Fe+tGuR0lu`HRvyo<6(^H}dMCM%u-wXk;T zl1>5e3s7wMJO(%I3(>SMg2U(?}FANkhRwDj%kY4TVB%wHb%CZ&Avn3L$or%5&FM>0U?W} z6d|{6Yp(CzXXS9+iYzz@_7Cj%ZW@#mCc7N+tZ-Vdu+oYGXiD#Q>y_K~W#)v=$;NtW z3PphF=M4m$U%u93qhfL!OY0Pj`;=Y3&V};;0pFcrgwLZM4Hi>-cZrua zOfLyFg8_VBW1k<=1BzL^L^-6){73u9vOED!NBD3ChgC;&FQ`>`JBmgI2%XQn9A&NhzM>lUUhuc z-_O!<`^Yks<6$g3)UttbX5K?J2wb26}@(a;6J^+`>S3Ddw zVfy*a?hS0i4nHJH&;0c3!?LQ4mao5yp1aX}xT~v}BfWvXPka1`;&S?WY(VIwL$I_d zWO_OYV}tW-VFpx6J}er@lC;z}>mgfv%o2P$QW>!RYz}*NXfxM+AQ`WEyTQr8wFDcQ z^u!sTtdGPo#mnp%AJ;scnOh0;8`YI|T5h+xFHp>cCM{o%T!V4zbp1ocaOk3?{7-XP zYQp-$#jrhdSz?&NCH%uGv@yR-*UIdS*L}IGaE0fF_q_==A=*^wT0<*~RpFK9;_c$m zcW<|LF_n66sp#APuA%i<{4F*ym7=|gIH3z)v27o?a%5>WPo2}4Q9#eVFTC~9%t0@Z^p5ap!X8$7mW%rL zuT4$v2kOSG1Dd*1E ze{mt22?2t=_`p}UiNq=^iJ4oy5+Z%HlFRXz=IJZ*ufbbKG3eRl ze;VFj3>v1iXoWmTPB)wuH=lQOd4H2C`ccC_`qD;Fwm(^eh4t}Gtk$d|Ax2tNq8jw!ya|a{$_XLlD zcjv4vzaX#!_`v%2GKO!3exa$;Wep#1&4ytgut9cHDgou;ihm6(&E7EC4?*7+O+3Rn z@QZH#bw$yVa1K5PF9$B8Z}q3#7u$AtQ}RUVNb7v0C6?=d`|ph>B!%e^5&#ZuYkHMgH?)YIh!(c-i6z51{J-WIvXY>?;wmdHmAVa5nC88^u)@twD4(EqQw3aWVm(81g@&&K_h1yW}csu>r0MNIWTMSXBEe zSJ_)FQlzc;2NDb%i8gEe)>}|VIY?^6-J{6A)~yyLp&mmS3Gn9KrOxKNNzT+hoF6f5 zD(Q-EdnD+D?8dpU%H=2)e{B~I;ESCmrNN5LFCpB;GUpuznA0pfRe;Zr0S+CDK?jI{ z<;+N1t^DUwOVfJX?_t{?@T-#5hb6#wJpO^E_ersh*?4cD{rhz$@PjV28%SVs`P>Dh z3-=VdInh-A^wH+dv=DcTdYExPpHqnR0P$Z#A;9Lk(x5Y`^nYinsv_)A^8_@W1+-O5 zRdUp5u&gZ{mm2Mx3$wSv{L`37@ag_~-dQ$Ez5AU6!-xh7T=&M-dYQx^TF#JE=P&=J zs}J@btA`t^>@Q}1ONv|SqHK0}@4ur*6gr^M8~3r}?em0`8mSlO{Z}kRFb+MTz8LG> z`usDXM&I*3SGJqGQX!a20YJ+k4S$*n{QH~y*RTEMycR#cA9c)^jq=$ z4xsH6fX(qZ(FW()haecjo@tllu4BSog66OPg*Sy?EBMT%UR*>grLK*}Hg(JR?)vf7 z8Rd~@5zTwdK@hQ3F3(>N32A4<3hux!y3i zwtiDo{5SO_?v-dQGksz6ky&>SvkU+GL*Hw!*Ce|Si74<0LuaJNwk9E9_BO1FZ|PK+ z{p(9iq0({r=LU4eF^uU>!%MdjGdmR-f$z$<4DyyE+`@>=_rx-Ji<&!pzfgU5q<*_( zId9;Ly1t>}?yzKw`x-&zasHsKM1%3ANr|n~yNATns(zv5{=KLY0Q0)sH#MW;LZJGF zL`6dadP`oQ>y7&FAF}tpav$q9+L`(ihEpCJkrz6+-YBw6AD2Y89*{lP5E*zV z;FQai7{lI z6e~3;G4w8v0 z6<16t-Qa>4?g+i9a~m>{b3xirMz0I!eh*}#lFFdAiTCIWZ=kp%4~*W|ZT*!>N)*xx zL7#ppuwKM+{CUBPI+?PxrG0}Lioa(E8tv1l=li4^-6!DkTWsAGCWY8=U3er%{A$I( ztLc|d15@t*OQM#O@8jbDLwo*%px5ebHq*3ekc+r-*>mlN=apf|DN6y_Oo~wznb~s+1g=N^D=MtCYJg12-Yn>(wbuxSc zXKvvvNXfaI^6GiHiv=%F@7veP&rya35)MnWp46_x+dH!vT8V`AY>`mg7YLA97CU;s zGl1a^72hXlW@pA_Q;L0#aEt8J&&Z;%wGYE}svC;Qoni>O8^1riqs;!AP%Sh8)Cw1= z;$OQ=@Sx@335Ndp&3SyE=fLaOpetB5&C6IkBPdIH=Cj!E2ZL(Yzakw$~uM&f>74DAIpqlF&;6LzR5ZeK-%(y_KYocl{@Z zpvwws9ebecQ=3<5hOpGKeWLN1HcP>~zMVf5zJ89>+H=PDD;-oRI%p`eGnG&-5`D99Q6lDHC7A-Dur0AD_at{}lzC z63%y_3Aib-I;H`*E90rpudJz8y@6Dg8Idq29u2H~-~CYR&S&8*gRF?;%P&5D68+kD zgFz2ZRLM_s{b=UKoz-^C2v_1gPvX=z@z_Fhn+=8I;~m(Gxh z4^fxjV6U|X)KiU10y}W zuA{v6-_p}YLI*#5TrWo31nTY|rE(kqs81KIE880E&jW`Yl(McGB?_RN{Mbt^WOZ2R z>zj@b93+FkRmwRIXvyz_4jp(r92=Z zE>qK#={k7zpRqJ_i=~D8iUc(d^3$E0Y0@GOsW;SR|2ru0z0)o}mghU^6@o0p*klE) zL3EFl?#<6Qke~zN{Chdi-c;>585Anr#(9gn69t(%qc*`Q$X>+nvu}-MES8c63PzrOqgG1iH!Sc*LIZInVkP>V$CZg z+T+#!-c0OZdsp16^7`#|b~#;|tF^RuEZ-#^i6xMPcL39r*g7E~C9>&5C7w}<_)pxBh(eitfz)q%}n7Ya77Wp9<>X5e)j(o5p;oX`h6xD3yL!7oO8 zdE-b|X*eVubR=LG&G-f_{G_TjAtv#|U+P>t<8U1erY7h ziQn8l0=R5K*&lxYJm${`mEHjT%_rfje?eTifXr}S<6#Z4&D@y0T zX$rjET+>O~YZMAl4;K4*!sljj$lHj!GRRWUg_0A`5FD_ZzjTsVeg_o_Z0?F^`J2WsQe)ml1zaulP7@i>p^AYGa<%rdv93uSrzMkdcl{2< zN4P|)ZPeNCDBf&Ol9%sR4rJM8#jb1kDYjM(i`7UMUR+3;o_5@6ww##qk}VZmM-e8U zV(t=Pf`8oahb2G4NMd{#O{6fI8bH(ciiIEMGMNqZa$TYSMTa~st(o)2t{F5`RE#bvP1~PE z%{bwgY0ul$R>l%Y*k}Y!-?sSj%K8czZC&?98q{o_6VitthXo(N(=wpY8f zl!@fgG^4Od;Kk(e)QdLWLfIFGi*YBdrux?kXg0W9!f{1Y)x;$D$cUDsvLNY2-&}&b zrF*vJ;{k=8=$gu*@($hdtgAr?CM=xv?Ph6jNB(fKyJqcgxl6OSmTq&e{>?lynMJ*? zHRX!yUv4y*M)`lQ;nVYXwIkYV2A zbhigvFEVqm!4`qE5VyOgcit+thnQRq%bl?FXU~3*-7=cwgXFWcXxfjzd1=Wc*fvRh z-L&FAb<(~j&MbkRl5jf3@!X6#)^cVtqSl7~>GcPpvNgvoV#j>ya(%@*fDNHwo z2{z10RDJvBLC&5D#eTSDu3v7m%?85`S-S=L7FTVwUku@uMrg{`Sh(}xkO9oh@+QQ8 z^1!Dvd0DK@|EUe1e!_O>LlKW4sAWo&hLA%F2=XvFq0%(4o8{@_hzRvB5%M)`Tw}#I z|GyVNU(ip;9q_gES>NT54=xPP9viLIVEG|25eQyQ^ zaMS6#d7pon^O}YC!>aSP_MHfFbhfj+^l^#&w?ar|5Oa)V?78luN0-)WwWR}hD=DRa z|0?9xCPRY+wbmc1l(r{U%g!o(pbGYUz!P{>4@dz=eyTgV)>SjpQczt@*)MnL4GQC# z-HqmbtlGbg8nHic#h#+$#V)fR^TYL+4ciU-V>PP1a}G}X<|Z3-SpGMguH~Gt_I;1! z91yg|zn>Eg%M#(Y3{f}NhTp01$tu`kal49J%5$Z|w6D~(F%a@k!utD~)l_^RJAd-u zB%50V(ODft)5n&%6Mf@1gUq9?uZnL}kE?pF0)R_q1DH0!`R(qReW%R-wyF*L*#&(5 z-FvieSdqyZDr3jX0S0nw7_lmdFOnc1Kal&PpnF?3>XHUeq>hmbdR#v{5dZmbbNp!u zM;-zqgBX{>TwXM1wz!{NXe+f`dh!`Q8%H^0*;wBke}qh{BC|D=;L zUFQ_|oXj*o1s15Qy?aQ0pG~LwU!^+3E*Y8mhg*x1p?ZszNuUo&y2lf>99PgUv*90n zz6hyoKcke%3!^L?%cDir#h%U)Ot|L8PgE{PCH*gdqvHw)Uqn2Q%AOiYf3$#_Z^ugx ziG=@ttT_JjK0EoJ5RkzHr+(p+ci%mmqZ@p0H2#t`xBMwZo+Kv(8(#E;qrKM-in1!^ znJQ|D2mA_f@^h11a)e_zeY z`t!tuofp2VE4<23(VALgyQsb-_%1;KIdzBU6aJ^i!w#qy_&%fYWL47UHDG@rpAYRI z`wtI8;gLS$m|wQjJ2p!)1-g}nKX4J#Th`2DX-oefS8o~CfoTDmt7 zr3DcQr9lbl&W)6Yfwa;{ODQQadNc?~ch~4#{`dX;pXbeU9DB1D+i`E_eO>o?ed?4) zndy9&^$$E~IQ9h__emv4dnQC(M~ho$w{FfGrtUSpc+E$VS^&gu;2tavee`*fV7;#N zSnEKa;!9b+a6l38Z4Q`{!8bp0P+jDH_Hl-UE1sN{#tSo}YKELSB05;z#j%YrvfTVKt|+k>8+M;Fh4e+--Q^TG@K zy{rEC$e~7p{xajI&q?BAUkCoY;?n_Ba?&9GM!Ns0Uf-#jIrU2miZK%`54vsFafk$* zE6Cc|KzTn^Zbbg86=)FRFR^2t(`&G2oMFLbyz9~W9X`ovhf<^p>uGrOQ&!8VmKlG9BwQet#-9WLAB2_b<|t4Sy|{6DaVHk=uLZpQRxvMB~bx zZ;Um!@B8$=+{&sG*2Jrz>3E#`xjdv2@ZIQPfZt_VE8>6TXv(d7qCtD(dFy+ELyr7!!?3-cUN~<;h7m7kJwsm4e zZer8#h?e{PjcF#K%$QAypgPP_Ti3NUsCOsY$d)s&_SgSiv!UJrL7F=8jTxaH{e#P6 zJZI{zH*B+IGI)#&z75nY+9^6d36`}L&!=Edxs%Q?N^Kg=LV|gA!$hoo1#LJAI~S?f zDVk0)XrDjq{g3@D!H;kQ>!AEg8-F%d%Fg5SzA-%&lBv0j4mm%O@0J_>7r(pz!Ib7Q zO@ASN45N$w!zP5i@1C7#N#oeF^%V}?`0!%kuc-Vcby(4NTz+NiCeKdak04Oo>kobl zc;>hAxXb}@Fe5^3ZCw-kO!(Z8I4Yu9|E>^#4Md{GslKgcSWfP^K%L=+{cWqb`LH2L zY|P*;@Me+V=ooE_)=H%Z4ZBLzK4V`HgLUuB^`}|)RGOVnL_Q0pDX`dsK&&J zY4(3$yw%x@HvD|UJ$vkTT7uccGYW)uzPxK3RQWD!dZa0v4xGe2DUf?X1R6Gj%creT zo8CtXh^ZBf@J(%$Z9U?=(N6u0B=3tFu@JLHBq@F(r+eAt(>qRWKY61?vfj{7`Mu?5 zcNc{0Pex2ZECKhNGw&f}3W~>dtLmN?ee^jOZ|6aK&&4;aDTCI($?{Bg+SBwk;6apF z)a%bVZE&ir1*A(5i`mF1jj)I@+OSQFl_81b-l6U~@_ofwy6M!f^O`Zc@6Z5b`x$2y znsGCCjqxk~(1KX~rp~`|;P=AV)bwBU$P|x^UiJI8_<+H>u}*xO$Mijg#wwF}lBsVo zb{;=E=1SzL{nm9wbw+AqkdA#f7>#Vi|F>Qw4Sp6St zt4r^Eyo9nRN}cBW|GBk7VVA1px-<8_zWT3c`AvAZc)^9P5}4Wu20-y{eEewKxYlz`NRy36yWQbZ{)Md1 z(IS9=)n%;b`>R-b*I$ek)vwE1lkpB%W6%Q_GO?u1ev7xFp{Hb-M@{HwtZzg%3n=tOgg&cCRKLlP!opfkY zSJ#xh#XZPUE7ui!Ce88ky;bQ#`#F`g9al*^&#Q!0G5q0f&dl;$>y*yKw3iFrL z8rAX`sSDG;zJMWr<;F|I^1Nl2D8cJkc2t}32^L3#wg&ibTiFn^WG zy?y*f9?tgI;2{F-Y}rfBVAwiA2?2O2EvCrZA0k)lP}_znjjfRUdrWPNDg%{;N%nG2Uu6eX2O4d(Vm*_>bu!meGTN33{9rIVSm$>- zcYX;%uT47#(Ks?q=|}6|xq9+VC#<6&6uhbISMG5{QO^`v{h1~AdZW`?P8gdfpp^^K zPkZqD%TK3N!|B%$-M~SahkF6?^B-(LlQFLzsix`!>lmHmUw^oz5Zuu$$1}L%*^Ds! zPI|$PzmfZ|fr~qus#V4JBF4!XW!{8mNgt9E7Ttn8MQK?)5Csb>OVilZNNP0WvyDLM z{hit$*5Fbj{Wz9?nQ)q+7uKGm`K=dEq_5l0ZBMjTe_s3)9OfoGRvv5xsPhX)f_I&4KRZs~m&+^s z-L(DDzxK8hx>4V1qtjag!I#P|s^=n(Xf^9|1`nPH1c#&;tF$8%eNKNgh!ufBKYxRJ z@zbs^8g-!hUZ`3gV6Oewbq6BE;`gA^Z2y0Tb@2;06^b-d_n)=a^1-Ed3Rs5=w~h8V zHtkhH$!Vym=Ekc66nQD;COHrHRMUKk$w;R8X!eLK5XHg{2hdw=gXf0ciT&cuRGUXM ziA`g9c2$q(@Q=DWVf(H4x8VNCzPiSez(=6Px+}DcA=Z+yGRnV0RcYAn%^P@w@5{?! zsV7kbwWvfLhwr&3|2l>H4F`W&QnTWBT=mNQvuQ)XS-?M+Tn&DkOD~|lffK*VR>kZ8 zUMO_g@3_F;Qar5N&}MQ=AL(`#*7(IG_)z|aw^o}g+k|JMmWy>a?vc#~a3`yR=gGHm zZ^Z3Nem(Qm8jemE1neKS4xobH%oDe3%^hU870eM%S6V6dZ*$V4Vu=*GUbf|fP}+l* zk!aSG1evwX+NUhnuakBk8bLXR_2&O4RapL&BPYKE|55`TO6SivOx^vU@=HjS z8p#=#!T>}(CIqC!^t2N2oO|SmhmTMTHORrumm5GAUmLtAAS^6WY9HQ#Or?*GFl#!n zT-;aN#%w6~kEVW5LD3QMGwj%fZh^e|Ux7%+g3cR*jxz7fIUH-F1Gw>@*hTIiED*c2 zbey>^vbS-KY>~?aVte}*+P90nk65txK}xaJ<5q;5M^Ja)K*f&gDQ3{a4+a#s&pid~ z%cvZjZtHLS7FN(?m`fepkc?0{_Fu*PnL^H#!SU^$)9oMlhC_E`>epKFwJ&L9f9MMC zuPqWR>r(=!CSL(PyTe`Bh+Z_zDO-9ZH;9X+dD2n>#WlfQgFQP^+rRwQ0SmW2==;EM zHkj@X?CSNFtCX_|Y(JX;ez4_!>=U`aaeX>VKIjUD&^r_SSh!j6pkYfXV;1XD^26ns#H|q(EdV2647b4 zed$uGP^WqOQL1fV-saj}uV_I=tHI3mP~MhVO8*_+sFMxyZ{Ej&l{t3~7q4cV7-4x5 zE?S6cUNBTIC@dW?GPbVQfEh58KfV+6Cb_zz92BtCb3hE%W;%6?RO_M=8X(!B5{Dz zYAF6}A8KOR0!C)vyu*3SZwn$t=TA1vLC!)Qs#Iyb7LBzDDTOQM*|(dX_gpkq$fZja z%#$Lwa#59ft<~gk(MdeREt|k~Nxu%tAClG2XJ+e9`_N3#OS?Pnzw>bq3jP(!# znYd*BkrSc_%y{me$z+8cJ{vOW-&beet_TWGSHmG~g=9fiBPx4G`gdX-+|^QGK0s8AC(C&fU|Y+^`sVmj5`V0(Kkk zgzy?Oy;;UB8Q=z!8axp^b*U^MQmBs8km^yTN#;J@hc{w-rOQMyRY7QB1kik}#`d8l zzeGDonl@bd7*=A=zHom?8{+u>=53YI3V{aU9Y=foj`ujb+virw4F~z7yA8K%4RSUU z7T}KY!-wDf(hAjFWI8zNu&uh)==X4GyPOtYb}1(Z6P-M`bFuPXvCaNYn1{*dkhUQ6 z9wi?&sLvZ55PRROx&$O_z&dpr-63m6XVh}W`OQK9AcY>YA^&`no_X+jDygK&cSoP9 zznR4EhNdG5*2*fcl5nFu9F!lsjXrKFtm-i@mnDNfou>avvF?EDT;MlpXN^9ySEJ?6-X?$H>y|eb+gv&sSnmd1 zKXSUAZj75pziSR#H~ox1WkZ9;(E+VX0rEeyQU2%$ znLera*+X5g?cck0o0csNYzPUx?6U-RpCy^fh6=Q8J#oUwn3s-`{!Q3;drp;tca)|j zfa|b>=FMx#3!=C|b5m#`Q>>ZJ$GtRkM*Wy@ai5JyX zydY)Ts_&_SEbb&byGCtj=eX-WeB+qy&w^dYk(%6`7CJsZ3uA2hBOD=3!L;zzT^ckI z`$*4M4_TJc7UTjm#6oTXOy+(IPK}t)$1Qe{JRSmHid8<6Kcq_2Zba=(LTOsa&}+V$ zCc%$1F;bsEMdlf5B^=sdt{U%+l8px;)lLqE3gQWXO7!}VQSGc?yBrER=wmirw-b11 zNuYG}0Q=Khv+TmX6J_}qRC3}Q%J+WLadRG)*PImwY50sTC5%zGmLnEVKg;v_esZu~ zk(#|8F1ko>H#Q7&o3G71ZmN4^xp7#2`Gc-eU>Ph?@8JHLiyV_PtU&i@3VHkm$ zD;qWf37V$4M7-ZM%U~Uphf1f#hb0%vtFa%2q_AebEjNTk9_Huk(?_VLegg+?hU|&PwG4TC9FsR&O?1SBfmBZ?hh|iV~E>&DA%H5+nXfn zfu^1eQlZwy>te|1=*HD9+&QucIH$Z#@jdXVqy+oxl7tyv6ffqbZ1Vn4G!%I1fZTD* zZsu7gk`{R+Pepw}5)AZZS-rh;A#OHqq{s$V(pQCij=Z?Sw8C*}@PqT6Rv?{G8&=QF zVa>Kqf_2u7WG$*c>_sH^O`bgAt7?6*pMFM>B6n2hsTuaY9!DgcJrX!5wa|iHq@VIW zEVg)IhtG&!3+(?cFl>_(Y|#*ga&uY1^2wXcd7tY2XxRaldMTcVB|vZ|mrddyvh9pMQI zSU}df%oIdlOJY|EbVL>r<1f3 zl&>9!q=kQ)Kq-L2c+2|$NkqnnzkEGib6)Zqdt45JsJZ@{^5*fo2ax_`$dU+WKo0-l z^X(dJ*@4@vLS)4et@Aof#Q8Bs0&O%lbpQL&xe%HdvMs*(-XZTC2w#eyDRJ}J8ZtW# z@iz;GVPSdDmKk)14#9~No`H#^f@F`JCkKNCcV*t2>{Bc>kE3B9{eCPZPa& zv14`z*Jw(x1rKyaUK(7tDds2OofCDlLZLxkBsSBjm@B8T{-%ue(6H#(7ckI88+-TlKORch$U*mkA zxGGbP(ln-D>NvVlD2j$+1_oy-LCR!Dgle-sGSX_ z=!`V0e#?y(>$fPa7_TZv&dKH(ef+%RomroR0rCgE6vg~!765XiAtd0+B{%SQ;v5L* zE3z==dh~;JIhTBi1{MA4`w-jl+tb5i9!+0k?9j;`Ze`X*p-%3Efk6D4$&7S$fgqBO zc2Yi?UI71C;ZYm>d+{2c4Gh>n8ZCjYzzdgYwj1go*2cI}Vf%xd=#j1Z3NF*j zUoFASWA209^gZxA);1cBf1JExj&^WNl0P8P+dJVmvZH}QDdVqQY!oFmaYWhNgsPu< z^)Q$Bc1>G!HRJNFO6#W+Sqycj(9xR+okzuM0+};9kcXd85#)^{L8u*n7sJyKwoHx& z-_ILU{`KE^5_0wGCwZl@u+b6MoIC6(uP8kH4;ak%yrA92ZNBi!khMFn$W75;D zRb9$;m5(KyE3RV<39KG^Xx_DBL&scS;{}CN75U|lHy1OGbObzo^xs_sty+gwfR8lo#%ZAYykAQ?lrJ zd8Z0MWqxNevVHOP@>GZ%+oy-~Sim;f_E0CZ4UcZid)MyAkTMHA9IvqwnEB9e{dneZ zkOJ0HiM<>f2|TGj{(Cfmuh?0qTM`_sWKacp=U})_9H_Y=7)qXN^8HDLC6z4I)8Y+) z#O4|6V*RQJw-zh$yLf}1Re?kIwE8!WLD$)RjH}O78-&pky?wJ+TUpA@!OppUE^@f` zorVz_^w9}PZUZD7frDX}Kv&)Jdj(3S?P#35JfSa=tL4>MtUnQcGChz@DJYg)K&L%c zB}nnwTaWY#+Xk+NE#4?BEA5bvK(8Rr9|p8|V|W@bQ4mYxmQ!`#0D_hC@I`VKrzr;A z(K7;`4Q?q$8k(1~e{ZIu%bi3jgaD%@*AQK zfVwD%CVTf8T=P`Um%gUk@ZM1j4;fz25<%uPm;+wtCp0#PE{K3m^ipD<8;PH){Ugxk z*52iQ0%r+)M`$xj?s@5FzG8qo{UTPZ$1tOe_fe;R@Tfn)1fa~!!2@`js{K0m*De^5J3Lj|IXBwzbnbIg`84SUS~K#kB7wSTSxmZNnMv(Mc}$X2tI zZzmubYVyt>dH4rR@>?JEloJXk*sgV|xlJ;NVRoLndj*cl5IHh7s;smkgio>9^Amn> zkle*Lg-vc@lQ5I2Pa7X1nb)izjSL%`oAiD*&u5?TE5!eb+{oJWpYUU+lJ!e$o5Cgf z2(%?l4rBvh1Iz%{pCP(B1hgl1?NqM!Lw0<8_|77gK>s>`U&7|EvFQ$@^62N=6G$p2 zs_J_`HyT?FsaLqG3*8d?bkke%kEqz66C|ny4*SX{vXJEx{K(z%s#Wh}P$r;qPFWC6 z?TAk%aw2E<<`$w^Bw7!Xkj&b1t3uQFS}i*|joo8@D#lAPp%g%&6FB>y#w?4L%#S*fskW4UTXQ~fWv$c*51k?Tpyx`)k7-mw?6x)w81vjuJbtu zgc?#}14wu8J_sp<-e?1=WTU_WXPY71j?!s(Kd|e4kI?)ucC#M~rzF8A?oXI{JVmOg zU6F#!^NZ;9k?6?cgtJ|x(sJ1(Rosl9x=PmJNH{t{;ykmXwZN14jXgmA`;~wLh z+0Q_}T=wg{EAq<~VIb_x2g{EGEFzAR>ssnP>eEEId|a>oIwUhAw(2OBhlqZ2!PeOt z8@n6V;kFZz3GX}_GbfTA1a$9$m^0E7-Q_3hu6;&CN4!Ls^wd@?neK$neN54m=hWE{ zf3SHE=qY8vBfG5?eRCNdSkDcRu#r#v?Sj-nuwvJDXyAVu(V8proQza68iQ-~&MUka zuEp3?d>nuh0@YfK4p2Z1lJ~i+ZiCQnk;G68c+s6&;OnhmOq<7ch6#ri;eRk(eytkb zzy$97*@_AH;Kb+B@{QPUK4bKE;|kXYygv^GWZzmID3-7B9$gY(H0?S3x%k{N_L6s{ zc1FHz*CJw2WK-o;J6Z<>IQeJhJ^Rf32n6~O#YCXL7)8hPg^(W%Po}xXFvTiGCP#^c zFdBJ|QkX?>C49aw6c|mq@vf0uTBLee_eiU>ok>_nL%cYPgob*pxCiz0{u=iJD#&U9 zG6>TXcC_YOh>mws=Xa4hW|_ddrtsLg3oge1A2eLGt(YTOe13>%j55G8W$n=a@bwaW zWbl4`^X(58mzG(>S`Roz%EAsu-T+psPly0B9Nf>V?bwa0h3qt+uIr@!`yt-nY`EA2 z$xmcY$*9v#`mAP#owz4x@EEaWP2U$`xh$dr7$q0+t#TRNO0In;Ms?cE@}Zxd>?r!( zY+U^~nZX4-tksE_{yHDt7#Mg87<->IU}0=!)MvDuj+aX&;4`%VAAbB;|KC`a|6Oz+ z2cA>#!;de2S%LSg;v1~RUxRwv>*J28c#`N|BqEc_4)|l|Bo3Mp7FrM4_pTtoOCL(h0EK$SKhD3`u*%ZRcY)e zCYw^v^3tSLtvqCfGMsfyghSAb-Ck}F--d36saU8A5 zm$KG&LQtRTUe^p+h-jeP)z!w$6cCuDVK+A^6i6(L`O&M=PGa4_w8kXQL%R@kpa&~L zEM5#KYb!f@TED>K+LnXY6D?J{L7P^OFnE{$Oa@*Tta_}&GN{HMRC|LbQMi$wK}huH z(bN@w7y<>#cLJRnN1E=M4mf|EiJ9tJ(`K77m;y*zAH{tm-Zs&LFT0T|iIXTNym{z8 z$g^sunMiI{6>p+*kr&iNM|++%Iz>L`dl3Q%{Ypdr#Y!+kvt5mh27}6zpW_fsG-n+n2QBlzyFnLt8&TF|%dr&#pFL#`Y)Mu@q^oP$2 z0}*(0%r;|hZWrvG!p5J`K~3or>@etW5=Kd*$osfmu=-rNd3!|)?1@&Lq5Knrc3FFKwRzj37l0{<(+m! zo)N5B1J%!BvYy1UQ46ZeTS6=O^^9v)Ww5nl92c6d92fpz?G9&#J0!|?LVvq-@G~%p zq&O{-`#4?i{&PC_fc#_&n90_#^$DnuKnvZDX3{h){RVW6BmkpmxC8I3Fo6E1oy^WP zQ2-*kmSk>|2lIvkmzcUyOYhu_9H!NqKe_KpP7D6Zo$zs0{`tzct?gVApFyODpD6Yj z#a0zt&wuLo$3YPqeYD=rq#3xH;H#J)7VD{&@y`!>6B?C!0|@LpgFg0Tj3~KZ;fbich#b1r>axS&zz!PvyMB z<6`BTtE&pRJ;D6g!aED8k8FqV5~@G1uIehS2O%2fwf%sp<%8)IrqtZ7-<5kQ3a1J& zW7S69A{PZ9l70&{t4HQ{1Lby!ZzKe$8S@7|E#pb}u-7$*qLJSyc8n5MSeBMaHT1+%%{37Qh>Pk#GO&LA7qS4*W@pET%_kmm#+sc1^e$ga0-3Q zB0a9>y6Y3Y)lHOV8fS@ca%yxi3(_?1_`CO96HIDB zZcZLZy&9C*d#+C)6~Z}^?A}E1Ho;8Ta_$hmJnOjt1&4rY+N?7TeqwrHthaj&`H{zk zVGIurV8|#E&kotLLqGZ{4PtDr0v#XE;Qj;AYn@77i$}tkyCqBC&S#7K@-JVOe7Tdw zwcYXKQ7CqhjUoGG_gmB#gXxeBPo`m+mup2bbzu)KDB%oO4}8b2F1nq^1ILrs-U}mA zqTDTPrd}Opya{I38_TrLZM&{*@c&#`nbrL|CYUFXu%Ayd%Qy)wiY;H-%EnVO{0OZsnq7ZP-FdL-sj3=fP3DYn33v(XKtrT-ap3cZRF+WKFfdbW(wyxjjGZ z*ouMl#Z)lQp0wdfAYt9qR->=0HS@A<=!$4GTbPWCL0J0iZJGaBtLfC)V17eO+;P1y zpo^3>$Z5mDW&Me{%TOX>HQ-p2Kk}4t`d_}JWd3xtV&cnBVXXF_UvEL~nnldk_;E+y z6*=h|Tubc((~VA#KGNR4v|NZDibIq0}eE&g<2N)539u{8dQcwG>NG?M!=jdX7wL9v2^V(~in7O#hJU zS5;%sFh%ToI}WbV4!b^OF^c}%+r$Cdx%-)oR~2fTnKFy7ZNm@pdLOpp2M!=#^7zL7 z97Y`LSjfmLb9A(ux(T*=?oRrbu)dg!rmDT z>Nty^Ossb2zsC1*&yq=jo^;7rj%`GL(Z4u)gBgS}9+9=c$VGFkQOD$tKGxL5)m$Hlqe_wB( zlbXR@qpXIE!8q|OX&U%iMxJv=%%Z*_U8F3MpZ#=6Y?SSZuU4eV2Gi2gvfcNa5TWH# zTNc=&-{AmEAzWM%%q3-|ypN@&#+6q1@jivPgzS`>E>Z+rOD9Ok&Gxky>-jc_ArNrz zX!e8V_kGQ``A?@DA6@EoyS=tIzvac5xtLFeM%H<*;d+;M^6+9KqF@%A=5{2H2_Ao-NDJRY8NqvOLbOc{o{Q^!{?na#L~7&%k3HFxxLvSS0mJ zz-)o+88?U{g5slZkg9y6J-x@^oM!7(tLN2{(+2q183AsJzVEEm>W2UAccJ&)JRO@=$1-K|ZEg_GYoKQctoEyOHwS0IK}P5&iuks1H^ zGiS_9`{srA{i1$5CG!0~OGe~isPk$e=DifU0MZ@LA%>+Lf@1Gk!a}>B)eYUZf~jZ~ zXe^`5wa}c3Bzie<100`xaT-Tf9~lTXAW#XvZHQsLDZ=oItHVxRo85~jzqoItskS6P zTpr09llt2ju^VvmkaFw3z4_B$&p8T>Gsq=vx(Dgl<+o_~Brhkv;DS3idQvUVq8@n> zdL_VyyOp0G(YVp`iriZLMfi%wQD6~4U(A;SWJoq_;l5D3ii__-H=E@PCdz`omqm19 zo^^4AZ1=xyu-3gp6OG{e38BA_vXdxYlN880g(7-GEEL` zgR`IgLi^>Ign9G6uG3{7PvgG;`MWM-KmlqStL|wD5%GFcqP`x(~X*p=Fes+4ddlz)Yrv38s9`6!&Jv=)poF3Hf7XZ^hY9q1ceqQ%KD zB}ugU+WG{IsC z9TMdwx9#b8_NN%-Hl8<4qgHjGJr4;4*MyTc4!I#O?o7)+`%C0@=Z^3Cblh828B{v< z2qhEP;j!_p`xA!PqsE%n;;(8*s}39IyXBb}5;^@5#b%o{*JObNrO~^Wzu4B4DP=}t zwcTSx$?2I%i(Z7Dc!cn3KU40Cp#S=J<)|%-(9djE_Vyv`WYAoLbMF^i56cuFWHVyN zaUHR4GG68XY+}s}DjQX4v~S&?RqYji7x?V8Ujp+e4WE3NSI_JZ*4kq$bD!ewsiv^J z=cvvKe9V-de?*n<_Uiz!y8a>LBmfYE1c*S|XH19DzwjxIVo8i^-N91aykPS1FC#3D zak0hA@uHQ#{IFLN*rN53Slv&fLl=3s`U0pwWMljYztMxd<3Ua!;c90;_iNmo8X$dF zcuJn@vcty4+=z|`mF)p&vH&zS#^FvUji&=mX z*8%s(o;OgAH;cosENUO=)ICyjtKn1#tIlT&IB;8x$7})PpraF;7F?c3&q{1 zvamnG<9n_q%{&Qsz zSE*=8<@#T1@9ej9kYECaW7NiXqR+Hhn-yAOPyN5ei(i)Pa6m$LO7o?e+`>GZl(KA5S3;=d1WyI?cIMvT%Vbs6VCLh-2*S zS^mLw+*W@#Rz0wi@g!hN-TZ)N|G25RPo#3Tq0Eyn$4#rE`nLRg*KcSU4hcsyuCbW6 z{66E_`{YN|F>7pWJl4sU*Z!T9tJy@VR_y&V)%^&zU7L(~l|B#29+3zq!QXzpzeZ1; ztUv$Uw_cL}EH}vxgMXi3fv4cruR9#_?ZRVvgh;JHVgurrrU?d-agjDxS_ZR;+shqF zIIL1GwODwwG+^B|asN~Iq>BSf$@0A93I?B^daGgFwrzbHzIy7kH4k;+znPo-6qu1H zq$Oq=W;mJ@=3e`C^}ef^bTOrLT-DcDP-QlwAN4kWrk&z6E!v>N6xtV%a3o*_>5IUkL8M(+ymr>jpS4GU&rV_*HzGtlpQN3uw@7y$r0|+g>pPTQ#pq$8 zJ4tp2ScS%wtlv*qY8&=%YUebs0nSBoO5X`Mn#d_bE`+bdW-q!+fBD@~X!^aR5iZS@ zDU(c-Hs&roR`ezVAQw!#%P3kuTbYo}1jpskQZ{qsTx7yd|0xH&P;K;vw3vqIU_55E z&q%0t`~NC@b#tDY90r|n)26OHlK(w`{T(s&)jzkN%dP7%;*=u8*auSxY-$9fA2^-g zIMht{IS@i$Pb22Ne#)x6?OUsfx5J7g4n(jUUn-7-E4agsLlFtN_^tJua zp|A4vqpCUOpaP#uru`wb*SD?$+t%i$g$fife;MyIzj5uW=f9C5idTut=MirT1>L5& zG@GZn&E*g*`t4*N_f!xBg+9jdJX~AP{3KX@1}44Ytk1=xD)Z-AGiyf0`nC|^Y->3H zDq~_sQh)kfLS+lDdKG{loXVI_Fa)0lcUq>Eq)8Lp&QN0Eei1y6wB>yXk|Wiu0-xEb zxU~EL1Kl5Bjij7>iRPQn^__@mr~+R(ZheEVpNjat2!{WIIt@A83^I}ZUOUoHuviaI z_jvEgQu(EHfEmRQg)QeJaN^?S*?;1pJ#z0Tpu?Cn*I(L9JtS~fBA~m~&Q^f@YeMbz z+&Cebdc&z@_xW}EFSc6(poMz8JIMx#K(~Aj<#-ZtGi{`ZN2!zyfd7ux9D}ih^p+8Y zJGs^_QT37S^E|0JIC06DG(&D)d`YX#qfh4>`o3`*`GC~G7S0aywAOJGoP%FPU)7Mo_=*X(!j>7Aqk zL^m3Nrxfx0bgsw3@Z{x9+X&t;m3xKewTPGJ;N2)oR`$)4VUgZn-|TvI-{0!^CeM7_ zeBCYM^H;pl;UQenEp#4;)o7);y?1+bDAcHrRzVKd17^}re^?;q;cH@~Gkh+$&r5=7 zwWkOPKV>REO!ZG%%oClxJ?o?a@=A@nLXUPos_@^L(RQxumdbUsJ#ANLNe@{if0AvN z!Yxb7rST86Kv_GX@umfVj#pjS--~bXXKb%n*%FQmMZ8`7WAJv*=x%%rE+^z}-Eic{j@+xQqSayZ&rYqN z3QP@|>hI3!40*;3XgKYd%lTAmlJAehRy~53RS%FtYzZ`c6^5A}j34yi9uS593S+hc z-^F|Ij#n10Z!fS}3pP1bc%rm;T|em9qhDw!5|=gf2~6aQzByg#tRQI2dv02^$f`$P z=?VJtSqxhJE5?*XO$H%w-um`HO|Vfbl-M~deNX-xvuopca`u7M=4WJ-pF3A%t3Au! z`A@EV%8k_@;Ftl&Rn!4f9{@vR0K>dq0_iP2W9Io4G+x+-f;D=mx!&%+cDnr>IJEjr zm9%$(OtYzsvf|O%>Hx_tFU!Ls|Jvw_7h(QSY}oL64#E^$Hrlr@9WTi*PEgu)BNfzp z)dSC26xkBvb%lCT%!A|Hnuzaf>UKlb3HC2ARgQ-1WJ>%jZn@OgR)=(8BXb+7f*pWH zm>(HyB}o}N_3H%NDXW1&5_NC^vedo`1bg!0Fpj~m=KB6oH|W6Z5T=TyV-DG#8vF4U zbU{{txcmfO`dUwkdeK0}0!63^1~8Ep(XjO?2|OG=Sezd?kN5F@NBri#mx{{EkvgHh z?hvf*7_w~ZIUB3xDz=}XHcPJd#uS~M?9yj`i3`@*-Ed<}NSZuOi$7$TTroMF7pWiN zDi2KRXfo(y{|lEckY;QG)>FPJr4XSTi;;Byn!Xd@v^^LiEDL7~2NguC5xsyR+om-o+ zD2W_T-^9@);!LICumB%o7s7GQ$OFm$eW2lb@fh^&?Qw0umBP*58+jlT=wp2>J`oZS za3_cMKS|A{S}XX(Zt;AZkS_^1Y=0qn?^olvxQ5NYed34laSl);e1E&vhGc;Lq4U}5 zu5{3F-Un3O`@=EGLfgenA$JW2hNRsME-h&?p2_@P)f-84n*IPsTI#;`6v@7DhZa8c zhG%0HFj??mDU%$q-uYJKNBqZ6a{UF6EloXCBum>d1lM+ED_9PPVf zU+jY)iD2eDcQpT9?fI;4p_|6ftzhxhyMkNh*FCKJV+7a=9q==MYgxmCNhgl9?{9T) zJ83D$NZD%*-etd13lb1Fc&eGOT%&y@BpVT+zh*EhZzxzlLh+fIVCQ+&|H5(DC+few z#bYC=us*p`Fd!%ic8rdcLtv$5dkpcp>9JYn(>w&jmL4AGB{XcZ7p8U>mXGCr$2@$n zn?0eo{@&o$PjtWJ#9j}c(zcPIJ%?8fp;KTR8T5r`L#@z1I(+iGvrF%b12Z7Zw3P zz%!uH9se!m`N3N9wHK~YuU>KH6;b`AX?Xf}%EZgI@V+5k+WeW{;w~xRMi!HJz!5#k ztjVI|9hZa&dm()&audaz#G7tPyT>~(6Zf5DO+~`6mGK&R-M=gnbWK3_BOY<6MHk_F zM+^!TCwg^Dl%Sr(sMPNF3}S@Vlt{ky0C`{jmI~yDZybU*Y(PhhWmk$<&ozd+l>P&tPfl|a^go-&&y&2D8Y7J8;~0j|RY z)wCYs-GuoRBmH;MVFxubz%mZ)c=+wAFg(Lpdr*mm=DfHKa&JYtSnn#qTK1F6;w#U= z6*sNuWch`Fgqi7AvyA+(L%c~VOWKWPne67tV{Cjq?bF?6`_!b|&*T5WTL9-kxCCOu z*m?e-WPjmg;;+L0%>poO=Sp}DxG%dOMb9IMD*U_q1Q{RXW;gLz$I)W1Jr!@&3;Za$ zZc&cWzZ_kdlKjD%_8p&nR@NAwyY%*$hxkWKZ1R6On5|(nil-M^kB(o%JTI_LTZ55Q zI`<4tFFTRiLd+?9DnrLM3_80EXEate(|M#rK4!-x z5v|jiHLXtJ?zDT2Bza>Oz2!XlwQHEVfwh8eP0II*cuoa;s_n$Kbsitt_GxRd#v%FL zm9_ITYuV63jWG8Y9X~6^%7x2 z_9dr$H@l3)i4C4^nC!<$$#;$Hb71(bposCuvrxNmQ zal$YA*vU7}|KdKm#2N*K+e%vFAAczKq-F2oK3XP8J?z3w`SJc=6xZ{3#1qxm?moY| ztrP4YDhvv_hXR7v8G`@A8|iHLV7zT^ucP1NflJ=DTCISeGf7yeC^p&_8o=I&*WVHh-|2-*GNQtb68;E#^n)Q989C*T1xY zulA!bgBFOzfLY?uzaEZqEX(cE1Gr~8Z=4IHx1@zGY)$O}4;%pkzul&R!ml^$qbMd` z|Ezffs7JeR9WA=e?r}w9YuY@(p8PfS$CGD|JJ)Q92 z=EJ~Egm%XaTDFb1C-x``qlV2PzZE|}N;tTfK`1a!CA`1ce_)2qRO#}K$QMP3O`!P+z=Ojk7G5su`CAY0rOY+c|*}-h; zjYwP0O&zD1_>XXF!D$Iox371ToqA$J2U;ETC)A5Kw8V}fzB-8t1XH4~AY?BhbN40p z<5|h~Ux{}CO1Msc~lh!bmM zpK1C+BJANyb@`WW%`@>g3~3>y}5&l4B^G+(ZjVn%;I zvOZ<;m2%mF0?)0n;Sbol4tSpSlEeF}nNkY9Ibm3OZonDq&8y6f!}u?H0lgN3#GNwN z7m*~O?e90*pcRakqhF54oWxcH>J$=uBlmf%w|+|YGjl4mErvC-blYw7d=L48>xxt9 z>Xsmo_4FGiyV>z1L@M|H-AP^G5Sv>!}_x0}gI_G@h3vJAqwZh|S%dDcY z+4W$93hW|H&Zns)7^3I9XOv%n&5ZU`xLq*aiw~1hR57ksVoa3s2sh*Cy6pF?PVU=z z63?$y__B950yh-i9TEVx~z^GP}lVvq*s=Hr+H0F#!L(c%g|bQSUXi6gDk! zbxq}hb%Wq_xy~hl#ny-lvNiX^H2Vlc0sGk|?PMqUCG;UznmvC0HSs}J)eSw3uCD4O zJKLpOp$mejYau=^wgL2Ky@c;U`X&PcL8DXJBP2$Y2}|9}fF7lQuD4IQb@rrmYhxj{ zu1%#|>y@6Cc0Mt`k=}gq;yVuXSH1aiW_MTJmvnXB6d^Axy5E15Z!ef

v8bqzad$ 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 From 22def9b01c8c123271cde53b0e4ce7d76a0f5416 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Mon, 11 Jul 2022 20:18:10 +0200 Subject: [PATCH 29/31] frontend: Don't copy resources to language dirs Since 355e89ce5, the frontend references resources via root-relative URLs. This means that `resources` dirs in the language dirs are no longer accessed and can be removed. Achieve this by defining a specific `assets` production config that doesn't include `src/resources`. As of fd35c8f4a, this shrinks the frontend size by 55% (279M -> 124M). Also, the nginx location configs now can be simplified. --- frontend/angular.json | 4 ++++ frontend/package.json | 2 +- frontend/sync-assets.js | 2 +- nginx-mempool.conf | 6 +----- production/mempool.crontab | 2 +- production/nginx/server-common.conf | 8 +------- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/frontend/angular.json b/frontend/angular.json index 4eb697071..1ed29cad9 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -170,6 +170,10 @@ }, "configurations": { "production": { + "assets": [ + "src/favicon.ico", + "src/robots.txt" + ], "fileReplacements": [ { "replace": "src/environments/environment.ts", diff --git a/frontend/package.json b/frontend/package.json index d2f7f2f6c..b5055a5de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging", "start:mixed": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c mixed", "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets && npm run build-mempool.js", - "sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources", + "sync-assets": "rsync -av ./src/resources ./dist/mempool/browser && node sync-assets.js", "sync-assets-dev": "node sync-assets.js dev", "generate-config": "node generate-config.js", "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js", diff --git a/frontend/sync-assets.js b/frontend/sync-assets.js index a6b59bdb1..8937e2abb 100644 --- a/frontend/sync-assets.js +++ b/frontend/sync-assets.js @@ -4,7 +4,7 @@ var fs = require('fs'); const CONFIG_FILE_NAME = 'mempool-frontend-config.json'; let configContent = {}; -var PATH = 'dist/mempool/browser/en-US/resources/'; +var PATH = 'dist/mempool/browser/resources/'; if (process.argv[2] && process.argv[2] === 'dev') { PATH = 'src/resources/'; } diff --git a/nginx-mempool.conf b/nginx-mempool.conf index 58d45f3cc..a6f701478 100644 --- a/nginx-mempool.conf +++ b/nginx-mempool.conf @@ -18,7 +18,7 @@ expires 10m; } location /resources { - try_files /$lang/$uri /$lang/$uri/ $uri $uri/ /en-US/$uri @index-redirect; + try_files $uri @index-redirect; expires 1h; } location @index-redirect { @@ -27,10 +27,6 @@ # location block using regex are matched in order - # used to rewrite resources from // to /en-US/ - location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh|hi)/resources/ { - rewrite ^/[a-zA-Z-]*/resources/(.*) /en-US/resources/$1; - } # used for cookie override location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh|hi)/ { try_files $uri $uri/ /$1/index.html =404; diff --git a/production/mempool.crontab b/production/mempool.crontab index cc1bcd878..0e7b6af6b 100644 --- a/production/mempool.crontab +++ b/production/mempool.crontab @@ -5,5 +5,5 @@ 37 13 * * * sleep 30 ; /mempool/mempool.space/backup >/dev/null 2>&1 & # hourly liquid asset update -6 * * * * cd $HOME/liquid/frontend && npm run sync-assets && rsync -av $HOME/liquid/frontend/dist/mempool/browser/en-US/resources/assets* $HOME/public_html/liquid/en-US/resources/ >/dev/null 2>&1 +6 * * * * cd $HOME/liquid/frontend && npm run sync-assets && rsync -av $HOME/liquid/frontend/dist/mempool/browser/resources/assets* $HOME/public_html/liquid/resources/ >/dev/null 2>&1 diff --git a/production/nginx/server-common.conf b/production/nginx/server-common.conf index 26e81f7fa..dedd36411 100644 --- a/production/nginx/server-common.conf +++ b/production/nginx/server-common.conf @@ -58,12 +58,6 @@ location = / { expires 5m; } -# used to rewrite resources from // to /en-US/ -# cache /resources/** for 1 week since they don't change often -location ~ ^/[a-z][a-z]/resources/(.*) { - try_files $uri /en-US/resources/$1 =404; - expires 1w; -} # cache //main.f40e91d908a068a2.js forever since they never change location ~ ^/([a-z][a-z])/(.+\..+\.(js|css)) { try_files $uri =404; @@ -84,7 +78,7 @@ location ~ ^/([a-z][a-z])/ { # cache /resources/** for 1 week since they don't change often location /resources { - try_files $uri /en-US/$uri /en-US/index.html; + try_files $uri /en-US/index.html; expires 1w; } # cache /main.f40e91d908a068a2.js forever since they never change From 004768132bb74f476f1873d12d20fbda87f81b56 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Tue, 6 Sep 2022 21:17:15 +0200 Subject: [PATCH 30/31] Show clearnet nodes on world map --- frontend/src/app/lightning/nodes-map/nodes-map.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index 6c809916e..c2cf385ca 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -122,10 +122,13 @@ export class NodesMap implements OnInit { return 10 * Math.pow(params[2] / maxLiquidity, 0.2) + 3; }, tooltip: { + position: function(point, params, dom, rect, size) { + return point; + }, trigger: 'item', show: true, backgroundColor: 'rgba(17, 19, 31, 1)', - borderRadius: 4, + borderRadius: 0, shadowColor: 'rgba(0, 0, 0, 0.5)', textStyle: { color: '#b1b1b1', @@ -155,7 +158,6 @@ export class NodesMap implements OnInit { borderColor: 'black', borderWidth: 0, }, - blendMode: 'lighter', zlevel: 2, }, ] From dcfcac2cc65be0cd5a74d7fc3995e1a2c22b3dc5 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Fri, 9 Sep 2022 14:56:18 +0200 Subject: [PATCH 31/31] Show summary stats and world map in isp and country node list page --- backend/src/api/explorer/nodes.api.ts | 8 +- .../lightning/channel/channel.component.html | 86 ++++++++++++------- .../nodes-map/nodes-map.component.html | 6 +- .../nodes-map/nodes-map.component.scss | 8 ++ .../nodes-map/nodes-map.component.ts | 56 ++++++++++-- .../nodes-per-country.component.html | 56 +++++++++++- .../nodes-per-country.component.ts | 50 +++++++++-- .../nodes-per-isp.component.html | 56 +++++++++++- .../nodes-per-isp.component.scss | 2 +- .../nodes-per-isp/nodes-per-isp.component.ts | 39 +++++++-- 10 files changed, 301 insertions(+), 66 deletions(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 9d82dc83d..cbd70a34f 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -434,12 +434,14 @@ class NodesApi { SELECT nodes.public_key, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, geo_names_city.names as city, geo_names_country.names as country, - geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision, + nodes.longitude, nodes.latitude, nodes.as_number, geo_names_isp.names as isp FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' + LEFT JOIN geo_names geo_names_isp on geo_names_isp.id = nodes.as_number AND geo_names_isp.type = 'as_organization' WHERE geo_names_country.id = ? ORDER BY capacity DESC `; @@ -449,6 +451,7 @@ class NodesApi { rows[i].country = JSON.parse(rows[i].country); rows[i].city = JSON.parse(rows[i].city); rows[i].subdivision = JSON.parse(rows[i].subdivision); + rows[i].isp = JSON.parse(rows[i].isp); } return rows; } catch (e) { @@ -463,7 +466,8 @@ class NodesApi { SELECT nodes.public_key, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, geo_names_city.names as city, geo_names_country.names as country, - geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision, + nodes.longitude, nodes.latitude FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' diff --git a/frontend/src/app/lightning/channel/channel.component.html b/frontend/src/app/lightning/channel/channel.component.html index 9a0c424fb..3942cda6e 100644 --- a/frontend/src/app/lightning/channel/channel.component.html +++ b/frontend/src/app/lightning/channel/channel.component.html @@ -16,7 +16,8 @@

- +
@@ -51,38 +52,57 @@
- -
- -
- -
-
- +
+
+ + + + + + + +
Capacity + + +
-
- -
-
- -
- - -
-

Opening transaction

- -
- -
- -
-

Closing transaction

   - -
- -
-
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + + +
+

Opening transaction

+ +
+ + +
+ +
+

Closing transaction

   + + +
+ + +
+
@@ -108,7 +128,7 @@
- +
@@ -152,4 +172,4 @@
-
\ No newline at end of file + diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.html b/frontend/src/app/lightning/nodes-map/nodes-map.component.html index 75f8aeb08..d739dd2c9 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.html +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.html @@ -1,13 +1,13 @@ -
+
-
+
Lightning nodes world map
(Tor nodes excluded)
-
diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss index 4e363a534..d7ad42b46 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.scss +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.scss @@ -16,6 +16,11 @@ padding-bottom: 100px; }; } +.full-container.widget { + min-height: 240px; + height: 240px; + padding: 0px; +} .chart { width: 100%; @@ -38,3 +43,6 @@ padding-bottom: 55px; } } +.chart.widget { + padding: 0px; +} diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index c2cf385ca..b783e225a 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnDestroy, OnInit } from '@angular/core'; import { SeoService } from 'src/app/services/seo.service'; import { ApiService } from 'src/app/services/api.service'; import { Observable, tap, zip } from 'rxjs'; @@ -18,6 +18,10 @@ import { getFlagEmoji } from 'src/app/shared/common.utils'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class NodesMap implements OnInit { + @Input() widget: boolean = false; + @Input() nodes: any[] | undefined = undefined; + @Input() type: 'none' | 'isp' | 'country' = 'none'; + observable$: Observable; chartInstance = undefined; @@ -43,13 +47,48 @@ export class NodesMap implements OnInit { this.observable$ = zip( this.assetsService.getWorldMapJson$, - this.apiService.getWorldNodes$() + this.nodes ? [this.nodes] : this.apiService.getWorldNodes$() ).pipe(tap((data) => { registerMap('world', data[0]); + let maxLiquidity = data[1].maxLiquidity; + let inputNodes: any[] = data[1].nodes; + let mapCenter: number[] = [0, 5]; + if (this.type === 'country') { + mapCenter = [0, 0]; + } else if (this.type === 'isp') { + mapCenter = [0, 10]; + } + + let mapZoom = 1.3; + if (!inputNodes) { + inputNodes = []; + for (const node of data[1]) { + if (this.type === 'country') { + mapCenter[0] += node.longitude; + mapCenter[1] += node.latitude; + } + inputNodes.push([ + node.longitude, + node.latitude, + node.public_key, + node.alias, + node.capacity, + node.channels, + node.country, + node.iso_code, + ]); + maxLiquidity = Math.max(maxLiquidity ?? 0, node.capacity); + } + if (this.type === 'country') { + mapCenter[0] /= data[1].length; + mapCenter[1] /= data[1].length; + mapZoom = 6; + } + } + const nodes: any[] = []; - console.log(data[1].nodes[0]); - for (const node of data[1].nodes) { + for (const node of inputNodes) { // We add a bit of noise so nodes at the same location are not all // on top of each other const random = Math.random() * 2 * Math.PI; @@ -66,11 +105,12 @@ export class NodesMap implements OnInit { ]); } - this.prepareChartOptions(nodes, data[1].maxLiquidity); + maxLiquidity = Math.max(1, maxLiquidity); + this.prepareChartOptions(nodes, maxLiquidity, mapCenter, mapZoom); })); } - prepareChartOptions(nodes, maxLiquidity) { + prepareChartOptions(nodes, maxLiquidity, mapCenter, mapZoom) { let title: object; if (nodes.length === 0) { title = { @@ -91,8 +131,8 @@ export class NodesMap implements OnInit { geo: { animation: false, silent: true, - center: [0, 5], - zoom: 1.3, + center: mapCenter, + zoom: mapZoom, tooltip: { show: false }, diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html index 190cf6219..543cf951c 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html @@ -1,9 +1,58 @@
-

+

Lightning nodes in {{ country?.name }} {{ country?.flag }}

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
Nodes{{ countryNodes.nodes.length }}
Liquidity + + + {{ countryNodes.sumLiquidity | amountShortener: 1 }} + sats + +   + + +
Channels{{ countryNodes.sumChannels }}
ISP Count{{ countryNodes.ispCount }}
Top ISP + + {{ countryNodes.topIsp.name }} [ASN {{ countryNodes.topIsp.id }}] + +
+
+
+
+ +
+
+
+
+
@@ -15,9 +64,8 @@ - - - + + diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts index e0bf5eb66..19394a828 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { map, Observable } from 'rxjs'; +import { map, Observable, share } from 'rxjs'; import { ApiService } from 'src/app/services/api.service'; import { SeoService } from 'src/app/services/seo.service'; import { getFlagEmoji } from 'src/app/shared/common.utils'; @@ -32,6 +32,8 @@ export class NodesPerCountry implements OnInit { this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country) .pipe( map(response => { + this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`); + this.country = { name: response.country.en, flag: getFlagEmoji(this.route.snapshot.params.country) @@ -45,14 +47,50 @@ export class NodesPerCountry implements OnInit { iso: response.nodes[i].iso_code, }; } - - this.seoService.setTitle($localize`Lightning nodes in ${this.country.name}`); - return response.nodes; - }) + + const sumLiquidity = response.nodes.reduce((partialSum, a) => partialSum + a.capacity, 0); + const sumChannels = response.nodes.reduce((partialSum, a) => partialSum + a.channels, 0); + const isps = {}; + const topIsp = { + count: 0, + id: '', + name: '', + }; + for (const node of response.nodes) { + if (!node.isp) { + continue; + } + if (!isps[node.isp]) { + isps[node.isp] = { + count: 0, + asns: [], + }; + } + if (isps[node.isp].asns.indexOf(node.as_number) === -1) { + isps[node.isp].asns.push(node.as_number); + } + isps[node.isp].count++; + + if (isps[node.isp].count > topIsp.count) { + topIsp.count = isps[node.isp].count; + topIsp.id = isps[node.isp].asns.join(','); + topIsp.name = node.isp; + } + } + + return { + nodes: response.nodes, + sumLiquidity: sumLiquidity, + sumChannels: sumChannels, + topIsp: topIsp, + ispCount: Object.keys(isps).length + }; + }), + share() ); } - trackByPublicKey(index: number, node: any) { + trackByPublicKey(index: number, node: any): string { return node.public_key; } } diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html index 4ea3c2d11..441dc429e 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.html @@ -1,5 +1,54 @@
-

Lightning nodes on ISP: {{ isp?.name }} [AS {{isp?.id}}]

+

Lightning nodes on ISP: {{ isp?.name }}

+ +
+
+
+
Channels Location
{{ node.alias }}
+ + + + + + + + + + + + + + + + + + + + + + +
ASN{{ isp?.id }}
Nodes{{ ispNodes.nodes.length }}
Liquidity + + + {{ ispNodes.sumLiquidity | amountShortener: 1 }} + sats + +   + + +
Channels{{ ispNodes.sumChannels }}
Top country + + {{ ispNodes.topCountry.country }} {{ ispNodes.topCountry.flag }} + +
+
+
+
+ +
+
+
+
@@ -12,9 +61,8 @@ - - - + + diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss index 02b47e8be..b829c5b59 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.scss @@ -59,4 +59,4 @@ @media (max-width: 576px) { display: none } -} \ No newline at end of file +} diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts index f7edf783a..24664aab0 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts @@ -1,8 +1,9 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { map, Observable } from 'rxjs'; +import { map, Observable, share } from 'rxjs'; import { ApiService } from 'src/app/services/api.service'; import { SeoService } from 'src/app/services/seo.service'; +import { getFlagEmoji } from 'src/app/shared/common.utils'; import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component'; @Component({ @@ -33,7 +34,7 @@ export class NodesPerISP implements OnInit { map(response => { this.isp = { name: response.isp, - id: this.route.snapshot.params.isp + id: this.route.snapshot.params.isp.split(',').join(', ') }; this.seoService.setTitle($localize`Lightning nodes on ISP: ${response.isp} [AS${this.route.snapshot.params.isp}]`); @@ -46,12 +47,40 @@ export class NodesPerISP implements OnInit { }; } - return response.nodes; - }) + const sumLiquidity = response.nodes.reduce((partialSum, a) => partialSum + a.capacity, 0); + const sumChannels = response.nodes.reduce((partialSum, a) => partialSum + a.channels, 0); + const countries = {}; + const topCountry = { + count: 0, + country: '', + iso: '', + flag: '', + }; + for (const node of response.nodes) { + if (!node.geolocation.iso) { + continue; + } + countries[node.geolocation.iso] = countries[node.geolocation.iso] ?? 0 + 1; + if (countries[node.geolocation.iso] > topCountry.count) { + topCountry.count = countries[node.geolocation.iso]; + topCountry.country = node.geolocation.country; + topCountry.iso = node.geolocation.iso; + } + } + topCountry.flag = getFlagEmoji(topCountry.iso); + + return { + nodes: response.nodes, + sumLiquidity: sumLiquidity, + sumChannels: sumChannels, + topCountry: topCountry, + }; + }), + share() ); } - trackByPublicKey(index: number, node: any) { + trackByPublicKey(index: number, node: any): string { return node.public_key; } }
Channels Location
{{ node.alias }}