Browse Source

Merge branch 'feature/7120-control-themes' into refactor/controltemplate-binding-priority

pull/7679/head
Steven Kirk 4 years ago
parent
commit
489e2a25a8
  1. 1
      .editorconfig
  2. 48
      azure-pipelines-integrationtests.yml
  3. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  4. 7
      native/Avalonia.Native/src/OSX/AvnView.mm
  5. 52
      native/Avalonia.Native/src/OSX/SystemDialogs.mm
  6. 20
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  7. 20
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  8. 30
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  9. 3
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  10. 1
      samples/ControlCatalog.Android/environment.device.txt
  11. 1
      samples/ControlCatalog.Android/environment.emulator.txt
  12. 1
      samples/ControlCatalog.NetCore/Program.cs
  13. 1
      samples/ControlCatalog.Web/App.razor.cs
  14. 2
      samples/ControlCatalog/ControlCatalog.csproj
  15. 5
      samples/ControlCatalog/MainView.xaml
  16. 5
      samples/ControlCatalog/MainView.xaml.cs
  17. 1
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  18. 76
      samples/ControlCatalog/Pages/DialogsPage.xaml
  19. 234
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  20. 6
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  21. 13
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  22. 235
      samples/ControlCatalog/Pages/PointerCanvas.cs
  23. 109
      samples/ControlCatalog/Pages/PointerContactsTab.cs
  24. 322
      samples/ControlCatalog/Pages/PointersPage.cs
  25. 66
      samples/ControlCatalog/Pages/PointersPage.xaml
  26. 78
      samples/ControlCatalog/Pages/PointersPage.xaml.cs
  27. 4
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  28. 21
      samples/IntegrationTestApp/MainWindow.axaml
  29. 66
      samples/IntegrationTestApp/MainWindow.axaml.cs
  30. 35
      samples/IntegrationTestApp/ShowWindowTest.axaml
  31. 40
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  32. 4
      samples/RenderDemo/Pages/FormattedTextPage.axaml.cs
  33. 12
      samples/RenderDemo/Pages/GlyphRunPage.xaml
  34. 113
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  35. 5
      src/Android/Avalonia.Android/AndroidPlatform.cs
  36. 39
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  37. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  38. 244
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  39. 177
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
  40. 20
      src/Android/Avalonia.Android/SystemDialogImpl.cs
  41. BIN
      src/Avalonia.Base/Assets/BiDi.trie
  42. BIN
      src/Avalonia.Base/Assets/GraphemeBreak.trie
  43. BIN
      src/Avalonia.Base/Assets/UnicodeData.trie
  44. 3
      src/Avalonia.Base/AvaloniaObject.cs
  45. 119
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  46. 37
      src/Avalonia.Base/GeometryCollection.cs
  47. 5
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  48. 4
      src/Avalonia.Base/Input/IInputElement.cs
  49. 7
      src/Avalonia.Base/Input/IKeyboardDevice.cs
  50. 10
      src/Avalonia.Base/Input/IPenDevice.cs
  51. 43
      src/Avalonia.Base/Input/IPointer.cs
  52. 58
      src/Avalonia.Base/Input/InputElement.cs
  53. 174
      src/Avalonia.Base/Input/PenDevice.cs
  54. 22
      src/Avalonia.Base/Input/PointerEventArgs.cs
  55. 6
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  56. 125
      src/Avalonia.Base/Input/PointerPoint.cs
  57. 19
      src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
  58. 17
      src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs
  59. 13
      src/Avalonia.Base/Input/TouchDevice.cs
  60. 71
      src/Avalonia.Base/Layout/LayoutHelper.cs
  61. 14
      src/Avalonia.Base/Layout/LayoutManager.cs
  62. 24
      src/Avalonia.Base/Layout/Layoutable.cs
  63. 10
      src/Avalonia.Base/Logging/LogArea.cs
  64. 18
      src/Avalonia.Base/Media/DrawingCollection.cs
  65. 420
      src/Avalonia.Base/Media/DrawingGroup.cs
  66. 129
      src/Avalonia.Base/Media/FormattedText.cs
  67. 45
      src/Avalonia.Base/Media/GeometryCollection.cs
  68. 12
      src/Avalonia.Base/Media/GeometryDrawing.cs
  69. 58
      src/Avalonia.Base/Media/GeometryGroup.cs
  70. 20
      src/Avalonia.Base/Media/GlyphRun.cs
  71. 24
      src/Avalonia.Base/Media/PlatformGeometry.cs
  72. 23
      src/Avalonia.Base/Media/TextAlignment.cs
  73. 109
      src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
  74. 16
      src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs
  75. 38
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  76. 31
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  77. 66
      src/Avalonia.Base/Media/TextFormatting/TextLine.cs
  78. 77
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  79. 472
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs
  80. 28
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
  81. 25
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs
  82. 44
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  83. 7
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
  84. 409
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs
  85. 1
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs
  86. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
  87. 5
      src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs
  88. 35
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
  89. 1086
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs
  90. 156
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
  91. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
  92. 7
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  93. 107
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
  94. 88
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  95. 35
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageProvider.cs
  96. 40
      src/Avalonia.Base/Platform/Storage/FileIO/StorageProviderHelpers.cs
  97. 44
      src/Avalonia.Base/Platform/Storage/FilePickerFileType.cs
  98. 48
      src/Avalonia.Base/Platform/Storage/FilePickerFileTypes.cs
  99. 19
      src/Avalonia.Base/Platform/Storage/FilePickerOpenOptions.cs
  100. 29
      src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs

1
.editorconfig

@ -21,6 +21,7 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true csharp_new_line_between_query_expression_clauses = true
# trim_trailing_whitespace = true
# Indentation preferences # Indentation preferences
csharp_indent_block_contents = true csharp_indent_block_contents = true

48
azure-pipelines-integrationtests.yml

@ -0,0 +1,48 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
jobs:
- job: Mac
pool:
name: 'AvaloniaMacPool'
steps:
- script: ./tests/Avalonia.IntegrationTests.Appium/macos-clean-build-test.sh
displayName: 'run integration tests'
- job: Windows
pool:
vmImage: 'windows-2022'
steps:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.202'
inputs:
version: 6.0.202
- task: Windows Application Driver@0
inputs:
OperationType: 'Start'
AgentResolution: '4K'
displayName: 'Start WinAppDriver'
- task: DotNetCoreCLI@2
inputs:
command: 'build'
projects: 'samples/IntegrationTestApp/IntegrationTestApp.csproj'
- task: DotNetCoreCLI@2
inputs:
command: 'test'
projects: 'tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj'
- task: Windows Application Driver@0
inputs:
OperationType: 'Stop'
displayName: 'Stop WinAppDriver'

4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -49,6 +49,7 @@
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; }; BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; }; BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -101,6 +102,7 @@
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; }; AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = "<group>"; }; BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = "<group>"; };
BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = "<group>"; }; BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = "<group>"; };
ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -108,6 +110,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */,
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */, 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */,
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */, 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */,
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
@ -122,6 +125,7 @@
AB661C1C2148230E00291242 /* Frameworks */ = { AB661C1C2148230E00291242 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */,
522D5958258159C1006F7F7A /* Carbon.framework */, 522D5958258159C1006F7F7A /* Carbon.framework */,
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */, 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */,
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,

7
native/Avalonia.Native/src/OSX/AvnView.mm

@ -439,7 +439,12 @@
if(_parent != nullptr) if(_parent != nullptr)
{ {
_lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); auto handled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
if (key != LeftCtrl && key != RightCtrl) {
_lastKeyHandled = handled;
} else {
_lastKeyHandled = false;
}
} }
} }

52
native/Avalonia.Native/src/OSX/SystemDialogs.mm

@ -1,5 +1,6 @@
#include "common.h" #include "common.h"
#include "INSWindowHolder.h" #include "INSWindowHolder.h"
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs> class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
{ {
@ -7,6 +8,7 @@ public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events, IAvnSystemDialogEvents* events,
bool allowMultiple,
const char* title, const char* title,
const char* initialDirectory) override const char* initialDirectory) override
{ {
@ -14,6 +16,7 @@ public:
{ {
auto panel = [NSOpenPanel openPanel]; auto panel = [NSOpenPanel openPanel];
panel.allowsMultipleSelection = allowMultiple;
panel.canChooseDirectories = true; panel.canChooseDirectories = true;
panel.canCreateDirectories = true; panel.canCreateDirectories = true;
panel.canChooseFiles = false; panel.canChooseFiles = false;
@ -118,7 +121,15 @@ public:
{ {
auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
panel.allowedFileTypes = allowedTypes; // Prefer allowedContentTypes if available
if (@available(macOS 11.0, *))
{
panel.allowedContentTypes = ConvertToUTType(allowedTypes);
}
else
{
panel.allowedFileTypes = allowedTypes;
}
} }
} }
@ -207,7 +218,18 @@ public:
{ {
auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
panel.allowedFileTypes = allowedTypes; // Prefer allowedContentTypes if available
if (@available(macOS 11.0, *))
{
panel.allowedContentTypes = ConvertToUTType(allowedTypes);
}
else
{
panel.allowedFileTypes = allowedTypes;
}
panel.allowsOtherFileTypes = false;
panel.extensionHidden = false;
} }
} }
@ -250,6 +272,32 @@ public:
} }
} }
} }
private:
NSMutableArray* ConvertToUTType(NSArray<NSString*>* allowedTypes)
{
auto originalCount = [allowedTypes count];
auto mapped = [[NSMutableArray alloc] init];
if (@available(macOS 11.0, *))
{
for (int i = 0; i < originalCount; i++)
{
auto utTypeStr = allowedTypes[i];
auto utType = [UTType typeWithIdentifier:utTypeStr];
if (utType == nil)
{
utType = [UTType typeWithMIMEType:utTypeStr];
}
if (utType != nil)
{
[mapped addObject:utType];
}
}
}
return mapped;
}
}; };

