Merged PR 3215853: Fix spacing/layout for block characters and many retroactively-recategorized emoji (and more!)

This encompasses a handful of problems with column counting.

The Terminal project didn't set a fallback column counter. Oops. I've fixed this to use the `DxEngine` as the fallback.

The `DxEngine` didn't implement its fallback method. Oops. I've fixed this to use the `CustomTextLayout` to figure out the advances based on the same font and fallback pattern as the real final layout, just without "rounding" it into cells yet.
- `CustomTextLayout` has been updated to move the advance-correction into a separate phase from glyph shaping. Previously, we corrected the advances to nice round cell counts during shaping, which is fine for drawing, but hard for column count analysis.
- Now that there are separate phases, an `Analyze` method was added to the `CustomTextLayout` which just performs the text analysis steps and the glyph shaping, but no advance correction to column boundaries nor actual drawing.

I've taken the caching code that I was working on to improve chafa, and I've brought it into this. Now that we're doing a lot of fallback and heavy lifting in terms of analysis via the layout, we should cache the results until the font changes.

I've adjusted how column counting is done overall. It's always been in these phases:
1. We used a quick-lookup of ranges of characters we knew to rapidly decide `Narrow`, `Wide` or `Invalid` (a.k.a. "I dunno")
2. If it was `Invalid`, we consulted a table based off of the Unicode standard that has either `Narrow`, `Wide`, or `Ambiguous` as a result.
3. If it's still `Ambiguous`, we consult a render engine fallback (usually GDI or now DX) to see how many columns it would take.
4. If we still don't know, then it's `Wide` to be safe.
- I've added an additional flow here. The quick-lookup can now return `Ambiguous` off the bat for some glyph characters in the x2000-x3000 range that used to just be simple shapes but have been retroactively recategorized as emoji and are frequently now using full width color glyphs.
- This new state causes the lookup to go immediately to the render engine if it is available instead of consulting the Unicode standard table first because the half/fullwidth table doesn't appear to have been updated for this nuance to reclass these characters as ambiguous, but we'd like to keep that table as a "generated from the spec" sort of table and keep our exceptions in the "quick lookup" function.

I have confirmed the following things "just work" now:
- The windows logo flag from the demo. (💖🌌😊)
- The dotted chart on the side of crossterm demo (•)
- The powerline characters that make arrows with the Consolas patched font (██)
- An accented é
- The warning and checkmark symbols appearing same size as the X. (✔⚠🔥)

Related work items: #21167256, #21237515, #21243859, #21274645, #21296827
This commit is contained in:
Michael Niksa 2019-05-01 23:13:53 +00:00 committed by Dustin Howett
parent d4d59fa339
commit 87e85603b9
19 changed files with 996 additions and 128 deletions

View File

@ -7,6 +7,7 @@
#include <DefaultSettings.h>
#include <unicode.hpp>
#include <Utf16Parser.hpp>
#include "..\..\types\inc\GlyphWidth.hpp"
using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Terminal::Core;
@ -294,6 +295,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
_renderer->AddRenderEngine(dxEngine.get());
// Set up the renderer to be used to calculate the width of a glyph,
// should we be unable to figure out it's width another way.
auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
SetGlyphWidthFallback(pfn);
// Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
// and react accordingly.

View File

@ -3,6 +3,8 @@
#include "precomp.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
// This class is intended to test boundary conditions for:
// SetConsoleActiveScreenBuffer
class BufferTests
@ -23,6 +25,8 @@ class BufferTests
END_TEST_METHOD()
TEST_METHOD(ScrollLargeBufferPerformance);
TEST_METHOD(ChafaGifPerformance);
};
void BufferTests::TestSetConsoleActiveScreenBufferInvalid()
@ -144,3 +148,66 @@ void BufferTests::ScrollLargeBufferPerformance()
Log::Comment(String().Format(L"%d calls took %d ms. Avg %d ms per call", count, delta, delta/count));
}
void BufferTests::ChafaGifPerformance()
{
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"IsPerfTest", L"true")
END_TEST_METHOD_PROPERTIES()
const auto Out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO Info;
GetConsoleScreenBufferInfo(Out, &Info);
// We need a large buffer
Info.dwSize.Y = 9999;
SetConsoleScreenBufferSize(Out, Info.dwSize);
SetConsoleCursorPosition(Out, { 0});
DWORD Mode = 0;
GetConsoleMode(Out, &Mode);
Mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(Out, Mode);
SetConsoleOutputCP(CP_UTF8);
// Taken from: https://blog.kowalczyk.info/article/zy/Embedding-binary-resources-on-Windows.html
HGLOBAL res_handle = NULL;
HRSRC res;
char * res_data;
DWORD res_size;
// NOTE: providing g_hInstance is important, NULL might not work
HMODULE hModule = (HMODULE)&__ImageBase;
res = FindResource(hModule, MAKEINTRESOURCE(CHAFA_CONTENT), RT_RCDATA);
if (!res)
{
VERIFY_FAIL(L"Couldn't find resource.");
return;
}
res_handle = LoadResource(hModule, res);
if (!res_handle)
{
VERIFY_FAIL(L"Couldn't load resource.");
return;
}
res_data = (char*)LockResource(res_handle);
res_size = SizeofResource(hModule, res);
/* you can now use the resource data */
Log::Comment(L"Working. Please wait...");
const auto now = std::chrono::steady_clock::now();
DWORD count = 0;
for (DWORD pos = 0; pos < res_size; pos += 1000)
{
DWORD written = 0;
WriteConsoleA(Out, res_data + pos, min(1000, res_size-pos), &written, nullptr);
count++;
}
const auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - now).count();
Log::Comment(String().Format(L"%d calls took %d ms. Avg %d ms per call", count, delta, delta / count));
}

View File

@ -30,12 +30,16 @@
<ClInclude Include="Common.hpp" />
<ClInclude Include="OneCoreDelay.hpp" />
<ClInclude Include="precomp.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\types\lib\types.vcxproj">
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Host.Tests.Feature.rc" />
</ItemGroup>
<PropertyGroup>
<ProjectGuid>{8CDB8850-7484-4EC7-B45B-181F85B2EE54}</ProjectGuid>
<Keyword>Win32Proj</Keyword>

View File

@ -98,8 +98,16 @@
<ClInclude Include="OneCoreDelay.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Host.Tests.Feature.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

Binary file not shown.

482
src/host/ft_host/chafa.txt Normal file
View File

