Browse Source

Merge branch 'master' into feature/IAddChild

pull/8436/head
Benedikt Stebner 4 years ago
committed by GitHub
parent
commit
14a86320f0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .editorconfig
  2. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  3. 18
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  4. 1
      samples/ControlCatalog.NetCore/Program.cs
  5. 2
      samples/ControlCatalog/MainView.xaml
  6. 235
      samples/ControlCatalog/Pages/PointerCanvas.cs
  7. 109
      samples/ControlCatalog/Pages/PointerContactsTab.cs
  8. 322
      samples/ControlCatalog/Pages/PointersPage.cs
  9. 66
      samples/ControlCatalog/Pages/PointersPage.xaml
  10. 78
      samples/ControlCatalog/Pages/PointersPage.xaml.cs
  11. 21
      samples/IntegrationTestApp/MainWindow.axaml
  12. 66
      samples/IntegrationTestApp/MainWindow.axaml.cs
  13. 35
      samples/IntegrationTestApp/ShowWindowTest.axaml
  14. 40
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  15. 119
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  16. 5
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  17. 4
      src/Avalonia.Base/Input/IInputElement.cs
  18. 7
      src/Avalonia.Base/Input/IKeyboardDevice.cs
  19. 10
      src/Avalonia.Base/Input/IPenDevice.cs
  20. 43
      src/Avalonia.Base/Input/IPointer.cs
  21. 58
      src/Avalonia.Base/Input/InputElement.cs
  22. 174
      src/Avalonia.Base/Input/PenDevice.cs
  23. 22
      src/Avalonia.Base/Input/PointerEventArgs.cs
  24. 6
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  25. 125
      src/Avalonia.Base/Input/PointerPoint.cs
  26. 19
      src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
  27. 17
      src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs
  28. 13
      src/Avalonia.Base/Input/TouchDevice.cs
  29. 71
      src/Avalonia.Base/Layout/LayoutHelper.cs
  30. 24
      src/Avalonia.Base/Layout/Layoutable.cs
  31. 8
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  32. 498
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs
  33. 28
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
  34. 25
      src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs
  35. 44
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  36. 7
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
  37. 422
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs
  38. 1
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs
  39. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
  40. 5
      src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs
  41. 42
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
  42. 1192
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs
  43. 156
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
  44. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
  45. 14
      src/Avalonia.Base/Point.cs
  46. 1
      src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs
  47. 2
      src/Avalonia.Base/Utilities/ArrayBuilder.cs
  48. 2
      src/Avalonia.Base/Utilities/ArraySlice.cs
  49. 40
      src/Avalonia.Base/Utilities/BidiDictionary.cs
  50. 6
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  51. 20
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
  52. 12
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  53. 2
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  54. 12
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  55. 8
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  56. 8
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  57. 8
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  58. 12
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  59. 2
      src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
  60. 4
      src/Avalonia.Controls/GridSplitter.cs
  61. 52
      src/Avalonia.Controls/MenuItem.cs
  62. 16
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  63. 4
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  64. 8
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  65. 2
      src/Avalonia.Controls/Selection/InternalSelectionModel.cs
  66. 12
      src/Avalonia.Controls/ToolTipService.cs
  67. 6
      src/Avalonia.Controls/TopLevel.cs
  68. 52
      src/Avalonia.Controls/Window.cs
  69. 10
      src/Avalonia.Controls/WindowBase.cs
  70. 28
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
  71. 4
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
  72. 12
      src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml
  73. 12
      src/Shared/RawEventGrouping.cs
  74. 2
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  75. 2
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  76. 217
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  77. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  78. 429
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  79. 53
      src/Windows/Avalonia.Win32/WindowImpl.cs
  80. 52
      tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
  81. 24
      tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs
  82. 31
      tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs
  83. 140
      tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_LayoutRounding.cs
  84. 2
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs
  85. 9
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs
  86. 2
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs
  87. 9
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs
  88. 84
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs
  89. 108
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs
  90. 2
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs
  91. 85
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs
  92. 58
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs
  93. 4
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs
  94. 10
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs
  95. 64
      tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  96. 65
      tests/Avalonia.Controls.UnitTests/BorderTests.cs
  97. 12
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  98. 41
      tests/Avalonia.Controls.UnitTests/DecoratorTests.cs
  99. 64
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
  100. 66
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.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_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true csharp_new_line_between_query_expression_clauses = true
trim_trailing_whitespace = true # trim_trailing_whitespace = true
# Indentation preferences # Indentation preferences
csharp_indent_block_contents = true csharp_indent_block_contents = true

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