20
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -48,7 +48,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
[Window setContentMaxSize:lastMaxSize]; [Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false]; [Window setOpaque:false];
[Window setHasShadow:true];
} }
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@ -224,7 +223,7 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
if (ret == nullptr) if (ret == nullptr)
return E_POINTER; return E_POINTER;
if(Window != nullptr){ if(Window != nullptr && _shown){
auto frame = [Window frame]; auto frame = [Window frame];
ret->Width = frame.size.width; ret->Width = frame.size.width;
ret->Height = frame.size.height; ret->Height = frame.size.height;
@ -298,14 +297,15 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
} }
@try { @try {
lastSize = NSSize {x, y}; if(x != lastSize.width || y != lastSize.height) {
lastSize = NSSize{x, y};
if (!_shown) {
BaseEvents->Resized(AvnSize{x, y}, reason); if (!_shown) {
} BaseEvents->Resized(AvnSize{x, y}, reason);
else if(Window != nullptr) { } else if (Window != nullptr) {
[Window setContentSize:lastSize]; [Window setContentSize:lastSize];
[Window invalidateShadow]; [Window invalidateShadow];
}
} }
} }
@finally { @finally {

20
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -24,6 +24,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
_lastTitle = @""; _lastTitle = @"";
_parent = nullptr; _parent = nullptr;
WindowEvents = events; WindowEvents = events;
[Window setHasShadow:true];
OnInitialiseNSWindow(); OnInitialiseNSWindow();
} }
@ -119,13 +121,16 @@ void WindowImpl::BringToFront()
{ {
if(Window != nullptr) if(Window != nullptr)
{ {
if(IsDialog()) if (![Window isMiniaturized])
{
Activate();
}
else
{ {
[Window orderFront:nullptr]; if(IsDialog())
{
Activate();
}
else
{
[Window orderFront:nullptr];
}
} }
[Window invalidateShadow]; [Window invalidateShadow];
@ -488,6 +493,8 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
} }
if (_shown) { if (_shown) {
_actualWindowState = _lastWindowState;
switch (state) { switch (state) {
case Maximized: case Maximized:
if (currentState == FullScreen) { if (currentState == FullScreen) {
@ -545,7 +552,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
break; break;
} }
_actualWindowState = _lastWindowState;
WindowEvents->WindowStateChanged(_actualWindowState); WindowEvents->WindowStateChanged(_actualWindowState);
} }

30
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -9,39 +9,37 @@
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat> <AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<RuntimeIdentifiers>android-arm64;android-x64</RuntimeIdentifiers>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="..\..\build\Assets\Icon.png"> <AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link> <Link>Resources\drawable\Icon.png</Link>
</AndroidResource> </AndroidResource>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''"> <PropertyGroup Condition="'$(RunAOTCompilation)'=='' and '$(Configuration)'=='Release' and '$(TF_BUILD)'==''">
<DebugSymbols>True</DebugSymbols>
<RunAOTCompilation>True</RunAOTCompilation> <RunAOTCompilation>True</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(RunAOTCompilation)'=='True'">
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk> <EnableLLVM>True</EnableLLVM>
<RunAOTCompilation>False</RunAOTCompilation> <AndroidAotAdditionalArguments>no-write-symbols,nodebug</AndroidAotAdditionalArguments>
<AndroidAotMode>Hybrid</AndroidAotMode>
<AndroidGenerateJniMarshalMethods>True</AndroidGenerateJniMarshalMethods>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(AndroidEnableProfiler)'=='True'">
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk> <IsEmulator Condition="'$(IsEmulator)' == ''">True</IsEmulator>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" /> <AndroidEnvironment Condition="'$(IsEmulator)'=='True'" Include="environment.emulator.txt" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" /> <AndroidEnvironment Condition="'$(IsEmulator)'!='True'" Include="environment.device.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" /> <ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

3
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application> <application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest> </manifest>

1
samples/ControlCatalog.Android/environment.device.txt

@ -0,0 +1 @@
DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend

1
samples/ControlCatalog.Android/environment.emulator.txt

@ -0,0 +1 @@
DOTNET_DiagnosticPorts=10.0.2.2:9001,suspend

1
samples/ControlCatalog.NetCore/Program.cs

@ -115,7 +115,6 @@ namespace ControlCatalog.NetCore
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions
{ {
EnableMultitouch = true
}) })
.UseSkia() .UseSkia()
.AfterSetup(builder => .AfterSetup(builder =>

1
samples/ControlCatalog.Web/App.razor.cs

@ -11,6 +11,7 @@ public partial class App
{ {
ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
}) })
//.With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
.SetupWithSingleViewLifetime(); .SetupWithSingleViewLifetime();
base.OnParametersSet(); base.OnParametersSet();

2
samples/ControlCatalog/ControlCatalog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

5
samples/ControlCatalog/MainView.xaml

@ -69,6 +69,9 @@
<TabItem Header="CalendarDatePicker"> <TabItem Header="CalendarDatePicker">
<pages:CalendarDatePickerPage /> <pages:CalendarDatePickerPage />
</TabItem> </TabItem>
<TabItem Header="Dialogs">
<pages:DialogsPage />
</TabItem>
<TabItem Header="Drag+Drop"> <TabItem Header="Drag+Drop">
<pages:DragAndDropPage /> <pages:DragAndDropPage />
</TabItem> </TabItem>
@ -109,7 +112,7 @@
<TabItem Header="OpenGL"> <TabItem Header="OpenGL">
<pages:OpenGlPage /> <pages:OpenGlPage />
</TabItem> </TabItem>
<TabItem Header="Pointers (Touch)"> <TabItem Header="Pointers">
<pages:PointersPage /> <pages:PointersPage />
</TabItem> </TabItem>
<TabItem Header="ProgressBar"> <TabItem Header="ProgressBar">

5
samples/ControlCatalog/MainView.xaml.cs

@ -24,11 +24,6 @@ namespace ControlCatalog
{ {
IList tabItems = ((IList)sideBar.Items); IList tabItems = ((IList)sideBar.Items);
tabItems.Add(new TabItem() tabItems.Add(new TabItem()
{
Header = "Dialogs",
Content = new DialogsPage()
});
tabItems.Add(new TabItem()
{ {
Header = "Screens", Header = "Screens",
Content = new ScreenPage() Content = new ScreenPage()

1
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -90,6 +90,7 @@
</Style> </Style>
</Button.Styles> </Button.Styles>
</Button> </Button>
<Button Classes="accent">Accent</Button>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"

76
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -1,29 +1,57 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl x:Class="ControlCatalog.Pages.DialogsPage"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="https://github.com/avaloniaui"
x:Class="ControlCatalog.Pages.DialogsPage"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4" Margin="4"> <StackPanel Margin="4"
<CheckBox Name="UseFilters">Use filters</CheckBox> Orientation="Vertical"
<Button Name="OpenFile">_Open File</Button> Spacing="4">
<Button Name="OpenMultipleFiles">Open _Multiple File</Button>
<Button Name="SaveFile">_Save File</Button>
<Button Name="SelectFolder">Select Fo_lder</Button>
<Button Name="OpenBoth">Select _Both</Button>
<TextBlock x:Name="PickerLastResultsVisible" <TextBlock Text="Windows:" />
Classes="h2"
IsVisible="False"
Text="Last picker results:" />
<ItemsPresenter x:Name="PickerLastResults" />
<TextBlock Margin="0, 8, 0, 0" <Expander Header="Window dialogs">
Classes="h1" <StackPanel Spacing="4">
Text="Window dialogs" /> <Button Name="DecoratedWindow">Decorated _window</Button>
<Button Name="DecoratedWindow">Decorated _window</Button> <Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button>
<Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button> <Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button>
<Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button> <Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button>
<Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button> <Button Name="OwnedWindow">Own_ed window</Button>
<Button Name="OwnedWindow">Own_ed window</Button> <Button Name="OwnedWindowNoTaskbar">Owned window (No tas_kbar icon)</Button>
<Button Name="OwnedWindowNoTaskbar">Owned window (No tas_kbar icon)</Button> </StackPanel>
</Expander>
<TextBlock Margin="0,20,0,0" Text="Pickers:" />
<CheckBox Name="UseFilters">Use filters</CheckBox>
<Expander Header="FilePicker API">
<StackPanel Spacing="4">
<CheckBox Name="ForceManaged">Force managed dialog</CheckBox>
<CheckBox Name="OpenMultiple">Open multiple</CheckBox>
<Button Name="OpenFolderPicker">Select Fo_lder</Button>
<Button Name="OpenFilePicker">_Open File</Button>
<Button Name="SaveFilePicker">_Save File</Button>
<Button Name="OpenFileFromBookmark">Open File Bookmark</Button>
<Button Name="OpenFolderFromBookmark">Open Folder Bookmark</Button>
</StackPanel>
</Expander>
<Expander Header="Legacy OpenFileDialog">
<StackPanel Spacing="4">
<Button Name="OpenFile">_Open File</Button>
<Button Name="OpenMultipleFiles">Open _Multiple File</Button>
<Button Name="SaveFile">_Save File</Button>
<Button Name="SelectFolder">Select Fo_lder</Button>
<Button Name="OpenBoth">Select _Both</Button>
</StackPanel>
</Expander>
<TextBlock x:Name="PickerLastResultsVisible"
Classes="h2"
IsVisible="False"
Text="Last picker results:" />
<ItemsPresenter x:Name="PickerLastResults" />
<TextBox Name="BookmarkContainer" Watermark="Bookmark" />
<TextBox Name="OpenedFileContent"
MaxLines="10"
Watermark="Picked file content" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

234
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -1,13 +1,21 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Dialogs; using Avalonia.Dialogs;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
#pragma warning disable 4014 using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
#pragma warning disable CS0618 // Type or member is obsolete
#nullable enable
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
public class DialogsPage : UserControl public class DialogsPage : UserControl
@ -18,13 +26,16 @@ namespace ControlCatalog.Pages
var results = this.Get<ItemsPresenter>("PickerLastResults"); var results = this.Get<ItemsPresenter>("PickerLastResults");
var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible"); var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
var bookmarkContainer = this.Get<TextBox>("BookmarkContainer");
var openedFileContent = this.Get<TextBox>("OpenedFileContent");
var openMultiple = this.Get<CheckBox>("OpenMultiple");
string? lastSelectedDirectory = null; IStorageFolder? lastSelectedDirectory = null;
List<FileDialogFilter>? GetFilters() List<FileDialogFilter> GetFilters()
{ {
if (this.Get<CheckBox>("UseFilters").IsChecked != true) if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return null; return new List<FileDialogFilter>();
return new List<FileDialogFilter> return new List<FileDialogFilter>
{ {
new FileDialogFilter new FileDialogFilter
@ -39,12 +50,23 @@ namespace ControlCatalog.Pages
}; };
} }
List<FilePickerFileType>? GetFileTypes()
{
if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return null;
return new List<FilePickerFileType>
{
FilePickerFileTypes.All,
FilePickerFileTypes.TextPlain
};
}
this.Get<Button>("OpenFile").Click += async delegate this.Get<Button>("OpenFile").Click += async delegate
{ {
// Almost guaranteed to exist // Almost guaranteed to exist
var fullPath = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName; var uri = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
var initialFileName = fullPath == null ? null : System.IO.Path.GetFileName(fullPath); var initialFileName = uri == null ? null : System.IO.Path.GetFileName(uri);
var initialDirectory = fullPath == null ? null : System.IO.Path.GetDirectoryName(fullPath); var initialDirectory = uri == null ? null : System.IO.Path.GetDirectoryName(uri);
var result = await new OpenFileDialog() var result = await new OpenFileDialog()
{ {
@ -62,7 +84,7 @@ namespace ControlCatalog.Pages
{ {
Title = "Open multiple files", Title = "Open multiple files",
Filters = GetFilters(), Filters = GetFilters(),
Directory = lastSelectedDirectory, Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
AllowMultiple = true AllowMultiple = true
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
results.Items = result; results.Items = result;
@ -70,11 +92,13 @@ namespace ControlCatalog.Pages
}; };
this.Get<Button>("SaveFile").Click += async delegate this.Get<Button>("SaveFile").Click += async delegate
{ {
var filters = GetFilters();
var result = await new SaveFileDialog() var result = await new SaveFileDialog()
{ {
Title = "Save file", Title = "Save file",
Filters = GetFilters(), Filters = filters,
Directory = lastSelectedDirectory, Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
DefaultExtension = filters?.Any() == true ? "txt" : null,
InitialFileName = "test.txt" InitialFileName = "test.txt"
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
results.Items = new[] { result }; results.Items = new[] { result };
@ -85,14 +109,9 @@ namespace ControlCatalog.Pages
var result = await new OpenFolderDialog() var result = await new OpenFolderDialog()
{ {
Title = "Select folder", Title = "Select folder",
Directory = lastSelectedDirectory, Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
lastSelectedDirectory = new BclStorageFolder(new System.IO.DirectoryInfo(result));
if (!string.IsNullOrEmpty(result))
{
lastSelectedDirectory = result;
}
results.Items = new [] { result }; results.Items = new [] { result };
resultsVisible.IsVisible = result != null; resultsVisible.IsVisible = result != null;
}; };
@ -101,7 +120,7 @@ namespace ControlCatalog.Pages
var result = await new OpenFileDialog() var result = await new OpenFileDialog()
{ {
Title = "Select both", Title = "Select both",
Directory = lastSelectedDirectory, Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
AllowMultiple = true AllowMultiple = true
}.ShowManagedAsync(GetWindow(), new ManagedFileDialogOptions }.ShowManagedAsync(GetWindow(), new ManagedFileDialogOptions
{ {
@ -116,20 +135,20 @@ namespace ControlCatalog.Pages
}; };
this.Get<Button>("DecoratedWindowDialog").Click += delegate this.Get<Button>("DecoratedWindowDialog").Click += delegate
{ {
new DecoratedWindow().ShowDialog(GetWindow()); _ = new DecoratedWindow().ShowDialog(GetWindow());
}; };
this.Get<Button>("Dialog").Click += delegate this.Get<Button>("Dialog").Click += delegate
{ {
var window = CreateSampleWindow(); var window = CreateSampleWindow();
window.Height = 200; window.Height = 200;
window.ShowDialog(GetWindow()); _ = window.ShowDialog(GetWindow());
}; };
this.Get<Button>("DialogNoTaskbar").Click += delegate this.Get<Button>("DialogNoTaskbar").Click += delegate
{ {
var window = CreateSampleWindow(); var window = CreateSampleWindow();
window.Height = 200; window.Height = 200;
window.ShowInTaskbar = false; window.ShowInTaskbar = false;
window.ShowDialog(GetWindow()); _ = window.ShowDialog(GetWindow());
}; };
this.Get<Button>("OwnedWindow").Click += delegate this.Get<Button>("OwnedWindow").Click += delegate
{ {
@ -146,13 +165,166 @@ namespace ControlCatalog.Pages
window.Show(GetWindow()); window.Show(GetWindow());
}; };
this.Get<Button>("OpenFilePicker").Click += async delegate
{
var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions()
{
Title = "Open file",
FileTypeFilter = GetFileTypes(),
SuggestedStartLocation = lastSelectedDirectory,
AllowMultiple = openMultiple.IsChecked == true
});
await SetPickerResult(result);
};
this.Get<Button>("SaveFilePicker").Click += async delegate
{
var fileTypes = GetFileTypes();
var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions()
{
Title = "Save file",
FileTypeChoices = fileTypes,
SuggestedStartLocation = lastSelectedDirectory,
SuggestedFileName = "FileName",
DefaultExtension = fileTypes?.Any() == true ? "txt" : null,
ShowOverwritePrompt = false
});
if (file is not null && file.CanOpenWrite)
{
// Sync disposal of StreamWriter is not supported on WASM
#if NET6_0_OR_GREATER
await using var stream = await file.OpenWrite();
await using var reader = new System.IO.StreamWriter(stream);
#else
using var stream = await file.OpenWrite();
using var reader = new System.IO.StreamWriter(stream);
#endif
await reader.WriteLineAsync(openedFileContent.Text);
lastSelectedDirectory = await file.GetParentAsync();
}
await SetPickerResult(file is null ? null : new [] {file});
};
this.Get<Button>("OpenFolderPicker").Click += async delegate
{
var folders = await GetStorageProvider().OpenFolderPickerAsync(new FolderPickerOpenOptions()
{
Title = "Folder file",
SuggestedStartLocation = lastSelectedDirectory,
AllowMultiple = openMultiple.IsChecked == true
});
await SetPickerResult(folders);
lastSelectedDirectory = folders.FirstOrDefault();
};
this.Get<Button>("OpenFileFromBookmark").Click += async delegate
{
var file = bookmarkContainer.Text is not null
? await GetStorageProvider().OpenFileBookmarkAsync(bookmarkContainer.Text)
: null;
await SetPickerResult(file is null ? null : new[] { file });
};
this.Get<Button>("OpenFolderFromBookmark").Click += async delegate
{
var folder = bookmarkContainer.Text is not null
? await GetStorageProvider().OpenFolderBookmarkAsync(bookmarkContainer.Text)
: null;
await SetPickerResult(folder is null ? null : new[] { folder });
lastSelectedDirectory = folder;
};
async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)
{
items ??= Array.Empty<IStorageItem>();
var mappedResults = items.Select(FullPathOrName).ToList();
bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmark() : "Can't bookmark";
if (items.FirstOrDefault() is IStorageItem item)
{
var resultText = item is IStorageFile ? "File:" : "Folder:";
resultText += Environment.NewLine;
var props = await item.GetBasicPropertiesAsync();
resultText += @$"Size: {props.Size}
DateCreated: {props.DateCreated}
DateModified: {props.DateModified}
CanBookmark: {item.CanBookmark}
";
if (item is IStorageFile file)
{
resultText += @$"
CanOpenRead: {file.CanOpenRead}
CanOpenWrite: {file.CanOpenWrite}
Content:
";
if (file.CanOpenRead)
{
#if NET6_0_OR_GREATER
await using var stream = await file.OpenRead();
#else
using var stream = await file.OpenRead();
#endif
using var reader = new System.IO.StreamReader(stream);
// 4GB file test, shouldn't load more than 10000 chars into a memory.
const int length = 10000;
var buffer = ArrayPool<char>.Shared.Rent(length);
try
{
var charsRead = await reader.ReadAsync(buffer, 0, length);
resultText += new string(buffer, 0, charsRead);
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
}
openedFileContent.Text = resultText;
lastSelectedDirectory = await item.GetParentAsync();
if (lastSelectedDirectory is not null)
{
mappedResults.Insert(0, "Parent: " + FullPathOrName(lastSelectedDirectory));
}
}
results.Items = mappedResults;
resultsVisible.IsVisible = mappedResults.Any();
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var openedFileContent = this.Get<TextBox>("OpenedFileContent");
try
{
var storageProvider = GetStorageProvider();
openedFileContent.Text = $@"CanOpen: {storageProvider.CanOpen}
CanSave: {storageProvider.CanSave}
CanPickFolder: {storageProvider.CanPickFolder}";
}
catch (Exception ex)
{
openedFileContent.Text = "Storage provider is not available: " + ex.Message;
}
} }
private Window CreateSampleWindow() private Window CreateSampleWindow()
{ {
Button button; Button button;
Button dialogButton; Button dialogButton;
var window = new Window var window = new Window
{ {
Height = 200, Height = 200,
@ -191,7 +363,22 @@ namespace ControlCatalog.Pages
return window; return window;
} }
Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner"); private IStorageProvider GetStorageProvider()
{
var forceManaged = this.Get<CheckBox>("ForceManaged").IsChecked ?? false;
return forceManaged
? new ManagedStorageProvider<Window>(GetWindow(), null)
: GetTopLevel().StorageProvider;
}
private static string FullPathOrName(IStorageItem? item)
{
if (item is null) return "(null)";
return item.TryGetUri(out var uri) ? uri.ToString() : item.Name;
}
Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner");
TopLevel GetTopLevel() => this.VisualRoot as TopLevel ?? throw new NullReferenceException("Invalid Owner");
private void InitializeComponent() private void InitializeComponent()
{ {
@ -199,3 +386,4 @@ namespace ControlCatalog.Pages
} }
} }
} }
#pragma warning restore CS0618 // Type or member is obsolete

6
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -76,21 +76,21 @@
<StackPanel Orientation="Vertical" Margin="10"> <StackPanel Orientation="Vertical" Margin="10">
<Label Target="upDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of decimal NumericUpDown:</Label> <Label Target="upDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of decimal NumericUpDown:</Label>
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5" <NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Value="{Binding DecimalValue}" VerticalAlignment="Center" Value="{Binding DecimalValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/> Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Margin="10"> <StackPanel Orientation="Vertical" Margin="10">
<Label Target="DoubleUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of double NumericUpDown:</Label> <Label Target="DoubleUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of double NumericUpDown:</Label>
<NumericUpDown Name="DoubleUpDown" Minimum="0" Maximum="10" Increment="0.5" <NumericUpDown Name="DoubleUpDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Value="{Binding DoubleValue}" VerticalAlignment="Center" Value="{Binding DoubleValue}"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/> Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Margin="10"> <StackPanel Orientation="Vertical" Margin="10">
<Label Target="ValidationUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">NumericUpDown with Validation Errors:</Label> <Label Target="ValidationUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">NumericUpDown with Validation Errors:</Label>
<NumericUpDown x:Name="ValidationUpDown" Minimum="0" Maximum="10" Increment="0.5" <NumericUpDown x:Name="ValidationUpDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" VerticalAlignment="Center"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"> Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}">
<DataValidationErrors.Error> <DataValidationErrors.Error>
<sys:Exception /> <sys:Exception />

13
samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs

@ -84,15 +84,10 @@ namespace ControlCatalog.Pages
} }
} }
public IList<CultureInfo> Cultures { get; } = new List<CultureInfo>() // Trimmed-mode friendly where we might not have cultures
{ public IList<CultureInfo?> Cultures { get; } = CultureInfo.GetCultures(CultureTypes.SpecificCultures)
new CultureInfo("en-US"), .Where(c => new[] { "en-US", "en-GB", "fr-FR", "ar-DZ", "zh-CH", "cs-CZ" }.Contains(c.Name))
new CultureInfo("en-GB"), .ToArray();
new CultureInfo("fr-FR"),
new CultureInfo("ar-DZ"),
new CultureInfo("zh-CN"),
new CultureInfo("cs-CZ")
};
public FormatObject SelectedFormat public FormatObject SelectedFormat
{ {

235
samples/ControlCatalog/Pages/PointerCanvas.cs

@ -0,0 +1,235 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Threading;
namespace ControlCatalog.Pages;
public class PointerCanvas : Control
{
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private int _events;
private IDisposable? _statusUpdated;
private Dictionary<int, PointerPoints> _pointers = new();
private PointerPointProperties? _lastProperties;
private PointerUpdateKind? _lastNonOtherUpdateKind;
class PointerPoints
{
struct CanvasPoint
{
public IBrush Brush;
public Point Point;
public double Radius;
public double? Pressure;
}
readonly CanvasPoint[] _points = new CanvasPoint[1000];
int _index;
public void Render(DrawingContext context, bool drawPoints)
{
CanvasPoint? prev = null;
for (var c = 0; c < _points.Length; c++)
{
var i = (c + _index) % _points.Length;
var pt = _points[i];
var pressure = (pt.Pressure ?? prev?.Pressure ?? 0.5);
var thickness = pressure * 10;
var radius = pressure * pt.Radius;
if (drawPoints)
{
if (pt.Brush != null)
{
context.DrawEllipse(pt.Brush, null, pt.Point, radius, radius);
}
}
else
{
if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null
&& prev.Value.Pressure != null && pt.Pressure != null)
{
var linePen = new Pen(Brushes.Black, thickness, null, PenLineCap.Round, PenLineJoin.Round);
context.DrawLine(linePen, prev.Value.Point, pt.Point);
}
}
prev = pt;
}
}
void AddPoint(Point pt, IBrush brush, double radius, float? pressure = null)
{
_points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius, Pressure = pressure };
_index = (_index + 1) % _points.Length;
}
public void HandleEvent(PointerEventArgs e, Visual v)
{
e.Handled = true;
var currentPoint = e.GetCurrentPoint(v);
if (e.RoutedEvent == PointerPressedEvent)
AddPoint(currentPoint.Position, Brushes.Green, 10);
else if (e.RoutedEvent == PointerReleasedEvent)
AddPoint(currentPoint.Position, Brushes.Red, 10);
else
{
var pts = e.GetIntermediatePoints(v);
for (var c = 0; c < pts.Count; c++)
{
var pt = pts[c];
AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
c == pts.Count - 1 ? 5 : 2, pt.Properties.Pressure);
}
}
}
}
private int _threadSleep;
public static DirectProperty<PointerCanvas, int> ThreadSleepProperty =
AvaloniaProperty.RegisterDirect<PointerCanvas, int>(nameof(ThreadSleep), c => c.ThreadSleep, (c, v) => c.ThreadSleep = v);
public int ThreadSleep
{
get => _threadSleep;
set => SetAndRaise(ThreadSleepProperty, ref _threadSleep, value);
}
private bool _drawOnlyPoints;
public static DirectProperty<PointerCanvas, bool> DrawOnlyPointsProperty =
AvaloniaProperty.RegisterDirect<PointerCanvas, bool>(nameof(DrawOnlyPoints), c => c.DrawOnlyPoints, (c, v) => c.DrawOnlyPoints = v);
public bool DrawOnlyPoints
{
get => _drawOnlyPoints;
set => SetAndRaise(DrawOnlyPointsProperty, ref _drawOnlyPoints, value);
}
private string? _status;
public static DirectProperty<PointerCanvas, string?> StatusProperty =
AvaloniaProperty.RegisterDirect<PointerCanvas, string?>(nameof(DrawOnlyPoints), c => c.Status, (c, v) => c.Status = v,
defaultBindingMode: Avalonia.Data.BindingMode.TwoWay);
public string? Status
{
get => _status;
set => SetAndRaise(StatusProperty, ref _status, value);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_statusUpdated = DispatcherTimer.Run(() =>
{
if (_stopwatch.Elapsed.TotalMilliseconds > 250)
{
Status = $@"Events per second: {(_events / _stopwatch.Elapsed.TotalSeconds)}
PointerUpdateKind: {_lastProperties?.PointerUpdateKind}
Last PointerUpdateKind != Other: {_lastNonOtherUpdateKind}
IsLeftButtonPressed: {_lastProperties?.IsLeftButtonPressed}
IsRightButtonPressed: {_lastProperties?.IsRightButtonPressed}
IsMiddleButtonPressed: {_lastProperties?.IsMiddleButtonPressed}
IsXButton1Pressed: {_lastProperties?.IsXButton1Pressed}
IsXButton2Pressed: {_lastProperties?.IsXButton2Pressed}
IsBarrelButtonPressed: {_lastProperties?.IsBarrelButtonPressed}
IsEraser: {_lastProperties?.IsEraser}
IsInverted: {_lastProperties?.IsInverted}
Pressure: {_lastProperties?.Pressure}
XTilt: {_lastProperties?.XTilt}
YTilt: {_lastProperties?.YTilt}
Twist: {_lastProperties?.Twist}";
_stopwatch.Restart();
_events = 0;
}
return true;
}, TimeSpan.FromMilliseconds(10));
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_statusUpdated?.Dispose();
}
void HandleEvent(PointerEventArgs e)
{
_events++;
if (_threadSleep != 0)
{
Thread.Sleep(_threadSleep);
}
InvalidateVisual();
var lastPointer = e.GetCurrentPoint(this);
_lastProperties = lastPointer.Properties;
if (_lastProperties.PointerUpdateKind != PointerUpdateKind.Other)
{
_lastNonOtherUpdateKind = _lastProperties.PointerUpdateKind;
}
if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
{
_pointers.Remove(e.Pointer.Id);
return;
}
if (e.Pointer.Type != PointerType.Pen
|| lastPointer.Properties.Pressure > 0)
{
if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
_pointers[e.Pointer.Id] = pt = new PointerPoints();
pt.HandleEvent(e, this);
}
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Brushes.White, Bounds);
foreach (var pt in _pointers.Values)
pt.Render(context, _drawOnlyPoints);
base.Render(context);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.ClickCount == 2)
{
_pointers.Clear();
InvalidateVisual();
return;
}
HandleEvent(e);
base.OnPointerPressed(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
HandleEvent(e);
base.OnPointerMoved(e);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
HandleEvent(e);
base.OnPointerReleased(e);
}
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
_lastProperties = null;
base.OnPointerCaptureLost(e);
}
}

109
samples/ControlCatalog/Pages/PointerContactsTab.cs

@ -0,0 +1,109 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.Immutable;
namespace ControlCatalog.Pages;
public class PointerContactsTab : Control
{
class PointerInfo
{
public Point Point { get; set; }
public Color Color { get; set; }
}
private static Color[] AllColors = new[]
{
Colors.Aqua,
Colors.Beige,
Colors.Chartreuse,
Colors.Coral,
Colors.Fuchsia,
Colors.Crimson,
Colors.Lavender,
Colors.Orange,
Colors.Orchid,
Colors.ForestGreen,
Colors.SteelBlue,
Colors.PapayaWhip,
Colors.PaleVioletRed,
Colors.Goldenrod,
Colors.Maroon,
Colors.Moccasin,
Colors.Navy,
Colors.Wheat,
Colors.Violet,
Colors.Sienna,
Colors.Indigo,
Colors.Honeydew
};
private Dictionary<IPointer, PointerInfo> _pointers = new Dictionary<IPointer, PointerInfo>();
public PointerContactsTab()
{
ClipToBounds = true;
}
void UpdatePointer(PointerEventArgs e)
{
if (!_pointers.TryGetValue(e.Pointer, out var info))
{
if (e.RoutedEvent == PointerMovedEvent)
return;
var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
var color = colors[new Random().Next(0, colors.Length - 1)];
_pointers[e.Pointer] = info = new PointerInfo { Color = color };
}
info.Point = e.GetPosition(this);
InvalidateVisual();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
UpdatePointer(e);
e.Pointer.Capture(this);
e.Handled = true;
base.OnPointerPressed(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
UpdatePointer(e);
e.Handled = true;
base.OnPointerMoved(e);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
_pointers.Remove(e.Pointer);
e.Handled = true;
InvalidateVisual();
}
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
_pointers.Remove(e.Pointer);
InvalidateVisual();
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
foreach (var pt in _pointers.Values)
{
var brush = new ImmutableSolidColorBrush(pt.Color);
context.DrawEllipse(brush, null, pt.Point, 75, 75);
}
}
}

322
samples/ControlCatalog/Pages/PointersPage.cs

@ -1,322 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace ControlCatalog.Pages;
public class PointersPage : Decorator
{
public PointersPage()
{
Child = new TabControl
{
Items = new[]
{
new TabItem() { Header = "Contacts", Content = new PointerContactsTab() },
new TabItem() { Header = "IntermediatePoints", Content = new PointerIntermediatePointsTab() }
}
};
}
class PointerContactsTab : Control
{
class PointerInfo
{
public Point Point { get; set; }
public Color Color { get; set; }
}
private static Color[] AllColors = new[]
{
Colors.Aqua,
Colors.Beige,
Colors.Chartreuse,
Colors.Coral,
Colors.Fuchsia,
Colors.Crimson,
Colors.Lavender,
Colors.Orange,
Colors.Orchid,
Colors.ForestGreen,
Colors.SteelBlue,
Colors.PapayaWhip,
Colors.PaleVioletRed,
Colors.Goldenrod,
Colors.Maroon,
Colors.Moccasin,
Colors.Navy,
Colors.Wheat,
Colors.Violet,
Colors.Sienna,
Colors.Indigo,
Colors.Honeydew
};
private Dictionary<IPointer, PointerInfo> _pointers = new Dictionary<IPointer, PointerInfo>();
public PointerContactsTab()
{
ClipToBounds = true;
}
void UpdatePointer(PointerEventArgs e)
{
if (!_pointers.TryGetValue(e.Pointer, out var info))
{
if (e.RoutedEvent == PointerMovedEvent)
return;
var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
var color = colors[new Random().Next(0, colors.Length - 1)];
_pointers[e.Pointer] = info = new PointerInfo {Color = color};
}
info.Point = e.GetPosition(this);
InvalidateVisual();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
UpdatePointer(e);
e.Pointer.Capture(this);
e.Handled = true;
base.OnPointerPressed(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
UpdatePointer(e);
e.Handled = true;
base.OnPointerMoved(e);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
_pointers.Remove(e.Pointer);
e.Handled = true;
InvalidateVisual();
}
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
_pointers.Remove(e.Pointer);
InvalidateVisual();
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
foreach (var pt in _pointers.Values)
{
var brush = new ImmutableSolidColorBrush(pt.Color);
context.DrawEllipse(brush, null, pt.Point, 75, 75);
}
}
}
public class PointerIntermediatePointsTab : Decorator
{
public PointerIntermediatePointsTab()
{
this[TextElement.ForegroundProperty] = Brushes.Black;
var slider = new Slider
{
Margin = new Thickness(5),
Minimum = 0,
Maximum = 500
};
var status = new TextBlock()
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
};
Child = new Grid
{
Children =
{
new PointerCanvas(slider, status),
new Border
{
Background = Brushes.LightYellow,
Child = new StackPanel
{
Children =
{
new StackPanel
{
Orientation = Orientation.Horizontal,
Children =
{
new TextBlock { Text = "Thread sleep:" },
new TextBlock()
{
[!TextBlock.TextProperty] =slider.GetObservable(Slider.ValueProperty)
.Select(x=>x.ToString()).ToBinding()
}
}
},
slider
}
},
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Width = 300,
Height = 60
},
status
}
};
}
class PointerCanvas : Control
{
private readonly Slider _slider;
private readonly TextBlock _status;
private int _events;
private Stopwatch _stopwatch = Stopwatch.StartNew();
private Dictionary<int, PointerPoints> _pointers = new();
class PointerPoints
{
struct CanvasPoint
{
public IBrush Brush;
public Point Point;
public double Radius;
}
readonly CanvasPoint[] _points = new CanvasPoint[1000];
int _index;
public void Render(DrawingContext context)
{
CanvasPoint? prev = null;
for (var c = 0; c < _points.Length; c++)
{
var i = (c + _index) % _points.Length;
var pt = _points[i];
if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null)
context.DrawLine(new Pen(Brushes.Black), prev.Value.Point, pt.Point);
prev = pt;
if (pt.Brush != null)
context.DrawEllipse(pt.Brush, null, pt.Point, pt.Radius, pt.Radius);
}
}
void AddPoint(Point pt, IBrush brush, double radius)
{
_points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius };
_index = (_index + 1) % _points.Length;
}
public void HandleEvent(PointerEventArgs e, Visual v)
{
e.Handled = true;
if (e.RoutedEvent == PointerPressedEvent)
AddPoint(e.GetPosition(v), Brushes.Green, 10);
else if (e.RoutedEvent == PointerReleasedEvent)
AddPoint(e.GetPosition(v), Brushes.Red, 10);
else
{
var pts = e.GetIntermediatePoints(v);
for (var c = 0; c < pts.Count; c++)
{
var pt = pts[c];
AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
c == pts.Count - 1 ? 5 : 2);
}
}
}
}
public PointerCanvas(Slider slider, TextBlock status)
{
_slider = slider;
_status = status;
DispatcherTimer.Run(() =>
{
if (_stopwatch.Elapsed.TotalSeconds > 1)
{
_status.Text = "Events per second: " + (_events / _stopwatch.Elapsed.TotalSeconds);
_stopwatch.Restart();
_events = 0;
}
return this.GetVisualRoot() != null;
}, TimeSpan.FromMilliseconds(10));
}
void HandleEvent(PointerEventArgs e)
{
_events++;
Thread.Sleep((int)_slider.Value);
InvalidateVisual();
if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
{
_pointers.Remove(e.Pointer.Id);
return;
}
if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
_pointers[e.Pointer.Id] = pt = new PointerPoints();
pt.HandleEvent(e, this);
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Brushes.White, Bounds);
foreach(var pt in _pointers.Values)
pt.Render(context);
base.Render(context);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.ClickCount == 2)
{
_pointers.Clear();
InvalidateVisual();
return;
}
HandleEvent(e);
base.OnPointerPressed(e);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
HandleEvent(e);
base.OnPointerMoved(e);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
HandleEvent(e);
base.OnPointerReleased(e);
}
}
}
}

66
samples/ControlCatalog/Pages/PointersPage.xaml

@ -0,0 +1,66 @@
<UserControl x:Class="ControlCatalog.Pages.PointersPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlCatalog.Pages">
<TabControl>
<TabItem Header="Contacts">
<local:PointerContactsTab />
</TabItem>
<TabItem Header="IntermediatePoints">
<Panel TextElement.Foreground="Black">
<local:PointerCanvas x:Name="IntermediatePointsCanvas"
DrawOnlyPoints="True"
Status="{Binding #Status1TextBlock.Text, Mode=OneWayToSource}"
ThreadSleep="{Binding #ThreadSleepSlider.Value}" />
<Border Width="300"
Height="60"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<StackPanel Background="LightYellow">
<TextBlock Text="{Binding #ThreadSleepSlider.Value, StringFormat='Thread sleep: {0} / 500'}" />
<Slider x:Name="ThreadSleepSlider"
Value="50"
Maximum="500"
Minimum="0" />
</StackPanel>
</Border>
<TextBlock x:Name="Status1TextBlock"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</Panel>
</TabItem>
<TabItem Header="Pressure">
<Panel TextElement.Foreground="Black">
<local:PointerCanvas x:Name="PressureCanvas"
DrawOnlyPoints="False"
Status="{Binding #Status2TextBlock.Text, Mode=OneWayToSource}"
ThreadSleep="0" />
<TextBlock x:Name="Status2TextBlock"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</Panel>
</TabItem>
<TabItem Header="Capture">
<WrapPanel>
<Border Name="BorderCapture1"
MinWidth="250"
MinHeight="170"
Margin="5"
Padding="50"
Background="{DynamicResource SystemAccentColor}"
ToolTip.Placement="Bottom">
<TextBlock>Capture 1</TextBlock>
</Border>
<Border Name="BorderCapture2"
MinWidth="250"
MinHeight="170"
Margin="5"
Padding="50"
Background="{DynamicResource SystemAccentColor}"
ToolTip.Placement="Bottom">
<TextBlock>Capture 2</TextBlock>
</Border>
</WrapPanel>
</TabItem>
</TabControl>
</UserControl>

78
samples/ControlCatalog/Pages/PointersPage.xaml.cs

@ -0,0 +1,78 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages;
public class PointersPage : UserControl
{
public PointersPage()
{
this.InitializeComponent();
var border1 = this.Get<Border>("BorderCapture1");
var border2 = this.Get<Border>("BorderCapture2");
border1.PointerPressed += Border_PointerPressed;
border1.PointerReleased += Border_PointerReleased;
border1.PointerCaptureLost += Border_PointerCaptureLost;
border1.PointerMoved += Border_PointerUpdated;
border1.PointerEntered += Border_PointerUpdated;
border1.PointerExited += Border_PointerUpdated;
border2.PointerPressed += Border_PointerPressed;
border2.PointerReleased += Border_PointerReleased;
border2.PointerCaptureLost += Border_PointerCaptureLost;
border2.PointerMoved += Border_PointerUpdated;
border2.PointerEntered += Border_PointerUpdated;
border2.PointerExited += Border_PointerUpdated;
}
private void Border_PointerUpdated(object sender, PointerEventArgs e)
{
var textBlock = (TextBlock)((Border)sender).Child;
var position = e.GetPosition((Border)sender);
textBlock.Text = @$"Type: {e.Pointer.Type}
Captured: {e.Pointer.Captured == sender}
PointerId: {e.Pointer.Id}
Position: {(int)position.X} {(int)position.Y}";
e.Handled = true;
}
private void Border_PointerCaptureLost(object sender, PointerCaptureLostEventArgs e)
{
var textBlock = (TextBlock)((Border)sender).Child;
textBlock.Text = @$"Type: {e.Pointer.Type}
Captured: {e.Pointer.Captured == sender}
PointerId: {e.Pointer.Id}
Position: ??? ???";
e.Handled = true;
}
private void Border_PointerReleased(object sender, PointerReleasedEventArgs e)
{
if (e.Pointer.Captured == sender)
{
e.Pointer.Capture(null);
e.Handled = true;
}
else
{
throw new InvalidOperationException("How?");
}
}
private void Border_PointerPressed(object sender, PointerPressedEventArgs e)
{
e.Pointer.Capture((Border)sender);
e.Handled = true;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

4
samples/ControlCatalog/Pages/TextBlockPage.xaml

@ -118,7 +118,7 @@
</StackPanel> </StackPanel>
</Border> </Border>
<Border> <Border>
<TextBlock Margin="10" TextWrapping="Wrap"> <RichTextBlock Margin="10" TextWrapping="Wrap">
This <Span FontWeight="Bold">is</Span> a This <Span FontWeight="Bold">is</Span> a
<Span Background="Silver" Foreground="Maroon">TextBlock</Span> <Span Background="Silver" Foreground="Maroon">TextBlock</Span>
with <Span TextDecorations="Underline">several</Span> with <Span TextDecorations="Underline">several</Span>
@ -126,7 +126,7 @@
<Span Foreground="Blue"> <Span Foreground="Blue">
using a <Bold>variety</Bold> of <Italic>styles</Italic> using a <Bold>variety</Bold> of <Italic>styles</Italic>
</Span>. </Span>.
</TextBlock> </RichTextBlock>
</Border> </Border>
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>

21
samples/IntegrationTestApp/MainWindow.axaml

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow" x:Class="IntegrationTestApp.MainWindow"
Name="MainWindow"
Title="IntegrationTestApp"> Title="IntegrationTestApp">
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
@ -94,6 +95,26 @@
</StackPanel> </StackPanel>
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="Window">
<StackPanel>
<TextBox Name="ShowWindowSize" Watermark="Window Size"/>
<ComboBox Name="ShowWindowMode" SelectedIndex="0">
<ComboBoxItem>NonOwned</ComboBoxItem>
<ComboBoxItem>Owned</ComboBoxItem>
<ComboBoxItem>Modal</ComboBoxItem>
</ComboBox>
<ComboBox Name="ShowWindowLocation" SelectedIndex="0">
<ComboBoxItem>Manual</ComboBoxItem>
<ComboBoxItem>CenterScreen</ComboBoxItem>
<ComboBoxItem>CenterOwner</ComboBoxItem>
</ComboBox>
<Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
<Button Name="RestoreAll">Restore All</Button>
</StackPanel>
</TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

66
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
namespace IntegrationTestApp namespace IntegrationTestApp
{ {
@ -46,6 +48,62 @@ namespace IntegrationTestApp
} }
} }
private void ShowWindow()
{
var sizeTextBox = this.GetControl<TextBox>("ShowWindowSize");
var modeComboBox = this.GetControl<ComboBox>("ShowWindowMode");
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var owner = (Window)this.GetVisualRoot()!;
var window = new ShowWindowTest
{
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
};
if (size.HasValue)
{
window.Width = size.Value.Width;
window.Height = size.Value.Height;
}
sizeTextBox.Text = string.Empty;
switch (modeComboBox.SelectedIndex)
{
case 0:
window.Show();
break;
case 1:
window.Show(owner);
break;
case 2:
window.ShowDialog(owner);
break;
}
}
private void SendToBack()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows)
{
window.Activate();
}
}
private void RestoreAll()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows)
{
if (window.WindowState == WindowState.Minimized)
window.WindowState = WindowState.Normal;
}
}
private void MenuClicked(object? sender, RoutedEventArgs e) private void MenuClicked(object? sender, RoutedEventArgs e)
{ {
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem"); var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem");
@ -64,6 +122,14 @@ namespace IntegrationTestApp
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1; this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset") if (source?.Name == "MenuClickedMenuItemReset")
this.FindControl<TextBlock>("ClickedMenuItem").Text = "None"; this.FindControl<TextBlock>("ClickedMenuItem").Text = "None";
if (source?.Name == "ShowWindow")
ShowWindow();
if (source?.Name == "SendToBack")
SendToBack();
if (source?.Name == "ExitFullscreen")
WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll")
RestoreAll();
} }
} }
} }

35
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -0,0 +1,35 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="IntegrationTestApp.ShowWindowTest"
Name="SecondaryWindow"
Title="Show Window Test">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Column="0" Grid.Row="1">Client Size</Label>
<TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
Text="{Binding ClientSize, Mode=OneWay}"/>
<Label Grid.Column="0" Grid.Row="2">Frame Size</Label>
<TextBox Name="FrameSize" Grid.Column="1" Grid.Row="2" IsReadOnly="True"
Text="{Binding FrameSize, Mode=OneWay}"/>
<Label Grid.Column="0" Grid.Row="3">Position</Label>
<TextBox Name="Position" Grid.Column="1" Grid.Row="3" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="4">Owner Rect</Label>
<TextBox Name="OwnerRect" Grid.Column="1" Grid.Row="4" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="5">Screen Rect</Label>
<TextBox Name="ScreenRect" Grid.Column="1" Grid.Row="5" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="6">Scaling</Label>
<TextBox Name="Scaling" Grid.Column="1" Grid.Row="6" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="7">WindowState</Label>
<ComboBox Name="WindowState" Grid.Column="1" Grid.Row="7" SelectedIndex="{Binding WindowState}">
<ComboBoxItem>Normal</ComboBoxItem>
<ComboBoxItem>Minimized</ComboBoxItem>
<ComboBoxItem>Maximized</ComboBoxItem>
<ComboBoxItem>Fullscreen</ComboBoxItem>
</ComboBox>
</Grid>
</Window>

40
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

@ -0,0 +1,40 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Rendering;
namespace IntegrationTestApp
{
public class ShowWindowTest : Window
{
public ShowWindowTest()
{
InitializeComponent();
DataContext = this;
PositionChanged += (s, e) => this.GetControl<TextBox>("Position").Text = $"{Position}";
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
var scaling = PlatformImpl!.DesktopScaling;
this.GetControl<TextBox>("Position").Text = $"{Position}";
this.GetControl<TextBox>("ScreenRect").Text = $"{Screens.ScreenFromVisual(this)?.WorkingArea}";
this.GetControl<TextBox>("Scaling").Text = $"{scaling}";
if (Owner is not null)
{
var ownerRect = this.GetControl<TextBox>("OwnerRect");
var owner = (Window)Owner;
ownerRect.Text = $"{owner.Position}, {PixelSize.FromSize(owner.FrameSize!.Value, scaling)}";
}
}
}
}

4
samples/RenderDemo/Pages/FormattedTextPage.axaml.cs

@ -55,6 +55,10 @@ namespace RenderDemo.Pages
formattedText.SetFontStyle(FontStyle.Italic, 28, 28); formattedText.SetFontStyle(FontStyle.Italic, 28, 28);
context.DrawText(formattedText, new Point(10, 0)); context.DrawText(formattedText, new Point(10, 0));
var geometry = formattedText.BuildGeometry(new Point(10 + formattedText.Width + 10, 0));
context.DrawGeometry(gradient, null, geometry);
} }
} }
} }

12
samples/RenderDemo/Pages/GlyphRunPage.xaml

@ -2,13 +2,13 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RenderDemo.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RenderDemo.Pages.GlyphRunPage"> x:Class="RenderDemo.Pages.GlyphRunPage">
<Border <Grid
ColumnDefinitions="*,*"
Background="White"> Background="White">
<Image <local:GlyphRunControl Grid.Column="0"/>
x:Name="imageControl" <local:GlyphRunGeometryControl Grid.Column="1"/>
Stretch="None"> </Grid>
</Image>
</Border>
</UserControl> </UserControl>