@ -0,0 +1,482 @@
[?25l
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂╴▂┈▪⎽▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▘⎺▆▅▍▗╾⎼▃▊▅╾┚┶╲⎽▄▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅┳▃▂⎽⎽▃▃▅▅▄▄▗▖⎺▆▆▅▎▍▄▃▂▊▗▆⎼▃▂▝▖⎺◆⎺▅▅▃▂▄▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▋▃▂⎼▊▗▊▅▄▄▃▅▋▆▆▅▅▮▎▂▂⎽▊▍▍▂⎺⎺▍▋⎺▆▆▂▲▄▃▃▆▄▂╴│╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅━╾⎼▃▍▄▌▄▃━▋▃▍▅▄▄▊▃▃⎺▆▆▋▍▍⎺▆▆▍▊▅▅⎻▌┈▆▆▄▂▅▃⎺▆▄▃▂⎽⎽⎽▂▃▖▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▄▃▂▊⎽▉▃▅▅▅▍▂▄▄▃▂▌▊▗▆▅▅▄▎▅▅▄▄▍▊▄━━▋▖┈▪▝▄⎺▌▅▂▪▃▂⎽⎽╲  ▝▗▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▊▂⎽⎺⎺▍▲▌⎼▄▃▖●▊━▅▄▄▍▋▗▃━━▌▎▃▃▂▂▖▉⎼⎼⎼▃▆▅▄▂⎽⎺▍┈▎⎺⎺▆▆▆▎▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▘⎺⎺⎺┳▆▆▃▂▂▂▋▅▌▃▂▂▂▎▌▝▄▄▃▌▘▝▃▃▂▋▉■┈⎽▉▏⎺▆▅▃▆▘┈▌▎▋ ╵▌▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗⎻⎻▆━▘▅▋⎺╴⎺⎻▍▆▘┈⎺▆▆⎺▍▂⎽▂⎽▌▘▎⎺⎺⎺▋▉⎺▖┈⎺▎▎▊▍▖┈▆▗▝▅▃▂└⎽▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂▂▃▃▃▝▄▘▅▄⎺▄▲▲▄▄▃▃▘▅▅▄▄▄▄⎺▄▄▃▃▋ ━━━━▎▂⎽▉▝┈▘▎◀▎▆▄▃▂▂▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▋⎼⎽▂▂▌▆╺━▲⎼⎼▋▅▅▅▄▃▲▅▅▅▄▄▅▄▖▏▄▄▃▃▋ ▂▂┈⎽▌▎▊⎺▅▃▂▌▏▌▉▘▌▎ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎺⎺┑▂▂▂▄▄▄▂▂▄▄▃▃▃▃▂▄▃▃▃◆⎺▆▆▆▆▝▅▄▄▄▃▂⎽▆▅▄▂▘┥▊▂▘⎽▗▗▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▄▅┒▅⎺▅▃⎽┈▅▃▃ ▆▅▄▃▂▋▂▪▉▋▌▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲●▝⎼▆▄▂┈⎺▅▃▂◀▋   ▂▃▘▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄⎺⎺▆▅▄▄▄▄▅▆⎺┯▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚┆ ╵╴┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂▂▂┈│▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▗▆▅▅▖▌▂▂⎼▊▄▃▃▃▂⎺▄▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▆▄▃▃▂▃▃▅▄▄▃▖▖▆▅▅▄▎▍▄▄▃▊▌▃▃▂▂▖⎺⎺▆▄▄▂▃▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▋▄▃▃▃▅▖▄▃▃▃▅▋▅▄▄▼▮▎▃▃▂▊▍▌▅▄▃▍▋⎺▆▅▂▗▃▂▆▄▂┈▍│╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▃─━▃━▍▄▌━━━▋▃▍▄▃▃▊▃▃▂⎽⎺▋▍▍▃▂▂▌▋▆▅⎻▌┈▆▄▂▲▃⎺▆▅▃▂⎽⎽⎽▂▃▄▖▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▌▃▂⎼▂⎽▗▅▅▄▄▍▃▂▂▲━▌▊▘▆▆▆▌▍▖⎺⎺▆▍▊▄━━▋▖┈▉▄▍▆▅▄▲▄▂▂▪▲▪  ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▊⎼⎼▃▂▍⎽▗▃▃▂▂▝▊▄▃▃▃▍▋▗▄▄▃▌▃▆▅▅▄▌▉─⎼▃▅▅▄▂⎽▖▝▍┈▎▆▆▆▆⎺▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂◀⎽⎽━▋▆▆▂⎽⎺⎺⎺▅▍⎽⎽▲⎺▎▌▂▅▅▄▌●▍━━━▌▉⎽⎽⎽▉▏⎺▅▃⎽▄▘┊▌▏▎▮┕▉▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▆▆▼⎻▆▅▋┳▆▆▅▍▆▅▆▅▅▅⎺▍▃▂▂⎽▍▘▝▃▂▂▋▉⎺▖⎺┳▎┫▉▏▆▄▏▝▄▂⎽┊⎽▊▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▄▃━▃▄▝▄▍▃▃━━▮▉▃▂▂▲▗▄▆▅▅▅▅●▄▎⎺▆▆▋ ▏━━━▎▘▏▋▌┈▋▎▎▆▅▄▃▃▃▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅╺▃▂▂▂▌▆▗▄▃▃▂▋▅▅▅▄▃▲▅▅▅▄▄▅▄▖▂▂⎽⎽●▖ ▂▂┈⎽▌▎⎺▅▃▂▂▌╲▌▗▘▌▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺┑▂▂▂▼▼▼▂▂▄▄▃▃▃▃▂▄▃▃▃▃━▅▄▅⎼▝▅▄▄▃▂⎽▆▅▃▂╵⎺▅▘▊▃▘▗┄▗▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▄▄▝⎺▅▃⎽┈▅▃▂└⎺▅▄▃▂▂▘▃┈▎▋▍▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲●⎼▆▄▃┈⎺▅▃▂⎽▌▊ ┈⎽▃▅▚⎻─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄╹⎺▅▄▄▄▄▅▆⎺⎺▂▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╷·╶ ┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂▂⎽┈│▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▗▄▄▃▖▌▂▂⎼▊▖▅▅▄▄▖▄▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▆▅▄▄▃▗▃▃▃▃▂▂▖▄▄▃▃▎▍⎼▄▃▊▍▄▄▄▃▃▖⎺⎺▆▃▃⎽▄▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▋▅▅▄▗▅▊▃▂▂▂▅▋▃━━┑▮▎▃▃▂▊▍▌━━━▍▋⎺▆▅▖▃▂⎺▅▃⎽┊▍│╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄━▅▄─▘▄▍━▅▄▋▃▍▅▄▄▊▃▃▂▂⎺▋▍▂▃▂▃▍▋▆⎻▄▌▆▅▃▲▄▂⎺▅▄▃▂▂▂▂▃▅▆▖⎺╎▆▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▌▄▄▃╸▂▗▄▃▃▃▍▃▄▄▃▃▌▊▘⎺▆▆▌▍▝▃▂▂▌▊▄━╾▋▖┈┃▃▆▅▃━╾▃▂▂■⎽╴  ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▊▃▂▂▂▍▲▗▂▂⎼⎽▃▊▂▂▂⎺▝▋▗▄▄▄▌▝▍⎺⎺⎺▌▉⎼⎼▂▝▄▃⎽┈▝▆▍┈▎⎺⎺⎺╴⎺▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂⎽⎼▃▂▂▆▆⎺⎺▆▆▆▅▍▆▆▆▅▎▌▂▅▅▄▗▖▗▅▄▄▋▉⎽┈▝▉⎺▆▄▂▅▂▋┊▌╻┚▼╲▋▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▂⎺⎺⎺▍▅▋▅▅▄▄▎▆▝▄▃▃▃▏▍▃▂▂▂▍▏⎽▅▅▄▋▉⎺▖▆▼▎▋╴▌⎺▅▃▚▝▃⎽┈└⎽▊▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▅▅⎻⎻⎻▝▄▎━▅▅┰▮▪▄▄▄▃▋▅▆▅▅▅▅▝▘▎⎽▂┈▊ ▏━━⎼▎▋┦┊▊▉▋▎▌▅▄▃▃▃▄▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▝▄▄▄▃▘▆▗▂▂▂▂▋▅▅▄▃⎺⎺▅▅▅▄▄▅▄▖▄▄▄▃▄▋ ▂▂┈⎽▗▖▅▄▂⎽╴▌▪▌▋▘▍▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺▪▲⎺⎺┯⎺▘▂▂▄▄▃▃▃▃▂▄▃▃▃▃━▅▄▅⎼▝▅▄▃▂▖⎺▝▃▂⎽⎺▅▘▎▊▏▌▏╴▗▉▍▊▝▝▊▎▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▄╵⎺▅▃⎽┈▆▃▂┓┡▅▄▃▃▂▃▘▅◀▎▊▎▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲⎺▆▅▃⎽⎺▅▃▂▘··▊┈⎽▃▅┯▅▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▘⎺▆▅▄▄▄▅▆⎺╴ ┄▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿╌▅┚╴▘╶ ┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽╵┊▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▃▂▲⎽▗▗▲⎼⎼▊▖▅▆▆▅▖▅▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▖▆▆▅▅▗▃▂▂▂▲▗▖▂▲▪⎼▎▍▄▃▃▊▌▆▆▅▅▄▖⎺▆▅▂▂▆▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▆▆▅▅▅▊▲⎽⎼▪▅▋▄▄▃▖▮▎▃▂▂▊▍▌▄▄▄▍▋▆▅▄▍▂┊▆▄▂▊·▍│╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅⎻⎻▆▅▘▄▍▄▃▃▋▃▍▃▃▂▊▂▃⎽⎺⎺▋▍▃▃▃▂▍▋▅⎻╾▌▅▃▂▄▂⎽▆▄▃▂▂▂▃▃▅▆⎺▖▂╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▅▅▅▗▂▗▃▂▂▂▍▃▂┈▂⎺▌▊▗▆▆▅▘▍▍⎼▄▄▍▊━╾⎼▋▖ ▊⎺▅▃▃╾▄▃▂▂⎽▂   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▋▄▄▃▃▘▮▌▲⎽⎺▆▄▊▆▆▆▅▍▋▗▄▃▃▌▎▍▂▂⎽▌▉⎼▃▂▝▃▂▎┈⎺▝▍╴▎▆┱╷▍▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂━━▲▂▄▆▆▆▆▅▅▘▅▌▄▃▃━▎▌▝▅▄▄▌▖▍▆▆▆▌▉▮┈▄⎺▆▄▂▅▃┙▋┊▌▎▋▖┝▌▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋▃▂▂▂▍▅▋▄▃━━▎▆▎▅▄▄▄▏▍▂▂⎽⎽▌▃▃▃▃▂▋▉⎺▆⎻◆▎▋╏⎺▆▃▆┻▊▂┈┈╴⎽▂▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗⎺⎺▆▆▆▃▄▗▄▄▃▃▮▍⎽⎽⎽▂▊▅▅▅▅▄▄▉▘▃▃▃▂▋ ━━⎼▃▍▍▍▋▊┈▋▎▌▗▄▃▃▄▅▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▘▆▆▅▅▘▆▊▂▂▆▆▋▅▘▆▆▅▅▘▅▅▄▄▅▄▖▆▆▅▅▆▌ ▎⎽┈▉▅▅▄▂⎽▏▋▌·▌▉▎▘▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺▪▝▆▆▆▅▘▂▂▄▄▃▃▃▃▂▄▃▃▃▃━▅▄▅⎼▝▄▃▂⎽⎽▅▝▂┈·▅▃▊▎▊▶▘┆┈▗▊▍▊▝▝▊▗▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▄▆▅▃⎽⎺▆▄▂└╷▄▃▃▃▃▄▘┈┕⎽▂▎▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲▄▅▃⎽⎺▅▄▂┈▘ ·▝▂▃▅⎺⎺▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃┑▆▅▄▄▅▅▆⎺┆┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿⎽▅⎻╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂⎽┊·╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▆▅▅▄▄▗▃▃▂⎺▗▆⎺▆▆▎▝▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅┱⎺⎺▆▆▗▃▂▂⎽▄▄▃▃▂▂⎺▎▍▂⎺▲▊▌▆▆▆▆▃▖▆▅▄▂▅╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎺⎺⎺▗┎▊⎼▄▃▖▏▋⎺⎺⎺▉▪▎▲▮⎺▊▍▌▅▅▅▍▋▅▄━▌·▆▄▂┈▊ ▍│╵·▆▅▄▄▃▂▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄⎽⎺▼⎺▍▄▍▃▂▂▋▃▍▆▆▆▊▂▃▆▆▅▋▎▃▄▃▃▍▊▄━⎼▗▄▂▅▃⎽▆▅▄▃▂▂▃▄▅▆┈┈▖▆▂╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘⎺⎺⎻▊▂▗▂▂⎽⎺▍▂▅▅▅▄▌▊▗▄▄▄▘▍▂▲▲▪▌▊━⎼▂▋▖┈⎺▆▄▂┳▄▃▂▖┷▂▃   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▅▅▅▄▘▲▌▆▆▆▅▄▊▃▃▃▃▘▋▂━━━▌▎▍▃▂▂▌▉▃▂▝▝▂⎽▗┈▉▝▍┈▎⎺┱▏▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝⎼━━┷▆▆▅▅▄▄▘▅▌▄▄▄▄▎▌▝▃▃▃▌▎▍⎺⎺▆▋▉▪┊▆▅▅▃▆▄▂▏▋┊▌┧▌╲▋▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋▂▂▂⎼▍▅▋━━━━▎▆▖▂▂⎽▂▏▗▏⎺▃⎺▍▖▄▄▄▃▌▉▆⎻━─▎▋▌▆▄▂▅▘▊⎽┈┈⎽▂▃▖▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▘⎽⎽⎽▊▃▄▃▃▂▂▂▮▍▆▆▆▅▅▅▃▃▃▃▃▘▘▄▄▄▄▋ ╾⎼▂⎽▍╷▅⎽┩▋⎺▎▊▗▄▄▄▅⎺▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▼⎺⎺⎺▖▆▗⎺⎺▆▆▋▅▋▃▃▂▂▘▅▅▄▄▅▄▖▆▆▆▆▆▌ ▎▊┈⎺▝▄▂▅▗┒▊▌▏▌▪┕┙▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺▪▝▅▅▄▄▘▂▂▄▄▃▃▃▃▂▄▃▃▃▃━▅▄▅⎼▝▃▂⎽▆▅▘▍▪┈▅▃▂▊▎▊⎽·╎┈▗▊▍▊▝▝▉▎▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▆▝▃▂⎺▅▄▂▍▅▅▅▄▃▃▃▄▅┈┈▋▂▄▂▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅▮▆▅▃⎽⎺▅▄▂┈┈▍▉⎽▂▃▅■▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃⎺▆▅▅▄▅▆⎺⎺▉▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿╌▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▘━■▆▊▗▄▅▆▆▅▅▅▅▄▄▗▆▆▆▅▖▆⎺⎺▆▎▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▘▃▂▂▎▃⎽⎽▄▄▄▃▅▅▅▅▎▍▆▆▆▅▌▗▆▆▆▗▆▅▄▃┊▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂▂▂▋▅▊▃▃▂▊▮▋▅▅▄▄▆▎▅▅▅▊▍▌▅▅▅▍▋▄━⎼▅▆▅▃⎽ ▊ ▍│╵·▆▅▄▂▂▂▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗▂▂⎽⎽▍▄▍▂▂⎺▋▃▍▄▃▃▊▃▗▄▄▃▋▎▃▄▄▃▍▋━⎼▂▗▂■▃┈⎺▅▄▃▃▃▃▄▅▆┈ ┈▖╹▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▆▆▆▊┶▉⎺⎺⎺▆▍▃▏▅▅▅▌▊▂▂▂▲▋▍▂▂▂▲▍▊⎼▂▪▋▖┈▆▅▃▂┱▄▃⎽┈▂▃▖   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅┙▆▆▆▆▘▲▋▅▅▅▅▄▊▄▄▃▃▍▋▝▃▃▃▋▎▝▃▃▂▌▉⎽▝⎺▊⎽▝▌┊▌▝▍┈▎⎺⎺⎺ ▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▅┯⎻⎻▆▆▃▄▃▃┛▅▍▂▂⎽⎺▎▌▎⎽⎽⎺▌▎▍▆⎺▖▋▉▅▆▄▼▃▆▄▂▎▌▋┊▌╏▎▂╵▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋⎼━━─▘▅▋▅▅▄▄▍▆▆▆▆▅▅▏▍▅▅▅▅▖▖▄▄▄▄▌▉⎻━─▃▎▋▆▄▂▆▄▘▊⎽┈⎽▂▄▘▎▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▘⎽▃▂▖▝▄▘▂▃▂▖▮▊▂▆▆▆▖▖▎━━▅━▘▘▄▄▄▄▋ ▃▂┈▋▍▍╹▋▌⎺▆▎▝▗▄▄▅⎺┈▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▅▆▆▆▄▋▅▋▂▃▃▂▌▅▅▄▄▅▅▖▆┈⎺▆◀▌ ▎▍┈▅▍▂▂▍▗┱▎▌▏▌┹▃▋▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▝▃▃▃▃▘▂▄◆⎺⎺⎺┻▃▂▄▃▃▃▃━▅▄▅⎼▝▂⎽▆▅▃▋▍⎺▅▄▂▘▊▎▊▂▂⎽╴▗⎽▎▘▝▝▊▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃▃⎺▅▅⎽⎺▅▃▂╴⎺▆▏▗▃▃▃▄▅⎺ ┈▂▄▆▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄┄⎺▅▃⎽⎺▅▄▂▃⎽┈⎽⎽▂▃▅▮▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃━▆▅▅▅▅▆⎺⎺┋┆▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴▘╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂╸⎽▅▆▪▊▗▄▅▆▆▅▅▅▅▄▄▖▄▄▄▃■▆▆▆▆▎▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅⎽⎼⎼▃▎▃▄▆▄▄▄┲▃▃▂▂┓▍▄▄▃▃▌▆▆▆▅▍▅▄▃▂┈▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎼⎼▃▋⎼▊▂▂┵⎺▅▋▂▂▲⎽▄▎━━━▊▍▌▅▅▅▍▋━⎽▂▅▅▃▂⎽·▊ ▍│╵·▆▅▂▂▃▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄⎽▃▂▂▎▄▍▲⎺⎺▋▃▍▄▄▄▊▃▘▄▂▄▋▍▃▃▃▃▍▋⎼▂⎺▃▲▄▂⎺▆▅▄▃▃▃▄▅⎺⎺  ┈▖╏▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▂⎽⎽▊⎽▉▆▆▆▆▍▃▃▂▂▂▋▊▃▃▃▂▋▍▂▲▲▪▍▊▂▲▅▋▃⎺▅▃▂▲▄⎼⎽⎽▂▃▅▖   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅┙⎺⎺⎺⎺▘▲▋▅▅▄▄▄▊⎺⎺⎺⎺▍▋▍⎺⎺⎺▌▎▍▂▂▂▌▉▮⎺▅▊▄▝▌ ▖▝▍┈▎·▌▉▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▆▅▆▅▆▆┻▃▃▂┛▅▝▅▅▅▅▎▌▅▅▅▅▌▖▍⎺⎺⎺▋▉▆⎻━⎼▂▄▂⎽▍▅▋┊▌╏▋▖╵▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋▂▃▂▂▘▅▗▄▄▄▃▍▆▲━■━━▏▍▅▅▅▅▍▃▃▄▄▃▌┊━⎼▂⎽▎▆▅▃▆▄▃▂▊⎽▂▃▄▆▃▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━▲▂▂▂⎼●▄▘▂⎽▂▉▮▊▃▃▃▂▃▖▘▂▂▂▂▍▘▃▄▄▃▋ ▂▮┈▆▍▍▊▋▋⎺▆▎▝▗▅▆⎺⎺ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▅▅▅▃▆▋▅▗⎺⎺⎺⎺▋▅▅▆▆▅▅▖▆▆▆▆◀▌ ▎▆▅━▌▖▌▉▌▍▊▌╿▌▃┬▍▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▪▂▃▂━▍▂▝▃▄▄▃▘▃▂▄▃▃▃▃━▅▄▅⎼▋┈▅▄▃⎽▗▍▅▄▂⎽╼▊▎▊▂▎▎▃▗▂▎▘▝▝▊▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▝▆▅▃▖⎺▝▃⎽▋⎺▆▅▘▄▃▄▄▆⎺▊┈▂▄▆▼▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▅⎺▅▃▂⎽▅▄▃▂▮┈┈▝▂▄▅▃▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃━▆▅▅▅▅▆⎺▼⎺·╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂━◀▅▅▖▊▗▄▅▆▆▅▅▅▅▄▄▅▂▲▲▪▖▅▆▅▅▖▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▂▂⎽⎼▎▃▃▄▃▄▄▄▪▪⎼▅▅▍⎼⎼⎼▊▍▅▅▅▄▍▃▃⎽⎽╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂▂⎼▊▄▊▂▂⎺⎺▏▗▂▂▂⎺▎▎▃▃▂▊▍▌▄▄▄▍▋⎼▂⎺▄▄▂┊▏·▊ ▍│╵··▃▂▃▄▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅⎼⎽▃▃▍▄▌⎺⎺▆▋▃▍⎺⎺⎺▊▂╍▂▂⎽▋▍▂▂▂▃▍▋┹ ▆▂▄▂▼▆▅▄▄▄▄▄▆⎺⎺┊  ┈▖╏▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▂▂▂▊⎽▉▅▆▅▅▍▂▆▆▆▅▌▊▄▆▆▆▋▘▝▄▄▃▌▊▮▆▅▋▖▅▄▃▂━▄▄⎽▂▄▅⎺▋   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▲⎽▮⎺▌▲▋▄▄▄▃▄▊▄▄▄▄▍▋▄▄▄▄▘▅▍▂▂⎺▌▉▆▅━▊▄▝▋╵⎺▝▍┈▎┃▋▖▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▅⎺⎻▗▆▆▲▂━━▌▅▌▲▲▲━▎▌▝▅▅▅▌▖▗▆▆▆▌▉⎻╾⎼▂▅▃⎽⎽▍▝▋┊▌╏▎ ┕▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗━━━━▘▅▗▃▃▃▃▍▆▂▂▃▂▂▮▍▂▂▂▂▌▮▂▅▄▅▋┊⎼▂┈▏▅▃⎽▅▃▂▂▊▂▃▄▆▃▅┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▆▂▃▃▂▂▝▄▘■⎽⎺⎺▮▊▆⎺■⎺▋┒▃▆▆▆▆▍▘▂▂▂▂▋ ▏▊▆⎻▍▍▄▊▏▆▅▖▝▅▆⎺▏⎺ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▗▄▅▅▅▋▅▋▃▃▃▃▌▪▂▂▂▂▂▘▅▅▅▅▅▌ ▎⎻╾▃▌▊┣▌┌▍▊▌▊▌⎽▌▋▌ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▍▲▂▲▲▍▂▎▃▃▃▃▍▃▂▄▃▃▃▃━▅▄▅⎼▋ ▃▂┈▊▝▅▄▂⎽▂▘▊▎▊▖▄▍▆▂▄┊┈▋▊▋▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▅▃▂▃▆▂┱▼⎺⎺▃━━▆▅▅⎽▆━━▆━━▅▆▄▃⎽⎽▅▝▼┈┈▆▅▄▏▗▄▅▆┳▍▂▂▄▆▼▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅▃▂⎽▅▄▃▂⎽●┈⎽▂▄▆●▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄╶◆⎺▆▅▅▅▆⎺╲▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂▂▄▖▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▅▄▄▄▄⎺▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▂▂▂╾▎▃▃▄▃▄▄▄▃▃▃▅▅▗▆▆▆▆▌▗▄▃▃▗▂⎽⎽·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌━━▂▊▆▊⎺⎺⎺▆▪▗▅▅▅▅▪▎▆▆▅▊▍▌━━━▗▋▂⎺▆▃▂⎽⎺▏·▊ ▍·┄·⎽▂▃▄▅▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▂⎼⎼⎼▍▄▌▆▆▆▋▃▍▄▄▄▊▃▏▄▄▄▋▍▍▄▂▃▍▋▪▆▄▌▃⎽⎺▆▅▄▄▄▅▆━▍⎽┈  ┈▖╏▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▃▃▃▊⎽▉▅▅▅▅▍▃●━━━▌▊▝▃▃▄▋▍▂▂▂▂▌▊▆▅━▋▆▄▃▂⎽⎽┰▂▃▄▅⎺▏▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▂▂▂⎽▍⎽▋▄▄▃▅▄▊▄▄▄▄▍▋▝▄▄▄▌▝▆▆▆▆▌▉⎻━⎼▊▆▝▋▝▌▝▍┈▎╷▋▎╷▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅⎺⎺⎺▖▆▆▲━━▲▌▅▌▂▂▂▂▎▌▎▂⎽⎽▌▏▗▄▄▄▌▉╾▃⎽▚▃⎽▖▗▍▅▋┊▌▂▋▪·▂▍▝▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▃▄▄▄▍▅▗▂▃▃▂▍▆▆▆⎺⎺▆▆▍▅▆▆▅▍▘▝▄▄▄▋┊⎽▪┈▅▆▄▂▅▄▃▂▂▊▃▄▆▃▅▆┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━┻▃▃▃▃▝▄▘⎺⎺⎺▆▲▊▃▃▃▃▘▅▏▅▆▆▅▍▄▎⎽⎽┊▋ ▏▆⎻╾▎▍⎺▆▅▅▖▖▆⎺◀▗┆ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▗▄▄▅▄▋▅▋▃▄▄▄▌▖▅▂▂▂▂▎▃▃▃▃▄▋ ▎╾▃▂▌▎┄╎ ▎▊▌▏▌▎▗▌▍ ▊▏▎▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▍⎼▂⎽⎼▍⎽▘▆▆▆▆▍┹▅▆▆▆▆▃━▅▄▅⎼▋ ▎⎽┈⎺▝▃▂⎽▎▝▃▊▎▊▖▉▘▂▄▆┊┈▝▝▋▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃┅▄▄▅▄▄▘━━▆▅▅⎽▆━━▆━━▝▄▂⎽▆▄▃▌▎┈▆▅▅▄▏▗▅▆▍▋⎽▃▄▆┯▃▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▅▆▆▃▂⎽▅▄▃▂⎽⎽╲⎽▃▄▆◀▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▮⎺▆▅▅▆▆⎺⎺▊▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂▂▂▖▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▂▂▂▂⎽▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▂▂▂▂▎▃▃▄▃▄▄▄▃▃▃▅▅▌▃▃▃▂▗▲▂▂⎼▍▃┊▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▃━▲▊▅▊⎺⎺⎺▆▪▶▃▃▃▂┱▎▂▂▂▊▍▍▃▃▃▗┫╺▆▄▃⎽▪ ··▊ ▍│·▂▂▄▅▅▅▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▂▄⎼⎼▍▄▌▅▅▆▘▃▍▂▂▲▊▃▋▄▄▄▋▍▍▂▂⎽▌▋▆⎻━▌⎽⎺▆▅▅▄▅▅▆┈▎▝▂▖  ┈▖▆▂╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍⎼⎼▃▊⎽▉▅▅▅▅▍▃▄▄▄▃▌▊▌▂▂▂▋▍▆▆▆▆▍▊▅━⎼▗▅▃▂▂⎽▂┳▃▄▅⎺╴┊▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▂▂▂⎽▍⎽▋▄▄▃▅▄▊▂▂▂⎽▍▋▆⎺⎺▆▌▎▄▄▄▄▖▉╾⎼▂▊▍▝▋▘└▝▍┈▎·┈·▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▆▆⎺▊⎺▆▲▲▲▲▌▅▌▆▆▆▆▎▌▄▄▄▄▍▎▝▅▅▅▌▉▂┈▝▊▂┄▍▗▍▅▋┊▌▅▍┊▂▃▘ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▄⎻⎻▄▍▅▗▂▃▂▂▍▆▝▄▄▄▃⎺▍▅▅▅▅▌▘▎▂▂▂▋▉▏▖▅◆▄▂▆▄▃▃▂▃▝▄■▃▅⎺▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▆▃▃▄▃▃▝▄▘▆⎺⎺⎺▮▊▝▄▄▄▖▖▪⎽▂▂┈▍▗▅▆▆▆▋ ▅╺╾▃▍╏⎺⎺▆▅▅▎▋▎▋┕▏▅ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▗▃▄▄▄▋▅▗⎽⎽⎽⎽▋┱▃▅▆▆▅▉⎽⎽■▆■▖ ▎▂┈▊▌▉▉▌▉┱·▌▌▌╸▄▍▎ ▊▏▎▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▍⎼┰▪⎼▍⎽▍▅▅▅▅▍▊⎽▲▲━━▪━▅▄▆⎼▋ ▎▌▆▅▋⎽⎽▗▆▉▉▊▎▊┝▂▂▄▆⎺ ┈╲▂▘▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▃▆▃▃▂▃▅▮⎼━▪┰▎━━▆▅▅⎽▆━━▆━━▝▂⎽▅▄▂▊▌▆⎺▆▅▄▄╲▗▆┈▍⎽▃▅▆▆━▃▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅━▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▄▆▅▄▂⎽▅▄▃▂⎽⎽▂⎽▃▄▆▆▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─⎺⎺▆▆▅▆⎺▅▼╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂▂▂▖▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▖▃⎽┈·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▂▂▂▂▎▃▃▄▃▄▄▄▃▃▃▅▅▃▂▃▃▄▗▅▂▂▂▍▃╵▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▃━▲▊▅▊⎺⎺⎺▆▮▲⎽▄━⎽▆▘▂▂▆▊▍▍⎺⎺⎺▃▆▆▅▃▂▄┊ ··▊ ▍ ▂▂▄▅▆▆▅▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▂⎽▄⎼▍▄▌▆▆▆▋▃▍▃▃▃▊▂▄▆⎺▆▋▍▌▆▆▆▍▊▄━⎼▗▎⎺▆▅▅▅▆▆⎺┈⎽▂▃┊  ┈▖╏▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍⎼⎼▃▊⎽▉▅▅▅▅▍▃▂▂▂⎺▌▊▗▅▅▅▋▎▃▄▄▃▍▊━⎼▂▗▄▃▂▂▂▂╈▅▆▖▄▉ ▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▂▂▂⎽▍▲▋▄▄▄▅▄▊▆▆▆▆▍▋▂▃▃▃▌▎⎽▲▲▲▌▉▃▂▊▝▝▗┊⎺▝▍┈▎╏▎▝▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▆▆▆⎺▖⎺▆▲━━▲▌▅▌▄▄▄▄▎▌▃▄▄▃▌▎▍▂▂▂▋▉▪┈▆▉⎽▝▍▉▋▝▋┊▌▗▌▂▃▅⎽ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▄⎻⎻▄▍▅▗▂▃▃▂▍▆▮▅▲━▪⎺▍⎺⎽▮┈▌▖▎▆▆▆▋▉▆▅━▲▃▆▅▄▃▃▃▄▝■▃▅⎺▖▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━▃▃▄▃▃▝▄▘⎺⎺⎺▉▮▊⎽▂▂▂▊╶▎▅▅▅▅▍⎽▅▃▃▃▋ ━⎼▂⎽▍▍⎺▆▆▅▆▎◀▎┏▌▗▃ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▗▄▄▄▅▋▅▋▆▆▆▆▌▖▋▅▅▅▅▘▂▂▃▂▊▉▎⎽┈⎺▌▌▍▗▎▊▌▏▌▗▆▋▎┈▂▃▘▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▍⎼▂⎽⎼▍▂▍▆▆▆▆▍▗▖▂▂⎽▅▎▅▅▅▅▆▋┈▎▆⎻━▋ ▉▉┊▶▪▊▎▊⎽▂▄▆⎺╴┈▂▘⎽▘▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▃▃▂▃▆⎽▂▃⎽┈▎▝▄▅▅▄▅⎺━━▆━━▋ ▝▃▂⎽▊▋⎺▆▅▅▅▅▏▌┈ ▃▃▅▆▆▆━▃▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▉▆▆▆▆▆▃▃▄▄▅▅▅▄▃┳⎽▃▂▂▃▆▄▃▃⎽▝▄▃▂▂⎽▪▂▃▅▆▌┭▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄⎺⎺▆▆▆▆⎺⎺▆⎺╶▊▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂┷▂▂⎽▖▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▂▂▂⎼▎▃▃▄▃▄▄▄▃▃▃▅▅▃▂▃▃▄▊▅▆▅▅▖▃╵▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▲▂▂▊▘▊▂▂▆⎺▏▲╸⎽╺▅▅▗▄▄▄▄▍▗▅▄▄▍▅▅▃▂┉⎽  ··▊ ╴⎽▃▄▅▆▆▆▅▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▂⎼▃▃▍▄▌⎺⎺▆▋▃▍⎺⎽⎺▊▃●▃▃▃▌▍▌━━■▍▋━⎼▂▗┇▆▆▅▅▆⎺⎺┈⎼▂▃▄▖  ┈▖╹▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▃▃▂▊⎽▉▅▆▅▅▍▂▅▆▆▆▌▊▌▅▅▄▋▎▝▄▂▄▌▊⎼▂▪▗▃▃▂▂▃▃▼▉⎺⎺▊▝┊▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎽▂⎽⎽▌▲▋▄▄▄▆▄▊▄▄▄▄▍▋▍▂▂▂▌▗▍▂▃▂▌▉⎽▝⎺▊▖┓▋┈▊▝▍┈▎▊▉▊▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅⎺⎺⎺▖▆▆▲▂▂━▌▅▌▅▆▅▂▎▌▆⎺⎺⎺▌▄▆▆▆▆▋▉▝▆⎻▊▖▊▍▗▍▝▋┊▌▃▂▄▅▂▂ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▄▃▃▃▘▅▗▃▃▃▃▍▆▃▃▃▃▃▆▝▄▄▄▄▍▂▍▃▃▼▋▉⎻━⎼▃⎽▅▄▃▃▃▄▄▊▃▅⎺┈▪▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━┻▃▃▃▃▝▄▘⎽⎽▆⎺▮▊⎺⎺⎺⎺▆▖▎▄▄▄▄▍▘▝▃▃▃▋ ▃▂┈▊▍▋⎺▆▆▆⎺▎◀▎▉▍·▋ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▗▄▅▅▅▋▅▋▄▄▄▄▌▪▖▮╍⎽▂┣▃▆⎺⎺⎺▋ ▎▌⎺▅▌▍▌▌┡▍·▌▝▌▘▖▌▌▂▘▅▘▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▍▲▂▲▲▍▂▎▄▄▄▄▍▋▂▄▄▄▄▉▂▂▂▂▂▖ ▍⎻╾⎼▋▍╴┊⎺┩▪▊▎▊▃▅▆▘┅▎▂▃▃▚▋▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▆▅⎺⎺⎺▎▋▝▄▄▄▖▎━━▆━━▋ ▍▘┈▌▝▆▆▅▅▅▅▆⎺▌ ⎽▃▅▆▂▆▆■▼▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅╴▄▄▄▄▄▃▼⎺▼▼▅▝▄▃┳⎽▃▂▃▅▄▃⎽▂▅▝▃▂▂▂▃▂▃▅⎺▼▘┭▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺▆▝⎺▆▆▆▆⎺▄▄⎺┘╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂╍◀▅▅▖▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▲⎽⎼⎼▎▃▄▆▄▄▄▄▃▃▃▅▅▃▂▃▃▄▃▂▃▃▂┳▃╵▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂⎼⎼▋▘▊▂▂▂⎺▏▲╸⎺·▅▅▅▂⎽▂▮▗▂▂▂⎽▍▄▃▂⎽┉ ▉┊ ⎺┈▂▃▄▅▆⎺▆▆▅▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▄▄▃▃▍▄▍▲⎺⎺▋▃▗▆▆▆▆▃▅▃▃▃▋▍▍▃▃▃▌▋⎼▂⎽▄⎺▆▆▆▆┒▌▪⎽▂▃▄▄▖  ┈▖▆▂╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▂▂▂▊⎽▉▆▆▆▆▍▂▅▅▅▅▘▊▌⎺⎺⎺▋▎▍▂▂⎽▌▊▂▪▅▄▃▃▃▃▄▅▌▊│┊·▉┊▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎺⎺⎺⎺▌▲▋▅▅▄▄▄▊▃▃▃━▍▋▍▆▆▅▌▎▅▆▆▅▍▉⎺▅▅▊▄▌▌┊▊▝▍┈▎┢▎▉▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▅▅⎻▆▆▆┻▃▃━┛▅▌▄▄▄▄▎▌▃▃▃▃▌▏▃▃▃▃▖▉▆⎻━▉▏▍▍▉▍▋▋┊▌▂▄▅▂▃▖ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗━━━▃▘▅▗▄▄▄▃▍▆▂▂▂⎽▂╏▍▄▄▄▄▍▘▝▃▃▃▋┈━⎼▃▂▅▄▄▃▃▄▅┩╌▅⎺▖▆▋▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━▂▂▂▂▂▝▄▘⎽⎽▆▉▮▊▆▆▆▆▆▖▎⎺▼◀⎺▍╸▎┐⎺▊ ⎽▘┈▝▍▆▆▆▆⎺⎺▎·▎▉▍·▋ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▅▅⎺▆▆▋▅▋━━━━▌▄▄▄▄▄▅┃▂▃▃▃▃┛ ▎▆▅⎻▌╌▎▍▗▎▊▌┊▌┎▄▶▂▄▘▅┈▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▪▂▃▃━▍▂╽▂▂▂▂▌▊▝▄▄▄▖┃⎺▂▃▃▃▊ ▎─▃▂▋┒▎┻▉┨▏▊▎▝▅▆▍▌⎽▂▃▃▃▖▊▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆▅▆▆▆▆▎▗▖▅▆┈⎺▏▆▆▆▆▆▋ ▍▘ ▝▝▆▅▅▅▆▆⎺▌⎽▂▃▅▆⎺▂▆▅▆▼▃▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▊▂▃▃▃▃▚▝▃▄▄▃▄▝▃┳⎽▃▂▝▃▂⎽▆▅▄▝▂▂▂▃▂▄▅┳━▄▘┭▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯╶▅╴▆▆▆⎺━▝▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂╶▪▅▆▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅⎽⎼⎽▂▎▃▄⎽▄▗▄▄▃▃▃▅▅▃▂▃▃▄▃▄▃⎺▂▂▃╵▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎽⎽▂▋▄▊▃▃▂▊▘▲╸⎺·▅▅▅▅▄▅▆▗▄▂▂▄▍▂▂⎽┊╴  ··┅▂▃▄▆▆⎺⎺⎺▆▄▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄▂▂▂▂▎▄▍▂▂⎺▋▃▗▆▆▆▅▂▄▆▆▆▆▍▗⎺▆▆▌▋┹⎽▝▆⎺▆▆⎺┈▍▂▂▃▄▄▅▄▖  ┈▖╹▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘⎽⎽⎽▊▂▉⎺⎺⎺▆▍▂▄▅▄▄▘▊▅▅▅▅▋▎▗▅▅▅▌▊⎽▅▅▄▃▃▃▄▅▆▏▊┇╵▊▎ ▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅╶⎺⎺⎺⎺▘▲▋▅▅▅▅▄▊━━━━▍▋▌━━━▌▎▂▄▃▄▌▉⎺▆⎻▊▏┓▋┈▝▆▍┈▎·▎╻▏▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▄▆▆▅━▆▆▃▄▄▃┛▅▌▄▄▄▃▎▌▝▄▄▃▌▘▝▃▃▃▋▉⎻━━▉▏▍▍▗▌▅▋┊▝▄▅▂▃▃▖ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋▂▂▂▃▘▅▋▅▅▅▄▎▆▂┈┈▂▉⎺▍●┹▂⎽▌▅⎺⎺⎺▆▋┊⎼▂⎽⎽▅▄▄▄▄▅▂▏▉⎺┈▌▘▋▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━⎽⎽⎼⎼⎽▝▄▘▂▂⎽▖▮▊▅▅▅▅▘▄▄▅▅▅▅▎▃▗▄▄▄▋ ▏▊┊⎺▍⎺▆▆╴⎺▊▎┕▎▉▍·▋ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▄▂▂▂▗▆▅▆▆▆⎺▋▅▋━━━━▌▪▊▅▅▅▅▊▘▝▄▄▃▊ ▎▅⎻━▌▘▍▍▝▗┫▌▋▌▘⎽▃▄▅▅▅╲▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺⎽▝▃▄▃▃▘⎽╽▂▂▂⎽▌▗▖⎽⎽▂▃┣▄⎺▆⎺⎺▊ ▎▃▂▂▋┄·╷┹▋▄▊▏▆⎺┊▎⎽▃▃▄▃▃▂▊▂▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▄▅▅▅▅▎▋▃▄▄▄▄▊▂▂▂▂▃▖ ▍▘┈▆▝▅▅▆▆⎺⎺◀▊▂▄▆⎺■⎺⎺▅▄▄▆▼▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▊▲▂▂▲▪▚▌▃▃▃▃▖▎▃┳⎽▃▂▋▂⎽▆▅▄▃▋▂▂⎼▂▄▆⎺⎽━▄▘┭▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▄▃⎺⎺▼▼▄▄▄▄▃⎺▆⎺┳⎻⎺⎺▆▆·⎺▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▖▘▂▂⎺▎▃▂▂⎽▄▄▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▅▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂▂⎺▊▗▊⎼▄▃▖▅▲╸⎺·▅▅▅▅▄▅▆▊▅▅▅▅▖▂▘▘⎽╴ ▉┊⎽▂▃▅▆⎺▪■⎺▆▅▄▖▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗▂▆▂⎺▍▄▍▃▃▂▋▃▗▆▆▆▅▃▄▄▄▄▖▍▄▄▄▃▚┫▲┊╹▆⎺⎺■⎺⎽⎽▆▗▄▅▅▅▄▖  ┈▖╏▆╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▆⎺▅▊▂▗▂▂⎽⎺▍▂▅▅▅▄▄▊▃▃▂▂▋▎▍━━▅▌▊▆⎺▆▄▄▄▄▅▆⎺▌▊▪┍▎▉ ▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅┙▆▆▆▅▘▲▌▆▆▆▆▄▊▃━━━▍▋▝▄▄▃▌▎▝▃▃▂▌▉▆▅⎻▊╎▝▋┈▊▝▍┈▎▌▎╴╵▮▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝⎻⎻⎻▄▆▆▅▅▄▄▘▅▌▄▄▄▃▎▌▎⎽▂■▌▗▖⎺⎺⎺▌▉━╾⎼▉▏▍▍▗▌▋▋┈▝▆▂▃▄▄▖ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋▃▃▂▂▌▅▋━━━━▎▆▂┈┈▂▉⎺▍▅▅▅▅▍▂▄▄▄▄▖┊▂▪┈╸▄▄▄▅▆▂▃┓▊┊ ▌▆╎▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂▂▂▂▊▃▄▃▃▃▂▂▮▊▅▅▅▅▘▃⎽▂▂▂━▘⎺▝▄▄▄▋ ▏▮┈▆▍▆⎺┐▎┈▘▎◀▎╷▍▏┊ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▅▃▂▂▲▪▆▗⎺⎺⎺▆▋▅▋━━━━▌▪▊▂▂▂▂▎▘▎⎺⎺▉▊ ▎━━━▌▎▍▄▊▎▊▌▏▌⎽▃▄▅▆▆▅▘▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺▪▝▅▅▅▄▘⎺╽▂▂▂⎽▌▊▆▆▆▆▆▉▖▃▄▄▃▘ ▎▘┈⎽▋▌▘▉▘▂▂▘▘⎺ ┊▂▃▄▄▄▃▂▝▊┗▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▄▅▅▅▅▎▋▂▅▅▅━▊┈▃▃▃▃▊┊▍▌┈▆▝▅▆▆⎺⎺▍▚▂▄▆⎺⎺⎺⎺▆▅▃▃▄▆▃▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▊▂▂▂▲▂▚▌▎▘┈⎽⎽▆▆┳▆▆▆▊┈▝▄▄▃▃▋▃⎼▃▄▆▆▅⎽━▄▘┭▅▅▅▅▅▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▝▅▅▅▄▅▝▄▄▄▃⎺▆▗⎺⎺⎺⎺⎺⎺▅▝▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▖▆▆▆▆▗▃▂▂▂▲┱▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▅▅▅▆▆▂▂▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎺⎺▆▖▅▊▂▲⎼▪▅▲╸⎺·▅▅▅▅▄▅▆▆▃▃▂▂▂▌▲▘⎽┊⎽▪┈⎽▃▄▅▆▪⎺⎺⎺⎺▆▄▃▂▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄▆▅⎺⎻▍▄▍▄▄▃▋▃▗⎺▆▆▆▃▃▃▃▂▂▍▍⎼⎼⎼▝┫▲┊▼▆⎺⎺▍┈▶▂━▗▅▅▅▅▄┊  ▝▖⎺┒╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▆⎻▆▅⎺▗▃▂▂▂▍▂▅▅▅▅▘▊▝⎼▄▄▋▎▍▂▂▂▌▊⎺▆▆▄▄▅▆⎺├▃▏▊┇╸▋▝┊▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▅▅▄▄▘⎽▌▲⎼⎺▆▄▊▄▃▃━▍▋▍▂▂⎽▌▎▍▅▆▆▌▉▅⎻▄▊▝▝▌┈▋▋▍┈▎▊⎽╷▲▮▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂┗━━▃▂▆▆▆▆▅▅▅▅▌▅▅▄▄▎▌▎▆▆▆▌▏▄▄▄▄▌▉━─⎼▉▏▝▎▌▍┈▋┈▆▂▃▄▄▃▖ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▋⎼⎼⎼▃▍▅▋▄▄▃━▎▆▂▂▂▂▂▏▍▄▃▃▃▍▏⎽▅▄▄▋ ⎽╸┈▶▄▄▅▆▂▄▅▶▊┊┈▎╴▪▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▘▂▆⎺⎺▃▄▪▄▄▄▃▮▊▆▆▆▆▅▖▎▄▄▄▃▍▘▎⎽▂⎻▊ ▏▖⎺▆▍⎺▖▉▌┈▋▎◀▎▗▎▏┊ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▘▆⎺⎺▆▆▆▊▂▂▂▆▋▅▋▃▃━━▌▪▊⎺⎺⎺■▎▖▗▅▅▅▋ ▎━━━▌▌▪┊▃╴▊▌┊▝▃▄▅▆▆▅▅▘▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺▪▝▆▆▆▅▘▂┃▃▃▂▂▌▋▗▄▄▄▘▊▂▝▄▄▄▋ ▎▂┈⎽▋▋▌⎽⎽▂▄▘┝▍┈▂▃▄▄▄▃▂▪▝▊▎▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▅▆▆▆▆▎▋▝▃▃▃▃▗▅▎⎺┊▗▋╴▍▖▆▆▝▆⎺┈▋▎⎽▃▄▆⎺▂⎺⎺▆▅▃▂⎽▂▄▎▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▊▃▃▃▃▃▚▗▆▆▆▆▆▊▃▃▃▂▃▖┈▝▃▃▃▃⎽▂▃▅▆▆▄▅⎽━▄▘┭▅▅▅▅▄▄▅▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▝▂▂▂▂▃▝▄▄▄▃⎺▂⎺⎺⎺⎺⎺⎺▄▘▃▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▖▅▅▅▄▗▃▃▃▃▂▂▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▅▅▅▆▅▃⎽▄▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▆▅▅▗▅▊▃▂▂▂▘▲▶⎽━▅▅▅▅▄▅▆▅▲▮╸⎻▃▌▲▘⎽┊┈⎽▂▃▄▅▆┈⎺⎽▂■▆▅▃▂▄▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅⎻▆▅▅▘▄▍━▅▄▋▃▍▂▂⎽▊▃▂▂▂⎽⎽▗▃▂⎺⎽▄▆▪┊⎽▗▎⎽■▗▂▃▅▗▆▆▅▄▃▖  ▝▖▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▅▅▄▄┷▗▄▃▃▃▍▃▎⎺▆▆▌▊▄▃▃▃▋▎▍⎺▆▆▌▊▆▆▅▗▅▆⎺ ┃┫▶▊▶▍▝▉ ▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▝▃▃▃▂▍▲▗▂▂⎼⎽▃▊▅▅▄▄▍▋▍▂⎺⎺▌▎▗▄▄▄▌▉⎻▄▄▊▆▝┪╴▝▊▍┈▎▗┛⎽╹▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▲▂⎼⎼⎼▆▆⎺⎺⎺▆▆▅▌▃▆▆▂▎▌▆▆▅▅▌▏⎽▅▅▄▌▉─⎼⎼▉▏▎▍⎽⎽▃▘┊▗▄▄▄▄▃▂ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▂▂⎽⎽▍▅▋▅▅▄▄▎▆▎▃▃▃▖▏▍▃▄▆▂▘▘▎▂▘┈▋ ⎽▪┈⎽▅▅▆▂▄▆⎺▍▊┊ ▌╷▶▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▆▆▆▅▅▝▄▎━━▂┱▮▊▏⎺⎺⎺▋▖▎▃▃▂▂▍▄▆▆▆▅▋ ▏▖┈⎺▍▍▂▉▌▋▘▎▎╷▍▏┊ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▘▅▅▅▅▘▆▊▂▂▂▂▋▅▋▅▄▄▄▌┳▊⎺⎺⎺▆▊▂■■▆━▋ ▎━━━▌▊┋▖▍▎▊▌▏▝▅▆▆▆▆▅▄▎▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺┑▲⎺⎺┯⎺▌▂▎▄▄▄▄▌▋▃▃▂■▪┫▆▎⎽▘┈▊ ▎⎽┈⎽▋▗▂▂▃▄▆▍▊▍┊⎼▄▅▄▄▃⎽▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▖▅▆⎺⎺▎▋▍▂▂▂▂┠▂▅▅▄▄▘╴▍▖⎺⎺▆▖▎ ▪⎽▃▅▆▼⎺▼⎺⎺▆▄▂┈ ⎽▂▘▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅▉▅▅▄▄▄▃▗▅▅▅▅▅▊ ▄▃▃▃▊┈▝▃▃⎼⎼▂▃▅■⎺▅▄▅⎽━▄▘┭▅▅▅▼■▼▄▆▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▎╾┯⎼⎼▂▪▅▄◆┯●▼⎺⎺⎺▆⎺┬▄▘▃▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▖▄▄▃▃▗▃▅▄▄▃▖▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▅▅▅▆▄▂⎽▄▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▋▅▄▄▃▘▖▄▃▃▃▅■▂▂━▂▅▅▅▄▅▆▅▅▋▝⎺▃▌▲▘┈┉▂▂▃▄▆⎺⎺⎽■▂▂⎺▆▄▂▃▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄━▄▄━▘▄▌━━━▋▃▍▄▃▃▊▃▂▂▂⎽⎽▗▆▆▆▅▅▆▪┊⎽▋▂▂⎽▂▄▄▆▌▆▆▅▄▂┈ ┈▆▖·▆▆▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▝▄▃▃▂▂▗▅▅▄▄▍▃▃▂▂⎽▌▊▄▃▃▂▋▎▅▅▄▄▖▊▆▅▅▋▆⎺⎺▝▖▍▌▊▏╹▊⎼┊▌   ▝▏▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▊▂▂⎼⎼▍⎽▗▃▃▂▂▝▊⎺⎺▆▆▍▋▍⎽⎺⎺▌▎▍━━┳▌▉▅▅⎻▊▆▝▊┈▌▝▍┈▎⎽▂⎽·▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂⎽▂▂▂▂▆▆▂⎽⎺⎺⎺▅▌▅▄▄▄▎▌▆▅▅▅▌▘▝▃▂▂▋▉─━─▊▎▅⎽⎽▃▄⎽┈▗▅▅▄▄▂⎽ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗⎺⎺▆▆▍▅▋▆▆▆▅▍▆●▂▲▲▪⎺▍▃▆▅▂▘▃▎▗⎺▆▌┊▂▪⎽⎽▆▆▃▄▆⎺▉▍▊┊┈▎ ▶▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▅⎻⎻▄▃▝▄▍▄▃▃▃▮▊▝▂▂▂▘▖▎▃▃▂▂▍▂▄▄▃▃▖┈▏▅┈▖▍▎▍▋▋┈▋▎╷▎▉▍·▃ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▝▄▃▃▃┛▆▊▄▃▃▃▋▅▗⎺▆▆▆▌┳▊┓⎺▆▆┃●▝▃▃▃▊ ▎━━┙▌▌▗▍▋╸⎽▌▎▝▆▆▆▆▅▘▃▎▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺┑▂▂▃▼▼▼▂▍▃▃▆▆▍▋▃▂■■▪╋▘▎⎺╴┊▊ ▎▂┈⎽▋⎽▂▃▅▆▍▍▂⎽▪▪▅▅▄▃▂┈▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▅▂▂▂▂┈▎▋▍▂▂▂⎽▊▖▂▆▆▆▊╴▍▪┈┝▊▌▋┈▂▃▅▆━▼▅▼┈▆▄▃┈ ▆▄┛▌▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅⎼⎻⎺·▅▅▃▗▅▅▅▅▅▊┈⎽┈⎽▘▊▏▝▄⎼▂▃▄▆▲▝▆▅▄▅⎽━▄▘┭▅▅⎺▆▅▆┑⎺▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▍┭⎼▄▄▂▝▅▅▅▄▅■⎺⎺⎻▪▄─▄▘▃▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▆▃▃▂▃▃▃▆▅▅▄▖▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▅▅▄⎽▂⎽⎽▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▃▃▂▂▄▊▅▅▄▄▅▗▄▄▃▃▆▅▅▄▅▆▅▅▋▝⎺▃▌▲▘▂▂▃▄▅▆⎺▏┈▂■▂⎺▆▅▃▂▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅━━─▂▍▄▌▄▃━▋▃▍▃▂━▊▃▃▃▂▂⎽▋▅▅▄▄▄┫▲⎽⎽▌▃⎼▂▄▅▆▎▌▆▆▅▃▂┈ ▆▅▖▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▌▂⎼⎽▊⎽▗▃▆▅▅▎▃▲▅▄▃▌▊▝▄▃▃▋▎▄▃▃▂▖▊▅▅▄▋▖┈┃▝▝╷▏▊┃┆▋▆╷▌   ▝▗▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▊⎼▃▂⎺▍▲▌▪▄▄▖▝▊▃▂▂▂▍▋▍▂⎽⎺▌▎▍▄▄▃▌▉▅▅▆▊▖▝▋┈▊▆▍┈▂▂▂⎽▶▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂◀⎽⎽⎺▆⎺▆▃▂▂⎽▋▅▌┈⎺▆▆▎▌▎▆▆▅▌▘▍⎽⎽⎽▋▉━━━▊▏⎽▂▃▅▂▃┊▗▅▅▄▃■▘▝▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▅▼⎻⎻▆▅▋⎺╴·▆▍▆▅▅▄▄▄▆▍▃▃▃▂▍▖▆▅▅▅▋ ▂▂▂▃⎺▃▄▆╴┈▉▍▊┊ ▌╷▋▝┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▗▃▃▂▂▂▝▄▘▅▅▄▄▲▊━▅▅▄▖▖▎▄▃▃▃▍⎽▃▂▲━▖ ▎▊┈▋▍▌┌╹▊┈▍▎╷▎⎽▍▏┊ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅╺▂▂⎼⎼▌▆▉━▲⎼⎼▋▅▋▂▃⎽▂▋▪▊┊⎺⎺⎺┃▘▎▂⎽▘▊ ▎▃⎻▘▌▋▊▉▍⎽▃▘▌▆⎺⎺▆▆▄▃▖▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺■⎼⎺┑▂▂▂▄▄▄▂▘▆▅▅▅▍▊▗▃▃▃▼╋▂▎▅▅▅▋ ▎▄▂▃▋▃▄▅⎺▎▎▂⎼⎼▪▅▅▄▃▂┈┈▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃┅▂━▲━━▘▋▝▃▂▂▂▊⎽▅▄▄▄▊╴▍▖┈▋▊▌▋▂▄▅⎺▼▼▄⎺⎺▆▅▃⎽ ▆▅▃▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▗▆▆▆▆▅▗▆┖┈▃⎺▊┊⎽⎼▂▃▄▆⎺■▝▆▅▄▅⎽━▄▘┭▅▅▆▄▃▃▅▘▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▝▂▲▲▲▂▝▃▃▂▂▃▅■┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄⎺ ╴▪┏╸┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▆▂▲⎽▪▃▃▆▆▅▅▎▄▃▃▃▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▅▄▄▃▂▂⎽⎽▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂⎼⎼▋▅▊▆▅▅▄▘▗⎺▆▅▅▗▅▅▄▅▆▅▅▋▝⎺▃▶▂▂▃▃▄▅▆◀▍▲▂▂━▂⎺▆▄▂▂▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▲⎼▃▃▍▄▌▅▄▄▋▃▍▆▅▄▊▃▄▄▃▃▂▊▅▄▄▃▄┫━▂▃▌━▂▄▅▆▊▎▌▆▅▄▂⎽ ┈▅▄■▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▃▂▄▊⎽▗▄▄▃▄▎▂▝▄▃━▌▊▃━▲⎼▋▎▃▂▂▲▌▊▌▮▪▋▃┈▊▉│▊▏▊●┣▝ ┈▋   ▝▗▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▂┊⎺▆▍⎽╺━▲━▪▝▊▂▅▅▄▍▋▝▃▃▂▌▎▝▃▃▂▌▉▆▆⎺▊▍▝▋┈╵┈▍┈▂▂▂╸▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▆⎺⎺▆▅▆▆▄▃▃▂▌⎽▌▃▃▂▂▎▌▎⎽⎺▖▌▎▍⎽⎺┳▋▉┅⎻⎻▊⎽▂▃▅▆▃▖┊▗▅▄▃▂▅▘ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗⎻━━⎻▘▅▗┈⎽⎺⎺▍▆▖⎺⎺⎺▆▏▍▅▅▄▄▍▖▅▄▄▄▌┊▃▃⎼╼▃▄▆⎺▖┈▉▍▊┊ ▌▘⎺▆▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▆▂▃━▂▂▝▄▘▆▆▅▅┗▊▅▄▄▃▘▃▎■━▅▄▘▏▂▅▄▄▋ ▏▊┈⎽▎▍▆▊▍┈▍▎╷▎⎽▍▏┊ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌▃▂▃⎽▌▆╺▃━▂━▋▅▋▅▅▄▄▌▪▊▂▃⎽⎽▎▘▎⎽⎺▪▊ ▎▅▆▊▌▊·⎽▂▃▅▘▋⎺⎺⎺▆▅▃▂┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆⎺⎺⎺⎺▆┑▂▂▂▄▄▄⎽▎▂▂⎽▅▍▊▌▅▅▄▘▊▲▅▄▄▄▋ ▎▃⎼╼▝▅▆┈▊▎┻▃▲━┯▅▅▄▂╴┈▆▋▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆▆▅▅▅▅▘▋▝▅▄▄▖▊▂▄▃▃▃▊┊▍▏ ▌▊▌▂▄▆⎺▼▄▃▄⎺⎺▅▃▂ ┈▅▃▂▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▌▎▅┊▗⎺▗▅⎺⎺▆▆▘⎽▂▃▄▅⎺┈▲■▝▆▅▄▅⎽━▄▘┭▅■▅▃⎽⎽▃▘⎻─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▝▄▃▃▃▄▋▂▂▲━━▃⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄⎺ ╴▪┣⎺┈▎⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅━▄▃▃▃▃▘⎺▆▆▎▖▂━▮▅▅▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▆╸▃▄▂▂▂▂⎽⎽▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎼▃▂▋⎼▊⎺▆▆▅▆▋▄▃▂⎺▗▅▅▄▅▆▅▅▋▝⎺▃▃▃▄▄▄▆⎺⎽▲▂▃▃▃▲⎺▆▅▃▂▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▃▂▂⎽▍▄▍▆▅▅▋▃▍▂⎺⎺▊▂▅▆▅▅▖▗▄▄▃▃▘▋⎼⎼╼▂▃▄▅⎺⎺▊▎▌▆▄▃⎽▘┈▆▄▂▮▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍⎺⎽▆▊▂▗▅▄▄▃▍▃▌⎺▆▆▌▊▗▄▄▃▋▎▃▂▲▪▌▊▂▂▂▋▃┈▉▌▌▍▏▊▍▝▍╴▆▖   ▝▂▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎺▆▼⎻▍⎽▝▃▃▂━▝▊▅▅▄▄▍▋▍━▅▄▌▎▝▃▂▂▌▉┊▄▆▊▊▝┫▉⎽▂▎┈▃▂⎽·▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▆▆▆▅▅▆▆▲▄▄▃▌▅▝▃━▲━▎▌▝▃▂▘▌▎▍⎺▅╍▋▉▅▅▆▉▂▄▅▂▃▅▖┊▗▅▄▂▆▄▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▅─⎼▃▘▅▗▂▂▼▂▍▆▏▄▃▃▂▮▍▏⎺▆┈▍▖▅▄▄▃▍┈⎼╼━⎻▅▆⎺┑·┈▉▍▊┊ ▌▎▆▄▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅━⎼⎼▃▄▝▄▘┈┈▆▄▮▊▎▮⎺⎺▊▅▄▄▄▃▃▘▏▲▅▄▄▋ ▎▶⎽▖▍▌▝▍▌▊┃▎╷▎⎽▌┊▎ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌┈┊▆⎺▌▆▆▅▄▃▃┛▅▋▅▄▄▃▌▖▊▄▄▃▃▎▘▎⎽⎺┅▊┊▎▆┈▉▌▎⎽▂▄▅▆▍╿⎺⎺▆▅▄▂▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺┳╴▆▆▆▆▪▂▂▂▄▄▄⎽▪▅▄▃▃▍▗▖⎺⎺⎺⎺┠▲▅▄▃▄▋ ▎╼━▘▅▖▍┈⎽▪▲▲▲▮▅▅▄▃⎽⎺┊▘▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▅⎺▆▃▋▄▃▃▂▂▊■▄▃▃▂▊┊▍▘┈▂▝▃▄▆■▼▅▄▃▼⎺▆▄▂┈┊▅▃⎽▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▌▃▂▂⎽▖▗▅⎺▆▆▆▘▃▄▅▆▅▘⎽▲●▝▆▅▄▅⎽━▄▘┭■▅▃⎽ ⎺⎽▂▝─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▅▄▆▆▆⎺▋▂▂━▪━▃⎺┳⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄⎺▉╴▪▍⎺▆▲⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴▘╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▄▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▄▄▃▃▃▃▎▂⎺▆▎⎽▄▃▃▂┱▃▂▃▃▄▃▄▂⎺▂▂▃╵▏·╸▗▅▂▂▃▃▂▂▃▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂▂⎺▋▄▊▂⎺▆▆▘▊▃▂▲▂▎▄▲⎽▅▆▅▅▋▝⎺▗▄▄▅▆▆⎺┈▂▃⎽▃▃▃▲▆▅▃▂▃▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄▂⎽⎺⎺▍▄▍⎺▆▅▋▃▍━▄▄▊▃▅▂⎺⎺▋▋▅▅▄▃▘▋┷━▄▃▄▆⎺▆╷▊▎▌▅▃▂⎽▃⎺▅▃▂┳▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▆┯⎻▊▂▗▆▅▅▄▍▃▄▃▃▂▌▊▌⎺▆▆▋▎▃▃▂━▌▊▃⎼─▋▖┈▊┝▝▖▏▊┃▎▋┮▅▖   ▝▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▅⎻▆▅▘⎽▝▄▃▃▃▝▊▂▂┭⎺▍▋▍▅▄▄▌▎▝▄▃▂▌▉⎽⎽▂▊▃▝▊⎽▂▃▎┈▂▂┛▉▝▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▅▄▄▄▆▆■━▅▄▌▅▌▆▆▅▅▝▌▃▆▅▄▘▎▍⎽▪⎺▋▉⎺┈▆╲▄▅▂▄▅▆▋┊▗▄▃■▄▂▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▄▃▂▂▌▅▗▃▂▂▂▍▆▝▃━■■▏▍▃▃▂▂▍▖▅▅▄▄▌┊━⎻▅▆⎺⎺▎▉▏┈▉▍▊┊ ▎⎺▅▃▎▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅⎼▃▂▂▖▝▄▘┊▆▆▆▎▊▝▄▃▃▖▖▘⎺⎺▆▆╿⎽■━▄▄▋ ▂▃⎼┘▍▌▌▊▌┈⎽▎╷▎⎽▌▗▘ ▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌┈▆▆▆▍▆▅▆▅▄▄▘▅▋⎽⎽⎺⎺▋▅▄▄▃■▪┨▘▎⎽⎽┉▊┊▎▌ ⎽▌⎽▃▄▅▆⎺▖▊⎺⎺▆▄▃┈▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▃▆▅▅▄▘┑▂▂▂▄▄▄▂▝▅▄▄▄▘▊▝▃▃▂▂┣┙▎▅▄▄▋┈▎⎻▅▚▋▍▊┈⎼▲▂▂▮▆▅▄▃▂▉▆▄▂▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃▗▍⎺▆▆▅▊⎽▅▄▃▃▊┈▍▘▖▂▃▅▆▼▃▆▅▄▼▼▆▄▂┈┊▅▃┈╶▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▝▃▂▲▲▂▗▆▝⎺⎺▆▘▅▆⎺▄▄▅⎽▲●▝▆▅▄▅⎽━▄▘▆▄▂ ┈▅▃▉▎─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎┚▄▝▃▂▂▲━▄⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄⎺·┆⎺▄▅▅⎺▲▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴▘╵╴┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▗▅▆▆▅▅▅▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▃▄▃▃▃▃▎▂▂▆▎▄▆▆▅▅▗▃▂▃▃▄▃▄▂⎺▂▂▃╵▏▂┈⎽▂▃▃▃▃▂⎽▃▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂▆▆▖▄▊▂⎺⎺▉▘▋▆▅▄▄▆▗▄▃▂▂▅▅▋▝⎺▃▅▆▆▪▗⎽▂▃⎼▄▄▃▂⎺▆▄▂▃▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄⎺⎺⎻▆▘▄▍⎽▆▆▋▃▍▅▄▃▊▃▗▂▲⎼▋▗▆▆▅▄▍▋⎻▅▆▗▆⎺▄▪⎺▊┏▗▄▂▲▄▃▆▄▂⎽▆▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍┯┱⎻▗▂▗⎺▆▅▅▍▂▝▃▅▅▌▊▌▄▃▂▋▎▅▄▃▃▖▊┶━▄▋▖┈▊▉▊▊▏▊▶├╴▆▄▂  ┈▝▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎻▆▅▅▘▮▗▅▄▃▃▝▊━▄▄▃▍▋▍⎽⎺⎺▌▎╏▅▄▄▌▉▂▃⎼▊▄▂┹▂▃▃▘┈▂⎽▎⎺▉▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▄▃▃▃▆▆▪━━▅▌▅▌▃▂▂⎺▎▌▆▆▅▄▌▘▝▂⎽⎽▋▉▪┈▮▂▆▂▄▅▆▆▋┊▗▄▂▅▃⎽▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▃▂⎼⎼▍▅▗▄▃▂▂▍▆▖⎺▆▅▅▏▍▄▃▂━▘▗▎▆▅▅▌▉▅▆⎺▃▎▌╴▋╴┈▉▍▊┊ ⎺▆▄⎽▖▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃━▃▂▂⎽⎺▃▄▘┊┊▆▆▏▪▄▃▃▆▖▖▏▄▃▂▂▍▂▃▂■━▖ ⎼╼━▘▍·▄▊╴⎽⎽▎╷▎╶▌·▋┈▍┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▗▆▅▅▄▍▆▆▆▆▅▅▘▅▋▄▃▃▂▌▪▖⎺⎺▆▆▊▏▝▂▂▃▊┊▎▊⎽▖▝▃▅▆⎺⎺▌●╿⎺▆▅▃⎽┈▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▅▅▄▄▃▘┑▂▂▂▄▄▄▂▂⎺▼⎺⎺┻▊▋▃▂▆▆●▃▎▆▅▆▋┊▍▖┊▊▋▍▪⎼▂▂▂▂▆▅▅▃▂⎺▆▄▎▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃▋▝▂▂┈⎽┃▃▂▅▅▄▊ ▍⎼▂▄▅⎺▼▄▃▆▅▃▼▆▅▃┈┊▆▄┈┈╶▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▼⎺▆▆▅▆▊┈▏┈▂⎺▉⎺▅▄▄▄▅⎽▲●▝▆▅▄▅⎽━▄⎺▆▄▂┈┊▅▃▘▉▎─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▄┹▄▃▃▃▃▅⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄◀▉╸⎺▆▄▄▅▆▮▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶ ┚│▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▗▅▆▆▅▂⎽▅▄▄▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▃▄▃▃▃▃▎▃▂⎺▎▖▄▃▂▂▎▃▂▃▃▄▃▄▂⎺▂▂▃┊⎽⎽▂▃▃▄▄▄▃▂▂▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎺▆▅▖▄▊▃▂⎺⎺▅▋▃▂⎺▉▪▘⎺▆▅▅▆■▋▝⎺▗▆⎻▎⎽▍▃⎼▄▃▄▄▃▂⎺▅▃⎽▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗⎺▆▆▆▘▄▍⎽⎺▆▋▃▍⎽⎺▆▊▂▆▆▅▄▋▗▖▆▆▆▍▋▅╴⎺▌┈⎺┒▅┍▊▮▗▃▂▅▄▆▅▃⎽┵╸▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▅▅▅▗▂▗⎽▆▅▅▍▃▆▆▅▅▌▊▗▄▃━▋▎▗▅▅▄▖▊⎻▅▆▋▖┈·│┕▉▏▊┠▍▆▅▃▂  ┈▄▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎻▅▅▄▘▮▋▅▄▄▃▝▊▅▄▃▃▘▋▂▅▄▄▌▎▗▃━━▌▉─━━▊╵⎽▂▃▄▄▘┈▎┟▋╵⎺▉▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝▄▃▂▂▆▆▪━━━▌▅▌━▅▄▄▎▌▃▂▂⎺▌▎▝▄▃▂▋▉⎽▂▃⎼▃▄▅▆⎺⎺▋┊▗▂▆▄▂┑▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▂⎼▃▂▍▅▋▄▃▃▂▍▆▄▃▂▂⎽▏▍⎺⎺▆▆▌▘▎▂▗⎺▌▉⎺▖┈╻▎▋ ▍▏┈▉▍▊┊┈▆▄▂▅▖▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▘⎽⎽⎺⎺▄▄▘┈┊⎺⎺▏▍▖⎺▆▆▅▄▎▄▃■■▍▄▅▅▄▃▖ ▏⎻▅▚▍▎╴⎽╴⎽▂▎╷▎╶▋┊│┈▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▗▆▅⎻▄▍▆▆━▆▅▅▘▅▘▄▃▂▂▘▖▊▄▃▂▂┃⎽▎▄▄▃▋ ▂▃⎼╸▄▅▆┈▋▌▎▋╿▆▅▃▂▖ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▅▄▃▃▃▎┑▂▂▂▄▄▄▂▂▄▄▃▃▃▗▖⎺⎺▆▆▎▘▎⎺┊┊▊╴▎▍ ▊▋▍⎼▂▂▂⎽⎽▆▅▄▂▶⎺▅▃▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃▝▃▃▂▂▃▄▂▄▃▂▆▋⎽▂▃▄▆⎺▝▄▄▃▆▅▼▆▅▃⎽┊▆▄·┈┈▖ ▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▊ ▃▂⎽⎽▉●▃▄▄▄▅⎽▲●▝▆▅▄▅⎽━▼▆▄▂┈┊▅▃▘▋▊▏─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▅▆▆▅▅▅▮⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄◀·⎺▆▅▃▃▄▆▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚╴╄╶ ┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽⎼▅▆▆▅▗▃▂▂▗▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▃▄▃▃▃▃▎▃▂⎺▎▊▃▂⎼⎼▎▌▃▂▂▪▃▄▂⎺▂▂▃▂▂▂▃▄▄▄▄▄▃⎽▂▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎺▆▅▖▄▊▃▂⎺⎺▅▋━▄▃▖▄▎╾▄▃▊▆▂⎽▪⎽▗▖▂▂⎼▍▄⎽⎽┷▄▃▂┊▆▄▂⎽▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗⎺⎻▆▅▘▄▍⎽⎺▆▋▃▍▄▃▂▊▃▋▃▂⎺▋▍▝▃▂▂▌▋▅┈▂▌┈▝▝⎽▝▊⎺▗▂▗▃▅▅▃▂▮▮▆▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘┱▆▅▗┺▗⎽▆▅▅▍▃▃▂⎽▆▌▊▌⎽⎺▆▋▎▍⎺⎺▆▌▊▆▆▅▋▖ ▊▉▝⎽▏▊┇⎺▅▃▂▗╸┈⎺▄▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗╺▅▄▃▘⎽▋▅▄▄▄▝▊▮⎺▆▅▍▋▆▅▅▄▌▎▍▅▄▄▌▉▄▄▅▊▂▃▄▄▄▄▍┈▎▉▋▝╵⎺▏▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝▃▃▂▂▆▆▄━━━▌▅▌▅▄▃▃▎▌▄▃▂▲▘▏▃▃▄▃▌▉⎼⎼━▄▄▅▆⎺┈⎺▋┊▗■▄▂⎽▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅╺⎼⎼▃▂▍▅▋▄▄▃▃▍▆▎▆▅▄▄▪▍▅▃▃▂▌▎▝▃▂▂▋▉▏▪▂▗▎▋╶▉⎽┈▉▍▊┊⎺▅▃▆▃▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂⎽⎺⎺▆▄▄▘⎽┊⎺⎺▲▪▃▂⎽▂▊▉▪⎽▼┱▆▌╸▎⎺▆▆▋┈▆■┈▌▍▍⎽▂⎽⎽⎽▎╷▎╷▌▂⎺▆▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▗▅⎻⎻▄▍▆▆▼▆▅▅▅▅▄■▆▆▅▅┱▃▅▄▃▃┫▖▗▃■■▋ ▎━━▘▆▖⎺ ▋▊▪▍▍▝▄▂▍▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▅▄▃▃▂▆┑▂▂▂▄▄▄▂▂▄▄▃▃▃▊▝▄▃▃▃┃▮▝▂▃┈▊┈▎▊┈▃▝▂▂▂▂▂⎽▆▅▄▂┈▆▅▃⎽▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━╍▆▆▆⎺▊▄▎▆▅▅▘▃▄▅▆·┒▆▄▄▃▆▆■▅▃▂┈▆▄▂▊┈▅╶▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▊⎽⎽▅▄▖▂■▃▄▄▄▅⎽▲●▝▆▅▄▅⎽━▆▅▃┈┊▅▃▂▌▋▊▎─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▼▼▼▼▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶▚▄◀⎺▆▅▄▃▃▄▅▲▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚┤└╶ ┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▗▅▆▆▅▗▅▄▃▖▅▃▃▄▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▃▄▃▃▃▃▎▃▂⎺▎▖▅▄▃▃▎▗▆▅▄▄▃▄▂⎺▂▂▂▃▃▄▄▅▅▅▄▃▂⎽▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎺▆▅▖▗▊▃▂⎺⎺▪▋▄▃━┑▅▎▅▄▃▘■▄▄▃▂▘▋⎼⎼╼▂▂▂⎽━▄▃┊▆▄▂▝┊▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗⎺⎻▆▅▘▄▍⎽⎺▆▋▃▍▃▅▄▊▃▏▃━━▋▍▃▂▲⎼▍▋▲▂⎼▌┈▅▝⎺⎺▆▆▂▲▄⎺▆▄▂⎽⎽▄╸▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▘▅▆▅━┷▗⎽▆▅▅▍▃▲▄▃▃▌▊▂▅▄▃▋▎▝▄▃▂▌▊▄▋⎼▋▖┈·▉▝▖▏▊⎺▆▄▂▲▄▪┊⎺▄▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎻━▄▄▘▮▋▅▄▄▃▝▊▃▂⎽⎺▍▋▝▃▂⎽▋▎▍⎽⎺▌▉▆▆⎺▊▃▄▄▅▄▃▎┈▎┆▎┕⎺▆▎▍▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝▃▃▂▂▆▆▪━━━▌▅▍⎺▆▆▅▎▌▎⎺▆▆▌▃▆▅▄▄▌▉━⎻▄▅▆⎺⎺⎺┈▆▖┊▆▅▃⎽▪▋▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▂⎼▃▂▍▅▋▄▃▃▂▍▆╏▄▄▃▃▏▍▅▄▃▃▍⎽▃■▲▪▌▉▂▃⎼▗▎▋╴▎▖┈▉▍▊⎺▅▃■▄▂▏▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂⎽⎺⎺▆▄▄▘┈┊⎺⎺▏▉▂▅▄▃▋▃▎▅▄▄▃▍▘▝▂⎽┹▊┊▎▊┈⎽▎⎽⎽▂⎽⎽⎽▎╷▎╷▍⎺⎺▅▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▗▅⎻▄▃▍▆▆━▆▅▅▘▅▅▅▄⎺⎺▅▖┫▂⎺⎺▆▎▘▎▆▅▆▋┈▎▅▆▊▌▎▎ ▌▊⎺▍▍▝▂⎽▘▎ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▅▄▃▃▃▎┑▂▂▂▄▄▄▂▂▄▄▃▃▃┻▅▄▃▃▄▉▖▂▅▅▅▋ ▎▂⎼▂▂▃▃▂▂⎽▆▅▄▂┈┈▅▃▂⎽▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅▆▊┈⎽▃┈▗▗▅▆⎺▘▄┒▆▄▄▃▘⎺▆▄▂┈▆▄▂┡⎺▅▄▂▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅╇▅▄▃▃▄▆■▃▄▄▄▅⎽▲●▝▆▅▄▅⎽┯▅▃⎽ ▆▄▂⎽▌▋▘▏─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶╻▅·⎺▅▄▃▃▃▄▅▲▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿⎽▅⎻╷·┘╴┚│▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▗▅▆▆▅▘▆▅▅▖▅⎽▮▅▅▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▃▄▃▃▃▃▎▂⎺▆▎▖▆▆▅▄▎▍⎼▂⎺▊▃▄▂⎺▂━▃▄▅▅▅▅▅▅▄▃▃▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎽▆▆▖▄▊▂⎺⎺⎺▆▋▆▅▄▼▮▎▂⎺⎺▊▗▖▆▆▅▌▋━┷▄▂▂▂▲╾▃■⎺▅▃⎽┈▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▗⎺⎺▆▆▘▄▍⎽▆▆▋▃▍▄▄▃▋▃▏⎺▆▅▋▍▆▅▄▄▖▋⎼⎼╼▌┈▊▋╴⎺▅▅▃▄▃▆▅▃▂⎽⎽▮╸▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▅⎺▅▗▂▗⎺▆▅▅▍▃▃▃━━▌▊▅▅▄▄▋▎▗▃━━▌▊⎽▂▂▋▖ ▊▉▝┪▶▊⎺▅▃▲▄▃▂⎺⎺▝▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎻▅╾▄▘▲▗▅▄▃▃▝▊┭▄▃▃▍▋▄▃▂▲▘▎▂▄▃▃▌▉┈▅▍▝▄▅▅▅▄▃▍┈▎┇▎▪⎺▆▎▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▝▄▃▃▂▆▆▪━━▅▌▅▌▂⎽⎽⎺▎▌▝▄▃▃▌▘▍⎽┊▗▌▉▅▆▆⎺⎺▆⎺⎺⎺▆▖┊▝▃⎽▂╵▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▂⎼⎼▄▍▅▗▄▃▂▂▍▆▏▆▆▅▅╍▍▂▪⎺⎺▌▄▆▅▅▄▋ ⎼╼━┛▎▋▉▊▖┈▉▍▊▆▄▂▅▃⎽┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂▂⎺▄⎺▃▄▘┊┊▆▆▮▉▄▃▂▲▗▅▃▆▅▄▄▖⎽▶■▅▅▋ ▏▶▂▖▎▂▂▂⎽⎽⎽▎┊▎╶◀⎺▅▄▖┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▗▆▅⎻▄▍▆▆▆▆▅▄▘▅▅▅▄▃▲▅▖▂▆▅▄▖▘▘▎▂▃▘▊╴▎▪┈▊▌▎▊ ▌⎺⎺▍┥▝⎽│▍▎ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▅▄▃▃▃▘┑▂▂▂▄▄▄▂▂▄▄▃▃▃▃▂⎽▼▼▼▂▃▆▅▄▄▘ ▝⎼▂▃▃▃▃▂⎽⎺▆▄▃▂┈▆▄▂⎽▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅▆▊⎽▅▄▄▃▂▅▃▆▄▅┒▆▄▄▃⎺▆▄▂┈⎺▅▃╸⎺▅▘▃⎽▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃━⎺▃▃■■▃▄▄▄▅⎽▲●▝▆▅▄▅■▅▃⎽┈▆▄▂┈┈▌▋▊▎─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶╻⎺▅▄▃▃▃▄▅▆▮▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿⎽▅⎻┆ ╵╴┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊⎽▄▅▆▆▅▍▆▆▆▍▗▄▃▂▂▄▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▄▄▃▃▃▃▎▂⎺▆▎▖▪⎺▆▅▎▍▃▂▂▊▄▂▲▘▂┷▅▅▆▆▅▆▅▄▃▂▄▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▂⎺▆▖▗▊▂⎺▆▆▅▋⎺▆▅▅▮▎━▄▄▊▗▌▃▂▂▌▋▄▄▅▃▂▂━▄▂⎺▆▄▂┊·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄▂⎺⎺▅▘▄▍⎺▆▅▋▃▍▅▅▄▊▃▲▄▃▂▋▍▍▂⎺⎺▌▋┷━━▌┈▊⎺⎺▆▄▃╾▃┊▅▄▂⎽⎽⎽▮▆▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▆┱⎺▗▂▗▆▅▄▄▍▂▅▄▃▂▘▊▃▂⎽⎺▋▍▍▆▅▅▌▊▃⎼⎼▋▖ ▪▖│▉▌▉▅▃▂▪▃▂⎽⎺┈▝▆▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎻▆▅▅▘⎽▗▄▃▃▂▝▊━━▄▄▍▋▍▆▆▅▌▎▄▄▃▃▌▉⎽■⎽▅▅▅▅▄▃▂▍┈▎▉▎┊▆▅▖▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▄▄▃▃▆▆■━▅▄▌▅▌▃▃▂▂▎▌▅▄▃━▌▏▂▪▄▃▋▉▆┈▆▉▏▖▍⎺▆▅▖┊▝▂┫▎┋▌▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊▃▂▂⎼▍▅▗▃▂▂▂▍▆╽┊⎺▆▆⎺▍▅▄▄▃▌▘▎⎽⎽▼▋┊┅⎻⎻▘▎▋▎▌╴┈▉▌▉▄▂▅▃▂▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▘▂⎽⎽▊▃▄▘┊▆▆▅▮─▅▄▃▃▘▖▎▂⎽⎽▅▍╸▎▅▅▅▋ ▂▃▃▖⎽▂▂▂⎽⎽▍▎·▎┍▋▆▄▃▂┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌⎺▆▅▅▍▆▅▆▅▄▄▘▅▅▅▄▃▲▅▅▅▄▄▃▄▉▂▂▅▄▄▋┊▎▏ ▉▌▎▌┈▊⎺▆▘┑▌▂▄▏▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺▆▅▄▄▄▎┑▂▂▂▄▄▄▂▂▄▄▃▃▃▃▂▄▃▃▃▘▆⎽▘⎺┊▊⎽▂▂▃▄▃▃▂⎽⎽▆▅▃⎽▋▆▄▂╸▗▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▝▄▄▃▃▃▆▄━▆▄▅┒▆▄▄⎺▆▄▂┈⎺▅▃▘⎺▆▄▘▂⎽▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲●▝▆▅▄▝▆▄▂ ▆▄▃┈┈┈▌▋▉▝─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺╶┝⎺▆▄▃▃▃▄▅▆⎺▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚┆ ╶╶┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂▂⎽⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▍▂⎺▆▍▗▆▆▅▄▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▅▄▄▃▃▃▃▘▆▆▆▎▖▂⎺▆▆▎▍▆▅▄▄▖▄▄▃▃▂▆▆▆▆▅▆▅▄▂▄▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌▃▂▆▋▄▊⎺▆▆▅▆▋▮▆▆▆▪▎▅▄▃▊▍▄▃▂▂▖▋▅▅▅▃▂▲▅▃▂▆▅▃╴╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▄▃▂⎺⎺▍▄▍▆▅▅▋▃▍▆▅▅▊▃▃▃▃▃▋▍▍▅▄▃▍▋▄⎻⎻▌┈▄▃▆▄▃━▃┈▆▄▃▂⎽⎽⎽▂┳▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍▪⎺┳▊⎽▗▅▄▄▃▍▂▅▄▄▃▘▊▂▲⎼▄▋▍▝▃▂⎽▌▊╼━━▋▖┈▉▌▅▉▮▊▄▂■▄▂▂⎺┈ ▝▗▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗▆▼⎻▆▘▲▝▃▃━━▝▊▃━━▅▍▋▝▃▂⎽▋▗▍⎺▆▆▍▉▂▂▂▄▆▆▅▄▃⎽▍┈▎┣▋⎺▆▅▖▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂▅▅▅▄▄▆▆▲▄▄▃▌▅▌▄▃▃▂▎▌▎⎺▆▆▌▃▅▄▄▃▖▉▪┈▖▉▏▝⎺⎺▅▄▂╴▌⎽▌▖▊▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▊⎼▃▃▂▘▅▗▂▂⎽⎽▍▆▂┈┊▆⎺⎺▍▅▄▃▃▍▏▶▅▄▄▋▉▅▅▆▎▋▍▉╵┈▉▍▗▃▆▄▂▂▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▅▂⎼▂▂▂▝▄▘┈┈▆▄▮─▅▅▄▄▘▃▎▅▄▄▃▍▘▎⎽⎽▪▊ ⎼⎼╼━▂▂▂▂▏┈▘▎▉▎▝▆▅▃▂▂┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌┊⎺▆▆▍▆▆▅▄▃▃┛▅▅▅▄▃▲▅▅▅▅▅▆▆▖▃▅▅▄▄▘ ▎▉┈⎽▌▎▪ ▪▆▅▘▏▌┏▍▍▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺┊▆▆▅▅▘┑▂▂▂▄▄▄▂▂▄▄▃▃▃▃▂▄▃▃▃▊⎽▅▄▄▄▂▃▃▃▄▄▃▃▂⎽▆▅▃▂▌▝▄▂⎽╴▗▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆▄▄▆▆▄▄▄━▆▄▅┒▆▄▄▆▄▃┈┈▅▃┈▌▆▄▃▘▂⎽▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲●▝▆▅▆▆▄▂┈▆▅▃⎽┈ ┈▌╿▊▄─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆⎺▪⎺▆▅▄▃▃▄▅▆⎺⎺▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╿⎽▅⎻╴▘╶ ┸┅▆▄▄▄▅▅▅▆▅▆▆▅
▄▄▄▃▃⎽⎽⎺╵╺━━⎽▃▃▃━⎺▃▃▃▂▃▃▃▄▃▃▄▃▂▂▂⎽▂⎽╴▏▪┋└·▝▆■▆▃▝▃▃▃▂▃▃▄▄▃▄▃▂▂┺ ▃▃▃▄·⎽▗▃▂▉╺■⎺▆▆▄▄
▄▄▅▆▅▅▅▅▄▅▆▄▃▃▂▂▂■■▆▊▗▄▅▆▆▅▍▂⎺▆▍▗▃▂⎺▆▃▄▼┖⎽·▆▅▆▆▂▆▆▆▆▄▂▃▄▄▃▃▆▆▄▃▃▃▃▃▃▂▆▃▂▃▄▃▃▃▂▂▪
▲▅▅▆▂┷▃▄▅▆▆▅▅▅▆▲▪▄▃▃▃▆▆▅▅▎▖▂⎺▆▆▎▍●⎺▆▊▗▄⎺▆▅▄▖⎺⎺⎺▅▅▄▃▃▅▅▆▆▂▅▅▆▆▆▆▆▃▅▄▄▅▆▅▃▄▄▄▅▅▄▃▂
▂▄▄▄▄▄▄▄▄▃▅▅▅▃▌⎼⎼▃▋▗▊▆▅▅▄▆▋▮▆▆▆▮▎⎽▆▆▊▍▆▆▅▅▖▋▆▆▆▃▂━▄▂▪▅▃⎽╴╵·▆▅▄▄▅▅▅▅▅▄▄▅▃╷╵·⎺·⎻┄▆
▅▃▆▅▄▆▅▄▄▄▄▃▆▅▃▃▂▂▎▄▌▅▄▄▋▃▍▆▅▅▊▂▄▆▅▄▋▍▗▄▃■▍▋▅▅▅▌┈▄⎺▅▃●▄╾⎺▅▃▂┷⎽⎽⎽▂▖▆┗╷▆·┈⎺ ┈▉┺┎╲╎
▅▄▅▅▆▆▃▎⎺╲▄▃▄▍⎽⎽⎺▋⎽▉▝▃▃▅▍▂▅▄▄▃▘▊▗▄▃▃▘▎▃▂▃▃▌▊━━▃▋▖┈▪▝▝╷▏▄▃▲▗▃╾⎺╺  ▝▗▃▅▄▃▂▏╲▉╴╽▪▉╴
⎽┈▅▃▃▄▃▂▗▲▼▅▗⎺▆▆▼▍⎽╺━▲▅▪▝▊▃━▅▅▍▋▍━▅▄▌▘▝▃▃▂▌▉▃⎼▃⎽▆▅▄▃▂┅▍┈▎▝┓▆▆▅▎▌▖ ▍▄▄▄▃▂▃▂╌╶▪▌⎼┊
▃▂▂▃▄▃▃▅▆⎺▃▂⎺⎺▆▆▅▆▆▄▃▃▂▋⎽▌▄▃▃▂▎▌▝▂▂⎽▌▅▍⎺⎺⎺▋▉▮┈▮▉▏⎺⎺▆▄▂⎽ ▌▊▋·▊▊▌ ▖▄▝■▌▖▂▂▂▂▂⎽▂▄▄▄
⎺▆▄▄▅▄▅▅▄▅▅▗▅╾─▄▘▅▗⎽⎽⎺⎺▍▆⎽┊┊▅⎺⎺▍▏⎺▆▆▍▖▗▄▄▄▋▉▆▖▆▖▎▋▎▋╴┈▉▪⎺⎽▄▃⎽⎽▊┊▍▅▝▌▅▅▄▄▃●▆▆▆▅▆▅
▃▃▄▄▃▄▖▅▄▃▆┻▂▂⎼▪▝▄▍┈▆▅▅▲─▅▅▄▄▘╻▃▃▂▂■▘▏▝▅▄▄▋ ━━━┷▂▂▂⎽▊┈▋▎▮▎⎺▅▄▃▂▂┊▏▖▝▄▅▅▅▅▆▆▅▅▅▅▄
▅▄▃▃▂▃▃▃▂▅▌⎽▅▂▄▌▆▉▃━▂━▋▅▅▅▄▃▲▅▅▅▄▄▅▄▖▘▎▘▅┈▊ ▎▂┈⎽▌▎▎┊▆▅▃▌▏▌┅▍▌▍ ▊┊▍▉▌▌▄▄▗▅▅▅▅▃▃▄▂
▅▅▅▆▅▅▄▮⎺⎺⎺⎺⎺⎺▆▆┑▂▂▂▄▄▄▂▂▄▄▃▃▃▃▂▄▃▃▃┹▄▃▃▂▃▄▄▄▄▄▄▃▂⎽▆▅▃▂▎▶▝▃⎽▅┊▗▊▍▊▝▝▊▖▄▄▊·▎▘▅▅▘▉
▂▅▃▂▃▂▌▄▂▅▆▄⎽▆▆▅▅▅▆▆▃▂▃▆■▆▃▆▄▃━━▆▅▅⎽▆━━▆━━▄▃━▆▃▄┒▆▼▆▅▃┈┈▅▃┈┈▆▅▃▂▋▂⎽▎▋▋▂■■▅▄▅▅┓⎽▃
▄▃▄▅▅▅▘▂▂▄▅▂▂▂▂▂▄▄▃▃▄▅▅┷▅⎺ ▄⎺▃▃▄▄▅▅▅▄▃┳⎽▃▂■■▃▄▄▄▅⎽▲●▝▆⎺▆▄▂┈▆▅▃▂▊  ┈▝⎽▋▂─■▲▲⎽▂▄▄▅
▃▄▄▄▄▃▝▅▄━▆━━▆━━▆▆▆⎺▆▆▅▄▄▃▃▃▃▂▃▃▂▎▃▃▄▄▄▄▃⎺▆⎺┯⎺◆▄─▄▘▃▃▃▃▄▆┘▪▆▅▄▃▃▄▅▆┯◆⎽▪▍▝▄▄▃▂⎽▋▗
╁⎺▆▅▅▄▅▅▃▅▝▅▄▃▄▄▅▆━▌▂▂▅▍▅╸▅▅▅▆▆▆▆▅▅▄▂▄▅▖━▃▃▂·╎■╷▆▅▄▅╵┒┕▄▪⎽╷·▅┚┤└╶·┸┅▆▄▄▄▅▅▅▆▅▆▆▅
[?25h

View File

@ -32,3 +32,5 @@ using namespace WEX::TestExecution;
// Include our common helpers
#include "common.hpp"
#include "resource.h"

View File

@ -0,0 +1 @@
#define CHAFA_CONTENT 300

View File

@ -545,6 +545,8 @@ void SCREEN_INFORMATION::RefreshFontWithRenderer()
->TriggerFontChange(ServiceLocator::LocateGlobals().dpi,
GetDesiredFont(),
GetCurrentFont());
NotifyGlyphWidthFontChanged();
}
}
}

