Browse Source

Merge remote-tracking branch 'origin/master' into rndr4

# Conflicts:
#	samples/ControlCatalog.NetCore/Program.cs
#	src/Avalonia.SourceGenerator/IsExternalInit.cs
#	src/Shared/IsExternalInit.cs
#	src/tools/DevGenerators/IsExternalInit.cs
pull/8105/head
Nikita Tsukanov 4 years ago
parent
commit
8c171d0174
  1. 2
      .editorconfig
  2. 1
      Avalonia.sln
  3. 48
      azure-pipelines-integrationtests.yml
  4. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  5. 52
      native/Avalonia.Native/src/OSX/SystemDialogs.mm
  6. 3
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  7. 20
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  8. 2
      samples/ControlCatalog.NetCore/Program.cs
  9. 1
      samples/ControlCatalog.Web/App.razor.cs
  10. 2
      samples/ControlCatalog/ControlCatalog.csproj
  11. 5
      samples/ControlCatalog/MainView.xaml
  12. 5
      samples/ControlCatalog/MainView.xaml.cs
  13. 76
      samples/ControlCatalog/Pages/DialogsPage.xaml
  14. 234
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  15. 6
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  16. 13
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  17. 235
      samples/ControlCatalog/Pages/PointerCanvas.cs
  18. 109
      samples/ControlCatalog/Pages/PointerContactsTab.cs
  19. 322
      samples/ControlCatalog/Pages/PointersPage.cs
  20. 66
      samples/ControlCatalog/Pages/PointersPage.xaml
  21. 78
      samples/ControlCatalog/Pages/PointersPage.xaml.cs
  22. 27
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  23. 4
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  24. 21
      samples/IntegrationTestApp/MainWindow.axaml
  25. 66
      samples/IntegrationTestApp/MainWindow.axaml.cs
  26. 35
      samples/IntegrationTestApp/ShowWindowTest.axaml
  27. 40
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  28. 3
      src/Android/Avalonia.Android/AndroidPlatform.cs
  29. 32
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  30. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  31. 244
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  32. 177
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
  33. 20
      src/Android/Avalonia.Android/SystemDialogImpl.cs
  34. BIN
      src/Avalonia.Base/Assets/BiDi.trie
  35. BIN
      src/Avalonia.Base/Assets/GraphemeBreak.trie
  36. BIN
      src/Avalonia.Base/Assets/UnicodeData.trie
  37. 3
      src/Avalonia.Base/AvaloniaObject.cs
  38. 53
      src/Avalonia.Base/Controls/Classes.cs
  39. 14
      src/Avalonia.Base/Controls/IClassesChangedListener.cs
  40. 119
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  41. 2
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  42. 5
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  43. 4
      src/Avalonia.Base/Input/IInputElement.cs
  44. 7
      src/Avalonia.Base/Input/IKeyboardDevice.cs
  45. 10
      src/Avalonia.Base/Input/IPenDevice.cs
  46. 43
      src/Avalonia.Base/Input/IPointer.cs
  47. 58
      src/Avalonia.Base/Input/InputElement.cs
  48. 174
      src/Avalonia.Base/Input/PenDevice.cs
  49. 22
      src/Avalonia.Base/Input/PointerEventArgs.cs
  50. 6
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  51. 125
      src/Avalonia.Base/Input/PointerPoint.cs
  52. 19
      src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
  53. 17
      src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs
  54. 13
      src/Avalonia.Base/Input/TouchDevice.cs
  55. 71
      src/Avalonia.Base/Layout/LayoutHelper.cs
  56. 8
      src/Avalonia.Base/Layout/LayoutManager.cs
  57. 24
      src/Avalonia.Base/Layout/Layoutable.cs
  58. 10
      src/Avalonia.Base/Logging/LogArea.cs
  59. 7
      src/Avalonia.Base/Media/GlyphRun.cs
  60. 23
      src/Avalonia.Base/Media/TextAlignment.cs
  61. 109
      src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
  62. 16
      src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs
  63. 38
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  64. 31
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  65. 66
      src/Avalonia.Base/Media/TextFormatting/TextLine.cs
  66. 77
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  67. 472
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs
  68. 28
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
  69. 25
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs
  70. 44
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  71. 7
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
  72. 409
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs
  73. 1
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs
  74. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
  75. 5
      src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs
  76. 35
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
  77. 1086
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs
  78. 156
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
  79. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
  80. 107
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
  81. 88
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  82. 35
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageProvider.cs
  83. 40
      src/Avalonia.Base/Platform/Storage/FileIO/StorageProviderHelpers.cs
  84. 44
      src/Avalonia.Base/Platform/Storage/FilePickerFileType.cs
  85. 48
      src/Avalonia.Base/Platform/Storage/FilePickerFileTypes.cs
  86. 19
      src/Avalonia.Base/Platform/Storage/FilePickerOpenOptions.cs
  87. 29
      src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs
  88. 12
      src/Avalonia.Base/Platform/Storage/FolderPickerOpenOptions.cs
  89. 20
      src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs
  90. 32
      src/Avalonia.Base/Platform/Storage/IStorageFile.cs
  91. 11
      src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
  92. 53
      src/Avalonia.Base/Platform/Storage/IStorageItem.cs
  93. 56
      src/Avalonia.Base/Platform/Storage/IStorageProvider.cs
  94. 17
      src/Avalonia.Base/Platform/Storage/PickerOptions.cs
  95. 43
      src/Avalonia.Base/Platform/Storage/StorageItemProperties.cs
  96. 14
      src/Avalonia.Base/Point.cs
  97. 1
      src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs
  98. 26
      src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs
  99. 3
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  100. 2
      src/Avalonia.Base/Utilities/ArrayBuilder.cs

2
.editorconfig

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

1
Avalonia.sln