@ -224,7 +224,7 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
if (ret == nullptr) if (ret == nullptr)
return E_POINTER; return E_POINTER;
if(Window != nullptr){ if(Window != nullptr && _shown){
auto frame = [Window frame]; auto frame = [Window frame];
ret->Width = frame.size.width; ret->Width = frame.size.width;
ret->Height = frame.size.height; ret->Height = frame.size.height;

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

@ -119,13 +119,16 @@ void WindowImpl::BringToFront()
{ {
if(Window != nullptr) if(Window != nullptr)
{ {
if(IsDialog()) if (![Window isMiniaturized])
{ {
Activate(); if(IsDialog())
} {
else Activate();
{ }
[Window orderFront:nullptr]; else
{
[Window orderFront:nullptr];
}
} }
[Window invalidateShadow]; [Window invalidateShadow];
@ -488,6 +491,8 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
} }
if (_shown) { if (_shown) {
_actualWindowState = _lastWindowState;
switch (state) { switch (state) {
case Maximized: case Maximized:
if (currentState == FullScreen) { if (currentState == FullScreen) {
@ -545,7 +550,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
break; break;
} }
_actualWindowState = _lastWindowState;
WindowEvents->WindowStateChanged(_actualWindowState); WindowEvents->WindowStateChanged(_actualWindowState);
} }

1
samples/ControlCatalog.NetCore/Program.cs

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

2
samples/ControlCatalog/MainView.xaml

@ -109,7 +109,7 @@
<TabItem Header="OpenGL"> <TabItem Header="OpenGL">
<pages:OpenGlPage /> <pages:OpenGlPage />
</TabItem> </TabItem>
<TabItem Header="Pointers (Touch)"> <TabItem Header="Pointers">
<pages:PointersPage /> <pages:PointersPage />
</TabItem> </TabItem>
<TabItem Header="ProgressBar"> <TabItem Header="ProgressBar">

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

21
samples/IntegrationTestApp/MainWindow.axaml

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

66
samples/IntegrationTestApp/MainWindow.axaml.cs

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

35
samples/IntegrationTestApp/ShowWindowTest.axaml

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

40
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -159,7 +159,6 @@ namespace Avalonia.Media.TextFormatting
{ {
var flowDirection = paragraphProperties.FlowDirection; var flowDirection = paragraphProperties.FlowDirection;
var drawableTextRuns = new List<DrawableTextRun>(); var drawableTextRuns = new List<DrawableTextRun>();
var biDiData = new BidiData((sbyte)flowDirection); var biDiData = new BidiData((sbyte)flowDirection);
foreach (var textRun in textRuns) foreach (var textRun in textRuns)
@ -174,10 +173,9 @@ namespace Avalonia.Media.TextFormatting
{ {
biDiData.Append(textRun.Text); biDiData.Append(textRun.Text);
} }
} }
var biDi = BidiAlgorithm.Instance.Value!; var biDi = new BidiAlgorithm();
biDi.Process(biDiData); biDi.Process(biDiData);
@ -290,9 +288,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textCharacters">The text characters to form <see cref="ShapeableTextCharacters"/> from.</param> /// <param name="textCharacters">The text characters to form <see cref="ShapeableTextCharacters"/> from.</param>
/// <param name="levels">The bidi levels.</param> /// <param name="levels">The bidi levels.</param>
/// <returns></returns> /// <returns></returns>
private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels( private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels(IReadOnlyList<TextRun> textCharacters, ArraySlice<sbyte> levels)
IReadOnlyList<TextRun> textCharacters,
ReadOnlySlice<sbyte> levels)
{ {
if (levels.Length == 0) if (levels.Length == 0)
{ {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -36,29 +36,9 @@ namespace Avalonia.Media.TextFormatting.Unicode
static UnicodeData() static UnicodeData()
{ {
unsafe s_unicodeDataTrie = new UnicodeTrie(UnicodeDataTrie.Data);
{ s_graphemeBreakTrie = new UnicodeTrie(GraphemeBreakTrie.Data);
var unicodeData = UnicodeDataTrie.Data; s_biDiTrie = new UnicodeTrie(BidiTrie.Data);
fixed (byte* unicodeDataPtr = unicodeData)
{
s_unicodeDataTrie = new UnicodeTrie(new UnmanagedMemoryStream(unicodeDataPtr, unicodeData.Length));
}
var graphemeData = GraphemeBreakTrie.Data;
fixed (byte* graphemeDataPtr = graphemeData)
{
s_graphemeBreakTrie = new UnicodeTrie(new UnmanagedMemoryStream(graphemeDataPtr, graphemeData.Length));
}
var bidiData = BiDiTrie.Data;
fixed (byte* bidiDataPtr = bidiData)
{
s_biDiTrie = new UnicodeTrie(new UnmanagedMemoryStream(bidiDataPtr, bidiData.Length));
}
}
} }
/// <summary> /// <summary>
@ -67,7 +47,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's general category.</returns> /// <returns>The code point's general category.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GeneralCategory GetGeneralCategory(int codepoint) public static GeneralCategory GetGeneralCategory(uint codepoint)
{ {
return (GeneralCategory)(s_unicodeDataTrie.Get(codepoint) & CATEGORY_MASK); return (GeneralCategory)(s_unicodeDataTrie.Get(codepoint) & CATEGORY_MASK);
} }
@ -78,7 +58,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's script.</returns> /// <returns>The code point's script.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Script GetScript(int codepoint) public static Script GetScript(uint codepoint)
{ {
return (Script)((s_unicodeDataTrie.Get(codepoint) >> SCRIPT_SHIFT) & SCRIPT_MASK); return (Script)((s_unicodeDataTrie.Get(codepoint) >> SCRIPT_SHIFT) & SCRIPT_MASK);
} }
@ -89,7 +69,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's biDi class.</returns> /// <returns>The code point's biDi class.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BidiClass GetBiDiClass(int codepoint) public static BidiClass GetBiDiClass(uint codepoint)
{ {
return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK); return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK);
} }
@ -100,7 +80,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket type.</returns> /// <returns>The code point's paired bracket type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BidiPairedBracketType GetBiDiPairedBracketType(int codepoint) public static BidiPairedBracketType GetBiDiPairedBracketType(uint codepoint)
{ {
return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK); return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK);
} }
@ -111,9 +91,9 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's paired bracket.</returns> /// <returns>The code point's paired bracket.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Codepoint GetBiDiPairedBracket(int codepoint) public static Codepoint GetBiDiPairedBracket(uint codepoint)
{ {
return new Codepoint((int)(s_biDiTrie.Get(codepoint) & BIDIPAIREDBRACKED_MASK)); return new Codepoint((s_biDiTrie.Get(codepoint) & BIDIPAIREDBRACKED_MASK));
} }
/// <summary> /// <summary>
@ -122,7 +102,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's line break class.</returns> /// <returns>The code point's line break class.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static LineBreakClass GetLineBreakClass(int codepoint) public static LineBreakClass GetLineBreakClass(uint codepoint)
{ {
return (LineBreakClass)((s_unicodeDataTrie.Get(codepoint) >> LINEBREAK_SHIFT) & LINEBREAK_MASK); return (LineBreakClass)((s_unicodeDataTrie.Get(codepoint) >> LINEBREAK_SHIFT) & LINEBREAK_MASK);
} }
@ -133,7 +113,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="codepoint">The codepoint in question.</param> /// <param name="codepoint">The codepoint in question.</param>
/// <returns>The code point's grapheme break type.</returns> /// <returns>The code point's grapheme break type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GraphemeBreakClass GetGraphemeClusterBreak(int codepoint) public static GraphemeBreakClass GetGraphemeClusterBreak(uint codepoint)
{ {
return (GraphemeBreakClass)s_graphemeBreakTrie.Get(codepoint); return (GraphemeBreakClass)s_graphemeBreakTrie.Get(codepoint);
} }

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

File diff suppressed because it is too large

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

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

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

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

14
src/Avalonia.Base/Point.cs

@ -188,7 +188,7 @@ namespace Avalonia
} }
/// <summary> /// <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> /// </summary>
/// <param name="other">The other point to test equality against.</param> /// <param name="other">The other point to test equality against.</param>
/// <returns>True if this point is equal to other; False otherwise.</returns> /// <returns>True if this point is equal to other; False otherwise.</returns>
@ -200,6 +200,18 @@ namespace Avalonia
// ReSharper enable CompareOfFloatsByEqualityOperator // 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> /// <summary>
/// Checks for equality between a point and an object. /// Checks for equality between a point and an object.
/// </summary> /// </summary>

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

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

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

@ -101,7 +101,7 @@ namespace Avalonia.Utilities
/// </summary> /// </summary>
/// <param name="value">The array slice.</param> /// <param name="value">The array slice.</param>
/// <returns>The <see cref="ArraySlice{T}"/>.</returns> /// <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; var position = _size;

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

@ -116,7 +116,7 @@ namespace Avalonia.Utilities
/// </summary> /// </summary>
public static implicit operator ReadOnlySlice<T>(ArraySlice<T> slice) public static implicit operator ReadOnlySlice<T>(ArraySlice<T> slice)
{ {
return new ReadOnlySlice<T>(slice._data).AsSlice(slice.Start, slice.Length); return new ReadOnlySlice<T>(slice._data, 0, slice.Length, slice.Start);
} }
/// <summary> /// <summary>

40
src/Avalonia.Base/Utilities/BidiDictionary.cs

@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace Avalonia.Utilities
{
/// <summary>
/// A simple bi-directional dictionary.
/// </summary>
/// <typeparam name="T1">Key type</typeparam>
/// <typeparam name="T2">Value type</typeparam>
internal sealed class BidiDictionary<T1, T2> where T1 : notnull where T2 : notnull
{
public Dictionary<T1, T2> Forward { get; } = new Dictionary<T1, T2>();
public Dictionary<T2, T1> Reverse { get; } = new Dictionary<T2, T1>();
public void Clear()
{
Forward.Clear();
Reverse.Clear();
}
public void Add(T1 key, T2 value)
{
Forward.Add(key, value);
Reverse.Add(value, key);
}
#pragma warning disable CS8601
public bool TryGetValue(T1 key, out T2 value) => Forward.TryGetValue(key, out value);
#pragma warning restore CS8601
#pragma warning disable CS8601
public bool TryGetKey(T2 value, out T1 key) => Reverse.TryGetValue(value, out key);
#pragma warning restore CS8601
public bool ContainsKey(T1 key) => Forward.ContainsKey(key);
public bool ContainsValue(T2 value) => Reverse.ContainsKey(value);
}
}

6
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -49,9 +49,9 @@ namespace Avalonia.Build.Tasks
string projectDirectory, string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
{ {
var typeSystem = new CecilTypeSystem(references var typeSystem = new CecilTypeSystem(
.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
.Concat(new[] { input }), input); input);
var asm = typeSystem.TargetAssemblyDefinition; var asm = typeSystem.TargetAssemblyDefinition;

20
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs

@ -128,8 +128,8 @@ namespace Avalonia.Controls.Primitives
if (_inputTarget != null) if (_inputTarget != null)
{ {
_inputTarget.PointerEnter += InputTarget_PointerEnter; _inputTarget.PointerEntered += InputTarget_PointerEntered;
_inputTarget.PointerLeave += InputTarget_PointerLeave; _inputTarget.PointerExited += InputTarget_PointerExited;
_inputTarget.PointerPressed += InputTarget_PointerPressed; _inputTarget.PointerPressed += InputTarget_PointerPressed;
_inputTarget.PointerMoved += InputTarget_PointerMoved; _inputTarget.PointerMoved += InputTarget_PointerMoved;
_inputTarget.PointerReleased += InputTarget_PointerReleased; _inputTarget.PointerReleased += InputTarget_PointerReleased;
@ -194,8 +194,8 @@ namespace Avalonia.Controls.Primitives
if (_inputTarget != null) if (_inputTarget != null)
{ {
_inputTarget.PointerEnter -= InputTarget_PointerEnter; _inputTarget.PointerEntered -= InputTarget_PointerEntered;
_inputTarget.PointerLeave -= InputTarget_PointerLeave; _inputTarget.PointerExited -= InputTarget_PointerExited;
_inputTarget.PointerPressed -= InputTarget_PointerPressed; _inputTarget.PointerPressed -= InputTarget_PointerPressed;
_inputTarget.PointerMoved -= InputTarget_PointerMoved; _inputTarget.PointerMoved -= InputTarget_PointerMoved;
_inputTarget.PointerReleased -= InputTarget_PointerReleased; _inputTarget.PointerReleased -= InputTarget_PointerReleased;
@ -362,7 +362,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
// We only want to bother with the color name tool tip if we can provide color names. // We only want to bother with the color name tool tip if we can provide color names.
if (_selectionEllipsePanel != null && if (_selectionEllipsePanel != null &&
@ -373,7 +373,7 @@ namespace Avalonia.Controls.Primitives
UpdatePseudoClasses(); UpdatePseudoClasses();
base.OnPointerLeave(e); base.OnPointerExited(e);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -848,16 +848,16 @@ namespace Avalonia.Controls.Primitives
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
/// <inheritdoc cref="InputElement.PointerEnter"/> /// <inheritdoc cref="InputElement.PointerEntered"/>
private void InputTarget_PointerEnter(object? sender, PointerEventArgs args) private void InputTarget_PointerEntered(object? sender, PointerEventArgs args)
{ {
_isPointerOver = true; _isPointerOver = true;
UpdatePseudoClasses(); UpdatePseudoClasses();
args.Handled = true; args.Handled = true;
} }
/// <inheritdoc cref="InputElement.PointerLeave"/> /// <inheritdoc cref="InputElement.PointerExited"/>
private void InputTarget_PointerLeave(object? sender, PointerEventArgs args) private void InputTarget_PointerExited(object? sender, PointerEventArgs args)
{ {
_isPointerOver = false; _isPointerOver = false;
UpdatePseudoClasses(); UpdatePseudoClasses();

12
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -139,18 +139,18 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
base.OnPointerEnter(e); base.OnPointerEntered(e);
if (OwningRow != null) if (OwningRow != null)
{ {
IsMouseOver = true; IsMouseOver = true;
} }
} }
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
base.OnPointerLeave(e); base.OnPointerExited(e);
if (OwningRow != null) if (OwningRow != null)
{ {
@ -178,9 +178,9 @@ namespace Avalonia.Controls
{ {
var handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled); var handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
// Do not handle PointerPressed with touch, // Do not handle PointerPressed with touch or pen,
// so we can start scroll gesture on the same event. // so we can start scroll gesture on the same event.
if (e.Pointer.Type != PointerType.Touch) if (e.Pointer.Type != PointerType.Touch && e.Pointer.Type != PointerType.Pen)
{ {
e.Handled = handled; e.Handled = handled;
} }

2
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -855,7 +855,7 @@ namespace Avalonia.Controls
if (OwningGrid != null && OwningGrid.UseLayoutRounding) if (OwningGrid != null && OwningGrid.UseLayoutRounding)
{ {
var scale = LayoutHelper.GetLayoutScale(HeaderCell); var scale = LayoutHelper.GetLayoutScale(HeaderCell);
var roundSize = LayoutHelper.RoundLayoutSize(new Size(leftEdge + ActualWidth, 1), scale, scale); var roundSize = LayoutHelper.RoundLayoutSizeUp(new Size(leftEdge + ActualWidth, 1), scale, scale);
LayoutRoundedWidth = roundSize.Width - leftEdge; LayoutRoundedWidth = roundSize.Width - leftEdge;
} }
else else

12
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -83,9 +83,9 @@ namespace Avalonia.Controls
{ {
PointerPressed += DataGridColumnHeader_PointerPressed; PointerPressed += DataGridColumnHeader_PointerPressed;
PointerReleased += DataGridColumnHeader_PointerReleased; PointerReleased += DataGridColumnHeader_PointerReleased;
PointerMoved += DataGridColumnHeader_PointerMove; PointerMoved += DataGridColumnHeader_PointerMoved;
PointerEnter += DataGridColumnHeader_PointerEnter; PointerEntered += DataGridColumnHeader_PointerEntered;
PointerLeave += DataGridColumnHeader_PointerLeave; PointerExited += DataGridColumnHeader_PointerExited;
} }
private void OnAreSeparatorsVisibleChanged(AvaloniaPropertyChangedEventArgs e) private void OnAreSeparatorsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
@ -452,7 +452,7 @@ namespace Avalonia.Controls
SetDragCursor(mousePosition); SetDragCursor(mousePosition);
} }
private void DataGridColumnHeader_PointerEnter(object sender, PointerEventArgs e) private void DataGridColumnHeader_PointerEntered(object sender, PointerEventArgs e)
{ {
if (!IsEnabled) if (!IsEnabled)
{ {
@ -464,7 +464,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
private void DataGridColumnHeader_PointerLeave(object sender, PointerEventArgs e) private void DataGridColumnHeader_PointerExited(object sender, PointerEventArgs e)
{ {
if (!IsEnabled) if (!IsEnabled)
{ {
@ -506,7 +506,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
private void DataGridColumnHeader_PointerMove(object sender, PointerEventArgs e) private void DataGridColumnHeader_PointerMoved(object sender, PointerEventArgs e)
{ {
if (OwningGrid == null || !IsEnabled) if (OwningGrid == null || !IsEnabled)
{ {

8
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -607,15 +607,15 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
base.OnPointerEnter(e); base.OnPointerEntered(e);
IsMouseOver = true; IsMouseOver = true;
} }
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
IsMouseOver = false; IsMouseOver = false;
base.OnPointerLeave(e); base.OnPointerExited(e);
} }
internal void ApplyCellsState() internal void ApplyCellsState()

8
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -375,7 +375,7 @@ namespace Avalonia.Controls
ApplyHeaderStatus(); ApplyHeaderStatus();
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
if (IsEnabled) if (IsEnabled)
{ {
@ -383,10 +383,10 @@ namespace Avalonia.Controls
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
base.OnPointerEnter(e); base.OnPointerEntered(e);
} }
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
if (IsEnabled) if (IsEnabled)
{ {
@ -394,7 +394,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
base.OnPointerLeave(e); base.OnPointerExited(e);
} }
private void SetIsCheckedNoCallBack(bool value) private void SetIsCheckedNoCallBack(bool value)

8
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@ -158,23 +158,23 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
if (OwningRow != null) if (OwningRow != null)
{ {
OwningRow.IsMouseOver = true; OwningRow.IsMouseOver = true;
} }
base.OnPointerEnter(e); base.OnPointerEntered(e);
} }
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
if (OwningRow != null) if (OwningRow != null)
{ {
OwningRow.IsMouseOver = false; OwningRow.IsMouseOver = false;
} }
base.OnPointerLeave(e); base.OnPointerExited(e);
} }
//TODO TabStop //TODO TabStop

12
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -189,7 +189,7 @@ namespace Avalonia.Controls.Primitives
EventHandler<PointerPressedEventArgs> cellMouseLeftButtonDown = Cell_MouseLeftButtonDown; EventHandler<PointerPressedEventArgs> cellMouseLeftButtonDown = Cell_MouseLeftButtonDown;
EventHandler<PointerReleasedEventArgs> cellMouseLeftButtonUp = Cell_MouseLeftButtonUp; EventHandler<PointerReleasedEventArgs> cellMouseLeftButtonUp = Cell_MouseLeftButtonUp;
EventHandler<PointerEventArgs> cellMouseEnter = Cell_MouseEnter; EventHandler<PointerEventArgs> cellMouseEntered = Cell_MouseEntered;
EventHandler<RoutedEventArgs> cellClick = Cell_Click; EventHandler<RoutedEventArgs> cellClick = Cell_Click;
for (int i = 1; i < Calendar.RowsPerMonth; i++) for (int i = 1; i < Calendar.RowsPerMonth; i++)
@ -206,7 +206,7 @@ namespace Avalonia.Controls.Primitives
cell.SetValue(Grid.ColumnProperty, j); cell.SetValue(Grid.ColumnProperty, j);
cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown; cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp; cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
cell.PointerEnter += cellMouseEnter; cell.PointerEntered += cellMouseEntered;
cell.Click += cellClick; cell.Click += cellClick;
children.Add(cell); children.Add(cell);
} }
@ -222,7 +222,7 @@ namespace Avalonia.Controls.Primitives
EventHandler<PointerPressedEventArgs> monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown; EventHandler<PointerPressedEventArgs> monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
EventHandler<PointerReleasedEventArgs> monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp; EventHandler<PointerReleasedEventArgs> monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;
EventHandler<PointerEventArgs> monthMouseEnter = Month_MouseEnter; EventHandler<PointerEventArgs> monthMouseEntered = Month_MouseEntered;
for (int i = 0; i < Calendar.RowsPerYear; i++) for (int i = 0; i < Calendar.RowsPerYear; i++)
{ {
@ -238,7 +238,7 @@ namespace Avalonia.Controls.Primitives
month.SetValue(Grid.ColumnProperty, j); month.SetValue(Grid.ColumnProperty, j);
month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown; month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown;
month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp; month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp;
month.PointerEnter += monthMouseEnter; month.PointerEntered += monthMouseEntered;
children.Add(month); children.Add(month);
} }
} }
@ -876,7 +876,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
internal void Cell_MouseEnter(object? sender, PointerEventArgs e) internal void Cell_MouseEntered(object? sender, PointerEventArgs e)
{ {
if (Owner != null) if (Owner != null)
{ {
@ -1162,7 +1162,7 @@ namespace Avalonia.Controls.Primitives
} }
} }
private void Month_MouseEnter(object? sender, PointerEventArgs e) private void Month_MouseEntered(object? sender, PointerEventArgs e)
{ {
if (_isMouseLeftButtonDownYearView) if (_isMouseLeftButtonDownYearView)
{ {

2
src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs

@ -26,7 +26,7 @@ namespace Avalonia.Controls.Converters
if (visibility == ScrollBarVisibility.Auto) if (visibility == ScrollBarVisibility.Auto)
{ {
if (extent == viewport) if (MathUtilities.AreClose(extent, viewport))
{ {
return false; return false;
} }

4
src/Avalonia.Controls/GridSplitter.cs

@ -349,9 +349,9 @@ namespace Avalonia.Controls
} }
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
base.OnPointerEnter(e); base.OnPointerEntered(e);
GridResizeDirection direction = GetEffectiveResizeDirection(); GridResizeDirection direction = GetEffectiveResizeDirection();

52
src/Avalonia.Controls/MenuItem.cs

@ -79,25 +79,33 @@ namespace Avalonia.Controls
/// Defines the <see cref="Click"/> event. /// Defines the <see cref="Click"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent = public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble); RoutedEvent.Register<MenuItem, RoutedEventArgs>(
nameof(Click),
RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Defines the <see cref="PointerEnterItem"/> event. /// Defines the <see cref="PointerEnteredItem"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerEnterItemEvent = public static readonly RoutedEvent<PointerEventArgs> PointerEnteredItemEvent =
RoutedEvent.Register<MenuItem, PointerEventArgs>(nameof(PointerEnterItem), RoutingStrategies.Bubble); RoutedEvent.Register<MenuItem, PointerEventArgs>(
nameof(PointerEnteredItem),
RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Defines the <see cref="PointerLeaveItem"/> event. /// Defines the <see cref="PointerExitedItem"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveItemEvent = public static readonly RoutedEvent<PointerEventArgs> PointerExitedItemEvent =
RoutedEvent.Register<MenuItem, PointerEventArgs>(nameof(PointerLeaveItem), RoutingStrategies.Bubble); RoutedEvent.Register<MenuItem, PointerEventArgs>(
nameof(PointerExitedItem),
RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Defines the <see cref="SubmenuOpened"/> event. /// Defines the <see cref="SubmenuOpened"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<RoutedEventArgs> SubmenuOpenedEvent = public static readonly RoutedEvent<RoutedEventArgs> SubmenuOpenedEvent =
RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(SubmenuOpened), RoutingStrategies.Bubble); RoutedEvent.Register<MenuItem, RoutedEventArgs>(
nameof(SubmenuOpened),
RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property. /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
@ -174,24 +182,24 @@ namespace Avalonia.Controls
/// Occurs when the pointer enters a menu item. /// Occurs when the pointer enters a menu item.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A bubbling version of the <see cref="InputElement.PointerEnter"/> event for menu items. /// A bubbling version of the <see cref="InputElement.PointerEntered"/> event for menu items.
/// </remarks> /// </remarks>
public event EventHandler<PointerEventArgs>? PointerEnterItem public event EventHandler<PointerEventArgs>? PointerEnteredItem
{ {
add { AddHandler(PointerEnterItemEvent, value); } add { AddHandler(PointerEnteredItemEvent, value); }
remove { RemoveHandler(PointerEnterItemEvent, value); } remove { RemoveHandler(PointerEnteredItemEvent, value); }
} }
/// <summary> /// <summary>
/// Raised when the pointer leaves a menu item. /// Raised when the pointer leaves a menu item.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A bubbling version of the <see cref="InputElement.PointerLeave"/> event for menu items. /// A bubbling version of the <see cref="InputElement.PointerExited"/> event for menu items.
/// </remarks> /// </remarks>
public event EventHandler<PointerEventArgs>? PointerLeaveItem public event EventHandler<PointerEventArgs>? PointerExitedItem
{ {
add { AddHandler(PointerLeaveItemEvent, value); } add { AddHandler(PointerExitedItemEvent, value); }
remove { RemoveHandler(PointerLeaveItemEvent, value); } remove { RemoveHandler(PointerExitedItemEvent, value); }
} }
/// <summary> /// <summary>
@ -437,22 +445,22 @@ namespace Avalonia.Controls
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
base.OnPointerEnter(e); base.OnPointerEntered(e);
var point = e.GetCurrentPoint(null); var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerEnterItemEvent, this, e.Pointer, this.VisualRoot, point.Position, RaiseEvent(new PointerEventArgs(PointerEnteredItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
e.Timestamp, point.Properties, e.KeyModifiers)); e.Timestamp, point.Properties, e.KeyModifiers));
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
base.OnPointerLeave(e); base.OnPointerExited(e);
var point = e.GetCurrentPoint(null); var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerLeaveItemEvent, this, e.Pointer, this.VisualRoot, point.Position, RaiseEvent(new PointerEventArgs(PointerExitedItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
e.Timestamp, point.Properties, e.KeyModifiers)); e.Timestamp, point.Properties, e.KeyModifiers));
} }

16
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -53,9 +53,9 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed += PointerPressed; Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased; Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
_root = Menu.VisualRoot; _root = Menu.VisualRoot;
@ -89,9 +89,9 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed -= PointerPressed; Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased; Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
if (_root is InputElement inputRoot) if (_root is InputElement inputRoot)
@ -297,7 +297,7 @@ namespace Avalonia.Controls.Platform
e.Handled = true; e.Handled = true;
} }
protected internal virtual void PointerEnter(object? sender, PointerEventArgs e) protected internal virtual void PointerEntered(object? sender, PointerEventArgs e)
{ {
var item = GetMenuItem(e.Source as IControl); var item = GetMenuItem(e.Source as IControl);
@ -358,7 +358,7 @@ namespace Avalonia.Controls.Platform
} }
} }
protected internal virtual void PointerLeave(object? sender, PointerEventArgs e) protected internal virtual void PointerExited(object? sender, PointerEventArgs e)
{ {
var item = GetMenuItem(e.Source as IControl); var item = GetMenuItem(e.Source as IControl);

4
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -635,8 +635,8 @@ namespace Avalonia.Controls.Presenters
if (useLayoutRounding) if (useLayoutRounding)
{ {
sizeForChild = LayoutHelper.RoundLayoutSize(sizeForChild, scale, scale); sizeForChild = LayoutHelper.RoundLayoutSizeUp(sizeForChild, scale, scale);
availableSize = LayoutHelper.RoundLayoutSize(availableSize, scale, scale); availableSize = LayoutHelper.RoundLayoutSizeUp(availableSize, scale, scale);
} }
switch (horizontalContentAlignment) switch (horizontalContentAlignment)

8
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -218,9 +218,9 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void OnPointerEnter(PointerEventArgs e) protected override void OnPointerEntered(PointerEventArgs e)
{ {
base.OnPointerEnter(e); base.OnPointerEntered(e);
if (AllowAutoHide) if (AllowAutoHide)
{ {
@ -228,9 +228,9 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void OnPointerLeave(PointerEventArgs e) protected override void OnPointerExited(PointerEventArgs e)
{ {
base.OnPointerLeave(e); base.OnPointerExited(e);
if (AllowAutoHide) if (AllowAutoHide)
{ {

2
src/Avalonia.Controls/Selection/InternalSelectionModel.cs

@ -168,7 +168,7 @@ namespace Avalonia.Controls.Selection
{ {
if (_writableSelectedItems is INotifyCollectionChanged incc) if (_writableSelectedItems is INotifyCollectionChanged incc)
{ {
incc.CollectionChanged += OnSelectedItemsCollectionChanged; incc.CollectionChanged -= OnSelectedItemsCollectionChanged;
} }
} }

12
src/Avalonia.Controls/ToolTipService.cs

@ -26,14 +26,14 @@ namespace Avalonia.Controls
if (e.OldValue != null) if (e.OldValue != null)
{ {
control.PointerEnter -= ControlPointerEnter; control.PointerEntered -= ControlPointerEntered;
control.PointerLeave -= ControlPointerLeave; control.PointerExited -= ControlPointerExited;
} }
if (e.NewValue != null) if (e.NewValue != null)
{ {
control.PointerEnter += ControlPointerEnter; control.PointerEntered += ControlPointerEntered;
control.PointerLeave += ControlPointerLeave; control.PointerExited += ControlPointerExited;
} }
if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip)) if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip))
@ -80,7 +80,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void ControlPointerEnter(object? sender, PointerEventArgs e) private void ControlPointerEntered(object? sender, PointerEventArgs e)
{ {
StopTimer(); StopTimer();
@ -101,7 +101,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="sender">The event sender.</param> /// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void ControlPointerLeave(object? sender, PointerEventArgs e) private void ControlPointerExited(object? sender, PointerEventArgs e)
{ {
var control = (Control)sender!; var control = (Control)sender!;
Close(control); Close(control);

6
src/Avalonia.Controls/TopLevel.cs

@ -484,7 +484,11 @@ namespace Avalonia.Controls
/// Raises the <see cref="Opened"/> event. /// Raises the <see cref="Opened"/> event.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnOpened(EventArgs e) => Opened?.Invoke(this, e); protected virtual void OnOpened(EventArgs e)
{
FrameSize = PlatformImpl?.FrameSize;
Opened?.Invoke(this, e);
}
/// <summary> /// <summary>
/// Raises the <see cref="Closed"/> event. /// Raises the <see cref="Closed"/> event.

52
src/Avalonia.Controls/Window.cs

@ -871,10 +871,10 @@ namespace Avalonia.Controls
var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1; var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
// TODO: We really need non-client size here. // Use frame size, falling back to client size if the platform can't give it to us.
var rect = new PixelRect( var rect = FrameSize.HasValue ?
PixelPoint.Origin, new PixelRect(PixelSize.FromSize(FrameSize.Value, scaling)) :
PixelSize.FromSize(ClientSize, scaling)); new PixelRect(PixelSize.FromSize(ClientSize, scaling));
if (startupLocation == WindowStartupLocation.CenterScreen) if (startupLocation == WindowStartupLocation.CenterScreen)
{ {
@ -901,10 +901,10 @@ namespace Avalonia.Controls
{ {
if (owner != null) if (owner != null)
{ {
// TODO: We really need non-client size here. var ownerSize = owner.FrameSize ?? owner.ClientSize;
var ownerRect = new PixelRect( var ownerRect = new PixelRect(
owner.Position, owner.Position,
PixelSize.FromSize(owner.ClientSize, scaling)); PixelSize.FromSize(ownerSize, scaling));
Position = ownerRect.CenterRect(rect).Position; Position = ownerRect.CenterRect(rect).Position;
} }
} }
@ -991,28 +991,28 @@ namespace Avalonia.Controls
/// <inheritdoc/> /// <inheritdoc/>
protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason) protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason)
{ {
if (ClientSize == clientSize) if (ClientSize != clientSize || double.IsNaN(Width) || double.IsNaN(Height))
return;
var sizeToContent = SizeToContent;
// If auto-sizing is enabled, and the resize came from a user resize (or the reason was
// unspecified) then turn off auto-resizing for any window dimension that is not equal
// to the requested size.
if (sizeToContent != SizeToContent.Manual &&
CanResize &&
reason == PlatformResizeReason.Unspecified ||
reason == PlatformResizeReason.User)
{ {
if (clientSize.Width != ClientSize.Width) var sizeToContent = SizeToContent;
sizeToContent &= ~SizeToContent.Width;
if (clientSize.Height != ClientSize.Height) // If auto-sizing is enabled, and the resize came from a user resize (or the reason was
sizeToContent &= ~SizeToContent.Height; // unspecified) then turn off auto-resizing for any window dimension that is not equal
SizeToContent = sizeToContent; // to the requested size.
} if (sizeToContent != SizeToContent.Manual &&
CanResize &&
reason == PlatformResizeReason.Unspecified ||
reason == PlatformResizeReason.User)
{
if (clientSize.Width != ClientSize.Width)
sizeToContent &= ~SizeToContent.Width;
if (clientSize.Height != ClientSize.Height)
sizeToContent &= ~SizeToContent.Height;
SizeToContent = sizeToContent;
}
Width = clientSize.Width; Width = clientSize.Width;
Height = clientSize.Height; Height = clientSize.Height;
}
base.HandleResized(clientSize, reason); base.HandleResized(clientSize, reason);
} }

10
src/Avalonia.Controls/WindowBase.cs

@ -236,10 +236,14 @@ namespace Avalonia.Controls
/// <param name="reason">The reason for the resize.</param> /// <param name="reason">The reason for the resize.</param>
protected override void HandleResized(Size clientSize, PlatformResizeReason reason) protected override void HandleResized(Size clientSize, PlatformResizeReason reason)
{ {
ClientSize = clientSize;
FrameSize = PlatformImpl?.FrameSize; FrameSize = PlatformImpl?.FrameSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize); if (ClientSize != clientSize)
{
ClientSize = clientSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}
} }
/// <summary> /// <summary>

28
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@ -30,20 +30,28 @@ namespace Avalonia.Diagnostics.ViewModels
if (control is AvaloniaObject ao) if (control is AvaloniaObject ao)
{ {
MarginThickness = ao.GetValue(Layoutable.MarginProperty); try
if (HasPadding)
{ {
PaddingThickness = ao.GetValue(Decorator.PaddingProperty); _updatingFromControl = true;
} MarginThickness = ao.GetValue(Layoutable.MarginProperty);
if (HasPadding)
{
PaddingThickness = ao.GetValue(Decorator.PaddingProperty);
}
if (HasBorder) if (HasBorder)
{
BorderThickness = ao.GetValue(Border.BorderThicknessProperty);
}
HorizontalAlignment = ao.GetValue(Layoutable.HorizontalAlignmentProperty);
VerticalAlignment = ao.GetValue(Layoutable.VerticalAlignmentProperty);
}
finally
{ {
BorderThickness = ao.GetValue(Border.BorderThicknessProperty); _updatingFromControl = false;
} }
HorizontalAlignment = ao.GetValue(Layoutable.HorizontalAlignmentProperty);
VerticalAlignment = ao.GetValue(Layoutable.VerticalAlignmentProperty);
} }
UpdateSize(); UpdateSize();

4
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs

@ -121,8 +121,8 @@ namespace Avalonia.Diagnostics.Views
if (header != null) if (header != null)
{ {
header.PointerEnter += AddAdorner; header.PointerEntered += AddAdorner;
header.PointerLeave += RemoveAdorner; header.PointerExited += RemoveAdorner;
} }
item.TemplateApplied -= TreeViewItemTemplateApplied; item.TemplateApplied -= TreeViewItemTemplateApplied;

12
src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml

@ -19,16 +19,4 @@
</FocusAdornerTemplate> </FocusAdornerTemplate>
</Setter> </Setter>
</Style> </Style>
<!-- DottedLine FocusAdorner -->
<Style Selector="Window.DottedLineFocusAdorner :is(Control)">
<Setter Property="FocusAdorner">
<FocusAdornerTemplate>
<Rectangle Stroke="{StaticResource SystemControlFocusVisualPrimaryBrush}"
StrokeThickness="1"
StrokeDashArray="1,2"
Margin="1" />
</FocusAdornerTemplate>
</Setter>
</Style>
</Styles> </Styles>

12
src/Shared/RawEventGrouping.cs

@ -2,10 +2,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Collections.Pooled; using Avalonia.Collections.Pooled;
using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Threading; using Avalonia.Threading;
using JetBrains.Annotations;
namespace Avalonia; namespace Avalonia;
@ -19,7 +17,7 @@ internal class RawEventGrouper : IDisposable
private readonly Action<RawInputEventArgs> _eventCallback; private readonly Action<RawInputEventArgs> _eventCallback;
private readonly Queue<RawInputEventArgs> _inputQueue = new(); private readonly Queue<RawInputEventArgs> _inputQueue = new();
private readonly Action _dispatchFromQueue; private readonly Action _dispatchFromQueue;
readonly Dictionary<long, RawTouchEventArgs> _lastTouchPoints = new(); readonly Dictionary<long, RawPointerEventArgs> _lastTouchPoints = new();
RawInputEventArgs? _lastEvent; RawInputEventArgs? _lastEvent;
public RawEventGrouper(Action<RawInputEventArgs> eventCallback) public RawEventGrouper(Action<RawInputEventArgs> eventCallback)
@ -49,7 +47,7 @@ internal class RawEventGrouper : IDisposable
_lastEvent = null; _lastEvent = null;
if (ev is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchUpdate) if (ev is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchUpdate)
_lastTouchPoints.Remove(touchUpdate.TouchPointId); _lastTouchPoints.Remove(touchUpdate.RawPointerId);
_eventCallback?.Invoke(ev); _eventCallback?.Invoke(ev);
@ -88,11 +86,11 @@ internal class RawEventGrouper : IDisposable
{ {
if (args is RawTouchEventArgs touchEvent) if (args is RawTouchEventArgs touchEvent)
{ {
if (_lastTouchPoints.TryGetValue(touchEvent.TouchPointId, out var lastTouchEvent)) if (_lastTouchPoints.TryGetValue(touchEvent.RawPointerId, out var lastTouchEvent))
MergeEvents(lastTouchEvent, touchEvent); MergeEvents(lastTouchEvent, touchEvent);
else else
{ {
_lastTouchPoints[touchEvent.TouchPointId] = touchEvent; _lastTouchPoints[touchEvent.RawPointerId] = touchEvent;
AddToQueue(touchEvent); AddToQueue(touchEvent);
} }
} }
@ -105,7 +103,7 @@ internal class RawEventGrouper : IDisposable
{ {
_lastTouchPoints.Clear(); _lastTouchPoints.Clear();
if (args is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchEvent) if (args is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchEvent)
_lastTouchPoints[touchEvent.TouchPointId] = touchEvent; _lastTouchPoints[touchEvent.RawPointerId] = touchEvent;
} }
AddToQueue(args); AddToQueue(args);
} }

2
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -85,7 +85,7 @@ namespace Avalonia.Skia
var second = glyphInfos[length - 1]; var second = glyphInfos[length - 1];
if (!new Codepoint((int)second.Codepoint).IsBreakChar) if (!new Codepoint(second.Codepoint).IsBreakChar)
{ {
return; return;
} }

2
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -85,7 +85,7 @@ namespace Avalonia.Direct2D1.Media
var second = glyphInfos[length - 1]; var second = glyphInfos[length - 1];
if (!new Codepoint((int)second.Codepoint).IsBreakChar) if (!new Codepoint(second.Codepoint).IsBreakChar)
{ {
return; return;
} }

217
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -225,20 +225,17 @@ namespace Avalonia.Win32.Interop
[Flags] [Flags]
public enum ModifierKeys public enum ModifierKeys
{ {
MK_CONTROL = 0x0008, MK_NONE = 0x0000,
MK_LBUTTON = 0x0001, MK_LBUTTON = 0x0001,
MK_MBUTTON = 0x0010,
MK_RBUTTON = 0x0002, MK_RBUTTON = 0x0002,
MK_SHIFT = 0x0004, MK_SHIFT = 0x0004,
MK_CONTROL = 0x0008,
MK_ALT = 0x0020,
MK_MBUTTON = 0x0010,
MK_ALT = 0x0020,
MK_XBUTTON1 = 0x0020, MK_XBUTTON1 = 0x0020,
MK_XBUTTON2 = 0x0040 MK_XBUTTON2 = 0x0040
} }
@ -514,6 +511,33 @@ namespace Avalonia.Win32.Interop
CS_DROPSHADOW = 0x00020000 CS_DROPSHADOW = 0x00020000
} }
[Flags]
public enum PointerDeviceChangeFlags
{
PDC_ARRIVAL = 0x001,
PDC_REMOVAL = 0x002,
PDC_ORIENTATION_0 = 0x004,
PDC_ORIENTATION_90 = 0x008,
PDC_ORIENTATION_180 = 0x010,
PDC_ORIENTATION_270 = 0x020,
PDC_MODE_DEFAULT = 0x040,
PDC_MODE_CENTERED = 0x080,
PDC_MAPPING_CHANGE = 0x100,
PDC_RESOLUTION = 0x200,
PDC_ORIGIN = 0x400,
PDC_MODE_ASPECTRATIOPRESERVED = 0x800
}
public enum PointerInputType
{
PT_NONE = 0x00000000,
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004,
PT_TOUCHPAD = 0x00000005
}
public enum WindowsMessage : uint public enum WindowsMessage : uint
{ {
WM_NULL = 0x0000, WM_NULL = 0x0000,
@ -689,6 +713,25 @@ namespace Avalonia.Win32.Interop
WM_EXITSIZEMOVE = 0x0232, WM_EXITSIZEMOVE = 0x0232,
WM_DROPFILES = 0x0233, WM_DROPFILES = 0x0233,
WM_MDIREFRESHMENU = 0x0234, WM_MDIREFRESHMENU = 0x0234,
WM_POINTERDEVICECHANGE = 0x0238,
WM_POINTERDEVICEINRANGE = 0x239,
WM_POINTERDEVICEOUTOFRANGE = 0x23A,
WM_NCPOINTERUPDATE = 0x0241,
WM_NCPOINTERDOWN = 0x0242,
WM_NCPOINTERUP = 0x0243,
WM_POINTERUPDATE = 0x0245,
WM_POINTERDOWN = 0x0246,
WM_POINTERUP = 0x0247,
WM_POINTERENTER = 0x0249,
WM_POINTERLEAVE = 0x024A,
WM_POINTERACTIVATE = 0x024B,
WM_POINTERCAPTURECHANGED = 0x024C,
WM_TOUCHHITTESTING = 0x024D,
WM_POINTERWHEEL = 0x024E,
WM_POINTERHWHEEL = 0x024F,
DM_POINTERHITTEST = 0x0250,
WM_IME_SETCONTEXT = 0x0281, WM_IME_SETCONTEXT = 0x0281,
WM_IME_NOTIFY = 0x0282, WM_IME_NOTIFY = 0x0282,
WM_IME_CONTROL = 0x0283, WM_IME_CONTROL = 0x0283,
@ -844,6 +887,134 @@ namespace Avalonia.Win32.Interop
SCF_ISSECURE = 0x00000001, SCF_ISSECURE = 0x00000001,
} }
[Flags]
public enum PointerFlags
{
POINTER_FLAG_NONE = 0x00000000,
POINTER_FLAG_NEW = 0x00000001,
POINTER_FLAG_INRANGE = 0x00000002,
POINTER_FLAG_INCONTACT = 0x00000004,
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
POINTER_FLAG_SECONDBUTTON = 0x00000020,
POINTER_FLAG_THIRDBUTTON = 0x00000040,
POINTER_FLAG_FOURTHBUTTON = 0x00000080,
POINTER_FLAG_FIFTHBUTTON = 0x00000100,
POINTER_FLAG_PRIMARY = 0x00002000,
POINTER_FLAG_CONFIDENCE = 0x00000400,
POINTER_FLAG_CANCELED = 0x00000800,
POINTER_FLAG_DOWN = 0x00010000,
POINTER_FLAG_UPDATE = 0x00020000,
POINTER_FLAG_UP = 0x00040000,
POINTER_FLAG_WHEEL = 0x00080000,
POINTER_FLAG_HWHEEL = 0x00100000,
POINTER_FLAG_CAPTURECHANGED = 0x00200000,
POINTER_FLAG_HASTRANSFORM = 0x00400000
}
public enum PointerButtonChangeType : ulong
{
POINTER_CHANGE_NONE,
POINTER_CHANGE_FIRSTBUTTON_DOWN,
POINTER_CHANGE_FIRSTBUTTON_UP,
POINTER_CHANGE_SECONDBUTTON_DOWN,
POINTER_CHANGE_SECONDBUTTON_UP,
POINTER_CHANGE_THIRDBUTTON_DOWN,
POINTER_CHANGE_THIRDBUTTON_UP,
POINTER_CHANGE_FOURTHBUTTON_DOWN,
POINTER_CHANGE_FOURTHBUTTON_UP,
POINTER_CHANGE_FIFTHBUTTON_DOWN,
POINTER_CHANGE_FIFTHBUTTON_UP
}
[Flags]
public enum PenFlags
{
PEN_FLAGS_NONE = 0x00000000,
PEN_FLAGS_BARREL = 0x00000001,
PEN_FLAGS_INVERTED = 0x00000002,
PEN_FLAGS_ERASER = 0x00000004,
}
[Flags]
public enum PenMask
{
PEN_MASK_NONE = 0x00000000,
PEN_MASK_PRESSURE = 0x00000001,
PEN_MASK_ROTATION = 0x00000002,
PEN_MASK_TILT_X = 0x00000004,
PEN_MASK_TILT_Y = 0x00000008
}
[Flags]
public enum TouchFlags
{
TOUCH_FLAG_NONE = 0x00000000
}
[Flags]
public enum TouchMask
{
TOUCH_MASK_NONE = 0x00000000,
TOUCH_MASK_CONTACTAREA = 0x00000001,
TOUCH_MASK_ORIENTATION = 0x00000002,
TOUCH_MASK_PRESSURE = 0x00000004,
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct POINTER_TOUCH_INFO
{
public POINTER_INFO pointerInfo;
public TouchFlags touchFlags;
public TouchMask touchMask;
public int rcContactLeft;
public int rcContactTop;
public int rcContactRight;
public int rcContactBottom;
public int rcContactRawLeft;
public int rcContactRawTop;
public int rcContactRawRight;
public int rcContactRawBottom;
public uint orientation;
public uint pressure;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct POINTER_PEN_INFO
{
public POINTER_INFO pointerInfo;
public PenFlags penFlags;
public PenMask penMask;
public uint pressure;
public uint rotation;
public int tiltX;
public int tiltY;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct POINTER_INFO
{
public PointerInputType pointerType;
public uint pointerId;
public uint frameId;
public PointerFlags pointerFlags;
public IntPtr sourceDevice;
public IntPtr hwndTarget;
public int ptPixelLocationX;
public int ptPixelLocationY;
public int ptHimetricLocationX;
public int ptHimetricLocationY;
public int ptPixelLocationRawX;
public int ptPixelLocationRawY;
public int ptHimetricLocationRawX;
public int ptHimetricLocationRawY;
public uint dwTime;
public uint historyCount;
public int inputData;
public ModifierKeys dwKeyStates;
public ulong PerformanceCount;
public PointerButtonChangeType ButtonChangeType;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD public struct RGBQUAD
{ {
@ -911,6 +1082,36 @@ namespace Avalonia.Win32.Interop
public const int SizeOf_BITMAPINFOHEADER = 40; public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll", SetLastError = true)]
public static extern bool IsMouseInPointerEnabled();
[DllImport("user32.dll", SetLastError = true)]
public static extern int EnableMouseInPointer(bool enable);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerCursorId(uint pointerId, out uint cursorId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerType(uint pointerId, out PointerInputType pointerType);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerInfo(uint pointerId, out POINTER_INFO pointerInfo);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerInfoHistory(uint pointerId, ref int entriesCount, [MarshalAs(UnmanagedType.LPArray), In, Out] POINTER_INFO[] pointerInfos);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerPenInfo(uint pointerId, out POINTER_PEN_INFO penInfo);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerPenInfoHistory(uint pointerId, ref int entriesCount, [MarshalAs(UnmanagedType.LPArray), In, Out] POINTER_PEN_INFO[] penInfos);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerTouchInfo(uint pointerId, out POINTER_TOUCH_INFO touchInfo);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerTouchInfoHistory(uint pointerId, ref int entriesCount, [MarshalAs(UnmanagedType.LPArray), In, Out] POINTER_TOUCH_INFO[] touchInfos);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
MonitorEnumDelegate lpfnEnum, IntPtr dwData); MonitorEnumDelegate lpfnEnum, IntPtr dwData);

1
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -68,6 +68,7 @@ namespace Avalonia
/// <remarks> /// <remarks>
/// Multitouch allows a surface (a touchpad or touchscreen) to recognize the presence of more than one point of contact with the surface at the same time. /// Multitouch allows a surface (a touchpad or touchscreen) to recognize the presence of more than one point of contact with the surface at the same time.
/// </remarks> /// </remarks>
[Obsolete("Multitouch is always enabled on supported Windows versions")]
public bool? EnableMultitouch { get; set; } = true; public bool? EnableMultitouch { get; set; } = true;
/// <summary> /// <summary>

429
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -1,4 +1,6 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -26,8 +28,8 @@ namespace Avalonia.Win32
uint timestamp = unchecked((uint)GetMessageTime()); uint timestamp = unchecked((uint)GetMessageTime());
RawInputEventArgs e = null; RawInputEventArgs e = null;
var shouldTakeFocus = false; var shouldTakeFocus = false;
var message = (WindowsMessage)msg;
switch ((WindowsMessage)msg) switch (message)
{ {
case WindowsMessage.WM_ACTIVATE: case WindowsMessage.WM_ACTIVATE:
{ {
@ -82,7 +84,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_DESTROY: case WindowsMessage.WM_DESTROY:
{ {
UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null); UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null);
// We need to release IMM context and state to avoid leaks. // We need to release IMM context and state to avoid leaks.
if (Imm32InputMethod.Current.HWND == _hwnd) if (Imm32InputMethod.Current.HWND == _hwnd)
{ {
@ -108,9 +110,9 @@ namespace Avalonia.Win32
var newDisplayRect = Marshal.PtrToStructure<RECT>(lParam); var newDisplayRect = Marshal.PtrToStructure<RECT>(lParam);
_scaling = dpi / 96.0; _scaling = dpi / 96.0;
ScalingChanged?.Invoke(_scaling); ScalingChanged?.Invoke(_scaling);
using (SetResizeReason(PlatformResizeReason.DpiChange)) using (SetResizeReason(PlatformResizeReason.DpiChange))
{ {
SetWindowPos(hWnd, SetWindowPos(hWnd,
IntPtr.Zero, IntPtr.Zero,
newDisplayRect.left, newDisplayRect.left,
@ -178,6 +180,10 @@ namespace Avalonia.Win32
case WindowsMessage.WM_MBUTTONDOWN: case WindowsMessage.WM_MBUTTONDOWN:
case WindowsMessage.WM_XBUTTONDOWN: case WindowsMessage.WM_XBUTTONDOWN:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
shouldTakeFocus = ShouldTakeFocusOnClick; shouldTakeFocus = ShouldTakeFocusOnClick;
if (ShouldIgnoreTouchEmulatedMessage()) if (ShouldIgnoreTouchEmulatedMessage())
{ {
@ -188,7 +194,7 @@ namespace Avalonia.Win32
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
(WindowsMessage)msg switch message switch
{ {
WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown, WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown, WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown,
@ -207,6 +213,10 @@ namespace Avalonia.Win32
case WindowsMessage.WM_MBUTTONUP: case WindowsMessage.WM_MBUTTONUP:
case WindowsMessage.WM_XBUTTONUP: case WindowsMessage.WM_XBUTTONUP:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
if (ShouldIgnoreTouchEmulatedMessage()) if (ShouldIgnoreTouchEmulatedMessage())
{ {
break; break;
@ -216,7 +226,7 @@ namespace Avalonia.Win32
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
(WindowsMessage)msg switch message switch
{ {
WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp, WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp,
WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp, WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp,
@ -231,11 +241,19 @@ namespace Avalonia.Win32
} }
// Mouse capture is lost // Mouse capture is lost
case WindowsMessage.WM_CANCELMODE: case WindowsMessage.WM_CANCELMODE:
_mouseDevice.Capture(null); if (!IsMouseInPointerEnabled)
{
_mouseDevice.Capture(null);
}
break; break;
case WindowsMessage.WM_MOUSEMOVE: case WindowsMessage.WM_MOUSEMOVE:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
if (ShouldIgnoreTouchEmulatedMessage()) if (ShouldIgnoreTouchEmulatedMessage())
{ {
break; break;
@ -259,42 +277,58 @@ namespace Avalonia.Win32
timestamp, timestamp,
_owner, _owner,
RawPointerEventType.Move, RawPointerEventType.Move,
DipFromLParam(lParam), GetMouseModifiers(wParam)); DipFromLParam(lParam),
GetMouseModifiers(wParam));
break; break;
} }
case WindowsMessage.WM_MOUSEWHEEL: case WindowsMessage.WM_MOUSEWHEEL:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
e = new RawMouseWheelEventArgs( e = new RawMouseWheelEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
PointToClient(PointFromLParam(lParam)), PointToClient(PointFromLParam(lParam)),
new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta), GetMouseModifiers(wParam)); new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta),
GetMouseModifiers(wParam));
break; break;
} }
case WindowsMessage.WM_MOUSEHWHEEL: case WindowsMessage.WM_MOUSEHWHEEL:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
e = new RawMouseWheelEventArgs( e = new RawMouseWheelEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
PointToClient(PointFromLParam(lParam)), PointToClient(PointFromLParam(lParam)),
new Vector(-(ToInt32(wParam) >> 16) / wheelDelta, 0), GetMouseModifiers(wParam)); new Vector(-(ToInt32(wParam) >> 16) / wheelDelta, 0),
GetMouseModifiers(wParam));
break; break;
} }
case WindowsMessage.WM_MOUSELEAVE: case WindowsMessage.WM_MOUSELEAVE:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
_trackingMouse = false; _trackingMouse = false;
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
RawPointerEventType.LeaveWindow, RawPointerEventType.LeaveWindow,
new Point(-1, -1), WindowsKeyboardDevice.Instance.Modifiers); new Point(-1, -1),
WindowsKeyboardDevice.Instance.Modifiers);
break; break;
} }
@ -303,11 +337,15 @@ namespace Avalonia.Win32
case WindowsMessage.WM_NCMBUTTONDOWN: case WindowsMessage.WM_NCMBUTTONDOWN:
case WindowsMessage.WM_NCXBUTTONDOWN: case WindowsMessage.WM_NCXBUTTONDOWN:
{ {
if (IsMouseInPointerEnabled)
{
break;
}
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
(WindowsMessage)msg switch message switch
{ {
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType
.NonClientLeftButtonDown, .NonClientLeftButtonDown,
@ -323,6 +361,10 @@ namespace Avalonia.Win32
} }
case WindowsMessage.WM_TOUCH: case WindowsMessage.WM_TOUCH:
{ {
if (_wmPointerEnabled)
{
break;
}
var touchInputCount = wParam.ToInt32(); var touchInputCount = wParam.ToInt32();
var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount]; var pTouchInputs = stackalloc TOUCHINPUT[touchInputCount];
@ -348,6 +390,120 @@ namespace Avalonia.Win32
return IntPtr.Zero; return IntPtr.Zero;
} }
break;
}
case WindowsMessage.WM_NCPOINTERDOWN:
case WindowsMessage.WM_NCPOINTERUP:
case WindowsMessage.WM_POINTERDOWN:
case WindowsMessage.WM_POINTERUP:
case WindowsMessage.WM_POINTERUPDATE:
{
if (!_wmPointerEnabled)
{
break;
}
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
var eventType = GetEventType(message, info);
var args = CreatePointerArgs(device, timestamp, eventType, point, modifiers, info.pointerId);
args.IntermediatePoints = CreateLazyIntermediatePoints(info);
e = args;
break;
}
case WindowsMessage.WM_POINTERDEVICEOUTOFRANGE:
case WindowsMessage.WM_POINTERLEAVE:
case WindowsMessage.WM_POINTERCAPTURECHANGED:
{
if (!_wmPointerEnabled)
{
break;
}
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
var eventType = device is TouchDevice ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow;
e = CreatePointerArgs(device, timestamp, eventType, point, modifiers, info.pointerId);
break;
}
case WindowsMessage.WM_POINTERWHEEL:
case WindowsMessage.WM_POINTERHWHEEL:
{
if (!_wmPointerEnabled)
{
break;
}
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
var val = (ToInt32(wParam) >> 16) / wheelDelta;
var delta = message == WindowsMessage.WM_POINTERWHEEL ? new Vector(0, val) : new Vector(val, 0);
e = new RawMouseWheelEventArgs(device, timestamp, _owner, point.Position, delta, modifiers)
{
RawPointerId = info.pointerId
};
break;
}
case WindowsMessage.WM_POINTERDEVICEINRANGE:
{
if (!_wmPointerEnabled)
{
break;
}
// Do not generate events, but release mouse capture on any other device input.
GetDevicePointerInfo(wParam, out var device, out var info, out var point, out var modifiers, ref timestamp);
if (device != _mouseDevice)
{
_mouseDevice.Capture(null);
return IntPtr.Zero;
}
break;
}
case WindowsMessage.WM_POINTERACTIVATE:
{
//occurs when a pointer activates an inactive window.
//we should handle this and return PA_ACTIVATE or PA_NOACTIVATE
//https://docs.microsoft.com/en-us/previous-versions/windows/desktop/inputmsg/wm-pointeractivate
break;
}
case WindowsMessage.WM_POINTERDEVICECHANGE:
{
//notifies about changes in the settings of a monitor that has a digitizer attached to it.
//https://docs.microsoft.com/en-us/previous-versions/windows/desktop/inputmsg/wm-pointerdevicechange
break;
}
case WindowsMessage.WM_NCPOINTERUPDATE:
{
//NC stands for non-client area - window header and window border
//As I found above in an old message handling - we dont need to handle NC pointer move/updates.
//All we need is pointer down and up. So this is skipped for now.
break;
}
case WindowsMessage.WM_POINTERENTER:
{
//this is not handled by WM_MOUSEENTER so I think there is no need to handle this too.
//but we can detect a new pointer by this message and calling IS_POINTER_NEW_WPARAM
//note: by using a pen there can be a pointer leave or enter inside a window coords
//when you are just lift up the pen above the display
break;
}
case WindowsMessage.DM_POINTERHITTEST:
{
//DM stands for direct manipulation.
//https://docs.microsoft.com/en-us/previous-versions/windows/desktop/directmanipulation/direct-manipulation-portal
break;
}
case WindowsMessage.WM_TOUCHHITTESTING:
{
//This is to determine the most probable touch target.
//provides an input bounding box and receives hit proximity
//https://docs.microsoft.com/en-us/previous-versions/windows/desktop/inputmsg/wm-touchhittesting
//https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-touch_hit_testing_input
break;
}
case WindowsMessage.WM_PARENTNOTIFY:
{
//This message is sent in a dialog scenarios. Contains mouse position.
//Old message, but listed in the wm_pointer reference
//https://docs.microsoft.com/en-us/previous-versions/windows/desktop/inputmsg/wm-parentnotify
break; break;
} }
case WindowsMessage.WM_NCPAINT: case WindowsMessage.WM_NCPAINT:
@ -446,7 +602,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_GETMINMAXINFO: case WindowsMessage.WM_GETMINMAXINFO:
{ {
MINMAXINFO mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam); MINMAXINFO mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam);
_maxTrackSize = mmi.ptMaxTrackSize; _maxTrackSize = mmi.ptMaxTrackSize;
if (_minSize.Width > 0) if (_minSize.Width > 0)
@ -530,7 +686,7 @@ namespace Avalonia.Win32
if (_managedDrag.PreprocessInputEvent(ref e)) if (_managedDrag.PreprocessInputEvent(ref e))
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
#endif #endif
if(shouldTakeFocus) if(shouldTakeFocus)
{ {
SetFocus(_hwnd); SetFocus(_hwnd);
@ -540,7 +696,7 @@ namespace Avalonia.Win32
{ {
Input(e); Input(e);
if ((WindowsMessage)msg == WindowsMessage.WM_KEYDOWN) if (message == WindowsMessage.WM_KEYDOWN)
{ {
// Handling a WM_KEYDOWN message should cause the subsequent WM_CHAR message to // Handling a WM_KEYDOWN message should cause the subsequent WM_CHAR message to
// be ignored. This should be safe to do as WM_CHAR should only be produced in // be ignored. This should be safe to do as WM_CHAR should only be produced in
@ -549,6 +705,11 @@ namespace Avalonia.Win32
_ignoreWmChar = e.Handled; _ignoreWmChar = e.Handled;
} }
if (s_intermediatePointsPooledList.Count > 0)
{
s_intermediatePointsPooledList.Dispose();
}
if (e.Handled) if (e.Handled)
{ {
return IntPtr.Zero; return IntPtr.Zero;
@ -561,6 +722,196 @@ namespace Avalonia.Win32
} }
} }
private unsafe Lazy<IReadOnlyList<RawPointerPoint>> CreateLazyIntermediatePoints(POINTER_INFO info)
{
var historyCount = Math.Min((int)info.historyCount, MaxPointerHistorySize);
if (historyCount > 1)
{
return new Lazy<IReadOnlyList<RawPointerPoint>>(() =>
{
s_intermediatePointsPooledList.Clear();
s_intermediatePointsPooledList.Capacity = historyCount;
// Pointers in history are ordered from newest to oldest, so we need to reverse iteration.
// Also we skip the newest pointer, because original event arguments already contains it.
if (info.pointerType == PointerInputType.PT_TOUCH)
{
if (GetPointerTouchInfoHistory(info.pointerId, ref historyCount, s_historyTouchInfos))
{
for (int i = historyCount - 1; i >= 1; i--)
{
var historyTouchInfo = s_historyTouchInfos[i];
s_intermediatePointsPooledList.Add(CreateRawPointerPoint(historyTouchInfo));
}
}
}
else if (info.pointerType == PointerInputType.PT_PEN)
{
if (GetPointerPenInfoHistory(info.pointerId, ref historyCount, s_historyPenInfos))
{
for (int i = historyCount - 1; i >= 1; i--)
{
var historyPenInfo = s_historyPenInfos[i];
s_intermediatePointsPooledList.Add(CreateRawPointerPoint(historyPenInfo));
}
}
}
else
{
// Currently Windows does not return history info for mouse input, but we handle it just for case.
if (GetPointerInfoHistory(info.pointerId, ref historyCount, s_historyInfos))
{
for (int i = historyCount - 1; i >= 1; i--)
{
var historyInfo = s_historyInfos[i];
s_intermediatePointsPooledList.Add(CreateRawPointerPoint(historyInfo));
}
}
}
return s_intermediatePointsPooledList;
});
}
return null;
}
private RawPointerEventArgs CreatePointerArgs(IInputDevice device, ulong timestamp, RawPointerEventType eventType, RawPointerPoint point, RawInputModifiers modifiers, uint rawPointerId)
{
return device is TouchDevice
? new RawTouchEventArgs(device, timestamp, _owner, eventType, point, modifiers, rawPointerId)
: new RawPointerEventArgs(device, timestamp, _owner, eventType, point, modifiers)
{
RawPointerId = rawPointerId
};
}
private void GetDevicePointerInfo(IntPtr wParam,
out IPointerDevice device, out POINTER_INFO info, out RawPointerPoint point,
out RawInputModifiers modifiers, ref uint timestamp)
{
var pointerId = (uint)(ToInt32(wParam) & 0xFFFF);
GetPointerType(pointerId, out var type);
modifiers = default;
switch (type)
{
case PointerInputType.PT_PEN:
device = _penDevice;
GetPointerPenInfo(pointerId, out var penInfo);
info = penInfo.pointerInfo;
point = CreateRawPointerPoint(penInfo);
if (penInfo.penFlags.HasFlag(PenFlags.PEN_FLAGS_BARREL))
{
modifiers |= RawInputModifiers.PenBarrelButton;
}
if (penInfo.penFlags.HasFlag(PenFlags.PEN_FLAGS_ERASER))
{
modifiers |= RawInputModifiers.PenEraser;
}
if (penInfo.penFlags.HasFlag(PenFlags.PEN_FLAGS_INVERTED))
{
modifiers |= RawInputModifiers.PenInverted;
}
break;
case PointerInputType.PT_TOUCH:
device = _touchDevice;
GetPointerTouchInfo(pointerId, out var touchInfo);
info = touchInfo.pointerInfo;
point = CreateRawPointerPoint(touchInfo);
break;
default:
device = _mouseDevice;
GetPointerInfo(pointerId, out info);
point = CreateRawPointerPoint(info);
break;
}
if (info.dwTime != 0)
{
timestamp = info.dwTime;
}
modifiers |= GetInputModifiers(info.pointerFlags);
}
private RawPointerPoint CreateRawPointerPoint(POINTER_INFO pointerInfo)
{
var point = PointToClient(new PixelPoint(pointerInfo.ptPixelLocationX, pointerInfo.ptPixelLocationY));
return new RawPointerPoint
{
Position = point
};
}
private RawPointerPoint CreateRawPointerPoint(POINTER_TOUCH_INFO info)
{
var pointerInfo = info.pointerInfo;
var point = PointToClient(new PixelPoint(pointerInfo.ptPixelLocationX, pointerInfo.ptPixelLocationY));
return new RawPointerPoint
{
Position = point,
// POINTER_PEN_INFO.pressure is normalized to a range between 0 and 1024, with 512 as a default.
// But in our API we use range from 0.0 to 1.0.
Pressure = info.pressure / 1024f
};
}
private RawPointerPoint CreateRawPointerPoint(POINTER_PEN_INFO info)
{
var pointerInfo = info.pointerInfo;
var point = PointToClient(new PixelPoint(pointerInfo.ptPixelLocationX, pointerInfo.ptPixelLocationY));
return new RawPointerPoint
{
Position = point,
// POINTER_PEN_INFO.pressure is normalized to a range between 0 and 1024, with 512 as a default.
// But in our API we use range from 0.0 to 1.0.
Pressure = info.pressure / 1024f,
Twist = info.rotation,
XTilt = info.tiltX,
YTilt = info.tiltY
};
}
private static RawPointerEventType GetEventType(WindowsMessage message, POINTER_INFO info)
{
var isTouch = info.pointerType == PointerInputType.PT_TOUCH;
if (info.pointerFlags.HasFlag(PointerFlags.POINTER_FLAG_CANCELED))
{
return isTouch ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow;
}
var eventType = ToEventType(info.ButtonChangeType, isTouch);
if (eventType == RawPointerEventType.LeftButtonDown &&
message == WindowsMessage.WM_NCPOINTERDOWN)
{
eventType = RawPointerEventType.NonClientLeftButtonDown;
}
return eventType;
}
private static RawPointerEventType ToEventType(PointerButtonChangeType type, bool isTouch)
{
return type switch
{
PointerButtonChangeType.POINTER_CHANGE_FIRSTBUTTON_DOWN when isTouch => RawPointerEventType.TouchBegin,
PointerButtonChangeType.POINTER_CHANGE_FIRSTBUTTON_DOWN when !isTouch => RawPointerEventType.LeftButtonDown,
PointerButtonChangeType.POINTER_CHANGE_SECONDBUTTON_DOWN => RawPointerEventType.RightButtonDown,
PointerButtonChangeType.POINTER_CHANGE_THIRDBUTTON_DOWN => RawPointerEventType.MiddleButtonDown,
PointerButtonChangeType.POINTER_CHANGE_FOURTHBUTTON_DOWN => RawPointerEventType.XButton1Down,
PointerButtonChangeType.POINTER_CHANGE_FIFTHBUTTON_DOWN => RawPointerEventType.XButton2Down,
PointerButtonChangeType.POINTER_CHANGE_FIRSTBUTTON_UP when isTouch => RawPointerEventType.TouchEnd,
PointerButtonChangeType.POINTER_CHANGE_FIRSTBUTTON_UP when !isTouch => RawPointerEventType.LeftButtonUp,
PointerButtonChangeType.POINTER_CHANGE_SECONDBUTTON_UP => RawPointerEventType.RightButtonUp,
PointerButtonChangeType.POINTER_CHANGE_THIRDBUTTON_UP => RawPointerEventType.MiddleButtonUp,
PointerButtonChangeType.POINTER_CHANGE_FOURTHBUTTON_UP => RawPointerEventType.XButton1Up,
PointerButtonChangeType.POINTER_CHANGE_FIFTHBUTTON_UP => RawPointerEventType.XButton2Up,
_ when isTouch => RawPointerEventType.TouchUpdate,
_ => RawPointerEventType.Move
};
}
private void UpdateInputMethod(IntPtr hkl) private void UpdateInputMethod(IntPtr hkl)
{ {
// note: for non-ime language, also create it so that emoji panel tracks cursor // note: for non-ime language, also create it so that emoji panel tracks cursor
@ -568,11 +919,11 @@ namespace Avalonia.Win32
if (langid == _langid && Imm32InputMethod.Current.HWND == Hwnd) if (langid == _langid && Imm32InputMethod.Current.HWND == Hwnd)
{ {
return; return;
} }
_langid = langid; _langid = langid;
Imm32InputMethod.Current.SetLanguageAndWindow(this, Hwnd, hkl); Imm32InputMethod.Current.SetLanguageAndWindow(this, Hwnd, hkl);
} }
private static int ToInt32(IntPtr ptr) private static int ToInt32(IntPtr ptr)
@ -597,10 +948,7 @@ namespace Avalonia.Win32
private bool ShouldIgnoreTouchEmulatedMessage() private bool ShouldIgnoreTouchEmulatedMessage()
{ {
if (!_multitouch) // Note: GetMessageExtraInfo doesn't work with WM_POINTER events.
{
return false;
}
// MI_WP_SIGNATURE // MI_WP_SIGNATURE
// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages // https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
@ -613,6 +961,11 @@ namespace Avalonia.Win32
private static RawInputModifiers GetMouseModifiers(IntPtr wParam) private static RawInputModifiers GetMouseModifiers(IntPtr wParam)
{ {
var keys = (ModifierKeys)ToInt32(wParam); var keys = (ModifierKeys)ToInt32(wParam);
return GetInputModifiers(keys);
}
private static RawInputModifiers GetInputModifiers(ModifierKeys keys)
{
var modifiers = WindowsKeyboardDevice.Instance.Modifiers; var modifiers = WindowsKeyboardDevice.Instance.Modifiers;
if (keys.HasAllFlags(ModifierKeys.MK_LBUTTON)) if (keys.HasAllFlags(ModifierKeys.MK_LBUTTON))
@ -642,5 +995,37 @@ namespace Avalonia.Win32
return modifiers; return modifiers;
} }
private static RawInputModifiers GetInputModifiers(PointerFlags flags)
{
var modifiers = WindowsKeyboardDevice.Instance.Modifiers;
if (flags.HasAllFlags(PointerFlags.POINTER_FLAG_FIRSTBUTTON))
{
modifiers |= RawInputModifiers.LeftMouseButton;
}
if (flags.HasAllFlags(PointerFlags.POINTER_FLAG_SECONDBUTTON))
{
modifiers |= RawInputModifiers.RightMouseButton;
}
if (flags.HasAllFlags(PointerFlags.POINTER_FLAG_THIRDBUTTON))
{
modifiers |= RawInputModifiers.MiddleMouseButton;
}
if (flags.HasAllFlags(PointerFlags.POINTER_FLAG_FOURTHBUTTON))
{
modifiers |= RawInputModifiers.XButton1MouseButton;
}
if (flags.HasAllFlags(PointerFlags.POINTER_FLAG_FIFTHBUTTON))
{
modifiers |= RawInputModifiers.XButton2MouseButton;
}
return modifiers;
}
} }
} }

53
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
@ -21,6 +22,7 @@ using Avalonia.Win32.OpenGl;
using Avalonia.Win32.WinRT; using Avalonia.Win32.WinRT;
using Avalonia.Win32.WinRT.Composition; using Avalonia.Win32.WinRT.Composition;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
using Avalonia.Collections.Pooled;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Win32 namespace Avalonia.Win32
@ -68,18 +70,19 @@ namespace Avalonia.Win32
private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE);
private readonly TouchDevice _touchDevice; private readonly TouchDevice _touchDevice;
private readonly MouseDevice _mouseDevice; private readonly MouseDevice _mouseDevice;
private readonly PenDevice _penDevice;
private readonly ManagedDeferredRendererLock _rendererLock; private readonly ManagedDeferredRendererLock _rendererLock;
private readonly FramebufferManager _framebuffer; private readonly FramebufferManager _framebuffer;
private readonly IGlPlatformSurface _gl; private readonly IGlPlatformSurface _gl;
private readonly bool _wmPointerEnabled;
private Win32NativeControlHost _nativeControlHost; private Win32NativeControlHost _nativeControlHost;
private WndProc _wndProcDelegate; private WndProc _wndProcDelegate;
private string _className; private string _className;
private IntPtr _hwnd; private IntPtr _hwnd;
private bool _multitouch;
private IInputRoot _owner; private IInputRoot _owner;
private WindowProperties _windowProperties; private WindowProperties _windowProperties;
private bool _trackingMouse; private bool _trackingMouse;//ToDo - there is something missed. Needs investigation @Steven Kirk
private bool _topmost; private bool _topmost;
private double _scaling = 1; private double _scaling = 1;
private WindowState _showWindowState; private WindowState _showWindowState;
@ -96,10 +99,17 @@ namespace Avalonia.Win32
private uint _langid; private uint _langid;
private bool _ignoreWmChar; private bool _ignoreWmChar;
private const int MaxPointerHistorySize = 512;
private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new();
private static readonly POINTER_TOUCH_INFO[] s_historyTouchInfos = new POINTER_TOUCH_INFO[MaxPointerHistorySize];
private static readonly POINTER_PEN_INFO[] s_historyPenInfos = new POINTER_PEN_INFO[MaxPointerHistorySize];
private static readonly POINTER_INFO[] s_historyInfos = new POINTER_INFO[MaxPointerHistorySize];
public WindowImpl() public WindowImpl()
{ {
_touchDevice = new TouchDevice(); _touchDevice = new TouchDevice();
_mouseDevice = new WindowsMouseDevice(); _mouseDevice = new WindowsMouseDevice();
_penDevice = new PenDevice();
#if USE_MANAGED_DRAG #if USE_MANAGED_DRAG
_managedDrag = new ManagedWindowResizeDragHelper(this, capture => _managedDrag = new ManagedWindowResizeDragHelper(this, capture =>
@ -128,6 +138,8 @@ namespace Avalonia.Win32
egl.Display is AngleWin32EglDisplay angleDisplay && egl.Display is AngleWin32EglDisplay angleDisplay &&
angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
_wmPointerEnabled = Win32Platform.WindowsVersion >= PlatformConstants.Windows8;
CreateWindow(); CreateWindow();
_framebuffer = new FramebufferManager(_hwnd); _framebuffer = new FramebufferManager(_hwnd);
UpdateInputMethod(GetKeyboardLayout(0)); UpdateInputMethod(GetKeyboardLayout(0));
@ -208,6 +220,8 @@ namespace Avalonia.Win32
} }
} }
private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.Primary)?.PixelDensity ?? 1;
public double RenderScaling => _scaling; public double RenderScaling => _scaling;
public double DesktopScaling => RenderScaling; public double DesktopScaling => RenderScaling;
@ -280,6 +294,8 @@ namespace Avalonia.Win32
protected IntPtr Hwnd => _hwnd; protected IntPtr Hwnd => _hwnd;
private bool IsMouseInPointerEnabled => _wmPointerEnabled && IsMouseInPointerEnabled();
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{ {
TransparencyLevel = EnableBlur(transparencyLevel); TransparencyLevel = EnableBlur(transparencyLevel);
@ -469,10 +485,14 @@ namespace Avalonia.Win32
{ {
GetWindowRect(_hwnd, out var rc); GetWindowRect(_hwnd, out var rc);
return new PixelPoint(rc.left, rc.top); var border = HiddenBorderSize;
return new PixelPoint(rc.left + border.Width, rc.top + border.Height);
} }
set set
{ {
var border = HiddenBorderSize;
value = new PixelPoint(value.X - border.Width, value.Y - border.Height);
SetWindowPos( SetWindowPos(
Handle.Handle, Handle.Handle,
IntPtr.Zero, IntPtr.Zero,
@ -486,6 +506,24 @@ namespace Avalonia.Win32
private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full; private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full;
private PixelSize HiddenBorderSize
{
get
{
// Windows 10 and 11 add a 7 pixel invisible border on the left/right/bottom of windows for resizing
if (Win32Platform.WindowsVersion.Major < 10 || !HasFullDecorations)
{
return PixelSize.Empty;
}
DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var clientRect, Marshal.SizeOf(typeof(RECT)));
GetWindowRect(_hwnd, out var frameRect);
var borderWidth = GetSystemMetrics(SystemMetric.SM_CXBORDER);
return new PixelSize(clientRect.left - frameRect.left - borderWidth, 0);
}
}
public void Move(PixelPoint point) => Position = point; public void Move(PixelPoint point) => Position = point;
public void SetMinMaxSize(Size minSize, Size maxSize) public void SetMinMaxSize(Size minSize, Size maxSize)
@ -790,12 +828,7 @@ namespace Avalonia.Win32
Handle = new WindowImplPlatformHandle(this); Handle = new WindowImplPlatformHandle(this);
_multitouch = Win32Platform.Options.EnableMultitouch ?? true; RegisterTouchWindow(_hwnd, 0);
if (_multitouch)
{
RegisterTouchWindow(_hwnd, 0);
}
if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8) if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{ {
@ -922,7 +955,7 @@ namespace Avalonia.Win32
if (WindowState == WindowState.Maximized) if (WindowState == WindowState.Maximized)
{ {
_extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0); _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0);
_offScreenMargin = new Thickness(borderThickness.left / RenderScaling, borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, borderThickness.bottom / RenderScaling); _offScreenMargin = new Thickness(borderThickness.left / PrimaryScreenRenderScaling, borderThickness.top / PrimaryScreenRenderScaling, borderThickness.right / PrimaryScreenRenderScaling, borderThickness.bottom / PrimaryScreenRenderScaling);
} }
else else
{ {

52
tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

@ -189,7 +189,7 @@ namespace Avalonia.Base.UnitTests.Input
// Ensure that e.Handled is reset between controls. // Ensure that e.Handled is reset between controls.
root.PointerMoved += (s, e) => e.Handled = true; root.PointerMoved += (s, e) => e.Handled = true;
decorator.PointerEnter += (s, e) => e.Handled = true; decorator.PointerEntered += (s, e) => e.Handled = true;
SetHit(renderer, decorator); SetHit(renderer, decorator);
impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); impl.Object.Input!(CreateRawPointerMovedArgs(device, root));
@ -231,7 +231,7 @@ namespace Avalonia.Base.UnitTests.Input
} }
}); });
AddEnterLeaveHandlers(HandleEvent, canvas, decorator); AddEnteredExitedHandlers(HandleEvent, canvas, decorator);
// Enter decorator // Enter decorator
SetHit(renderer, decorator); SetHit(renderer, decorator);
@ -246,17 +246,17 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal( Assert.Equal(
new[] new[]
{ {
((object?)decorator, "PointerEnter"), ((object?)decorator, nameof(InputElement.PointerEntered)),
(decorator, "PointerMoved"), (decorator, nameof(InputElement.PointerMoved)),
(decorator, "PointerLeave"), (decorator, nameof(InputElement.PointerExited)),
(canvas, "PointerEnter"), (canvas, nameof(InputElement.PointerEntered)),
(canvas, "PointerMoved") (canvas, nameof(InputElement.PointerMoved))
}, },
result); result);
} }
[Fact] [Fact]
public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order() public void PointerEntered_Exited_Should_Be_Raised_In_Correct_Order()
{ {
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
@ -289,7 +289,7 @@ namespace Avalonia.Base.UnitTests.Input
SetHit(renderer, canvas); SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator); AddEnteredExitedHandlers(HandleEvent, root, canvas, border, decorator);
SetHit(renderer, decorator); SetHit(renderer, decorator);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
@ -297,16 +297,16 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal( Assert.Equal(
new[] new[]
{ {
((object?)canvas, "PointerLeave"), ((object?)canvas, nameof(InputElement.PointerExited)),
(decorator, "PointerEnter"), (decorator, nameof(InputElement.PointerEntered)),
(border, "PointerEnter"), (border, nameof(InputElement.PointerEntered)),
}, },
result); result);
} }
// https://github.com/AvaloniaUI/Avalonia/issues/7896 // https://github.com/AvaloniaUI/Avalonia/issues/7896
[Fact] [Fact]
public void PointerEnter_Leave_Should_Set_Correct_Position() public void PointerEntered_Exited_Should_Set_Correct_Position()
{ {
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
@ -331,7 +331,7 @@ namespace Avalonia.Base.UnitTests.Input
} }
}); });
AddEnterLeaveHandlers(HandleEvent, root, canvas); AddEnteredExitedHandlers(HandleEvent, root, canvas);
SetHit(renderer, canvas); SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition)); impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition));
@ -342,10 +342,10 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal( Assert.Equal(
new[] new[]
{ {
((object?)canvas, "PointerEnter", expectedPosition), ((object?)canvas, nameof(InputElement.PointerEntered), expectedPosition),
(root, "PointerEnter", expectedPosition), (root, nameof(InputElement.PointerEntered), expectedPosition),
(canvas, "PointerLeave", expectedPosition), (canvas, nameof(InputElement.PointerExited), expectedPosition),
(root, "PointerLeave", expectedPosition) (root, nameof(InputElement.PointerExited), expectedPosition)
}, },
result); result);
} }
@ -415,7 +415,7 @@ namespace Avalonia.Base.UnitTests.Input
} }
}); });
AddEnterLeaveHandlers(HandleEvent, root, canvas); AddEnteredExitedHandlers(HandleEvent, root, canvas);
// Init pointer over. // Init pointer over.
SetHit(renderer, canvas); SetHit(renderer, canvas);
@ -429,22 +429,22 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal( Assert.Equal(
new[] new[]
{ {
((object?)canvas, "PointerEnter", lastClientPosition), ((object?)canvas, nameof(InputElement.PointerEntered), lastClientPosition),
(root, "PointerEnter", lastClientPosition), (root, nameof(InputElement.PointerEntered), lastClientPosition),
(canvas, "PointerLeave", lastClientPosition), (canvas, nameof(InputElement.PointerExited), lastClientPosition),
(root, "PointerLeave", lastClientPosition), (root, nameof(InputElement.PointerExited), lastClientPosition),
}, },
result); result);
} }
private static void AddEnterLeaveHandlers( private static void AddEnteredExitedHandlers(
EventHandler<PointerEventArgs> handler, EventHandler<PointerEventArgs> handler,
params IInputElement[] controls) params IInputElement[] controls)
{ {
foreach (var c in controls) foreach (var c in controls)
{ {
c.PointerEnter += handler; c.PointerEntered += handler;
c.PointerLeave += handler; c.PointerExited += handler;
c.PointerMoved += handler; c.PointerMoved += handler;
} }
} }