View File

@ -12,6 +12,8 @@ using namespace WEX::Logging;
static const std::wstring emoji = L"\xD83E\xDD22"; // U+1F922 nauseated face
static const std::wstring ambiguous = L"\x414"; // U+0414 cyrillic capital de
// codepoint and utf16 encoded string
static const std::vector<std::tuple<unsigned int, std::wstring, CodepointWidth>> testData =
{
@ -34,7 +36,7 @@ class CodepointWidthDetectorTests
TEST_METHOD(CodepointWidthDetectDefersMapPopulation)
{
const CodepointWidthDetector widthDetector;
CodepointWidthDetector widthDetector;
VERIFY_IS_TRUE(widthDetector._map.empty());
widthDetector.IsWide(UNICODE_SPACE);
VERIFY_IS_TRUE(widthDetector._map.empty());
@ -45,13 +47,13 @@ class CodepointWidthDetectorTests
TEST_METHOD(CanLookUpEmoji)
{
const CodepointWidthDetector widthDetector;
CodepointWidthDetector widthDetector;
VERIFY_IS_TRUE(widthDetector.IsWide(emoji));
}
TEST_METHOD(TestUnicodeRangeCompare)
{
const CodepointWidthDetector::UnicodeRangeCompare compare;
CodepointWidthDetector::UnicodeRangeCompare compare;
// test comparing 2 search terms
CodepointWidthDetector::UnicodeRange a{ 0x10 };
CodepointWidthDetector::UnicodeRange b{ 0x15 };
@ -60,7 +62,7 @@ class CodepointWidthDetectorTests
TEST_METHOD(CanExtractCodepoint)
{
const CodepointWidthDetector widthDetector;
CodepointWidthDetector widthDetector;
for (const auto& data : testData)
{
const auto& expected = std::get<0>(data);
@ -72,7 +74,7 @@ class CodepointWidthDetectorTests
TEST_METHOD(CanGetWidths)
{
const CodepointWidthDetector widthDetector;
CodepointWidthDetector widthDetector;
for (const auto& data : testData)
{
const auto& expected = std::get<2>(data);
@ -82,4 +84,41 @@ class CodepointWidthDetectorTests
}
}
static bool FallbackMethod(const std::wstring_view glyph)
{
if (glyph.size() < 1)
{
return false;
}
else
{
return (glyph.at(0) % 2) == 1;
}
}
TEST_METHOD(AmbiguousCache)
{
// Set up a detector with fallback.
CodepointWidthDetector widthDetector;
widthDetector.SetFallbackMethod(std::bind(&FallbackMethod, std::placeholders::_1));
// Ensure fallback cache is empty.
VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size());
// Lookup ambiguous width character.
widthDetector.IsWide(ambiguous);
// Cache should hold it.
VERIFY_ARE_EQUAL(1u, widthDetector._fallbackCache.size());
// Cached item should match what we expect
const auto it = widthDetector._fallbackCache.begin();
VERIFY_ARE_EQUAL(ambiguous, it->first);
VERIFY_ARE_EQUAL(FallbackMethod(ambiguous), it->second);
// Cache should empty when font changes.
widthDetector.NotifyFontChanged();
VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size());
}
};

View File

@ -49,6 +49,27 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory2* const factory,
}
}
// Routine Description:
// - Figures out how many columns this layout should take. This will use the analyze step only.
// Arguments:
// - columns - The number of columns the layout should consume when done.
// Return Value:
// - S_OK or suitable DirectX/DirectWrite/Direct2D result code.
[[nodiscard]]
HRESULT STDMETHODCALLTYPE CustomTextLayout::GetColumns(_Out_ UINT32* columns)
{
*columns = 0;
RETURN_IF_FAILED(_AnalyzeRuns());
RETURN_IF_FAILED(_ShapeGlyphRuns());
const auto totalAdvance = std::accumulate(_glyphAdvances.cbegin(), _glyphAdvances.cend(), 0.0f);
*columns = static_cast<UINT32>(ceil(totalAdvance / _width));
return S_OK;
}
// Routine Description:
// - Implements a drawing interface similarly to the default IDWriteTextLayout which will
// take the string from construction, analyze it for complexity, shape up the glyphs,
@ -72,6 +93,7 @@ HRESULT STDMETHODCALLTYPE CustomTextLayout::Draw(_In_opt_ void* clientDrawingCon
{
RETURN_IF_FAILED(_AnalyzeRuns());
RETURN_IF_FAILED(_ShapeGlyphRuns());
RETURN_IF_FAILED(_CorrectGlyphRuns());
RETURN_IF_FAILED(_DrawGlyphRuns(clientDrawingContext, renderer, { originX, originY }));
return S_OK;
@ -117,6 +139,15 @@ HRESULT CustomTextLayout::_AnalyzeRuns() noexcept
// Perform our custom font fallback analyzer that mimics the pattern of the real analyzers.
RETURN_IF_FAILED(_AnalyzeFontFallback(this, 0, textLength));
// Ensure that a font face is attached to every run
for (auto& run : _runs)
{
if (!run.fontFace)
{
run.fontFace = _font;
}
}
// Resequence the resulting runs in order before returning to caller.
size_t totalRuns = _runs.size();
std::vector<LinkedRun> runs;
@ -195,13 +226,13 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
{
try
{
// Shapes a single run of text into glyphs.
// Alternately, you could iteratively interleave shaping and line
// breaking to reduce the number glyphs held onto at once. It's simpler
// for this demostration to just do shaping and line breaking as two
// separate processes, but realize that this does have the consequence that
// certain advanced fonts containing line specific features (like Gabriola)
// will shape as if the line is not broken.
// Shapes a single run of text into glyphs.
// Alternately, you could iteratively interleave shaping and line
// breaking to reduce the number glyphs held onto at once. It's simpler
// for this demostration to just do shaping and line breaking as two
// separate processes, but realize that this does have the consequence that
// certain advanced fonts containing line specific features (like Gabriola)
// will shape as if the line is not broken.
Run& run = _runs.at(runIndex);
UINT32 textStart = run.textStart;
@ -217,17 +248,6 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
return S_FALSE; // Nothing to do..
}
// Get the font for this run
::Microsoft::WRL::ComPtr<IDWriteFontFace> face;
if (run.font)
{
RETURN_IF_FAILED(run.font->CreateFontFace(&face));
}
else
{
face = _font;
}
// Allocate space for shaping to fill with glyphs and other information,
// with about as many glyphs as there are text characters. We'll actually
// need more glyphs than codepoints if they are decomposed into separate
@ -255,7 +275,7 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
hr = _analyzer->GetGlyphs(
&_text[textStart],
textLength,
face.Get(),
run.fontFace.Get(),
run.isSideways, // isSideways,
WI_IsFlagSet(run.bidiLevel, 1), // isRightToLeft
&run.script,
@ -306,7 +326,7 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
&_glyphIndices[glyphStart],
&glyphProps[0],
actualGlyphCount,
face.Get(),
run.fontFace.Get(),
fontSize,
run.isSideways,
(run.bidiLevel & 1), // isRightToLeft
@ -321,69 +341,108 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
RETURN_IF_FAILED(hr);
// If we need to detect font fallback, we can do it this way:
// if (!_font->Equals(face.Get()))
// Set the final glyph count of this run and advance the starting glyph.
run.glyphCount = actualGlyphCount;
glyphStart += actualGlyphCount;
}
CATCH_RETURN();
return S_OK;
}
// Routine Description:
// - Adjusts the glyph information from shaping to fit the layout pattern required
// for our renderer.
// This is effectively a loop of _CorrectGlyphRun. See it for details.
// Arguments:
// - <none> - Uses internal state
// Return Value:
// - S_OK or suitable DirectWrite or STL error code
[[nodiscard]]
HRESULT CustomTextLayout::_CorrectGlyphRuns() noexcept
{
try
{
// Correct each run separately. This is needed whenever script, locale,
// or reading direction changes.
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
{
LOG_IF_FAILED(_CorrectGlyphRun(runIndex));
}
}
CATCH_RETURN();
return S_OK;
}
// Routine Description:
// - Adjusts the advances for each glyph in the run so it fits within a fixed-column count of cells.
// Arguments:
// - runIndex - The ID number of the internal runs array to use while shaping
// Return Value:
// - S_OK or suitable DirectWrite or STL error code
[[nodiscard]]
HRESULT CustomTextLayout::_CorrectGlyphRun(const UINT32 runIndex) noexcept
{
try
{
Run& run = _runs.at(runIndex);
if (run.textLength == 0)
{
return S_FALSE; // Nothing to do..
}
// We're going to walk through and check for advances that don't match the space that we expect to give out.
DWRITE_FONT_METRICS1 metrics;
run.fontFace->GetMetrics(&metrics);
// Walk through advances and space out characters that are too small to consume their box.
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
{
IDWriteFontFace1* face1;
RETURN_IF_FAILED(face.Get()->QueryInterface(&face1));
// Advance is how wide in pixels the glyph is
auto& advance = _glyphAdvances[i];
DWRITE_FONT_METRICS1 metrics;
face1->GetMetrics(&metrics);
// Offsets is how far to move the origin (in pixels) from where it is
auto& offset = _glyphOffsets[i];
// Walk through advances and space out characters that are too small to consume their box.
for (auto i = glyphStart; i < (glyphStart + actualGlyphCount); i++)
// Get how many columns we expected the glyph to have and mutiply into pixels.
const auto columns = _textClusterColumns[i];
const auto advanceExpected = static_cast<float>(columns * _width);
// If what we expect is bigger than what we have... pad it out.
if (advanceExpected > advance)
{
// Advance is how wide in pixels the glyph is
auto& advance = _glyphAdvances[i];
// Get the amount of space we have leftover.
const auto diff = advanceExpected - advance;
// Offsets is how far to move the origin (in pixels) from where it is
auto& offset = _glyphOffsets[i];
// Move the X offset (pixels to the right from the left edge) by half the excess space
// so half of it will be left of the glyph and the other half on the right.
offset.advanceOffset += diff / 2;
// Get how many columns we expected the glyph to have and mutiply into pixels.
const auto columns = _textClusterColumns[i];
const auto advanceExpected = static_cast<float>(columns * _width);
// Set the advance to the perfect width we want.
advance = advanceExpected;
}
// If what we expect is smaller than what we have... rescale the font size to get a smaller glyph to fit.
else if (advanceExpected < advance)
{
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
// height proportional to the width that we desire.
INT32 advanceInDesignUnits;
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices[i], &advanceInDesignUnits));
// If what we expect is bigger than what we have... pad it out.
if (advanceExpected > advance)
{
// Get the amount of space we have leftover.
const auto diff = advanceExpected - advance;
// When things are drawn, we want the font size (as specified in the base font in the original format)
// to be scaled by some factor.
// i.e. if the original font size was 16, we might want to draw this glyph with a 15.2 size font so
// the width (and height) of the glyph will shrink to fit the monospace cell box.
// Move the X offset (pixels to the right from the left edge) by half the excess space
// so half of it will be left of the glyph and the other half on the right.
offset.advanceOffset += diff / 2;
// This pattern is copied from the DxRenderer's algorithm for figuring out the font height for a specific width
// and was advised by the DirectWrite team.
const float widthAdvance = static_cast<float>(advanceInDesignUnits) / metrics.designUnitsPerEm;
const auto fontSizeWant = advanceExpected / widthAdvance;
run.fontScale = fontSizeWant / _format->GetFontSize();
// Set the advance to the perfect width we want.
advance = advanceExpected;
}
// If what we expect is smaller than what we have... rescale the font size to get a smaller glyph to fit.
else if (advanceExpected < advance)
{
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
// height proportional to the width that we desire.
INT32 advanceInDesignUnits;
RETURN_IF_FAILED(face1->GetDesignGlyphAdvances(1, &_glyphIndices[i], &advanceInDesignUnits));
DWRITE_GLYPH_METRICS glyphMetrics;
RETURN_IF_FAILED(face1->GetDesignGlyphMetrics(&_glyphIndices[i], 1, &glyphMetrics));
// When things are drawn, we want the font size (as specified in the base font in the original format)
// to be scaled by some factor.
// i.e. if the original font size was 16, we might want to draw this glyph with a 15.2 size font so
// the width (and height) of the glyph will shrink to fit the monospace cell box.
// This pattern is copied from the DxRenderer's algorithm for figuring out the font height for a specific width
// and was advised by the DirectWrite team.
const float widthAdvance = static_cast<float>(advanceInDesignUnits) / metrics.designUnitsPerEm;
const auto fontSizeWant = advanceExpected / widthAdvance;
run.fontScale = fontSizeWant / fontSizeFormat;
// Set the advance to the perfect width that we want.
advance = advanceExpected;
}
// Set the advance to the perfect width that we want.
advance = advanceExpected;
}
}
@ -398,15 +457,12 @@ HRESULT CustomTextLayout::_ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphSta
// 0.0f
// );
//}
// Set the final glyph count of this run and advance the starting glyph.
run.glyphCount = actualGlyphCount;
glyphStart += actualGlyphCount;
}
CATCH_RETURN();
return S_OK;
}
// Routine Description:
// - Takes the analyzed and shaped textual information from the layout process and
// forwards it into the given renderer in a run-by-run fashion.
@ -434,24 +490,12 @@ HRESULT CustomTextLayout::_DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
// Get the run
Run& run = _runs.at(runIndex);
// Get the font face from the font metadata provided by the fallback analysis
::Microsoft::WRL::ComPtr<IDWriteFontFace> face;
if (run.font)
{
RETURN_IF_FAILED(run.font->CreateFontFace(&face));
}
else
{
face = _font;
}
// Prepare the glyph run and description objects by converting our
// internal storage representation into something that matches DWrite's structures.
DWRITE_GLYPH_RUN glyphRun = { 0 };
glyphRun.bidiLevel = run.bidiLevel;
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
glyphRun.fontFace = face.Get();
glyphRun.fontFace = run.fontFace.Get();
glyphRun.glyphAdvances = _glyphAdvances.data() + run.glyphStart;
glyphRun.glyphCount = run.glyphCount;
glyphRun.glyphIndices = _glyphIndices.data() + run.glyphStart;
@ -467,12 +511,12 @@ HRESULT CustomTextLayout::_DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
// Try to draw it
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
mutableOrigin.x,
mutableOrigin.y,
DWRITE_MEASURING_MODE_NATURAL,
&glyphRun,
&glyphRunDescription,
nullptr));
mutableOrigin.x,
mutableOrigin.y,
DWRITE_MEASURING_MODE_NATURAL,
&glyphRun,
&glyphRunDescription,
nullptr));
// Shift origin to the right for the next run based on the amount of space consumed.
mutableOrigin.x = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
@ -826,7 +870,22 @@ HRESULT STDMETHODCALLTYPE CustomTextLayout::_SetMappedFont(UINT32 textPosition,
while (textLength > 0)
{
auto& run = _FetchNextRun(textLength);
run.font = font;
if (font != nullptr)
{
// Get font face from font metadata
::Microsoft::WRL::ComPtr<IDWriteFontFace> face;
RETURN_IF_FAILED(font->CreateFontFace(&face));
// QI for Face5 interface from base face interface, store into run
RETURN_IF_FAILED(face.As(&run.fontFace));
}
else
{
run.fontFace = _font;
}
// Store the font scale as well.
run.fontScale = scale;
}
}
@ -890,7 +949,7 @@ CustomTextLayout::LinkedRun& CustomTextLayout::_FetchNextRun(UINT32& textLength)
// - <none> - Updates internal state
void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
{
if (_runIndex < _runs.size()
&& _runs[_runIndex].ContainsTextPosition(textPosition))
@ -912,7 +971,7 @@ void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
// - <none> - Updates internal state, the back half will be selected after running
void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
{
UINT32 runTextStart = _runs.at(_runIndex).textStart;
if (splitPosition <= runTextStart)

View File

@ -29,6 +29,9 @@ namespace Microsoft::Console::Render
const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters,
size_t const width);
[[nodiscard]]
HRESULT STDMETHODCALLTYPE GetColumns(_Out_ UINT32* columns);
// IDWriteTextLayout methods (but we don't actually want to implement them all, so just this one matching the existing interface)
[[nodiscard]]
HRESULT STDMETHODCALLTYPE Draw(_In_opt_ void* clientDrawingContext,
@ -87,7 +90,7 @@ namespace Microsoft::Console::Render
script(),
isNumberSubstituted(),
isSideways(),
font{ nullptr },
fontFace{ nullptr },
fontScale{ 1.0 }
{ }
@ -99,7 +102,7 @@ namespace Microsoft::Console::Render
UINT8 bidiLevel;
bool isNumberSubstituted;
bool isSideways;
::Microsoft::WRL::ComPtr<IDWriteFont> font;
::Microsoft::WRL::ComPtr<IDWriteFontFace5> fontFace;
FLOAT fontScale;
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const
@ -142,6 +145,10 @@ namespace Microsoft::Console::Render
[[nodiscard]]
HRESULT _ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphStart) noexcept;
[[nodiscard]]
HRESULT _CorrectGlyphRuns() noexcept;
[[nodiscard]]
HRESULT _CorrectGlyphRun(const UINT32 runIndex) noexcept;
[[nodiscard]]
HRESULT _DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
IDWriteTextRenderer* renderer,
const D2D_POINT_2F origin) noexcept;