@ -38,6 +38,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs
EndProjectSection

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 */; };
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
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 */
/* Begin PBXFileReference section */
@ -101,6 +102,7 @@
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@ -108,6 +110,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */,
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */,
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */,
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
@ -122,6 +125,7 @@
AB661C1C2148230E00291242 /* Frameworks */ = {
isa = PBXGroup;
children = (
ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */,
522D5958258159C1006F7F7A /* Carbon.framework */,
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */,
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,

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

@ -1,5 +1,6 @@
#include "common.h"
#include "INSWindowHolder.h"
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
{
@ -7,6 +8,7 @@ public:
FORWARD_IUNKNOWN()
virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
bool allowMultiple,
const char* title,
const char* initialDirectory) override
{
@ -14,6 +16,7 @@ public:
{
auto panel = [NSOpenPanel openPanel];
panel.allowsMultipleSelection = allowMultiple;
panel.canChooseDirectories = true;
panel.canCreateDirectories = true;
panel.canChooseFiles = false;
@ -118,7 +121,15 @@ public:
{
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:@";"];
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;
}
};

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

@ -48,7 +48,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
[Window setHasShadow:true];
}
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@ -224,7 +223,7 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
if (ret == nullptr)
return E_POINTER;
if(Window != nullptr){
if(Window != nullptr && _shown){
auto frame = [Window frame];
ret->Width = frame.size.width;
ret->Height = frame.size.height;

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

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

2
samples/ControlCatalog.NetCore/Program.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

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

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

2
samples/ControlCatalog/ControlCatalog.csproj

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

5
samples/ControlCatalog/MainView.xaml

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

5
samples/ControlCatalog/MainView.xaml.cs

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

76
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -1,29 +1,57 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DialogsPage">
<StackPanel Orientation="Vertical" Spacing="4" Margin="4">
<CheckBox Name="UseFilters">Use filters</CheckBox>
<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>
<UserControl x:Class="ControlCatalog.Pages.DialogsPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Margin="4"
Orientation="Vertical"
Spacing="4">
<TextBlock x:Name="PickerLastResultsVisible"
Classes="h2"
IsVisible="False"
Text="Last picker results:" />
<ItemsPresenter x:Name="PickerLastResults" />
<TextBlock Text="Windows:" />
<TextBlock Margin="0, 8, 0, 0"
Classes="h1"
Text="Window dialogs" />
<Button Name="DecoratedWindow">Decorated _window</Button>
<Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button>
<Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button>
<Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button>
<Button Name="OwnedWindow">Own_ed window</Button>
<Button Name="OwnedWindowNoTaskbar">Owned window (No tas_kbar icon)</Button>
<Expander Header="Window dialogs">
<StackPanel Spacing="4">
<Button Name="DecoratedWindow">Decorated _window</Button>
<Button Name="DecoratedWindowDialog">Decorated w_indow (dialog)</Button>
<Button Name="Dialog" ToolTip.Tip="Shows a dialog">_Dialog</Button>
<Button Name="DialogNoTaskbar">Dialog (_No taskbar icon)</Button>
<Button Name="OwnedWindow">Own_ed window</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>
</UserControl>

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

@ -1,13 +1,21 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Dialogs;
using Avalonia.Layout;
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
{
public class DialogsPage : UserControl
@ -18,13 +26,16 @@ namespace ControlCatalog.Pages
var results = this.Get<ItemsPresenter>("PickerLastResults");
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)
return null;
return new List<FileDialogFilter>();
return new List<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
{
// Almost guaranteed to exist
var fullPath = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
var initialFileName = fullPath == null ? null : System.IO.Path.GetFileName(fullPath);
var initialDirectory = fullPath == null ? null : System.IO.Path.GetDirectoryName(fullPath);
var uri = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
var initialFileName = uri == null ? null : System.IO.Path.GetFileName(uri);
var initialDirectory = uri == null ? null : System.IO.Path.GetDirectoryName(uri);
var result = await new OpenFileDialog()
{
@ -62,7 +84,7 @@ namespace ControlCatalog.Pages
{
Title = "Open multiple files",
Filters = GetFilters(),
Directory = lastSelectedDirectory,
Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
AllowMultiple = true
}.ShowAsync(GetWindow());
results.Items = result;
@ -70,11 +92,13 @@ namespace ControlCatalog.Pages
};
this.Get<Button>("SaveFile").Click += async delegate
{
var filters = GetFilters();
var result = await new SaveFileDialog()
{
Title = "Save file",
Filters = GetFilters(),
Directory = lastSelectedDirectory,
Filters = filters,
Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
DefaultExtension = filters?.Any() == true ? "txt" : null,
InitialFileName = "test.txt"
}.ShowAsync(GetWindow());
results.Items = new[] { result };
@ -85,14 +109,9 @@ namespace ControlCatalog.Pages
var result = await new OpenFolderDialog()
{
Title = "Select folder",
Directory = lastSelectedDirectory,
Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null
}.ShowAsync(GetWindow());
if (!string.IsNullOrEmpty(result))
{
lastSelectedDirectory = result;
}
lastSelectedDirectory = new BclStorageFolder(new System.IO.DirectoryInfo(result));
results.Items = new [] { result };
resultsVisible.IsVisible = result != null;
};
@ -101,7 +120,7 @@ namespace ControlCatalog.Pages
var result = await new OpenFileDialog()
{
Title = "Select both",
Directory = lastSelectedDirectory,
Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null,
AllowMultiple = true
}.ShowManagedAsync(GetWindow(), new ManagedFileDialogOptions
{
@ -116,20 +135,20 @@ namespace ControlCatalog.Pages
};
this.Get<Button>("DecoratedWindowDialog").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
_ = new DecoratedWindow().ShowDialog(GetWindow());
};
this.Get<Button>("Dialog").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowDialog(GetWindow());
_ = window.ShowDialog(GetWindow());
};
this.Get<Button>("DialogNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowInTaskbar = false;
window.ShowDialog(GetWindow());
_ = window.ShowDialog(GetWindow());
};
this.Get<Button>("OwnedWindow").Click += delegate
{
@ -146,13 +165,166 @@ namespace ControlCatalog.Pages
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()
{
Button button;
Button dialogButton;
var window = new Window
{
Height = 200,
@ -191,7 +363,22 @@ namespace ControlCatalog.Pages
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()
{
@ -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">
<Label Target="upDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of decimal NumericUpDown:</Label>
<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}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10">
<Label Target="DoubleUpDown" FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of double NumericUpDown:</Label>
<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}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10">
<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"
CultureInfo="en-US" VerticalAlignment="Center"
VerticalAlignment="Center"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}">
<DataValidationErrors.Error>
<sys:Exception />

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

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

27
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,22 +1,37 @@
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A progress bar control</TextBlock>
<StackPanel>
<StackPanel Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Maximum</TextBlock>
<NumericUpDown x:Name="maximum" Value="100" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Minimum</TextBlock>
<NumericUpDown x:Name="minimum" Value="0" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Progress Text Format</TextBlock>
<TextBox x:Name="stringFormat" Text="{}{0:0}%" VerticalAlignment="Center"/>
</StackPanel>
<CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" />
<CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" />
<StackPanel Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16">
<StackPanel Spacing="16">
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" />
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/>
</StackPanel>
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" />
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/>
</StackPanel>
<StackPanel Margin="16">
<Slider Name="hprogress" Maximum="100" Value="40" />
<Slider Name="vprogress" Maximum="100" Value="60" />
<Slider Name="hprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="40" />
<Slider Name="vprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="60" />
</StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.value}"/>
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />

4
samples/ControlCatalog/Pages/TextBlockPage.xaml

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

21
samples/IntegrationTestApp/MainWindow.axaml

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow"
Name="MainWindow"
Title="IntegrationTestApp">
<NativeMenu.Menu>
<NativeMenu>
@ -94,6 +95,26 @@
</StackPanel>
</DockPanel>
</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>
</DockPanel>
</Window>

66
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
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)
{
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem");
@ -64,6 +122,14 @@ namespace IntegrationTestApp
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset")
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)}";
}
}
}
}

3
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -1,10 +1,8 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Android;
using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
@ -55,7 +53,6 @@ namespace Avalonia.Android
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>()
.Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
.Bind<IRenderLoop>().ToConstant(new RenderLoop())