113
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -9,14 +9,6 @@ namespace RenderDemo.Pages
{ {
public class GlyphRunPage : UserControl public class GlyphRunPage : UserControl
{ {
private Image _imageControl;
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
public GlyphRunPage() public GlyphRunPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
@ -25,19 +17,43 @@ namespace RenderDemo.Pages
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
}
}
public class GlyphRunControl : Control
{
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
_imageControl = this.FindControl<Image>("imageControl"); private DispatcherTimer _timer;
_imageControl.Source = new DrawingImage();
DispatcherTimer.Run(() => protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_timer.Tick += (s,e) =>
{ {
UpdateGlyphRun(); InvalidateVisual();
};
_timer.Start();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
return true; _timer = null;
}, TimeSpan.FromSeconds(1));
} }
private void UpdateGlyphRun() public override void Render(DrawingContext context)
{ {
var c = (char)_rand.Next(65, 90); var c = (char)_rand.Next(65, 90);
@ -57,27 +73,70 @@ namespace RenderDemo.Pages
_characters[0] = c; _characters[0] = c;
var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight; var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices);
var drawingGroup = new DrawingGroup(); context.DrawGlyphRun(Brushes.Black, glyphRun);
}
}
var glyphRunDrawing = new GlyphRunDrawing public class GlyphRunGeometryControl : Control
{
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
private float _fontSize = 20;
private int _direction = 10;
private DispatcherTimer _timer;
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer = new DispatcherTimer
{ {
Foreground = Brushes.Black, Interval = TimeSpan.FromSeconds(1)
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices)
}; };
drawingGroup.Children.Add(glyphRunDrawing); _timer.Tick += (s, e) =>
var geometryDrawing = new GeometryDrawing
{ {
Pen = new Pen(Brushes.Black), InvalidateVisual();
Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
}; };
drawingGroup.Children.Add(geometryDrawing); _timer.Start();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
_timer = null;
}
public override void Render(DrawingContext context)
{
var c = (char)_rand.Next(65, 90);
if (_fontSize + _direction > 200)
{
_direction = -10;
}
if (_fontSize + _direction < 20)
{
_direction = 10;
}
_fontSize += _direction;
_glyphIndices[0] = _glyphTypeface.GetGlyph(c);
_characters[0] = c;
var glyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices);
var geometry = glyphRun.BuildGeometry();
(_imageControl.Source as DrawingImage).Drawing = drawingGroup; context.DrawGeometry(Brushes.Green, null, geometry);
} }
} }
} }

5
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -1,10 +1,8 @@
using System; using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Android; using Avalonia.Android;
using Avalonia.Android.Platform; using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Input;
using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
@ -55,14 +53,11 @@ namespace Avalonia.Android
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>() .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance) .Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface()) .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>()
.Bind<IRenderTimer>().ToConstant(new ChoreographerTimer()) .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>(); .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
SkiaPlatform.Initialize();
if (options.UseGpu) if (options.UseGpu)
{ {
EglPlatformOpenGlInterface.TryInitialize(); EglPlatformOpenGlInterface.TryInitialize();

39
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -4,10 +4,14 @@ using Android.Content.Res;
using AndroidX.Lifecycle; using AndroidX.Lifecycle;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls; using Avalonia.Controls;
using Android.Runtime;
using Android.App;
using Android.Content;
using System;
namespace Avalonia.Android namespace Avalonia.Android
{ {
public abstract class AvaloniaActivity<TApp> : AppCompatActivity where TApp : Application, new() public abstract class AvaloniaActivity : AppCompatActivity
{ {
internal class SingleViewLifetime : ISingleViewApplicationLifetime internal class SingleViewLifetime : ISingleViewApplicationLifetime
{ {
@ -20,32 +24,32 @@ namespace Avalonia.Android
} }
} }
internal Action<int, Result, Intent> ActivityResult;
internal AvaloniaView View; internal AvaloniaView View;
internal AvaloniaViewModel _viewModel; internal AvaloniaViewModel _viewModel;
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid(); protected abstract AppBuilder CreateAppBuilder();
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
var builder = AppBuilder.Configure<TApp>(); var builder = CreateAppBuilder();
CustomizeAppBuilder(builder);
View = new AvaloniaView(this);
SetContentView(View);
var lifetime = new SingleViewLifetime(); var lifetime = new SingleViewLifetime();
lifetime.View = View;
builder.AfterSetup(x => builder.AfterSetup(x =>
{ {
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
View = new AvaloniaView(this);
if (_viewModel.Content != null) if (_viewModel.Content != null)
{ {
View.Content = _viewModel.Content; View.Content = _viewModel.Content;
} }
SetContentView(View);
lifetime.View = View;
View.Prepare(); View.Prepare();
}); });
@ -78,5 +82,24 @@ namespace Avalonia.Android
base.OnDestroy(); base.OnDestroy();
} }
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
ActivityResult?.Invoke(requestCode, resultCode, data);
}
}
public abstract class AvaloniaActivity<TApp> : AvaloniaActivity where TApp : Application, new()
{
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected override AppBuilder CreateAppBuilder()
{
var builder = AppBuilder.Configure<TApp>();
return CustomizeAppBuilder(builder);
}
} }
} }

8
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -7,6 +7,7 @@ using Android.Views.InputMethods;
using Avalonia.Android.OpenGL; using Avalonia.Android.OpenGL;
using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Android.Platform.Storage;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
@ -16,11 +17,13 @@ using Avalonia.Input.TextInput;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces; using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Rendering; using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform namespace Avalonia.Android.Platform.SkiaPlatform
{ {
class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo,
ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider
{ {
private readonly IGlPlatformSurface _gl; private readonly IGlPlatformSurface _gl;
private readonly IFramebufferPlatformSurface _framebuffer; private readonly IFramebufferPlatformSurface _framebuffer;
@ -46,6 +49,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView); NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
StorageProvider = new AndroidStorageProvider((AvaloniaActivity)avaloniaView.Context);
} }
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) => public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
@ -225,6 +229,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public ITextInputMethodImpl TextInputMethod => _textInputMethod; public ITextInputMethodImpl TextInputMethod => _textInputMethod;
public INativeControlHostImpl NativeControlHost { get; } public INativeControlHostImpl NativeControlHost { get; }
public IStorageProvider StorageProvider { get; }
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{ {

244
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -0,0 +1,244 @@
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Android.Content;
using Android.Provider;
using Avalonia.Logging;
using Avalonia.Platform.Storage;
using Java.Lang;
using AndroidUri = Android.Net.Uri;
using Exception = System.Exception;
using JavaFile = Java.IO.File;
namespace Avalonia.Android.Platform.Storage;
internal abstract class AndroidStorageItem : IStorageBookmarkItem
{
private Context? _context;
protected AndroidStorageItem(Context context, AndroidUri uri)
{
_context = context;
Uri = uri;
}
internal AndroidUri Uri { get; }
protected Context Context => _context ?? throw new ObjectDisposedException(nameof(AndroidStorageItem));
public string Name => GetColumnValue(Context, Uri, MediaStore.IMediaColumns.DisplayName)
?? Uri.PathSegments?.LastOrDefault() ?? string.Empty;
public bool CanBookmark => true;
public Task<string?> SaveBookmark()
{
Context.ContentResolver?.TakePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
return Task.FromResult(Uri.ToString());
}
public Task ReleaseBookmark()
{
Context.ContentResolver?.ReleasePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
return Task.CompletedTask;
}
public bool TryGetUri([NotNullWhen(true)] out Uri? uri)
{
uri = new Uri(Uri.ToString()!);
return true;
}
public abstract Task<StorageItemProperties> GetBasicPropertiesAsync();
protected string? GetColumnValue(Context context, AndroidUri contentUri, string column, string? selection = null, string[]? selectionArgs = null)
{
try
{
var projection = new[] { column };
using var cursor = context.ContentResolver!.Query(contentUri, projection, selection, selectionArgs, null);
if (cursor?.MoveToFirst() == true)
{
var columnIndex = cursor.GetColumnIndex(column);
if (columnIndex != -1)
return cursor.GetString(columnIndex);
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?.Log(this, "File metadata reader failed: '{Exception}'", ex);
}
return null;
}
public Task<IStorageFolder?> GetParentAsync()
{
using var javaFile = new JavaFile(Uri.Path!);
// Java file represents files AND directories. Don't be confused.
if (javaFile.ParentFile is {} parentFile
&& AndroidUri.FromFile(parentFile) is {} androidUri)
{
return Task.FromResult<IStorageFolder?>(new AndroidStorageFolder(Context, androidUri));
}
return Task.FromResult<IStorageFolder?>(null);
}
public void Dispose()
{
_context = null;
}
}
internal sealed class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
{
public AndroidStorageFolder(Context context, AndroidUri uri) : base(context, uri)
{
}
public override Task<StorageItemProperties> GetBasicPropertiesAsync()
{
return Task.FromResult(new StorageItemProperties());
}
}
internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile
{
public AndroidStorageFile(Context context, AndroidUri uri) : base(context, uri)
{
}
public bool CanOpenRead => true;
public bool CanOpenWrite => true;
public Task<Stream> OpenRead() => Task.FromResult(OpenContentStream(Context, Uri, false)
?? throw new InvalidOperationException("Failed to open content stream"));
public Task<Stream> OpenWrite() => Task.FromResult(OpenContentStream(Context, Uri, true)
?? throw new InvalidOperationException("Failed to open content stream"));
private Stream? OpenContentStream(Context context, AndroidUri uri, bool isOutput)
{
var isVirtual = IsVirtualFile(context, uri);
if (isVirtual)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?.Log(this, "Content URI was virtual: '{Uri}'", uri);
return GetVirtualFileStream(context, uri, isOutput);
}
return isOutput
? context.ContentResolver?.OpenOutputStream(uri)
: context.ContentResolver?.OpenInputStream(uri);
}
private bool IsVirtualFile(Context context, AndroidUri uri)
{
if (!DocumentsContract.IsDocumentUri(context, uri))
return false;
var value = GetColumnValue(context, uri, DocumentsContract.Document.ColumnFlags);
if (!string.IsNullOrEmpty(value) && int.TryParse(value, out var flagsInt))
{
var flags = (DocumentContractFlags)flagsInt;
return flags.HasFlag(DocumentContractFlags.VirtualDocument);
}
return false;
}
private Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput)
{
var mimeTypes = context.ContentResolver?.GetStreamTypes(uri, FilePickerFileTypes.All.MimeTypes![0]);
if (mimeTypes?.Length >= 1)
{
var mimeType = mimeTypes[0];
var asset = context.ContentResolver!
.OpenTypedAssetFileDescriptor(uri, mimeType, null);
var stream = isOutput
? asset?.CreateOutputStream()
: asset?.CreateInputStream();
return stream;
}
return null;
}
public override Task<StorageItemProperties> GetBasicPropertiesAsync()
{
ulong? size = null;
DateTimeOffset? itemDate = null;
DateTimeOffset? dateModified = null;
try
{
var projection = new[]
{
MediaStore.IMediaColumns.Size, MediaStore.IMediaColumns.DateAdded,
MediaStore.IMediaColumns.DateModified
};
using var cursor = Context.ContentResolver!.Query(Uri, projection, null, null, null);
if (cursor?.MoveToFirst() == true)
{
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.Size);
if (columnIndex != -1)
{
size = (ulong)cursor.GetLong(columnIndex);
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?
.Log(this, "File Size metadata reader failed: '{Exception}'", ex);
}
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.DateAdded);
if (columnIndex != -1)
{
var longValue = cursor.GetLong(columnIndex);
itemDate = longValue > 0 ? DateTimeOffset.FromUnixTimeMilliseconds(longValue) : null;
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?
.Log(this, "File DateAdded metadata reader failed: '{Exception}'", ex);
}
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.DateModified);
if (columnIndex != -1)
{
var longValue = cursor.GetLong(columnIndex);
dateModified = longValue > 0 ? DateTimeOffset.FromUnixTimeMilliseconds(longValue) : null;
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?
.Log(this, "File DateAdded metadata reader failed: '{Exception}'", ex);
}
}
}
catch (UnsupportedOperationException)
{
// It's not possible to get parameters of some files/folders.
}
return Task.FromResult(new StorageItemProperties(size, itemDate, dateModified));
}
}

177
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs

@ -0,0 +1,177 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Provider;
using Avalonia.Platform.Storage;
using AndroidUri = Android.Net.Uri;
namespace Avalonia.Android.Platform.Storage;
internal class AndroidStorageProvider : IStorageProvider
{
private readonly AvaloniaActivity _activity;
private int _lastRequestCode = 20000;
public AndroidStorageProvider(AvaloniaActivity activity)
{
_activity = activity;
}
public bool CanOpen => OperatingSystem.IsAndroidVersionAtLeast(19);
public bool CanSave => OperatingSystem.IsAndroidVersionAtLeast(19);
public bool CanPickFolder => OperatingSystem.IsAndroidVersionAtLeast(21);
public Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark)
{
var uri = AndroidUri.Parse(bookmark) ?? throw new ArgumentException("Couldn't parse Bookmark value", nameof(bookmark));
return Task.FromResult<IStorageBookmarkFolder?>(new AndroidStorageFolder(_activity, uri));
}
public Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark)
{
var uri = AndroidUri.Parse(bookmark) ?? throw new ArgumentException("Couldn't parse Bookmark value", nameof(bookmark));
return Task.FromResult<IStorageBookmarkFile?>(new AndroidStorageFile(_activity, uri));
}
public async Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
{
var mimeTypes = options.FileTypeFilter?.Where(t => t != FilePickerFileTypes.All)
.SelectMany(f => f.MimeTypes ?? Array.Empty<string>()).Distinct().ToArray() ?? Array.Empty<string>();
var intent = new Intent(Intent.ActionOpenDocument)
.AddCategory(Intent.CategoryOpenable)
.PutExtra(Intent.ExtraAllowMultiple, options.AllowMultiple)
.SetType(FilePickerFileTypes.All.MimeTypes![0]);
if (mimeTypes.Length > 0)
{
intent = intent.PutExtra(Intent.ExtraMimeTypes, mimeTypes);
}
if (TryGetInitialUri(options.SuggestedStartLocation) is { } initialUri)
{
intent = intent.PutExtra(DocumentsContract.ExtraInitialUri, initialUri);
}
var pickerIntent = Intent.CreateChooser(intent, options.Title ?? "Select file");
var uris = await StartActivity(pickerIntent, false);
return uris.Select(u => new AndroidStorageFile(_activity, u)).ToArray();
}
public async Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
{
var mimeTypes = options.FileTypeChoices?.Where(t => t != FilePickerFileTypes.All)
.SelectMany(f => f.MimeTypes ?? Array.Empty<string>()).Distinct().ToArray() ?? Array.Empty<string>();
var intent = new Intent(Intent.ActionCreateDocument)
.AddCategory(Intent.CategoryOpenable)
.SetType(FilePickerFileTypes.All.MimeTypes![0]);
if (mimeTypes.Length > 0)
{
intent = intent.PutExtra(Intent.ExtraMimeTypes, mimeTypes);
}
if (options.SuggestedFileName is { } fileName)
{
if (options.DefaultExtension is { } ext)
{
fileName += ext.StartsWith('.') ? ext : "." + ext;
}
intent = intent.PutExtra(Intent.ExtraTitle, fileName);
}
if (TryGetInitialUri(options.SuggestedStartLocation) is { } initialUri)
{
intent = intent.PutExtra(DocumentsContract.ExtraInitialUri, initialUri);
}
var pickerIntent = Intent.CreateChooser(intent, options.Title ?? "Save file");
var uris = await StartActivity(pickerIntent, true);
return uris.Select(u => new AndroidStorageFile(_activity, u)).FirstOrDefault();
}
public async Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
var intent = new Intent(Intent.ActionOpenDocumentTree)
.PutExtra(Intent.ExtraAllowMultiple, options.AllowMultiple);
if (TryGetInitialUri(options.SuggestedStartLocation) is { } initialUri)
{
intent = intent.PutExtra(DocumentsContract.ExtraInitialUri, initialUri);
}
var pickerIntent = Intent.CreateChooser(intent, options.Title ?? "Select folder");
var uris = await StartActivity(pickerIntent, false);
return uris.Select(u => new AndroidStorageFolder(_activity, u)).ToArray();
}
private async Task<List<AndroidUri>> StartActivity(Intent? pickerIntent, bool singleResult)
{
var resultList = new List<AndroidUri>(1);
var tcs = new TaskCompletionSource<Intent?>();
var currentRequestCode = _lastRequestCode++;
_activity.ActivityResult += OnActivityResult;
_activity.StartActivityForResult(pickerIntent, currentRequestCode);
var result = await tcs.Task;
if (result != null)
{
// ClipData first to avoid issue with multiple files selection.
if (!singleResult && result.ClipData is { } clipData)
{
for (var i = 0; i < clipData.ItemCount; i++)
{
var uri = clipData.GetItemAt(i)?.Uri;
if (uri != null)
{
resultList.Add(uri);
}
}
}
else if (result.Data is { } uri)
{
resultList.Add(uri);
}
}
if (result?.HasExtra("error") == true)
{
throw new Exception(result.GetStringExtra("error"));
}
return resultList;
void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (currentRequestCode != requestCode)
{
return;
}
_activity.ActivityResult -= OnActivityResult;
_ = tcs.TrySetResult(resultCode == Result.Ok ? data : null);
}
}
private static AndroidUri? TryGetInitialUri(IStorageFolder? folder)
{
if (OperatingSystem.IsAndroidVersionAtLeast(26)
&& (folder as AndroidStorageItem)?.Uri is { } uri)
{
return uri;
}
return null;
}
}

20
src/Android/Avalonia.Android/SystemDialogImpl.cs

@ -1,20 +0,0 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
namespace Avalonia.Android
{
internal class SystemDialogImpl : ISystemDialogImpl
{
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, Window parent)
{
throw new NotImplementedException();
}
public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent)
{
throw new NotImplementedException();
}
}
}

BIN
src/Avalonia.Base/Assets/BiDi.trie

Binary file not shown.

BIN
src/Avalonia.Base/Assets/GraphemeBreak.trie

Binary file not shown.

BIN
src/Avalonia.Base/Assets/UnicodeData.trie

Binary file not shown.

3
src/Avalonia.Base/AvaloniaObject.cs

@ -935,7 +935,8 @@ namespace Avalonia
public void Dispose() public void Dispose()
{ {
_subscription.Dispose(); // _subscription can be null, if Subscribe failed with an exception.
_subscription?.Dispose();
_owner._directBindings!.Remove(this); _owner._directBindings!.Remove(this);
} }

119
src/Avalonia.Base/Controls/ResourceDictionary.cs

@ -1,38 +1,45 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// An indexed dictionary of resources. /// An indexed dictionary of resources.
/// </summary> /// </summary>
public class ResourceDictionary : AvaloniaDictionary<object, object?>, IResourceDictionary public class ResourceDictionary : IResourceDictionary
{ {
private Dictionary<object, object?>? _inner;
private IResourceHost? _owner; private IResourceHost? _owner;
private AvaloniaList<IResourceProvider>? _mergedDictionaries; private AvaloniaList<IResourceProvider>? _mergedDictionaries;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class. /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
/// </summary> /// </summary>
public ResourceDictionary() public ResourceDictionary() { }
{
CollectionChanged += OnCollectionChanged;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class. /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
/// </summary> /// </summary>
public ResourceDictionary(IResourceHost owner) public ResourceDictionary(IResourceHost owner) => Owner = owner;
: this()
public int Count => _inner?.Count ?? 0;
public object? this[object key]
{ {
Owner = owner; get => _inner?[key];
set
{
Inner[key] = value;
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
} }
public ICollection<object> Keys => (ICollection<object>?)_inner?.Keys ?? Array.Empty<object>();
public ICollection<object?> Values => (ICollection<object?>?)_inner?.Values ?? Array.Empty<object?>();
public IResourceHost? Owner public IResourceHost? Owner
{ {
get => _owner; get => _owner;
@ -80,7 +87,7 @@ namespace Avalonia.Controls
{ {
get get
{ {
if (Count > 0) if (_inner?.Count > 0)
{ {
return true; return true;
} }
@ -100,11 +107,43 @@ namespace Avalonia.Controls
} }
} }
bool ICollection<KeyValuePair<object, object?>>.IsReadOnly => false;
private Dictionary<object, object?> Inner => _inner ??= new();
public event EventHandler? OwnerChanged; public event EventHandler? OwnerChanged;
public void Add(object key, object? value)
{
Inner.Add(key, value);
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
public void Clear()
{
if (_inner?.Count > 0)
{
_inner.Clear();
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
public bool ContainsKey(object key) => _inner?.ContainsKey(key) ?? false;
public bool Remove(object key)
{
if (_inner?.Remove(key) == true)
{
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
return true;
}
return false;
}
public bool TryGetResource(object key, out object? value) public bool TryGetResource(object key, out object? value)
{ {
if (TryGetValue(key, out value)) if (_inner is not null && _inner.TryGetValue(key, out value))
{ {
return true; return true;
} }
@ -120,9 +159,52 @@ namespace Avalonia.Controls
} }
} }
value = null;
return false; return false;
} }
public bool TryGetValue(object key, out object? value)
{
if (_inner is not null)
return _inner.TryGetValue(key, out value);
value = null;
return false;
}
void ICollection<KeyValuePair<object, object?>>.Add(KeyValuePair<object, object?> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<object, object?>>.Contains(KeyValuePair<object, object?> item)
{
return (_inner as ICollection<KeyValuePair<object, object?>>)?.Contains(item) ?? false;
}
void ICollection<KeyValuePair<object, object?>>.CopyTo(KeyValuePair<object, object?>[] array, int arrayIndex)
{
(_inner as ICollection<KeyValuePair<object, object?>>)?.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<object, object?>>.Remove(KeyValuePair<object, object?> item)
{
if ((_inner as ICollection<KeyValuePair<object, object?>>)?.Remove(item) == true)
{
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
return true;
}
return false;
}
public IEnumerator<KeyValuePair<object, object?>> GetEnumerator()
{
return _inner?.GetEnumerator() ?? Enumerable.Empty<KeyValuePair<object, object?>>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
void IResourceProvider.AddOwner(IResourceHost owner) void IResourceProvider.AddOwner(IResourceHost owner)
{ {
owner = owner ?? throw new ArgumentNullException(nameof(owner)); owner = owner ?? throw new ArgumentNullException(nameof(owner));
@ -134,7 +216,7 @@ namespace Avalonia.Controls
Owner = owner; Owner = owner;
var hasResources = Count > 0; var hasResources = _inner?.Count > 0;
if (_mergedDictionaries is object) if (_mergedDictionaries is object)
{ {
@ -159,7 +241,7 @@ namespace Avalonia.Controls
{ {
Owner = null; Owner = null;
var hasResources = Count > 0; var hasResources = _inner?.Count > 0;
if (_mergedDictionaries is object) if (_mergedDictionaries is object)
{ {
@ -176,10 +258,5 @@ namespace Avalonia.Controls
} }
} }
} }
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
} }
} }

37
src/Avalonia.Base/GeometryCollection.cs

@ -1,37 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Avalonia.Animation;
#nullable enable
namespace Avalonia.Media
{
public class GeometryCollection : Animatable, IList<Geometry>, IReadOnlyList<Geometry>
{
private List<Geometry> _inner;
public GeometryCollection() => _inner = new List<Geometry>();
public GeometryCollection(IEnumerable<Geometry> collection) => _inner = new List<Geometry>(collection);
public GeometryCollection(int capacity) => _inner = new List<Geometry>(capacity);
public Geometry this[int index]
{
get => _inner[index];
set => _inner[index] = value;
}
public int Count => _inner.Count;
public bool IsReadOnly => false;
public void Add(Geometry item) => _inner.Add(item);
public void Clear() => _inner.Clear();
public bool Contains(Geometry item) => _inner.Contains(item);
public void CopyTo(Geometry[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex);
public IEnumerator<Geometry> GetEnumerator() => _inner.GetEnumerator();
public int IndexOf(Geometry item) => _inner.IndexOf(item);
public void Insert(int index, Geometry item) => _inner.Insert(index, item);
public bool Remove(Geometry item) => _inner.Remove(item);
public void RemoveAt(int index) => _inner.RemoveAt(index);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

5
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -66,7 +66,8 @@ namespace Avalonia.Input.GestureRecognizers
public void PointerPressed(PointerPressedEventArgs e) public void PointerPressed(PointerPressedEventArgs e)
{ {
if (e.Pointer.IsPrimary && e.Pointer.Type == PointerType.Touch) if (e.Pointer.IsPrimary &&
(e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen))
{ {
EndGesture(); EndGesture();
_tracking = e.Pointer; _tracking = e.Pointer;
@ -101,7 +102,7 @@ namespace Avalonia.Input.GestureRecognizers
if (_scrolling) if (_scrolling)
{ {
var vector = _trackedRootPoint - rootPoint; var vector = _trackedRootPoint - rootPoint;
var elapsed = _lastMoveTimestamp.HasValue ? var elapsed = _lastMoveTimestamp.HasValue && _lastMoveTimestamp < e.Timestamp ?
TimeSpan.FromMilliseconds(e.Timestamp - _lastMoveTimestamp.Value) : TimeSpan.FromMilliseconds(e.Timestamp - _lastMoveTimestamp.Value) :
TimeSpan.Zero; TimeSpan.Zero;

4
src/Avalonia.Base/Input/IInputElement.cs

@ -42,12 +42,12 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Occurs when the pointer enters the control. /// Occurs when the pointer enters the control.
/// </summary> /// </summary>
event EventHandler<PointerEventArgs>? PointerEnter; event EventHandler<PointerEventArgs>? PointerEntered;
/// <summary> /// <summary>
/// Occurs when the pointer leaves the control. /// Occurs when the pointer leaves the control.
/// </summary> /// </summary>
event EventHandler<PointerEventArgs>? PointerLeave; event EventHandler<PointerEventArgs>? PointerExited;
/// <summary> /// <summary>
/// Occurs when the pointer is pressed over the control. /// Occurs when the pointer is pressed over the control.

7
src/Avalonia.Base/Input/IKeyboardDevice.cs

@ -43,12 +43,17 @@ namespace Avalonia.Input
Control = 2, Control = 2,
Shift = 4, Shift = 4,
Meta = 8, Meta = 8,
LeftMouseButton = 16, LeftMouseButton = 16,
RightMouseButton = 32, RightMouseButton = 32,
MiddleMouseButton = 64, MiddleMouseButton = 64,
XButton1MouseButton = 128, XButton1MouseButton = 128,
XButton2MouseButton = 256, XButton2MouseButton = 256,
KeyboardMask = Alt | Control | Shift | Meta KeyboardMask = Alt | Control | Shift | Meta,
PenInverted = 512,
PenEraser = 1024,
PenBarrelButton = 2048
} }
[NotClientImplementable] [NotClientImplementable]

10
src/Avalonia.Base/Input/IPenDevice.cs

@ -0,0 +1,10 @@
namespace Avalonia.Input
{
/// <summary>
/// Represents a pen/stylus device.
/// </summary>
public interface IPenDevice : IPointerDevice
{
}
}

43
src/Avalonia.Base/Input/IPointer.cs

@ -2,20 +2,59 @@ using Avalonia.Metadata;
namespace Avalonia.Input namespace Avalonia.Input
{ {
/// <summary>
/// Identifies specific pointer generated by input device.
/// </summary>
/// <remarks>
/// Some devices, for instance, touchscreen might generate a pointer on each physical contact.
/// </remarks>
[NotClientImplementable] [NotClientImplementable]
public interface IPointer public interface IPointer
{ {
/// <summary>
/// Gets a unique identifier for the input pointer.
/// </summary>
int Id { get; } int Id { get; }
/// <summary>
/// Captures pointer input to the specified control.
/// </summary>
/// <param name="control">The control.</param>
/// <remarks>
/// When an element captures the pointer, it receives pointer input whether the cursor is
/// within the control's bounds or not. The current pointer capture control is exposed
/// by the <see cref="Captured"/> property.
/// </remarks>
void Capture(IInputElement? control); void Capture(IInputElement? control);
/// <summary>
/// Gets the control that is currently capturing by the pointer, if any.
/// </summary>
/// <remarks>
/// When an element captures the pointer, it receives pointer input whether the cursor is
/// within the control's bounds or not. To set the pointer capture, call the
/// <see cref="Capture"/> method.
/// </remarks>
IInputElement? Captured { get; } IInputElement? Captured { get; }
/// <summary>
/// Gets the pointer device type.
/// </summary>
PointerType Type { get; } PointerType Type { get; }
/// <summary>
/// Gets a value that indicates whether the input is from the primary pointer when multiple pointers are registered.
/// </summary>
bool IsPrimary { get; } bool IsPrimary { get; }
} }
/// <summary>
/// Enumerates pointer device types.
/// </summary>
public enum PointerType public enum PointerType
{ {
Mouse, Mouse,
Touch Touch,
Pen
} }
} }

58
src/Avalonia.Base/Input/InputElement.cs

@ -128,16 +128,20 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel | RoutingStrategies.Bubble); RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Defines the <see cref="PointerEnter"/> event. /// Defines the <see cref="PointerEntered"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent = public static readonly RoutedEvent<PointerEventArgs> PointerEnteredEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct); RoutedEvent.Register<InputElement, PointerEventArgs>(
nameof(PointerEntered),
RoutingStrategies.Direct);
/// <summary> /// <summary>
/// Defines the <see cref="PointerLeave"/> event. /// Defines the <see cref="PointerExited"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent = public static readonly RoutedEvent<PointerEventArgs> PointerExitedEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct); RoutedEvent.Register<InputElement, PointerEventArgs>(
nameof(PointerExited),
RoutingStrategies.Direct);
/// <summary> /// <summary>
/// Defines the <see cref="PointerMoved"/> event. /// Defines the <see cref="PointerMoved"/> event.
@ -208,8 +212,8 @@ namespace Avalonia.Input
KeyDownEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyDown(e)); KeyDownEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyDown(e));
KeyUpEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyUp(e)); KeyUpEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyUp(e));
TextInputEvent.AddClassHandler<InputElement>((x, e) => x.OnTextInput(e)); TextInputEvent.AddClassHandler<InputElement>((x, e) => x.OnTextInput(e));
PointerEnterEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnterCore(e)); PointerEnteredEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnteredCore(e));
PointerLeaveEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerLeaveCore(e)); PointerExitedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerExitedCore(e));
PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e)); PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e));
PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e)); PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e));
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e)); PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
@ -279,19 +283,19 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Occurs when the pointer enters the control. /// Occurs when the pointer enters the control.
/// </summary> /// </summary>
public event EventHandler<PointerEventArgs>? PointerEnter public event EventHandler<PointerEventArgs>? PointerEntered
{ {
add { AddHandler(PointerEnterEvent, value); } add { AddHandler(PointerEnteredEvent, value); }
remove { RemoveHandler(PointerEnterEvent, value); } remove { RemoveHandler(PointerEnteredEvent, value); }
} }
/// <summary> /// <summary>
/// Occurs when the pointer leaves the control. /// Occurs when the pointer leaves the control.
/// </summary> /// </summary>
public event EventHandler<PointerEventArgs>? PointerLeave public event EventHandler<PointerEventArgs>? PointerExited
{ {
add { AddHandler(PointerLeaveEvent, value); } add { AddHandler(PointerExitedEvent, value); }
remove { RemoveHandler(PointerLeaveEvent, value); } remove { RemoveHandler(PointerExitedEvent, value); }
} }
/// <summary> /// <summary>
@ -539,18 +543,18 @@ namespace Avalonia.Input
} }
/// <summary> /// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs. /// Called before the <see cref="PointerEntered"/> event occurs.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnPointerEnter(PointerEventArgs e) protected virtual void OnPointerEntered(PointerEventArgs e)
{ {
} }
/// <summary> /// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs. /// Called before the <see cref="PointerExited"/> event occurs.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnPointerLeave(PointerEventArgs e) protected virtual void OnPointerExited(PointerEventArgs e)
{ {
} }
@ -561,7 +565,9 @@ namespace Avalonia.Input
protected virtual void OnPointerMoved(PointerEventArgs e) protected virtual void OnPointerMoved(PointerEventArgs e)
{ {
if (_gestureRecognizers?.HandlePointerMoved(e) == true) if (_gestureRecognizers?.HandlePointerMoved(e) == true)
{
e.Handled = true; e.Handled = true;
}
} }
/// <summary> /// <summary>
@ -571,7 +577,9 @@ namespace Avalonia.Input
protected virtual void OnPointerPressed(PointerPressedEventArgs e) protected virtual void OnPointerPressed(PointerPressedEventArgs e)
{ {
if (_gestureRecognizers?.HandlePointerPressed(e) == true) if (_gestureRecognizers?.HandlePointerPressed(e) == true)
{
e.Handled = true; e.Handled = true;
}
} }
/// <summary> /// <summary>
@ -581,7 +589,9 @@ namespace Avalonia.Input
protected virtual void OnPointerReleased(PointerReleasedEventArgs e) protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
{ {
if (_gestureRecognizers?.HandlePointerReleased(e) == true) if (_gestureRecognizers?.HandlePointerReleased(e) == true)
{
e.Handled = true; e.Handled = true;
}
} }
/// <summary> /// <summary>
@ -634,23 +644,23 @@ namespace Avalonia.Input
} }
/// <summary> /// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs. /// Called before the <see cref="PointerEntered"/> event occurs.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void OnPointerEnterCore(PointerEventArgs e) private void OnPointerEnteredCore(PointerEventArgs e)
{ {
IsPointerOver = true; IsPointerOver = true;
OnPointerEnter(e); OnPointerEntered(e);
} }
/// <summary> /// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs. /// Called before the <see cref="PointerExited"/> event occurs.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void OnPointerLeaveCore(PointerEventArgs e) private void OnPointerExitedCore(PointerEventArgs e)
{ {
IsPointerOver = false; IsPointerOver = false;
OnPointerLeave(e); OnPointerExited(e);
} }
/// <summary> /// <summary>