24
tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs

@ -219,30 +219,36 @@ namespace Avalonia.Input.UnitTests
{ {
for (int i = 0; i < touchPointIds.Length; i++) for (int i = 0; i < touchPointIds.Length; i++)
{ {
inputManager.ProcessInput(new RawTouchEventArgs(device, 0, inputManager.ProcessInput(new RawPointerEventArgs(device, 0,
root, root,
type, type,
new Point(0, 0), new Point(0, 0),
RawInputModifiers.None, RawInputModifiers.None)
touchPointIds[i])); {
RawPointerId = touchPointIds[i]
});
} }
} }
private static void TapOnce(IInputManager inputManager, TouchDevice device, IInputRoot root, ulong timestamp = 0, long touchPointId = 0) private static void TapOnce(IInputManager inputManager, TouchDevice device, IInputRoot root, ulong timestamp = 0, long touchPointId = 0)
{ {
inputManager.ProcessInput(new RawTouchEventArgs(device, timestamp, inputManager.ProcessInput(new RawPointerEventArgs(device, timestamp,
root, root,
RawPointerEventType.TouchBegin, RawPointerEventType.TouchBegin,
new Point(0, 0), new Point(0, 0),
RawInputModifiers.None, RawInputModifiers.None)
touchPointId)); {
inputManager.ProcessInput(new RawTouchEventArgs(device, timestamp, RawPointerId = touchPointId
});
inputManager.ProcessInput(new RawPointerEventArgs(device, timestamp,
root, root,
RawPointerEventType.TouchEnd, RawPointerEventType.TouchEnd,
new Point(0, 0), new Point(0, 0),
RawInputModifiers.None, RawInputModifiers.None)
touchPointId)); {
RawPointerId = touchPointId
});
} }
} }
} }