32
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -4,10 +4,14 @@ using Android.Content.Res;
using AndroidX.Lifecycle;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls;
using Android.Runtime;
using Android.App;
using Android.Content;
using System;
namespace Avalonia.Android
{
public abstract class AvaloniaActivity<TApp> : AppCompatActivity where TApp : Application, new()
public abstract class AvaloniaActivity : AppCompatActivity
{
internal class SingleViewLifetime : ISingleViewApplicationLifetime
{
@ -20,16 +24,15 @@ namespace Avalonia.Android
}
}
internal Action<int, Result, Intent> ActivityResult;
internal AvaloniaView View;
internal AvaloniaViewModel _viewModel;
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected abstract AppBuilder CreateAppBuilder();
protected override void OnCreate(Bundle savedInstanceState)
{
var builder = AppBuilder.Configure<TApp>();
CustomizeAppBuilder(builder);
var builder = CreateAppBuilder();
var lifetime = new SingleViewLifetime();
@ -79,5 +82,24 @@ namespace Avalonia.Android
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.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Android.Platform.Storage;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces;
@ -16,11 +17,13 @@ using Avalonia.Input.TextInput;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Rendering;
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 IFramebufferPlatformSurface _framebuffer;
@ -46,6 +49,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
StorageProvider = new AndroidStorageProvider((AvaloniaActivity)avaloniaView.Context);
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
@ -225,6 +229,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public ITextInputMethodImpl TextInputMethod => _textInputMethod;
public INativeControlHostImpl NativeControlHost { get; }
public IStorageProvider StorageProvider { get; }
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()
{
_subscription.Dispose();
// _subscription can be null, if Subscribe failed with an exception.
_subscription?.Dispose();
_owner._directBindings!.Remove(this);
}

53
src/Avalonia.Base/Controls/Classes.cs

@ -14,6 +14,8 @@ namespace Avalonia.Controls
/// </remarks>
public class Classes : AvaloniaList<string>, IPseudoClasses
{
private List<IClassesChangedListener>? _listeners;
/// <summary>
/// Initializes a new instance of the <see cref="Classes"/> class.
/// </summary>
@ -39,6 +41,11 @@ namespace Avalonia.Controls
{
}
/// <summary>
/// Gets the number of listeners subscribed to this collection for unit testing purposes.
/// </summary>
internal int ListenerCount => _listeners?.Count ?? 0;
/// <summary>
/// Parses a classes string.
/// </summary>
@ -62,6 +69,7 @@ namespace Avalonia.Controls
if (!Contains(name))
{
base.Add(name);
NotifyChanged();
}
}
@ -89,6 +97,7 @@ namespace Avalonia.Controls
}
base.AddRange(c);
NotifyChanged();
}
/// <summary>
@ -103,6 +112,8 @@ namespace Avalonia.Controls
RemoveAt(i);
}
}
NotifyChanged();
}
/// <summary>
@ -122,6 +133,7 @@ namespace Avalonia.Controls
if (!Contains(name))
{
base.Insert(index, name);
NotifyChanged();
}
}
@ -154,6 +166,7 @@ namespace Avalonia.Controls
if (toInsert != null)
{
base.InsertRange(index, toInsert);
NotifyChanged();
}
}
@ -169,7 +182,14 @@ namespace Avalonia.Controls
public override bool Remove(string name)
{
ThrowIfPseudoclass(name, "removed");
return base.Remove(name);
if (base.Remove(name))
{
NotifyChanged();
return true;
}
return false;
}
/// <summary>
@ -197,6 +217,7 @@ namespace Avalonia.Controls
if (toRemove != null)
{
base.RemoveAll(toRemove);
NotifyChanged();
}
}
@ -214,6 +235,7 @@ namespace Avalonia.Controls
var name = this[index];
ThrowIfPseudoclass(name, "removed");
base.RemoveAt(index);
NotifyChanged();
}
/// <summary>
@ -224,6 +246,7 @@ namespace Avalonia.Controls
public override void RemoveRange(int index, int count)
{
base.RemoveRange(index, count);
NotifyChanged();
}
/// <summary>
@ -255,6 +278,7 @@ namespace Avalonia.Controls
}
base.AddRange(source);
NotifyChanged();
}
/// <inheritdoc/>
@ -263,13 +287,38 @@ namespace Avalonia.Controls
if (!Contains(name))
{
base.Add(name);
NotifyChanged();
}
}
/// <inheritdoc/>
bool IPseudoClasses.Remove(string name)
{
return base.Remove(name);
if (base.Remove(name))
{
NotifyChanged();
return true;
}
return false;
}
internal void AddListener(IClassesChangedListener listener)
{
(_listeners ??= new()).Add(listener);
}
internal void RemoveListener(IClassesChangedListener listener)
{
_listeners?.Remove(listener);
}
private void NotifyChanged()
{
if (_listeners is null)
return;
foreach (var listener in _listeners)
listener.Changed();
}
private void ThrowIfPseudoclass(string name, string operation)

14
src/Avalonia.Base/Controls/IClassesChangedListener.cs

@ -0,0 +1,14 @@
namespace Avalonia.Controls
{
/// <summary>
/// Internal interface for listening to changes in <see cref="Classes"/> in a more
/// performant manner than subscribing to CollectionChanged.
/// </summary>
internal interface IClassesChangedListener
{
/// <summary>
/// Notifies the listener that the <see cref="Classes"/> collection has changed.
/// </summary>
void Changed();
}
}

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

@ -1,38 +1,45 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
/// An indexed dictionary of resources.
/// </summary>
public class ResourceDictionary : AvaloniaDictionary<object, object?>, IResourceDictionary
public class ResourceDictionary : IResourceDictionary
{
private Dictionary<object, object?>? _inner;
private IResourceHost? _owner;
private AvaloniaList<IResourceProvider>? _mergedDictionaries;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
/// </summary>
public ResourceDictionary()
{
CollectionChanged += OnCollectionChanged;
}
public ResourceDictionary() { }
/// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
/// </summary>
public ResourceDictionary(IResourceHost owner)
: this()
public ResourceDictionary(IResourceHost owner) => Owner = owner;
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
{
get => _owner;
@ -80,7 +87,7 @@ namespace Avalonia.Controls
{
get
{
if (Count > 0)
if (_inner?.Count > 0)
{
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 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)
{
if (TryGetValue(key, out value))
if (_inner is not null && _inner.TryGetValue(key, out value))
{
return true;
}
@ -120,9 +159,52 @@ namespace Avalonia.Controls
}
}
value = null;
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)
{
owner = owner ?? throw new ArgumentNullException(nameof(owner));
@ -134,7 +216,7 @@ namespace Avalonia.Controls
Owner = owner;
var hasResources = Count > 0;
var hasResources = _inner?.Count > 0;
if (_mergedDictionaries is object)
{
@ -159,7 +241,7 @@ namespace Avalonia.Controls
{
Owner = null;
var hasResources = Count > 0;
var hasResources = _inner?.Count > 0;
if (_mergedDictionaries is object)
{
@ -176,10 +258,5 @@ namespace Avalonia.Controls
}
}
}
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
}

2
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -55,7 +55,7 @@ namespace Avalonia.Data.Core.Plugins
private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName)
{
if (instance is IReflectableType reflectableType)
if (instance is IReflectableType reflectableType && instance is not Type)
return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags);
var type = instance.GetType();

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

@ -66,7 +66,8 @@ namespace Avalonia.Input.GestureRecognizers
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();
_tracking = e.Pointer;
@ -101,7 +102,7 @@ namespace Avalonia.Input.GestureRecognizers
if (_scrolling)
{
var vector = _trackedRootPoint - rootPoint;
var elapsed = _lastMoveTimestamp.HasValue ?
var elapsed = _lastMoveTimestamp.HasValue && _lastMoveTimestamp < e.Timestamp ?
TimeSpan.FromMilliseconds(e.Timestamp - _lastMoveTimestamp.Value) :
TimeSpan.Zero;

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

@ -42,12 +42,12 @@ namespace Avalonia.Input
/// <summary>
/// Occurs when the pointer enters the control.
/// </summary>
event EventHandler<PointerEventArgs>? PointerEnter;
event EventHandler<PointerEventArgs>? PointerEntered;
/// <summary>
/// Occurs when the pointer leaves the control.
/// </summary>
event EventHandler<PointerEventArgs>? PointerLeave;
event EventHandler<PointerEventArgs>? PointerExited;
/// <summary>
/// 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,
Shift = 4,
Meta = 8,
LeftMouseButton = 16,
RightMouseButton = 32,
MiddleMouseButton = 64,
XButton1MouseButton = 128,
XButton2MouseButton = 256,
KeyboardMask = Alt | Control | Shift | Meta
KeyboardMask = Alt | Control | Shift | Meta,
PenInverted = 512,
PenEraser = 1024,
PenBarrelButton = 2048
}
[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
{
/// <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]
public interface IPointer
{
/// <summary>
/// Gets a unique identifier for the input pointer.
/// </summary>
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);
/// <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; }
/// <summary>
/// Gets the pointer device type.
/// </summary>
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; }
}
/// <summary>
/// Enumerates pointer device types.
/// </summary>
public enum PointerType
{
Mouse,
Touch
Touch,
Pen
}
}

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