174
src/Avalonia.Base/Input/PenDevice.cs

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Input
{
/// <summary>
/// Represents a pen/stylus device.
/// </summary>
public class PenDevice : IPenDevice, IDisposable
{
private readonly Dictionary<long, Pointer> _pointers = new();
private readonly Dictionary<long, PixelPoint> _lastPositions = new();
private int _clickCount;
private Rect _lastClickRect;
private ulong _lastClickTime;
private MouseButton _lastMouseDownButton;
private bool _disposed;
public void ProcessRawEvent(RawInputEventArgs e)
{
if (!e.Handled && e is RawPointerEventArgs margs)
ProcessRawEvent(margs);
}
private void ProcessRawEvent(RawPointerEventArgs e)
{
e = e ?? throw new ArgumentNullException(nameof(e));
if (!_pointers.TryGetValue(e.RawPointerId, out var pointer))
{
if (e.Type == RawPointerEventType.LeftButtonUp
|| e.Type == RawPointerEventType.TouchEnd)
return;
_pointers[e.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(),
PointerType.Pen, _pointers.Count == 0);
}
_lastPositions[e.RawPointerId] = e.Root.PointToScreen(e.Position);
var props = new PointerPointProperties(e.InputModifiers, e.Type.ToUpdateKind(),
e.Point.Twist, e.Point.Pressure, e.Point.XTilt, e.Point.YTilt);
var keyModifiers = e.InputModifiers.ToKeyModifiers();
bool shouldReleasePointer = false;
switch (e.Type)
{
case RawPointerEventType.LeaveWindow:
shouldReleasePointer = true;
break;
case RawPointerEventType.LeftButtonDown:
e.Handled = PenDown(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult);
break;
case RawPointerEventType.LeftButtonUp:
e.Handled = PenUp(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult);
break;
case RawPointerEventType.Move:
e.Handled = PenMove(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult, e.IntermediatePoints);
break;
}
if (shouldReleasePointer)
{
pointer.Dispose();
_pointers.Remove(e.RawPointerId);
_lastPositions.Remove(e.RawPointerId);
}
}
private bool PenDown(Pointer pointer, ulong timestamp,
IInputElement root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers, IInputElement? hitTest)
{
var source = pointer.Captured ?? hitTest;
if (source != null)
{
pointer.Capture(source);
var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
var doubleClickTime = settings?.DoubleClickTime.TotalMilliseconds ?? 500;
var doubleClickSize = settings?.DoubleClickSize ?? new Size(4, 4);
if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime)
{
_clickCount = 0;
}
++_clickCount;
_lastClickTime = timestamp;
_lastClickRect = new Rect(p, new Size())
.Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2));
_lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton();
var e = new PointerPressedEventArgs(source, pointer, root, p, timestamp, properties, inputModifiers, _clickCount);
source.RaiseEvent(e);
return e.Handled;
}
return false;
}
private bool PenMove(Pointer pointer, ulong timestamp,
IInputRoot root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers, IInputElement? hitTest,
Lazy<IReadOnlyList<RawPointerPoint>?>? intermediatePoints)
{
var source = pointer.Captured ?? hitTest;
if (source is not null)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, root,
p, timestamp, properties, inputModifiers, intermediatePoints);
source.RaiseEvent(e);
return e.Handled;
}
return false;
}
private bool PenUp(Pointer pointer, ulong timestamp,
IInputElement root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers, IInputElement? hitTest)
{
var source = pointer.Captured ?? hitTest;
if (source is not null)
{
var e = new PointerReleasedEventArgs(source, pointer, root, p, timestamp, properties, inputModifiers,
_lastMouseDownButton);
source?.RaiseEvent(e);
pointer.Capture(null);
return e.Handled;
}
return false;
}
public void Dispose()
{
if (_disposed)
return;
var values = _pointers.Values.ToList();
_pointers.Clear();
_disposed = true;
foreach (var p in values)
p.Dispose();
}
[Obsolete]
IInputElement? IPointerDevice.Captured => _pointers.Values
.FirstOrDefault(p => p.IsPrimary)?.Captured;
[Obsolete]
void IPointerDevice.Capture(IInputElement? control) => _pointers.Values
.FirstOrDefault(p => p.IsPrimary)?.Capture(control);
[Obsolete]
Point IPointerDevice.GetPosition(IVisual relativeTo) => new Point(-1, -1);
public IPointer? TryGetPointer(RawPointerEventArgs ev)
{
return _pointers.TryGetValue(ev.RawPointerId, out var pointer)
? pointer
: null;
}
}
}

22
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -67,7 +67,14 @@ namespace Avalonia.Input
public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer; public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer;
} }
/// <summary>
/// Gets specific pointer generated by input device.
/// </summary>
public IPointer Pointer { get; } public IPointer Pointer { get; }
/// <summary>
/// Gets the time when the input occurred.
/// </summary>
public ulong Timestamp { get; } public ulong Timestamp { get; }
private IPointerDevice? _device; private IPointerDevice? _device;
@ -91,7 +98,10 @@ namespace Avalonia.Input
return mods; return mods;
} }
} }
/// <summary>
/// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated.
/// </summary>
public KeyModifiers KeyModifiers { get; } public KeyModifiers KeyModifiers { get; }
private Point GetPosition(Point pt, IVisual? relativeTo) private Point GetPosition(Point pt, IVisual? relativeTo)
@ -102,7 +112,12 @@ namespace Avalonia.Input
return pt; return pt;
return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; return pt * _rootVisual.TransformToVisual(relativeTo) ?? default;
} }
/// <summary>
/// Gets the pointer position relative to a control.
/// </summary>
/// <param name="relativeTo">The control.</param>
/// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
[Obsolete("Use GetCurrentPoint")] [Obsolete("Use GetCurrentPoint")]
@ -130,7 +145,8 @@ namespace Avalonia.Input
for (var c = 0; c < previousPoints.Count; c++) for (var c = 0; c < previousPoints.Count; c++)
{ {
var pt = previousPoints[c]; var pt = previousPoints[c];
points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), _properties); var pointProperties = new PointerPointProperties(_properties, pt);
points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), pointProperties);
} }
points[points.Length - 1] = GetCurrentPoint(relativeTo); points[points.Length - 1] = GetCurrentPoint(relativeTo);

6
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -97,7 +97,7 @@ namespace Avalonia.Input
// Do not pass rootVisual, when we have unknown (negative) position, // Do not pass rootVisual, when we have unknown (negative) position,
// so GetPosition won't return invalid values. // so GetPosition won't return invalid values.
var hasPosition = position.X >= 0 && position.Y >= 0; var hasPosition = position.X >= 0 && position.Y >= 0;
var e = new PointerEventArgs(InputElement.PointerLeaveEvent, element, pointer, var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer,
hasPosition ? root : null, hasPosition ? position : default, hasPosition ? root : null, hasPosition ? position : default,
timestamp, properties, inputModifiers); timestamp, properties, inputModifiers);
@ -177,7 +177,7 @@ namespace Avalonia.Input
el = root.PointerOverElement; el = root.PointerOverElement;
var e = new PointerEventArgs(InputElement.PointerLeaveEvent, el, pointer, root, position, var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, root, position,
timestamp, properties, inputModifiers); timestamp, properties, inputModifiers);
if (el != null && branch != null && !el.IsAttachedToVisualTree) if (el != null && branch != null && !el.IsAttachedToVisualTree)
{ {
@ -195,7 +195,7 @@ namespace Avalonia.Input
el = root.PointerOverElement = element; el = root.PointerOverElement = element;
_lastPointer = (pointer, root.PointToScreen(position)); _lastPointer = (pointer, root.PointToScreen(position));
e.RoutedEvent = InputElement.PointerEnterEvent; e.RoutedEvent = InputElement.PointerEnteredEvent;
while (el != null && el != branch) while (el != null && el != branch)
{ {

125
src/Avalonia.Base/Input/PointerPoint.cs

@ -1,5 +1,10 @@
using Avalonia.Input.Raw;
namespace Avalonia.Input namespace Avalonia.Input
{ {
/// <summary>
/// Provides basic properties for the input pointer associated with a single mouse, pen/stylus, or touch contact.
/// </summary>
public sealed class PointerPoint public sealed class PointerPoint
{ {
public PointerPoint(IPointer pointer, Point position, PointerPointProperties properties) public PointerPoint(IPointer pointer, Point position, PointerPointProperties properties)
@ -8,25 +13,109 @@ namespace Avalonia.Input
Position = position; Position = position;
Properties = properties; Properties = properties;
} }
/// <summary>
/// Gets specific pointer generated by input device.
/// </summary>
public IPointer Pointer { get; } public IPointer Pointer { get; }
/// <summary>
/// Gets extended information about the input pointer.
/// </summary>
public PointerPointProperties Properties { get; } public PointerPointProperties Properties { get; }
/// <summary>
/// Gets the location of the pointer input in client coordinates.
/// </summary>
public Point Position { get; } public Point Position { get; }
} }
/// <summary>
/// Provides extended properties for a PointerPoint object.
/// </summary>
public sealed class PointerPointProperties public sealed class PointerPointProperties
{ {
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the primary action mode of an input device.
/// </summary>
public bool IsLeftButtonPressed { get; } public bool IsLeftButtonPressed { get; }
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the tertiary action mode of an input device.
/// </summary>
public bool IsMiddleButtonPressed { get; } public bool IsMiddleButtonPressed { get; }
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the secondary action mode (if supported) of an input device.
/// </summary>
public bool IsRightButtonPressed { get; } public bool IsRightButtonPressed { get; }
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the first extended mouse button (XButton1).
/// </summary>
public bool IsXButton1Pressed { get; } public bool IsXButton1Pressed { get; }
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the second extended mouse button (XButton2).
/// </summary>
public bool IsXButton2Pressed { get; } public bool IsXButton2Pressed { get; }
/// <summary>
/// Gets a value that indicates whether the barrel button of the pen/stylus device is pressed.
/// </summary>
public bool IsBarrelButtonPressed { get; }
/// <summary>
/// Gets a value that indicates whether the input is from a pen eraser.
/// </summary>
public bool IsEraser { get; }
/// <summary>
/// Gets a value that indicates whether the digitizer pen is inverted.
/// </summary>
public bool IsInverted { get; }
/// <summary>
/// Gets the clockwise rotation in degrees of a pen device around its own major axis (such as when the user spins the pen in their fingers).
/// </summary>
/// <returns>
/// A value between 0.0 and 359.0 in degrees of rotation. The default value is 0.0.
/// </returns>
public float Twist { get; }
/// <summary>
/// Gets a value that indicates the force that the pointer device (typically a pen/stylus) exerts on the surface of the digitizer.
/// </summary>
/// <returns>
/// A value from 0 to 1.0. The default value is 0.5.
/// </returns>
public float Pressure { get; } = 0.5f;
/// <summary>
/// Gets the plane angle between the Y-Z plane and the plane that contains the Y axis and the axis of the input device (typically a pen/stylus).
/// </summary>
/// <returns>
/// The value is 0.0 when the finger or pen is perpendicular to the digitizer surface, between 0.0 and 90.0 when tilted to the right of perpendicular, and between 0.0 and -90.0 when tilted to the left of perpendicular. The default value is 0.0.
/// </returns>
public float XTilt { get; }
/// <summary>
/// Gets the plane angle between the X-Z plane and the plane that contains the X axis and the axis of the input device (typically a pen/stylus).
/// </summary>
/// <returns>
/// The value is 0.0 when the finger or pen is perpendicular to the digitizer surface, between 0.0 and 90.0 when tilted towards the user, and between 0.0 and -90.0 when tilted away from the user. The default value is 0.0.
/// </returns>
public float YTilt { get; }
/// <summary>
/// Gets the kind of pointer state change.
/// </summary>
public PointerUpdateKind PointerUpdateKind { get; } public PointerUpdateKind PointerUpdateKind { get; }
private PointerPointProperties() private PointerPointProperties()
{ {
} }
public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind) public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind)
{ {
PointerUpdateKind = kind; PointerUpdateKind = kind;
@ -36,10 +125,13 @@ namespace Avalonia.Input
IsRightButtonPressed = modifiers.HasAllFlags(RawInputModifiers.RightMouseButton); IsRightButtonPressed = modifiers.HasAllFlags(RawInputModifiers.RightMouseButton);
IsXButton1Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton1MouseButton); IsXButton1Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton1MouseButton);
IsXButton2Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton2MouseButton); IsXButton2Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton2MouseButton);
IsInverted = modifiers.HasAllFlags(RawInputModifiers.PenInverted);
IsEraser = modifiers.HasAllFlags(RawInputModifiers.PenEraser);
IsBarrelButtonPressed = modifiers.HasAllFlags(RawInputModifiers.PenBarrelButton);
// The underlying input source might be reporting the previous state, // The underlying input source might be reporting the previous state,
// so make sure that we reflect the current state // so make sure that we reflect the current state
if (kind == PointerUpdateKind.LeftButtonPressed) if (kind == PointerUpdateKind.LeftButtonPressed)
IsLeftButtonPressed = true; IsLeftButtonPressed = true;
if (kind == PointerUpdateKind.LeftButtonReleased) if (kind == PointerUpdateKind.LeftButtonReleased)
@ -62,6 +154,33 @@ namespace Avalonia.Input
IsXButton2Pressed = false; IsXButton2Pressed = false;
} }
public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind,
float twist, float pressure, float xTilt, float yTilt
) : this (modifiers, kind)
{
Twist = twist;
Pressure = pressure;
XTilt = xTilt;
YTilt = yTilt;
}
internal PointerPointProperties(PointerPointProperties basedOn, RawPointerPoint rawPoint)
{
IsLeftButtonPressed = basedOn.IsLeftButtonPressed;
IsMiddleButtonPressed = basedOn.IsMiddleButtonPressed;
IsRightButtonPressed = basedOn.IsRightButtonPressed;
IsXButton1Pressed = basedOn.IsXButton1Pressed;
IsXButton2Pressed = basedOn.IsXButton2Pressed;
IsInverted = basedOn.IsInverted;
IsEraser = basedOn.IsEraser;
IsBarrelButtonPressed = basedOn.IsBarrelButtonPressed;
Twist = rawPoint.Twist;
Pressure = rawPoint.Pressure;
XTilt = rawPoint.XTilt;
YTilt = rawPoint.YTilt;
}
public static PointerPointProperties None { get; } = new PointerPointProperties(); public static PointerPointProperties None { get; } = new PointerPointProperties();
} }

19
src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs

@ -56,11 +56,12 @@ namespace Avalonia.Input.Raw
Contract.Requires<ArgumentNullException>(device != null); Contract.Requires<ArgumentNullException>(device != null);
Contract.Requires<ArgumentNullException>(root != null); Contract.Requires<ArgumentNullException>(root != null);
Point = new RawPointerPoint();
Position = position; Position = position;
Type = type; Type = type;
InputModifiers = inputModifiers; InputModifiers = inputModifiers;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RawPointerEventArgs"/> class. /// Initializes a new instance of the <see cref="RawPointerEventArgs"/> class.
/// </summary> /// </summary>
@ -87,6 +88,11 @@ namespace Avalonia.Input.Raw
InputModifiers = inputModifiers; InputModifiers = inputModifiers;
} }
/// <summary>
/// Gets the raw pointer identifier.
/// </summary>
public long RawPointerId { get; set; }
/// <summary> /// <summary>
/// Gets the pointer properties and position, in client DIPs. /// Gets the pointer properties and position, in client DIPs.
/// </summary> /// </summary>
@ -130,10 +136,17 @@ namespace Avalonia.Input.Raw
/// Pointer position, in client DIPs. /// Pointer position, in client DIPs.
/// </summary> /// </summary>
public Point Position { get; set; } public Point Position { get; set; }
public float Twist { get; set; }
public float Pressure { get; set; }
public float XTilt { get; set; }
public float YTilt { get; set; }
public RawPointerPoint() public RawPointerPoint()
{ {
Position = default; this = default;
Pressure = 0.5f;
} }
} }
} }

17
src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs

@ -1,15 +1,26 @@
using System;
namespace Avalonia.Input.Raw namespace Avalonia.Input.Raw
{ {
public class RawTouchEventArgs : RawPointerEventArgs public class RawTouchEventArgs : RawPointerEventArgs
{ {
public RawTouchEventArgs(IInputDevice device, ulong timestamp, IInputRoot root, public RawTouchEventArgs(IInputDevice device, ulong timestamp, IInputRoot root,
RawPointerEventType type, Point position, RawInputModifiers inputModifiers, RawPointerEventType type, Point position, RawInputModifiers inputModifiers,
long touchPointId) long rawPointerId)
: base(device, timestamp, root, type, position, inputModifiers) : base(device, timestamp, root, type, position, inputModifiers)
{ {
TouchPointId = touchPointId; RawPointerId = rawPointerId;
}
public RawTouchEventArgs(IInputDevice device, ulong timestamp, IInputRoot root,
RawPointerEventType type, RawPointerPoint point, RawInputModifiers inputModifiers,
long rawPointerId)
: base(device, timestamp, root, type, point, inputModifiers)
{
RawPointerId = rawPointerId;
} }
public long TouchPointId { get; set; } [Obsolete("Use RawPointerId")]
public long TouchPointId { get => RawPointerId; set => RawPointerId = value; }
} }
} }

13
src/Avalonia.Base/Input/TouchDevice.cs

@ -40,14 +40,14 @@ namespace Avalonia.Input
{ {
if (ev.Handled || _disposed) if (ev.Handled || _disposed)
return; return;
var args = (RawTouchEventArgs)ev; var args = (RawPointerEventArgs)ev;
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer)) if (!_pointers.TryGetValue(args.RawPointerId, out var pointer))
{ {
if (args.Type == RawPointerEventType.TouchEnd) if (args.Type == RawPointerEventType.TouchEnd)
return; return;
var hit = args.InputHitTestResult; var hit = args.InputHitTestResult;
_pointers[args.TouchPointId] = pointer = new Pointer(Pointer.GetNextFreeId(), _pointers[args.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(),
PointerType.Touch, _pointers.Count == 0); PointerType.Touch, _pointers.Count == 0);
pointer.Capture(hit); pointer.Capture(hit);
} }
@ -88,7 +88,7 @@ namespace Avalonia.Input
if (args.Type == RawPointerEventType.TouchEnd) if (args.Type == RawPointerEventType.TouchEnd)
{ {
_pointers.Remove(args.TouchPointId); _pointers.Remove(args.RawPointerId);
using (pointer) using (pointer)
{ {
target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, target.RaiseEvent(new PointerReleasedEventArgs(target, pointer,
@ -101,7 +101,7 @@ namespace Avalonia.Input
if (args.Type == RawPointerEventType.TouchCancel) if (args.Type == RawPointerEventType.TouchCancel)
{ {
_pointers.Remove(args.TouchPointId); _pointers.Remove(args.RawPointerId);
using (pointer) using (pointer)
pointer.Capture(null); pointer.Capture(null);
_lastPointer = null; _lastPointer = null;
@ -129,8 +129,7 @@ namespace Avalonia.Input
public IPointer? TryGetPointer(RawPointerEventArgs ev) public IPointer? TryGetPointer(RawPointerEventArgs ev)
{ {
return ev is RawTouchEventArgs args return _pointers.TryGetValue(ev.RawPointerId, out var pointer)
&& _pointers.TryGetValue(args.TouchPointId, out var pointer)
? pointer ? pointer
: null; : null;
} }

71
src/Avalonia.Base/Layout/LayoutHelper.cs

@ -36,11 +36,28 @@ namespace Avalonia.Layout
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding, public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding,
Thickness borderThickness) Thickness borderThickness)
{ {
return MeasureChild(control, availableSize, padding + borderThickness); if (IsParentLayoutRounded(control, out double scale))
{
padding = RoundLayoutThickness(padding, scale, scale);
borderThickness = RoundLayoutThickness(borderThickness, scale, scale);
}
if (control != null)
{
control.Measure(availableSize.Deflate(padding + borderThickness));
return control.DesiredSize.Inflate(padding + borderThickness);
}
return new Size().Inflate(padding + borderThickness);
} }
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding) public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding)
{ {
if (IsParentLayoutRounded(control, out double scale))
{
padding = RoundLayoutThickness(padding, scale, scale);
}
if (control != null) if (control != null)
{ {
control.Measure(availableSize.Deflate(padding)); control.Measure(availableSize.Deflate(padding));
@ -137,7 +154,7 @@ namespace Avalonia.Layout
/// <summary> /// <summary>
/// Rounds a size to integer values for layout purposes, compensating for high DPI screen /// Rounds a size to integer values for layout purposes, compensating for high DPI screen
/// coordinates. /// coordinates by rounding the size up to the nearest pixel.
/// </summary> /// </summary>
/// <param name="size">Input size.</param> /// <param name="size">Input size.</param>
/// <param name="dpiScaleX">DPI along x-dimension.</param> /// <param name="dpiScaleX">DPI along x-dimension.</param>
@ -149,9 +166,9 @@ namespace Avalonia.Layout
/// associated with the UseLayoutRounding property and should not be used as a general rounding /// associated with the UseLayoutRounding property and should not be used as a general rounding
/// utility. /// utility.
/// </remarks> /// </remarks>
public static Size RoundLayoutSize(Size size, double dpiScaleX, double dpiScaleY) public static Size RoundLayoutSizeUp(Size size, double dpiScaleX, double dpiScaleY)
{ {
return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY)); return new Size(RoundLayoutValueUp(size.Width, dpiScaleX), RoundLayoutValueUp(size.Height, dpiScaleY));
} }
/// <summary> /// <summary>
@ -178,10 +195,9 @@ namespace Avalonia.Layout
); );
} }
/// <summary> /// <summary>
/// Calculates the value to be used for layout rounding at high DPI. /// Calculates the value to be used for layout rounding at high DPI by rounding the value
/// up or down to the nearest pixel.
/// </summary> /// </summary>
/// <param name="value">Input value to be rounded.</param> /// <param name="value">Input value to be rounded.</param>
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param> /// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
@ -217,7 +233,46 @@ namespace Avalonia.Layout
return newValue; return newValue;
} }
/// <summary>
/// Calculates the value to be used for layout rounding at high DPI by rounding the value up
/// to the nearest pixel.
/// </summary>
/// <param name="value">Input value to be rounded.</param>
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
/// <returns>Adjusted value that will produce layout rounding on screen at high dpi.</returns>
/// <remarks>
/// This is a layout helper method. It takes DPI into account and also does not return
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper
/// associated with the UseLayoutRounding property and should not be used as a general rounding
/// utility.
/// </remarks>
public static double RoundLayoutValueUp(double value, double dpiScale)
{
double newValue;
// If DPI == 1, don't use DPI-aware rounding.
if (!MathUtilities.IsOne(dpiScale))
{
newValue = Math.Ceiling(value * dpiScale) / dpiScale;
// If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue),
// use the original value.
if (double.IsNaN(newValue) ||
double.IsInfinity(newValue) ||
MathUtilities.AreClose(newValue, double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Ceiling(value);
}
return newValue;
}
/// <summary> /// <summary>
/// Calculates the min and max height for a control. Ported from WPF. /// Calculates the min and max height for a control. Ported from WPF.
/// </summary> /// </summary>

14
src/Avalonia.Base/Layout/LayoutManager.cs

@ -28,7 +28,7 @@ namespace Avalonia.Layout
public LayoutManager(ILayoutRoot owner) public LayoutManager(ILayoutRoot owner)
{ {
_owner = owner ?? throw new ArgumentNullException(nameof(owner)); _owner = owner ?? throw new ArgumentNullException(nameof(owner));
_executeLayoutPass = ExecuteLayoutPass; _executeLayoutPass = ExecuteQueuedLayoutPass;
} }
public virtual event EventHandler? LayoutUpdated; public virtual event EventHandler? LayoutUpdated;
@ -94,6 +94,16 @@ namespace Avalonia.Layout
QueueLayoutPass(); QueueLayoutPass();
} }
private void ExecuteQueuedLayoutPass()
{
if (!_queued)
{
return;
}
ExecuteLayoutPass();
}
/// <inheritdoc/> /// <inheritdoc/>
public virtual void ExecuteLayoutPass() public virtual void ExecuteLayoutPass()
{ {
@ -319,8 +329,8 @@ namespace Avalonia.Layout
{ {
if (!_queued && !_running) if (!_queued && !_running)
{ {
Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout);
_queued = true; _queued = true;
Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout);
} }
} }

24
src/Avalonia.Base/Layout/Layoutable.cs