View File

@ -1317,14 +1317,28 @@ HRESULT DxEngine::GetFontSize(_Out_ COORD* const pFontSize) noexcept
// Routine Description:
// - Currently unused by this renderer.
// Arguments:
// - glyph - <unused>
// - pResult - Filled with false.
// - glyph - The glyph run to process for column width.
// - pResult - True if it should take two columns. False if it should take one.
// Return Value:
// - S_OK
// - S_OK or relevant DirectWrite error.
[[nodiscard]]
HRESULT DxEngine::IsGlyphWideByFont(const std::wstring_view /*glyph*/, _Out_ bool* const pResult) noexcept
HRESULT DxEngine::IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept
{
*pResult = false;
Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout.
// Create the text layout
CustomTextLayout layout(_dwriteFactory.Get(),
_dwriteTextAnalyzer.Get(),
_dwriteTextFormat.Get(),
_dwriteFontFace.Get(),
{ &cluster, 1 },
_glyphCell.cx);
UINT32 columns = 0;
RETURN_IF_FAILED(layout.GetColumns(&columns));
*pResult = columns != 1;
return S_OK;
}

View File

@ -43,15 +43,13 @@ CodepointWidth CodepointWidthDetector::GetWidth(const std::wstring_view glyph) c
// - true if wch is wide
bool CodepointWidthDetector::IsWide(const wchar_t wch) const noexcept
{
const auto width = GetCharWidth(wch);
if (width == CodepointWidth::Invalid)
try
{
return _lookupIsWide({ &wch, 1 });
}
else
{
return width == CodepointWidth::Wide;
return IsWide({ &wch, 1 });
}
CATCH_LOG();
return true;
}
// Routine Description:
@ -65,11 +63,28 @@ bool CodepointWidthDetector::IsWide(const std::wstring_view glyph) const
THROW_HR_IF(E_INVALIDARG, glyph.empty());
if (glyph.size() == 1)
{
const auto width = GetCharWidth(glyph.front());
// We first attempt to look at our custom quick lookup table of char width preferences.
const auto width = GetQuickCharWidth(glyph.front());
// If it's invalid, the quick width had no opinion, so go to the lookup table.
if (width == CodepointWidth::Invalid)
{
return _lookupIsWide(glyph);
}
// If it's ambiguous, the quick width wanted us to ask the font directly, try that if we can.
// If not, go to the lookup table.
else if (width == CodepointWidth::Ambiguous)
{
if (_hasFallback)
{
return _checkFallbackViaCache(glyph);
}
else
{
return _lookupIsWide(glyph);
}
}
// Otherwise, return Wide as True and Narrow as False.
else
{
return width == CodepointWidth::Wide;
@ -89,20 +104,55 @@ bool CodepointWidthDetector::IsWide(const std::wstring_view glyph) const
// - true if codepoint is wide or if it can't be confirmed to be narrow
bool CodepointWidthDetector::_lookupIsWide(const std::wstring_view glyph) const noexcept
{
const CodepointWidth width = GetWidth(glyph);
if (width == CodepointWidth::Ambiguous)
try
{
if (_hasFallback)
// Use our generated table to try to lookup the width based on the Unicode standard.
const CodepointWidth width = GetWidth(glyph);
// If it's ambiguous, then ask the font if we can.
if (width == CodepointWidth::Ambiguous)
{
return _pfnFallbackMethod(glyph);
if (_hasFallback)
{
return _checkFallbackViaCache(glyph);
}
}
// If it's not ambiguous, it should say wide or narrow. Turn that into True = Wide or False = Narrow.
else
{
return width == CodepointWidth::Wide;
}
}
CATCH_LOG();
// If we got this far, we couldn't figure it out.
// It's better to be too wide than too narrow.
return true;
}
// Routine Description:
// - Checks the fallback function but caches the results until the font changes
// because the lookup function is usually very expensive and will return the same results
// for the same inputs.
// Arguments:
// - glyph - the utf16 encoded codepoint to check width of
// - true if codepoint is wide or false if it is narrow
bool CodepointWidthDetector::_checkFallbackViaCache(const std::wstring_view glyph) const
{
const std::wstring findMe{ glyph };
// TODO: Cache needs to be emptied when font changes.
auto it = _fallbackCache.find(findMe);
if (it == _fallbackCache.end())
{
auto result = _pfnFallbackMethod(glyph);
_fallbackCache.insert_or_assign(findMe, result);
return result;
}
else
{
return width == CodepointWidth::Wide;
return it->second;
}
// better to be too wide than too narrow
return true;
}
// Routine Description:
@ -146,6 +196,19 @@ void CodepointWidthDetector::SetFallbackMethod(std::function<bool(const std::wst
_hasFallback = true;
}
// Method Description:
// - Resets the internal ambiguous character width cache mechanism
// since it will be different when the font changes and we should
// re-query the new font for that information.
// Arguments:
// - <none>
// Return Value:
// - <none>
void CodepointWidthDetector::NotifyFontChanged() const noexcept
{
_fallbackCache.clear();
}
void CodepointWidthDetector::_populateUnicodeSearchMap()
{
// generated from http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt

View File

@ -37,3 +37,14 @@ void SetGlyphWidthFallback(std::function<bool(const std::wstring_view)> pfnFallb
{
widthDetector.SetFallbackMethod(pfnFallback);
}
// Function Description:
// - Forwards notification about font changing to glyph width detector
// Arguments:
// - <none>
// Return Value:
// - <none>
void NotifyGlyphWidthFontChanged()
{
widthDetector.NotifyFontChanged();
}

View File

@ -143,7 +143,7 @@ std::deque<std::unique_ptr<KeyEvent>> CharToKeyEvents(const wchar_t wch,
WORD CharType = 0;
GetStringTypeW(CT_CTYPE3, &wch, 1, &CharType);
if (WI_IsFlagSet(CharType, C3_ALPHA) || GetCharWidth(wch) == CodepointWidth::Wide)
if (WI_IsFlagSet(CharType, C3_ALPHA) || GetQuickCharWidth(wch) == CodepointWidth::Wide)
{
keyState = 0;
}
@ -341,7 +341,10 @@ std::deque<std::unique_ptr<KeyEvent>> SynthesizeNumpadEvents(const wchar_t wch,
// display font data (cached) instead.
// May-23-2017 migrie Forced Box-Drawing Characters (x2500-x257F) to narrow.
// Jan-16-2018 migrie Seperated core lookup from asking the renderer the width
CodepointWidth GetCharWidth(const wchar_t wch) noexcept
// May-01-2019 MiNiksa Forced lookup-via-renderer for retroactively recategorized emoji
// that used to be narrow but now might be wide. (approx x2194-x2b55, not inclusive)
// Also forced block characters segment (x2580-x259F) to narrow
CodepointWidth GetQuickCharWidth(const wchar_t wch) noexcept
{
// 0x00-0x1F is ambiguous by font
if (0x20 <= wch && wch <= 0x7e)
@ -372,7 +375,103 @@ CodepointWidth GetCharWidth(const wchar_t wch) noexcept
{
return CodepointWidth::Narrow;
}
// 0x2580 - 0x259F is the block element characters.
// Technically these are ambiguous width, but many many things assume they're narrow.
else if (0x2580 <= wch && wch <= 0x259F)
{
return CodepointWidth::Narrow;
}
// 0x2010 - 0x2B59 varies between narrow, ambiguous, and wide by character and font (Unicode 9.0)
// However, there are a bunch of retroactive-emoji in this range. Things that weren't emoji and then they became
// "emoji" later. As a result, they jumped from a fixed narrow definition to a now ambiguous definition.
// There are others in this range already defined as wide or ambiguous, but we're just going to
// implicitly say they're all ambiguous here to force a font lookup.
// I picked the ones that looked like color double-wide emoji in my browser that weren't already
// covered easily by the half-width/full-width table (see CodepointWidthDetector.cpp)
// See https://unicode.org/Public/emoji/12.0/emoji-data.txt
else if ((0x2194 <= wch && wch <= 0x2199) ||
(0x21A9 <= wch && wch <= 0x21AA) ||
(0x231A <= wch && wch <= 0x231B) ||
0x2328 == wch ||
0x23CF == wch ||
(0x23E9 <= wch && wch <= 0x23F3) ||
(0x23F8 <= wch && wch <= 0x23FA) ||
0x24C2 == wch ||
(0x25AA <= wch && wch <= 0x25AB) ||
0x25B6 == wch ||
0x25C0 == wch ||
(0x25FB <= wch && wch <= 0x25FE) ||
(0x2600 <= wch && wch <= 0x2604) ||
0x260E == wch ||
0x2611 == wch ||
(0x2614 <= wch && wch <= 0x2615) ||
0x2618 == wch ||
0x261D == wch ||
0x2620 == wch ||
(0x2622 <= wch && wch <= 0x2623) ||
0x2626 == wch ||
0x262A == wch ||
(0x262E <= wch && wch <= 0x262F) ||
(0x2638 <= wch && wch <= 0x263A) ||
0x2640 == wch ||
0x2642 == wch ||
(0x2648 <= wch && wch <= 0x2653) ||
(0x265F <= wch && wch <= 0x2660) ||
0x2663 == wch ||
(0x2665 <= wch && wch <= 0x2666) ||
0x2668 == wch ||
0x267B == wch ||
(0x267E <= wch && wch <= 0x267F) ||
(0x2692 <= wch && wch <= 0x2697) ||
0x2699 == wch ||
(0x269B <= wch && wch <= 0x269C) ||
(0x26A0 <= wch && wch <= 0x26A1) ||
(0x26AA <= wch && wch <= 0x26AB) ||
(0x26B0 <= wch && wch <= 0x26B1) ||
(0x26BD <= wch && wch <= 0x26BE) ||
(0x26C4 <= wch && wch <= 0x26C5) ||
0x26C8 == wch ||
0x26CE == wch ||
0x26CF == wch ||
0x26D1 == wch ||
(0x26D3 <= wch && wch <= 0x26D4) ||
(0x26E9 <= wch && wch <= 0x26EA) ||
(0x26F0 <= wch && wch <= 0x26F5) ||
(0x26F7 <= wch && wch <= 0x26FA) ||
0x26FD == wch ||
0x2702 == wch ||
0x2705 == wch ||
(0x2708 <= wch && wch <= 0x2709) ||
(0x270A <= wch && wch <= 0x270B) ||
(0x270C <= wch && wch <= 0x270D) ||
0x270F == wch ||
0x2712 == wch ||
0x2714 == wch ||
0x2716 == wch ||
0x271D == wch ||
0x2721 == wch ||
0x2728 == wch ||
(0x2733 <= wch && wch <= 0x2734) ||
0x2744 == wch ||
0x2747 == wch ||
0x274C == wch ||
0x274E == wch ||
(0x2753 <= wch && wch <= 0x2755) ||
0x2757 == wch ||
(0x2763 <= wch && wch <= 0x2764) ||
(0x2795 <= wch && wch <= 0x2797) ||
0x27A1 == wch ||
0x27B0 == wch ||
0x27BF == wch ||
(0x2934 <= wch && wch <= 0x2935) ||
(0x2B05 <= wch && wch <= 0x2B07) ||
(0x2B1B <= wch && wch <= 0x2B1C) ||
0x2B50 == wch ||
0x2B55 == wch
)
{
return CodepointWidth::Ambiguous;
}
else if (0x2B5A <= wch && wch <= 0x2E44)
{
// From Unicode 9.0, this range is narrow (assorted languages)

View File

@ -107,6 +107,7 @@ public:
bool IsWide(const std::wstring_view glyph) const;
bool IsWide(const wchar_t wch) const noexcept;
void SetFallbackMethod(std::function<bool(const std::wstring_view)> pfnFallback);
void NotifyFontChanged() const noexcept;
#ifdef UNIT_TESTING
friend class CodepointWidthDetectorTests;
@ -114,9 +115,11 @@ public:
private:
bool _lookupIsWide(const std::wstring_view glyph) const noexcept;
bool _checkFallbackViaCache(const std::wstring_view glyph) const;
unsigned int _extractCodepoint(const std::wstring_view glyph) const noexcept;
void _populateUnicodeSearchMap();
mutable std::map<std::wstring, bool> _fallbackCache;
std::map<UnicodeRange, CodepointWidth, UnicodeRangeCompare> _map;
std::function<bool(std::wstring_view)> _pfnFallbackMethod;
bool _hasFallback = false;

View File

@ -12,3 +12,4 @@ Abstract:
bool IsGlyphFullWidth(const std::wstring_view glyph);
bool IsGlyphFullWidth(const wchar_t wch);
void SetGlyphWidthFallback(std::function<bool(std::wstring_view)> pfnFallback);
void NotifyGlyphWidthFontChanged();

View File

@ -45,6 +45,6 @@ std::deque<std::unique_ptr<KeyEvent>> SynthesizeKeyboardEvents(const wchar_t wch
std::deque<std::unique_ptr<KeyEvent>> SynthesizeNumpadEvents(const wchar_t wch, const unsigned int codepage);
CodepointWidth GetCharWidth(const wchar_t wch) noexcept;
CodepointWidth GetQuickCharWidth(const wchar_t wch) noexcept;
wchar_t Utf16ToUcs2(const std::wstring_view charData);