@ -128,16 +128,20 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="PointerEnter"/> event.
/// Defines the <see cref="PointerEntered"/> event.
/// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct);
public static readonly RoutedEvent<PointerEventArgs> PointerEnteredEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(
nameof(PointerEntered),
RoutingStrategies.Direct);
/// <summary>
/// Defines the <see cref="PointerLeave"/> event.
/// Defines the <see cref="PointerExited"/> event.
/// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct);
public static readonly RoutedEvent<PointerEventArgs> PointerExitedEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>(
nameof(PointerExited),
RoutingStrategies.Direct);
/// <summary>
/// Defines the <see cref="PointerMoved"/> event.
@ -208,8 +212,8 @@ namespace Avalonia.Input
KeyDownEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyDown(e));
KeyUpEvent.AddClassHandler<InputElement>((x, e) => x.OnKeyUp(e));
TextInputEvent.AddClassHandler<InputElement>((x, e) => x.OnTextInput(e));
PointerEnterEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnterCore(e));
PointerLeaveEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerLeaveCore(e));
PointerEnteredEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerEnteredCore(e));
PointerExitedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerExitedCore(e));
PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerMoved(e));
PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerPressed(e));
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
@ -279,19 +283,19 @@ namespace Avalonia.Input
/// <summary>
/// Occurs when the pointer enters the control.
/// </summary>
public event EventHandler<PointerEventArgs>? PointerEnter
public event EventHandler<PointerEventArgs>? PointerEntered
{
add { AddHandler(PointerEnterEvent, value); }
remove { RemoveHandler(PointerEnterEvent, value); }
add { AddHandler(PointerEnteredEvent, value); }
remove { RemoveHandler(PointerEnteredEvent, value); }
}
/// <summary>
/// Occurs when the pointer leaves the control.
/// </summary>
public event EventHandler<PointerEventArgs>? PointerLeave
public event EventHandler<PointerEventArgs>? PointerExited
{
add { AddHandler(PointerLeaveEvent, value); }
remove { RemoveHandler(PointerLeaveEvent, value); }
add { AddHandler(PointerExitedEvent, value); }
remove { RemoveHandler(PointerExitedEvent, value); }
}
/// <summary>
@ -539,18 +543,18 @@ namespace Avalonia.Input
}
/// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs.
/// Called before the <see cref="PointerEntered"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnPointerEnter(PointerEventArgs e)
protected virtual void OnPointerEntered(PointerEventArgs e)
{
}
/// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs.
/// Called before the <see cref="PointerExited"/> event occurs.
/// </summary>
/// <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)
{
if (_gestureRecognizers?.HandlePointerMoved(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -571,7 +577,9 @@ namespace Avalonia.Input
protected virtual void OnPointerPressed(PointerPressedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerPressed(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -581,7 +589,9 @@ namespace Avalonia.Input
protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerReleased(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -634,23 +644,23 @@ namespace Avalonia.Input
}
/// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs.
/// Called before the <see cref="PointerEntered"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerEnterCore(PointerEventArgs e)
private void OnPointerEnteredCore(PointerEventArgs e)
{
IsPointerOver = true;
OnPointerEnter(e);
OnPointerEntered(e);
}
/// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs.
/// Called before the <see cref="PointerExited"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerLeaveCore(PointerEventArgs e)
private void OnPointerExitedCore(PointerEventArgs e)
{
IsPointerOver = false;
OnPointerLeave(e);
OnPointerExited(e);
}
/// <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;
}
/// <summary>
/// Gets specific pointer generated by input device.
/// </summary>
public IPointer Pointer { get; }
/// <summary>
/// Gets the time when the input occurred.
/// </summary>
public ulong Timestamp { get; }
private IPointerDevice? _device;
@ -91,7 +98,10 @@ namespace Avalonia.Input
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; }
private Point GetPosition(Point pt, IVisual? relativeTo)
@ -102,7 +112,12 @@ namespace Avalonia.Input
return pt;
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);
[Obsolete("Use GetCurrentPoint")]
@ -130,7 +145,8 @@ namespace Avalonia.Input
for (var c = 0; c < previousPoints.Count; 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);

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

@ -97,7 +97,7 @@ namespace Avalonia.Input
// Do not pass rootVisual, when we have unknown (negative) position,
// so GetPosition won't return invalid values.
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,
timestamp, properties, inputModifiers);
@ -177,7 +177,7 @@ namespace Avalonia.Input
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);
if (el != null && branch != null && !el.IsAttachedToVisualTree)
{
@ -195,7 +195,7 @@ namespace Avalonia.Input
el = root.PointerOverElement = element;
_lastPointer = (pointer, root.PointToScreen(position));
e.RoutedEvent = InputElement.PointerEnterEvent;
e.RoutedEvent = InputElement.PointerEnteredEvent;
while (el != null && el != branch)
{

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

@ -1,5 +1,10 @@
using Avalonia.Input.Raw;
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 PointerPoint(IPointer pointer, Point position, PointerPointProperties properties)
@ -8,25 +13,109 @@ namespace Avalonia.Input
Position = position;
Properties = properties;
}
/// <summary>
/// Gets specific pointer generated by input device.
/// </summary>
public IPointer Pointer { get; }
/// <summary>
/// Gets extended information about the input pointer.
/// </summary>
public PointerPointProperties Properties { get; }
/// <summary>
/// Gets the location of the pointer input in client coordinates.
/// </summary>
public Point Position { get; }
}
/// <summary>
/// Provides extended properties for a PointerPoint object.
/// </summary>
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; }
/// <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; }
/// <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; }
/// <summary>
/// Gets a value that indicates whether the pointer input was triggered by the first extended mouse button (XButton1).
/// </summary>
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; }
/// <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; }
private PointerPointProperties()
{
{
}
public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind)
{
PointerUpdateKind = kind;
@ -36,10 +125,13 @@ namespace Avalonia.Input
IsRightButtonPressed = modifiers.HasAllFlags(RawInputModifiers.RightMouseButton);
IsXButton1Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton1MouseButton);
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,
// so make sure that we reflect the current state
if (kind == PointerUpdateKind.LeftButtonPressed)
IsLeftButtonPressed = true;
if (kind == PointerUpdateKind.LeftButtonReleased)
@ -62,6 +154,33 @@ namespace Avalonia.Input
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();
}

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