31
tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs

@ -173,37 +173,6 @@ namespace Avalonia.Base.UnitTests.Layout
target.Verify(x => x.InvalidateMeasure(root), Times.Once()); target.Verify(x => x.InvalidateMeasure(root), Times.Once());
} }
[Theory]
[InlineData(16, 6, 5.333333333333333)]
[InlineData(18, 10, 4)]
public void UseLayoutRounding_Arranges_Center_Alignment_Correctly_With_Fractional_Scaling(
double containerWidth,
double childWidth,
double expectedX)
{
Border target;
var root = new TestRoot
{
LayoutScaling = 1.5,
UseLayoutRounding = true,
Child = new Decorator
{
Width = containerWidth,
Height = 100,
Child = target = new Border
{
Width = childWidth,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
};
root.Measure(new Size(100, 100));
root.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds);
}
[Fact] [Fact]
public void LayoutUpdated_Is_Called_At_End_Of_Layout_Pass() public void LayoutUpdated_Is_Called_At_End_Of_Layout_Pass()
{ {

140
tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_LayoutRounding.cs

@ -0,0 +1,140 @@
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.UnitTests;
using Xunit;
using Xunit.Sdk;
namespace Avalonia.Base.UnitTests.Layout
{
public class LayoutableTests_LayoutRounding
{
[Theory]
[InlineData(100, 100)]
[InlineData(101, 101.33333333333333)]
[InlineData(103, 103.33333333333333)]
public void Measure_Adjusts_DesiredSize_Upwards_When_Constraint_Allows(double desiredSize, double expectedSize)
{
var target = new TestLayoutable(new Size(desiredSize, desiredSize));
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
Assert.Equal(new Size(expectedSize, expectedSize), target.DesiredSize);
}
[Fact]
public void Measure_Constrains_Adjusted_DesiredSize_To_Constraint()
{
var target = new TestLayoutable(new Size(101, 101));
var root = CreateRoot(1.5, target, constraint: new Size(101, 101));
root.LayoutManager.ExecuteInitialLayoutPass();
// Desired width/height with layout rounding is 101.3333 but constraint is 101,101 so
// layout rounding should be ignored.
Assert.Equal(new Size(101, 101), target.DesiredSize);
}
[Fact]
public void Measure_Adjusts_DesiredSize_Upwards_When_Margin_Present()
{
var target = new TestLayoutable(new Size(101, 101), margin: 1);
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel margin is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Final size = 101.3333 + 2.6666 = 104
AssertEqual(new Size(104, 104), target.DesiredSize);
}
[Fact]
public void Arrange_Adjusts_Bounds_Upwards_With_Margin()
{
var target = new TestLayoutable(new Size(101, 101), margin: 1);
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel margin is rounded up to 1.3333
// - Size of 101 gets rounded up to 101.3333
AssertEqual(new Point(1.3333333333333333, 1.3333333333333333), target.Bounds.Position);
AssertEqual(new Size(101.33333333333333, 101.33333333333333), target.Bounds.Size);
}
[Theory]
[InlineData(16, 6, 5.333333333333333)]
[InlineData(18, 10, 4)]
public void Arranges_Center_Alignment_Correctly_With_Fractional_Scaling(
double containerWidth,
double childWidth,
double expectedX)
{
Border target;
var root = new TestRoot
{
LayoutScaling = 1.5,
UseLayoutRounding = true,
Child = new Decorator
{
Width = containerWidth,
Height = 100,
Child = target = new Border
{
Width = childWidth,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
};
root.Measure(new Size(100, 100));
root.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds);
}
private static TestRoot CreateRoot(
double scaling,
Control child,
Size? constraint = null)
{
return new TestRoot
{
LayoutScaling = scaling,
UseLayoutRounding = true,
Child = child,
ClientSize = constraint ?? new Size(1000, 1000),
};
}
private static void AssertEqual(Point expected, Point actual)
{
if (!expected.NearlyEquals(actual))
{
throw new EqualException(expected, actual);
}
}
private static void AssertEqual(Size expected, Size actual)
{
if (!expected.NearlyEquals(actual))
{
throw new EqualException(expected, actual);
}
}
private class TestLayoutable : Control
{
private Size _desiredSize;
public TestLayoutable(Size desiredSize, double margin = 0)
{
_desiredSize = desiredSize;
Margin = new Thickness(margin);
}
protected override Size MeasureOverride(Size availableSize) => _desiredSize;
}
}
}

2
tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs

@ -27,7 +27,7 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
private bool Run(BiDiTestData testData) private bool Run(BiDiTestData testData)
{ {
var bidi = BidiAlgorithm.Instance.Value; var bidi = new BidiAlgorithm();
// Run the algorithm... // Run the algorithm...
ArraySlice<sbyte> resultLevels; ArraySlice<sbyte> resultLevels;

9
tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs

@ -30,7 +30,7 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
private bool Run(BiDiClassData t) private bool Run(BiDiClassData t)
{ {
var bidi = BidiAlgorithm.Instance.Value; var bidi = new BidiAlgorithm();
var bidiData = new BidiData(t.ParagraphLevel); var bidiData = new BidiData(t.ParagraphLevel);
var text = Encoding.UTF32.GetString(MemoryMarshal.Cast<int, byte>(t.CodePoints).ToArray()); var text = Encoding.UTF32.GetString(MemoryMarshal.Cast<int, byte>(t.CodePoints).ToArray());
@ -39,9 +39,12 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
bidiData.Append(text.AsMemory()); bidiData.Append(text.AsMemory());
// Act // Act
bidi.Process(bidiData); for (int i = 0; i < 10; i++)
{
bidi.Process(bidiData);
}
var resultLevels = bidi.ResolvedLevels; var resultLevels = bidi.ResolvedLevels;
var resultParagraphLevel = bidi.ResolvedParagraphEmbeddingLevel; var resultParagraphLevel = bidi.ResolvedParagraphEmbeddingLevel;
// Assert // Assert

2
tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs

@ -129,6 +129,8 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
ParagraphEmbeddingLevel = paragraphEmbeddingLevel, ParagraphEmbeddingLevel = paragraphEmbeddingLevel,
Levels = levels Levels = levels
}); });
break;
} }
} }
} }