@ -548,6 +548,14 @@ namespace Avalonia.Layout
if (IsVisible) if (IsVisible)
{ {
var margin = Margin; var margin = Margin;
var useLayoutRounding = UseLayoutRounding;
var scale = 1.0;
if (useLayoutRounding)
{
scale = LayoutHelper.GetLayoutScale(this);
margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale);
}
ApplyStyling(); ApplyStyling();
ApplyTemplate(); ApplyTemplate();
@ -584,16 +592,14 @@ namespace Avalonia.Layout
height = Math.Min(height, MaxHeight); height = Math.Min(height, MaxHeight);
height = Math.Max(height, MinHeight); height = Math.Max(height, MinHeight);
width = Math.Min(width, availableSize.Width); if (useLayoutRounding)
height = Math.Min(height, availableSize.Height);
if (UseLayoutRounding)
{ {
var scale = LayoutHelper.GetLayoutScale(this); (width, height) = LayoutHelper.RoundLayoutSizeUp(new Size(width, height), scale, scale);
width = LayoutHelper.RoundLayoutValue(width, scale);
height = LayoutHelper.RoundLayoutValue(height, scale);
} }
width = Math.Min(width, availableSize.Width);
height = Math.Min(height, availableSize.Height);
return NonNegative(new Size(width, height).Inflate(margin)); return NonNegative(new Size(width, height).Inflate(margin));
} }
else else
@ -678,8 +684,8 @@ namespace Avalonia.Layout
if (useLayoutRounding) if (useLayoutRounding)
{ {
size = LayoutHelper.RoundLayoutSize(size, scale, scale); size = LayoutHelper.RoundLayoutSizeUp(size, scale, scale);
availableSizeMinusMargins = LayoutHelper.RoundLayoutSize(availableSizeMinusMargins, scale, scale); availableSizeMinusMargins = LayoutHelper.RoundLayoutSizeUp(availableSizeMinusMargins, scale, scale);
} }
size = ArrangeOverride(size).Constrain(size); size = ArrangeOverride(size).Constrain(size);

10
src/Avalonia.Base/Logging/LogArea.cs

@ -44,5 +44,15 @@ namespace Avalonia.Logging
/// The log event comes from X11Platform. /// The log event comes from X11Platform.
/// </summary> /// </summary>
public const string X11Platform = nameof(X11Platform); public const string X11Platform = nameof(X11Platform);
/// <summary>
/// The log event comes from AndroidPlatform.
/// </summary>
public const string AndroidPlatform = nameof(AndroidPlatform);
/// <summary>
/// The log event comes from IOSPlatform.
/// </summary>
public const string IOSPlatform = nameof(IOSPlatform);
} }
} }

18
src/Avalonia.Base/Media/DrawingCollection.cs

@ -0,0 +1,18 @@
using System.Collections.Generic;
using Avalonia.Collections;
namespace Avalonia.Media
{
public sealed class DrawingCollection : AvaloniaList<Drawing>
{
public DrawingCollection()
{
ResetBehavior = ResetBehavior.Remove;
}
public DrawingCollection(IEnumerable<Drawing> items) : base(items)
{
ResetBehavior = ResetBehavior.Remove;
}
}
}

420
src/Avalonia.Base/Media/DrawingGroup.cs

@ -1,6 +1,10 @@
using Avalonia.Collections; using System;
using System.Collections.Generic;
using Avalonia.Media.Imaging;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -18,6 +22,14 @@ namespace Avalonia.Media
public static readonly StyledProperty<IBrush> OpacityMaskProperty = public static readonly StyledProperty<IBrush> OpacityMaskProperty =
AvaloniaProperty.Register<DrawingGroup, IBrush>(nameof(OpacityMask)); AvaloniaProperty.Register<DrawingGroup, IBrush>(nameof(OpacityMask));
public static readonly DirectProperty<DrawingGroup, DrawingCollection> ChildrenProperty =
AvaloniaProperty.RegisterDirect<DrawingGroup, DrawingCollection>(
nameof(Children),
o => o.Children,
(o, v) => o.Children = v);
private DrawingCollection _children = new DrawingCollection();
public double Opacity public double Opacity
{ {
get => GetValue(OpacityProperty); get => GetValue(OpacityProperty);
@ -42,8 +54,23 @@ namespace Avalonia.Media
set => SetValue(OpacityMaskProperty, value); set => SetValue(OpacityMaskProperty, value);
} }
/// <summary>
/// Gets or sets the collection that contains the child geometries.
/// </summary>
[Content] [Content]
public AvaloniaList<Drawing> Children { get; } = new AvaloniaList<Drawing>(); public DrawingCollection Children
{
get => _children;
set
{
SetAndRaise(ChildrenProperty, ref _children, value);
}
}
public DrawingContext Open()
{
return new DrawingContext(new DrawingGroupDrawingContext(this));
}
public override void Draw(DrawingContext context) public override void Draw(DrawingContext context)
{ {
@ -75,5 +102,394 @@ namespace Avalonia.Media
return rect; return rect;
} }
private class DrawingGroupDrawingContext : IDrawingContextImpl
{
private readonly DrawingGroup _drawingGroup;
private readonly IPlatformRenderInterface _platformRenderInterface = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
private Matrix _transform;
private bool _disposed;
// Root drawing created by this DrawingContext.
//
// If there is only a single child of the root DrawingGroup, _rootDrawing
// will reference the single child, and the root _currentDrawingGroup
// value will be null. Otherwise, _rootDrawing will reference the
// root DrawingGroup, and be the same value as the root _currentDrawingGroup.
//
// Either way, _rootDrawing always references the root drawing.
protected Drawing? _rootDrawing;
// Current DrawingGroup that new children are added to
protected DrawingGroup? _currentDrawingGroup;
// Previous values of _currentDrawingGroup
private Stack<DrawingGroup?>? _previousDrawingGroupStack;
public DrawingGroupDrawingContext(DrawingGroup drawingGroup)
{
_drawingGroup = drawingGroup;
}
public Matrix Transform
{
get => _transform;
set
{
_transform = value;
PushTransform(new MatrixTransform(value));
}
}
public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect)
{
if ((brush == null) && (pen == null))
{
return;
}
// Instantiate the geometry
var geometry = _platformRenderInterface.CreateEllipseGeometry(rect);
// Add Drawing to the Drawing graph
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry));
}
public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
{
if (((brush == null) && (pen == null)) || (geometry == null))
{
return;
}
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry));
}
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
if (foreground == null || glyphRun == null)
{
return;
}
// Add a GlyphRunDrawing to the Drawing graph
GlyphRunDrawing glyphRunDrawing = new GlyphRunDrawing
{
Foreground = foreground,
GlyphRun = glyphRun,
};
// Add Drawing to the Drawing graph
AddDrawing(glyphRunDrawing);
}
public void DrawLine(IPen pen, Point p1, Point p2)
{
if (pen == null)
{
return;
}
// Instantiate the geometry
var geometry = _platformRenderInterface.CreateLineGeometry(p1, p2);
// Add Drawing to the Drawing graph
AddNewGeometryDrawing(null, pen, new PlatformGeometry(geometry));
}
public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default)
{
if ((brush == null) && (pen == null))
{
return;
}
// Instantiate the geometry
var geometry = _platformRenderInterface.CreateRectangleGeometry(rect.Rect);
// Add Drawing to the Drawing graph
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry));
}
public void Clear(Color color)
{
throw new NotImplementedException();
}
public IDrawingContextLayerImpl CreateLayer(Size size)
{
throw new NotImplementedException();
}
public void Custom(ICustomDrawOperation custom)
{
throw new NotImplementedException();
}
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
{
throw new NotImplementedException();
}
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{
throw new NotImplementedException();
}
public void PopBitmapBlendMode()
{
throw new NotImplementedException();
}
public void PopClip()
{
throw new NotImplementedException();
}
public void PopGeometryClip()
{
throw new NotImplementedException();
}
public void PopOpacity()
{
throw new NotImplementedException();
}
public void PopOpacityMask()
{
throw new NotImplementedException();
}
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
{
throw new NotImplementedException();
}
public void PushClip(Rect clip)
{
throw new NotImplementedException();
}
public void PushClip(RoundedRect clip)
{
throw new NotImplementedException();
}
public void PushGeometryClip(IGeometryImpl clip)
{
throw new NotImplementedException();
}
public void PushOpacity(double opacity)
{
throw new NotImplementedException();
}
public void PushOpacityMask(IBrush mask, Rect bounds)
{
throw new NotImplementedException();
}
public void Dispose()
{
// Dispose may be called multiple times without throwing
// an exception.
if (!_disposed)
{
// Match any outstanding Push calls with a Pop
if (_previousDrawingGroupStack != null)
{
int stackCount = _previousDrawingGroupStack.Count;
for (int i = 0; i < stackCount; i++)
{
Pop();
}
}
// Call CloseCore with the root DrawingGroup's children
DrawingCollection rootChildren;
if (_currentDrawingGroup != null)
{
// If we created a root DrawingGroup because multiple elements
// exist at the root level, provide it's Children collection
// directly.
rootChildren = _currentDrawingGroup.Children;
}
else
{
// Create a new DrawingCollection if we didn't create a
// root DrawingGroup because the root level only contained
// a single child.
//
// This collection is needed by DrawingGroup.Open because
// Open always replaces it's Children collection. It isn't
// strictly needed for Append, but always using a collection
// simplifies the TransactionalAppend implementation (i.e.,
// a seperate implemention isn't needed for a single element)
rootChildren = new DrawingCollection();
//
// We may need to opt-out of inheritance through the new Freezable.
// This is controlled by this.CanBeInheritanceContext.
//
if (_rootDrawing != null)
{
rootChildren.Add(_rootDrawing);
}
}
// Inform our derived classes that Close was called
_drawingGroup.Children = rootChildren;
_disposed = true;
}
}
/// <summary>
/// Pop
/// </summary>
private void Pop()
{
// Verify that Pop hasn't been called too many times
if ((_previousDrawingGroupStack == null) || (_previousDrawingGroupStack.Count == 0))
{
throw new InvalidOperationException("DrawingGroupStack count missmatch.");
}
// Restore the previous value of the current drawing group
_currentDrawingGroup = _previousDrawingGroupStack.Pop();
}
/// <summary>
/// PushTransform -
/// Push a Transform which will apply to all drawing operations until the corresponding
/// Pop.
/// </summary>
/// <param name="transform"> The Transform to push. </param>
private void PushTransform(Transform transform)
{
// Instantiate a new drawing group and set it as the _currentDrawingGroup
var drawingGroup = PushNewDrawingGroup();
// Set the transform on the new DrawingGroup
drawingGroup.Transform = transform;
}
/// <summary>
/// Creates a new DrawingGroup for a Push* call by setting the
/// _currentDrawingGroup to a newly instantiated DrawingGroup,
/// and saving the previous _currentDrawingGroup value on the
/// _previousDrawingGroupStack.
/// </summary>
private DrawingGroup PushNewDrawingGroup()
{
// Instantiate a new drawing group
DrawingGroup drawingGroup = new DrawingGroup();
// Add it to the drawing graph, like any other Drawing
AddDrawing(drawingGroup);
// Lazily allocate the stack when it is needed because many uses
// of DrawingDrawingContext will have a depth of one.
if (null == _previousDrawingGroupStack)
{
_previousDrawingGroupStack = new Stack<DrawingGroup?>(2);
}
// Save the previous _currentDrawingGroup value.
//
// If this is the first call, the value of _currentDrawingGroup
// will be null because AddDrawing doesn't create a _currentDrawingGroup
// for the first drawing. Having null on the stack is valid, and simply
// denotes that this new DrawingGroup is the first child in the root
// DrawingGroup. It is also possible for the first value on the stack
// to be non-null, which means that the root DrawingGroup has other
// children.
_previousDrawingGroupStack.Push(_currentDrawingGroup);
// Set this drawing group as the current one so that subsequent drawing's
// are added as it's children until Pop is called.
_currentDrawingGroup = drawingGroup;
return drawingGroup;
}
/// <summary>
/// Contains the functionality common to GeometryDrawing operations of
/// instantiating the GeometryDrawing, setting it's Freezable state,
/// and Adding it to the Drawing Graph.
/// </summary>
private void AddNewGeometryDrawing(IBrush? brush, IPen? pen, Geometry? geometry)
{
if (geometry == null)
{
throw new ArgumentNullException(nameof(geometry));
}
// Instantiate the GeometryDrawing
GeometryDrawing geometryDrawing = new GeometryDrawing
{
// We may need to opt-out of inheritance through the new Freezable.
// This is controlled by this.CanBeInheritanceContext.
Brush = brush,
Pen = pen,
Geometry = geometry
};
// Add it to the drawing graph
AddDrawing(geometryDrawing);
}
/// <summary>
/// Adds a new Drawing to the DrawingGraph.
///
/// This method avoids creating a DrawingGroup for the common case
/// where only a single child exists in the root DrawingGroup.
/// </summary>
private void AddDrawing(Drawing newDrawing)
{
if (newDrawing == null)
{
throw new ArgumentNullException(nameof(newDrawing));
}
if (_rootDrawing == null)
{
// When a DrawingGroup is set, it should be made the root if
// a root drawing didnt exist.
Contract.Requires<NotSupportedException>(_currentDrawingGroup == null);
// If this is the first Drawing being added, avoid creating a DrawingGroup
// and set this drawing as the root drawing. This optimizes the common
// case where only a single child exists in the root DrawingGroup.
_rootDrawing = newDrawing;
}
else if (_currentDrawingGroup == null)
{
// When the second drawing is added at the root level, set a
// DrawingGroup as the root and add both drawings to it.
// Instantiate the DrawingGroup
_currentDrawingGroup = new DrawingGroup();
// Add both Children
_currentDrawingGroup.Children.Add(_rootDrawing);
_currentDrawingGroup.Children.Add(newDrawing);
// Set the new DrawingGroup as the current
_rootDrawing = _currentDrawingGroup;
}
else
{
// If there already is a current drawing group, then simply add
// the new drawing too it.
_currentDrawingGroup.Children.Add(newDrawing);
}
}
}
} }
} }

129
src/Avalonia.Base/Media/FormattedText.cs

@ -1223,7 +1223,7 @@ namespace Avalonia.Media
public double OverhangTrailing public double OverhangTrailing
{ {
get get
{ {
return BlackBoxMetrics.OverhangTrailing; return BlackBoxMetrics.OverhangTrailing;
} }
} }
@ -1252,6 +1252,46 @@ namespace Avalonia.Media
} }
} }
/// <summary>
/// Obtains geometry for the text, including underlines and strikethroughs.
/// </summary>
/// <param name="origin">The left top origin of the resulting geometry.</param>
/// <returns>The geometry returned contains the combined geometry
/// of all of the glyphs, underlines and strikeThroughs that represent the formatted text.
/// Overlapping contours are merged by performing a Boolean union operation.</returns>
public Geometry? BuildGeometry(Point origin)
{
GeometryGroup? accumulatedGeometry = null;
var lineOrigin = origin;
DrawingGroup drawing = new DrawingGroup();
using (var ctx = drawing.Open())
{
using (var enumerator = GetEnumerator())
{
while (enumerator.MoveNext())
{
var currentLine = enumerator.Current;
if (currentLine != null)
{
currentLine.Draw(ctx, lineOrigin);
AdvanceLineOrigin(ref lineOrigin, currentLine);
}
}
}
}
Transform? transform = new TranslateTransform(origin.X, origin.Y);
// recursively go down the DrawingGroup to build up the geometry
CombineGeometryRecursive(drawing, ref transform, ref accumulatedGeometry);
return accumulatedGeometry;
}
/// <summary> /// <summary>
/// Draws the text object /// Draws the text object
/// </summary> /// </summary>
@ -1284,6 +1324,93 @@ namespace Avalonia.Media
} }
} }
private void CombineGeometryRecursive(Drawing drawing, ref Transform? transform, ref GeometryGroup? accumulatedGeometry)
{
if (drawing is DrawingGroup group)
{
transform = group.Transform;
if (group.Children is DrawingCollection children)
{
// recursively go down for DrawingGroup
foreach (var child in children)
{
CombineGeometryRecursive(child, ref transform, ref accumulatedGeometry);
}
}
}
else
{
if (drawing is GlyphRunDrawing glyphRunDrawing)
{
// process glyph run
var glyphRun = glyphRunDrawing.GlyphRun;
if (glyphRun != null)
{
var glyphRunGeometry = glyphRun.BuildGeometry();
glyphRunGeometry.Transform = transform;
if (accumulatedGeometry == null)
{
accumulatedGeometry = new GeometryGroup
{
FillRule = FillRule.NonZero
};
}
accumulatedGeometry.Children.Add(glyphRunGeometry);
}
}
else
{
if (drawing is GeometryDrawing geometryDrawing)
{
// process geometry (i.e. TextDecoration on the line)
var geometry = geometryDrawing.Geometry;
if (geometry != null)
{
geometry.Transform = transform;
if (geometry is LineGeometry lineGeometry)
{
// For TextDecoration drawn by DrawLine(), the geometry is a LineGeometry which has no
// bounding area. So this line won't show up. Work aroud it by increase the Bounding rect
// to be Pen's thickness
var bounds = lineGeometry.Bounds;
if (bounds.Height == 0)
{
bounds = bounds.WithHeight(geometryDrawing.Pen?.Thickness ?? 0);
}
else if (bounds.Width == 0)
{
bounds = bounds.WithWidth(geometryDrawing.Pen?.Thickness ?? 0);
}
// convert the line geometry into a rectangle geometry
// we lost line cap info here
geometry = new RectangleGeometry(bounds);
}
if (accumulatedGeometry == null)
{
accumulatedGeometry = new GeometryGroup
{
FillRule = FillRule.NonZero
};
}
accumulatedGeometry.Children.Add(geometry);
}
}
}
}
}
private CachedMetrics DrawAndCalculateMetrics(DrawingContext? drawingContext, Point drawingOffset, bool getBlackBoxMetrics) private CachedMetrics DrawAndCalculateMetrics(DrawingContext? drawingContext, Point drawingOffset, bool getBlackBoxMetrics)
{ {
var metrics = new CachedMetrics(); var metrics = new CachedMetrics();

45
src/Avalonia.Base/Media/GeometryCollection.cs

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using Avalonia.Collections;
#nullable enable
namespace Avalonia.Media
{
public sealed class GeometryCollection : AvaloniaList<Geometry>
{
public GeometryCollection()
{
ResetBehavior = ResetBehavior.Remove;
this.ForEachItem(
x =>
{
Parent?.Invalidate();
},
x =>
{
Parent?.Invalidate();
},
() => throw new NotSupportedException());
}
public GeometryCollection(IEnumerable<Geometry> items) : base(items)
{
ResetBehavior = ResetBehavior.Remove;
this.ForEachItem(
x =>
{
Parent?.Invalidate();
},
x =>
{
Parent?.Invalidate();
},
() => throw new NotSupportedException());
}
public GeometryGroup? Parent { get; set; }
}
}

12
src/Avalonia.Base/Media/GeometryDrawing.cs

@ -21,14 +21,14 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Brush"/> property. /// Defines the <see cref="Brush"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBrush> BrushProperty = public static readonly StyledProperty<IBrush?> BrushProperty =
AvaloniaProperty.Register<GeometryDrawing, IBrush>(nameof(Brush), Brushes.Transparent); AvaloniaProperty.Register<GeometryDrawing, IBrush?>(nameof(Brush), Brushes.Transparent);
/// <summary> /// <summary>
/// Defines the <see cref="Pen"/> property. /// Defines the <see cref="Pen"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<Pen> PenProperty = public static readonly StyledProperty<Pen?> PenProperty =
AvaloniaProperty.Register<GeometryDrawing, Pen>(nameof(Pen)); AvaloniaProperty.Register<GeometryDrawing, Pen?>(nameof(Pen));
/// <summary> /// <summary>
/// Gets or sets the <see cref="Avalonia.Media.Geometry"/> that describes the shape of this <see cref="GeometryDrawing"/>. /// Gets or sets the <see cref="Avalonia.Media.Geometry"/> that describes the shape of this <see cref="GeometryDrawing"/>.
@ -43,7 +43,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the <see cref="Avalonia.Media.IBrush"/> used to fill the interior of the shape described by this <see cref="GeometryDrawing"/>. /// Gets or sets the <see cref="Avalonia.Media.IBrush"/> used to fill the interior of the shape described by this <see cref="GeometryDrawing"/>.
/// </summary> /// </summary>
public IBrush Brush public IBrush? Brush
{ {
get => GetValue(BrushProperty); get => GetValue(BrushProperty);
set => SetValue(BrushProperty, value); set => SetValue(BrushProperty, value);
@ -52,7 +52,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the <see cref="Avalonia.Media.IPen"/> used to stroke this <see cref="GeometryDrawing"/>. /// Gets or sets the <see cref="Avalonia.Media.IPen"/> used to stroke this <see cref="GeometryDrawing"/>.
/// </summary> /// </summary>
public IPen Pen public IPen? Pen
{ {
get => GetValue(PenProperty); get => GetValue(PenProperty);
set => SetValue(PenProperty, value); set => SetValue(PenProperty, value);

58
src/Avalonia.Base/GeometryGroup.cs → src/Avalonia.Base/Media/GeometryGroup.cs

@ -1,7 +1,4 @@
using System; using Avalonia.Metadata;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
#nullable enable #nullable enable
@ -13,29 +10,36 @@ namespace Avalonia.Media
/// </summary> /// </summary>
public class GeometryGroup : Geometry public class GeometryGroup : Geometry
{ {
public static readonly DirectProperty<GeometryGroup, GeometryCollection?> ChildrenProperty = public static readonly DirectProperty<GeometryGroup, GeometryCollection> ChildrenProperty =
AvaloniaProperty.RegisterDirect<GeometryGroup, GeometryCollection?> ( AvaloniaProperty.RegisterDirect<GeometryGroup, GeometryCollection> (
nameof(Children), nameof(Children),
o => o.Children, o => o.Children,
(o, v) => o.Children = v); (o, v)=> o.Children = v);
public static readonly StyledProperty<FillRule> FillRuleProperty = public static readonly StyledProperty<FillRule> FillRuleProperty =
AvaloniaProperty.Register<GeometryGroup, FillRule>(nameof(FillRule)); AvaloniaProperty.Register<GeometryGroup, FillRule>(nameof(FillRule));
private GeometryCollection? _children; private GeometryCollection _children;
private bool _childrenSet;
public GeometryGroup()
{
_children = new GeometryCollection
{
Parent = this
};
}
/// <summary> /// <summary>
/// Gets or sets the collection that contains the child geometries. /// Gets or sets the collection that contains the child geometries.
/// </summary> /// </summary>
[Content] [Content]
public GeometryCollection? Children public GeometryCollection Children
{ {
get => _children ??= (!_childrenSet ? new GeometryCollection() : null); get => _children;
set set
{ {
SetAndRaise(ChildrenProperty, ref _children, value); OnChildrenChanged(_children, value);
_childrenSet = true; SetAndRaise(ChildrenProperty, ref _children, value);
} }
} }
@ -52,16 +56,28 @@ namespace Avalonia.Media
public override Geometry Clone() public override Geometry Clone()
{ {
var result = new GeometryGroup { FillRule = FillRule, Transform = Transform }; var result = new GeometryGroup { FillRule = FillRule, Transform = Transform };
if (_children?.Count > 0)
if (_children.Count > 0)
{
result.Children = new GeometryCollection(_children); result.Children = new GeometryCollection(_children);
}
return result; return result;
} }
protected void OnChildrenChanged(GeometryCollection oldChildren, GeometryCollection newChildren)
{
oldChildren.Parent = null;
newChildren.Parent = this;
}
protected override IGeometryImpl? CreateDefiningGeometry() protected override IGeometryImpl? CreateDefiningGeometry()
{ {
if (_children?.Count > 0) if (_children.Count > 0)
{ {
var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>(); var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
return factory.CreateGeometryGroup(FillRule, _children); return factory.CreateGeometryGroup(FillRule, _children);
} }
@ -72,10 +88,18 @@ namespace Avalonia.Media
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ChildrenProperty || change.Property == FillRuleProperty) switch (change.Property.Name)
{ {
InvalidateGeometry(); case nameof(FillRule):
case nameof(Children):
InvalidateGeometry();
break;
} }
} }
internal void Invalidate()
{
InvalidateGeometry();
}
} }
} }

20
src/Avalonia.Base/Media/GlyphRun.cs

@ -194,6 +194,19 @@ namespace Avalonia.Media
} }
} }
/// <summary>
/// Obtains geometry for the glyph run.
/// </summary>
/// <returns>The geometry returned contains the combined geometry of all glyphs in the glyph run.</returns>
public Geometry BuildGeometry()
{
var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
var geometryImpl = platformRenderInterface.BuildGlyphRunGeometry(this);
return new PlatformGeometry(geometryImpl);
}
/// <summary> /// <summary>
/// Retrieves the offset from the leading edge of the <see cref="GlyphRun"/> /// Retrieves the offset from the leading edge of the <see cref="GlyphRun"/>
/// to the leading or trailing edge of a caret stop containing the specified character hit. /// to the leading or trailing edge of a caret stop containing the specified character hit.
@ -721,10 +734,9 @@ namespace Avalonia.Media
private void Set<T>(ref T field, T value) private void Set<T>(ref T field, T value)
{ {
if (_glyphRunImpl != null) _glyphRunImpl?.Dispose();
{
throw new InvalidOperationException("GlyphRun can't be changed after it has been initialized.'"); _glyphRunImpl = null;
}
_glyphRunMetrics = null; _glyphRunMetrics = null;

24
src/Avalonia.Base/Media/PlatformGeometry.cs

@ -0,0 +1,24 @@
using Avalonia.Platform;
namespace Avalonia.Media
{
internal class PlatformGeometry : Geometry
{
private readonly IGeometryImpl _geometryImpl;
public PlatformGeometry(IGeometryImpl geometryImpl)
{
_geometryImpl = geometryImpl;
}
public override Geometry Clone()
{
return new PlatformGeometry(_geometryImpl);
}
protected override IGeometryImpl? CreateDefiningGeometry()
{
return _geometryImpl;
}
}
}

23
src/Avalonia.Base/Media/TextAlignment.cs

@ -19,5 +19,28 @@ namespace Avalonia.Media
/// The text is right-aligned. /// The text is right-aligned.
/// </summary> /// </summary>
Right, Right,
/// <summary>
/// The beginning of the text is aligned to the edge of the available space.
/// </summary>
Start,
/// <summary>
/// The end of the text is aligned to the edge of the available space.
/// </summary>
End,
/// <summary>
/// Text alignment is inferred from the text content.
/// </summary>
/// <remarks>
/// When the TextAlignment property is set to DetectFromContent, alignment is inferred from the text content of the control. For example, English text is left aligned, and Arabic text is right aligned.
/// </remarks>
DetectFromContent,
/// <summary>
/// Text is justified within the available space.
/// </summary>
Justify
} }
} }

109
src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode;
namespace Avalonia.Media.TextFormatting
{
internal class InterWordJustification : JustificationProperties
{
public InterWordJustification(double width)
{
Width = width;
}
public override double Width { get; }
public override void Justify(TextLine textLine)
{
var paragraphWidth = Width;
if (double.IsInfinity(paragraphWidth))
{
return;
}
if (textLine.NewLineLength > 0)
{
return;
}
var textLineBreak = textLine.TextLineBreak;
if (textLineBreak is not null && textLineBreak.TextEndOfLine is not null)
{
if (textLineBreak.RemainingRuns is null || textLineBreak.RemainingRuns.Count == 0)
{
return;
}
}
var breakOportunities = new Queue<int>();
foreach (var textRun in textLine.TextRuns)
{
var text = textRun.Text;
if (text.IsEmpty)
{
continue;
}
var start = text.Start;
var lineBreakEnumerator = new LineBreakEnumerator(text);
while (lineBreakEnumerator.MoveNext())
{
var currentBreak = lineBreakEnumerator.Current;
if (!currentBreak.Required && currentBreak.PositionWrap != text.Length)
{
breakOportunities.Enqueue(start + currentBreak.PositionMeasure);
}
}
}
if (breakOportunities.Count == 0)
{
return;
}
var remainingSpace = Math.Max(0, paragraphWidth - textLine.WidthIncludingTrailingWhitespace);
var spacing = remainingSpace / breakOportunities.Count;
foreach (var textRun in textLine.TextRuns)
{
var text = textRun.Text;
if (text.IsEmpty)
{
continue;
}
if (textRun is ShapedTextCharacters shapedText)
{
var glyphRun = shapedText.GlyphRun;
var shapedBuffer = shapedText.ShapedBuffer;
var currentPosition = text.Start;
while (breakOportunities.Count > 0)
{
var characterIndex = breakOportunities.Dequeue();
if (characterIndex < currentPosition)
{
continue;
}
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex);
var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex];
shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing);
}
glyphRun.GlyphAdvances = shapedBuffer.GlyphAdvances;
}
}
}
}
}

16
src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs

@ -0,0 +1,16 @@
namespace Avalonia.Media.TextFormatting
{
public abstract class JustificationProperties
{
/// <summary>
/// Gets the width in which the range is justified.
/// </summary>
public abstract double Width { get; }
/// <summary>
/// Justifies given text line.
/// </summary>
/// <param name="textLine">Text line to collapse.</param>
public abstract void Justify(TextLine textLine);
}
}