@ -56,11 +56,12 @@ namespace Avalonia.Input.Raw
Contract.Requires<ArgumentNullException>(device != null);
Contract.Requires<ArgumentNullException>(root != null);
Point = new RawPointerPoint();
Position = position;
Type = type;
InputModifiers = inputModifiers;
}
/// <summary>
/// Initializes a new instance of the <see cref="RawPointerEventArgs"/> class.
/// </summary>
@ -87,6 +88,11 @@ namespace Avalonia.Input.Raw
InputModifiers = inputModifiers;
}
/// <summary>
/// Gets the raw pointer identifier.
/// </summary>
public long RawPointerId { get; set; }
/// <summary>
/// Gets the pointer properties and position, in client DIPs.
/// </summary>
@ -130,10 +136,17 @@ namespace Avalonia.Input.Raw
/// Pointer position, in client DIPs.
/// </summary>
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()
{
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
{
public class RawTouchEventArgs : RawPointerEventArgs
{
public RawTouchEventArgs(IInputDevice device, ulong timestamp, IInputRoot root,
RawPointerEventType type, Point position, RawInputModifiers inputModifiers,
long touchPointId)
long rawPointerId)
: 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)
return;
var args = (RawTouchEventArgs)ev;
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer))
var args = (RawPointerEventArgs)ev;
if (!_pointers.TryGetValue(args.RawPointerId, out var pointer))
{
if (args.Type == RawPointerEventType.TouchEnd)
return;
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);
pointer.Capture(hit);
}
@ -88,7 +88,7 @@ namespace Avalonia.Input
if (args.Type == RawPointerEventType.TouchEnd)
{
_pointers.Remove(args.TouchPointId);
_pointers.Remove(args.RawPointerId);
using (pointer)
{
target.RaiseEvent(new PointerReleasedEventArgs(target, pointer,
@ -101,7 +101,7 @@ namespace Avalonia.Input
if (args.Type == RawPointerEventType.TouchCancel)
{
_pointers.Remove(args.TouchPointId);
_pointers.Remove(args.RawPointerId);
using (pointer)
pointer.Capture(null);
_lastPointer = null;
@ -129,8 +129,7 @@ namespace Avalonia.Input
public IPointer? TryGetPointer(RawPointerEventArgs ev)
{
return ev is RawTouchEventArgs args
&& _pointers.TryGetValue(args.TouchPointId, out var pointer)
return _pointers.TryGetValue(ev.RawPointerId, out var pointer)
? pointer
: 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,
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)
{
if (IsParentLayoutRounded(control, out double scale))
{
padding = RoundLayoutThickness(padding, scale, scale);
}
if (control != null)
{
control.Measure(availableSize.Deflate(padding));
@ -137,7 +154,7 @@ namespace Avalonia.Layout
/// <summary>
/// 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>
/// <param name="size">Input size.</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
/// utility.
/// </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>
@ -178,10 +195,9 @@ namespace Avalonia.Layout
);
}
/// <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>
/// <param name="value">Input value to be rounded.</param>
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
@ -217,7 +233,46 @@ namespace Avalonia.Layout
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>
/// Calculates the min and max height for a control. Ported from WPF.
/// </summary>

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

@ -350,7 +350,7 @@ namespace Avalonia.Layout
{
for (var i = 0; i < count; ++i)
{
var l = _effectiveViewportChangedListeners[i];
var l = listeners[i];
if (!l.Listener.IsAttachedToVisualTree)
{
@ -362,7 +362,7 @@ namespace Avalonia.Layout
if (viewport != l.Viewport)
{
l.Listener.EffectiveViewportChanged(new EffectiveViewportChangedEventArgs(viewport));
_effectiveViewportChangedListeners[i] = new EffectiveViewportChangedListener(l.Listener, viewport);
l.Viewport = viewport;
}
}
}
@ -414,7 +414,7 @@ namespace Avalonia.Layout
}
}
private readonly struct EffectiveViewportChangedListener
private class EffectiveViewportChangedListener
{
public EffectiveViewportChangedListener(ILayoutable listener, Rect viewport)
{
@ -423,7 +423,7 @@ namespace Avalonia.Layout
}
public ILayoutable Listener { get; }
public Rect Viewport { get; }
public Rect Viewport { get; set; }
}
}
}

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

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

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

@ -734,10 +734,9 @@ namespace Avalonia.Media
private void Set<T>(ref T field, T value)
{
if (_glyphRunImpl != null)
{
throw new InvalidOperationException("GlyphRun can't be changed after it has been initialized.'");
}
_glyphRunImpl?.Dispose();
_glyphRunImpl = null;
_glyphRunMetrics = null;

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

@ -19,5 +19,28 @@ namespace Avalonia.Media
/// The text is right-aligned.
/// </summary>
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)
{
var textWrapping = paragraphProperties.TextWrapping;
FlowDirection flowDirection;
FlowDirection resolvedFlowDirection;
TextLineBreak? nextLineBreak = null;
List<DrawableTextRun> drawableTextRuns;
@ -24,17 +24,17 @@ namespace Avalonia.Media.TextFormatting
if (previousLineBreak?.RemainingRuns != null)
{
flowDirection = previousLineBreak.FlowDirection;
resolvedFlowDirection = previousLineBreak.FlowDirection;
drawableTextRuns = previousLineBreak.RemainingRuns.ToList();
nextLineBreak = previousLineBreak;
}
else
{
drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection);
drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out resolvedFlowDirection);
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:
{
textLine = new TextLineImpl(drawableTextRuns, firstTextSourceIndex, textSourceLength,
paragraphWidth, paragraphProperties, flowDirection, nextLineBreak);
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine();
@ -55,7 +55,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.Wrap:
{
textLine = PerformTextWrapping(drawableTextRuns, firstTextSourceIndex, paragraphWidth, paragraphProperties,
flowDirection, nextLineBreak);
resolvedFlowDirection, nextLineBreak);
break;
}
default:
@ -159,7 +159,6 @@ namespace Avalonia.Media.TextFormatting
{
var flowDirection = paragraphProperties.FlowDirection;
var drawableTextRuns = new List<DrawableTextRun>();
var biDiData = new BidiData((sbyte)flowDirection);
foreach (var textRun in textRuns)
@ -174,10 +173,9 @@ namespace Avalonia.Media.TextFormatting
{
biDiData.Append(textRun.Text);
}
}
var biDi = BidiAlgorithm.Instance.Value!;
var biDi = new BidiAlgorithm();
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="levels">The bidi levels.</param>
/// <returns></returns>
private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels(
IReadOnlyList<TextRun> textCharacters,
ReadOnlySlice<sbyte> levels)
private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels(IReadOnlyList<TextRun> textCharacters, ArraySlice<sbyte> levels)
{
if (levels.Length == 0)
{
@ -404,9 +400,9 @@ namespace Avalonia.Media.TextFormatting
{
endOfLine = textEndOfLine;
textRuns.Add(textRun);
textSourceLength += textEndOfLine.TextSourceLength;
textSourceLength += textRun.TextSourceLength;
textRuns.Add(textRun);
break;
}
@ -431,9 +427,9 @@ namespace Avalonia.Media.TextFormatting
break;
}
case DrawableTextRun drawableTextRun:
default:
{
textRuns.Add(drawableTextRun);
textRuns.Add(textRun);
break;
}
}
@ -552,11 +548,11 @@ namespace Avalonia.Media.TextFormatting
/// <param name="firstTextSourceIndex">The first text source index.</param>
/// <param name="paragraphWidth">The paragraph width.</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>
/// <returns>The wrapped text line.</returns>
private static TextLineImpl PerformTextWrapping(List<DrawableTextRun> textRuns, int firstTextSourceIndex,
double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection,
double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection,
TextLineBreak? currentLineBreak)
{
if(textRuns.Count == 0)
@ -684,16 +680,16 @@ namespace Avalonia.Media.TextFormatting
var remainingCharacters = splitResult.Second;
var lineBreak = remainingCharacters?.Count > 0 ?
new TextLineBreak(currentLineBreak?.TextEndOfLine, flowDirection, remainingCharacters) :
new TextLineBreak(currentLineBreak?.TextEndOfLine, resolvedFlowDirection, remainingCharacters) :
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,
paragraphWidth, paragraphProperties, flowDirection,
paragraphWidth, paragraphProperties, resolvedFlowDirection,
lineBreak);
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,
_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)
{
@ -501,6 +501,35 @@ namespace Avalonia.Media.TextFormatting
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;
}

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

@ -15,9 +15,15 @@ namespace Avalonia.Media.TextFormatting
/// The contained text runs.
/// </value>
public abstract IReadOnlyList<TextRun> TextRuns { get; }
/// <summary>
/// Gets the first TextSource position of the current line.
/// </summary>
public abstract int FirstTextSourceIndex { get; }
/// <summary>
/// Gets the total number of TextSource positions of the current line.
/// </summary>
public abstract int Length { get; }
/// <summary>
@ -56,7 +62,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a value that indicates whether content of the line overflows the specified paragraph width.
/// </summary>
/// <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>
public abstract bool HasOverflowed { get; }
@ -75,7 +81,7 @@ namespace Avalonia.Media.TextFormatting
/// The number of newline characters.
/// </returns>
public abstract int NewLineLength { get; }
/// <summary>
/// Gets the distance that black pixels extend beyond the bottom alignment edge of a line.
/// </summary>
@ -149,6 +155,15 @@ namespace Avalonia.Media.TextFormatting
/// </returns>
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>
/// Gets the character hit corresponding to the specified distance from the beginning of the line.
/// </summary>
@ -192,50 +207,5 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textLength">number of characters of the specified range</param>
/// <returns>an array of bounding rectangles.</returns>
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 TextParagraphProperties _paragraphProperties;
private TextLineMetrics _textLineMetrics;
private readonly FlowDirection _flowDirection;
private readonly FlowDirection _resolvedFlowDirection;
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)
{
FirstTextSourceIndex = firstTextSourceIndex;
@ -25,7 +25,7 @@ namespace Avalonia.Media.TextFormatting
_paragraphWidth = paragraphWidth;
_paragraphProperties = paragraphProperties;
_flowDirection = flowDirection;
_resolvedFlowDirection = resolvedFlowDirection;
}
/// <inheritdoc/>
@ -136,7 +136,7 @@ namespace Avalonia.Media.TextFormatting
}
var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties,
_flowDirection, TextLineBreak, true);
_resolvedFlowDirection, TextLineBreak, true);
if (collapsedRuns.Count > 0)
{
@ -144,7 +144,14 @@ namespace Avalonia.Media.TextFormatting
}
return collapsedLine;
}
/// <inheritdoc/>
public override void Justify(JustificationProperties justificationProperties)
{
justificationProperties.Justify(this);
_textLineMetrics = CreateLineMetrics();
}
/// <inheritdoc/>
@ -167,7 +174,7 @@ namespace Avalonia.Media.TextFormatting
return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _);
}
return _flowDirection == FlowDirection.LeftToRight ?
return _resolvedFlowDirection == FlowDirection.LeftToRight ?
new CharacterHit(FirstTextSourceIndex) :
new CharacterHit(FirstTextSourceIndex + Length);
}
@ -260,7 +267,7 @@ namespace Avalonia.Media.TextFormatting
//Look at the left and right edge of the current run
if (currentRun.IsLeftToRight)
{
if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
{
if (characterIndex <= currentPosition)
{
@ -735,7 +742,7 @@ namespace Avalonia.Media.TextFormatting
// Build up the collection of ordered runs.
var run = _textRuns[0];
OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _flowDirection));
OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _resolvedFlowDirection));
var current = orderedRun;
@ -743,7 +750,7 @@ namespace Avalonia.Media.TextFormatting
{
run = _textRuns[i];
current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _flowDirection));
current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _resolvedFlowDirection));
current = current.Next;
}
@ -762,7 +769,7 @@ namespace Avalonia.Media.TextFormatting
{
var currentRun = _textRuns[i];
var level = GetRunBidiLevel(currentRun, _flowDirection);
var level = GetRunBidiLevel(currentRun, _resolvedFlowDirection);
if (level > max)
{
@ -1242,8 +1249,7 @@ namespace Avalonia.Media.TextFormatting
}
}
var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth,
_paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection);
var start = GetParagraphOffsetX(width, widthIncludingWhitespace);
if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight))
{
@ -1257,6 +1263,55 @@ namespace Avalonia.Media.TextFormatting
-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
{
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 reverse mapping maps the end index to the start index.
/// </remarks>
private readonly Dictionary<int, int> _isolatePairs = new Dictionary<int, int>();
private readonly BidiDictionary<int, int> _isolatePairs = new BidiDictionary<int, int>();
/// <summary>
/// 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>
/// Gets the resolved levels.
/// </summary>
@ -1414,35 +1408,37 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// Sets the direction of a bracket pair, including setting the direction of
/// NSM's inside the brackets and following.
/// </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>
private void SetPairedBracketDirection(in BracketPair bracketPair, BidiClass direction)
private void SetPairedBracketDirection(in BracketPair pairedBracket, BidiClass direction)
{
// Set the direction of the brackets
_runResolvedClasses[bracketPair.OpeningIndex] = direction;
_runResolvedClasses[bracketPair.ClosingIndex] = direction;
_runResolvedClasses[pairedBracket.OpeningIndex] = direction;
_runResolvedClasses[pairedBracket.ClosingIndex] = direction;
// 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)
{
_runOriginalClasses[i] = direction;
}
else
else if (_runOriginalClasses[i] != BidiClass.BoundaryNeutral)
{
break;
}
}
// 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)
{
_runResolvedClasses[i] = direction;
_runOriginalClasses[i] = direction;
}
else
else if (_runOriginalClasses[i] != BidiClass.BoundaryNeutral)
{
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<BidiPairedBracketType> _savedPairedBracketTypes;
private ArrayBuilder<sbyte> _tempLevelBuffer;
public BidiData(sbyte paragraphEmbeddingLevel = 0)
public BidiData(sbyte paragraphEmbeddingLevel)
{
ParagraphEmbeddingLevel = paragraphEmbeddingLevel;
}
public BidiData(ReadOnlySlice<char> text, sbyte paragraphEmbeddingLevel = 0) : this(paragraphEmbeddingLevel)
{
Append(text);
}
public sbyte ParagraphEmbeddingLevel { get; private set; }
public bool HasBrackets { get; private set; }
@ -64,13 +59,23 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// </remarks>
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)
{
_classes.Add(text.Length);
_pairedBracketTypes.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);
@ -115,13 +120,13 @@ namespace Avalonia.Media.TextFormatting.Unicode
// Opening bracket types can never have a null pairing.
codepoint.TryGetPairedBracket(out var paired);
_pairedBracketValues[i] = Codepoint.GetCanonicalType(paired).Value;
_pairedBracketValues[i] = (int)Codepoint.GetCanonicalType(paired).Value;
HasBrackets = true;
}
else if (pbt == BidiPairedBracketType.Close)
{
_pairedBracketValues[i] = Codepoint.GetCanonicalType(codepoint).Value;
_pairedBracketValues[i] = (int)Codepoint.GetCanonicalType(codepoint).Value;
HasBrackets = true;
}

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

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

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

@ -450,6 +450,20 @@ namespace Avalonia.Media.TextFormatting.Unicode
_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;
return shouldBreak;

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

@ -30,6 +30,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
Cherokee, //Cher
Chorasmian, //Chrs
Coptic, //Copt
CyproMinoan, //Cpmn
Cypriot, //Cprt
Cyrillic, //Cyrl
Devanagari, //Deva
@ -109,6 +110,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
Oriya, //Orya
Osage, //Osge
Osmanya, //Osma
OldUyghur, //Ougr
Palmyrene, //Palm
PauCinHau, //Pauc
OldPermic, //Perm
@ -151,8 +153,11 @@ namespace Avalonia.Media.TextFormatting.Unicode
Thai, //Thai
Tibetan, //Tibt
Tirhuta, //Tirh
Tangsa, //Tnsa
Toto, //Toto
Ugaritic, //Ugar
Vai, //Vaii
Vithkuqi, //Vith
WarangCiti, //Wara
Wancho, //Wcho
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
{
@ -17,14 +18,14 @@ namespace Avalonia.Media.TextFormatting.Unicode
internal const int SCRIPT_SHIFT = CATEGORY_BITS;
internal const int LINEBREAK_SHIFT = CATEGORY_BITS + SCRIPT_BITS;
internal const int BIDIPAIREDBRACKEDTYPE_SHIFT = BIDIPAIREDBRACKED_BITS;
internal const int BIDICLASS_SHIFT = BIDIPAIREDBRACKED_BITS + BIDIPAIREDBRACKEDTYPE_BITS;
internal const int CATEGORY_MASK = (1 << CATEGORY_BITS) - 1;
internal const int SCRIPT_MASK = (1 << SCRIPT_BITS) - 1;
internal const int LINEBREAK_MASK = (1 << LINEBREAK_BITS) - 1;
internal const int BIDIPAIREDBRACKED_MASK = (1 << BIDIPAIREDBRACKED_BITS) - 1;
internal const int BIDIPAIREDBRACKEDTYPE_MASK = (1 << BIDIPAIREDBRACKEDTYPE_BITS) - 1;
internal const int BIDICLASS_MASK = (1 << BIDICLASS_BITS) - 1;
@ -35,9 +36,9 @@ namespace Avalonia.Media.TextFormatting.Unicode
static UnicodeData()
{
s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!);
s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!);
s_biDiTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.BiDi.trie")!);
s_unicodeDataTrie = new UnicodeTrie(UnicodeDataTrie.Data);
s_graphemeBreakTrie = new UnicodeTrie(GraphemeBreakTrie.Data);
s_biDiTrie = new UnicodeTrie(BidiTrie.Data);
}
/// <summary>
@ -46,7 +47,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's general category.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GeneralCategory GetGeneralCategory(int codepoint)
public static GeneralCategory GetGeneralCategory(uint codepoint)
{
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>
/// <returns>The code point's script.</returns>
[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);
}
@ -68,31 +69,31 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's biDi class.</returns>
[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);
}
/// <summary>
/// Gets the <see cref="BidiPairedBracketType"/> for a Unicode codepoint.
/// </summary>
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket type.</returns>
[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);
}
/// <summary>
/// Gets the paired bracket for a Unicode codepoint.
/// </summary>
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket.</returns>
[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>
@ -101,7 +102,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's line break class.</returns>
[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);
}
@ -112,7 +113,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's grapheme break type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GraphemeBreakClass GetGraphemeClusterBreak(int codepoint)
public static GraphemeBreakClass GetGraphemeClusterBreak(uint 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
// Copied from: https://github.com/toptensoftware/RichTextKit
using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Avalonia.Media.TextFormatting.Unicode
{
internal class UnicodeTrie
{
private readonly int[] _data;
private readonly uint[] _data;
private readonly int _highStart;
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)
{
int dataLength;
using (var bw = new BinaryReader(stream, Encoding.UTF8, true))
// Read the header info
using (var br = new BinaryReader(stream, Encoding.UTF8, true))
{
_highStart = bw.ReadInt32BE();
_errorValue = bw.ReadUInt32BE();
dataLength = bw.ReadInt32BE() / 4;
_highStart = br.ReadInt32();
_errorValue = br.ReadUInt32();
_data = new uint[br.ReadInt32() / sizeof(uint)];
}
using (var infl1 = new DeflateStream(stream, CompressionMode.Decompress, true))
using (var infl2 = new DeflateStream(infl1, CompressionMode.Decompress, true))
using (var bw = new BinaryReader(infl2, Encoding.UTF8, true))
// Read the data in compressed format.
using (var br = new BinaryReader(stream, Encoding.UTF8, true))
{
_data = new int[dataLength];
for (int i = 0; i < _data.Length; i++)
{
_data[i] = bw.ReadInt32();
_data[i] = br.ReadUInt32();
}
}
}
public UnicodeTrie(byte[] buf) : this(new MemoryStream(buf))
{
}
internal UnicodeTrie(int[] data, int highStart, uint errorValue)
/// <summary>
/// Initializes a new instance of the <see cref="UnicodeTrie"/> class.
/// </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;
_highStart = highStart;
_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)
{
// Write the header info
using (var bw = new BinaryWriter(stream, Encoding.UTF8, true))
{
bw.WriteBE(_highStart);
bw.WriteBE(_errorValue);
bw.WriteBE(_data.Length * 4);
bw.Write(_highStart);
bw.Write(_errorValue);
bw.Write(_data.Length * sizeof(uint));
}
// Double compress the data
using (var def1 = new DeflateStream(stream, CompressionLevel.Optimal, true))
using (var def2 = new DeflateStream(def1, CompressionLevel.Optimal, true))
using (var bw = new BinaryWriter(def2, Encoding.UTF8, true))
// Write the data.
using (var bw = new BinaryWriter(stream, 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;
if ((codePoint < 0) || (codePoint > 0x10ffff))
{
return _errorValue;
}
uint index;
ref uint dataBase = ref MemoryMarshal.GetReference(_data.AsSpan());
if ((codePoint < 0xd800) || ((codePoint > 0xdbff) && (codePoint <= 0xffff)))
if (codePoint is < 0x0d800 or (> 0x0dbff and <= 0x0ffff))
{
// Ordinary BMP code point, excluding leading surrogates.
// BMP uses a single level lookup. BMP index starts at offset 0 in the index.
// data is stored in the index array itself.
index = (_data[codePoint >> UnicodeTrieBuilder.SHIFT_2] << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK);
return (uint)_data[index];
// BMP uses a single level lookup. BMP index starts at offset 0 in the Trie2 index.
// 16 bit data is stored in the index array itself.
index = _data[codePoint >> UnicodeTrieBuilder.SHIFT_2];
index = (index << UnicodeTrieBuilder.INDEX_SHIFT) + (codePoint & UnicodeTrieBuilder.DATA_MASK);
return Unsafe.Add(ref dataBase, (nint)index);
}
if (codePoint <= 0xffff)
@ -109,20 +136,57 @@ namespace Avalonia.Media.TextFormatting.Unicode
// lead surrogate code units and code points.
// The main index has the code unit 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);
return (uint)_data[index];
// Note: this expression could be refactored for slightly improved efficiency, but
// 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)
{
// Supplemental code point, use two-level lookup.
index = _data[(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 = UnicodeTrieBuilder.INDEX_1_OFFSET - UnicodeTrieBuilder.OMITTED_BMP_INDEX_1_LENGTH + (codePoint >> UnicodeTrieBuilder.SHIFT_1);
index = _data[index];
index += (codePoint >> UnicodeTrieBuilder.SHIFT_2) & UnicodeTrieBuilder.INDEX_2_MASK;
index = _data[index];
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
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
var destIdx = 0;
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
for (i = 0; i < 0xc2 - 0xc0; i++)
{ // C0..C1
data[destIdx++] = (dataMove + BAD_UTF8_DATA_OFFSET);
data[destIdx++] = (uint)(dataMove + BAD_UTF8_DATA_OFFSET);
}
for (; i < 0xe0 - 0xc0; i++)
{ // C2..DF
data[destIdx++] = (dataMove + _index2[i << (6 - SHIFT_2)]);
data[destIdx++] = (uint)(dataMove + _index2[i << (6 - SHIFT_2)]);
}
if (_highStart > 0x10000)
@ -396,21 +396,21 @@ namespace Avalonia.Media.TextFormatting.Unicode
// write 16-bit index-1 values for supplementary code points
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,
// shifted right by INDEX_SHIFT, after adding dataMove
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
for (i = 0; i < _dataLength; i++)
{
data[destIdx++] = (int)_data[i];
data[destIdx++] = _data[i];
}
return new UnicodeTrie(data, _highStart, _errorValue);

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; }
}

12
src/Avalonia.Base/Platform/Storage/FolderPickerOpenOptions.cs

@ -0,0 +1,12 @@
namespace Avalonia.Platform.Storage;
/// <summary>
/// Options class for <see cref="IStorageProvider.OpenFolderPickerAsync"/> method.
/// </summary>
public class FolderPickerOpenOptions : PickerOptions
{
/// <summary>
/// Gets or sets an option indicating whether open picker allows users to select multiple folders.
/// </summary>
public bool AllowMultiple { get; set; }
}

20
src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs

@ -0,0 +1,20 @@
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
[NotClientImplementable]
public interface IStorageBookmarkItem : IStorageItem
{
Task ReleaseBookmark();
}
[NotClientImplementable]
public interface IStorageBookmarkFile : IStorageFile, IStorageBookmarkItem
{
}
[NotClientImplementable]
public interface IStorageBookmarkFolder : IStorageFolder, IStorageBookmarkItem
{
}

32
src/Avalonia.Base/Platform/Storage/IStorageFile.cs

@ -0,0 +1,32 @@
using System.IO;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Represents a file. Provides information about the file and its contents, and ways to manipulate them.
/// </summary>
[NotClientImplementable]
public interface IStorageFile : IStorageItem
{
/// <summary>
/// Returns true, if file is readable.
/// </summary>
bool CanOpenRead { get; }
/// <summary>
/// Opens a stream for read access.
/// </summary>
Task<Stream> OpenRead();
/// <summary>
/// Returns true, if file is writeable.
/// </summary>
bool CanOpenWrite { get; }
/// <summary>
/// Opens stream for writing to the file.
/// </summary>
Task<Stream> OpenWrite();
}

11
src/Avalonia.Base/Platform/Storage/IStorageFolder.cs

@ -0,0 +1,11 @@
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Manipulates folders and their contents, and provides information about them.
/// </summary>
[NotClientImplementable]
public interface IStorageFolder : IStorageItem
{
}

53
src/Avalonia.Base/Platform/Storage/IStorageItem.cs

@ -0,0 +1,53 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Manipulates storage items (files and folders) and their contents, and provides information about them
/// </summary>
/// <remarks>
/// This interface inherits <see cref="IDisposable"/> . It's recommended to dispose <see cref="IStorageItem"/> when it's not used anymore.
/// </remarks>
[NotClientImplementable]
public interface IStorageItem : IDisposable
{
/// <summary>
/// Gets the name of the item including the file name extension if there is one.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the full file-system path of the item, if the item has a path.
/// </summary>
/// <remarks>
/// Android backend might return file path with "content:" scheme.
/// Browser and iOS backends might return relative uris.
/// </remarks>
bool TryGetUri([NotNullWhen(true)] out Uri? uri);
/// <summary>
/// Gets the basic properties of the current item.
/// </summary>
Task<StorageItemProperties> GetBasicPropertiesAsync();
/// <summary>
/// Returns true is item can be bookmarked and reused later.
/// </summary>
bool CanBookmark { get; }
/// <summary>
/// Saves items to a bookmark.
/// </summary>
/// <returns>
/// Returns identifier of a bookmark. Can be null if OS denied request.
/// </returns>
Task<string?> SaveBookmark();
/// <summary>
/// Gets the parent folder of the current storage item.
/// </summary>
Task<IStorageFolder?> GetParentAsync();
}

56
src/Avalonia.Base/Platform/Storage/IStorageProvider.cs

@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
[NotClientImplementable]
public interface IStorageProvider
{
/// <summary>
/// Returns true if it's possible to open file picker on the current platform.
/// </summary>
bool CanOpen { get; }
/// <summary>
/// Opens file picker dialog.
/// </summary>
/// <returns>Array of selected <see cref="IStorageFile"/> or empty collection if user canceled the dialog.</returns>
Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options);
/// <summary>
/// Returns true if it's possible to open save file picker on the current platform.
/// </summary>
bool CanSave { get; }
/// <summary>
/// Opens save file picker dialog.
/// </summary>
/// <returns>Saved <see cref="IStorageFile"/> or null if user canceled the dialog.</returns>
Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options);
/// <summary>
/// Returns true if it's possible to open folder picker on the current platform.
/// </summary>
bool CanPickFolder { get; }
/// <summary>
/// Opens folder picker dialog.
/// </summary>
/// <returns>Array of selected <see cref="IStorageFolder"/> or empty collection if user canceled the dialog.</returns>
Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options);
/// <summary>
/// Open <see cref="IStorageBookmarkFile"/> from the bookmark ID.
/// </summary>
/// <param name="bookmark">Bookmark ID.</param>
/// <returns>Bookmarked file or null if OS denied request.</returns>
Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark);
/// <summary>
/// Open <see cref="IStorageBookmarkFolder"/> from the bookmark ID.
/// </summary>
/// <param name="bookmark">Bookmark ID.</param>
/// <returns>Bookmarked folder or null if OS denied request.</returns>
Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark);
}