9
tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs

@ -19,11 +19,6 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
var trie = GenerateBreakTypeTrie(); var trie = GenerateBreakTypeTrie();
UnicodeDataGenerator.GenerateTrieClass("GraphemeBreak", trie); UnicodeDataGenerator.GenerateTrieClass("GraphemeBreak", trie);
using (var stream = File.Create("Generated\\GraphemeBreak.trie"))
{
trie.Save(stream);
}
} }
private static UnicodeTrie GenerateBreakTypeTrie() private static UnicodeTrie GenerateBreakTypeTrie()
@ -38,9 +33,9 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
{ {
foreach (var (start, end, graphemeBreakType) in breakData) foreach (var (start, end, graphemeBreakType) in breakData)
{ {
if (!Enum.TryParse<GraphemeBreakClass>(graphemeBreakType, out var value)) if (!Enum.TryParse<GraphemeBreakClass>(graphemeBreakType.Replace("_", ""), out var value))
{ {
continue; continue;
} }
if (start == end) if (start == end)

84
tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs

@ -1,6 +1,10 @@
using System; using System;
using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Visuals.UnitTests.Media.TextFormatting;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Avalonia.Base.UnitTests.Media.TextFormatting namespace Avalonia.Base.UnitTests.Media.TextFormatting
{ {
@ -10,20 +14,74 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
/// </summary> /// </summary>
public class GraphemeBreakClassTrieGeneratorTests public class GraphemeBreakClassTrieGeneratorTests
{ {
[Theory(Skip = "Only run when we update the trie.")] private readonly ITestOutputHelper _outputHelper;
[ClassData(typeof(GraphemeBreakTestDataGenerator))]
public void Should_Enumerate(string text, int expectedLength) public GraphemeBreakClassTrieGeneratorTests(ITestOutputHelper outputHelper)
{ {
var textMemory = text.AsMemory(); _outputHelper = outputHelper;
}
var enumerator = new GraphemeEnumerator(textMemory); [Fact(/*Skip = "Only run when we update the trie."*/)]
public void Should_Enumerate()
{
var generator = new GraphemeBreakTestDataGenerator();
Assert.True(enumerator.MoveNext()); foreach (var testData in generator)
{
Assert.True(Run(testData));
}
}
Assert.Equal(expectedLength, enumerator.Current.Text.Length); private bool Run(GraphemeBreakData t)
{
var text = Encoding.UTF32.GetString(MemoryMarshal.Cast<int, byte>(t.Codepoints).ToArray());
var grapheme = Encoding.UTF32.GetString(MemoryMarshal.Cast<int, byte>(t.Grapheme).ToArray()).AsSpan();
var enumerator = new GraphemeEnumerator(text.AsMemory());
enumerator.MoveNext();
var actual = enumerator.Current.Text.Span;
var pass = true;
if(actual.Length != grapheme.Length)
{
pass = false;
}
if (pass)
{
for (int i = 0; i < grapheme.Length; i++)
{
var a = grapheme[i];
var b = actual[i];
if (a != b)
{
pass = false;
break;
}
}
}
if (!pass)
{
_outputHelper.WriteLine($"Failed line {t.LineNumber}");
_outputHelper.WriteLine($" Text: {text}");
_outputHelper.WriteLine($" Codepoints: {string.Join(" ", t.Codepoints)}");
_outputHelper.WriteLine($" Grapheme: {string.Join(" ", t.Grapheme)}");
_outputHelper.WriteLine($" Line: {t.Line}");
return false;
}
return true;
} }
[Fact(Skip = "Only run when we update the trie.")] [Fact(/*Skip = "Only run when we update the trie."*/)]
public void Should_Enumerate_Other() public void Should_Enumerate_Other()
{ {
const string text = "ABCDEFGHIJ"; const string text = "ABCDEFGHIJ";
@ -44,18 +102,10 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
Assert.Equal(10, count); Assert.Equal(10, count);
} }
[Fact(Skip = "Only run when we update the trie.")] [Fact(/*Skip = "Only run when we update the trie."*/)]
public void Should_Generate_Trie() public void Should_Generate_Trie()
{ {
GraphemeBreakClassTrieGenerator.Execute(); GraphemeBreakClassTrieGenerator.Execute();
} }
private class GraphemeBreakTestDataGenerator : TestDataGenerator
{
public GraphemeBreakTestDataGenerator()
: base("auxiliary/GraphemeBreakTest.txt")
{
}
}
} }
} }

108
tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs

@ -0,0 +1,108 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using Avalonia.Base.UnitTests.Media.TextFormatting;
namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
{
internal class GraphemeBreakTestDataGenerator : IEnumerable<GraphemeBreakData>
{
private readonly List<GraphemeBreakData> _testData;
public GraphemeBreakTestDataGenerator()
{
_testData = ReadData();
}
public IEnumerator<GraphemeBreakData> GetEnumerator()
{
return _testData.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private static List<GraphemeBreakData> ReadData()
{
var testData = new List<GraphemeBreakData>();
var url = Path.Combine(UnicodeDataGenerator.Ucd, "auxiliary/GraphemeBreakTest.txt");
using (var client = new HttpClient())
using (var result = client.GetAsync(url).GetAwaiter().GetResult())
{
if (!result.IsSuccessStatusCode)
{
return testData;
}
using (var stream = result.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
using (var reader = new StreamReader(stream))
{
var lineNumber = 0;
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
lineNumber++;
if (line == null)
{
break;
}
if (line.StartsWith("#") || string.IsNullOrEmpty(line))
{
continue;
}
var elements = line.Split('#');
elements = elements[0].Replace("÷\t", "÷").Trim('÷').Split('÷');
var graphemeChars = elements[0].Replace(" × ", " ").Split(' ');
var codepoints = graphemeChars.Where(x => x != "" && x != "×")
.Select(x => Convert.ToInt32(x, 16)).ToList();
var grapheme = codepoints.ToArray();
if(elements.Length > 1)
{
var remainingChars = elements[1].Replace(" × ", " ").Split(' ');
var remaining = remainingChars.Where(x => x != "" && x != "×").Select(x => Convert.ToInt32(x, 16)).ToArray();
codepoints.AddRange(remaining);
}
var data = new GraphemeBreakData
{
Line = line,
LineNumber = lineNumber,
Grapheme = grapheme,
Codepoints = codepoints.ToArray()
};
testData.Add(data);
}
}
}
return testData;
}
}
internal struct GraphemeBreakData
{
public string Line { get; set; }
public int LineNumber { get; set; }
public int[] Grapheme { get; set; }
public int[] Codepoints{ get; set; }
}
}

2
tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs

@ -134,7 +134,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
_outputHelper.WriteLine($"Expected Breaks: {string.Join(" ", breakPoints)}"); _outputHelper.WriteLine($"Expected Breaks: {string.Join(" ", breakPoints)}");
_outputHelper.WriteLine($" Actual Breaks: {string.Join(" ", foundBreaks)}"); _outputHelper.WriteLine($" Actual Breaks: {string.Join(" ", foundBreaks)}");
_outputHelper.WriteLine($" Text: {text}"); _outputHelper.WriteLine($" Text: {text}");
_outputHelper.WriteLine($" Char Props: {string.Join(" ", codePoints.Select(x => new Codepoint(x).LineBreakClass))}"); _outputHelper.WriteLine($" Char Props: {string.Join(" ", codePoints.Select(x => new Codepoint((uint)x).LineBreakClass))}");
_outputHelper.WriteLine(""); _outputHelper.WriteLine("");
} }

85
tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs

@ -1,85 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
namespace Avalonia.Base.UnitTests.Media.TextFormatting
{
public abstract class TestDataGenerator : IEnumerable<object[]>
{
private readonly string _fileName;
private readonly List<object[]> _testData;
protected TestDataGenerator(string fileName)
{
_fileName = fileName;
_testData = ReadTestData();
}
public IEnumerator<object[]> GetEnumerator()
{
return _testData.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private List<object[]> ReadTestData()
{
var testData = new List<object[]>();
using (var client = new HttpClient())
{
var url = Path.Combine(UnicodeDataGenerator.Ucd, _fileName);
using (var result = client.GetAsync(url).GetAwaiter().GetResult())
{
if (!result.IsSuccessStatusCode)
return testData;
using (var stream = result.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line == null)
{
break;
}
if (line.StartsWith("#") || string.IsNullOrEmpty(line))
{
continue;
}
var elements = line.Split('#');
elements = elements[0].Replace("÷\t", "÷").Trim('÷').Split('÷');
var chars = elements[0].Replace(" × ", " ").Split(' ');
var codepoints = chars.Where(x => x != "" && x != "×")
.Select(x => Convert.ToInt32(x, 16)).ToArray();
var text = string.Join(null, codepoints.Select(char.ConvertFromUtf32));
var length = codepoints.Select(x => x > ushort.MaxValue ? 2 : 1).Sum();
var data = new object[] { text, length };
testData.Add(data);
}
}
}
}
return testData;
}
}
}

58
tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs

@ -10,22 +10,22 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
{ {
internal static class UnicodeDataGenerator internal static class UnicodeDataGenerator
{ {
public const string Ucd = "https://www.unicode.org/Public/13.0.0/ucd/"; public const string Ucd = "https://www.unicode.org/Public/14.0.0/ucd/";
public static UnicodeTrie GenerateBiDiTrie(out BiDiDataEntries biDiDataEntries,out Dictionary<int, BiDiDataItem> biDiData) public static UnicodeTrie GenerateBiDiTrie(out BiDiDataEntries biDiDataEntries, out Dictionary<int, BiDiDataItem> biDiData)
{ {
biDiData = new Dictionary<int, BiDiDataItem>(); biDiData = new Dictionary<int, BiDiDataItem>();
var biDiClassEntries = var bidiClassEntries =
UnicodeEnumsGenerator.CreateBiDiClassEnum(); UnicodeEnumsGenerator.CreateBidiClassEnum();
var biDiClassMappings = CreateTagToIndexMappings(biDiClassEntries); var bidiClassMappings = CreateTagToIndexMappings(bidiClassEntries);
var biDiClassData = ReadBiDiData(); var bidiClassData = ReadBiDiData();
foreach (var (range, name) in biDiClassData) foreach (var (range, name) in bidiClassData)
{ {
var biDiClass = biDiClassMappings[name]; var biDiClass = bidiClassMappings[name];
AddBiDiClassRange(biDiData, range, biDiClass); AddBiDiClassRange(biDiData, range, biDiClass);
} }
@ -56,19 +56,14 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
biDiDataEntries = new BiDiDataEntries() biDiDataEntries = new BiDiDataEntries()
{ {
PairedBracketTypes = biDiPairedBracketTypeEntries, BiDiClasses = biDiClassEntries PairedBracketTypes = biDiPairedBracketTypeEntries, BiDiClasses = bidiClassEntries
}; };
var trie = biDiTrieBuilder.Freeze(); var trie = biDiTrieBuilder.Freeze();
GenerateTrieClass("BiDi", trie); GenerateTrieClass("Bidi", trie);
using (var stream = File.Create("Generated\\BiDi.trie"))
{
trie.Save(stream);
return trie; return trie;
}
} }
public static void GenerateTrieClass(string name, UnicodeTrie trie) public static void GenerateTrieClass(string name, UnicodeTrie trie)
@ -92,6 +87,8 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
writer.Write(" "); writer.Write(" ");
long length = stream.Length;
while (true) while (true)
{ {
var b = stream.ReadByte(); var b = stream.ReadByte();
@ -100,18 +97,22 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
{ {
break; break;
} }
writer.Write(b.ToString());
writer.Write(','); writer.Write(b.ToString());
if (stream.Position % 100 == 0) if (stream.Position % 100 > 0 && stream.Position != length)
{
writer.Write(", ");
}
else
{ {
writer.Write(',');
writer.Write(Environment.NewLine); writer.Write(Environment.NewLine);
writer.Write(" "); if (stream.Position != length)
{
continue; writer.Write(" ");
}
} }
} }
@ -161,12 +162,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
GenerateTrieClass("UnicodeData", trie); GenerateTrieClass("UnicodeData", trie);
using (var stream = File.Create("Generated\\UnicodeData.trie")) return trie;
{
trie.Save(stream);
return trie;
}
} }
private static Dictionary<int, UnicodeDataItem> GetUnicodeData(IReadOnlyDictionary<string, int> generalCategoryMappings, private static Dictionary<int, UnicodeDataItem> GetUnicodeData(IReadOnlyDictionary<string, int> generalCategoryMappings,
@ -461,9 +457,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
public int BracketType { get; set; } public int BracketType { get; set; }
public int BiDiClass { get; set; } public int BiDiClass { get; set; }
} }
} }
internal class UnicodeDataEntries internal class UnicodeDataEntries

4
tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs

@ -22,7 +22,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
foreach (var value in unicodeData.Values) foreach (var value in unicodeData.Values)
{ {
var data = unicodeDataTrie.Get(value.Codepoint); var data = unicodeDataTrie.Get((uint)value.Codepoint);
Assert.Equal(value.GeneralCategory, GetValue(data, 0, UnicodeData.CATEGORY_MASK)); Assert.Equal(value.GeneralCategory, GetValue(data, 0, UnicodeData.CATEGORY_MASK));
@ -35,7 +35,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
foreach (var value in biDiData.Values) foreach (var value in biDiData.Values)
{ {
var data = biDiTrie.Get(value.Codepoint); var data = biDiTrie.Get((uint)value.Codepoint);
Assert.Equal(value.Bracket, GetValue(data, 0, UnicodeData.BIDIPAIREDBRACKED_MASK)); Assert.Equal(value.Bracket, GetValue(data, 0, UnicodeData.BIDIPAIREDBRACKED_MASK));

10
tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs

@ -244,18 +244,18 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
return orderedLineBreakEntries.Values.ToList(); return orderedLineBreakEntries.Values.ToList();
} }
public static List<DataEntry> CreateBiDiClassEnum() public static List<DataEntry> CreateBidiClassEnum()
{ {
var entries = new List<DataEntry> { new DataEntry("Left_To_Right", "L", string.Empty) }; var entries = new List<DataEntry> { new DataEntry("Left_To_Right", "L", string.Empty) };
ParseDataEntries("# Bidi_Class (bc)", entries); ParseDataEntries("# Bidi_Class (bc)", entries);
using (var stream = File.Create("Generated\\BiDiClass.cs")) using (var stream = File.Create("Generated\\BidiClass.cs"))
using (var writer = new StreamWriter(stream)) using (var writer = new StreamWriter(stream))
{ {
writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode"); writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode");
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" public enum BiDiClass"); writer.WriteLine(" public enum BidiClass");
writer.WriteLine(" {"); writer.WriteLine(" {");
foreach (var entry in entries) foreach (var entry in entries)
@ -277,12 +277,12 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
ParseDataEntries("# Bidi_Paired_Bracket_Type (bpt)", entries); ParseDataEntries("# Bidi_Paired_Bracket_Type (bpt)", entries);
using (var stream = File.Create("Generated\\BiDiPairedBracketType.cs")) using (var stream = File.Create("Generated\\BidiPairedBracketType.cs"))
using (var writer = new StreamWriter(stream)) using (var writer = new StreamWriter(stream))
{ {
writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode"); writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode");
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" public enum BiDiPairedBracketType"); writer.WriteLine(" public enum BidiPairedBracketType");
writer.WriteLine(" {"); writer.WriteLine(" {");
foreach (var entry in entries) foreach (var entry in entries)

64
tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -594,6 +594,69 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
} }
} }
[Fact]
public void Should_Update_When_Control_Moved_Causing_Layout_Change()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator moveFrom;
Decorator moveTo;
Canvas moveMe;
var tree = new TestRoot
{
Width = 100,
Height = 100,
Child = new DockPanel
{
Children =
{
(moveFrom = new Decorator
{
Child = moveMe = new Canvas
{
Width = 100,
Height = 100,
},
}),
(moveTo = new Decorator()),
}
}
};
tree.Measure(Size.Infinity);
tree.Arrange(new Rect(tree.DesiredSize));
var scene = new Scene(tree);
var sceneBuilder = new SceneBuilder();
sceneBuilder.UpdateAll(scene);
var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
var moveToNode = (VisualNode)scene.FindNode(moveTo);
Assert.Equal(1, moveFromNode.Children.Count);
Assert.Same(moveMe, moveFromNode.Children[0].Visual);
Assert.Empty(moveToNode.Children);
moveFrom.Child = null;
moveTo.Child = moveMe;
tree.LayoutManager.ExecuteLayoutPass();
scene = scene.CloneScene();
moveFromNode = (VisualNode)scene.FindNode(moveFrom);
moveToNode = (VisualNode)scene.FindNode(moveTo);
moveFromNode.SortChildren(scene);
moveToNode.SortChildren(scene);
sceneBuilder.Update(scene, moveFrom);
sceneBuilder.Update(scene, moveTo);
sceneBuilder.Update(scene, moveMe);
Assert.Empty(moveFromNode.Children);
Assert.Equal(1, moveToNode.Children.Count);
Assert.Same(moveMe, moveToNode.Children[0].Visual);
}
}
[Fact] [Fact]
public void Should_Update_When_Control_Made_Invisible() public void Should_Update_When_Control_Made_Invisible()
{ {
@ -838,6 +901,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
Canvas canvas; Canvas canvas;
var tree = new TestRoot var tree = new TestRoot
{ {
ClientSize = new Size(100, 100),
Child = decorator = new Decorator Child = decorator = new Decorator
{ {
Margin = new Thickness(0, 10, 0, 0), Margin = new Thickness(0, 10, 0, 0),

65
tests/Avalonia.Controls.UnitTests/BorderTests.cs

@ -1,6 +1,8 @@
using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq; using Moq;
using Xunit; using Xunit;
@ -60,5 +62,68 @@ namespace Avalonia.Controls.UnitTests
renderer.Verify(x => x.AddDirty(target), Times.Once); renderer.Verify(x => x.AddDirty(target), Times.Once);
} }
public class UseLayoutRounding
{
[Fact]
public void Measure_Rounds_Padding()
{
var target = new Border
{
Padding = new Thickness(1),
Child = new Canvas
{
Width = 101,
Height = 101,
}
};
var root = CreatedRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel padding is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Desired size = 101.3333 + 2.6666 = 104
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
[Fact]
public void Measure_Rounds_BorderThickness()
{
var target = new Border
{
BorderThickness = new Thickness(1),
Child = new Canvas
{
Width = 101,
Height = 101,
}
};
var root = CreatedRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel border thickness is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Desired size = 101.3333 + 2.6666 = 104
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
private static TestRoot CreatedRoot(
double scaling,
Control child,
Size? constraint = null)
{
return new TestRoot
{
LayoutScaling = scaling,
UseLayoutRounding = true,
Child = child,
ClientSize = constraint ?? new Size(1000, 1000),
};
}
}
} }
} }

12
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -150,7 +150,7 @@ namespace Avalonia.Controls.UnitTests
target.Click += (s, e) => clicked = true; target.Click += (s, e) => clicked = true;
RaisePointerEnter(target); RaisePointerEntered(target);
RaisePointerMove(target, pt); RaisePointerMove(target, pt);
RaisePointerPressed(target, 1, MouseButton.Left, pt); RaisePointerPressed(target, 1, MouseButton.Left, pt);
@ -182,10 +182,10 @@ namespace Avalonia.Controls.UnitTests
target.Click += (s, e) => clicked = true; target.Click += (s, e) => clicked = true;
RaisePointerEnter(target); RaisePointerEntered(target);
RaisePointerMove(target, new Point(50,50)); RaisePointerMove(target, new Point(50,50));
RaisePointerPressed(target, 1, MouseButton.Left, new Point(50, 50)); RaisePointerPressed(target, 1, MouseButton.Left, new Point(50, 50));
RaisePointerLeave(target); RaisePointerExited(target);
Assert.Equal(_helper.Captured, target); Assert.Equal(_helper.Captured, target);
@ -224,7 +224,7 @@ namespace Avalonia.Controls.UnitTests
target.Click += (s, e) => clicked = true; target.Click += (s, e) => clicked = true;
RaisePointerEnter(target); RaisePointerEntered(target);
RaisePointerMove(target, pt); RaisePointerMove(target, pt);
RaisePointerPressed(target, 1, MouseButton.Left, pt); RaisePointerPressed(target, 1, MouseButton.Left, pt);
@ -422,12 +422,12 @@ namespace Avalonia.Controls.UnitTests
_helper.Up(button, mouseButton, pt); _helper.Up(button, mouseButton, pt);
} }
private void RaisePointerEnter(Button button) private void RaisePointerEntered(Button button)
{ {
_helper.Enter(button); _helper.Enter(button);
} }
private void RaisePointerLeave(Button button) private void RaisePointerExited(Button button)
{ {
_helper.Leave(button); _helper.Leave(button);
} }