38
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media.TextFormatting
TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null) TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null)
{ {
var textWrapping = paragraphProperties.TextWrapping; var textWrapping = paragraphProperties.TextWrapping;
FlowDirection flowDirection; FlowDirection resolvedFlowDirection;
TextLineBreak? nextLineBreak = null; TextLineBreak? nextLineBreak = null;
List<DrawableTextRun> drawableTextRuns; List<DrawableTextRun> drawableTextRuns;
@ -24,17 +24,17 @@ namespace Avalonia.Media.TextFormatting
if (previousLineBreak?.RemainingRuns != null) if (previousLineBreak?.RemainingRuns != null)
{ {
flowDirection = previousLineBreak.FlowDirection; resolvedFlowDirection = previousLineBreak.FlowDirection;
drawableTextRuns = previousLineBreak.RemainingRuns.ToList(); drawableTextRuns = previousLineBreak.RemainingRuns.ToList();
nextLineBreak = previousLineBreak; nextLineBreak = previousLineBreak;
} }
else else
{ {
drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection); drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out resolvedFlowDirection);
if (nextLineBreak == null && textEndOfLine != null) if (nextLineBreak == null && textEndOfLine != null)
{ {
nextLineBreak = new TextLineBreak(textEndOfLine, flowDirection); nextLineBreak = new TextLineBreak(textEndOfLine, resolvedFlowDirection);
} }
} }
@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.NoWrap: case TextWrapping.NoWrap:
{ {
textLine = new TextLineImpl(drawableTextRuns, firstTextSourceIndex, textSourceLength, textLine = new TextLineImpl(drawableTextRuns, firstTextSourceIndex, textSourceLength,
paragraphWidth, paragraphProperties, flowDirection, nextLineBreak); paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine(); textLine.FinalizeLine();
@ -55,7 +55,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.Wrap: case TextWrapping.Wrap:
{ {
textLine = PerformTextWrapping(drawableTextRuns, firstTextSourceIndex, paragraphWidth, paragraphProperties, textLine = PerformTextWrapping(drawableTextRuns, firstTextSourceIndex, paragraphWidth, paragraphProperties,
flowDirection, nextLineBreak); resolvedFlowDirection, nextLineBreak);
break; break;
} }
default: default:
@ -159,7 +159,6 @@ namespace Avalonia.Media.TextFormatting
{ {
var flowDirection = paragraphProperties.FlowDirection; var flowDirection = paragraphProperties.FlowDirection;
var drawableTextRuns = new List<DrawableTextRun>(); var drawableTextRuns = new List<DrawableTextRun>();
var biDiData = new BidiData((sbyte)flowDirection); var biDiData = new BidiData((sbyte)flowDirection);
foreach (var textRun in textRuns) foreach (var textRun in textRuns)
@ -174,10 +173,9 @@ namespace Avalonia.Media.TextFormatting
{ {
biDiData.Append(textRun.Text); biDiData.Append(textRun.Text);
} }
} }
var biDi = BidiAlgorithm.Instance.Value!; var biDi = new BidiAlgorithm();
biDi.Process(biDiData); biDi.Process(biDiData);
@ -290,9 +288,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textCharacters">The text characters to form <see cref="ShapeableTextCharacters"/> from.</param> /// <param name="textCharacters">The text characters to form <see cref="ShapeableTextCharacters"/> from.</param>
/// <param name="levels">The bidi levels.</param> /// <param name="levels">The bidi levels.</param>
/// <returns></returns> /// <returns></returns>
private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels( private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels(IReadOnlyList<TextRun> textCharacters, ArraySlice<sbyte> levels)
IReadOnlyList<TextRun> textCharacters,
ReadOnlySlice<sbyte> levels)
{ {
if (levels.Length == 0) if (levels.Length == 0)
{ {
@ -404,9 +400,9 @@ namespace Avalonia.Media.TextFormatting
{ {
endOfLine = textEndOfLine; endOfLine = textEndOfLine;
textRuns.Add(textRun); textSourceLength += textEndOfLine.TextSourceLength;
textSourceLength += textRun.TextSourceLength; textRuns.Add(textRun);
break; break;
} }
@ -431,9 +427,9 @@ namespace Avalonia.Media.TextFormatting
break; break;
} }
case DrawableTextRun drawableTextRun: default:
{ {
textRuns.Add(drawableTextRun); textRuns.Add(textRun);
break; break;
} }
} }
@ -552,11 +548,11 @@ namespace Avalonia.Media.TextFormatting
/// <param name="firstTextSourceIndex">The first text source index.</param> /// <param name="firstTextSourceIndex">The first text source index.</param>
/// <param name="paragraphWidth">The paragraph width.</param> /// <param name="paragraphWidth">The paragraph width.</param>
/// <param name="paragraphProperties">The text paragraph properties.</param> /// <param name="paragraphProperties">The text paragraph properties.</param>
/// <param name="flowDirection"></param> /// <param name="resolvedFlowDirection"></param>
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param> /// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
/// <returns>The wrapped text line.</returns> /// <returns>The wrapped text line.</returns>
private static TextLineImpl PerformTextWrapping(List<DrawableTextRun> textRuns, int firstTextSourceIndex, private static TextLineImpl PerformTextWrapping(List<DrawableTextRun> textRuns, int firstTextSourceIndex,
double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection, double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection,
TextLineBreak? currentLineBreak) TextLineBreak? currentLineBreak)
{ {
if(textRuns.Count == 0) if(textRuns.Count == 0)
@ -684,16 +680,16 @@ namespace Avalonia.Media.TextFormatting
var remainingCharacters = splitResult.Second; var remainingCharacters = splitResult.Second;
var lineBreak = remainingCharacters?.Count > 0 ? var lineBreak = remainingCharacters?.Count > 0 ?
new TextLineBreak(currentLineBreak?.TextEndOfLine, flowDirection, remainingCharacters) : new TextLineBreak(currentLineBreak?.TextEndOfLine, resolvedFlowDirection, remainingCharacters) :
null; null;
if (lineBreak is null && currentLineBreak?.TextEndOfLine != null) if (lineBreak is null && currentLineBreak?.TextEndOfLine != null)
{ {
lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, flowDirection); lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection);
} }
var textLine = new TextLineImpl(splitResult.First, firstTextSourceIndex, measuredLength, var textLine = new TextLineImpl(splitResult.First, firstTextSourceIndex, measuredLength,
paragraphWidth, paragraphProperties, flowDirection, paragraphWidth, paragraphProperties, resolvedFlowDirection,
lineBreak); lineBreak);
return textLine.FinalizeLine(); return textLine.FinalizeLine();

31
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -439,7 +439,7 @@ namespace Avalonia.Media.TextFormatting
var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth, var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth,
_paragraphProperties, previousLine?.TextLineBreak); _paragraphProperties, previousLine?.TextLineBreak);
if(textLine == null || textLine.Length == 0) if(textLine == null || textLine.Length == 0 || textLine.TextRuns.Count == 0 && textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
{ {
if(previousLine != null && previousLine.NewLineLength > 0) if(previousLine != null && previousLine.NewLineLength > 0)
{ {
@ -501,6 +501,35 @@ namespace Avalonia.Media.TextFormatting
Bounds = new Rect(left, 0, width, height); Bounds = new Rect(left, 0, width, height);
if(_paragraphProperties.TextAlignment == TextAlignment.Justify)
{
var whitespaceWidth = 0d;
foreach (var line in textLines)
{
var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
if(lineWhitespaceWidth > whitespaceWidth)
{
whitespaceWidth = lineWhitespaceWidth;
}
}
var justificationWidth = width - whitespaceWidth;
if(justificationWidth > 0)
{
var justificationProperties = new InterWordJustification(justificationWidth);
for (var i = 0; i < textLines.Count - 1; i++)
{
var line = textLines[i];
line.Justify(justificationProperties);
}
}
}
return textLines; return textLines;
} }

66
src/Avalonia.Base/Media/TextFormatting/TextLine.cs

@ -15,9 +15,15 @@ namespace Avalonia.Media.TextFormatting
/// The contained text runs. /// The contained text runs.
/// </value> /// </value>
public abstract IReadOnlyList<TextRun> TextRuns { get; } public abstract IReadOnlyList<TextRun> TextRuns { get; }
/// <summary>
/// Gets the first TextSource position of the current line.
/// </summary>
public abstract int FirstTextSourceIndex { get; } public abstract int FirstTextSourceIndex { get; }
/// <summary>
/// Gets the total number of TextSource positions of the current line.
/// </summary>
public abstract int Length { get; } public abstract int Length { get; }
/// <summary> /// <summary>
@ -56,7 +62,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a value that indicates whether content of the line overflows the specified paragraph width. /// Gets a value that indicates whether content of the line overflows the specified paragraph width.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// <c>true</c>, it the line overflows the specified paragraph width; otherwise, <c>false</c>. /// <c>true</c>, the line overflows the specified paragraph width; otherwise, <c>false</c>.
/// </returns> /// </returns>
public abstract bool HasOverflowed { get; } public abstract bool HasOverflowed { get; }
@ -75,7 +81,7 @@ namespace Avalonia.Media.TextFormatting
/// The number of newline characters. /// The number of newline characters.
/// </returns> /// </returns>
public abstract int NewLineLength { get; } public abstract int NewLineLength { get; }
/// <summary> /// <summary>
/// Gets the distance that black pixels extend beyond the bottom alignment edge of a line. /// Gets the distance that black pixels extend beyond the bottom alignment edge of a line.
/// </summary> /// </summary>
@ -149,6 +155,15 @@ namespace Avalonia.Media.TextFormatting
/// </returns> /// </returns>
public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList); public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
/// <summary>
/// Create a justified line based on justification text properties.
/// </summary>
/// <param name="justificationProperties">An object that represent the justification text properties.</param>
/// <returns>
/// A <see cref="TextLine"/> value that represents a justified line that can be displayed.
/// </returns>
public abstract void Justify(JustificationProperties justificationProperties);
/// <summary> /// <summary>
/// Gets the character hit corresponding to the specified distance from the beginning of the line. /// Gets the character hit corresponding to the specified distance from the beginning of the line.
/// </summary> /// </summary>
@ -192,50 +207,5 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textLength">number of characters of the specified range</param> /// <param name="textLength">number of characters of the specified range</param>
/// <returns>an array of bounding rectangles.</returns> /// <returns>an array of bounding rectangles.</returns>
public abstract IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength); public abstract IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength);
/// <summary>
/// Gets the text line offset x.
/// </summary>
/// <param name="width">The line width.</param>
/// <param name="widthIncludingTrailingWhitespace">The paragraph width including whitespace.</param>
/// <param name="paragraphWidth">The paragraph width.</param>
/// <param name="textAlignment">The text alignment.</param>
/// <param name="flowDirection">The flow direction of the line.</param>
/// <returns>The paragraph offset.</returns>
internal static double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace,
double paragraphWidth, TextAlignment textAlignment, FlowDirection flowDirection)
{
if (double.IsPositiveInfinity(paragraphWidth))
{
return 0;
}
if (flowDirection == FlowDirection.LeftToRight)
{
switch (textAlignment)
{
case TextAlignment.Center:
return Math.Max(0, (paragraphWidth - width) / 2);
case TextAlignment.Right:
return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
default:
return 0;
}
}
switch (textAlignment)
{
case TextAlignment.Center:
return Math.Max(0, (paragraphWidth - width) / 2);
case TextAlignment.Right:
return 0;
default:
return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
}
}
} }
} }