17
src/Avalonia.Base/Platform/Storage/PickerOptions.cs

@ -0,0 +1,17 @@
namespace Avalonia.Platform.Storage;
/// <summary>
/// Common options for <see cref="IStorageProvider.OpenFolderPickerAsync"/>, <see cref="IStorageProvider.OpenFilePickerAsync"/> and <see cref="IStorageProvider.SaveFilePickerAsync"/> methods.
/// </summary>
public class PickerOptions
{
/// <summary>
/// Gets or sets the text that appears in the title bar of a folder dialog.
/// </summary>
public string? Title { get; set; }
/// <summary>
/// Gets or sets the initial location where the file open picker looks for files to present to the user.
/// </summary>
public IStorageFolder? SuggestedStartLocation { get; set; }
}

43
src/Avalonia.Base/Platform/Storage/StorageItemProperties.cs

@ -0,0 +1,43 @@
using System;
namespace Avalonia.Platform.Storage;
/// <summary>
/// Provides access to the content-related properties of an item (like a file or folder).
/// </summary>
public class StorageItemProperties
{
public StorageItemProperties(
ulong? size = null,
DateTimeOffset? dateCreated = null,
DateTimeOffset? dateModified = null)
{
Size = size;
DateCreated = dateCreated;
DateModified = dateModified;
}
/// <summary>
/// Gets the size of the file in bytes.
/// </summary>
/// <remarks>
/// Can be null if property is not available.
/// </remarks>
public ulong? Size { get; }
/// <summary>
/// Gets the date and time that the current folder was created.
/// </summary>
/// <remarks>
/// Can be null if property is not available.
/// </remarks>
public DateTimeOffset? DateCreated { get; }
/// <summary>
/// Gets the date and time of the last time the file was modified.
/// </summary>
/// <remarks>
/// Can be null if property is not available.
/// </remarks>
public DateTimeOffset? DateModified { get; }
}