41
tests/Avalonia.Controls.UnitTests/DecoratorTests.cs

@ -1,6 +1,7 @@
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.UnitTests;
using Xunit; using Xunit;
namespace Avalonia.Controls.UnitTests namespace Avalonia.Controls.UnitTests
@ -116,5 +117,45 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Size(16, 16), target.DesiredSize); Assert.Equal(new Size(16, 16), target.DesiredSize);
} }
public class UseLayoutRounding
{
[Fact]
public void Measure_Rounds_Padding()
{
var target = new Decorator
{
Padding = new Thickness(1),
Child = new Canvas
{
Width = 101,
Height = 101,
}
};
var root = CreatedRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel padding is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Desired size = 101.3333 + 2.6666 = 104
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
private static TestRoot CreatedRoot(
double scaling,
Control child,
Size? constraint = null)
{
return new TestRoot
{
LayoutScaling = scaling,
UseLayoutRounding = true,
Child = child,
ClientSize = constraint ?? new Size(1000, 1000),
};
}
}
} }
} }

64
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@ -174,7 +174,7 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
[Fact] [Fact]
public void PointerEnter_Opens_Item_When_Old_Item_Is_Open() public void PointerEntered_Opens_Item_When_Old_Item_Is_Open()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
@ -187,11 +187,11 @@ namespace Avalonia.Controls.UnitTests.Platform
x.IsTopLevel == true && x.IsTopLevel == true &&
x.HasSubMenu == true && x.HasSubMenu == true &&
x.Parent == menu.Object); x.Parent == menu.Object);
var e = CreateArgs(MenuItem.PointerEnterItemEvent, nextItem); var e = CreateArgs(MenuItem.PointerEnteredItemEvent, nextItem);
menu.SetupGet(x => x.SelectedItem).Returns(item); menu.SetupGet(x => x.SelectedItem).Returns(item);
target.PointerEnter(nextItem, e); target.PointerEntered(nextItem, e);
Mock.Get(item).Verify(x => x.Close()); Mock.Get(item).Verify(x => x.Close());
menu.VerifySet(x => x.SelectedItem = nextItem); menu.VerifySet(x => x.SelectedItem = nextItem);
@ -202,31 +202,31 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
[Fact] [Fact]
public void PointerLeave_Deselects_Item_When_Menu_Not_Open() public void PointerExited_Deselects_Item_When_Menu_Not_Open()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var e = CreateArgs(MenuItem.PointerExitedItemEvent, item);
menu.SetupGet(x => x.SelectedItem).Returns(item); menu.SetupGet(x => x.SelectedItem).Returns(item);
target.PointerLeave(item, e); target.PointerExited(item, e);
menu.VerifySet(x => x.SelectedItem = null); menu.VerifySet(x => x.SelectedItem = null);
Assert.False(e.Handled); Assert.False(e.Handled);
} }
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open() public void PointerExited_Doesnt_Deselect_Item_When_Menu_Open()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock<IMenu>(); var menu = new Mock<IMenu>();
var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object); var item = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var e = CreateArgs(MenuItem.PointerExitedItemEvent, item);
menu.SetupGet(x => x.IsOpen).Returns(true); menu.SetupGet(x => x.IsOpen).Returns(true);
menu.SetupGet(x => x.SelectedItem).Returns(item); menu.SetupGet(x => x.SelectedItem).Returns(item);
target.PointerLeave(item, e); target.PointerExited(item, e);
menu.VerifySet(x => x.SelectedItem = null, Times.Never); menu.VerifySet(x => x.SelectedItem = null, Times.Never);
Assert.False(e.Handled); Assert.False(e.Handled);
@ -382,31 +382,31 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
[Fact] [Fact]
public void PointerEnter_Selects_Item() public void PointerEntered_Selects_Item()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = CreateArgs(MenuItem.PointerEnterItemEvent, item); var e = CreateArgs(MenuItem.PointerEnteredItemEvent, item);
target.PointerEnter(item, e); target.PointerEntered(item, e);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
Assert.False(e.Handled); Assert.False(e.Handled);
} }
[Fact] [Fact]
public void PointerEnter_Opens_Submenu_After_Delay() public void PointerEntered_Opens_Submenu_After_Delay()
{ {
var timer = new TestTimer(); var timer = new TestTimer();
var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce); var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = CreateArgs(MenuItem.PointerEnterItemEvent, item); var e = CreateArgs(MenuItem.PointerEnteredItemEvent, item);
target.PointerEnter(item, e); target.PointerEntered(item, e);
Mock.Get(item).Verify(x => x.Open(), Times.Never); Mock.Get(item).Verify(x => x.Open(), Times.Never);
timer.Pulse(); timer.Pulse();
@ -416,7 +416,7 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
[Fact] [Fact]
public void PointerEnter_Closes_Sibling_Submenu_After_Delay() public void PointerEntered_Closes_Sibling_Submenu_After_Delay()
{ {
var timer = new TestTimer(); var timer = new TestTimer();
var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce); var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
@ -424,11 +424,11 @@ namespace Avalonia.Controls.UnitTests.Platform
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsSubMenuOpen == true); var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsSubMenuOpen == true);
var e = CreateArgs(MenuItem.PointerEnterItemEvent, item); var e = CreateArgs(MenuItem.PointerEnteredItemEvent, item);
Mock.Get(parentItem).SetupGet(x => x.SubItems).Returns(new[] { item, sibling }); Mock.Get(parentItem).SetupGet(x => x.SubItems).Returns(new[] { item, sibling });
target.PointerEnter(item, e); target.PointerEntered(item, e);
Mock.Get(sibling).Verify(x => x.Close(), Times.Never); Mock.Get(sibling).Verify(x => x.Close(), Times.Never);
timer.Pulse(); timer.Pulse();
@ -438,48 +438,48 @@ namespace Avalonia.Controls.UnitTests.Platform
} }
[Fact] [Fact]
public void PointerLeave_Deselects_Item() public void PointerExited_Deselects_Item()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var e = CreateArgs(MenuItem.PointerExitedItemEvent, item);
Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(item); Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(item);
target.PointerLeave(item, e); target.PointerExited(item, e);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null);
Assert.False(e.Handled); Assert.False(e.Handled);
} }
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Sibling() public void PointerExited_Doesnt_Deselect_Sibling()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem); var sibling = Mock.Of<IMenuItem>(x => x.Parent == parentItem);
var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var e = CreateArgs(MenuItem.PointerExitedItemEvent, item);
Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(sibling); Mock.Get(parentItem).SetupGet(x => x.SelectedItem).Returns(sibling);
target.PointerLeave(item, e); target.PointerExited(item, e);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never);
Assert.False(e.Handled); Assert.False(e.Handled);
} }
[Fact] [Fact]
public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu() public void PointerExited_Doesnt_Deselect_Item_If_Pointer_Over_Submenu()
{ {
var target = new DefaultMenuInteractionHandler(false); var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of<IMenu>(); var menu = Mock.Of<IMenu>();
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true);
var e = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var e = CreateArgs(MenuItem.PointerExitedItemEvent, item);
target.PointerLeave(item, e); target.PointerExited(item, e);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = null, Times.Never);
Assert.False(e.Handled); Assert.False(e.Handled);
@ -510,11 +510,11 @@ namespace Avalonia.Controls.UnitTests.Platform
var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu); var parentItem = Mock.Of<IMenuItem>(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true); var item = Mock.Of<IMenuItem>(x => x.Parent == parentItem && x.HasSubMenu == true);
var childItem = Mock.Of<IMenuItem>(x => x.Parent == item); var childItem = Mock.Of<IMenuItem>(x => x.Parent == item);
var enter = CreateArgs(MenuItem.PointerEnterItemEvent, item); var enter = CreateArgs(MenuItem.PointerEnteredItemEvent, item);
var leave = CreateArgs(MenuItem.PointerLeaveItemEvent, item); var leave = CreateArgs(MenuItem.PointerExitedItemEvent, item);
// Pointer enters item; item is selected. // Pointer enters item; item is selected.
target.PointerEnter(item, enter); target.PointerEntered(item, enter);
Assert.True(timer.ActionIsQueued); Assert.True(timer.ActionIsQueued);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
Mock.Get(parentItem).Invocations.Clear(); Mock.Get(parentItem).Invocations.Clear();
@ -526,13 +526,13 @@ namespace Avalonia.Controls.UnitTests.Platform
Mock.Get(item).Invocations.Clear(); Mock.Get(item).Invocations.Clear();
// Pointer briefly exits item, but submenu remains open. // Pointer briefly exits item, but submenu remains open.
target.PointerLeave(item, leave); target.PointerExited(item, leave);
Mock.Get(item).Verify(x => x.Close(), Times.Never); Mock.Get(item).Verify(x => x.Close(), Times.Never);
Mock.Get(item).Invocations.Clear(); Mock.Get(item).Invocations.Clear();
// Pointer enters child item; is selected. // Pointer enters child item; is selected.
enter.Source = childItem; enter.Source = childItem;
target.PointerEnter(childItem, enter); target.PointerEntered(childItem, enter);
Mock.Get(item).VerifySet(x => x.SelectedItem = childItem); Mock.Get(item).VerifySet(x => x.SelectedItem = childItem);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
Mock.Get(item).Invocations.Clear(); Mock.Get(item).Invocations.Clear();