77
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -10,10 +10,10 @@ namespace Avalonia.Media.TextFormatting
private readonly double _paragraphWidth; private readonly double _paragraphWidth;
private readonly TextParagraphProperties _paragraphProperties; private readonly TextParagraphProperties _paragraphProperties;
private TextLineMetrics _textLineMetrics; private TextLineMetrics _textLineMetrics;
private readonly FlowDirection _flowDirection; private readonly FlowDirection _resolvedFlowDirection;
public TextLineImpl(List<DrawableTextRun> textRuns, int firstTextSourceIndex, int length, double paragraphWidth, public TextLineImpl(List<DrawableTextRun> textRuns, int firstTextSourceIndex, int length, double paragraphWidth,
TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight, TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection = FlowDirection.LeftToRight,
TextLineBreak? lineBreak = null, bool hasCollapsed = false) TextLineBreak? lineBreak = null, bool hasCollapsed = false)
{ {
FirstTextSourceIndex = firstTextSourceIndex; FirstTextSourceIndex = firstTextSourceIndex;
@ -25,7 +25,7 @@ namespace Avalonia.Media.TextFormatting
_paragraphWidth = paragraphWidth; _paragraphWidth = paragraphWidth;
_paragraphProperties = paragraphProperties; _paragraphProperties = paragraphProperties;
_flowDirection = flowDirection; _resolvedFlowDirection = resolvedFlowDirection;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -136,7 +136,7 @@ namespace Avalonia.Media.TextFormatting
} }
var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties, var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties,
_flowDirection, TextLineBreak, true); _resolvedFlowDirection, TextLineBreak, true);
if (collapsedRuns.Count > 0) if (collapsedRuns.Count > 0)
{ {
@ -144,7 +144,14 @@ namespace Avalonia.Media.TextFormatting
} }
return collapsedLine; return collapsedLine;
}
/// <inheritdoc/>
public override void Justify(JustificationProperties justificationProperties)
{
justificationProperties.Justify(this);
_textLineMetrics = CreateLineMetrics();
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -167,7 +174,7 @@ namespace Avalonia.Media.TextFormatting
return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _); return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _);
} }
return _flowDirection == FlowDirection.LeftToRight ? return _resolvedFlowDirection == FlowDirection.LeftToRight ?
new CharacterHit(FirstTextSourceIndex) : new CharacterHit(FirstTextSourceIndex) :
new CharacterHit(FirstTextSourceIndex + Length); new CharacterHit(FirstTextSourceIndex + Length);
} }
@ -260,7 +267,7 @@ namespace Avalonia.Media.TextFormatting
//Look at the left and right edge of the current run //Look at the left and right edge of the current run
if (currentRun.IsLeftToRight) if (currentRun.IsLeftToRight)
{ {
if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
{ {
if (characterIndex <= currentPosition) if (characterIndex <= currentPosition)
{ {
@ -735,7 +742,7 @@ namespace Avalonia.Media.TextFormatting
// Build up the collection of ordered runs. // Build up the collection of ordered runs.
var run = _textRuns[0]; var run = _textRuns[0];
OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _flowDirection)); OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _resolvedFlowDirection));
var current = orderedRun; var current = orderedRun;
@ -743,7 +750,7 @@ namespace Avalonia.Media.TextFormatting
{ {
run = _textRuns[i]; run = _textRuns[i];
current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _flowDirection)); current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _resolvedFlowDirection));
current = current.Next; current = current.Next;
} }
@ -762,7 +769,7 @@ namespace Avalonia.Media.TextFormatting
{ {
var currentRun = _textRuns[i]; var currentRun = _textRuns[i];
var level = GetRunBidiLevel(currentRun, _flowDirection); var level = GetRunBidiLevel(currentRun, _resolvedFlowDirection);
if (level > max) if (level > max)
{ {
@ -1242,8 +1249,7 @@ namespace Avalonia.Media.TextFormatting
} }
} }
var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, var start = GetParagraphOffsetX(width, widthIncludingWhitespace);
_paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection);
if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight))
{ {
@ -1257,6 +1263,55 @@ namespace Avalonia.Media.TextFormatting
-ascent, trailingWhitespaceLength, width, widthIncludingWhitespace); -ascent, trailingWhitespaceLength, width, widthIncludingWhitespace);
} }
/// <summary>
/// Gets the text line offset x.
/// </summary>
/// <param name="width">The line width.</param>
/// <param name="widthIncludingTrailingWhitespace">The paragraph width including whitespace.</param>
/// <returns>The paragraph offset.</returns>
private double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace)
{
if (double.IsPositiveInfinity(_paragraphWidth))
{
return 0;
}
var textAlignment = _paragraphProperties.TextAlignment;
var paragraphFlowDirection = _paragraphProperties.FlowDirection;
switch (textAlignment)
{
case TextAlignment.Start:
{
textAlignment = paragraphFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right;
break;
}
case TextAlignment.End:
{
textAlignment = paragraphFlowDirection == FlowDirection.RightToLeft ? TextAlignment.Left : TextAlignment.Right;
break;
}
case TextAlignment.DetectFromContent:
{
textAlignment = _resolvedFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right;
break;
}
}
switch (textAlignment)
{
case TextAlignment.Center:
return Math.Max(0, (_paragraphWidth - width) / 2);
case TextAlignment.Right:
return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
default:
return 0;
}
}
private sealed class OrderedBidiRun private sealed class OrderedBidiRun
{ {
public OrderedBidiRun(DrawableTextRun run, sbyte level) public OrderedBidiRun(DrawableTextRun run, sbyte level)

472
src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs

@ -0,0 +1,472 @@
using System;
namespace Avalonia.Media.TextFormatting.Unicode
{
internal static class BidiTrie
{
public static ReadOnlySpan<byte> Data => new byte[]
{
0, 0, 16, 0, 0, 0, 0, 0, 64, 180, 0, 0, 116, 3, 0, 0, 124, 3, 0, 0, 132, 3, 0, 0, 140, 3, 0, 0, 164, 3, 0, 0, 172, 3, 0, 0, 180, 3, 0, 0, 188, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 194, 3, 0, 0, 202, 3, 0, 0,
210, 3, 0, 0, 218, 3, 0, 0, 226, 3, 0, 0, 234, 3, 0, 0, 230, 3, 0, 0, 238, 3, 0, 0, 246, 3, 0, 0, 254, 3, 0, 0, 249, 3, 0, 0, 1, 4, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 9, 4, 0, 0, 17, 4, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 148, 3, 0, 0, 156, 3, 0, 0, 23, 4, 0, 0, 31, 4, 0, 0, 39, 4, 0, 0,
47, 4, 0, 0, 55, 4, 0, 0, 63, 4, 0, 0, 69, 4, 0, 0, 77, 4, 0, 0, 82, 4, 0, 0, 90, 4, 0, 0, 93, 4, 0, 0, 101, 4, 0, 0, 108, 4, 0, 0, 116, 4, 0, 0, 122, 4, 0, 0, 130, 4, 0, 0, 129, 4, 0, 0, 137, 4, 0, 0, 145, 4, 0, 0, 153, 4, 0, 0, 24, 9, 0, 0, 31, 9, 0, 0, 35, 9, 0, 0, 62, 4, 0, 0, 199, 9, 0, 0, 62, 4, 0, 0, 22, 11, 0, 0, 207, 9, 0, 0,
161, 4, 0, 0, 163, 4, 0, 0, 171, 4, 0, 0, 179, 4, 0, 0, 187, 4, 0, 0, 188, 4, 0, 0, 196, 4, 0, 0, 204, 4, 0, 0, 212, 4, 0, 0, 188, 4, 0, 0, 220, 4, 0, 0, 225, 4, 0, 0, 212, 4, 0, 0, 188, 4, 0, 0, 233, 4, 0, 0, 241, 4, 0, 0, 187, 4, 0, 0, 249, 4, 0, 0, 1, 5, 0, 0, 179, 4, 0, 0, 9, 5, 0, 0, 148, 3, 0, 0, 17, 5, 0, 0, 21, 5, 0, 0, 29, 5, 0, 0,
31, 5, 0, 0, 39, 5, 0, 0, 47, 5, 0, 0, 187, 4, 0, 0, 188, 4, 0, 0, 55, 5, 0, 0, 179, 4, 0, 0, 11, 4, 0, 0, 59, 5, 0, 0, 196, 4, 0, 0, 179, 4, 0, 0, 187, 4, 0, 0, 148, 3, 0, 0, 67, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 73, 5, 0, 0, 81, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 85, 5, 0, 0, 93, 5, 0, 0, 148, 3, 0, 0, 97, 5, 0, 0, 104, 5, 0, 0,
148, 3, 0, 0, 112, 5, 0, 0, 120, 5, 0, 0, 127, 5, 0, 0, 8, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 135, 5, 0, 0, 143, 5, 0, 0, 151, 5, 0, 0, 159, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 167, 5, 0, 0, 148, 3, 0, 0, 175, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 183, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 191, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 199, 5, 0, 0, 200, 4, 0, 0, 200, 4, 0, 0, 200, 4, 0, 0, 148, 3, 0, 0, 205, 5, 0, 0, 213, 5, 0, 0, 175, 5, 0, 0, 221, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 228, 5, 0, 0,
185, 4, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 236, 5, 0, 0, 244, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 246, 5, 0, 0, 231, 9, 0, 0, 254, 5, 0, 0, 148, 3, 0, 0, 5, 6, 0, 0, 13, 6, 0, 0, 148, 3, 0, 0, 21, 6, 0, 0, 27, 11, 0, 0, 148, 3, 0, 0, 248, 4, 0, 0, 29, 6, 0, 0, 9, 5, 0, 0, 37, 6, 0, 0, 11, 4, 0, 0, 45, 6, 0, 0,
148, 3, 0, 0, 52, 6, 0, 0, 148, 3, 0, 0, 57, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 63, 6, 0, 0, 71, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 78, 6, 0, 0, 86, 6, 0, 0, 90, 6, 0, 0, 98, 6, 0, 0, 163, 9, 0, 0, 223, 9, 0, 0, 106, 6, 0, 0, 114, 6, 0, 0, 171, 9, 0, 0, 175, 9, 0, 0, 130, 5, 0, 0, 122, 6, 0, 0, 130, 6, 0, 0, 138, 6, 0, 0, 148, 3, 0, 0, 146, 6, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0,
147, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 237, 9, 0, 0, 152, 6, 0, 0, 148, 3, 0, 0, 158, 6, 0, 0, 165, 6, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 11, 7, 0, 0, 243, 9, 0, 0, 231, 9, 0, 0, 171, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 178, 6, 0, 0, 231, 9, 0, 0,
231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 183, 6, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 251, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 2, 10, 0, 0, 9, 10, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 17, 10, 0, 0, 231, 9, 0, 0, 24, 10, 0, 0, 31, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0,
39, 10, 0, 0, 45, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 191, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 199, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 226, 3, 0, 0, 231, 9, 0, 0, 53, 10, 0, 0, 56, 10, 0, 0, 148, 3, 0, 0,
64, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 71, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 79, 10, 0, 0, 85, 10, 0, 0, 207, 6, 0, 0, 215, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 223, 6, 0, 0, 183, 5, 0, 0, 148, 3, 0, 0, 187, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 231, 9, 0, 0, 198, 6, 0, 0, 201, 3, 0, 0, 148, 3, 0, 0, 231, 6, 0, 0, 239, 6, 0, 0, 148, 3, 0, 0, 247, 6, 0, 0, 255, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 3, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 246, 5, 0, 0, 186, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
231, 9, 0, 0, 231, 9, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 231, 6, 0, 0, 231, 9, 0, 0, 11, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 16, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 21, 7, 0, 0, 29, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 99, 5, 0, 0, 231, 9, 0, 0, 245, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 37, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 45, 7, 0, 0, 52, 7, 0, 0, 148, 3, 0, 0,
59, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 57, 5, 0, 0, 67, 7, 0, 0, 148, 3, 0, 0, 75, 7, 0, 0, 82, 7, 0, 0, 148, 3, 0, 0, 161, 4, 0, 0, 87, 7, 0, 0, 148, 3, 0, 0, 186, 4, 0, 0, 148, 3, 0, 0, 95, 7, 0, 0, 103, 7, 0, 0, 188, 4, 0, 0, 148, 3, 0, 0, 107, 7, 0, 0, 187, 4, 0, 0, 115, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 251, 5, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 122, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 126, 7, 0, 0, 42, 9, 0, 0, 46, 9, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0,
62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 93, 10, 0, 0, 101, 10, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 106, 10, 0, 0, 110, 10, 0, 0, 118, 10, 0, 0, 179, 9, 0, 0, 183, 9, 0, 0, 155, 9, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 246, 10, 0, 0, 115, 9, 0, 0, 134, 7, 0, 0, 142, 7, 0, 0, 150, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
191, 9, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 144, 14, 0, 0, 208, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 8, 15, 0, 0, 72, 15, 0, 0, 136, 15, 0, 0, 152, 15, 0, 0, 216, 15, 0, 0, 228, 15, 0, 0, 80, 14, 0, 0,
80, 14, 0, 0, 36, 16, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 80, 14, 0, 0, 92, 16, 0, 0, 156, 16, 0, 0, 220, 16, 0, 0, 20, 17, 0, 0, 72, 17, 0, 0, 116, 17, 0, 0, 176, 17, 0, 0, 232, 17, 0, 0, 4, 18, 0, 0, 68, 18, 0, 0, 32, 10, 0, 0, 192, 12, 0, 0, 96, 10, 0, 0, 159, 10, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 223, 10, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 31, 11, 0, 0, 160, 1, 0, 0, 69, 11, 0, 0, 128, 11, 0, 0, 192, 11, 0, 0, 0, 12, 0, 0, 253, 12, 0, 0, 64, 12, 0, 0, 61, 13, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 125, 13, 0, 0, 141, 13, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0,
160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 160, 1, 0, 0, 128, 12, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 158, 7, 0, 0, 148, 3, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 166, 7, 0, 0, 183, 5, 0, 0, 148, 3, 0, 0, 180, 4, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 123, 9, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 174, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 187, 4, 0, 0, 181, 7, 0, 0, 188, 7, 0, 0, 195, 7, 0, 0, 11, 4, 0, 0, 203, 7, 0, 0, 9, 5, 0, 0, 148, 3, 0, 0, 161, 4, 0, 0, 210, 7, 0, 0, 148, 3, 0, 0, 216, 7, 0, 0, 11, 4, 0, 0, 221, 7, 0, 0, 229, 7, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 234, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 199, 6, 0, 0, 242, 7, 0, 0, 11, 4, 0, 0, 59, 5, 0, 0, 30, 5, 0, 0, 249, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 181, 7, 0, 0, 1, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 9, 8, 0, 0, 17, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 21, 8, 0, 0, 29, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 37, 8, 0, 0, 30, 5, 0, 0, 170, 7, 0, 0, 148, 3, 0, 0, 45, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 167, 5, 0, 0, 53, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 58, 8, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 65, 8, 0, 0, 73, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 76, 8, 0, 0, 30, 5, 0, 0, 84, 8, 0, 0, 88, 8, 0, 0, 96, 8, 0, 0, 148, 3, 0, 0, 103, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 110, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 118, 8, 0, 0, 124, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 130, 8, 0, 0, 138, 8, 0, 0, 148, 3, 0, 0, 142, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 61, 5, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 8, 0, 0, 156, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 161, 8, 0, 0, 148, 3, 0, 0, 167, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 217, 7, 0, 0, 148, 3, 0, 0, 173, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 181, 8, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 213, 4, 0, 0, 254, 10, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 226, 3, 0, 0, 35, 11, 0, 0, 189, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 196, 8, 0, 0, 204, 8, 0, 0, 210, 8, 0, 0, 148, 3, 0, 0, 216, 8, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 126, 10, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 240, 9, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 187, 3, 0, 0, 148, 3, 0, 0, 153, 7, 0, 0, 148, 3, 0, 0, 182, 3, 0, 0, 148, 3, 0, 0, 156, 7, 0, 0, 148, 3, 0, 0, 224, 8, 0, 0, 131, 9, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 226, 3, 0, 0, 232, 8, 0, 0, 226, 3, 0, 0, 239, 8, 0, 0, 246, 8, 0, 0, 43, 11, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
51, 11, 0, 0, 59, 11, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 167, 8, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 5, 8, 0, 0, 148, 3, 0, 0, 254, 8, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 231, 9, 0, 0, 142, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 71, 10, 0, 0, 147, 10, 0, 0, 151, 10, 0, 0, 79, 10, 0, 0, 6, 9, 0, 0, 182, 3, 0, 0, 148, 3, 0, 0,
12, 9, 0, 0, 148, 3, 0, 0, 155, 7, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 156, 6, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0,
231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 156, 10, 0, 0, 166, 7, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 71, 10, 0, 0, 231, 9, 0, 0,
231, 9, 0, 0, 164, 10, 0, 0, 172, 10, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 16, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0,
54, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 62, 9, 0, 0, 66, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 74, 9, 0, 0, 41, 4, 0, 0, 76, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0,
41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 62, 4, 0, 0, 215, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 84, 9, 0, 0, 41, 4, 0, 0, 92, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 46, 9, 0, 0,
137, 4, 0, 0, 50, 9, 0, 0, 100, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 104, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 107, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0,
41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 46, 9, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 50, 9, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0,
41, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 62, 4, 0, 0, 134, 10, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 41, 4, 0, 0, 142, 10, 0, 0, 231, 9, 0, 0, 180, 10, 0, 0, 231, 9, 0, 0, 188, 10, 0, 0, 193, 10, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 71, 10, 0, 0, 201, 10, 0, 0, 209, 10, 0, 0, 214, 10, 0, 0, 222, 10, 0, 0, 230, 10, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 231, 9, 0, 0, 238, 10, 0, 0, 231, 9, 0, 0, 243, 9, 0, 0, 139, 9, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0,
148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 148, 3, 0, 0, 16, 9, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 226, 3, 0, 0, 14, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0,
6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0,
6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0,
6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 6, 11, 0, 0, 115, 3, 1, 0, 115, 3, 1, 0, 115, 3, 1, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 84, 0, 0, 0, 12, 0,
0, 0, 84, 0, 0, 0, 88, 0, 0, 0, 12, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 84, 0, 0, 0, 88, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0,
0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 41, 0, 58, 0, 40, 0, 57, 0, 0, 0, 56, 0, 0, 0, 28, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 20, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 58, 0, 0, 0, 56, 0, 91, 0, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 58, 0, 0, 0, 56, 0, 123, 0, 57, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 12, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 20, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 4, 0, 0, 0, 20, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 8, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 59, 15, 58, 0, 58, 15, 57, 0, 61, 15, 58, 0, 60, 15, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 22, 58, 0, 155, 22, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0, 0, 0, 88, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 44, 0, 0, 0, 76, 0, 0, 0, 36, 0, 0, 0, 64, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 56, 0,
126, 32, 58, 0, 125, 32, 57, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 56, 0, 142, 32, 58, 0, 141, 32, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 42, 35, 58, 0, 41, 35, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 88, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48, 58, 0, 8, 48, 57, 0, 11, 48, 58, 0, 10, 48, 57, 0, 13, 48, 58, 0, 12, 48, 57, 0, 15, 48, 58, 0, 14, 48, 57, 0, 17, 48, 58, 0, 16, 48, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 21, 48, 58, 0, 20, 48, 57, 0, 23, 48, 58, 0, 22, 48, 57, 0, 25, 48, 58, 0,
24, 48, 57, 0, 27, 48, 58, 0, 26, 48, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 58, 0, 0, 0, 56, 0, 59, 255, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 58, 0, 0, 0, 56, 0, 91, 255, 57, 0, 0, 0, 56, 0, 96, 255, 58, 0, 95, 255, 57, 0, 0, 0, 56, 0, 99, 255, 58, 0, 98, 255, 57, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 28, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0,
0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0,
0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 9, 255, 58, 0, 8, 255, 57, 0, 0, 0, 56, 0, 0, 0, 28, 0, 0, 0, 20, 0, 0, 0, 28, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 20, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 24, 0,
0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 28, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 88, 0, 0, 0, 12, 0, 0, 0, 40, 0, 0, 0, 72, 0, 0, 0, 60, 0, 0, 0, 48, 0, 0, 0, 80, 0, 0, 0, 20, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0,
0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 20, 0, 0, 0, 56, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 20, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 90, 254, 58, 0, 89, 254, 57, 0, 92, 254, 58, 0, 91, 254, 57, 0, 94, 254, 58, 0, 93, 254, 57, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 8, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0,
0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 20, 0, 70, 32, 58, 0, 69, 32, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 88, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
9, 35, 58, 0, 8, 35, 57, 0, 11, 35, 58, 0, 10, 35, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 105, 39, 58, 0, 104, 39, 57, 0, 107, 39, 58, 0, 106, 39, 57, 0, 109, 39, 58, 0, 108, 39, 57, 0, 111, 39, 58, 0, 110, 39, 57, 0, 113, 39, 58, 0, 112, 39, 57, 0, 115, 39, 58, 0, 114, 39, 57, 0, 117, 39, 58, 0, 116, 39, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 198, 39, 58, 0, 197, 39, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 231, 39, 58, 0, 230, 39, 57, 0, 233, 39, 58, 0, 232, 39, 57, 0, 235, 39, 58, 0, 234, 39, 57, 0, 237, 39, 58, 0, 236, 39, 57, 0, 239, 39, 58, 0, 238, 39, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 132, 41, 58, 0, 131, 41, 57, 0, 134, 41, 58, 0, 133, 41, 57, 0, 136, 41, 58, 0, 135, 41, 57, 0, 138, 41, 58, 0, 137, 41, 57, 0, 140, 41, 58, 0, 139, 41, 57, 0, 144, 41, 58, 0,
143, 41, 57, 0, 142, 41, 58, 0, 141, 41, 57, 0, 146, 41, 58, 0, 145, 41, 57, 0, 148, 41, 58, 0, 147, 41, 57, 0, 150, 41, 58, 0, 149, 41, 57, 0, 152, 41, 58, 0, 151, 41, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 217, 41, 58, 0, 216, 41, 57, 0, 219, 41, 58, 0, 218, 41, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 253, 41, 58, 0, 252, 41, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 35, 46, 58, 0, 34, 46, 57, 0, 37, 46, 58, 0, 36, 46, 57, 0, 39, 46, 58, 0, 38, 46, 57, 0, 41, 46, 58, 0, 40, 46, 57, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 86, 46, 58, 0, 85, 46, 57, 0, 88, 46, 58, 0, 87, 46, 57, 0, 90, 46, 58, 0, 89, 46, 57, 0, 92, 46, 58, 0, 91, 46, 57, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 56, 0,
0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0,
0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
}
}

28
src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs

@ -66,7 +66,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// The forward mapping maps the start index to the end index. /// The forward mapping maps the start index to the end index.
/// The reverse mapping maps the end index to the start index. /// The reverse mapping maps the end index to the start index.
/// </remarks> /// </remarks>
private readonly Dictionary<int, int> _isolatePairs = new Dictionary<int, int>(); private readonly BidiDictionary<int, int> _isolatePairs = new BidiDictionary<int, int>();
/// <summary> /// <summary>
/// The working BiDi classes /// The working BiDi classes
@ -188,12 +188,6 @@ namespace Avalonia.Media.TextFormatting.Unicode
{ {
} }
/// <summary>
/// Gets a per-thread instance that can be re-used as often
/// as necessary.
/// </summary>
public static ThreadLocal<BidiAlgorithm> Instance { get; } = new ThreadLocal<BidiAlgorithm>(() => new BidiAlgorithm());
/// <summary> /// <summary>
/// Gets the resolved levels. /// Gets the resolved levels.
/// </summary> /// </summary>
@ -1414,35 +1408,37 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// Sets the direction of a bracket pair, including setting the direction of /// Sets the direction of a bracket pair, including setting the direction of
/// NSM's inside the brackets and following. /// NSM's inside the brackets and following.
/// </summary> /// </summary>
/// <param name="bracketPair">The paired brackets</param> /// <param name="pairedBracket">The paired brackets</param>
/// <param name="direction">The resolved direction for the bracket pair</param> /// <param name="direction">The resolved direction for the bracket pair</param>
private void SetPairedBracketDirection(in BracketPair bracketPair, BidiClass direction) private void SetPairedBracketDirection(in BracketPair pairedBracket, BidiClass direction)
{ {
// Set the direction of the brackets // Set the direction of the brackets
_runResolvedClasses[bracketPair.OpeningIndex] = direction; _runResolvedClasses[pairedBracket.OpeningIndex] = direction;
_runResolvedClasses[bracketPair.ClosingIndex] = direction; _runResolvedClasses[pairedBracket.ClosingIndex] = direction;
// Set the directionality of NSM's inside the brackets // Set the directionality of NSM's inside the brackets
for (var i = bracketPair.OpeningIndex + 1; i < bracketPair.ClosingIndex; i++) // BN characters (such as ZWJ or ZWSP) that appear between the base bracket character
// and the nonspacing mark should be ignored.
for (int i = pairedBracket.OpeningIndex + 1; i < pairedBracket.ClosingIndex; i++)
{ {
if (_runOriginalClasses[i] == BidiClass.NonspacingMark) if (_runOriginalClasses[i] == BidiClass.NonspacingMark)
{ {
_runOriginalClasses[i] = direction; _runOriginalClasses[i] = direction;
} }
else else if (_runOriginalClasses[i] != BidiClass.BoundaryNeutral)
{ {
break; break;
} }
} }
// Set the directionality of NSM's following the brackets // Set the directionality of NSM's following the brackets
for (var i = bracketPair.ClosingIndex + 1; i < _runLength; i++) for (int i = pairedBracket.ClosingIndex + 1; i < _runLength; i++)
{ {
if (_runOriginalClasses[i] == BidiClass.NonspacingMark) if (_runOriginalClasses[i] == BidiClass.NonspacingMark)
{ {
_runResolvedClasses[i] = direction; _runOriginalClasses[i] = direction;
} }
else else if (_runOriginalClasses[i] != BidiClass.BoundaryNeutral)
{ {
break; break;
} }

25
src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs

@ -18,17 +18,12 @@ namespace Avalonia.Media.TextFormatting.Unicode
private ArrayBuilder<BidiClass> _savedClasses; private ArrayBuilder<BidiClass> _savedClasses;
private ArrayBuilder<BidiPairedBracketType> _savedPairedBracketTypes; private ArrayBuilder<BidiPairedBracketType> _savedPairedBracketTypes;
private ArrayBuilder<sbyte> _tempLevelBuffer; private ArrayBuilder<sbyte> _tempLevelBuffer;
public BidiData(sbyte paragraphEmbeddingLevel = 0) public BidiData(sbyte paragraphEmbeddingLevel)
{ {
ParagraphEmbeddingLevel = paragraphEmbeddingLevel; ParagraphEmbeddingLevel = paragraphEmbeddingLevel;
} }
public BidiData(ReadOnlySlice<char> text, sbyte paragraphEmbeddingLevel = 0) : this(paragraphEmbeddingLevel)
{
Append(text);
}
public sbyte ParagraphEmbeddingLevel { get; private set; } public sbyte ParagraphEmbeddingLevel { get; private set; }
public bool HasBrackets { get; private set; } public bool HasBrackets { get; private set; }
@ -64,13 +59,23 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// </remarks> /// </remarks>
public ArraySlice<int> PairedBracketValues { get; private set; } public ArraySlice<int> PairedBracketValues { get; private set; }
/// <summary>
/// Appends text to the bidi data.
/// </summary>
/// <param name="text">The text to process.</param>
public void Append(ReadOnlySlice<char> text) public void Append(ReadOnlySlice<char> text)
{ {
_classes.Add(text.Length); _classes.Add(text.Length);
_pairedBracketTypes.Add(text.Length); _pairedBracketTypes.Add(text.Length);
_pairedBracketValues.Add(text.Length); _pairedBracketValues.Add(text.Length);
var i = Length; // Resolve the BidiCharacterType, paired bracket type and paired
// bracket values for all code points
HasBrackets = false;
HasEmbeddings = false;
HasIsolates = false;
int i = Length;
var codePointEnumerator = new CodepointEnumerator(text); var codePointEnumerator = new CodepointEnumerator(text);
@ -115,13 +120,13 @@ namespace Avalonia.Media.TextFormatting.Unicode
// Opening bracket types can never have a null pairing. // Opening bracket types can never have a null pairing.
codepoint.TryGetPairedBracket(out var paired); codepoint.TryGetPairedBracket(out var paired);
_pairedBracketValues[i] = Codepoint.GetCanonicalType(paired).Value; _pairedBracketValues[i] = (int)Codepoint.GetCanonicalType(paired).Value;
HasBrackets = true; HasBrackets = true;
} }
else if (pbt == BidiPairedBracketType.Close) else if (pbt == BidiPairedBracketType.Close)
{ {
_pairedBracketValues[i] = Codepoint.GetCanonicalType(codepoint).Value; _pairedBracketValues[i] = (int)Codepoint.GetCanonicalType(codepoint).Value;
HasBrackets = true; HasBrackets = true;
} }

44
src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs

@ -5,50 +5,52 @@ namespace Avalonia.Media.TextFormatting.Unicode
{ {
public readonly struct Codepoint public readonly struct Codepoint
{ {
private readonly uint _value;
/// <summary> /// <summary>
/// The replacement codepoint that is used for non supported values. /// The replacement codepoint that is used for non supported values.
/// </summary> /// </summary>
public static readonly Codepoint ReplacementCodepoint = new Codepoint('\uFFFD'); public static readonly Codepoint ReplacementCodepoint = new Codepoint('\uFFFD');
public Codepoint(int value) public Codepoint(uint value)
{ {
Value = value; _value = value;
} }
/// <summary> /// <summary>
/// Get the codepoint's value. /// Get the codepoint's value.
/// </summary> /// </summary>
public int Value { get; } public uint Value => _value;
/// <summary> /// <summary>
/// Gets the <see cref="Unicode.GeneralCategory"/>. /// Gets the <see cref="Unicode.GeneralCategory"/>.
/// </summary> /// </summary>
public GeneralCategory GeneralCategory => UnicodeData.GetGeneralCategory(Value); public GeneralCategory GeneralCategory => UnicodeData.GetGeneralCategory(_value);
/// <summary> /// <summary>
/// Gets the <see cref="Unicode.Script"/>. /// Gets the <see cref="Unicode.Script"/>.
/// </summary> /// </summary>
public Script Script => UnicodeData.GetScript(Value); public Script Script => UnicodeData.GetScript(_value);
/// <summary> /// <summary>
/// Gets the <see cref="Unicode.BidiClass"/>. /// Gets the <see cref="Unicode.BidiClass"/>.
/// </summary> /// </summary>
public BidiClass BiDiClass => UnicodeData.GetBiDiClass(Value); public BidiClass BiDiClass => UnicodeData.GetBiDiClass(_value);
/// <summary> /// <summary>
/// Gets the <see cref="Unicode.BidiPairedBracketType"/>. /// Gets the <see cref="Unicode.BidiPairedBracketType"/>.
/// </summary> /// </summary>
public BidiPairedBracketType PairedBracketType => UnicodeData.GetBiDiPairedBracketType(Value); public BidiPairedBracketType PairedBracketType => UnicodeData.GetBiDiPairedBracketType(_value);
/// <summary> /// <summary>
/// Gets the <see cref="Unicode.LineBreakClass"/>. /// Gets the <see cref="Unicode.LineBreakClass"/>.
/// </summary> /// </summary>
public LineBreakClass LineBreakClass => UnicodeData.GetLineBreakClass(Value); public LineBreakClass LineBreakClass => UnicodeData.GetLineBreakClass(_value);
/// <summary> /// <summary>
/// Gets the <see cref="GraphemeBreakClass"/>. /// Gets the <see cref="GraphemeBreakClass"/>.
/// </summary> /// </summary>
public GraphemeBreakClass GraphemeBreakClass => UnicodeData.GetGraphemeClusterBreak(Value); public GraphemeBreakClass GraphemeBreakClass => UnicodeData.GetGraphemeClusterBreak(_value);
/// <summary> /// <summary>
/// Determines whether this <see cref="Codepoint"/> is a break char. /// Determines whether this <see cref="Codepoint"/> is a break char.
@ -60,7 +62,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
{ {
get get
{ {
switch (Value) switch (_value)
{ {
case '\u000A': case '\u000A':
case '\u000B': case '\u000B':
@ -109,12 +111,12 @@ namespace Avalonia.Media.TextFormatting.Unicode
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Codepoint GetCanonicalType(Codepoint codePoint) internal static Codepoint GetCanonicalType(Codepoint codePoint)
{ {
if (codePoint.Value == 0x3008) if (codePoint._value == 0x3008)
{ {
return new Codepoint(0x2329); return new Codepoint(0x2329);
} }
if (codePoint.Value == 0x3009) if (codePoint._value == 0x3009)
{ {
return new Codepoint(0x232A); return new Codepoint(0x232A);
} }
@ -141,19 +143,19 @@ namespace Avalonia.Media.TextFormatting.Unicode
return false; return false;
} }
codepoint = UnicodeData.GetBiDiPairedBracket(Value); codepoint = UnicodeData.GetBiDiPairedBracket(_value);
return true; return true;
} }
public static implicit operator int(Codepoint codepoint) public static implicit operator int(Codepoint codepoint)
{ {
return codepoint.Value; return (int)codepoint._value;
} }
public static implicit operator uint(Codepoint codepoint) public static implicit operator uint(Codepoint codepoint)
{ {
return (uint)codepoint.Value; return codepoint._value;
} }
/// <summary> /// <summary>
@ -191,7 +193,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
if (0xDC00 <= low && low <= 0xDFFF) if (0xDC00 <= low && low <= 0xDFFF)
{ {
count = 2; count = 2;
return new Codepoint((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000); return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
} }
return ReplacementCodepoint; return ReplacementCodepoint;
@ -212,7 +214,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
if (0xD800 <= hi && hi <= 0xDBFF) if (0xD800 <= hi && hi <= 0xDBFF)
{ {
count = 2; count = 2;
return new Codepoint((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000); return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
} }
return ReplacementCodepoint; return ReplacementCodepoint;
@ -220,5 +222,13 @@ namespace Avalonia.Media.TextFormatting.Unicode
return new Codepoint(code); return new Codepoint(code);
} }
/// <summary>
/// Returns <see langword="true"/> if <paramref name="value"/> is between
/// <paramref name="lowerBound"/> and <paramref name="upperBound"/>, inclusive.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInRangeInclusive(Codepoint cp, uint lowerBound, uint upperBound)
=> (cp._value - lowerBound) <= (upperBound - lowerBound);
} }
} }

7
src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs

@ -0,0 +1,7 @@
namespace Avalonia.Media.TextFormatting.Unicode
{
internal static class GraphemeBreak
{
public static byte[] Data => new byte[0];
}
}

409
src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs

@ -0,0 +1,409 @@
using System;
namespace Avalonia.Media.TextFormatting.Unicode
{
internal static class GraphemeBreakTrie
{
public static ReadOnlySpan<byte> Data => new byte[]
{
0, 16, 14, 0, 0, 0, 0, 0, 144, 155, 0, 0, 89, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 113, 3, 0, 0, 137, 3, 0, 0, 145, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0,
97, 3, 0, 0, 105, 3, 0, 0, 153, 3, 0, 0, 161, 3, 0, 0, 157, 3, 0, 0, 165, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 173, 3, 0, 0, 181, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 185, 3, 0, 0, 193, 3, 0, 0, 201, 3, 0, 0,
209, 3, 0, 0, 217, 3, 0, 0, 225, 3, 0, 0, 231, 3, 0, 0, 239, 3, 0, 0, 97, 3, 0, 0, 105, 3, 0, 0, 244, 3, 0, 0, 252, 3, 0, 0, 1, 4, 0, 0, 9, 4, 0, 0, 15, 4, 0, 0, 23, 4, 0, 0, 22, 4, 0, 0, 30, 4, 0, 0, 35, 4, 0, 0, 43, 4, 0, 0, 176, 4, 0, 0, 183, 4, 0, 0, 187, 4, 0, 0, 97, 3, 0, 0, 51, 4, 0, 0, 97, 3, 0, 0, 194, 4, 0, 0, 59, 4, 0, 0,
202, 4, 0, 0, 204, 4, 0, 0, 212, 4, 0, 0, 220, 4, 0, 0, 228, 4, 0, 0, 229, 4, 0, 0, 237, 4, 0, 0, 245, 4, 0, 0, 253, 4, 0, 0, 254, 4, 0, 0, 6, 5, 0, 0, 11, 5, 0, 0, 253, 4, 0, 0, 254, 4, 0, 0, 19, 5, 0, 0, 27, 5, 0, 0, 228, 4, 0, 0, 35, 5, 0, 0, 43, 5, 0, 0, 220, 4, 0, 0, 51, 5, 0, 0, 203, 4, 0, 0, 59, 5, 0, 0, 97, 3, 0, 0, 67, 5, 0, 0,
35, 5, 0, 0, 75, 5, 0, 0, 220, 4, 0, 0, 228, 4, 0, 0, 81, 5, 0, 0, 89, 5, 0, 0, 220, 4, 0, 0, 97, 5, 0, 0, 99, 5, 0, 0, 67, 4, 0, 0, 220, 4, 0, 0, 228, 4, 0, 0, 97, 3, 0, 0, 107, 5, 0, 0, 88, 8, 0, 0, 97, 3, 0, 0, 115, 5, 0, 0, 122, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 126, 5, 0, 0, 134, 5, 0, 0, 97, 3, 0, 0, 138, 5, 0, 0, 145, 5, 0, 0,
97, 3, 0, 0, 153, 5, 0, 0, 161, 5, 0, 0, 168, 5, 0, 0, 50, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 176, 5, 0, 0, 184, 5, 0, 0, 192, 5, 0, 0, 200, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 124, 8, 0, 0, 124, 8, 0, 0, 124, 8, 0, 0, 133, 8, 0, 0, 133, 8, 0, 0, 139, 8, 0, 0, 157, 8, 0, 0, 157, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 182, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 208, 5, 0, 0, 214, 5, 0, 0, 23, 5, 0, 0, 23, 5, 0, 0, 97, 3, 0, 0, 220, 5, 0, 0, 228, 5, 0, 0, 97, 3, 0, 0, 127, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 79, 5, 0, 0,
233, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 241, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 248, 5, 0, 0, 97, 3, 0, 0, 255, 5, 0, 0, 7, 6, 0, 0, 97, 3, 0, 0, 149, 3, 0, 0, 14, 4, 0, 0, 97, 3, 0, 0, 15, 6, 0, 0, 18, 6, 0, 0, 26, 6, 0, 0, 32, 6, 0, 0, 40, 6, 0, 0, 48, 6, 0, 0,
97, 3, 0, 0, 55, 6, 0, 0, 97, 3, 0, 0, 62, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 68, 6, 0, 0, 76, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 133, 4, 0, 0, 139, 4, 0, 0, 94, 6, 0, 0, 93, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 149, 3, 0, 0, 171, 5, 0, 0, 97, 3, 0, 0, 180, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 187, 8, 0, 0, 194, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 197, 8, 0, 0, 204, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 204, 8, 0, 0, 97, 3, 0, 0, 209, 8, 0, 0, 215, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 223, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 229, 8, 0, 0, 237, 8, 0, 0, 239, 8, 0, 0, 247, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 3, 9, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 10, 9, 0, 0, 17, 9, 0, 0, 24, 9, 0, 0, 32, 9, 0, 0, 35, 9, 0, 0, 43, 9, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 51, 9, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 58, 9, 0, 0, 97, 3, 0, 0, 66, 9, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 83, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 226, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 153, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 89, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 72, 9, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 104, 6, 0, 0, 24, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 140, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 112, 6, 0, 0, 120, 6, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 237, 7, 0, 0, 93, 8, 0, 0, 128, 6, 0, 0, 136, 6, 0, 0, 97, 3, 0, 0, 144, 6, 0, 0, 151, 6, 0, 0, 125, 8, 0, 0, 202, 4, 0, 0, 156, 6, 0, 0, 101, 8, 0, 0, 164, 6, 0, 0, 97, 3, 0, 0, 170, 6, 0, 0, 178, 6, 0, 0, 182, 6, 0, 0, 97, 3, 0, 0, 190, 6, 0, 0, 5, 4, 0, 0, 198, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 206, 6, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0,
166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0,
170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0,
167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0,
171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0,
168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0,
172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0,
169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0,
166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0,
170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0,
167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0,
171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0,
168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0,
172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0, 169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 172, 8, 0, 0, 166, 8, 0, 0, 167, 8, 0, 0, 168, 8, 0, 0,
169, 8, 0, 0, 170, 8, 0, 0, 171, 8, 0, 0, 147, 8, 0, 0, 154, 8, 0, 0, 158, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 210, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 165, 3, 0, 0, 165, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 113, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 24, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
147, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 228, 13, 0, 0, 228, 13, 0, 0, 36, 14, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 100, 14, 0, 0, 116, 14, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0,
132, 13, 0, 0, 180, 14, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 132, 13, 0, 0, 228, 14, 0, 0, 36, 15, 0, 0, 100, 15, 0, 0, 156, 15, 0, 0, 132, 13, 0, 0, 208, 15, 0, 0, 4, 16, 0, 0, 60, 16, 0, 0, 88, 16, 0, 0, 140, 16, 0, 0, 64, 11, 0, 0, 112, 11, 0, 0, 226, 9, 0, 0, 33, 10, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 89, 10, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 173, 11, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 123, 10, 0, 0, 149, 1, 0, 0, 237, 11, 0, 0, 176, 10, 0, 0, 40, 12, 0, 0, 104, 12, 0, 0, 162, 12, 0, 0, 226, 12, 0, 0, 34, 13, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0,
149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 149, 1, 0, 0, 240, 10, 0, 0, 0, 11, 0, 0, 6, 7, 0, 0, 7, 4, 0, 0, 16, 4, 0, 0, 10, 7, 0, 0, 40, 6, 0, 0, 73, 4, 0, 0, 81, 4, 0, 0, 97, 3, 0, 0, 17, 4, 0, 0, 16, 7, 0, 0, 108, 8, 0, 0, 22, 7, 0, 0, 40, 6, 0, 0, 27, 7, 0, 0, 89, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
35, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 226, 3, 0, 0, 43, 7, 0, 0, 97, 5, 0, 0, 99, 5, 0, 0, 51, 7, 0, 0, 59, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 65, 7, 0, 0, 73, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 81, 7, 0, 0, 89, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 94, 7, 0, 0, 102, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 110, 7, 0, 0, 34, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 118, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 182, 3, 0, 0, 126, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 131, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 93, 4, 0, 0, 101, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 138, 7, 0, 0, 146, 7, 0, 0, 154, 7, 0, 0, 105, 4, 0, 0, 161, 7, 0, 0, 97, 3, 0, 0, 113, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 168, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 176, 7, 0, 0, 182, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 188, 7, 0, 0, 121, 4, 0, 0, 97, 3, 0, 0, 196, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 202, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 154, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 238, 7, 0, 0, 96, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
161, 4, 0, 0, 254, 7, 0, 0, 252, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 6, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 137, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 153, 3, 0, 0, 168, 4, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0,
137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0,
137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0,
137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 137, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 218, 6, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 34, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 226, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 234, 6, 0, 0, 238, 6, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 79, 5, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 236, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 246, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 30, 4, 0, 0, 97, 3, 0, 0, 254, 6, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 208, 7, 0, 0, 97, 3, 0, 0, 214, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
220, 7, 0, 0, 116, 8, 0, 0, 226, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 233, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 153, 3, 0, 0, 246, 7, 0, 0, 16, 4, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 153, 3, 0, 0, 14, 8, 0, 0, 153, 3, 0, 0, 21, 8, 0, 0, 28, 8, 0, 0, 36, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 44, 8, 0, 0, 52, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 214, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
214, 6, 0, 0, 97, 3, 0, 0, 57, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 214, 7, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 64, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0,
97, 3, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 79, 9, 0, 0, 209, 8, 0, 0, 97, 3, 0, 0, 84, 9, 0, 0, 92, 9, 0, 0, 99, 9, 0, 0, 252, 8, 0, 0, 80, 8, 0, 0, 107, 9, 0, 0, 114, 9, 0, 0, 122, 9, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0,
252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 72, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 253, 8, 0, 0, 130, 9, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0,
252, 8, 0, 0, 134, 9, 0, 0, 97, 3, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 67, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 138, 9, 0, 0, 252, 8, 0, 0, 146, 9, 0, 0, 97, 3, 0, 0, 152, 9, 0, 0, 97, 3, 0, 0, 160, 9, 0, 0, 165, 9, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 69, 8, 0, 0, 169, 9, 0, 0,
176, 9, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 97, 3, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0,
252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0,
252, 8, 0, 0, 252, 8, 0, 0, 252, 8, 0, 0, 253, 8, 0, 0, 88, 3, 1, 0, 88, 3, 1, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0,
3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0,
3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0,
3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0,
3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0,
6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0,
9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0,
10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0,
11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,
10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0,
11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0,
11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0,
13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0,
13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0,
14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
}
}

1
src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs

@ -35,7 +35,6 @@ namespace Avalonia.Media.TextFormatting.Unicode
EModifier, //EM EModifier, //EM
ZWJ, //ZWJ ZWJ, //ZWJ
ContingentBreak, //CB ContingentBreak, //CB
Unknown, //XX Unknown, //XX
Ambiguous, //AI Ambiguous, //AI
MandatoryBreak, //BK MandatoryBreak, //BK

14
src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs

@ -450,6 +450,20 @@ namespace Avalonia.Media.TextFormatting.Unicode
_lb30a = 0; _lb30a = 0;
} }
// Rule LB30b
if (_nextClass == LineBreakClass.EModifier && _lastPosition > 0)
{
// Mahjong Tiles (Unicode block) are extended pictographics but have a class of ID
// Unassigned codepoints with Line_Break=ID in some blocks are also assigned the Extended_Pictographic property.
// Those blocks are intended for future allocation of emoji characters.
var cp = Codepoint.ReadAt(_text, _lastPosition - 1, out int _);
if (Codepoint.IsInRangeInclusive(cp, 0x1F000, 0x1F02F))
{
shouldBreak = false;
}
}
_currentClass = _nextClass; _currentClass = _nextClass;
return shouldBreak; return shouldBreak;

5
src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs

@ -30,6 +30,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
Cherokee, //Cher Cherokee, //Cher
Chorasmian, //Chrs Chorasmian, //Chrs
Coptic, //Copt Coptic, //Copt
CyproMinoan, //Cpmn
Cypriot, //Cprt Cypriot, //Cprt
Cyrillic, //Cyrl Cyrillic, //Cyrl
Devanagari, //Deva Devanagari, //Deva
@ -109,6 +110,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
Oriya, //Orya Oriya, //Orya
Osage, //Osge Osage, //Osge
Osmanya, //Osma Osmanya, //Osma
OldUyghur, //Ougr
Palmyrene, //Palm Palmyrene, //Palm
PauCinHau, //Pauc PauCinHau, //Pauc
OldPermic, //Perm OldPermic, //Perm
@ -151,8 +153,11 @@ namespace Avalonia.Media.TextFormatting.Unicode
Thai, //Thai Thai, //Thai
Tibetan, //Tibt Tibetan, //Tibt
Tirhuta, //Tirh Tirhuta, //Tirh
Tangsa, //Tnsa
Toto, //Toto
Ugaritic, //Ugar Ugaritic, //Ugar
Vai, //Vaii Vai, //Vaii
Vithkuqi, //Vith
WarangCiti, //Wara WarangCiti, //Wara
Wancho, //Wcho Wancho, //Wcho
OldPersian, //Xpeo OldPersian, //Xpeo

35
src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices; using System.IO;
using System.Runtime.CompilerServices;
namespace Avalonia.Media.TextFormatting.Unicode namespace Avalonia.Media.TextFormatting.Unicode
{ {
@ -17,14 +18,14 @@ namespace Avalonia.Media.TextFormatting.Unicode
internal const int SCRIPT_SHIFT = CATEGORY_BITS; internal const int SCRIPT_SHIFT = CATEGORY_BITS;
internal const int LINEBREAK_SHIFT = CATEGORY_BITS + SCRIPT_BITS; internal const int LINEBREAK_SHIFT = CATEGORY_BITS + SCRIPT_BITS;
internal const int BIDIPAIREDBRACKEDTYPE_SHIFT = BIDIPAIREDBRACKED_BITS; internal const int BIDIPAIREDBRACKEDTYPE_SHIFT = BIDIPAIREDBRACKED_BITS;
internal const int BIDICLASS_SHIFT = BIDIPAIREDBRACKED_BITS + BIDIPAIREDBRACKEDTYPE_BITS; internal const int BIDICLASS_SHIFT = BIDIPAIREDBRACKED_BITS + BIDIPAIREDBRACKEDTYPE_BITS;
internal const int CATEGORY_MASK = (1 << CATEGORY_BITS) - 1; internal const int CATEGORY_MASK = (1 << CATEGORY_BITS) - 1;
internal const int SCRIPT_MASK = (1 << SCRIPT_BITS) - 1; internal const int SCRIPT_MASK = (1 << SCRIPT_BITS) - 1;
internal const int LINEBREAK_MASK = (1 << LINEBREAK_BITS) - 1; internal const int LINEBREAK_MASK = (1 << LINEBREAK_BITS) - 1;
internal const int BIDIPAIREDBRACKED_MASK = (1 << BIDIPAIREDBRACKED_BITS) - 1; internal const int BIDIPAIREDBRACKED_MASK = (1 << BIDIPAIREDBRACKED_BITS) - 1;
internal const int BIDIPAIREDBRACKEDTYPE_MASK = (1 << BIDIPAIREDBRACKEDTYPE_BITS) - 1; internal const int BIDIPAIREDBRACKEDTYPE_MASK = (1 << BIDIPAIREDBRACKEDTYPE_BITS) - 1;
internal const int BIDICLASS_MASK = (1 << BIDICLASS_BITS) - 1; internal const int BIDICLASS_MASK = (1 << BIDICLASS_BITS) - 1;
@ -35,9 +36,9 @@ namespace Avalonia.Media.TextFormatting.Unicode
static UnicodeData() static UnicodeData()
{ {
s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!); s_unicodeDataTrie = new UnicodeTrie(UnicodeDataTrie.Data);
s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!); s_graphemeBreakTrie = new UnicodeTrie(GraphemeBreakTrie.Data);
s_biDiTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.BiDi.trie")!); s_biDiTrie = new UnicodeTrie(BidiTrie.Data);
} }
/// <summary> /// <summary>
@ -46,7 +47,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's general category.</returns> /// <returns>The code point's general category.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GeneralCategory GetGeneralCategory(int codepoint) public static GeneralCategory GetGeneralCategory(uint codepoint)
{ {
return (GeneralCategory)(s_unicodeDataTrie.Get(codepoint) & CATEGORY_MASK); return (GeneralCategory)(s_unicodeDataTrie.Get(codepoint) & CATEGORY_MASK);
} }
@ -57,7 +58,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's script.</returns> /// <returns>The code point's script.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Script GetScript(int codepoint) public static Script GetScript(uint codepoint)
{ {
return (Script)((s_unicodeDataTrie.Get(codepoint) >> SCRIPT_SHIFT) & SCRIPT_MASK); return (Script)((s_unicodeDataTrie.Get(codepoint) >> SCRIPT_SHIFT) & SCRIPT_MASK);
} }
@ -68,31 +69,31 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's biDi class.</returns> /// <returns>The code point's biDi class.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BidiClass GetBiDiClass(int codepoint) public static BidiClass GetBiDiClass(uint codepoint)
{ {
return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK); return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK);
} }
/// <summary> /// <summary>
/// Gets the <see cref="BidiPairedBracketType"/> for a Unicode codepoint. /// Gets the <see cref="BidiPairedBracketType"/> for a Unicode codepoint.
/// </summary> /// </summary>
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket type.</returns> /// <returns>The code point's paired bracket type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BidiPairedBracketType GetBiDiPairedBracketType(int codepoint) public static BidiPairedBracketType GetBiDiPairedBracketType(uint codepoint)
{ {
return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK); return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK);
} }
/// <summary> /// <summary>
/// Gets the paired bracket for a Unicode codepoint. /// Gets the paired bracket for a Unicode codepoint.
/// </summary> /// </summary>
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket.</returns> /// <returns>The code point's paired bracket.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Codepoint GetBiDiPairedBracket(int codepoint) public static Codepoint GetBiDiPairedBracket(uint codepoint)
{ {
return new Codepoint((int)(s_biDiTrie.Get(codepoint) & BIDIPAIREDBRACKED_MASK)); return new Codepoint((s_biDiTrie.Get(codepoint) & BIDIPAIREDBRACKED_MASK));
} }
/// <summary> /// <summary>
@ -101,7 +102,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's line break class.</returns> /// <returns>The code point's line break class.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static LineBreakClass GetLineBreakClass(int codepoint) public static LineBreakClass GetLineBreakClass(uint codepoint)
{ {
return (LineBreakClass)((s_unicodeDataTrie.Get(codepoint) >> LINEBREAK_SHIFT) & LINEBREAK_MASK); return (LineBreakClass)((s_unicodeDataTrie.Get(codepoint) >> LINEBREAK_SHIFT) & LINEBREAK_MASK);
} }
@ -112,7 +113,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's grapheme break type.</returns> /// <returns>The code point's grapheme break type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GraphemeBreakClass GetGraphemeClusterBreak(int codepoint) public static GraphemeBreakClass GetGraphemeClusterBreak(uint codepoint)
{ {
return (GraphemeBreakClass)s_graphemeBreakTrie.Get(codepoint); return (GraphemeBreakClass)s_graphemeBreakTrie.Get(codepoint);
} }

1086
src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs

File diff suppressed because it is too large

156
src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs

@ -15,92 +15,119 @@
// Ported from: https://github.com/foliojs/unicode-trie // Ported from: https://github.com/foliojs/unicode-trie
// Copied from: https://github.com/toptensoftware/RichTextKit // Copied from: https://github.com/toptensoftware/RichTextKit
using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace Avalonia.Media.TextFormatting.Unicode namespace Avalonia.Media.TextFormatting.Unicode
{ {
internal class UnicodeTrie internal class UnicodeTrie
{ {
private readonly int[] _data; private readonly uint[] _data;
private readonly int _highStart; private readonly int _highStart;
private readonly uint _errorValue; private readonly uint _errorValue;
/// <summary>
/// Initializes a new instance of the <see cref="UnicodeTrie"/> class.
/// </summary>
/// <param name="rawData">The uncompressed trie data.</param>
public UnicodeTrie(ReadOnlySpan<byte> rawData)
{
var header = UnicodeTrieHeader.Parse(rawData);
int length = header.DataLength;
uint[] data = new uint[length / sizeof(uint)];
MemoryMarshal.Cast<byte, uint>(rawData.Slice(rawData.Length - length))
.CopyTo(data);
_highStart = header.HighStart;
_errorValue = header.ErrorValue;
_data = data;
}
/// <summary>
/// Initializes a new instance of the <see cref="UnicodeTrie"/> class.
/// </summary>
/// <param name="stream">The stream containing the data.</param>
public UnicodeTrie(Stream stream) public UnicodeTrie(Stream stream)
{ {
int dataLength; // Read the header info
using (var bw = new BinaryReader(stream, Encoding.UTF8, true)) using (var br = new BinaryReader(stream, Encoding.UTF8, true))
{ {
_highStart = bw.ReadInt32BE(); _highStart = br.ReadInt32();
_errorValue = bw.ReadUInt32BE(); _errorValue = br.ReadUInt32();
dataLength = bw.ReadInt32BE() / 4; _data = new uint[br.ReadInt32() / sizeof(uint)];
} }
using (var infl1 = new DeflateStream(stream, CompressionMode.Decompress, true)) // Read the data in compressed format.
using (var infl2 = new DeflateStream(infl1, CompressionMode.Decompress, true)) using (var br = new BinaryReader(stream, Encoding.UTF8, true))
using (var bw = new BinaryReader(infl2, Encoding.UTF8, true))
{ {
_data = new int[dataLength];
for (int i = 0; i < _data.Length; i++) for (int i = 0; i < _data.Length; i++)
{ {
_data[i] = bw.ReadInt32(); _data[i] = br.ReadUInt32();
} }
} }
} }
public UnicodeTrie(byte[] buf) : this(new MemoryStream(buf))
{
} /// <summary>
/// Initializes a new instance of the <see cref="UnicodeTrie"/> class.
internal UnicodeTrie(int[] data, int highStart, uint errorValue) /// </summary>
/// <param name="data">The uncompressed trie data.</param>
/// <param name="highStart">The start of the last range which ends at U+10ffff.</param>
/// <param name="errorValue">The value for out-of-range code points and illegal UTF-8.</param>
public UnicodeTrie(uint[] data, int highStart, uint errorValue)
{ {
_data = data; _data = data;
_highStart = highStart; _highStart = highStart;
_errorValue = errorValue; _errorValue = errorValue;
} }
/// <summary>
/// Saves the <see cref="UnicodeTrie"/> to the stream in a compressed format.
/// </summary>
/// <param name="stream">The output stream.</param>
internal void Save(Stream stream) internal void Save(Stream stream)
{ {
// Write the header info // Write the header info
using (var bw = new BinaryWriter(stream, Encoding.UTF8, true)) using (var bw = new BinaryWriter(stream, Encoding.UTF8, true))
{ {
bw.WriteBE(_highStart); bw.Write(_highStart);
bw.WriteBE(_errorValue); bw.Write(_errorValue);
bw.WriteBE(_data.Length * 4); bw.Write(_data.Length * sizeof(uint));
} }
// Double compress the data // Write the data.
using (var def1 = new DeflateStream(stream, CompressionLevel.Optimal, true)) using (var bw = new BinaryWriter(stream, Encoding.UTF8, true))
using (var def2 = new DeflateStream(def1, CompressionLevel.Optimal, true))
using (var bw = new BinaryWriter(def2, Encoding.UTF8, true))
{ {
foreach (var v in _data) for (int i = 0; i < _data.Length; i++)
{ {
bw.Write(v); bw.Write(_data[i]);
} }
bw.Flush();
def2.Flush();
def1.Flush();
} }
} }
public uint Get(int codePoint) /// <summary>
/// Get the value for a code point as stored in the trie.
/// </summary>
/// <param name="codePoint">The code point.</param>
/// <returns>The <see cref="uint"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Get(uint codePoint)
{ {
int index; uint index;
if ((codePoint < 0) || (codePoint > 0x10ffff)) ref uint dataBase = ref MemoryMarshal.GetReference(_data.AsSpan());
{
return _errorValue;
}
if ((codePoint < 0xd800) || ((codePoint > 0xdbff) && (codePoint <= 0xffff))) if (codePoint is < 0x0d800 or (> 0x0dbff and <= 0x0ffff))
{ {
// Ordinary BMP code point, excluding leading surrogates. // Ordinary BMP code point, excluding leading surrogates.
// BMP uses a single level lookup. BMP index starts at offset 0 in the index. // BMP uses a single level lookup. BMP index starts at offset 0 in the Trie2 index.
// data is stored in the index array itself. // 16 bit data is stored in the index array itself.
index = (_data[codePoint >> UnicodeTrieBuilder.SHIFT_2] << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK); index = _data[codePoint >> UnicodeTrieBuilder.SHIFT_2];
return (uint)_data[index]; index = (index << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK);
return Unsafe.Add(ref dataBase, (nint)index);
} }
if (codePoint <= 0xffff) if (codePoint <= 0xffff)
@ -109,20 +136,57 @@ namespace Avalonia.Media.TextFormatting.Unicode
// lead surrogate code units and code points. // lead surrogate code units and code points.
// The main index has the code unit data. // The main index has the code unit data.
// For this function, we need the code point data. // For this function, we need the code point data.
index = (_data[UnicodeTrieBuilder.LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> UnicodeTrieBuilder.SHIFT_2)] << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK); // Note: this expression could be refactored for slightly improved efficiency, but
return (uint)_data[index]; // surrogate code points will be so rare in practice that it's not worth it.
index = _data[UnicodeTrieBuilder.LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> UnicodeTrieBuilder.SHIFT_2)];
index = (index << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK);
return Unsafe.Add(ref dataBase, (nint)index);
} }
if (codePoint < _highStart) if (codePoint < _highStart)
{ {
// Supplemental code point, use two-level lookup. // Supplemental code point, use two-level lookup.
index = _data[(UnicodeTrieBuilder.INDEX_1_OFFSET - UnicodeTrieBuilder.OMITTED_BMP_INDEX_1_LENGTH) + (codePoint >> UnicodeTrieBuilder.SHIFT_1)]; index = UnicodeTrieBuilder.INDEX_1_OFFSET - UnicodeTrieBuilder.OMITTED_BMP_INDEX_1_LENGTH + (codePoint >> UnicodeTrieBuilder.SHIFT_1);
index = _data[index + ((codePoint >> UnicodeTrieBuilder.SHIFT_2) & UnicodeTrieBuilder.INDEX_2_MASK)]; index = _data[index];
index += (codePoint >> UnicodeTrieBuilder.SHIFT_2) & UnicodeTrieBuilder.INDEX_2_MASK;
index = _data[index];
index = (index << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK); index = (index << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK);
return (uint)_data[index]; return Unsafe.Add(ref dataBase, (nint)index);
}
if (codePoint <= 0x10ffff)
{
return Unsafe.Add(ref dataBase, (nint)(_data.Length - UnicodeTrieBuilder.DATA_GRANULARITY));
}
// Fall through. The code point is outside of the legal range of 0..0x10ffff.
return _errorValue;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct UnicodeTrieHeader
{
public int HighStart
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public uint ErrorValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public int DataLength
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
} }
return (uint)_data[_data.Length - UnicodeTrieBuilder.DATA_GRANULARITY]; [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UnicodeTrieHeader Parse(ReadOnlySpan<byte> data)
=> MemoryMarshal.Cast<byte, UnicodeTrieHeader>(data)[0];
} }
} }
} }

14
src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs

@ -368,24 +368,24 @@ namespace Avalonia.Media.TextFormatting.Unicode
// calculate the sizes of, and allocate, the index and data arrays // calculate the sizes of, and allocate, the index and data arrays
var indexLength = allIndexesLength + _dataLength; var indexLength = allIndexesLength + _dataLength;
var data = new int[indexLength]; var data = new uint[indexLength];
// write the index-2 array values shifted right by INDEX_SHIFT, after adding dataMove // write the index-2 array values shifted right by INDEX_SHIFT, after adding dataMove
var destIdx = 0; var destIdx = 0;
for (i = 0; i < INDEX_2_BMP_LENGTH; i++) for (i = 0; i < INDEX_2_BMP_LENGTH; i++)
{ {
data[destIdx++] = ((_index2[i] + dataMove) >> INDEX_SHIFT); data[destIdx++] = (uint)((_index2[i] + dataMove) >> INDEX_SHIFT);
} }
// write UTF-8 2-byte index-2 values, not right-shifted // write UTF-8 2-byte index-2 values, not right-shifted
for (i = 0; i < 0xc2 - 0xc0; i++) for (i = 0; i < 0xc2 - 0xc0; i++)
{ // C0..C1 { // C0..C1
data[destIdx++] = (dataMove + BAD_UTF8_DATA_OFFSET); data[destIdx++] = (uint)(dataMove + BAD_UTF8_DATA_OFFSET);
} }
for (; i < 0xe0 - 0xc0; i++) for (; i < 0xe0 - 0xc0; i++)
{ // C2..DF { // C2..DF
data[destIdx++] = (dataMove + _index2[i << (6 - SHIFT_2)]); data[destIdx++] = (uint)(dataMove + _index2[i << (6 - SHIFT_2)]);
} }
if (_highStart > 0x10000) if (_highStart > 0x10000)
@ -396,21 +396,21 @@ namespace Avalonia.Media.TextFormatting.Unicode
// write 16-bit index-1 values for supplementary code points // write 16-bit index-1 values for supplementary code points
for (i = 0; i < index1Length; i++) for (i = 0; i < index1Length; i++)
{ {
data[destIdx++] = (INDEX_2_OFFSET + _index1[i + OMITTED_BMP_INDEX_1_LENGTH]); data[destIdx++] = (uint)(INDEX_2_OFFSET + _index1[i + OMITTED_BMP_INDEX_1_LENGTH]);
} }
// write the index-2 array values for supplementary code points, // write the index-2 array values for supplementary code points,
// shifted right by INDEX_SHIFT, after adding dataMove // shifted right by INDEX_SHIFT, after adding dataMove
for (i = 0; i < _index2Length - index2Offset; i++) for (i = 0; i < _index2Length - index2Offset; i++)
{ {
data[destIdx++] = ((dataMove + _index2[index2Offset + i]) >> INDEX_SHIFT); data[destIdx++] = (uint)((dataMove + _index2[index2Offset + i]) >> INDEX_SHIFT);
} }
} }
// write 16-bit data values // write 16-bit data values
for (i = 0; i < _dataLength; i++) for (i = 0; i < _dataLength; i++)
{ {
data[destIdx++] = (int)_data[i]; data[destIdx++] = _data[i];
} }
return new UnicodeTrie(data, _highStart, _errorValue); return new UnicodeTrie(data, _highStart, _errorValue);