14
src/Avalonia.Base/Point.cs

@ -188,7 +188,7 @@ namespace Avalonia
}
/// <summary>
/// Returns a boolean indicating whether the point is equal to the other given point.
/// Returns a boolean indicating whether the point is equal to the other given point (bitwise).
/// </summary>
/// <param name="other">The other point to test equality against.</param>
/// <returns>True if this point is equal to other; False otherwise.</returns>
@ -200,6 +200,18 @@ namespace Avalonia
// ReSharper enable CompareOfFloatsByEqualityOperator
}
/// <summary>
/// Returns a boolean indicating whether the point is equal to the other given point
/// (numerically).
/// </summary>
/// <param name="other">The other point to test equality against.</param>
/// <returns>True if this point is equal to other; False otherwise.</returns>
public bool NearlyEquals(Point other)
{
return MathUtilities.AreClose(_x, other._x) &&
MathUtilities.AreClose(_y, other._y);
}
/// <summary>
/// Checks for equality between a point and an object.
/// </summary>

1
src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs

@ -158,6 +158,7 @@ namespace Avalonia.Rendering.SceneGraph
if (result != null && result.Parent != parent)
{
Deindex(scene, result);
((VisualNode?)result.Parent)?.RemoveChild(result);
result = null;
}

26
src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Controls;
#nullable enable
@ -10,21 +11,17 @@ namespace Avalonia.Styling.Activators
/// An <see cref="IStyleActivator"/> which is active when a set of classes match those on a
/// control.
/// </summary>
internal sealed class StyleClassActivator : StyleActivatorBase
internal sealed class StyleClassActivator : StyleActivatorBase, IClassesChangedListener
{
private readonly IList<string> _match;
private readonly IAvaloniaReadOnlyList<string> _classes;
private NotifyCollectionChangedEventHandler? _classesChangedHandler;
private readonly Classes _classes;
public StyleClassActivator(IAvaloniaReadOnlyList<string> classes, IList<string> match)
public StyleClassActivator(Classes classes, IList<string> match)
{
_classes = classes;
_match = match;
}
private NotifyCollectionChangedEventHandler ClassesChangedHandler =>
_classesChangedHandler ??= ClassesChanged;
public static bool AreClassesMatching(IReadOnlyList<string> classes, IList<string> toMatch)
{
int remainingMatches = toMatch.Count;
@ -55,23 +52,20 @@ namespace Avalonia.Styling.Activators
return remainingMatches == 0;
}
protected override void Initialize()
void IClassesChangedListener.Changed()
{
PublishNext(IsMatching());
_classes.CollectionChanged += ClassesChangedHandler;
}
protected override void Deinitialize()
protected override void Initialize()
{
_classes.CollectionChanged -= ClassesChangedHandler;
PublishNext(IsMatching());
_classes.AddListener(this);
}
private void ClassesChanged(object? sender, NotifyCollectionChangedEventArgs e)
protected override void Deinitialize()
{
if (e.Action != NotifyCollectionChangedAction.Move)
{
PublishNext(IsMatching());
}
_classes.RemoveListener(this);
}
private bool IsMatching() => AreClassesMatching(_classes, _match);

3
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls;
using Avalonia.Styling.Activators;
#nullable enable
@ -125,7 +126,7 @@ namespace Avalonia.Styling
{
if (subscribe)
{
var observable = new StyleClassActivator(control.Classes, _classes.Value);
var observable = new StyleClassActivator((Classes)control.Classes, _classes.Value);
return new SelectorMatch(observable);
}

2
src/Avalonia.Base/Utilities/ArrayBuilder.cs

@ -101,7 +101,7 @@ namespace Avalonia.Utilities
/// </summary>
/// <param name="value">The array slice.</param>
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
public ArraySlice<T> Add(in ReadOnlySlice<T> value)
public ArraySlice<T> Add(in ArraySlice<T> value)
{
var position = _size;

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

Loading…
Cancel
Save