66
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs

@ -1,5 +1,6 @@
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.UnitTests;
using Xunit; using Xunit;
namespace Avalonia.Controls.UnitTests.Presenters namespace Avalonia.Controls.UnitTests.Presenters
@ -232,5 +233,68 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(new Rect(32, 32, 0, 0), content.Bounds); Assert.Equal(new Rect(32, 32, 0, 0), content.Bounds);
} }
public class UseLayoutRounding
{
[Fact]
public void Measure_Rounds_Padding()
{
var target = new ContentPresenter
{
Padding = new Thickness(1),
Content = new Canvas
{
Width = 101,
Height = 101,
}
};
var root = CreatedRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel padding is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Desired size = 101.3333 + 2.6666 = 104
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
[Fact]
public void Measure_Rounds_BorderThickness()
{
var target = new ContentPresenter
{
BorderThickness = new Thickness(1),
Content = new Canvas
{
Width = 101,
Height = 101,
}
};
var root = CreatedRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel border thickness is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 101 gets rounded up to 101.3333
// - Desired size = 101.3333 + 2.6666 = 104
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
private static TestRoot CreatedRoot(
double scaling,
Control child,
Size? constraint = null)
{
return new TestRoot
{
LayoutScaling = scaling,
UseLayoutRounding = true,
Child = child,
ClientSize = constraint ?? new Size(1000, 1000),
};
}
}
} }
} }

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

Loading…
Cancel
Save