7
src/Avalonia.Base/Platform/IPlatformRenderInterface.cs

@ -58,6 +58,13 @@ namespace Avalonia.Platform
/// <returns>A combined geometry.</returns> /// <returns>A combined geometry.</returns>
IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2); IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2);
/// <summary>
/// Created a geometry implementation for the glyph run.
/// </summary>
/// <param name="glyphRun">The glyph run to build a geometry from.</param>
/// <returns>The geometry returned contains the combined geometry of all glyphs in the glyph run.</returns>
IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun);
/// <summary> /// <summary>
/// Creates a renderer. /// Creates a renderer.
/// </summary> /// </summary>

107
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs

@ -0,0 +1,107 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage.FileIO;
[Unstable]
public class BclStorageFile : IStorageBookmarkFile
{
private readonly FileInfo _fileInfo;
public BclStorageFile(FileInfo fileInfo)
{
_fileInfo = fileInfo ?? throw new ArgumentNullException(nameof(fileInfo));
}
public bool CanOpenRead => true;
public bool CanOpenWrite => true;
public string Name => _fileInfo.Name;
public virtual bool CanBookmark => true;
public Task<StorageItemProperties> GetBasicPropertiesAsync()
{
var props = new StorageItemProperties();
if (_fileInfo.Exists)
{
props = new StorageItemProperties(
(ulong)_fileInfo.Length,
_fileInfo.CreationTimeUtc,
_fileInfo.LastAccessTimeUtc);
}
return Task.FromResult(props);
}
public Task<IStorageFolder?> GetParentAsync()
{
if (_fileInfo.Directory is { } directory)
{
return Task.FromResult<IStorageFolder?>(new BclStorageFolder(directory));
}
return Task.FromResult<IStorageFolder?>(null);
}
public Task<Stream> OpenRead()
{
return Task.FromResult<Stream>(_fileInfo.OpenRead());
}
public Task<Stream> OpenWrite()
{
return Task.FromResult<Stream>(_fileInfo.OpenWrite());
}
public virtual Task<string?> SaveBookmark()
{
return Task.FromResult<string?>(_fileInfo.FullName);
}
public Task ReleaseBookmark()
{
// No-op
return Task.CompletedTask;
}
public bool TryGetUri([NotNullWhen(true)] out Uri? uri)
{
try
{
if (_fileInfo.Directory is not null)
{
uri = Path.IsPathRooted(_fileInfo.FullName) ?
new Uri(new Uri("file://"), _fileInfo.FullName) :
new Uri(_fileInfo.FullName, UriKind.Relative);
return true;
}
uri = null;
return false;
}
catch (SecurityException)
{
uri = null;
return false;
}
}
protected virtual void Dispose(bool disposing)
{
}
~BclStorageFile()
{
Dispose(disposing: false);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

88
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs

@ -0,0 +1,88 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage.FileIO;
[Unstable]
public class BclStorageFolder : IStorageBookmarkFolder
{
private readonly DirectoryInfo _directoryInfo;
public BclStorageFolder(DirectoryInfo directoryInfo)
{
_directoryInfo = directoryInfo ?? throw new ArgumentNullException(nameof(directoryInfo));
if (!_directoryInfo.Exists)
{
throw new ArgumentException("Directory must exist", nameof(directoryInfo));
}
}
public string Name => _directoryInfo.Name;
public bool CanBookmark => true;
public Task<StorageItemProperties> GetBasicPropertiesAsync()
{
var props = new StorageItemProperties(
null,
_directoryInfo.CreationTimeUtc,
_directoryInfo.LastAccessTimeUtc);
return Task.FromResult(props);
}
public Task<IStorageFolder?> GetParentAsync()
{
if (_directoryInfo.Parent is { } directory)
{
return Task.FromResult<IStorageFolder?>(new BclStorageFolder(directory));
}
return Task.FromResult<IStorageFolder?>(null);
}
public virtual Task<string?> SaveBookmark()
{
return Task.FromResult<string?>(_directoryInfo.FullName);
}
public Task ReleaseBookmark()
{
// No-op
return Task.CompletedTask;
}
public bool TryGetUri([NotNullWhen(true)] out Uri? uri)
{
try
{
uri = Path.IsPathRooted(_directoryInfo.FullName) ?
new Uri(new Uri("file://"), _directoryInfo.FullName) :
new Uri(_directoryInfo.FullName, UriKind.Relative);
return true;
}
catch (SecurityException)
{
uri = null;
return false;
}
}
protected virtual void Dispose(bool disposing)
{
}
~BclStorageFolder()
{
Dispose(disposing: false);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

35
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageProvider.cs

@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage.FileIO;
[Unstable]
public abstract class BclStorageProvider : IStorageProvider
{
public abstract bool CanOpen { get; }
public abstract Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options);
public abstract bool CanSave { get; }
public abstract Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options);
public abstract bool CanPickFolder { get; }
public abstract Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options);
public virtual Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark)
{
var file = new FileInfo(bookmark);
return file.Exists
? Task.FromResult<IStorageBookmarkFile?>(new BclStorageFile(file))
: Task.FromResult<IStorageBookmarkFile?>(null);
}
public virtual Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark)
{
var folder = new DirectoryInfo(bookmark);
return folder.Exists
? Task.FromResult<IStorageBookmarkFolder?>(new BclStorageFolder(folder))
: Task.FromResult<IStorageBookmarkFolder?>(null);
}
}

40
src/Avalonia.Base/Platform/Storage/FileIO/StorageProviderHelpers.cs

@ -0,0 +1,40 @@
using System;
using System.IO;
using System.Linq;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage.FileIO;
[Unstable]
public static class StorageProviderHelpers
{
public static string NameWithExtension(string path, string? defaultExtension, FilePickerFileType? filter)
{
var name = Path.GetFileName(path);
if (name != null && !Path.HasExtension(name))
{
if (filter?.Patterns?.Count > 0)
{
if (defaultExtension != null
&& filter.Patterns.Contains(defaultExtension))
{
return Path.ChangeExtension(path, defaultExtension.TrimStart('.'));
}
var ext = filter.Patterns.FirstOrDefault(x => x != "*.*");
ext = ext?.Split(new[] { "*." }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
if (ext != null)
{
return Path.ChangeExtension(path, ext);
}
}
if (defaultExtension != null)
{
return Path.ChangeExtension(path, defaultExtension);
}
}
return path;
}
}

44
src/Avalonia.Base/Platform/Storage/FilePickerFileType.cs

@ -0,0 +1,44 @@
using System.Collections.Generic;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Represents a name mapped to the associated file types (extensions).
/// </summary>
public sealed class FilePickerFileType
{
public FilePickerFileType(string name)
{
Name = name;
}
/// <summary>
/// File type name.
/// </summary>
public string Name { get; }
/// <summary>
/// List of extensions in GLOB format. I.e. "*.png" or "*.*".
/// </summary>
/// <remarks>
/// Used on Windows and Linux systems.
/// </remarks>
public IReadOnlyList<string>? Patterns { get; set; }
/// <summary>
/// List of extensions in MIME format.
/// </summary>
/// <remarks>
/// Used on Android, Browser and Linux systems.
/// </remarks>
public IReadOnlyList<string>? MimeTypes { get; set; }
/// <summary>
/// List of extensions in Apple uniform format.
/// </summary>
/// <remarks>
/// Used only on Apple devices.
/// See https://developer.apple.com/documentation/uniformtypeidentifiers/system_declared_uniform_type_identifiers.
/// </remarks>
public IReadOnlyList<string>? AppleUniformTypeIdentifiers { get; set; }
}

48
src/Avalonia.Base/Platform/Storage/FilePickerFileTypes.cs

@ -0,0 +1,48 @@
namespace Avalonia.Platform.Storage;
/// <summary>
/// Dictionary of well known file types.
/// </summary>
public static class FilePickerFileTypes
{
public static FilePickerFileType All { get; } = new("All")
{
Patterns = new[] { "*.*" },
MimeTypes = new[] { "*/*" }
};
public static FilePickerFileType TextPlain { get; } = new("Plain Text")
{
Patterns = new[] { "*.txt" },
AppleUniformTypeIdentifiers = new[] { "public.plain-text" },
MimeTypes = new[] { "text/plain" }
};
public static FilePickerFileType ImageAll { get; } = new("All Images")
{
Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp" },
AppleUniformTypeIdentifiers = new[] { "public.image" },
MimeTypes = new[] { "image/*" }
};
public static FilePickerFileType ImageJpg { get; } = new("JPEG image")
{
Patterns = new[] { "*.jpg", "*.jpeg" },
AppleUniformTypeIdentifiers = new[] { "public.jpeg" },
MimeTypes = new[] { "image/jpeg" }
};
public static FilePickerFileType ImagePng { get; } = new("PNG image")
{
Patterns = new[] { "*.png" },
AppleUniformTypeIdentifiers = new[] { "public.png" },
MimeTypes = new[] { "image/png" }
};
public static FilePickerFileType Pdf { get; } = new("PDF document")
{
Patterns = new[] { "*.pdf" },
AppleUniformTypeIdentifiers = new[] { "com.adobe.pdf" },
MimeTypes = new[] { "application/pdf" }
};
}

19
src/Avalonia.Base/Platform/Storage/FilePickerOpenOptions.cs

@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Options class for <see cref="IStorageProvider.OpenFilePickerAsync"/> method.
/// </summary>
public class FilePickerOpenOptions : PickerOptions
{
/// <summary>
/// Gets or sets an option indicating whether open picker allows users to select multiple files.
/// </summary>
public bool AllowMultiple { get; set; }
/// <summary>
/// Gets or sets the collection of file types that the file open picker displays.
/// </summary>
public IReadOnlyList<FilePickerFileType>? FileTypeFilter { get; set; }
}

29
src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Options class for <see cref="IStorageProvider.SaveFilePickerAsync"/> method.
/// </summary>
public class FilePickerSaveOptions : PickerOptions
{
/// <summary>
/// Gets or sets the file name that the file save picker suggests to the user.
/// </summary>
public string? SuggestedFileName { get; set; }
/// <summary>
/// Gets or sets the default extension to be used to save the file.
/// </summary>
public string? DefaultExtension { get; set; }
/// <summary>
/// Gets or sets the collection of valid file types that the user can choose to assign to a file.
/// </summary>
public IReadOnlyList<FilePickerFileType>? FileTypeChoices { get; set; }
/// <summary>
/// Gets or sets a value indicating whether file open picker displays a warning if the user specifies the name of a file that already exists.
/// </summary>
public bool? ShowOverwritePrompt { get; set; }
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save