diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-09-07 13:12:05 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-11-09 10:02:59 +0000 |
commit | 33fc33aa94d4add0878ec30dc818e34e1dd3cc2a (patch) | |
tree | f6af110909c79b2759136554f1143d8b0572af0a /chromium/ui/events | |
parent | 7d2c5d177e9813077a621df8d18c0deda73099b3 (diff) | |
download | qtwebengine-chromium-33fc33aa94d4add0878ec30dc818e34e1dd3cc2a.tar.gz |
BASELINE: Update Chromium to 104.0.5112.120
Change-Id: I5d2726c2ab018d75d055739b6ba64317904f05bb
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/438935
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/events')
82 files changed, 5882 insertions, 530 deletions
diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn index bdc40a6b505..62b4dd632d6 100644 --- a/chromium/ui/events/BUILD.gn +++ b/chromium/ui/events/BUILD.gn @@ -495,7 +495,7 @@ component("gesture_detection") { if (is_android) { sources += [ "gesture_detection/gesture_configuration_android.cc" ] - } else if (is_chromecast) { + } else if (is_castos) { sources += [ "gesture_detection/gesture_configuration_cast.cc" ] } else if (use_aura) { sources += [ "gesture_detection/gesture_configuration_aura.cc" ] @@ -712,7 +712,7 @@ if (!is_ios) { deps += [ "//ui/events:keyboard_hook" ] } - if (is_chromecast && !is_android) { + if (is_castos) { sources += [ "chromecast/scroller_unittest.cc" ] } diff --git a/chromium/ui/events/blink/blink_event_util.h b/chromium/ui/events/blink/blink_event_util.h index e0f3d387d19..ed105fc09f7 100644 --- a/chromium/ui/events/blink/blink_event_util.h +++ b/chromium/ui/events/blink/blink_event_util.h @@ -29,6 +29,10 @@ struct GestureEventData; struct GestureEventDetails; class MotionEvent; +// The scroll percentage per mousewheel tick. Used to determine scroll delta +// if percent based scrolling is enabled. +const float kScrollPercentPerLineOrChar = 0.05f; + blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( const MotionEvent& event, bool may_cause_scrolling, diff --git a/chromium/ui/events/blink/web_input_event.cc b/chromium/ui/events/blink/web_input_event.cc index 7143fb67ae4..1b7d11a6362 100644 --- a/chromium/ui/events/blink/web_input_event.cc +++ b/chromium/ui/events/blink/web_input_event.cc @@ -22,10 +22,6 @@ namespace ui { namespace { -// The scroll percentage per mousewheel tick. Used to determine scroll delta -// if percent based scrolling is enabled. -const float kScrollPercentPerLineOrChar = 0.05; - gfx::PointF GetScreenLocationFromEvent(const LocatedEvent& event) { return event.target() ? event.target()->GetScreenLocationF(event) : event.root_location_f(); diff --git a/chromium/ui/events/blink/web_input_event_builders_win.cc b/chromium/ui/events/blink/web_input_event_builders_win.cc index fdaef0451cd..6d8d63a7377 100644 --- a/chromium/ui/events/blink/web_input_event_builders_win.cc +++ b/chromium/ui/events/blink/web_input_event_builders_win.cc @@ -21,7 +21,6 @@ namespace ui { static const unsigned long kDefaultScrollLinesPerWheelDelta = 3; static const unsigned long kDefaultScrollCharsPerWheelDelta = 1; -static const unsigned long kScrollPercentPerLineOrChar = 5; // WebMouseEvent -------------------------------------------------------------- @@ -313,7 +312,7 @@ WebMouseWheelEvent WebMouseWheelEventBuilder::Build( // the percentage amount (out of 1, i.e. 1 == 100%) the targeted scroller // should scroll. This percentage will be resolved against the size of // the scroller in the renderer process. - scroll_delta *= kScrollPercentPerLineOrChar / 100.f; + scroll_delta *= kScrollPercentPerLineOrChar; result.delta_units = ui::ScrollGranularity::kScrollByPercentage; } else { // Convert wheel delta amount to a number of pixels to scroll. diff --git a/chromium/ui/events/cocoa/cocoa_event_utils.h b/chromium/ui/events/cocoa/cocoa_event_utils.h index f447342fa6f..12986a1c0f8 100644 --- a/chromium/ui/events/cocoa/cocoa_event_utils.h +++ b/chromium/ui/events/cocoa/cocoa_event_utils.h @@ -27,8 +27,8 @@ EVENTS_EXPORT int EventFlagsFromModifiers(NSUInteger modifiers); EVENTS_EXPORT int EventFlagsFromNSEventWithModifiers(NSEvent* event, NSUInteger modifiers); -// Returns true for |NSKeyUp| and for |NSFlagsChanged| when modifier key was -// released. +// Returns true for |NSEventTypeKeyUp| and for |NSEventTypeFlagsChanged| when +// modifier key was released. EVENTS_EXPORT bool IsKeyUpEvent(NSEvent* event); // Convert an NSEvent to an opaque serialization using CGEventCreateData. diff --git a/chromium/ui/events/cocoa/cocoa_event_utils.mm b/chromium/ui/events/cocoa/cocoa_event_utils.mm index 908e4c6ff30..dca4916c2ae 100644 --- a/chromium/ui/events/cocoa/cocoa_event_utils.mm +++ b/chromium/ui/events/cocoa/cocoa_event_utils.mm @@ -15,14 +15,15 @@ namespace { bool IsLeftButtonEvent(NSEvent* event) { NSEventType type = [event type]; - return type == NSLeftMouseDown || type == NSLeftMouseDragged || - type == NSLeftMouseUp; + return type == NSEventTypeLeftMouseDown || + type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp; } bool IsRightButtonEvent(NSEvent* event) { NSEventType type = [event type]; - return type == NSRightMouseDown || type == NSRightMouseDragged || - type == NSRightMouseUp; + return type == NSEventTypeRightMouseDown || + type == NSEventTypeRightMouseDragged || + type == NSEventTypeRightMouseUp; } bool IsMiddleButtonEvent(NSEvent* event) { @@ -30,8 +31,9 @@ bool IsMiddleButtonEvent(NSEvent* event) { return false; NSEventType type = [event type]; - return type == NSOtherMouseDown || type == NSOtherMouseDragged || - type == NSOtherMouseUp; + return type == NSEventTypeOtherMouseDown || + type == NSEventTypeOtherMouseDragged || + type == NSEventTypeOtherMouseUp; } // Return true if the target modifier key is up. OS X has an "official" flag @@ -60,11 +62,11 @@ namespace ui { int EventFlagsFromModifiers(NSUInteger modifiers) { int flags = 0; - flags |= (modifiers & NSAlphaShiftKeyMask) ? ui::EF_CAPS_LOCK_ON : 0; - flags |= (modifiers & NSShiftKeyMask) ? ui::EF_SHIFT_DOWN : 0; - flags |= (modifiers & NSControlKeyMask) ? ui::EF_CONTROL_DOWN : 0; - flags |= (modifiers & NSAlternateKeyMask) ? ui::EF_ALT_DOWN : 0; - flags |= (modifiers & NSCommandKeyMask) ? ui::EF_COMMAND_DOWN : 0; + flags |= (modifiers & NSEventModifierFlagCapsLock) ? ui::EF_CAPS_LOCK_ON : 0; + flags |= (modifiers & NSEventModifierFlagShift) ? ui::EF_SHIFT_DOWN : 0; + flags |= (modifiers & NSEventModifierFlagControl) ? ui::EF_CONTROL_DOWN : 0; + flags |= (modifiers & NSEventModifierFlagOption) ? ui::EF_ALT_DOWN : 0; + flags |= (modifiers & NSEventModifierFlagCommand) ? ui::EF_COMMAND_DOWN : 0; return flags; } @@ -73,7 +75,7 @@ int EventFlagsFromNSEventWithModifiers(NSEvent* event, NSUInteger modifiers) { if (IsLeftButtonEvent(event)) { // For Mac, convert Ctrl+LeftClick to a RightClick, and remove the Control // key modifier. - if (modifiers & NSControlKeyMask) + if (modifiers & NSEventModifierFlagControl) flags = (flags & ~ui::EF_CONTROL_DOWN) | ui::EF_RIGHT_MOUSE_BUTTON; else flags |= ui::EF_LEFT_MOUSE_BUTTON; @@ -82,50 +84,50 @@ int EventFlagsFromNSEventWithModifiers(NSEvent* event, NSUInteger modifiers) { flags |= IsRightButtonEvent(event) ? ui::EF_RIGHT_MOUSE_BUTTON : 0; flags |= IsMiddleButtonEvent(event) ? ui::EF_MIDDLE_MOUSE_BUTTON : 0; - if ([event type] == NSKeyDown && [event isARepeat]) + if ([event type] == NSEventTypeKeyDown && [event isARepeat]) flags |= ui::EF_IS_REPEAT; return flags; } bool IsKeyUpEvent(NSEvent* event) { - if ([event type] != NSFlagsChanged) - return [event type] == NSKeyUp; + if ([event type] != NSEventTypeFlagsChanged) + return [event type] == NSEventTypeKeyUp; switch ([event keyCode]) { case kVK_Command: return IsModifierKeyUp([event modifierFlags], NX_DEVICELCMDKEYMASK, - NX_DEVICERCMDKEYMASK, NSCommandKeyMask); + NX_DEVICERCMDKEYMASK, NSEventModifierFlagCommand); case kVK_RightCommand: return IsModifierKeyUp([event modifierFlags], NX_DEVICERCMDKEYMASK, - NX_DEVICELCMDKEYMASK, NSCommandKeyMask); + NX_DEVICELCMDKEYMASK, NSEventModifierFlagCommand); case kVK_CapsLock: - return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; + return ([event modifierFlags] & NSEventModifierFlagCapsLock) == 0; case kVK_Shift: return IsModifierKeyUp([event modifierFlags], NX_DEVICELSHIFTKEYMASK, - NX_DEVICERSHIFTKEYMASK, NSShiftKeyMask); + NX_DEVICERSHIFTKEYMASK, NSEventModifierFlagShift); case kVK_RightShift: return IsModifierKeyUp([event modifierFlags], NX_DEVICERSHIFTKEYMASK, - NX_DEVICELSHIFTKEYMASK, NSShiftKeyMask); + NX_DEVICELSHIFTKEYMASK, NSEventModifierFlagShift); case kVK_Option: return IsModifierKeyUp([event modifierFlags], NX_DEVICELALTKEYMASK, - NX_DEVICERALTKEYMASK, NSAlternateKeyMask); + NX_DEVICERALTKEYMASK, NSEventModifierFlagOption); case kVK_RightOption: return IsModifierKeyUp([event modifierFlags], NX_DEVICERALTKEYMASK, - NX_DEVICELALTKEYMASK, NSAlternateKeyMask); + NX_DEVICELALTKEYMASK, NSEventModifierFlagOption); case kVK_Control: return IsModifierKeyUp([event modifierFlags], NX_DEVICELCTLKEYMASK, - NX_DEVICERCTLKEYMASK, NSControlKeyMask); + NX_DEVICERCTLKEYMASK, NSEventModifierFlagControl); case kVK_RightControl: return IsModifierKeyUp([event modifierFlags], NX_DEVICERCTLKEYMASK, - NX_DEVICELCTLKEYMASK, NSControlKeyMask); + NX_DEVICELCTLKEYMASK, NSEventModifierFlagControl); case kVK_Function: - return ([event modifierFlags] & NSFunctionKeyMask) == 0; + return ([event modifierFlags] & NSEventModifierFlagFunction) == 0; } return false; } diff --git a/chromium/ui/events/cocoa/events_mac.mm b/chromium/ui/events/cocoa/events_mac.mm index b716ef85eec..a20fff8a55f 100644 --- a/chromium/ui/events/cocoa/events_mac.mm +++ b/chromium/ui/events/cocoa/events_mac.mm @@ -24,40 +24,40 @@ namespace ui { EventType EventTypeFromNative(const PlatformEvent& native_event) { NSEventType type = [native_event type]; switch (type) { - case NSKeyDown: - case NSKeyUp: - case NSFlagsChanged: + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + case NSEventTypeFlagsChanged: return IsKeyUpEvent(native_event) ? ET_KEY_RELEASED : ET_KEY_PRESSED; - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: + case NSEventTypeOtherMouseDown: return ET_MOUSE_PRESSED; - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseUp: return ET_MOUSE_RELEASED; - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: return ET_MOUSE_DRAGGED; - case NSMouseMoved: + case NSEventTypeMouseMoved: return ET_MOUSE_MOVED; - case NSScrollWheel: + case NSEventTypeScrollWheel: return ET_SCROLL; - case NSMouseEntered: + case NSEventTypeMouseEntered: return ET_MOUSE_ENTERED; - case NSMouseExited: + case NSEventTypeMouseExited: return ET_MOUSE_EXITED; case NSEventTypeSwipe: return ET_SCROLL_FLING_START; - case NSAppKitDefined: - case NSSystemDefined: + case NSEventTypeAppKitDefined: + case NSEventTypeSystemDefined: return ET_UNKNOWN; - case NSApplicationDefined: - case NSPeriodic: - case NSCursorUpdate: - case NSTabletPoint: - case NSTabletProximity: + case NSEventTypeApplicationDefined: + case NSEventTypePeriodic: + case NSEventTypeCursorUpdate: + case NSEventTypeTabletPoint: + case NSEventTypeTabletProximity: case NSEventTypeGesture: case NSEventTypeMagnify: case NSEventTypeRotate: @@ -112,17 +112,17 @@ int EventButtonFromNative(const PlatformEvent& native_event) { int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { NSEventType type = [native_event type]; switch (type) { - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSLeftMouseDragged: + case NSEventTypeLeftMouseDown: + case NSEventTypeLeftMouseUp: + case NSEventTypeLeftMouseDragged: return EF_LEFT_MOUSE_BUTTON; - case NSRightMouseDown: - case NSRightMouseUp: - case NSRightMouseDragged: + case NSEventTypeRightMouseDown: + case NSEventTypeRightMouseUp: + case NSEventTypeRightMouseDragged: return EF_RIGHT_MOUSE_BUTTON; - case NSOtherMouseDown: - case NSOtherMouseUp: - case NSOtherMouseDragged: + case NSEventTypeOtherMouseDown: + case NSEventTypeOtherMouseUp: + case NSEventTypeOtherMouseDragged: return EF_MIDDLE_MOUSE_BUTTON; default: break; @@ -269,7 +269,7 @@ uint32_t WindowsKeycodeFromNative(const PlatformEvent& native_event) { uint16_t TextFromNative(const PlatformEvent& native_event) { NSString* text = @""; - if ([native_event type] != NSFlagsChanged) + if ([native_event type] != NSEventTypeFlagsChanged) text = [native_event characters]; // These exceptions are based on web_input_event_builders_mac.mm: @@ -288,7 +288,7 @@ uint16_t TextFromNative(const PlatformEvent& native_event) { uint16_t UnmodifiedTextFromNative(const PlatformEvent& native_event) { NSString* text = @""; - if ([native_event type] != NSFlagsChanged) + if ([native_event type] != NSEventTypeFlagsChanged) text = [native_event charactersIgnoringModifiers]; // These exceptions are based on web_input_event_builders_mac.mm: diff --git a/chromium/ui/events/cocoa/events_mac_unittest.mm b/chromium/ui/events/cocoa/events_mac_unittest.mm index f317cb3165b..7e26a3b927b 100644 --- a/chromium/ui/events/cocoa/events_mac_unittest.mm +++ b/chromium/ui/events/cocoa/events_mac_unittest.mm @@ -148,59 +148,62 @@ NSArray* EventsMacTest::TrackpadScrollSequence(bool initial_rest, TEST_F(EventsMacTest, EventFlagsFromNative) { // Left click. - NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0); + NSEvent* left = + cocoa_test_event_utils::MouseEventWithType(NSEventTypeLeftMouseUp, 0); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left)); // Right click. - NSEvent* right = cocoa_test_event_utils::MouseEventWithType(NSRightMouseUp, - 0); + NSEvent* right = + cocoa_test_event_utils::MouseEventWithType(NSEventTypeRightMouseUp, 0); EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, EventFlagsFromNative(right)); // Middle click. - NSEvent* middle = cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, - 0); + NSEvent* middle = + cocoa_test_event_utils::MouseEventWithType(NSEventTypeOtherMouseUp, 0); EXPECT_EQ(EF_MIDDLE_MOUSE_BUTTON, EventFlagsFromNative(middle)); // Caps + Left NSEvent* caps = cocoa_test_event_utils::MouseEventWithType( - NSLeftMouseUp, NSAlphaShiftKeyMask); + NSEventTypeLeftMouseUp, NSEventModifierFlagCapsLock); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_CAPS_LOCK_ON, EventFlagsFromNative(caps)); // Shift + Left - NSEvent* shift = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, - NSShiftKeyMask); + NSEvent* shift = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeLeftMouseUp, NSEventModifierFlagShift); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_SHIFT_DOWN, EventFlagsFromNative(shift)); // Ctrl + Left. Note we map this to a right click on Mac and remove Control. - NSEvent* ctrl = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, - NSControlKeyMask); + NSEvent* ctrl = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeLeftMouseUp, NSEventModifierFlagControl); EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, EventFlagsFromNative(ctrl)); // Ctrl + Right. Remains a right click. NSEvent* ctrl_right = cocoa_test_event_utils::MouseEventWithType( - NSRightMouseUp, NSControlKeyMask); + NSEventTypeRightMouseUp, NSEventModifierFlagControl); EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON | EF_CONTROL_DOWN, EventFlagsFromNative(ctrl_right)); // Alt + Left - NSEvent* alt = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, - NSAlternateKeyMask); + NSEvent* alt = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeLeftMouseUp, NSEventModifierFlagOption); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_ALT_DOWN, EventFlagsFromNative(alt)); // Cmd + Left - NSEvent* cmd = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, - NSCommandKeyMask); + NSEvent* cmd = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeLeftMouseUp, NSEventModifierFlagCommand); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN, EventFlagsFromNative(cmd)); // Shift + Ctrl + Left. Also mapped to a right-click. Control removed. NSEvent* shiftctrl = cocoa_test_event_utils::MouseEventWithType( - NSLeftMouseUp, NSShiftKeyMask | NSControlKeyMask); + NSEventTypeLeftMouseUp, + NSEventModifierFlagShift | NSEventModifierFlagControl); EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON | EF_SHIFT_DOWN, EventFlagsFromNative(shiftctrl)); // Cmd + Alt + Right NSEvent* cmdalt = cocoa_test_event_utils::MouseEventWithType( - NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask); + NSEventTypeLeftMouseUp, + NSEventModifierFlagCommand | NSEventModifierFlagOption); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN, EventFlagsFromNative(cmdalt)); @@ -272,11 +275,12 @@ TEST_F(EventsMacTest, ButtonEvents) { // Test correct location when the window has a native titlebar. TEST_F(EventsMacTest, NativeTitlebarEventLocation) { gfx::Point location(5, 10); - NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | NSResizableWindowMask; + NSUInteger style_mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable; // First check that the window provided by ui::CocoaTest is how we think. - DCHECK_EQ(NSBorderlessWindowMask, [test_window() styleMask]); + DCHECK_EQ(NSWindowStyleMaskBorderless, [test_window() styleMask]); [test_window() setStyleMask:style_mask]; DCHECK_EQ(style_mask, [test_window() styleMask]); @@ -294,7 +298,7 @@ TEST_F(EventsMacTest, NativeTitlebarEventLocation) { NSRect content_rect = NSMakeRect(0, 0, 600, kTestHeight); NSRect frame_rect = [test_window() frameRectForContentRect:content_rect]; [test_window() setFrame:frame_rect display:YES]; - event = [NSEvent mouseEventWithType:NSLeftMouseDown + event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown location:NSMakePoint(0, 0) // Bottom-left corner. modifierFlags:0 timestamp:0 @@ -311,7 +315,7 @@ TEST_F(EventsMacTest, NativeTitlebarEventLocation) { // toolkit-views coordinate system. int height_change = NSHeight(frame_rect) - kTestHeight; EXPECT_GT(height_change, 0); - [test_window() setStyleMask:NSBorderlessWindowMask]; + [test_window() setStyleMask:NSWindowStyleMaskBorderless]; [test_window() setFrame:frame_rect display:YES]; EXPECT_EQ(gfx::Point(0, kTestHeight + height_change), gfx::ToFlooredPoint(ui::EventLocationFromNative(event))); @@ -332,20 +336,24 @@ TEST_F(EventsMacTest, NoWindowLocation) { // Testing for ui::EventTypeFromNative() not covered by ButtonEvents. TEST_F(EventsMacTest, EventTypeFromNative) { - NSEvent* event = cocoa_test_event_utils::KeyEventWithType(NSKeyDown, 0); + NSEvent* event = + cocoa_test_event_utils::KeyEventWithType(NSEventTypeKeyDown, 0); EXPECT_EQ(ui::ET_KEY_PRESSED, ui::EventTypeFromNative(event)); - event = cocoa_test_event_utils::KeyEventWithType(NSKeyUp, 0); + event = cocoa_test_event_utils::KeyEventWithType(NSEventTypeKeyUp, 0); EXPECT_EQ(ui::ET_KEY_RELEASED, ui::EventTypeFromNative(event)); - event = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseDragged, 0); + event = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeLeftMouseDragged, 0); EXPECT_EQ(ui::ET_MOUSE_DRAGGED, ui::EventTypeFromNative(event)); - event = cocoa_test_event_utils::MouseEventWithType(NSRightMouseDragged, 0); + event = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeRightMouseDragged, 0); EXPECT_EQ(ui::ET_MOUSE_DRAGGED, ui::EventTypeFromNative(event)); - event = cocoa_test_event_utils::MouseEventWithType(NSOtherMouseDragged, 0); + event = cocoa_test_event_utils::MouseEventWithType( + NSEventTypeOtherMouseDragged, 0); EXPECT_EQ(ui::ET_MOUSE_DRAGGED, ui::EventTypeFromNative(event)); - event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0); + event = cocoa_test_event_utils::MouseEventWithType(NSEventTypeMouseMoved, 0); EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(event)); event = cocoa_test_event_utils::EnterEvent(); @@ -516,7 +524,8 @@ TEST_F(EventsMacTest, TrackpadScrollThenFlick) { } } -// Check that NSFlagsChanged event is translated to key press or release event. +// Check that NSEventTypeFlagsChanged event is translated to key press or +// release event. TEST_F(EventsMacTest, HandleModifierOnlyKeyEvents) { struct { const char* description; @@ -525,24 +534,26 @@ TEST_F(EventsMacTest, HandleModifierOnlyKeyEvents) { EventType expected_type; KeyboardCode expected_key_code; } test_cases[] = { - {"CapsLock pressed", NSAlphaShiftKeyMask, kVK_CapsLock, ET_KEY_PRESSED, - VKEY_CAPITAL}, + {"CapsLock pressed", NSEventModifierFlagCapsLock, kVK_CapsLock, + ET_KEY_PRESSED, VKEY_CAPITAL}, {"CapsLock released", 0, kVK_CapsLock, ET_KEY_RELEASED, VKEY_CAPITAL}, - {"Shift pressed", NSShiftKeyMask, kVK_Shift, ET_KEY_PRESSED, VKEY_SHIFT}, + {"Shift pressed", NSEventModifierFlagShift, kVK_Shift, ET_KEY_PRESSED, + VKEY_SHIFT}, {"Shift released", 0, kVK_Shift, ET_KEY_RELEASED, VKEY_SHIFT}, - {"Control pressed", NSControlKeyMask, kVK_Control, ET_KEY_PRESSED, - VKEY_CONTROL}, + {"Control pressed", NSEventModifierFlagControl, kVK_Control, + ET_KEY_PRESSED, VKEY_CONTROL}, {"Control released", 0, kVK_Control, ET_KEY_RELEASED, VKEY_CONTROL}, - {"Option pressed", NSAlternateKeyMask, kVK_Option, ET_KEY_PRESSED, + {"Option pressed", NSEventModifierFlagOption, kVK_Option, ET_KEY_PRESSED, VKEY_MENU}, {"Option released", 0, kVK_Option, ET_KEY_RELEASED, VKEY_MENU}, - {"Command pressed", NSCommandKeyMask, kVK_Command, ET_KEY_PRESSED, - VKEY_LWIN}, + {"Command pressed", NSEventModifierFlagCommand, kVK_Command, + ET_KEY_PRESSED, VKEY_LWIN}, {"Command released", 0, kVK_Command, ET_KEY_RELEASED, VKEY_LWIN}, - {"Shift pressed with CapsLock on", NSShiftKeyMask | NSAlphaShiftKeyMask, - kVK_Shift, ET_KEY_PRESSED, VKEY_SHIFT}, - {"Shift released with CapsLock off", NSAlphaShiftKeyMask, kVK_Shift, - ET_KEY_RELEASED, VKEY_SHIFT}, + {"Shift pressed with CapsLock on", + NSEventModifierFlagShift | NSEventModifierFlagCapsLock, kVK_Shift, + ET_KEY_PRESSED, VKEY_SHIFT}, + {"Shift released with CapsLock off", NSEventModifierFlagCapsLock, + kVK_Shift, ET_KEY_RELEASED, VKEY_SHIFT}, }; for (const auto& test_case : test_cases) { SCOPED_TRACE(::testing::Message() << "While checking case: " diff --git a/chromium/ui/events/devices/device_data_manager.cc b/chromium/ui/events/devices/device_data_manager.cc index 03a5ccd6ece..16835a48ea0 100644 --- a/chromium/ui/events/devices/device_data_manager.cc +++ b/chromium/ui/events/devices/device_data_manager.cc @@ -28,7 +28,8 @@ namespace ui { namespace { bool InputDeviceEquals(const ui::InputDevice& a, const ui::InputDevice& b) { - return a.id == b.id && a.enabled == b.enabled; + return a.id == b.id && a.enabled == b.enabled && + a.suspected_imposter == b.suspected_imposter; } } // namespace diff --git a/chromium/ui/events/devices/input_device.h b/chromium/ui/events/devices/input_device.h index e3cc0019366..d61a4f2d525 100644 --- a/chromium/ui/events/devices/input_device.h +++ b/chromium/ui/events/devices/input_device.h @@ -57,6 +57,10 @@ struct EVENTS_DEVICES_EXPORT InputDevice { // If the device is enabled, and whether events should be dispatched to UI. bool enabled = true; + // If the device is suspected to be identifying as another device type + // (Currently only applies to Mice pretending to be keyboards). + bool suspected_imposter = false; + // The path to the input device in the sysfs filesystem. base::FilePath sys_path; diff --git a/chromium/ui/events/event_constants.h b/chromium/ui/events/event_constants.h index c16556cd8c1..8728f1200f2 100644 --- a/chromium/ui/events/event_constants.h +++ b/chromium/ui/events/event_constants.h @@ -13,91 +13,90 @@ namespace ui { // this list and/or reorder it, but make sure you also touch the various other // enums/constants that want to stay in sync with this. For example, // KeyEventFlags and MouseEventFlags should not overlap EventFlags. -enum EventFlags { - EF_NONE = 0, // Used to denote no flags explicitly - - // Universally applicable status bits. - EF_IS_SYNTHESIZED = 1 << 0, - - // Modifier key state. - EF_SHIFT_DOWN = 1 << 1, - EF_CONTROL_DOWN = 1 << 2, - EF_ALT_DOWN = 1 << 3, - EF_COMMAND_DOWN = 1 << 4, // GUI Key (e.g. Command on OS X - // keyboards, Search on Chromebook - // keyboards, Windows on MS-oriented - // keyboards) - EF_FUNCTION_DOWN = 1 << 5, // Function key. - EF_ALTGR_DOWN = 1 << 6, - EF_MOD3_DOWN = 1 << 7, - - // Other keyboard states. - EF_NUM_LOCK_ON = 1 << 8, - EF_CAPS_LOCK_ON = 1 << 9, - EF_SCROLL_LOCK_ON = 1 << 10, - - // Mouse buttons. - EF_LEFT_MOUSE_BUTTON = 1 << 11, - EF_MIDDLE_MOUSE_BUTTON = 1 << 12, - EF_RIGHT_MOUSE_BUTTON = 1 << 13, - EF_BACK_MOUSE_BUTTON = 1 << 14, - EF_FORWARD_MOUSE_BUTTON = 1 << 15, - EF_MOUSE_BUTTON = EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON | - EF_RIGHT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON | - EF_FORWARD_MOUSE_BUTTON, +using EventFlags = int; +// Used to denote no flags explicitly +constexpr EventFlags EF_NONE = 0; + +// Universally applicable status bits. +constexpr EventFlags EF_IS_SYNTHESIZED = 1 << 0; + +// Modifier key state. +constexpr EventFlags EF_SHIFT_DOWN = 1 << 1; +constexpr EventFlags EF_CONTROL_DOWN = 1 << 2; +constexpr EventFlags EF_ALT_DOWN = 1 << 3; +// GUI Key (e.g. Command on OS X keyboards, Search on Chromebook keyboards, +// Windows on MS-oriented keyboards) +constexpr EventFlags EF_COMMAND_DOWN = 1 << 4; +// Function key. +constexpr EventFlags EF_FUNCTION_DOWN = 1 << 5; +constexpr EventFlags EF_ALTGR_DOWN = 1 << 6; +constexpr EventFlags EF_MOD3_DOWN = 1 << 7; + +// Other keyboard states. +constexpr EventFlags EF_NUM_LOCK_ON = 1 << 8; +constexpr EventFlags EF_CAPS_LOCK_ON = 1 << 9; +constexpr EventFlags EF_SCROLL_LOCK_ON = 1 << 10; + +// Mouse buttons. +constexpr EventFlags EF_LEFT_MOUSE_BUTTON = 1 << 11; +constexpr EventFlags EF_MIDDLE_MOUSE_BUTTON = 1 << 12; +constexpr EventFlags EF_RIGHT_MOUSE_BUTTON = 1 << 13; +constexpr EventFlags EF_BACK_MOUSE_BUTTON = 1 << 14; +constexpr EventFlags EF_FORWARD_MOUSE_BUTTON = 1 << 15; +constexpr EventFlags EF_MOUSE_BUTTON = + EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON | + EF_BACK_MOUSE_BUTTON | EF_FORWARD_MOUSE_BUTTON; // An artificial value used to bridge platform differences. // Many commands on Mac as Cmd+Key are the counterparts of // Ctrl+Key on other platforms. #if BUILDFLAG(IS_APPLE) - EF_PLATFORM_ACCELERATOR = EF_COMMAND_DOWN, +constexpr EventFlags EF_PLATFORM_ACCELERATOR = EF_COMMAND_DOWN; #else - EF_PLATFORM_ACCELERATOR = EF_CONTROL_DOWN, +constexpr EventFlags EF_PLATFORM_ACCELERATOR = EF_CONTROL_DOWN; #endif -}; // Flags specific to key events. // WARNING: If you add or remove values make sure traits for serializing these // values are updated. -enum KeyEventFlags { - EF_IME_FABRICATED_KEY = 1 << 16, // Key event fabricated by the underlying - // IME without a user action. - // (Linux X11 only) - EF_IS_REPEAT = 1 << 17, - EF_FINAL = 1 << 18, // Do not remap; the event was created with - // the desired final values. - EF_IS_EXTENDED_KEY = 1 << 19, // Windows extended key (see WM_KEYDOWN doc) - EF_IS_STYLUS_BUTTON = 1 << 20, // Event was generated by a stylus button - EF_MAX_KEY_EVENT_FLAGS_VALUE = (1 << 21) - 1, -}; +using KeyEventFlags = EventFlags; +// Key event fabricated by the underlying IME without a user action. (Linux X11 +// only) +constexpr KeyEventFlags EF_IME_FABRICATED_KEY = 1 << 16; +constexpr KeyEventFlags EF_IS_REPEAT = 1 << 17; +// Do not remap; the event was created with the desired final values. +constexpr KeyEventFlags EF_FINAL = 1 << 18; +// Windows extended key (see WM_KEYDOWN doc) +constexpr KeyEventFlags EF_IS_EXTENDED_KEY = 1 << 19; +// Event was generated by a stylus button +constexpr KeyEventFlags EF_IS_STYLUS_BUTTON = 1 << 20; +constexpr KeyEventFlags EF_MAX_KEY_EVENT_FLAGS_VALUE = (1 << 21) - 1; // Flags specific to mouse events. -enum MouseEventFlags { - EF_IS_DOUBLE_CLICK = 1 << 16, - EF_IS_TRIPLE_CLICK = 1 << 17, - EF_IS_NON_CLIENT = 1 << 18, - EF_FROM_TOUCH = 1 << 19, // Indicates this mouse event is generated - // from an unconsumed touch/gesture event. - EF_TOUCH_ACCESSIBILITY = 1 << 20, // Indicates this event was generated from - // touch accessibility mode. - EF_CURSOR_HIDE = 1 << 21, // Indicates this mouse event is generated - // because the cursor was just hidden. This - // can be used to update hover state. - EF_PRECISION_SCROLLING_DELTA = // Indicates this mouse event is from high - 1 << 22, // precision touchpad and will come with a - // high precision delta. - EF_SCROLL_BY_PAGE = 1 << 23, // Indicates this mouse event is generated - // when users is requesting to scroll by - // pages. - EF_UNADJUSTED_MOUSE = 1 << 24, // Indicates this mouse event is unadjusted - // mouse events that has unadjusted - // movement delta, i.e. is from WM_INPUT - // on Windows. - EF_NOT_SUITABLE_FOR_MOUSE_WARPING = // Indicates this mouse event should not - 1 << 25, // trigger mouse warping (which moves the - // mouse to another display when the - // mouse hits the window boundaries). -}; +using MouseEventFlags = EventFlags; +constexpr MouseEventFlags EF_IS_DOUBLE_CLICK = 1 << 16; +constexpr MouseEventFlags EF_IS_TRIPLE_CLICK = 1 << 17; +constexpr MouseEventFlags EF_IS_NON_CLIENT = 1 << 18; +// Indicates this mouse event is generated from an unconsumed touch/gesture +// event. +constexpr MouseEventFlags EF_FROM_TOUCH = 1 << 19; +// Indicates this event was generated from touch accessibility mode. +constexpr MouseEventFlags EF_TOUCH_ACCESSIBILITY = 1 << 20; +// Indicates this mouse event is generated because the cursor was just hidden. +// This can be used to update hover state. +constexpr MouseEventFlags EF_CURSOR_HIDE = 1 << 21; +// Indicates this mouse event is from high precision touchpad and will come with +// a high precision delta. +constexpr MouseEventFlags EF_PRECISION_SCROLLING_DELTA = 1 << 22; +// Indicates this mouse event is generated when users is requesting to scroll by +// pages. +constexpr MouseEventFlags EF_SCROLL_BY_PAGE = 1 << 23; +// Indicates this mouse event is unadjusted mouse events that has unadjusted +// movement delta, i.e. is from WM_INPUT on Windows. +constexpr MouseEventFlags EF_UNADJUSTED_MOUSE = 1 << 24; +// Indicates this mouse event should not trigger mouse warping (which moves the +// mouse to another display when the mouse hits the window boundaries). +constexpr MouseEventFlags EF_NOT_SUITABLE_FOR_MOUSE_WARPING = 1 << 25; // Result of dispatching an event. enum EventResult { diff --git a/chromium/ui/events/event_switches.cc b/chromium/ui/events/event_switches.cc index 477aff13462..a28b059cbba 100644 --- a/chromium/ui/events/event_switches.cc +++ b/chromium/ui/events/event_switches.cc @@ -14,6 +14,13 @@ namespace switches { const char kCompensateForUnstablePinchZoom[] = "compensate-for-unstable-pinch-zoom"; +// Overrides touch slop distance for gesture detection. The touch slop distance +// is the maximum distance from the starting point of a touch sequence that a +// gesture can travel before it can no longer be considered a tap. Scroll +// gestures can only begin after this distance has been travelled. The switch +// value is a floating point number that is interpreted as a distance in pixels. +const char kTouchSlopDistance[] = "touch-slop-distance"; + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Tells chrome to interpret events from these devices as touch events. Only // available with XInput 2 (i.e. X server 1.8 or above). The id's of the diff --git a/chromium/ui/events/event_switches.h b/chromium/ui/events/event_switches.h index 158766a600e..08a45c39642 100644 --- a/chromium/ui/events/event_switches.h +++ b/chromium/ui/events/event_switches.h @@ -11,6 +11,7 @@ namespace switches { EVENTS_BASE_EXPORT extern const char kCompensateForUnstablePinchZoom[]; +EVENTS_BASE_EXPORT extern const char kTouchSlopDistance[]; #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) EVENTS_BASE_EXPORT extern const char kTouchDevices[]; diff --git a/chromium/ui/events/fuchsia/DIR_METADATA b/chromium/ui/events/fuchsia/DIR_METADATA index 5b3985ecc8b..7bba048979f 100644 --- a/chromium/ui/events/fuchsia/DIR_METADATA +++ b/chromium/ui/events/fuchsia/DIR_METADATA @@ -7,4 +7,3 @@ # https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto mixins: "//build/fuchsia/COMMON_METADATA" -os: FUCHSIA
\ No newline at end of file diff --git a/chromium/ui/events/fuchsia/fakes/DIR_METADATA b/chromium/ui/events/fuchsia/fakes/DIR_METADATA deleted file mode 100644 index 5b3985ecc8b..00000000000 --- a/chromium/ui/events/fuchsia/fakes/DIR_METADATA +++ /dev/null @@ -1,10 +0,0 @@ -# Metadata information for this directory. -# -# For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md -# -# For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto - -mixins: "//build/fuchsia/COMMON_METADATA" -os: FUCHSIA
\ No newline at end of file diff --git a/chromium/ui/events/gesture_detection/gesture_configuration_aura.cc b/chromium/ui/events/gesture_detection/gesture_configuration_aura.cc index 8fb64e014bd..5dabd73bc54 100644 --- a/chromium/ui/events/gesture_detection/gesture_configuration_aura.cc +++ b/chromium/ui/events/gesture_detection/gesture_configuration_aura.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/memory/singleton.h" +#include "base/strings/string_number_conversions.h" #include "build/chromeos_buildflags.h" #include "ui/events/event_switches.h" @@ -32,12 +33,21 @@ class GestureConfigurationAura : public GestureConfiguration { private: GestureConfigurationAura() : GestureConfiguration() { -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS) // On ChromeOS, use 6 which is derived from the android's default(8), // multiplied by base dpi ratio(0.75). See crbug.com/1083120 for more // details. set_max_touch_move_in_pixels_for_click(6); #endif + double touch_slop_distance; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kTouchSlopDistance) && + base::StringToDouble( + command_line->GetSwitchValueASCII(switches::kTouchSlopDistance), + &touch_slop_distance)) { + set_max_touch_move_in_pixels_for_click(touch_slop_distance); + } + set_double_tap_enabled(kDoubleTapAuraSupport); set_double_tap_timeout_in_ms(double_tap_timeout_in_ms()); set_gesture_begin_end_types_enabled(true); diff --git a/chromium/ui/events/gesture_detection/gesture_detector.cc b/chromium/ui/events/gesture_detection/gesture_detector.cc index cb9c65d1d8e..6c1d39bc1ff 100644 --- a/chromium/ui/events/gesture_detection/gesture_detector.cc +++ b/chromium/ui/events/gesture_detection/gesture_detector.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <algorithm> +#include <cmath> #include "base/cxx17_backports.h" #include "base/memory/raw_ptr.h" diff --git a/chromium/ui/events/gestures/blink/BUILD.gn b/chromium/ui/events/gestures/blink/BUILD.gn index 76de402ac88..d4373485671 100644 --- a/chromium/ui/events/gestures/blink/BUILD.gn +++ b/chromium/ui/events/gestures/blink/BUILD.gn @@ -10,7 +10,8 @@ source_set("blink") { "web_gesture_curve_impl.h", ] - if (is_chromecast && !is_android) { + # TODO(crbug.com/1314528): Should not need the is_android check. + if (is_castos && !is_android) { defines = [ "USE_MOBILE_FLING_CURVE" ] } diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl.cc b/chromium/ui/events/gestures/gesture_recognizer_impl.cc index e48876dd7bd..8d67ff2ee57 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl.cc +++ b/chromium/ui/events/gestures/gesture_recognizer_impl.cc @@ -62,7 +62,13 @@ bool RemoveValueFromMap(std::map<Key, T>* map, const Value& value) { GestureRecognizerImpl::GestureRecognizerImpl() = default; -GestureRecognizerImpl::~GestureRecognizerImpl() = default; +GestureRecognizerImpl::~GestureRecognizerImpl() { + // The gesture recognizer impl observes the gesture providers that are owned + // by `consumer_gesture_provider_`. Clear `consumer_gesture_provider_` + // explicitly so that the notifications sent by gesture providers during + // destruction are handled properly. + consumer_gesture_provider_.clear(); +} // Checks if this finger is already down, if so, returns the current target. // Otherwise, returns nullptr. diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl.h b/chromium/ui/events/gestures/gesture_recognizer_impl.h index d03d63889f4..fa757fbac6d 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl.h +++ b/chromium/ui/events/gestures/gesture_recognizer_impl.h @@ -21,6 +21,8 @@ namespace aura::test { FORWARD_DECLARE_TEST(GestureRecognizerTest, DestroyGestureProviderAuraBeforeAck); +FORWARD_DECLARE_TEST(GestureRecognizerTest, + ResetGestureRecognizerWithGestureProvider); } // namespace aura::test namespace ui { @@ -83,6 +85,8 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, private: FRIEND_TEST_ALL_PREFIXES(aura::test::GestureRecognizerTest, DestroyGestureProviderAuraBeforeAck); + FRIEND_TEST_ALL_PREFIXES(aura::test::GestureRecognizerTest, + ResetGestureRecognizerWithGestureProvider); // Sets up the target consumer for gestures based on the touch-event. void SetupTargets(const TouchEvent& event, GestureConsumer* consumer); diff --git a/chromium/ui/events/gestures/physics_based_fling_curve.cc b/chromium/ui/events/gestures/physics_based_fling_curve.cc index 448edd0b88c..eb437935398 100644 --- a/chromium/ui/events/gestures/physics_based_fling_curve.cc +++ b/chromium/ui/events/gestures/physics_based_fling_curve.cc @@ -7,6 +7,8 @@ #include <algorithm> #include <cmath> +#include "base/check.h" + namespace { // These constants are defined based on UX experiment. diff --git a/chromium/ui/events/keycodes/DEPS b/chromium/ui/events/keycodes/DEPS index d974f6c24be..4feb273fdab 100644 --- a/chromium/ui/events/keycodes/DEPS +++ b/chromium/ui/events/keycodes/DEPS @@ -7,8 +7,19 @@ include_rules = [ "-third_party", "-ui", - "+third_party/abseil-cpp/absl", "+ui/base/buildflags.h", # Doesn't bring in all of UI. "+ui/gfx/x", "+ui/events", + + # Abseil features must be allowlisted explicitly for now. See + # //styleguide/c++/c++11.html. Allowed features' headers will be listed + # explicitly here. + # Please keep this section in sync with //DEPS. + '-absl', + '-third_party/abseil-cpp', + '+third_party/abseil-cpp/absl/base/attributes.h', + "+third_party/abseil-cpp/absl/numeric/int128.h", + '+third_party/abseil-cpp/absl/types/optional.h', + '+third_party/abseil-cpp/absl/types/variant.h', + '+third_party/abseil-cpp/absl/utility/utility.h', ] diff --git a/chromium/ui/events/keycodes/dom/keycode_converter.cc b/chromium/ui/events/keycodes/dom/keycode_converter.cc index 88486fca8ba..d0f8c2abc91 100644 --- a/chromium/ui/events/keycodes/dom/keycode_converter.cc +++ b/chromium/ui/events/keycodes/dom/keycode_converter.cc @@ -338,7 +338,7 @@ DomKey KeycodeConverter::KeyStringToDomKey(base::StringPiece key) { // the key value is that character. const auto key_length = static_cast<int32_t>(key.length()); int32_t char_index = 0; - uint32_t character; + base_icu::UChar32 character; if (base::ReadUnicodeCharacter(key.data(), key_length, &char_index, &character) && ++char_index == key_length) { diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.h b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.h index a2246bc5460..0ee3b9a0093 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.h +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.h @@ -20,10 +20,10 @@ enum class DomCode; // including unit tests. But Mac uses a different set of virtual keycodes. // This function converts a windows virtual keycode into Mac's virtual key code // and corresponding unicode character. |flags| is the Cocoa modifiers mask -// such as NSControlKeyMask, NSShiftKeyMask, etc. +// such as NSEventModifierFlagControl, NSEventModifierFlagShift, etc. // When success, the corresponding Mac's virtual key code will be returned. // |keyboard_character| is the corresponding keyboard character, suitable for -// use in -[NSEvent characters]. If NSShiftKeyMask appears in |flags|, +// use in -[NSEvent characters]. If NSEventModifierFlagShift appears in |flags|, // |us_keyboard_shifted_character| is |keyboard_character| with a shift modifier // applied using a US keyboard layout (otherwise unmodified). // |us_keyboard_shifted_character| is suitable for -[NSEvent diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm index 0b53a810806..daf208147ae 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm @@ -221,9 +221,9 @@ const KeyCodeMap kKeyCodesMap[] = { bool IsKeypadOrNumericKeyEvent(NSEvent* event) { // Check that this is the type of event that has a keyCode. switch ([event type]) { - case NSKeyDown: - case NSKeyUp: - case NSFlagsChanged: + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + case NSEventTypeFlagsChanged: break; default: return false; @@ -533,15 +533,15 @@ UniChar MacKeycodeAndModifiersToCharacter(unsigned short mac_keycode, // Convert NSEvent modifiers to format UCKeyTranslate accepts. See docs // on UCKeyTranslate for more info. int unicode_modifiers = 0; - if (modifiers & NSShiftKeyMask) + if (modifiers & NSEventModifierFlagShift) unicode_modifiers |= shiftKey; - if (modifiers & NSAlphaShiftKeyMask) + if (modifiers & NSEventModifierFlagCapsLock) unicode_modifiers |= alphaLock; - // if (modifiers & NSControlKeyMask) + // if (modifiers & NSEventModifierFlagControl) // unicode_modifiers |= controlKey; - if (modifiers & NSAlternateKeyMask) + if (modifiers & NSEventModifierFlagOption) unicode_modifiers |= optionKey; - // if (modifiers & NSCommandKeyMask) + // if (modifiers & NSEventModifierFlagCommand) // unicode_modifiers |= cmdKey; UInt32 modifier_key_state = (unicode_modifiers >> 8) & 0xFF; @@ -570,7 +570,7 @@ int MacKeyCodeForWindowsKeyCode(KeyboardCode keycode, unichar* keyboard_character) { // In release code, |flags| is used to lookup accelerators, so logic to handle // caps lock properly isn't implemented. - DCHECK_EQ(0u, flags & NSAlphaShiftKeyMask); + DCHECK_EQ(0u, flags & NSEventModifierFlagCapsLock); KeyCodeMap from; from.keycode = keycode; @@ -592,7 +592,7 @@ int MacKeyCodeForWindowsKeyCode(KeyboardCode keycode, *us_keyboard_shifted_character = ptr->characterIgnoringAllModifiers; // Fill in |us_keyboard_shifted_character| according to flags. - if (flags & NSShiftKeyMask) { + if (flags & NSEventModifierFlagShift) { if (keycode >= VKEY_0 && keycode <= VKEY_9) { *us_keyboard_shifted_character = kShiftCharsForNumberKeys[keycode - VKEY_0]; @@ -787,7 +787,8 @@ KeyboardCode KeyboardCodeFromNSEvent(NSEvent* event) { // Numeric keys 0-9 should always return |keyCode| 0-9. // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Printable_keys_in_standard_position if (!IsKeypadOrNumericKeyEvent(event) && - ([event type] == NSKeyDown || [event type] == NSKeyUp)) { + ([event type] == NSEventTypeKeyDown || + [event type] == NSEventTypeKeyUp)) { // Handles Dvorak-QWERTY Cmd case. // https://github.com/WebKit/webkit/blob/4d41c98b1de467f5d2a8fcba84d7c5268f11b0cc/Source/WebCore/platform/mac/PlatformEventFactoryMac.mm#L329 NSString* characters = [event characters]; @@ -831,7 +832,7 @@ DomKey DomKeyFromNSEvent(NSEvent* event) { // Apply the lookup based on the character first since that has the // Keyboard layout and modifiers already applied; whereas the keyCode // doesn't. - if ([event type] == NSKeyDown || [event type] == NSKeyUp) { + if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp) { // Cannot use [event characters] to check whether it's a dead key, because // KeyUp event has the character form of the dead key in [event characters]. bool is_dead_key = false; @@ -860,8 +861,9 @@ DomKey DomKeyFromNSEvent(NSEvent* event) { // Unicode 'Other, Control' General Category. // https://w3c.github.io/uievents-key/#selecting-key-attribute-values bool unused_is_dead_key; - const int kAllowedModifiersMask = - NSShiftKeyMask | NSAlphaShiftKeyMask | NSAlternateKeyMask; + const int kAllowedModifiersMask = NSEventModifierFlagShift | + NSEventModifierFlagCapsLock | + NSEventModifierFlagOption; // MacKeycodeAndModifiersToCharacter() is efficient (around 6E-4 ms). dom_key_char = MacKeycodeAndModifiersToCharacter( [event keyCode], [event modifierFlags] & kAllowedModifiersMask, diff --git a/chromium/ui/events/mobile_scroller.cc b/chromium/ui/events/mobile_scroller.cc index 039eabd3104..701963bffd7 100644 --- a/chromium/ui/events/mobile_scroller.cc +++ b/chromium/ui/events/mobile_scroller.cc @@ -75,7 +75,7 @@ struct SplineConstants { float x_min = 0.0f; float y_min = 0.0f; for (int i = 0; i < NUM_SAMPLES; i++) { - const float alpha = static_cast<float>(i) / NUM_SAMPLES; + const float alpha = i / float{NUM_SAMPLES}; float x_max = 1.0f; float x, tx, coef; @@ -118,10 +118,10 @@ struct SplineConstants { float* velocity_coef) { *distance_coef = 1.f; *velocity_coef = 0.f; - const int index = static_cast<int>(NUM_SAMPLES * t); + const int index = base::ClampFloor(float{NUM_SAMPLES} * t); if (index < NUM_SAMPLES) { - const float t_inf = static_cast<float>(index) / NUM_SAMPLES; - const float t_sup = static_cast<float>(index + 1) / NUM_SAMPLES; + const float t_inf = index / float{NUM_SAMPLES}; + const float t_sup = (index + 1) / float{NUM_SAMPLES}; const float d_inf = spline_position_[index]; const float d_sup = spline_position_[index + 1]; *velocity_coef = (d_sup - d_inf) / (t_sup - t_inf); diff --git a/chromium/ui/events/ozone/device/device_event.cc b/chromium/ui/events/ozone/device/device_event.cc index e20079a79cf..7303803a08b 100644 --- a/chromium/ui/events/ozone/device/device_event.cc +++ b/chromium/ui/events/ozone/device/device_event.cc @@ -8,9 +8,13 @@ namespace ui { DeviceEvent::DeviceEvent(DeviceType type, ActionType action, - const base::FilePath& path) + const base::FilePath& path, + const PropertyMap& property_map) : device_type_(type), action_type_(action), - path_(path) {} + path_(path), + properties_(property_map) {} + +DeviceEvent::~DeviceEvent() = default; } // namespace ui diff --git a/chromium/ui/events/ozone/device/device_event.h b/chromium/ui/events/ozone/device/device_event.h index c2d6618943e..14f45f8e3de 100644 --- a/chromium/ui/events/ozone/device/device_event.h +++ b/chromium/ui/events/ozone/device/device_event.h @@ -6,9 +6,15 @@ #define UI_EVENTS_OZONE_DEVICE_DEVICE_EVENT_H_ #include "base/component_export.h" +#include "base/containers/flat_map.h" #include "base/files/file_path.h" namespace ui { +namespace { + +using PropertyMap = base::flat_map<std::string, std::string>; + +} // namespace class COMPONENT_EXPORT(EVENTS_OZONE) DeviceEvent { public: @@ -23,19 +29,25 @@ class COMPONENT_EXPORT(EVENTS_OZONE) DeviceEvent { CHANGE, }; - DeviceEvent(DeviceType type, ActionType action, const base::FilePath& path); + DeviceEvent(DeviceType type, + ActionType action, + const base::FilePath& path, + const PropertyMap& property_map = PropertyMap()); DeviceEvent(const DeviceEvent&) = delete; DeviceEvent& operator=(const DeviceEvent&) = delete; + ~DeviceEvent(); DeviceType device_type() const { return device_type_; } ActionType action_type() const { return action_type_; } base::FilePath path() const { return path_; } + const PropertyMap properties() const { return properties_; } private: DeviceType device_type_; ActionType action_type_; base::FilePath path_; + const PropertyMap properties_; }; } // namespace ui diff --git a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc index e3e2132d7c9..8deeea25a02 100644 --- a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc +++ b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc @@ -5,12 +5,15 @@ #include "ui/events/ozone/device/udev/device_manager_udev.h" #include <stddef.h> +#include <string> #include "base/logging.h" #include "base/observer_list.h" #include "base/strings/stringprintf.h" #include "base/task/current_thread.h" #include "base/trace_event/trace_event.h" +#include "device/udev_linux/udev.h" +#include "device/udev_linux/udev_loader.h" #include "ui/events/ozone/device/device_event.h" #include "ui/events/ozone/device/device_event_observer.h" @@ -159,10 +162,8 @@ void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) { std::unique_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage( udev_device* device) { const char* path = device::udev_device_get_devnode(device); - const char* action = device::udev_device_get_action(device); const char* subsystem = device::udev_device_get_property_value(device, "SUBSYSTEM"); - if (!path || !subsystem) return nullptr; @@ -177,6 +178,7 @@ std::unique_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage( else return nullptr; + const char* action = device::udev_device_get_action(device); DeviceEvent::ActionType action_type; if (!action || !strcmp(action, "add")) action_type = DeviceEvent::ADD; @@ -187,8 +189,19 @@ std::unique_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage( else return nullptr; + PropertyMap property_map; + udev_list_entry* property_list = + device::udev_device_get_properties_list_entry(device); + udev_list_entry* entry; + udev_list_entry_foreach(entry, property_list) { + const std::string key(device::udev_list_entry_get_name(entry)); + const std::string value( + device::udev_device_get_property_value(device, key.c_str())); + property_map.insert({key, value}); + } + return std::make_unique<DeviceEvent>(device_type, action_type, - base::FilePath(path)); + base::FilePath(path), property_map); } } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/BUILD.gn b/chromium/ui/events/ozone/evdev/BUILD.gn index b54cd8da6d9..ffb0749e886 100644 --- a/chromium/ui/events/ozone/evdev/BUILD.gn +++ b/chromium/ui/events/ozone/evdev/BUILD.gn @@ -65,12 +65,17 @@ component("evdev") { "input_device_factory_evdev.h", "input_device_factory_evdev_proxy.cc", "input_device_factory_evdev_proxy.h", + "input_device_opener.h", + "input_device_opener_evdev.cc", + "input_device_opener_evdev.h", "input_device_settings_evdev.cc", "input_device_settings_evdev.h", "input_injector_evdev.cc", "input_injector_evdev.h", "keyboard_evdev.cc", "keyboard_evdev.h", + "keyboard_imposter_checker_evdev.cc", + "keyboard_imposter_checker_evdev.h", "microphone_mute_switch_event_converter_evdev.cc", "microphone_mute_switch_event_converter_evdev.h", "mouse_button_map_evdev.cc", @@ -109,6 +114,8 @@ component("evdev") { "touch_filter/palm_detection_filter_factory.h", "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc", "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h", + "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.cc", + "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h", "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc", "touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h", "touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc", @@ -137,6 +144,8 @@ component("evdev") { if (is_chromeos_ash) { sources += [ + "fake_keyboard_heuristic_metrics.cc", + "fake_keyboard_heuristic_metrics.h", "numberpad_metrics.cc", "numberpad_metrics.h", ] @@ -193,8 +202,10 @@ component("evdev") { } source_set("event_device_info") { - public = [ "event_device_info.h" ] - sources = [ "event_device_info.cc" ] + sources = [ + "event_device_info.cc", + "event_device_info.h", + ] deps = [ "//base", @@ -237,6 +248,7 @@ source_set("unittests") { "event_factory_evdev_unittest.cc", "gamepad_event_converter_evdev_unittest.cc", "input_controller_evdev_unittest.cc", + "input_device_factory_evdev_unittest.cc", "input_injector_evdev_unittest.cc", "libgestures_glue/haptic_touchpad_handler.cc", "libgestures_glue/haptic_touchpad_handler.h", diff --git a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h index b4d8e1d6b34..0b4f26aaa61 100644 --- a/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h +++ b/chromium/ui/events/ozone/evdev/device_event_dispatcher_evdev.h @@ -196,7 +196,8 @@ class COMPONENT_EXPORT(EVDEV) DeviceEventDispatcherEvdev { // Device lifecycle events. virtual void DispatchKeyboardDevicesUpdated( - const std::vector<InputDevice>& devices) = 0; + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) = 0; virtual void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) = 0; virtual void DispatchMouseDevicesUpdated( @@ -209,7 +210,8 @@ class COMPONENT_EXPORT(EVDEV) DeviceEventDispatcherEvdev { virtual void DispatchDeviceListsComplete() = 0; virtual void DispatchStylusStateChanged(StylusState stylus_state) = 0; virtual void DispatchGamepadDevicesUpdated( - const std::vector<GamepadDevice>& devices) = 0; + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) = 0; virtual void DispatchUncategorizedDevicesUpdated( const std::vector<InputDevice>& devices) = 0; }; diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc index 84dd3e7864c..a5bf68079af 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc @@ -83,6 +83,14 @@ bool EventConverterEvdev::IsEnabled() const { return input_device_.enabled; } +void EventConverterEvdev::SetSuspectedImposter(bool is_suspected) { + input_device_.suspected_imposter = is_suspected; +} + +bool EventConverterEvdev::IsSuspectedImposter() const { + return input_device_.suspected_imposter; +} + void EventConverterEvdev::OnStopped() {} void EventConverterEvdev::OnEnabled() {} @@ -95,6 +103,10 @@ void EventConverterEvdev::OnFileCanWriteWithoutBlocking(int fd) { NOTREACHED(); } +KeyboardType EventConverterEvdev::GetKeyboardType() const { + return KeyboardType::NOT_KEYBOARD; +} + bool EventConverterEvdev::HasKeyboard() const { return false; } @@ -156,6 +168,11 @@ bool EventConverterEvdev::GetGamepadRumbleCapability() const { return false; } +std::vector<uint64_t> EventConverterEvdev::GetGamepadKeyBits() const { + NOTREACHED(); + return std::vector<uint64_t>(); +} + void EventConverterEvdev::PlayVibrationEffect(uint8_t amplitude, uint16_t duration_millis) { NOTREACHED(); @@ -225,6 +242,13 @@ void EventConverterEvdev::SetReportStylusStateCallback( void EventConverterEvdev::SetGetLatestStylusStateCallback( const GetLatestStylusStateCallback& callback) {} +void EventConverterEvdev::SetReceivedValidInputCallback( + ReceivedValidInputCallback callback) {} + +std::vector<uint64_t> EventConverterEvdev::GetKeyboardKeyBits() const { + return std::vector<uint64_t>(); +} + base::TimeTicks EventConverterEvdev::TimeTicksFromInputEvent( const input_event& event) { base::TimeTicks timestamp = diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.h b/chromium/ui/events/ozone/evdev/event_converter_evdev.h index 7d31dc8feac..8c9ee759ba7 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev.h +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.h @@ -19,6 +19,7 @@ #include "ui/events/devices/haptic_touchpad_effects.h" #include "ui/events/devices/input_device.h" #include "ui/events/devices/stylus_state.h" +#include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/evdev/event_dispatch_callback.h" #include "ui/events/ozone/evdev/touch_evdev_types.h" #include "ui/gfx/geometry/size.h" @@ -41,6 +42,9 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev using GetLatestStylusStateCallback = base::RepeatingCallback<void(const InProgressStylusState**)>; + using ReceivedValidInputCallback = + base::RepeatingCallback<void(const EventConverterEvdev* converter)>; + EventConverterEvdev(int fd, const base::FilePath& path, int id, @@ -80,6 +84,12 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev bool IsEnabled() const; + // Flag this device as being suspected for identifying as a device that it is + // not. + void SetSuspectedImposter(bool is_suspected); + + bool IsSuspectedImposter() const; + // Cleanup after we stop reading events (release buttons, etc). virtual void OnStopped(); @@ -92,6 +102,10 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev // Dump recent events into a file. virtual void DumpTouchEventLog(const char* filename); + // Returns value corresponding to keyboard status (No Keyboard, Keyboard in + // Blocklist, ect.). + virtual KeyboardType GetKeyboardType() const; + // Returns true if the converter is used for a keyboard device. virtual bool HasKeyboard() const; @@ -146,6 +160,9 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev // Returns whether the gamepad device supports rumble type force feedback. virtual bool GetGamepadRumbleCapability() const; + // Returns supported key bits of the gamepad. + virtual std::vector<uint64_t> GetGamepadKeyBits() const; + // Sets which keyboard keys should be processed. If |enable_filter| is // false, all keys are allowed and |allowed_keys| is ignored. virtual void SetKeyFilter(bool enable_filter, @@ -169,6 +186,13 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdev virtual void SetGetLatestStylusStateCallback( const GetLatestStylusStateCallback& callback); + // Set callback to trigger keyboard device update. + virtual void SetReceivedValidInputCallback( + ReceivedValidInputCallback callback); + + // Returns supported key bits of the keyboard. + virtual std::vector<uint64_t> GetKeyboardKeyBits() const; + // Helper to generate a base::TimeTicks from an input_event's time static base::TimeTicks TimeTicksFromInputEvent(const input_event& event); diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc index c2df3597633..60835c6af80 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc @@ -53,7 +53,7 @@ EventConverterEvdevImpl::EventConverterEvdevImpl( devinfo.product_id(), devinfo.version()), input_device_fd_(std::move(fd)), - has_keyboard_(devinfo.HasKeyboard()), + keyboard_type_(devinfo.GetKeyboardType()), has_touchpad_(devinfo.HasTouchpad()), has_numberpad_(devinfo.HasNumberpad()), has_stylus_switch_(devinfo.HasStylusSwitch()), @@ -65,6 +65,14 @@ EventConverterEvdevImpl::EventConverterEvdevImpl( if (has_numberpad_) NumberpadMetricsRecorder::GetInstance()->AddDevice(input_device_); #endif + // Converts unsigned long to uint64_t. + const auto key_bits = devinfo.GetKeyBits(); + key_bits_.resize(key_bits.size()); + for (int i = 0; i < KEY_CNT; i++) { + if (EvdevBitIsSet(key_bits.data(), i)) { + EvdevSetUint64Bit(key_bits_.data(), i); + } + } } EventConverterEvdevImpl::~EventConverterEvdevImpl() { @@ -96,8 +104,12 @@ void EventConverterEvdevImpl::OnFileCanReadWithoutBlocking(int fd) { ProcessEvents(inputs, read_size / sizeof(*inputs)); } +KeyboardType EventConverterEvdevImpl::GetKeyboardType() const { + return keyboard_type_; +} + bool EventConverterEvdevImpl::HasKeyboard() const { - return has_keyboard_; + return keyboard_type_ == KeyboardType::VALID_KEYBOARD; } bool EventConverterEvdevImpl::HasTouchpad() const { @@ -137,6 +149,10 @@ void EventConverterEvdevImpl::OnDisabled() { ReleaseMouseButtons(); } +std::vector<uint64_t> EventConverterEvdevImpl::GetKeyboardKeyBits() const { + return key_bits_; +} + ui::StylusState EventConverterEvdevImpl::GetStylusSwitchState() { if (!HasStylusSwitch()) { return ui::StylusState::REMOVED; @@ -234,6 +250,20 @@ void EventConverterEvdevImpl::OnKeyChange(unsigned int key, GenerateKeyMetrics(key, down); + // Checks for a key press that could only have occurred from a non-imposter + // keyboard. Disables Imposter flag and triggers a callback which will update + // the dispatched list of keyboards with this new information. + if (key_state_.count() == 1 && ((key >= KEY_1 && key <= KEY_EQUAL) || + (key >= KEY_Q && key <= KEY_RIGHTBRACE) || + (key <= KEY_A && key >= KEY_APOSTROPHE) || + (key >= KEY_BACKSLASH && key <= KEY_SLASH))) { + bool was_suspected = IsSuspectedImposter(); + SetSuspectedImposter(false); + if (was_suspected && received_valid_input_callback_) { + received_valid_input_callback_.Run(this); + } + } + dispatcher_->DispatchKeyEvent( KeyEventParams(input_device_.id, ui::EF_NONE, key, last_scan_code_, down, false /* suppress_auto_repeat */, timestamp)); @@ -295,6 +325,11 @@ void EventConverterEvdevImpl::OnButtonChange(int code, timestamp)); } +void EventConverterEvdevImpl::SetReceivedValidInputCallback( + ReceivedValidInputCallback callback) { + received_valid_input_callback_ = callback; +} + void EventConverterEvdevImpl::FlushEvents(const input_event& input) { if (!cursor_ || (x_offset_ == 0 && y_offset_ == 0)) return; diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h index e1604a132fc..12c4e37f64e 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h @@ -45,6 +45,7 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdevImpl // EventConverterEvdev: void OnFileCanReadWithoutBlocking(int fd) override; + KeyboardType GetKeyboardType() const override; bool HasKeyboard() const override; bool HasTouchpad() const override; bool HasCapsLockLed() const override; @@ -53,6 +54,7 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdevImpl void SetKeyFilter(bool enable_filter, std::vector<DomCode> allowed_keys) override; void OnDisabled() override; + std::vector<uint64_t> GetKeyboardKeyBits() const override; void ProcessEvents(const struct input_event* inputs, int count); @@ -75,11 +77,18 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdevImpl // non-axis-aligned movement properly. void FlushEvents(const input_event& input); + // Sets Callback to enable refreshing keyboard list after + // a valid input is initially received + void SetReceivedValidInputCallback( + ReceivedValidInputCallback callback) override; + // Input device file descriptor. const base::ScopedFD input_device_fd_; + // KeyboardType + KeyboardType keyboard_type_; + // Input modalities for this device. - bool has_keyboard_; bool has_touchpad_; bool has_numberpad_; bool has_stylus_switch_; @@ -116,6 +125,12 @@ class COMPONENT_EXPORT(EVDEV) EventConverterEvdevImpl // Callbacks for dispatching events. DeviceEventDispatcherEvdev* const dispatcher_; + + // Callback to update keyboard devices when valid input is received. + ReceivedValidInputCallback received_valid_input_callback_; + + // Supported keyboard key bits. + std::vector<uint64_t> key_bits_; }; } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc index ec9a2142ea9..0ceac82f6cd 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc @@ -18,6 +18,7 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/ozone/device/device_manager.h" #include "ui/events/ozone/evdev/event_converter_test_util.h" +#include "ui/events/ozone/evdev/event_device_test_util.h" #include "ui/events/ozone/evdev/event_factory_evdev.h" #include "ui/events/ozone/evdev/keyboard_evdev.h" #include "ui/events/ozone/evdev/testing/fake_cursor_delegate_evdev.h" @@ -31,12 +32,13 @@ const char kTestDevicePath[] = "/dev/input/test-device"; class MockEventConverterEvdevImpl : public EventConverterEvdevImpl { public: MockEventConverterEvdevImpl(base::ScopedFD fd, + const ui::EventDeviceInfo& info, CursorDelegateEvdev* cursor, DeviceEventDispatcherEvdev* dispatcher) : EventConverterEvdevImpl(std::move(fd), base::FilePath(kTestDevicePath), 1, - EventDeviceInfo(), + info, cursor, dispatcher) { SetEnabled(true); @@ -65,7 +67,16 @@ class EventConverterEvdevImplTest : public testing::Test { delete; // Overridden from testing::Test: - void SetUp() override { + void SetUp() override { SetUpDevice(ui::EventDeviceInfo()); } + + void TearDown() override { + device_.reset(); + cursor_.reset(); + events_out_.reset(); + test_clock_.reset(); + } + + void SetUpDevice(const ui::EventDeviceInfo& info) { // Set up pipe to satisfy message pump (unused). int evdev_io[2]; if (pipe(evdev_io)) @@ -84,18 +95,10 @@ class EventConverterEvdevImplTest : public testing::Test { dispatcher_ = ui::CreateDeviceEventDispatcherEvdevForTest(event_factory_.get()); device_ = std::make_unique<ui::MockEventConverterEvdevImpl>( - std::move(events_in), cursor_.get(), dispatcher_.get()); - + std::move(events_in), info, cursor_.get(), dispatcher_.get()); test_clock_ = std::make_unique<ui::test::ScopedEventTestTickClock>(); } - void TearDown() override { - device_.reset(); - cursor_.reset(); - events_out_.reset(); - test_clock_.reset(); - } - ui::FakeCursorDelegateEvdev* cursor() { return cursor_.get(); } ui::MockEventConverterEvdevImpl* device() { return device_.get(); } @@ -148,6 +151,21 @@ class EventConverterEvdevImplTest : public testing::Test { base::ScopedFD events_out_; }; +// Test fixture which defers device set up, tests need to call SetUpDevice(). +class DeferDeviceSetUpEventConverterEvdevImplTest + : public EventConverterEvdevImplTest { + public: + DeferDeviceSetUpEventConverterEvdevImplTest() = default; + + DeferDeviceSetUpEventConverterEvdevImplTest( + const DeferDeviceSetUpEventConverterEvdevImplTest&) = delete; + DeferDeviceSetUpEventConverterEvdevImplTest& operator=( + const DeferDeviceSetUpEventConverterEvdevImplTest&) = delete; + + // Overridden from EventConverterEvdevImplTest: + void SetUp() override {} +}; + TEST_F(EventConverterEvdevImplTest, KeyPress) { ui::MockEventConverterEvdevImpl* dev = device(); @@ -750,3 +768,17 @@ TEST_F(EventConverterEvdevImplTest, ShouldSwapMouseButtonsFromUserPreference) { EXPECT_EQ(ui::ET_MOUSE_RELEASED, event->type()); EXPECT_EQ(true, event->IsLeftMouseButton()); } + +TEST_F(DeferDeviceSetUpEventConverterEvdevImplTest, KeyboardHasKeys) { + ui::EventDeviceInfo devinfo; + CapabilitiesToDeviceInfo(ui::kLogitechKeyboardK120, &devinfo); + SetUpDevice(devinfo); + ui::MockEventConverterEvdevImpl* dev = device(); + + const std::vector<uint64_t> key_bits = dev->GetKeyboardKeyBits(); + + // KEY_A should be supported. + EXPECT_TRUE(ui::EvdevBitUint64IsSet(key_bits.data(), 30)); + // BTN_A shouldn't be supported. + EXPECT_FALSE(ui::EvdevBitUint64IsSet(key_bits.data(), 305)); +} diff --git a/chromium/ui/events/ozone/evdev/event_converter_test_util.cc b/chromium/ui/events/ozone/evdev/event_converter_test_util.cc index 2ccc123cdb7..604e07d386e 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_test_util.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_test_util.cc @@ -67,8 +67,10 @@ class TestDeviceEventDispatcherEvdev : public DeviceEventDispatcherEvdev { } void DispatchKeyboardDevicesUpdated( - const std::vector<InputDevice>& devices) override { - event_factory_evdev_->DispatchKeyboardDevicesUpdated(devices); + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override { + event_factory_evdev_->DispatchKeyboardDevicesUpdated(devices, + key_bits_mapping); } void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) override { @@ -101,8 +103,10 @@ class TestDeviceEventDispatcherEvdev : public DeviceEventDispatcherEvdev { } void DispatchGamepadDevicesUpdated( - const std::vector<GamepadDevice>& devices) override { - event_factory_evdev_->DispatchGamepadDevicesUpdated(devices); + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override { + event_factory_evdev_->DispatchGamepadDevicesUpdated(devices, + key_bits_mapping); } private: diff --git a/chromium/ui/events/ozone/evdev/event_device_info.cc b/chromium/ui/events/ozone/evdev/event_device_info.cc index 96dca421c65..d728dba3a3e 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info.cc +++ b/chromium/ui/events/ozone/evdev/event_device_info.cc @@ -8,6 +8,7 @@ #include <cstring> +#include "base/containers/fixed_flat_set.h" #include "base/feature_list.h" #include "base/files/file_path.h" #include "base/logging.h" @@ -33,10 +34,20 @@ namespace { // unusual. const size_t kMaximumDeviceNameLength = 256; -constexpr struct { +struct DeviceId { uint16_t vendor; uint16_t product_id; -} kKeyboardBlocklist[] = { + constexpr bool operator<(const DeviceId& other) const { + return vendor == other.vendor ? product_id < other.product_id + : vendor < other.vendor; + } +}; + +constexpr auto kKeyboardBlocklist = base::MakeFixedFlatSet<DeviceId>({ + {0x0111, 0x1830}, // SteelSeries Rival 3 Wireless (Bluetooth) + {0x0111, 0x183a}, // SteelSeries Aerox 3 Wireless (Bluetooth) + {0x0111, 0x1854}, // SteelSeries Aerox 5 Wireless (Bluetooth) + {0x0111, 0x185a}, // SteelSeries Aerox 9 Wireless (Bluetooth) {0x03f0, 0xa407}, // HP X4000 Wireless Mouse {0x045e, 0x0745}, // Microsoft Wireless Mobile Mouse 6000 {0x045e, 0x0821}, // Microsoft Surface Precision Mouse @@ -70,47 +81,184 @@ constexpr struct { {0x046d, 0xc093}, // Logitech M500s {0x046d, 0xc534}, // Logitech M170 {0x046d, 0xc53e}, // Logitech Spotlight Presentation Remote (USB dongle) + {0x04b4, 0x121f}, // SteelSeries Ikari {0x056e, 0x0134}, // Elecom Enelo IR LED Mouse 350 {0x056e, 0x0141}, // Elecom EPRIM Blue LED 5 button mouse 228 {0x056e, 0x0159}, // Elecom Blue LED Mouse 203 - {0x05e0, 0x1200}, // Zebra LS2208 barcode scanner + {0x05e0, 0x1200}, // Symbol Technologies / Zebra LS2208 barcode scanner {0x0951, 0x1727}, // HyperX Pulsefire Haste Gaming Mouse {0x0c45, 0x7403}, // RDing FootSwitch1F1 - {0x1038, 0x1369}, // SteelSeries Sensei RAW Frost Blue - {0x1038, 0x1824}, // SteelSeries Rival 3 Wired - {0x1038, 0x1830}, // SteelSeries Rival 3 Wireless (USB dongle) + {0x1038, 0x0470}, // SteelSeries Reaper Edge + {0x1038, 0x0471}, // SteelSeries Rival Rescuer + {0x1038, 0x0472}, // SteelSeries Rival 150 net café + {0x1038, 0x0473}, // SteelSeries Sensei SP + {0x1038, 0x0475}, // SteelSeries Rival 160 retail + {0x1038, 0x0777}, // SteelSeries MO3 + {0x1038, 0x1300}, // SteelSeries Kinzu + {0x1038, 0x1310}, // SteelSeries MO4 + {0x1038, 0x1311}, // SteelSeries MO4v2 + {0x1038, 0x1320}, // SteelSeries MO3v2 + {0x1038, 0x1330}, // SteelSeries MO5 + {0x1038, 0x1332}, // SteelSeries MO5 (Dongle) + {0x1038, 0x1356}, // SteelSeries Sensei Dark EDG + {0x1038, 0x1358}, // SteelSeries Sensei Dark Snake + {0x1038, 0x135a}, // SteelSeries Sensei Dell Alienware + {0x1038, 0x1360}, // SteelSeries Xai + {0x1038, 0x1361}, // SteelSeries Sensei + {0x1038, 0x1362}, // SteelSeries Sensei Raw Diablo III Mouse + {0x1038, 0x1364}, // SteelSeries Kana + {0x1038, 0x1366}, // SteelSeries Kinzu 2 + {0x1038, 0x1369}, // SteelSeries Sensei Raw + {0x1038, 0x136b}, // SteelSeries MLG Sensei + {0x1038, 0x136d}, // SteelSeries Sensei Raw: GW2 + {0x1038, 0x136f}, // SteelSeries Sensei Raw: CoD + {0x1038, 0x1370}, // SteelSeries Sensei Master + {0x1038, 0x1372}, // SteelSeries Sensei Master (Hub Controller) + {0x1038, 0x1373}, // SteelSeries Sensei Master (Flash Drive Controller) + {0x1038, 0x1374}, // SteelSeries Kana: CS:GO + {0x1038, 0x1376}, // SteelSeries Kana: DOTA + {0x1038, 0x1378}, // SteelSeries Kinzu v2.1 + {0x1038, 0x137a}, // SteelSeries Kana Pro + {0x1038, 0x137c}, // SteelSeries Wireless Sensei + {0x1038, 0x137e}, // SteelSeries Wireless Sensei (Charge Stand) + {0x1038, 0x1380}, // SteelSeries World of Tank mouse + {0x1038, 0x1382}, // SteelSeries Sims Mouse + {0x1038, 0x1384}, // SteelSeries Rival + {0x1038, 0x1386}, // SteelSeries SIMS 4 mouse + {0x1038, 0x1388}, // SteelSeries Kinzu v3 Mouse + {0x1038, 0x1390}, // SteelSeries Sensei Raw Heroes of the Storm Mouse + {0x1038, 0x1392}, // SteelSeries Rival DOTA 2 + {0x1038, 0x1394}, // SteelSeries Rival 300 CS:GO Fade Edition + {0x1038, 0x1396}, // SteelSeries Rival 300 Gaming Mouse + {0x1038, 0x1700}, // SteelSeries Rival 700 + {0x1038, 0x1701}, // SteelSeries Rival 700 (Basic) + {0x1038, 0x1702}, // SteelSeries Rival 100 Gaming Mouse (ELM4 - A) + {0x1038, 0x1704}, // SteelSeries Rival 95 PC BANG + {0x1038, 0x1705}, // SteelSeries Rival 100 For Alienware + {0x1038, 0x1706}, // SteelSeries Rival 95 + {0x1038, 0x1707}, // SteelSeries Rival 95 MSI edition + {0x1038, 0x1708}, // SteelSeries Rival 100 Gaming Mouse (PC Bang) + {0x1038, 0x1709}, // SteelSeries Rival 50 MSI edition + {0x1038, 0x170a}, // SteelSeries Rival 100 Dell China + {0x1038, 0x170b}, // SteelSeries Rival 100 DOTA 2 Mouse + {0x1038, 0x170c}, // SteelSeries Rival 100 DOTA 2 Mouse (Lenovo) + {0x1038, 0x170d}, // SteelSeries Rival 100 World of Tanks Mouse + {0x1038, 0x170e}, // SteelSeries Rival 500 (MBM) + {0x1038, 0x170f}, // SteelSeries Rival 500 (Basic) + {0x1038, 0x1710}, // SteelSeries Rival 300 Gaming Mouse + {0x1038, 0x1712}, // SteelSeries Rival 300 Fallout 4 Gaming Mouse + {0x1038, 0x1714}, // SteelSeries Rival 300 Predator Gaming Mouse + {0x1038, 0x1716}, // SteelSeries Rival 300 CS:GO Fade Edition + {0x1038, 0x1718}, // SteelSeries Rival 300 HP Omen + {0x1038, 0x171a}, // SteelSeries Rival 300 CS:GO Hyperbeast Edition + {0x1038, 0x171c}, // SteelSeries Rival 300 Evil Geniuses Edition + {0x1038, 0x171e}, // SteelSeries Rival 310 CSGO Howl + {0x1038, 0x171f}, // SteelSeries Rival 310 CSGO Howl (Basic) + {0x1038, 0x1720}, // SteelSeries Rival 310 + {0x1038, 0x1721}, // SteelSeries Rival 310 (Basic) + {0x1038, 0x1722}, // SteelSeries Sensei 310 + {0x1038, 0x1723}, // SteelSeries Sensei 310 (Basic) + {0x1038, 0x1724}, // SteelSeries Rival 600 + {0x1038, 0x1725}, // SteelSeries Rival 600 (Basic) + {0x1038, 0x1726}, // SteelSeries Rival 650 Wireless + {0x1038, 0x1727}, // SteelSeries Rival 650 Wireless (Basic) + {0x1038, 0x1729}, // SteelSeries Rival 110 Gaming Mouse + {0x1038, 0x172b}, // SteelSeries Rival 650 Wireless (Wired) + {0x1038, 0x172c}, // SteelSeries Rival 650 Wireless (Basic for wired) + {0x1038, 0x172d}, // SteelSeries Rival 110 (Dell) + {0x1038, 0x172e}, // SteelSeries Rival 600 Dota 2 + {0x1038, 0x172f}, // SteelSeries Rival 600 Dota 2 (Basic) + {0x1038, 0x1730}, // SteelSeries Rival 710 + {0x1038, 0x1731}, // SteelSeries Rival 710 (Basic) + {0x1038, 0x1736}, // SteelSeries Rival 310 PUBG Edition + {0x1038, 0x1737}, // SteelSeries Rival 310 PUBG Edition (Basic) + {0x1038, 0x1800}, // SteelSeries Sensei Raw Optical + {0x1038, 0x1801}, // SteelSeries Sensei Raw Optical (Basic) + {0x1038, 0x1802}, // SteelSeries Sensei Raw Optical RGB + {0x1038, 0x1803}, // SteelSeries Sensei Raw Optical RGB (Basic) + {0x1038, 0x1810}, // SteelSeries Rival 300S + {0x1038, 0x1812}, // SteelSeries Rival 300S Dell Silver + {0x1038, 0x1814}, // SteelSeries Rival 105 (Kana v3) Gaming Mouse + {0x1038, 0x1816}, // SteelSeries Rival 106 Gaming Mouse + {0x1038, 0x1818}, // SteelSeries Rival 610 Wireless + {0x1038, 0x1819}, // SteelSeries Rival 610 Wireless (Basic) + {0x1038, 0x181a}, // SteelSeries Rival 610 Wireless (Wired) + {0x1038, 0x181b}, // SteelSeries Rival 610 Wireless (Basic for wired) + {0x1038, 0x181c}, // SteelSeries Rival 310 Wireless + {0x1038, 0x181d}, // SteelSeries Rival 310 Wireless (Basic) + {0x1038, 0x181e}, // SteelSeries Rival 310 Wireless (Wired) + {0x1038, 0x181f}, // SteelSeries Rival 310 Wireless (Basic for wired) + {0x1038, 0x1820}, // SteelSeries Rival 610 + {0x1038, 0x1821}, // SteelSeries Rival 610 (Basic) + {0x1038, 0x1822}, // SteelSeries Sensei 610 + {0x1038, 0x1823}, // SteelSeries Sensei 610 (Basic) + {0x1038, 0x1824}, // SteelSeries Rival 3 + {0x1038, 0x1826}, // SteelSeries Sensei Raw Optical RGB v2 + {0x1038, 0x1827}, // SteelSeries Sensei Raw Optical RGB v2 (Basic) + {0x1038, 0x1828}, // SteelSeries Radical Wireless + {0x1038, 0x1829}, // SteelSeries Radical Wireless (Basic) + {0x1038, 0x182a}, // SteelSeries Prime Rainbow Six Edition + {0x1038, 0x182b}, // SteelSeries Prime Rainbow Six Edition (Basic) + {0x1038, 0x182c}, // SteelSeries Prime+ + {0x1038, 0x182d}, // SteelSeries Prime+ (Basic) + {0x1038, 0x182e}, // SteelSeries Prime + {0x1038, 0x182f}, // SteelSeries Prime (Basic) + {0x1038, 0x1830}, // SteelSeries Rival 3 Wireless + {0x1038, 0x1831}, // SteelSeries Rival 3 Wireless (Basic) + {0x1038, 0x1832}, // SteelSeries Sensei Ten + {0x1038, 0x1833}, // SteelSeries Sensei Ten (Basic) + {0x1038, 0x1834}, // SteelSeries Sensei Ten Neon Rider Edition + {0x1038, 0x1835}, // SteelSeries Sensei Ten Neon Rider Edition (Basic) + {0x1038, 0x1836}, // SteelSeries Aerox 3 + {0x1038, 0x1838}, // SteelSeries Aerox 3 Wireless (Dongle) + {0x1038, 0x1839}, // SteelSeries Aerox 3 Wireless (Basic for dongle) + {0x1038, 0x183a}, // SteelSeries Aerox 3 Wireless (Wired) + {0x1038, 0x183b}, // SteelSeries Aerox 3 Wireless (Basic for wired) + {0x1038, 0x183c}, // SteelSeries Rival 5 + {0x1038, 0x183d}, // SteelSeries Rival 5 (Basic) + {0x1038, 0x183e}, // SteelSeries Rival 5 Destiny 2 + {0x1038, 0x183f}, // SteelSeries Rival 5 Destiny 2 (Basic) + {0x1038, 0x1840}, // SteelSeries Prime Wireless (Dongle) + {0x1038, 0x1841}, // SteelSeries Prime Wireless (Basic for dongle) + {0x1038, 0x1842}, // SteelSeries Prime Wireless (Wired) + {0x1038, 0x1843}, // SteelSeries Prime Wireless (Basic for wired) + {0x1038, 0x1848}, // SteelSeries Prime Mini Wireless (Dongle) + {0x1038, 0x184a}, // SteelSeries Prime Mini Wireless (Wired) + {0x1038, 0x184c}, // SteelSeries Rival 3 (NVIDIA Support - Standard) + {0x1038, 0x184d}, // SteelSeries Prime Mini + {0x1038, 0x1850}, // SteelSeries Aerox 5 + {0x1038, 0x1852}, // SteelSeries Aerox 5 Wireless (Dongle) + {0x1038, 0x1854}, // SteelSeries Aerox 5 Wireless (Wired) + {0x1038, 0x1856}, // SteelSeries Prime CS:GO Neo Noir + {0x1038, 0x1858}, // SteelSeries Aerox 9 WL (Dongle) + {0x1038, 0x185a}, // SteelSeries Aerox 9 WL (Wired) {0x1050, 0x0010}, // Yubico.com Yubikey {0x1050, 0x0407}, // Yubico.com Yubikey 4 OTP+U2F+CCID {0x1532, 0x007a}, // Razer Viper Ultimate (Wired) {0x1532, 0x007b}, // Razer Viper Ultimate (Wireless) + {0x17ef, 0x60be}, // Lenovo Legion M200 RGB Gaming Mouse {0x17ef, 0x60e4}, // Lenovo Legion M300 RGB Gaming Mouse - // (which can send keystrokes as part of macros.) {0x17ef, 0x6123}, // Lenovo USB-C Wired Compact Mouse {0x1b1c, 0x1b7a}, // Corsair Sabre Pro Champion Gaming Mouse {0x1b1c, 0x1b94}, // Corsair Katar Pro Wireless (USB dongle) {0x1bae, 0x1b1c}, // Corsair Katar Pro Wireless (Bluetooth) {0x1bcf, 0x08a0}, // Kensington Pro Fit Full-size + {0x2201, 0x0100}, // AirTurn PEDpro {0x256c, 0x006d}, // Huion HS64 {0x258a, 0x1007}, // Acer Cestus 330 {0x2717, 0x003b}, // Xiaomi Mi Portable Mouse {0x28bd, 0x0914}, // XP-Pen Star G640 {0x28bd, 0x091f}, // XP-Pen Artist 12 Pro {0x28bd, 0x0928}, // XP-Pen Deco mini7W -}; +}); -constexpr struct { - uint16_t vendor; - uint16_t product_id; -} kStylusButtonDevices[] = { +constexpr DeviceId kStylusButtonDevices[] = { {0x413c, 0x81d5}, // Dell Active Pen PN579X }; // Certain devices need to be forced to use libinput in place of // evdev/libgestures -constexpr struct { - uint16_t vendor; - uint16_t product_id; -} kForceLibinputlist[] = { +constexpr DeviceId kForceLibinputlist[] = { {0x0002, 0x000e}, // HP Stream 14 touchpad {0x044e, 0x120a}, // Dell Latitude 3480 touchpad }; @@ -244,47 +392,46 @@ bool IsDenylistedAbsoluteMouseDevice(const input_id& id) { } // namespace -EventDeviceInfo::EventDeviceInfo() { - memset(ev_bits_, 0, sizeof(ev_bits_)); - memset(key_bits_, 0, sizeof(key_bits_)); - memset(rel_bits_, 0, sizeof(rel_bits_)); - memset(abs_bits_, 0, sizeof(abs_bits_)); - memset(msc_bits_, 0, sizeof(msc_bits_)); - memset(sw_bits_, 0, sizeof(sw_bits_)); - memset(led_bits_, 0, sizeof(led_bits_)); - memset(ff_bits_, 0, sizeof(ff_bits_)); - memset(prop_bits_, 0, sizeof(prop_bits_)); - memset(abs_info_, 0, sizeof(abs_info_)); -} +EventDeviceInfo::EventDeviceInfo() + : ev_bits_{}, + key_bits_{}, + rel_bits_{}, + abs_bits_{}, + msc_bits_{}, + sw_bits_{}, + led_bits_{}, + prop_bits_{}, + ff_bits_{}, + abs_info_{} {} EventDeviceInfo::~EventDeviceInfo() {} bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) { - if (!GetEventBits(fd, path, 0, ev_bits_, sizeof(ev_bits_))) + if (!GetEventBits(fd, path, 0, ev_bits_.data(), sizeof(ev_bits_))) return false; - if (!GetEventBits(fd, path, EV_KEY, key_bits_, sizeof(key_bits_))) + if (!GetEventBits(fd, path, EV_KEY, key_bits_.data(), sizeof(key_bits_))) return false; - if (!GetEventBits(fd, path, EV_REL, rel_bits_, sizeof(rel_bits_))) + if (!GetEventBits(fd, path, EV_REL, rel_bits_.data(), sizeof(rel_bits_))) return false; - if (!GetEventBits(fd, path, EV_ABS, abs_bits_, sizeof(abs_bits_))) + if (!GetEventBits(fd, path, EV_ABS, abs_bits_.data(), sizeof(abs_bits_))) return false; - if (!GetEventBits(fd, path, EV_MSC, msc_bits_, sizeof(msc_bits_))) + if (!GetEventBits(fd, path, EV_MSC, msc_bits_.data(), sizeof(msc_bits_))) return false; - if (!GetEventBits(fd, path, EV_SW, sw_bits_, sizeof(sw_bits_))) + if (!GetEventBits(fd, path, EV_SW, sw_bits_.data(), sizeof(sw_bits_))) return false; - if (!GetEventBits(fd, path, EV_LED, led_bits_, sizeof(led_bits_))) + if (!GetEventBits(fd, path, EV_LED, led_bits_.data(), sizeof(led_bits_))) return false; - if (!GetEventBits(fd, path, EV_FF, ff_bits_, sizeof(ff_bits_))) + if (!GetEventBits(fd, path, EV_FF, ff_bits_.data(), sizeof(ff_bits_))) return false; - if (!GetPropBits(fd, path, prop_bits_, sizeof(prop_bits_))) + if (!GetPropBits(fd, path, prop_bits_.data(), sizeof(prop_bits_))) return false; for (unsigned int i = 0; i < ABS_CNT; ++i) @@ -326,39 +473,39 @@ bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) { } void EventDeviceInfo::SetEventTypes(const unsigned long* ev_bits, size_t len) { - AssignBitset(ev_bits, len, ev_bits_, std::size(ev_bits_)); + AssignBitset(ev_bits, len, ev_bits_.data(), ev_bits_.size()); } void EventDeviceInfo::SetKeyEvents(const unsigned long* key_bits, size_t len) { - AssignBitset(key_bits, len, key_bits_, std::size(key_bits_)); + AssignBitset(key_bits, len, key_bits_.data(), key_bits_.size()); } void EventDeviceInfo::SetRelEvents(const unsigned long* rel_bits, size_t len) { - AssignBitset(rel_bits, len, rel_bits_, std::size(rel_bits_)); + AssignBitset(rel_bits, len, rel_bits_.data(), rel_bits_.size()); } void EventDeviceInfo::SetAbsEvents(const unsigned long* abs_bits, size_t len) { - AssignBitset(abs_bits, len, abs_bits_, std::size(abs_bits_)); + AssignBitset(abs_bits, len, abs_bits_.data(), abs_bits_.size()); } void EventDeviceInfo::SetMscEvents(const unsigned long* msc_bits, size_t len) { - AssignBitset(msc_bits, len, msc_bits_, std::size(msc_bits_)); + AssignBitset(msc_bits, len, msc_bits_.data(), msc_bits_.size()); } void EventDeviceInfo::SetSwEvents(const unsigned long* sw_bits, size_t len) { - AssignBitset(sw_bits, len, sw_bits_, std::size(sw_bits_)); + AssignBitset(sw_bits, len, sw_bits_.data(), sw_bits_.size()); } void EventDeviceInfo::SetLedEvents(const unsigned long* led_bits, size_t len) { - AssignBitset(led_bits, len, led_bits_, std::size(led_bits_)); + AssignBitset(led_bits, len, led_bits_.data(), led_bits_.size()); } void EventDeviceInfo::SetFfEvents(const unsigned long* ff_bits, size_t len) { - AssignBitset(ff_bits, len, ff_bits_, std::size(ff_bits_)); + AssignBitset(ff_bits, len, ff_bits_.data(), ff_bits_.size()); } void EventDeviceInfo::SetProps(const unsigned long* prop_bits, size_t len) { - AssignBitset(prop_bits, len, prop_bits_, std::size(prop_bits_)); + AssignBitset(prop_bits, len, prop_bits_.data(), prop_bits_.size()); } void EventDeviceInfo::SetAbsInfo(unsigned int code, @@ -401,55 +548,55 @@ void EventDeviceInfo::SetName(const std::string& name) { bool EventDeviceInfo::HasEventType(unsigned int type) const { if (type > EV_MAX) return false; - return EvdevBitIsSet(ev_bits_, type); + return EvdevBitIsSet(ev_bits_.data(), type); } bool EventDeviceInfo::HasKeyEvent(unsigned int code) const { if (code > KEY_MAX) return false; - return EvdevBitIsSet(key_bits_, code); + return EvdevBitIsSet(key_bits_.data(), code); } bool EventDeviceInfo::HasRelEvent(unsigned int code) const { if (code > REL_MAX) return false; - return EvdevBitIsSet(rel_bits_, code); + return EvdevBitIsSet(rel_bits_.data(), code); } bool EventDeviceInfo::HasAbsEvent(unsigned int code) const { if (code > ABS_MAX) return false; - return EvdevBitIsSet(abs_bits_, code); + return EvdevBitIsSet(abs_bits_.data(), code); } bool EventDeviceInfo::HasMscEvent(unsigned int code) const { if (code > MSC_MAX) return false; - return EvdevBitIsSet(msc_bits_, code); + return EvdevBitIsSet(msc_bits_.data(), code); } bool EventDeviceInfo::HasSwEvent(unsigned int code) const { if (code > SW_MAX) return false; - return EvdevBitIsSet(sw_bits_, code); + return EvdevBitIsSet(sw_bits_.data(), code); } bool EventDeviceInfo::HasLedEvent(unsigned int code) const { if (code > LED_MAX) return false; - return EvdevBitIsSet(led_bits_, code); + return EvdevBitIsSet(led_bits_.data(), code); } bool EventDeviceInfo::HasFfEvent(unsigned int code) const { if (code > FF_MAX) return false; - return EvdevBitIsSet(ff_bits_, code); + return EvdevBitIsSet(ff_bits_.data(), code); } bool EventDeviceInfo::HasProp(unsigned int code) const { if (code > INPUT_PROP_MAX) return false; - return EvdevBitIsSet(prop_bits_, code); + return EvdevBitIsSet(prop_bits_.data(), code); } int32_t EventDeviceInfo::GetAbsMinimum(unsigned int code) const { @@ -590,30 +737,29 @@ bool EventDeviceInfo::UseLibinput() const { } bool IsInKeyboardBlockList(input_id input_id_) { - for (const auto& blocklist_id : kKeyboardBlocklist) { - if (input_id_.vendor == blocklist_id.vendor && - input_id_.product == blocklist_id.product_id) - return true; - } - - return false; + DeviceId id = {input_id_.vendor, input_id_.product}; + return kKeyboardBlocklist.contains(id); } bool EventDeviceInfo::HasKeyboard() const { + return GetKeyboardType() == KeyboardType::VALID_KEYBOARD; +} + +KeyboardType EventDeviceInfo::GetKeyboardType() const { if (!HasEventType(EV_KEY)) - return false; + return KeyboardType::NOT_KEYBOARD; if (IsInKeyboardBlockList(input_id_)) - return false; + return KeyboardType::IN_BLOCKLIST; if (IsStylusButtonDevice()) - return false; + return KeyboardType::STYLUS_BUTTON_DEVICE; // Check first 31 keys: If we have all of them, consider it a full // keyboard. This is exactly what udev does for ID_INPUT_KEYBOARD. for (int key = KEY_ESC; key <= KEY_D; ++key) if (!HasKeyEvent(key)) - return false; + return KeyboardType::NOT_KEYBOARD; - return true; + return KeyboardType::VALID_KEYBOARD; } bool EventDeviceInfo::HasMouse() const { @@ -624,6 +770,7 @@ bool EventDeviceInfo::HasMouse() const { input_id_.product == kSteelSeriesStratusDuoBluetoothProductId) { return false; } + return HasRelXY() && !HasProp(INPUT_PROP_POINTING_STICK); } diff --git a/chromium/ui/events/ozone/evdev/event_device_info.h b/chromium/ui/events/ozone/evdev/event_device_info.h index 88cc90dc311..090d247ac9a 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info.h +++ b/chromium/ui/events/ozone/evdev/event_device_info.h @@ -10,6 +10,7 @@ #include <stddef.h> #include <stdint.h> +#include <array> #include <string> #include <vector> @@ -44,6 +45,14 @@ enum COMPONENT_EXPORT(EVDEV) EventDeviceType { DT_ALL, }; +// Status of Keyboard Device +enum COMPONENT_EXPORT(EVDEV) KeyboardType { + NOT_KEYBOARD, + IN_BLOCKLIST, + STYLUS_BUTTON_DEVICE, + VALID_KEYBOARD, +}; + // Device information for Linux input devices // // This stores and queries information about input devices; in @@ -136,6 +145,9 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo { // Has stylus EV_KEY events. bool HasStylus() const; + // Determine status of keyboard device (No keyboard, In blocklist, ect.). + KeyboardType GetKeyboardType() const; + // Determine whether there's a keyboard on this device. bool HasKeyboard() const; @@ -191,6 +203,10 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo { // The device type (internal or external.) InputDeviceType device_type() const { return device_type_; } + std::array<unsigned long, EVDEV_BITS_TO_LONGS(KEY_CNT)> GetKeyBits() const { + return key_bits_; + } + // Determines InputDeviceType from device identification. static InputDeviceType GetInputDeviceTypeFromId(input_id id); @@ -209,17 +225,17 @@ class COMPONENT_EXPORT(EVDEV) EventDeviceInfo { // do not tell us what the axes mean. LegacyAbsoluteDeviceType ProbeLegacyAbsoluteDevice() const; - unsigned long ev_bits_[EVDEV_BITS_TO_LONGS(EV_CNT)]; - unsigned long key_bits_[EVDEV_BITS_TO_LONGS(KEY_CNT)]; - unsigned long rel_bits_[EVDEV_BITS_TO_LONGS(REL_CNT)]; - unsigned long abs_bits_[EVDEV_BITS_TO_LONGS(ABS_CNT)]; - unsigned long msc_bits_[EVDEV_BITS_TO_LONGS(MSC_CNT)]; - unsigned long sw_bits_[EVDEV_BITS_TO_LONGS(SW_CNT)]; - unsigned long led_bits_[EVDEV_BITS_TO_LONGS(LED_CNT)]; - unsigned long prop_bits_[EVDEV_BITS_TO_LONGS(INPUT_PROP_CNT)]; - unsigned long ff_bits_[EVDEV_BITS_TO_LONGS(FF_CNT)]; - - struct input_absinfo abs_info_[ABS_CNT]; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(EV_CNT)> ev_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(KEY_CNT)> key_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(REL_CNT)> rel_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(ABS_CNT)> abs_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(MSC_CNT)> msc_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(SW_CNT)> sw_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(LED_CNT)> led_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(INPUT_PROP_CNT)> prop_bits_; + std::array<unsigned long, EVDEV_BITS_TO_LONGS(FF_CNT)> ff_bits_; + + std::array<input_absinfo, ABS_CNT> abs_info_; // Store the values for the multi-touch properties for each slot. std::vector<int32_t> slot_values_[EVDEV_ABS_MT_COUNT]; diff --git a/chromium/ui/events/ozone/evdev/event_device_info_unittest.cc b/chromium/ui/events/ozone/evdev/event_device_info_unittest.cc index 41b2e7a8a11..d10eaa0b003 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info_unittest.cc +++ b/chromium/ui/events/ozone/evdev/event_device_info_unittest.cc @@ -500,4 +500,22 @@ TEST(EventDeviceInfoTest, HPProBook6560bTouchpad) { EXPECT_EQ(ui::InputDeviceType::INPUT_DEVICE_INTERNAL, devinfo.device_type()); } +TEST(EventDeviceInfoTest, DeviceOnKeyboardBlocklist) { + EventDeviceInfo devinfo; + EXPECT_TRUE(CapabilitiesToDeviceInfo(kSymbolTechBarcodeScanner, &devinfo)); + + EXPECT_FALSE(devinfo.HasKeyboard()); + EXPECT_FALSE(devinfo.HasMouse()); + EXPECT_FALSE(devinfo.HasPointingStick()); + EXPECT_FALSE(devinfo.HasTouchpad()); + EXPECT_FALSE(devinfo.HasHapticTouchpad()); + EXPECT_FALSE(devinfo.HasTouchscreen()); + EXPECT_FALSE(devinfo.HasTablet()); + EXPECT_FALSE(devinfo.HasGamepad()); + EXPECT_FALSE(devinfo.IsStylusButtonDevice()); + EXPECT_FALSE(devinfo.HasStylusSwitch()); + EXPECT_FALSE(devinfo.HasValidMTAbsXY()); + EXPECT_FALSE(devinfo.IsSemiMultitouch()); +} + } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/event_device_test_util.cc b/chromium/ui/events/ozone/evdev/event_device_test_util.cc index e67b23c4683..e470b7e8977 100644 --- a/chromium/ui/events/ozone/evdev/event_device_test_util.cc +++ b/chromium/ui/events/ozone/evdev/event_device_test_util.cc @@ -1465,6 +1465,33 @@ const DeviceCapabilities kRedrixTouchpad = { std::size(kRedrixTouchpadAxes), }; +const DeviceCapabilities kSymbolTechBarcodeScanner = { + /* path */ + "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.0/0003:05E0:1200.0009/" + "input/input27/event19", + /* name */ "Symbol Technologies, Inc, 2008 Symbol Bar Code Scanner", + /* phys */ "usb-0000:00:14.0-6/input0", + /* uniq */ "S/N:D698905D483448AD806AAC8CD63B8B4A Rev:PAACES00-002-R033", + /* bustype */ "0003", + /* vendor */ "05e0", + /* product */ "1200", + /* version */ "0110", + /* prop */ "0", + /* ev */ "120013", + /* key */ + "1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe", + /* rel */ "0", + /* abs */ "0", + /* msc */ "10", + /* sw */ "0", + /* led */ "1f", + /* ff */ "0", + /* abs_axis */ nullptr, + /* abs_axis_count */ 0, + /* kbd_function_row_physmap */ "", + /* kbd_top_row_layout */ "", +}; + // NB: Please use the capture_device_capabilities.py script to add more // test data here. This will help ensure the data matches what the kernel // reports for a real device and is entered correctly. diff --git a/chromium/ui/events/ozone/evdev/event_device_test_util.h b/chromium/ui/events/ozone/evdev/event_device_test_util.h index e22a8b27311..2a99db93d4f 100644 --- a/chromium/ui/events/ozone/evdev/event_device_test_util.h +++ b/chromium/ui/events/ozone/evdev/event_device_test_util.h @@ -115,6 +115,7 @@ extern const DeviceCapabilities kMicrosoftBluetoothNumberPad; extern const DeviceCapabilities kDellLatitudeE6510Touchpad; extern const DeviceCapabilities kHPProBook6560bTouchpad; extern const DeviceCapabilities kJinlonKeyboard; +extern const DeviceCapabilities kSymbolTechBarcodeScanner; } // namespace ui #endif // UI_EVENTS_OZONE_EVDEV_EVENT_DEVICE_TEST_UTIL_H_ diff --git a/chromium/ui/events/ozone/evdev/event_device_util.h b/chromium/ui/events/ozone/evdev/event_device_util.h index 15000e350c9..4205b7dcdb6 100644 --- a/chromium/ui/events/ozone/evdev/event_device_util.h +++ b/chromium/ui/events/ozone/evdev/event_device_util.h @@ -10,16 +10,27 @@ namespace ui { #define EVDEV_LONG_BITS (CHAR_BIT * sizeof(long)) +#define EVDEV_INT64_BITS (CHAR_BIT * sizeof(int64_t)) #define EVDEV_BITS_TO_LONGS(x) (((x) + EVDEV_LONG_BITS - 1) / EVDEV_LONG_BITS) +#define EVDEV_BITS_TO_INT64(x) (((x) + EVDEV_INT64_BITS - 1) / EVDEV_INT64_BITS) static inline bool EvdevBitIsSet(const unsigned long* data, int bit) { return data[bit / EVDEV_LONG_BITS] & (1UL << (bit % EVDEV_LONG_BITS)); } +static inline bool EvdevBitUint64IsSet(const uint64_t* data, int bit) { + return data[bit / EVDEV_INT64_BITS] & + ((uint64_t)1 << (bit % EVDEV_INT64_BITS)); +} + static inline void EvdevSetBit(unsigned long* data, int bit) { data[bit / EVDEV_LONG_BITS] |= (1UL << (bit % EVDEV_LONG_BITS)); } +static inline void EvdevSetUint64Bit(uint64_t* data, int bit) { + data[bit / EVDEV_INT64_BITS] |= ((uint64_t)1 << (bit % EVDEV_INT64_BITS)); +} + } // namespace ui #endif // UI_EVENTS_OZONE_EVDEV_EVENT_DEVICE_UTIL_H_ diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc index bf457a11c34..671439ee09d 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc @@ -94,11 +94,13 @@ class ProxyDeviceEventDispatcher : public DeviceEventDispatcherEvdev { } void DispatchKeyboardDevicesUpdated( - const std::vector<InputDevice>& devices) override { + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override { ui_thread_runner_->PostTask( FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchKeyboardDevicesUpdated, - event_factory_evdev_, devices)); + event_factory_evdev_, devices, + std::move(key_bits_mapping))); } void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) override { @@ -146,11 +148,13 @@ class ProxyDeviceEventDispatcher : public DeviceEventDispatcherEvdev { } void DispatchGamepadDevicesUpdated( - const std::vector<GamepadDevice>& devices) override { + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override { ui_thread_runner_->PostTask( FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchGamepadDevicesUpdated, - event_factory_evdev_, devices)); + event_factory_evdev_, devices, + std::move(key_bits_mapping))); } void DispatchUncategorizedDevicesUpdated( @@ -394,8 +398,10 @@ void EventFactoryEvdev::DispatchUiEvent(Event* event) { } void EventFactoryEvdev::DispatchKeyboardDevicesUpdated( - const std::vector<InputDevice>& devices) { + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) { TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchKeyboardDevicesUpdated"); + input_controller_.SetKeyboardKeyBitsMapping(std::move(key_bits_mapping)); DeviceHotplugEventObserver* observer = DeviceDataManager::GetInstance(); observer->OnKeyboardDevicesUpdated(devices); } @@ -459,8 +465,10 @@ void EventFactoryEvdev::DispatchUncategorizedDevicesUpdated( } void EventFactoryEvdev::DispatchGamepadDevicesUpdated( - const std::vector<GamepadDevice>& devices) { + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) { TRACE_EVENT0("evdev", "EventFactoryEvdev::DispatchGamepadDevicesUpdated"); + input_controller_.SetGamepadKeyBitsMapping(std::move(key_bits_mapping)); gamepad_provider_->DispatchGamepadDevicesUpdated(devices); } diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev.h b/chromium/ui/events/ozone/evdev/event_factory_evdev.h index a7fa692fb3d..f3980da6f92 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.h +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.h @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/component_export.h" +#include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" #include "base/task/task_runner.h" #include "ui/events/event_modifiers.h" @@ -77,7 +78,9 @@ class COMPONENT_EXPORT(EVDEV) EventFactoryEvdev : public DeviceEventObserver, void DispatchTouchEvent(const TouchEventParams& params); // Device lifecycle events. - void DispatchKeyboardDevicesUpdated(const std::vector<InputDevice>& devices); + void DispatchKeyboardDevicesUpdated( + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping); void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices); void DispatchMouseDevicesUpdated(const std::vector<InputDevice>& devices, @@ -94,7 +97,9 @@ class COMPONENT_EXPORT(EVDEV) EventFactoryEvdev : public DeviceEventObserver, // Gamepad event and gamepad device event. These events are dispatched to // GamepadObserver through GamepadProviderOzone. void DispatchGamepadEvent(const GamepadEvent& event); - void DispatchGamepadDevicesUpdated(const std::vector<GamepadDevice>& devices); + void DispatchGamepadDevicesUpdated( + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping); protected: // DeviceEventObserver overrides: diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/event_factory_evdev_unittest.cc index f9c13253bb0..eac55378d50 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev_unittest.cc @@ -53,7 +53,7 @@ TEST_F(EventFactoryEvdevTest, OrdinalImpliesFlag) { EXPECT_CALL(event_observer_, WillProcessEvent) .WillOnce([](const PlatformEvent& platform_event) { MouseEvent mouse_event(platform_event); - EXPECT_TRUE(mouse_event.flags() & MouseEventFlags::EF_UNADJUSTED_MOUSE); + EXPECT_TRUE(mouse_event.flags() & EF_UNADJUSTED_MOUSE); EXPECT_EQ(mouse_event.movement(), gfx::Vector2dF(2.67, 3.14)); }); @@ -71,8 +71,7 @@ TEST_F(EventFactoryEvdevTest, NoOrdinalImpliesNoFlag) { EXPECT_CALL(event_observer_, WillProcessEvent) .WillOnce([](const PlatformEvent& platform_event) { MouseEvent mouse_event(platform_event); - EXPECT_FALSE(mouse_event.flags() & - MouseEventFlags::EF_UNADJUSTED_MOUSE); + EXPECT_FALSE(mouse_event.flags() & EF_UNADJUSTED_MOUSE); }); event_factory_.DispatchMouseMoveEvent( diff --git a/chromium/ui/events/ozone/evdev/event_thread_evdev.cc b/chromium/ui/events/ozone/evdev/event_thread_evdev.cc index 14f6ed14f36..591b071c4cb 100644 --- a/chromium/ui/events/ozone/evdev/event_thread_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_thread_evdev.cc @@ -18,6 +18,7 @@ #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" #include "ui/events/ozone/evdev/input_device_factory_evdev.h" #include "ui/events/ozone/evdev/input_device_factory_evdev_proxy.h" +#include "ui/events/ozone/evdev/input_device_opener_evdev.h" namespace ui { @@ -39,7 +40,8 @@ class EvdevThread : public base::Thread { void Init() override { TRACE_EVENT0("evdev", "EvdevThread::Init"); input_device_factory_ = - new InputDeviceFactoryEvdev(std::move(dispatcher_), cursor_); + new InputDeviceFactoryEvdev(std::move(dispatcher_), cursor_, + std::make_unique<InputDeviceOpenerEvdev>()); std::unique_ptr<InputDeviceFactoryEvdevProxy> proxy( new InputDeviceFactoryEvdevProxy(base::ThreadTaskRunnerHandle::Get(), diff --git a/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc b/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc new file mode 100644 index 00000000000..5bef3c9b59a --- /dev/null +++ b/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc @@ -0,0 +1,28 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h" + +#include "ui/events/ozone/features.h" + +namespace ui { + +FakeKeyboardHeuristicMetrics::FakeKeyboardHeuristicMetrics() + : feature_usage_metrics_("FakeKeyboardHeuristic", this) {} + +FakeKeyboardHeuristicMetrics::~FakeKeyboardHeuristicMetrics() = default; + +bool FakeKeyboardHeuristicMetrics::IsEligible() const { + return true; +} + +bool FakeKeyboardHeuristicMetrics::IsEnabled() const { + return base::FeatureList::IsEnabled(kEnableFakeKeyboardHeuristic); +} + +void FakeKeyboardHeuristicMetrics::RecordUsage(bool success) { + feature_usage_metrics_.RecordUsage(success); +} + +} // namespace ui
\ No newline at end of file diff --git a/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h b/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h new file mode 100644 index 00000000000..b07112a9a71 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h @@ -0,0 +1,26 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_ +#define UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_ + +#include "chromeos/components/feature_usage/feature_usage_metrics.h" + +namespace ui { +class FakeKeyboardHeuristicMetrics + : public feature_usage::FeatureUsageMetrics::Delegate { + public: + explicit FakeKeyboardHeuristicMetrics(); + ~FakeKeyboardHeuristicMetrics() override; + + bool IsEligible() const override; + bool IsEnabled() const override; + void RecordUsage(bool success); + + private: + feature_usage::FeatureUsageMetrics feature_usage_metrics_; +}; +} // namespace ui + +#endif // UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_ diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc index 46129d638b0..fec1d4fa814 100644 --- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc @@ -58,6 +58,14 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev( } } supports_rumble_ = devinfo.SupportsRumble(); + // Converts unsigned long to uint64_t. + const auto key_bits = devinfo.GetKeyBits(); + key_bits_.resize(key_bits.size()); + for (int i = 0; i < KEY_CNT; i++) { + if (EvdevBitIsSet(key_bits.data(), i)) { + EvdevSetUint64Bit(key_bits_.data(), i); + } + } } GamepadEventConverterEvdev::~GamepadEventConverterEvdev() { @@ -199,6 +207,10 @@ bool GamepadEventConverterEvdev::GetGamepadRumbleCapability() const { return supports_rumble_; } +std::vector<uint64_t> GamepadEventConverterEvdev::GetGamepadKeyBits() const { + return key_bits_; +} + void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) { base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev); // We may have missed Gamepad releases. Reset everything. diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h index 09f905350d0..9270c332e74 100644 --- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h +++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.h @@ -43,6 +43,7 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev void OnDisabled() override; std::vector<ui::GamepadDevice::Axis> GetGamepadAxes() const override; bool GetGamepadRumbleCapability() const override; + std::vector<uint64_t> GetGamepadKeyBits() const override; // This function processes one input_event from evdev. void ProcessEvent(const struct input_event& input); @@ -124,6 +125,8 @@ class COMPONENT_EXPORT(EVDEV) GamepadEventConverterEvdev // The effect id is needed to keep track of effects that are uploaded and // stored in the gamepad device. int effect_id_; + + std::vector<uint64_t> key_bits_; }; } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc index c0c77021b42..d0c7117804a 100644 --- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc @@ -23,6 +23,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/base_event_utils.h" +#include "ui/events/devices/device_util_linux.h" #include "ui/events/event.h" #include "ui/events/ozone/device/device_manager.h" #include "ui/events/ozone/evdev/event_converter_test_util.h" @@ -251,4 +252,15 @@ TEST_F(GamepadEventConverterEvdevTest, XboxGamepadVibrationEvents) { EXPECT_EQ(expected_events[1].value, received_cancel_event.value); } +TEST_F(GamepadEventConverterEvdevTest, XboxGamepadHasKeys) { + TestGamepadObserver observer; + std::unique_ptr<ui::TestGamepadEventConverterEvdev> dev = + CreateDevice(kXboxGamepad); + + const std::vector<uint64_t> key_bits = dev->GetGamepadKeyBits(); + + // BTN_A should be supported. + EXPECT_TRUE(EvdevBitUint64IsSet(key_bits.data(), 305)); +} + } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc index 61107110a3a..5e42002b8ed 100644 --- a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc @@ -108,6 +108,18 @@ void InputControllerEvdev::GetAutoRepeatRate(base::TimeDelta* delay, keyboard_->GetAutoRepeatRate(delay, interval); } +void InputControllerEvdev::SetKeyboardKeyBitsMapping( + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) { + keyboard_key_bits_mapping_ = std::move(key_bits_mapping); +} + +std::vector<uint64_t> InputControllerEvdev::GetKeyboardKeyBits(int id) { + auto key_bits_mapping_iter = keyboard_key_bits_mapping_.find(id); + return key_bits_mapping_iter == keyboard_key_bits_mapping_.end() + ? std::vector<uint64_t>() + : key_bits_mapping_iter->second; +} + void InputControllerEvdev::SetCurrentLayoutByName( const std::string& layout_name) { keyboard_->SetCurrentLayoutByName(layout_name); @@ -205,6 +217,18 @@ void InputControllerEvdev::SetPointingStickAcceleration(bool enabled) { ScheduleUpdateDeviceSettings(); } +void InputControllerEvdev::SetGamepadKeyBitsMapping( + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) { + gamepad_key_bits_mapping_ = std::move(key_bits_mapping); +} + +std::vector<uint64_t> InputControllerEvdev::GetGamepadKeyBits(int id) { + auto gamepad_key_bits_iter = gamepad_key_bits_mapping_.find(id); + return gamepad_key_bits_iter == gamepad_key_bits_mapping_.end() + ? std::vector<uint64_t>() + : gamepad_key_bits_iter->second; +} + void InputControllerEvdev::SetPrimaryButtonRight(bool right) { mouse_button_map_->SetPrimaryButtonRight(right); } diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.h b/chromium/ui/events/ozone/evdev/input_controller_evdev.h index 52765901c51..6aa1b8bdf41 100644 --- a/chromium/ui/events/ozone/evdev/input_controller_evdev.h +++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.h @@ -61,6 +61,9 @@ class COMPONENT_EXPORT(EVDEV) InputControllerEvdev : public InputController { void GetAutoRepeatRate(base::TimeDelta* delay, base::TimeDelta* interval) override; void SetCurrentLayoutByName(const std::string& layout_name) override; + void SetKeyboardKeyBitsMapping( + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override; + std::vector<uint64_t> GetKeyboardKeyBits(int id) override; void SetTouchEventLoggingEnabled(bool enabled) override; void SetTouchpadSensitivity(int value) override; void SetTouchpadScrollSensitivity(int value) override; @@ -81,6 +84,9 @@ class COMPONENT_EXPORT(EVDEV) InputControllerEvdev : public InputController { void SetPointingStickSensitivity(int value) override; void SetPointingStickPrimaryButtonRight(bool right) override; void SetPointingStickAcceleration(bool enabled) override; + void SetGamepadKeyBitsMapping( + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override; + std::vector<uint64_t> GetGamepadKeyBits(int id) override; void SetTouchpadAcceleration(bool enabled) override; void SetTouchpadScrollAcceleration(bool enabled) override; void SetTapToClickPaused(bool state) override; @@ -146,12 +152,18 @@ class COMPONENT_EXPORT(EVDEV) InputControllerEvdev : public InputController { // Keyboard state. KeyboardEvdev* const keyboard_; + // Keyboard keybits. + base::flat_map<int, std::vector<uint64_t>> keyboard_key_bits_mapping_; + // Mouse button map. MouseButtonMapEvdev* const mouse_button_map_; // Pointing stick button map. MouseButtonMapEvdev* const pointing_stick_button_map_; + // Gamepad keybits. + base::flat_map<int, std::vector<uint64_t>> gamepad_key_bits_mapping_; + // Device presence. bool has_mouse_ = false; bool has_pointing_stick_ = false; diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc index 025bade447f..59ceb06ca91 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/containers/contains.h" #include "base/files/scoped_file.h" #include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" @@ -24,6 +25,7 @@ #include "ui/events/ozone/evdev/event_converter_evdev_impl.h" #include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h" +#include "ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h" #include "ui/events/ozone/evdev/microphone_mute_switch_event_converter_evdev.h" #include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h" #include "ui/events/ozone/evdev/switches.h" @@ -52,24 +54,6 @@ namespace ui { namespace { -struct OpenInputDeviceParams { - // Unique identifier for the new device. - int id; - - // Device path to open. - base::FilePath path; - - // Dispatcher for events. - DeviceEventDispatcherEvdev* dispatcher; - - // State shared between devices. - CursorDelegateEvdev* cursor; -#if defined(USE_EVDEV_GESTURES) - GesturePropertyProvider* gesture_property_provider; -#endif - SharedPalmDetectionFilterState* shared_palm_state; -}; - #if defined(USE_EVDEV_GESTURES) void SetGestureIntProperty(GesturePropertyProvider* provider, int id, @@ -95,100 +79,6 @@ void SetGestureBoolProperty(GesturePropertyProvider* provider, #endif -std::unique_ptr<EventConverterEvdev> CreateConverter( - const OpenInputDeviceParams& params, - base::ScopedFD fd, - const EventDeviceInfo& devinfo) { -#if defined(USE_LIBINPUT) - // Use LibInputEventConverter for odd touchpads - if (devinfo.UseLibinput()) { - return LibInputEventConverter::Create(params.path, params.id, devinfo, - params.cursor, params.dispatcher); - } -#endif - -#if defined(USE_EVDEV_GESTURES) - // Touchpad or mouse: use gestures library. - // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent - if (devinfo.HasTouchpad() || devinfo.HasMouse() || - devinfo.HasPointingStick()) { - std::unique_ptr<GestureInterpreterLibevdevCros> gesture_interp = - std::make_unique<GestureInterpreterLibevdevCros>( - params.id, params.cursor, params.gesture_property_provider, - params.dispatcher); - return std::make_unique<EventReaderLibevdevCros>(std::move(fd), params.path, - params.id, devinfo, - std::move(gesture_interp)); - } -#endif - - // Touchscreen: use TouchEventConverterEvdev. - if (devinfo.HasTouchscreen()) { - return TouchEventConverterEvdev::Create( - std::move(fd), params.path, params.id, devinfo, - params.shared_palm_state, params.dispatcher); - } - - // Graphics tablet - if (devinfo.HasTablet()) { - return base::WrapUnique<EventConverterEvdev>(new TabletEventConverterEvdev( - std::move(fd), params.path, params.id, params.cursor, devinfo, - params.dispatcher)); - } - - if (devinfo.HasGamepad()) { - return base::WrapUnique<EventConverterEvdev>(new GamepadEventConverterEvdev( - std::move(fd), params.path, params.id, devinfo, params.dispatcher)); - } - - if (devinfo.IsStylusButtonDevice()) { - return base::WrapUnique<EventConverterEvdev>( - new StylusButtonEventConverterEvdev( - std::move(fd), params.path, params.id, devinfo, params.dispatcher)); - } - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - kEnableMicrophoneMuteSwitchDeviceSwitch) && - devinfo.IsMicrophoneMuteSwitchDevice()) { - return base::WrapUnique<EventConverterEvdev>( - new MicrophoneMuteSwitchEventConverterEvdev( - std::move(fd), params.path, params.id, devinfo, params.dispatcher)); - } - - // Everything else: use EventConverterEvdevImpl. - return base::WrapUnique<EventConverterEvdevImpl>( - new EventConverterEvdevImpl(std::move(fd), params.path, params.id, - devinfo, params.cursor, params.dispatcher)); -} - -// Open an input device and construct an EventConverterEvdev. -std::unique_ptr<EventConverterEvdev> OpenInputDevice( - const OpenInputDeviceParams& params) { - const base::FilePath& path = params.path; - TRACE_EVENT1("evdev", "OpenInputDevice", "path", path.value()); - - base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_NONBLOCK)); - if (fd.get() < 0) { - PLOG(ERROR) << "Cannot open " << path.value(); - return nullptr; - } - - // Use monotonic timestamps for events. The touch code in particular - // expects event timestamps to correlate to the monotonic clock - // (base::TimeTicks). - unsigned int clk = CLOCK_MONOTONIC; - if (ioctl(fd.get(), EVIOCSCLOCKID, &clk)) - PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; - - EventDeviceInfo devinfo; - if (!devinfo.Initialize(fd.get(), path)) { - LOG(ERROR) << "Failed to get device information for " << path.value(); - return nullptr; - } - - return CreateConverter(params, std::move(fd), devinfo); -} - bool IsUncategorizedDevice(const EventConverterEvdev& converter) { return !converter.HasTouchscreen() && !converter.HasKeyboard() && !converter.HasMouse() && !converter.HasPointingStick() && @@ -199,14 +89,17 @@ bool IsUncategorizedDevice(const EventConverterEvdev& converter) { InputDeviceFactoryEvdev::InputDeviceFactoryEvdev( std::unique_ptr<DeviceEventDispatcherEvdev> dispatcher, - CursorDelegateEvdev* cursor) + CursorDelegateEvdev* cursor, + std::unique_ptr<InputDeviceOpener> input_device_opener) : task_runner_(base::ThreadTaskRunnerHandle::Get()), cursor_(cursor), shared_palm_state_(new SharedPalmDetectionFilterState), #if defined(USE_EVDEV_GESTURES) gesture_property_provider_(new GesturePropertyProvider), #endif - dispatcher_(std::move(dispatcher)) { + dispatcher_(std::move(dispatcher)), + keyboard_imposter_checker_(new KeyboardImposterCheckerEvdev), + input_device_opener_(std::move(input_device_opener)) { } InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() = default; @@ -224,7 +117,8 @@ void InputDeviceFactoryEvdev::AddInputDevice(int id, params.gesture_property_provider = gesture_property_provider_.get(); #endif - std::unique_ptr<EventConverterEvdev> converter = OpenInputDevice(params); + std::unique_ptr<EventConverterEvdev> converter = + input_device_opener_->OpenInputDevice(params); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, @@ -278,11 +172,31 @@ void InputDeviceFactoryEvdev::AttachInputDevice( base::Unretained(this))); } + if ((converter->type() == InputDeviceType::INPUT_DEVICE_USB || + converter->type() == InputDeviceType::INPUT_DEVICE_BLUETOOTH) && + converter->HasKeyboard()) { + converter->SetReceivedValidInputCallback(base::BindRepeating( + &InputDeviceFactoryEvdev::UpdateKeyboardDevicesOnKeyPress, + base::Unretained(this))); + } + // Add initialized device to map. converters_[path] = std::move(converter); converters_[path]->Start(); + UpdateDirtyFlags(converters_[path].get()); + // Register device on physical port & get ids of devices on the same + // physical port. + std::vector<int> ids_to_check = + keyboard_imposter_checker_->OnDeviceAdded(converters_[path].get()); + // Check for imposters on all devices that share the same physical port. + for (const auto& it : converters_) { + if (base::Contains(ids_to_check, it.second->id()) && + keyboard_imposter_checker_->FlagIfImposter(it.second.get())) + UpdateDirtyFlags(it.second.get()); + } + // Sync settings to new device. ApplyInputDeviceSettings(); ApplyCapsLockLed(); @@ -307,6 +221,19 @@ void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath& path) { // Cancel libevent notifications from this converter. converter->Stop(); + // Decrement device count on physical port. Get ids of devices on the same + // physical port. + std::vector<int> ids_to_check = + keyboard_imposter_checker_->OnDeviceRemoved(converter.get()); + // Check for imposters on all devices that share the same physical port. + // Declassify any devices as no longer imposters, if the removal of this + // device changes their status. + for (const auto& it : converters_) { + if (base::Contains(ids_to_check, it.second->id()) && + !keyboard_imposter_checker_->FlagIfImposter(it.second.get())) + UpdateDirtyFlags(it.second.get()); + } + UpdateDirtyFlags(converter.get()); NotifyDevicesUpdated(); } @@ -600,14 +527,16 @@ void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() { } void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() { + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping; std::vector<InputDevice> keyboards; for (auto it = converters_.begin(); it != converters_.end(); ++it) { if (it->second->HasKeyboard()) { keyboards.push_back(InputDevice(it->second->input_device())); + key_bits_mapping[it->second->id()] = it->second->GetKeyboardKeyBits(); } } - - dispatcher_->DispatchKeyboardDevicesUpdated(keyboards); + dispatcher_->DispatchKeyboardDevicesUpdated(keyboards, + std::move(key_bits_mapping)); } void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() { @@ -616,7 +545,9 @@ void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() { for (auto it = converters_.begin(); it != converters_.end(); ++it) { if (it->second->HasMouse()) { mice.push_back(it->second->input_device()); - has_mouse = true; + // Some I2C touchpads falsely claim to be mice, see b/205272718 + if (it->second->type() != ui::InputDeviceType::INPUT_DEVICE_INTERNAL) + has_mouse = true; } else if (it->second->HasPointingStick()) { mice.push_back(it->second->input_device()); has_pointing_stick = true; @@ -641,16 +572,18 @@ void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() { } void InputDeviceFactoryEvdev::NotifyGamepadDevicesUpdated() { + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping; std::vector<GamepadDevice> gamepads; for (auto it = converters_.begin(); it != converters_.end(); ++it) { if (it->second->HasGamepad()) { gamepads.emplace_back(it->second->input_device(), it->second->GetGamepadAxes(), it->second->GetGamepadRumbleCapability()); + key_bits_mapping[it->second->id()] = it->second->GetGamepadKeyBits(); } } - dispatcher_->DispatchGamepadDevicesUpdated(gamepads); + dispatcher_->DispatchGamepadDevicesUpdated(gamepads, std::move(key_bits_mapping)); } void InputDeviceFactoryEvdev::NotifyUncategorizedDevicesUpdated() { @@ -663,6 +596,12 @@ void InputDeviceFactoryEvdev::NotifyUncategorizedDevicesUpdated() { dispatcher_->DispatchUncategorizedDevicesUpdated(uncategorized_devices); } +void InputDeviceFactoryEvdev::UpdateKeyboardDevicesOnKeyPress( + const EventConverterEvdev* converter) { + UpdateDirtyFlags(converter); + NotifyDevicesUpdated(); +} + void InputDeviceFactoryEvdev::SetIntPropertyForOneType( const EventDeviceType type, const std::string& name, diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h index 63e759b7e59..c4256533453 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.h @@ -21,7 +21,9 @@ #include "ui/events/devices/haptic_touchpad_effects.h" #include "ui/events/ozone/evdev/event_converter_evdev.h" #include "ui/events/ozone/evdev/event_device_info.h" +#include "ui/events/ozone/evdev/input_device_opener.h" #include "ui/events/ozone/evdev/input_device_settings_evdev.h" +#include "ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h" #include "ui/events/ozone/evdev/touch_evdev_types.h" #include "ui/events/ozone/evdev/touch_filter/shared_palm_detection_filter_state.h" #include "ui/ozone/public/input_controller.h" @@ -50,7 +52,8 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev { public: InputDeviceFactoryEvdev( std::unique_ptr<DeviceEventDispatcherEvdev> dispatcher, - CursorDelegateEvdev* cursor); + CursorDelegateEvdev* cursor, + std::unique_ptr<InputDeviceOpener> input_device_opener); InputDeviceFactoryEvdev(const InputDeviceFactoryEvdev&) = delete; InputDeviceFactoryEvdev& operator=(const InputDeviceFactoryEvdev&) = delete; @@ -118,6 +121,10 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev { void NotifyGamepadDevicesUpdated(); void NotifyUncategorizedDevicesUpdated(); + // Method used as callback to update keyboard list when a valid key press is + // received. + void UpdateKeyboardDevicesOnKeyPress(const EventConverterEvdev* converter); + void SetIntPropertyForOneType(const EventDeviceType type, const std::string& name, int value); @@ -175,6 +182,11 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev { // Device settings. These primarily affect libgestures behavior. InputDeviceSettingsEvdev input_device_settings_; + // Checks if a device identifying as a keyboard is another device + // mis-identifying as one. + const std::unique_ptr<KeyboardImposterCheckerEvdev> + keyboard_imposter_checker_; + // Owned per-device event converters (by path). // NB: This should be destroyed early, before any shared state. std::map<base::FilePath, std::unique_ptr<EventConverterEvdev>> converters_; @@ -182,6 +194,9 @@ class COMPONENT_EXPORT(EVDEV) InputDeviceFactoryEvdev { // The latest stylus state, updated every time a stylus report comes. InProgressStylusState latest_stylus_state_; + // Handles ioctl calls and creation of event converters. + const std::unique_ptr<InputDeviceOpener> input_device_opener_; + // Support weak pointers for attach & detach callbacks. base::WeakPtrFactory<InputDeviceFactoryEvdev> weak_ptr_factory_{this}; }; diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_unittest.cc new file mode 100644 index 00000000000..a6e97a1bc64 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_unittest.cc @@ -0,0 +1,440 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/ozone/evdev/input_device_factory_evdev.h" + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/devices/input_device.h" +#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" +#include "ui/events/ozone/evdev/event_converter_test_util.h" +#include "ui/events/ozone/evdev/event_device_info.h" +#include "ui/events/ozone/evdev/event_device_test_util.h" +#include "ui/events/ozone/evdev/input_device_opener.h" +#include "ui/events/ozone/features.h" + +namespace ui { + +class FakeEventConverterEvdev : public EventConverterEvdev { + public: + explicit FakeEventConverterEvdev(int fd, + base::FilePath path, + int id, + InputDeviceType type, + const std::string& name, + const std::string& phys, + uint16_t vendor_id, + uint16_t product_id, + uint16_t version, + bool has_mouse, + bool has_keyboard) + : EventConverterEvdev(fd, + path, + id, + type, + name, + phys, + vendor_id, + product_id, + version), + + has_mouse_(has_mouse), + has_keyboard_(has_keyboard) {} + + bool HasMouse() const override { return has_mouse_; } + + bool HasKeyboard() const override { return has_keyboard_; } + + void OnFileCanReadWithoutBlocking(int fd) override {} + void SetKeyFilter(bool enable_filter, + std::vector<DomCode> allowed_keys) override {} + + private: + bool has_mouse_; + bool has_keyboard_; +}; + +class StubDeviceEventDispatcherEvdev : public DeviceEventDispatcherEvdev { + public: + explicit StubDeviceEventDispatcherEvdev( + base::RepeatingCallback<void(const std::vector<InputDevice>& devices)> + callback) + : callback_(callback) {} + ~StubDeviceEventDispatcherEvdev() override = default; + + void DispatchKeyEvent(const KeyEventParams& params) override {} + void DispatchMouseMoveEvent(const MouseMoveEventParams& params) override {} + void DispatchMouseButtonEvent(const MouseButtonEventParams& params) override { + } + void DispatchMouseWheelEvent(const MouseWheelEventParams& params) override {} + void DispatchPinchEvent(const PinchEventParams& params) override {} + void DispatchScrollEvent(const ScrollEventParams& params) override {} + void DispatchTouchEvent(const TouchEventParams& params) override {} + void DispatchMicrophoneMuteSwitchValueChanged(bool muted) override {} + + void DispatchKeyboardDevicesUpdated( + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override { + callback_.Run(devices); + } + void DispatchTouchscreenDevicesUpdated( + const std::vector<TouchscreenDevice>& devices) override {} + void DispatchMouseDevicesUpdated(const std::vector<InputDevice>& devices, + bool has_mouse, + bool has_pointing_stick) override {} + void DispatchTouchpadDevicesUpdated(const std::vector<InputDevice>& devices, + bool has_haptic_touchpad) override {} + void DispatchUncategorizedDevicesUpdated( + const std::vector<InputDevice>& devices) override {} + void DispatchDeviceListsComplete() override {} + void DispatchStylusStateChanged(StylusState stylus_state) override {} + + void DispatchGamepadEvent(const GamepadEvent& event) override {} + + void DispatchGamepadDevicesUpdated( + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override {} + + private: + base::RepeatingCallback<void(const std::vector<InputDevice>& devices)> + callback_; +}; + +class FakeInputDeviceOpenerEvdev : public InputDeviceOpener { + public: + explicit FakeInputDeviceOpenerEvdev( + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters) + : converters_(std::move(converters)) {} + + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters_; + + std::unique_ptr<EventConverterEvdev> OpenInputDevice( + const OpenInputDeviceParams& params) override { + std::unique_ptr<FakeEventConverterEvdev> converter = + std::move(converters_[0]); + converters_.erase(converters_.begin()); + return std::move(converter); + } +}; + +class InputDeviceFactoryEvdevTest : public testing::Test { + public: + InputDeviceFactoryEvdevTest() = default; + + std::vector<InputDevice> keyboards_; + base::test::SingleThreadTaskEnvironment task_environment{ + base::test::TaskEnvironment::MainThreadType::UI}; + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<ui::DeviceEventDispatcherEvdev> dispatcher_; + + void SetUp() override { + dispatcher_ = std::make_unique<StubDeviceEventDispatcherEvdev>( + base::BindRepeating(&InputDeviceFactoryEvdevTest::DispatchCallback, + base::Unretained(this))); + } + + private: + void DispatchCallback(const std::vector<InputDevice>& devices) { + keyboards_ = devices; + } +}; + +TEST_F(InputDeviceFactoryEvdevTest, + AttachSingularKeyboardFakeKeyboardHeuristicEnabled) { + scoped_feature_list_.InitAndEnableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("path"), 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "name", "phys_path", 1, 1, 1, false, true); + + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, AttachSingularMouse) { + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("path"), 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "name", "phys_path", 1, 1, 1, true, false); + + converters.push_back(std::move(mouse_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(0)); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachMouseAndKeyboardDifferentPhysFakeKeyboardHeuristicEnabled) { + scoped_feature_list_.InitAndEnableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("mouse_path"), 1, + InputDeviceType::INPUT_DEVICE_INTERNAL, "mouse_name", + "phys_path/mouse", 1, 1, 1, true, false); + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 2, base::FilePath("keyboard_path"), 2, + InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard_name", + "phys_path/keyboard", 2, 2, 2, false, true); + + converters.push_back(std::move(mouse_converter)); + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + input_device_factory_->AddInputDevice(2, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachMouseAndKeyboardSamePhysFakeKeyboardHeuristicEnabled) { + scoped_feature_list_.InitAndEnableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("mouse_path"), 1, + InputDeviceType::INPUT_DEVICE_INTERNAL, "mouse_name", "phys_path", 1, + 1, 1, true, false); + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 2, base::FilePath("keyboard_path"), 2, + InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard_name", "phys_path", + 2, 2, 2, false, true); + + converters.push_back(std::move(mouse_converter)); + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + input_device_factory_->AddInputDevice(2, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_TRUE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachSingularKeyboardAndMouseFakeKeyboardHeuristicEnabled) { + scoped_feature_list_.InitAndEnableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> keyboard_and_mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("path"), 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "name", "phys_path", 1, 1, 1, true, true); + + converters.push_back(std::move(keyboard_and_mouse_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_TRUE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachSingularKeyboardFakeKeyboardHeuristicDisabled) { + scoped_feature_list_.InitAndDisableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("path"), 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "name", "phys_path", 1, 1, 1, false, true); + + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachMouseAndKeyboardDifferentPhysFakeKeyboardHeuristicDisabled) { + scoped_feature_list_.InitAndDisableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("mouse_path"), 1, + InputDeviceType::INPUT_DEVICE_INTERNAL, "mouse_name", + "phys_path/mouse", 1, 1, 1, true, false); + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 2, base::FilePath("keyboard_path"), 2, + InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard_name", + "phys_path/keyboard", 2, 2, 2, false, true); + + converters.push_back(std::move(mouse_converter)); + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + input_device_factory_->AddInputDevice(2, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachMouseAndKeyboardSamePhysFakeKeyboardHeuristicDisabled) { + scoped_feature_list_.InitAndDisableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("mouse_path"), 1, + InputDeviceType::INPUT_DEVICE_INTERNAL, "mouse_name", "phys_path", 1, + 1, 1, true, false); + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 2, base::FilePath("keyboard_path"), 2, + InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard_name", "phys_path", + 2, 2, 2, false, true); + + converters.push_back(std::move(mouse_converter)); + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + input_device_factory_->AddInputDevice(2, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachSingularKeyboardAndMouseFakeKeyboardHeuristicDisabled) { + scoped_feature_list_.InitAndDisableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + std::unique_ptr<FakeEventConverterEvdev> keyboard_and_mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, base::FilePath("path"), 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "name", "phys_path", 1, 1, 1, true, true); + + converters.push_back(std::move(keyboard_and_mouse_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +TEST_F(InputDeviceFactoryEvdevTest, + AttachAndRemoveDeviceFakeKeyboardHeuristicEnabled) { + scoped_feature_list_.InitAndEnableFeature(kEnableFakeKeyboardHeuristic); + std::vector<std::unique_ptr<FakeEventConverterEvdev>> converters; + base::RunLoop run_loop; + base::FilePath mouse_path("mouse_path"); + std::unique_ptr<FakeEventConverterEvdev> mouse_converter = + std::make_unique<FakeEventConverterEvdev>( + 1, mouse_path, 1, InputDeviceType::INPUT_DEVICE_INTERNAL, + "mouse_name", "phys_path", 1, 1, 1, true, false); + std::unique_ptr<FakeEventConverterEvdev> keyboard_converter = + std::make_unique<FakeEventConverterEvdev>( + 2, base::FilePath("keyboard_path"), 2, + InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard_name", "phys_path", + 2, 2, 2, false, true); + + converters.push_back(std::move(mouse_converter)); + converters.push_back(std::move(keyboard_converter)); + + std::unique_ptr<InputDeviceFactoryEvdev> input_device_factory_ = + std::make_unique<InputDeviceFactoryEvdev>( + std::move(dispatcher_), nullptr, + std::make_unique<FakeInputDeviceOpenerEvdev>(std::move(converters))); + input_device_factory_->OnStartupScanComplete(); + input_device_factory_->AddInputDevice(1, base::FilePath("unused_value")); + input_device_factory_->AddInputDevice(2, base::FilePath("unused_value")); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_TRUE(keyboards_.front().suspected_imposter); + + input_device_factory_->RemoveInputDevice(mouse_path); + EXPECT_EQ(keyboards_.size(), std::size_t(1)); + EXPECT_FALSE(keyboards_.front().suspected_imposter); +} + +} // namespace ui diff --git a/chromium/ui/events/ozone/evdev/input_device_opener.h b/chromium/ui/events/ozone/evdev/input_device_opener.h new file mode 100644 index 00000000000..0bf94b45765 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/input_device_opener.h @@ -0,0 +1,47 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_H_ +#define UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_H_ + +#include "ui/events/ozone/evdev/event_converter_evdev.h" +#include "ui/events/ozone/evdev/touch_filter/shared_palm_detection_filter_state.h" + +#if defined(USE_EVDEV_GESTURES) +#include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" +#endif + +namespace ui { + +class CursorDelegateEvdev; +class DeviceEventDispatcherEvdev; + +struct OpenInputDeviceParams { + // Unique identifier for the new device. + int id; + + // Device path to open. + base::FilePath path; + + // Dispatcher for events. + DeviceEventDispatcherEvdev* dispatcher; + + // State shared between devices. + CursorDelegateEvdev* cursor; +#if defined(USE_EVDEV_GESTURES) + GesturePropertyProvider* gesture_property_provider; +#endif + SharedPalmDetectionFilterState* shared_palm_state; +}; + +class COMPONENT_EXPORT(EVDEV) InputDeviceOpener { + public: + virtual ~InputDeviceOpener() = default; + + virtual std::unique_ptr<EventConverterEvdev> OpenInputDevice( + const OpenInputDeviceParams& params) = 0; +}; +} // namespace ui + +#endif // UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_H_ diff --git a/chromium/ui/events/ozone/evdev/input_device_opener_evdev.cc b/chromium/ui/events/ozone/evdev/input_device_opener_evdev.cc new file mode 100644 index 00000000000..e91a33054c3 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/input_device_opener_evdev.cc @@ -0,0 +1,134 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/ozone/evdev/input_device_opener_evdev.h" + +#include <fcntl.h> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/memory/ptr_util.h" +#include "base/trace_event/trace_event.h" +#include "ui/events/ozone/evdev/event_converter_evdev_impl.h" +#include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h" +#include "ui/events/ozone/evdev/microphone_mute_switch_event_converter_evdev.h" +#include "ui/events/ozone/evdev/stylus_button_event_converter_evdev.h" +#include "ui/events/ozone/evdev/switches.h" +#include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" +#include "ui/events/ozone/evdev/touch_event_converter_evdev.h" + +#if defined(USE_EVDEV_GESTURES) +#include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h" +#include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h" +#endif + +#if defined(USE_LIBINPUT) +#include "ui/events/ozone/evdev/libinput_event_converter.h" +#endif + +#ifndef EVIOCSCLOCKID +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) +#endif + +namespace ui { + +namespace { + +std::unique_ptr<EventConverterEvdev> CreateConverter( + const OpenInputDeviceParams& params, + base::ScopedFD fd, + const EventDeviceInfo& devinfo) { +#if defined(USE_LIBINPUT) + // Use LibInputEventConverter for odd touchpads + if (devinfo.UseLibinput()) { + return LibInputEventConverter::Create(params.path, params.id, devinfo, + params.cursor, params.dispatcher); + } +#endif + +#if defined(USE_EVDEV_GESTURES) + // Touchpad or mouse: use gestures library. + // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent + if (devinfo.HasTouchpad() || devinfo.HasMouse() || + devinfo.HasPointingStick()) { + std::unique_ptr<GestureInterpreterLibevdevCros> gesture_interp = + std::make_unique<GestureInterpreterLibevdevCros>( + params.id, params.cursor, params.gesture_property_provider, + params.dispatcher); + return std::make_unique<EventReaderLibevdevCros>(std::move(fd), params.path, + params.id, devinfo, + std::move(gesture_interp)); + } +#endif + + // Touchscreen: use TouchEventConverterEvdev. + if (devinfo.HasTouchscreen()) { + return TouchEventConverterEvdev::Create( + std::move(fd), params.path, params.id, devinfo, + params.shared_palm_state, params.dispatcher); + } + + // Graphics tablet + if (devinfo.HasTablet()) { + return base::WrapUnique<EventConverterEvdev>(new TabletEventConverterEvdev( + std::move(fd), params.path, params.id, params.cursor, devinfo, + params.dispatcher)); + } + + if (devinfo.HasGamepad()) { + return base::WrapUnique<EventConverterEvdev>(new GamepadEventConverterEvdev( + std::move(fd), params.path, params.id, devinfo, params.dispatcher)); + } + + if (devinfo.IsStylusButtonDevice()) { + return base::WrapUnique<EventConverterEvdev>( + new StylusButtonEventConverterEvdev( + std::move(fd), params.path, params.id, devinfo, params.dispatcher)); + } + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + kEnableMicrophoneMuteSwitchDeviceSwitch) && + devinfo.IsMicrophoneMuteSwitchDevice()) { + return base::WrapUnique<EventConverterEvdev>( + new MicrophoneMuteSwitchEventConverterEvdev( + std::move(fd), params.path, params.id, devinfo, params.dispatcher)); + } + + // Everything else: use EventConverterEvdevImpl. + return base::WrapUnique<EventConverterEvdevImpl>( + new EventConverterEvdevImpl(std::move(fd), params.path, params.id, + devinfo, params.cursor, params.dispatcher)); +} + +} // namespace + +std::unique_ptr<EventConverterEvdev> InputDeviceOpenerEvdev::OpenInputDevice( + const OpenInputDeviceParams& params) { + const base::FilePath& path = params.path; + TRACE_EVENT1("evdev", "OpenInputDevice", "path", path.value()); + + base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_NONBLOCK)); + if (fd.get() < 0) { + PLOG(ERROR) << "Cannot open " << path.value(); + return nullptr; + } + + // Use monotonic timestamps for events. The touch code in particular + // expects event timestamps to correlate to the monotonic clock + // (base::TimeTicks). + unsigned int clk = CLOCK_MONOTONIC; + if (ioctl(fd.get(), EVIOCSCLOCKID, &clk)) + PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; + + EventDeviceInfo devinfo; + if (!devinfo.Initialize(fd.get(), path)) { + LOG(ERROR) << "Failed to get device information for " << path.value(); + return nullptr; + } + + return CreateConverter(params, std::move(fd), devinfo); +} + +} // namespace ui diff --git a/chromium/ui/events/ozone/evdev/input_device_opener_evdev.h b/chromium/ui/events/ozone/evdev/input_device_opener_evdev.h new file mode 100644 index 00000000000..b09d269b05e --- /dev/null +++ b/chromium/ui/events/ozone/evdev/input_device_opener_evdev.h @@ -0,0 +1,30 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_EVDEV_H_ +#define UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_EVDEV_H_ + +#include "ui/events/ozone/evdev/input_device_opener.h" + +#include "base/files/scoped_file.h" +#include "ui/events/ozone/evdev/event_device_info.h" + +namespace ui { + +class COMPONENT_EXPORT(EVDEV) InputDeviceOpenerEvdev + : public InputDeviceOpener { + public: + InputDeviceOpenerEvdev() = default; + + InputDeviceOpenerEvdev(const InputDeviceOpenerEvdev&) = delete; + InputDeviceOpenerEvdev& operator=(const InputDeviceOpenerEvdev&) = delete; + + ~InputDeviceOpenerEvdev() override = default; + + std::unique_ptr<EventConverterEvdev> OpenInputDevice( + const OpenInputDeviceParams& params) override; +}; +} // namespace ui + +#endif // UI_EVENTS_OZONE_EVDEV_INPUT_DEVICE_OPENER_EVDEV_H_ diff --git a/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc b/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc new file mode 100644 index 00000000000..33c8bb01ff1 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc @@ -0,0 +1,74 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h" + +#include "base/containers/cxx20_erase_map.h" +#include "ui/events/ozone/features.h" + +namespace ui { + +std::vector<int> KeyboardImposterCheckerEvdev::GetIdsOnSamePhys( + const std::string& phys_path) { + std::vector<int> ids_on_same_phys; + std::pair<std::multimap<std::string, int>::iterator, + std::multimap<std::string, int>::iterator> + iterators = devices_on_phys_path_.equal_range(phys_path); + for (auto& it = iterators.first; it != iterators.second; ++it) { + ids_on_same_phys.push_back(it->second); + } + return ids_on_same_phys; +} + +std::vector<int> KeyboardImposterCheckerEvdev::OnDeviceAdded( + EventConverterEvdev* converter) { + if (!base::FeatureList::IsEnabled(kEnableFakeKeyboardHeuristic)) + return std::vector<int>(); + + devices_on_phys_path_.emplace(converter->input_device().phys, + converter->id()); + return GetIdsOnSamePhys(converter->input_device().phys); +} + +bool KeyboardImposterCheckerEvdev::FlagIfImposter( + EventConverterEvdev* converter) { + if (!base::FeatureList::IsEnabled(kEnableFakeKeyboardHeuristic)) + return false; +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (converter->GetKeyboardType() == KeyboardType::IN_BLOCKLIST) { + fake_keyboard_heuristic_metrics_.RecordUsage(false); + } +#endif + + if (!converter->HasKeyboard() || + (!converter->HasMouse() && + devices_on_phys_path_.count(converter->input_device().phys) < 2)) { + converter->SetSuspectedImposter(false); + return false; + } + + converter->SetSuspectedImposter(true); +#if BUILDFLAG(IS_CHROMEOS_ASH) + fake_keyboard_heuristic_metrics_.RecordUsage(true); +#endif + return true; +} + +std::vector<int> KeyboardImposterCheckerEvdev::OnDeviceRemoved( + EventConverterEvdev* converter) { + if (!base::FeatureList::IsEnabled(kEnableFakeKeyboardHeuristic)) + return std::vector<int>(); + + base::EraseIf(devices_on_phys_path_, + [&](const auto& devices_on_phys_path_entry) { + return devices_on_phys_path_entry.second == converter->id(); + }); + return GetIdsOnSamePhys(converter->input_device().phys); +} + +KeyboardImposterCheckerEvdev::KeyboardImposterCheckerEvdev() = default; + +KeyboardImposterCheckerEvdev::~KeyboardImposterCheckerEvdev() = default; + +} // namespace ui diff --git a/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h b/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h new file mode 100644 index 00000000000..871416c5f03 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h @@ -0,0 +1,47 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_OZONE_EVDEV_KEYBOARD_IMPOSTER_CHECKER_EVDEV_H_ +#define UI_EVENTS_OZONE_EVDEV_KEYBOARD_IMPOSTER_CHECKER_EVDEV_H_ + +#include <map> + +#include "base/component_export.h" +#include "ui/events/devices/input_device.h" +#include "ui/events/ozone/evdev/event_converter_evdev.h" + +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h" +#endif + +namespace ui { +class COMPONENT_EXPORT(EVDEV) KeyboardImposterCheckerEvdev { + public: + // Registers the id of a device on its phys path and returns the ids of all + // devices on that phys path. + std::vector<int> OnDeviceAdded(EventConverterEvdev* converter); + std::vector<int> OnDeviceRemoved(EventConverterEvdev* converter); + bool FlagIfImposter(EventConverterEvdev* converter); + + KeyboardImposterCheckerEvdev(); + + KeyboardImposterCheckerEvdev(const KeyboardImposterCheckerEvdev&) = delete; + KeyboardImposterCheckerEvdev& operator=(const KeyboardImposterCheckerEvdev&) = + delete; + + ~KeyboardImposterCheckerEvdev(); + + private: + std::vector<int> GetIdsOnSamePhys(const std::string& phys_path); + + // Number of devices per phys path. + std::multimap<std::string, int> devices_on_phys_path_; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + FakeKeyboardHeuristicMetrics fake_keyboard_heuristic_metrics_; +#endif +}; +} // namespace ui + +#endif // UI_EVENTS_OZONE_EVDEV_KEYBOARD_IMPOSTER_CHECKER_EVDEV_H_ diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_properties_service.cc b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_properties_service.cc index 40fc6c252a2..64537810dd2 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_properties_service.cc +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_properties_service.cc @@ -34,15 +34,15 @@ bool PropertyTypeMatchesValues(ui::GesturePropertyProvider::PropertyType type, GesturePropValue::Tag values_tag) { switch (type) { case ui::GesturePropertyProvider::PT_INT: - return values_tag == GesturePropValue::Tag::INTS; + return values_tag == GesturePropValue::Tag::kInts; case ui::GesturePropertyProvider::PT_SHORT: - return values_tag == GesturePropValue::Tag::SHORTS; + return values_tag == GesturePropValue::Tag::kShorts; case ui::GesturePropertyProvider::PT_BOOL: - return values_tag == GesturePropValue::Tag::BOOLS; + return values_tag == GesturePropValue::Tag::kBools; case ui::GesturePropertyProvider::PT_STRING: - return values_tag == GesturePropValue::Tag::STR; + return values_tag == GesturePropValue::Tag::kStr; case ui::GesturePropertyProvider::PT_REAL: - return values_tag == GesturePropValue::Tag::REALS; + return values_tag == GesturePropValue::Tag::kReals; } // This should never happen. return false; @@ -121,19 +121,19 @@ void GesturePropertiesService::SetProperty( } size_t num_values; switch (values->which()) { - case ozone::mojom::GesturePropValue::Tag::INTS: + case ozone::mojom::GesturePropValue::Tag::kInts: num_values = values->get_ints().size(); break; - case ozone::mojom::GesturePropValue::Tag::SHORTS: + case ozone::mojom::GesturePropValue::Tag::kShorts: num_values = values->get_shorts().size(); break; - case ozone::mojom::GesturePropValue::Tag::BOOLS: + case ozone::mojom::GesturePropValue::Tag::kBools: num_values = values->get_bools().size(); break; - case ozone::mojom::GesturePropValue::Tag::STR: + case ozone::mojom::GesturePropValue::Tag::kStr: num_values = 1; break; - case ozone::mojom::GesturePropValue::Tag::REALS: + case ozone::mojom::GesturePropValue::Tag::kReals: num_values = values->get_reals().size(); break; } diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc index 79712903656..4068429e0c1 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc @@ -503,11 +503,11 @@ bool IsMatchDeviceType(const std::string& match_type) { // Parse a boolean value keyword (e.g., on/off, true/false). int ParseBooleanKeyword(const std::string& value) { for (size_t i = 0; i < std::size(kTrue); ++i) { - if (base::LowerCaseEqualsASCII(value, kTrue[i])) + if (base::EqualsCaseInsensitiveASCII(value, kTrue[i])) return 1; } for (size_t i = 0; i < std::size(kFalse); ++i) { - if (base::LowerCaseEqualsASCII(value, kFalse[i])) + if (base::EqualsCaseInsensitiveASCII(value, kFalse[i])) return -1; } return 0; diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc index cf9d405f791..3d5d2ab7319 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc @@ -532,7 +532,7 @@ void TouchEventConverterEvdev::ReportTouchEvent( ui::PointerDetails details(event.reported_tool_type, /* pointer_id*/ 0, event.radius_x, event.radius_y, event.pressure, /* twist */ 0, event.tilt_x, event.tilt_y); - int flags = event.stylus_button ? ui::EventFlags::EF_LEFT_MOUSE_BUTTON : 0; + int flags = event.stylus_button ? ui::EF_LEFT_MOUSE_BUTTON : 0; dispatcher_->DispatchTouchEvent(TouchEventParams( input_device_.id, event.slot, event_type, gfx::PointF(event.x, event.y), details, timestamp, flags)); diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc index 2f00dbffe89..e79ac42dd92 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc @@ -166,7 +166,8 @@ class MockDeviceEventDispatcherEvdev : public DeviceEventDispatcherEvdev { void DispatchMicrophoneMuteSwitchValueChanged(bool muted) override {} void DispatchKeyboardDevicesUpdated( - const std::vector<InputDevice>& devices) override {} + const std::vector<InputDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override {} void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) override {} void DispatchMouseDevicesUpdated(const std::vector<InputDevice>& devices, @@ -183,7 +184,8 @@ class MockDeviceEventDispatcherEvdev : public DeviceEventDispatcherEvdev { void DispatchGamepadEvent(const GamepadEvent& event) override {} void DispatchGamepadDevicesUpdated( - const std::vector<GamepadDevice>& devices) override {} + const std::vector<GamepadDevice>& devices, + base::flat_map<int, std::vector<uint64_t>> key_bits_mapping) override {} private: base::RepeatingCallback<void(const GenericEventParams& params)> callback_; @@ -1919,12 +1921,12 @@ TEST_F(TouchEventConverterEvdevTest, ActiveStylusBarrelButtonWhileHovering) { auto down_event = dispatched_touch_event(0); EXPECT_EQ(ET_TOUCH_PRESSED, down_event.type); - EXPECT_TRUE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_TRUE(down_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, down_event.pointer_details.pointer_type); auto up_event = dispatched_touch_event(1); EXPECT_EQ(ET_TOUCH_RELEASED, up_event.type); - EXPECT_TRUE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_TRUE(down_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, up_event.pointer_details.pointer_type); } @@ -1966,24 +1968,24 @@ TEST_F(TouchEventConverterEvdevTest, ActiveStylusBarrelButton) { auto down_event = dispatched_touch_event(0); EXPECT_EQ(ET_TOUCH_PRESSED, down_event.type); - EXPECT_FALSE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_FALSE(down_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, down_event.pointer_details.pointer_type); auto button_down_event = dispatched_touch_event(1); EXPECT_EQ(ET_TOUCH_MOVED, button_down_event.type); - EXPECT_TRUE(button_down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_TRUE(button_down_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, button_down_event.pointer_details.pointer_type); auto button_up_event = dispatched_touch_event(2); EXPECT_EQ(ET_TOUCH_MOVED, button_up_event.type); - EXPECT_FALSE(button_up_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_FALSE(button_up_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, button_up_event.pointer_details.pointer_type); auto up_event = dispatched_touch_event(3); EXPECT_EQ(ET_TOUCH_RELEASED, up_event.type); - EXPECT_FALSE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON); + EXPECT_FALSE(down_event.flags & ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(EventPointerType::kPen, up_event.pointer_details.pointer_type); } diff --git a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc index 90f92656811..85c7124b7b2 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc @@ -215,7 +215,7 @@ void NeuralStylusPalmDetectionFilter::Filter( config.early_stage_sample_counts.find(stroke.samples_seen()) != config.early_stage_sample_counts.end()) { VLOG(1) << "About to run a early_stage prediction."; - if (DetectSpuriousStroke(ExtractFeatures(tracking_id), tracking_id, + if (DetectSpuriousStroke(ExtractFeatures(tracking_id), model_->config().output_threshold)) { VLOG(1) << "hold detected."; is_delay_.set(slot, true); @@ -238,9 +238,8 @@ void NeuralStylusPalmDetectionFilter::Filter( is_palm_.set(slot, IsHeuristicPalmStroke(stroke)); continue; } - is_palm_.set(slot, - DetectSpuriousStroke(ExtractFeatures(tracking_id), tracking_id, - model_->config().output_threshold)); + is_palm_.set(slot, DetectSpuriousStroke(ExtractFeatures(tracking_id), + model_->config().output_threshold)); if (is_palm_.test(slot)) { shared_palm_state_->latest_palm_touch_time = time; } @@ -307,7 +306,6 @@ bool NeuralStylusPalmDetectionFilter::IsHeuristicPalmStroke( bool NeuralStylusPalmDetectionFilter::DetectSpuriousStroke( const std::vector<float>& features, - int tracking_id, float threshold) const { auto inference_value = model_->Inference(features); if (VLOG_IS_ON(1)) { diff --git a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h index 6b023e3a2a9..7c2bcd20b1b 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h @@ -76,7 +76,6 @@ class COMPONENT_EXPORT(EVDEV) NeuralStylusPalmDetectionFilter std::vector<std::pair<float, int>>* biggest_strokes) const; bool DetectSpuriousStroke(const std::vector<float>& features, - int tracking_id, float threshold) const; // Extracts the feature vector for the specified stroke. std::vector<float> ExtractFeatures(int tracking_id) const; diff --git a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h index fdf858b96c6..c6c25888f11 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h @@ -74,6 +74,9 @@ struct COMPONENT_EXPORT(EVDEV) NeuralStylusPalmDetectionFilterModelConfig { // If true, uses current active tracking_id count as a feature. bool use_active_tracking_id_count = false; + // The model version (e.g. "alpha" for kohaku, "beta" for redrix) to use. + std::string model_version; + // If empty, the radius by the device is left as is. // If non empty, the radius reported by device is re-sized in features by the // polynomial defined in this vector. E.g. if this vector is {0.5, 1.3, diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc index dca26527bbc..a01d016e5d4 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc @@ -84,6 +84,28 @@ std::string FetchNeuralPalmRadiusPolynomial(const EventDeviceInfo& devinfo, // By default, return the original. return param_string; } + +std::string FetchNeuralPalmModelVersion(const EventDeviceInfo& devinfo, + const std::string param_string) { + if (!param_string.empty()) { + return param_string; + } + + // look at the command line. + absl::optional<base::Value> ozone_switch_value = base::JSONReader::Read( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + kOzoneNNPalmSwitchName)); + if (ozone_switch_value != absl::nullopt && ozone_switch_value->is_dict()) { + std::string* switch_string_value = + ozone_switch_value->FindStringKey(kOzoneNNPalmModelVersionProperty); + if (switch_string_value != nullptr) { + return *switch_string_value; + } + } + + // By default, return the original. + return param_string; +} } // namespace internal std::unique_ptr<PalmDetectionFilter> CreatePalmDetectionFilter( @@ -97,10 +119,12 @@ std::unique_ptr<PalmDetectionFilter> CreatePalmDetectionFilter( VLOG(1) << "Will attempt to use radius polynomial: " << polynomial_string; std::vector<float> radius_polynomial = internal::ParseRadiusPolynomial(polynomial_string); + std::string model_version = internal::FetchNeuralPalmModelVersion( + devinfo, kNeuralPalmModelVersion.Get()); // There's only one model right now. std::unique_ptr<NeuralStylusPalmDetectionFilterModel> model = std::make_unique<OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( - radius_polynomial); + model_version, radius_polynomial); return std::make_unique<NeuralStylusPalmDetectionFilter>( devinfo, std::move(model), shared_palm_state); } diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h index 8a9f6058b0c..6c64cc38faf 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h @@ -36,6 +36,12 @@ std::vector<float> ParseRadiusPolynomial(const std::string& radius_string); COMPONENT_EXPORT(EVDEV) std::string FetchNeuralPalmRadiusPolynomial(const EventDeviceInfo& devinfo, const std::string param_string); + +// Returns the model version to use on the current device. If empty will use +// alpha model. +COMPONENT_EXPORT(EVDEV) +std::string FetchNeuralPalmModelVersion(const EventDeviceInfo& devinfo, + const std::string param_string); } // namespace internal } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc index 053107fc3ed..451a149b921 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc @@ -6,6 +6,7 @@ #include <linux/input.h> +#include "base/command_line.h" #include "base/test/gtest_util.h" #include "base/test/scoped_chromeos_version_info.h" #include "base/test/scoped_feature_list.h" diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc index 97cc367e3ed..5bb81b253e8 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc @@ -14,7 +14,8 @@ #define USE_EIGEN 0 #endif -namespace ui { +namespace ui::internal_onedevice::alpha_model { + namespace { // ----------------------------------------------------------------------------- @@ -3878,7 +3879,6 @@ int32_t input_from_feature_columns_input_layer_concat_concat0Shape[2] = {1, 323}; int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1}; -namespace internal_onedevice { void Inference( const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,323 */ , @@ -3921,5 +3921,4 @@ void Inference( #endif } -} // namespace internal_onedevice -} // namespace ui +} // namespace ui::internal_onedevice::alpha_model diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h index 02bb87d20cb..601d4af9e7e 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h @@ -6,8 +6,7 @@ #define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_H_ #include <cstdint> -namespace ui { -namespace internal_onedevice { +namespace ui::internal_onedevice::alpha_model { struct alignas(16) FixedAllocations { float alloc0[20]; int32_t shape0[2]; @@ -36,6 +35,5 @@ void Inference( , FixedAllocations* __restrict fixed); -} // namespace internal_onedevice -} // namespace ui +} // namespace ui::internal_onedevice::alpha_model #endif // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_H_ diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.cc new file mode 100644 index 00000000000..8edd15bb7d9 --- /dev/null +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.cc @@ -0,0 +1,3936 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h" +#include <algorithm> +#include <cassert> +#include <cmath> +#include <cstdint> +#include <cstring> +#include <limits> +#include <tuple> + +#ifndef USE_EIGEN +#define USE_EIGEN 0 +#endif + +namespace ui::internal_onedevice::beta_model { +namespace { + +// ----------------------------------------------------------------------------- +// OP LIBRARY +// Copied here to make sure that the inference code always stays in sync with +// the lib that it was generated for. +// ----------------------------------------------------------------------------- + +// Default to using std::copy and std::fill over memcpy and memset as they +// are usually faster, thanks to the compiler getting stricter alignment +// guarantees. +#ifndef USE_TYPED_MEMSETMEMCPY +#define USE_TYPED_MEMSETMEMCPY 1 +#endif +#ifndef USE_EIGEN +#error Please define USE_EIGEN to either 0 or 1 +#endif + +// Helper to reinterpret memory as Eigen matrices. +#if USE_EIGEN +template <typename Scalar> +using ConstMatrixMap = typename Eigen::Map< + const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>; +template <typename Scalar> +using ConstRowVectorMap = + typename Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>>; +template <typename Scalar> +using RowVectorMap = + typename Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, 1>>; +template <typename Scalar> +using MatrixMap = + typename Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>; +template <typename Scalar, typename StorageIndex> +using SparseMatrix = Eigen::SparseMatrix<Scalar, Eigen::RowMajor, StorageIndex>; +#endif + +#if OP_LIB_BENCHMARK +class PerOpTimings { + public: + void Add(const std::string& op, absl::Duration time) { + time_per_op_[op] += time; + } + + void Reset() { time_per_op_.clear(); } + + void WriteTimingsToInfoLog() { + std::string message = "Per op totals:\n"; + absl::Duration total; + for (auto& entry : time_per_op_) { + total += entry.second; + absl::StrAppend( + &message, entry.first, ": ", + absl::LegacyPrecision(absl::ToDoubleMilliseconds(entry.second)), + " ms\n"); + } + absl::StrAppend(&message, "Total: ", + absl::LegacyPrecision(absl::ToDoubleMilliseconds(total)), + " ms\n"); + VLOG(1) << message; + } + + private: + std::map<std::string, absl::Duration> time_per_op_; +}; + +// Timer for individual operations. For each operation, add a statement like +// BENCHMARK_TIMER(name_part1, name_part2, ...); +// to the beginning of the code. All name parts will be concatenated together +// and a line will be logged after executing the operation showing the name and +// the elapsed time. +class BenchmarkTimer { + public: + explicit BenchmarkTimer(std::string name) + : name_(std::move(name)), start_(absl::Now()) {} + + ~BenchmarkTimer() { + const absl::Duration elapsed = absl::Now() - start_; + Singleton<PerOpTimings>::get()->Add(name_, elapsed); + VLOG(1) << "Time for " << name_ << ": " + << absl::ToDoubleMilliseconds(elapsed) << " ms"; + } + + private: + const std::string name_; + const absl::Time start_; +}; + +#define BENCHMARK_TIMER(...) BenchmarkTimer timer(absl::StrCat(__VA_ARGS__)); + +#else // OP_LIB_BENCHMARK +#define BENCHMARK_TIMER(...) +#endif // OP_LIB_BENCHMARK + +// The size of a shape in terms of number of coefficients. +inline int32_t ShapeSize(const int32_t rank, const int32_t* shape) { + int32_t size = 1; + for (int32_t i = 0; i < rank; ++i) + size *= shape[i]; + return size; +} + +// For convolutional operations, calculates the output size with VALID padding. +// Returns (height, width). +inline std::tuple<int, int> GetConvOutputSizeVALID(const int32_t* input_shape, + const int32_t* kernel_shape, + int32_t stride_y, + int32_t stride_x) { + return std::make_tuple( + (input_shape[1] + stride_y - kernel_shape[0]) / stride_y, + (input_shape[2] + stride_x - kernel_shape[1]) / stride_x); +} + +// For convolutional operations, calculates the output size with SAME padding. +// Returns (height, width). +inline std::tuple<int, int> GetConvOutputSizeSAME(const int32_t* input_shape, + int32_t stride_y, + int32_t stride_x) { + return std::make_tuple((input_shape[1] + stride_y - 1) / stride_y, + (input_shape[2] + stride_x - 1) / stride_x); +} + +// Helper to compute the size of the inner loop for an op that uses indices to +// specify which axes are reduced. +template <typename Tidx> +int32_t GetReduceInnerSize(int32_t input_tensor_rank, + const int32_t* __restrict input_shape, + int32_t index_tensor_rank, + const int32_t* __restrict index_shape, + const Tidx* __restrict index_values) { + assert(index_tensor_rank <= 1); + const int32_t num_indices = index_tensor_rank > 0 ? index_shape[0] : 1; + int32_t inner_size = 1; + for (int32_t i = 0; i < num_indices; ++i) { + Tidx index_value = index_values[i]; + if (index_value < 0) { + index_value = input_tensor_rank + index_value; + } + inner_size *= input_shape[index_value]; + } + return inner_size; +} + +template <typename T> +void ConcatV2Args2(int32_t arg0_rank, + const int32_t* __restrict arg0_shape, + const T* __restrict arg0_values, + int32_t arg1_rank, + const int32_t* __restrict arg1_shape, + const T* __restrict arg1_values, + const int32_t* __restrict axis_value, + T* __restrict output_values) { + BENCHMARK_TIMER("ConcatV2Args2"); + const int32_t axis = axis_value[0]; + const int32_t num_lines = ShapeSize(axis, arg0_shape); + const int32_t arg0_line_size = ShapeSize(arg0_rank - axis, arg0_shape + axis); + const int32_t arg1_line_size = ShapeSize(arg1_rank - axis, arg1_shape + axis); + for (int32_t line = 0; line < num_lines; ++line) { + std::copy(arg0_values, arg0_values + arg0_line_size, output_values); + arg0_values += arg0_line_size; + output_values += arg0_line_size; + std::copy(arg1_values, arg1_values + arg1_line_size, output_values); + arg1_values += arg1_line_size; + output_values += arg1_line_size; + } +} + +template <typename T> +void MatMul(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict weight_shape, + const T* __restrict weight_values, + T* __restrict output_values) { + BENCHMARK_TIMER("MatMul"); +#if USE_EIGEN + const auto in = + ConstMatrixMap<T>(input_values, input_shape[1], input_shape[0]); + const auto weight = + ConstMatrixMap<T>(weight_values, weight_shape[1], weight_shape[0]); + auto result = MatrixMap<T>(output_values, weight_shape[1], input_shape[0]); + result.noalias() = weight * in; +#else + const int32_t batch_size = input_shape[0]; + const int32_t num_inputs = weight_shape[0]; + const int32_t num_outputs = weight_shape[1]; + assert(input_shape[1] == num_inputs); + for (int32_t batch = 0; batch < batch_size; ++batch) { + for (int32_t out_i = 0; out_i < num_outputs; ++out_i) { + T value = 0; + for (int32_t in_i = 0; in_i < num_inputs; ++in_i) { + value += input_values[batch * num_inputs + in_i] * + weight_values[in_i * num_outputs + out_i]; + } + *output_values++ = value; + } + } +#endif +} + +template <typename T> +void DepthwiseConv2dNative(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + const T* __restrict kernel_values, + int32_t stride_y, + int32_t stride_x, + int32_t out_height, + int32_t out_width, + T* __restrict output_values) { + BENCHMARK_TIMER("DepthwiseConv2dNative"); + // Give the shape values nicer names. + assert(input_shape[3] == kernel_shape[2]); + const int32_t batch_size = input_shape[0]; + const int32_t kernel_height = kernel_shape[0]; + const int32_t kernel_width = kernel_shape[1]; + const int32_t in_depth = kernel_shape[2]; + const int32_t depth_mul = kernel_shape[3]; + const int32_t in_height = input_shape[1]; + const int32_t in_width = input_shape[2]; + + // Compute the amount of padding needed to get the desired output size. + const int32_t pad_height = + ((out_height - 1) * stride_y + kernel_height - in_height) / 2; + const int32_t pad_width = + ((out_width - 1) * stride_x + kernel_width - in_width) / 2; + + // Cache the strides for address computations. + const int32_t in_strides[4] = { + input_shape[1] * input_shape[2] * input_shape[3], // batch + input_shape[2] * input_shape[3], // y + input_shape[3], // x + 1, // channel + }; + const int32_t kernel_strides[4] = { + kernel_shape[1] * kernel_shape[2] * kernel_shape[3], // y + kernel_shape[2] * kernel_shape[3], // x + kernel_shape[3], // in channels + 1, // channel mult + }; + + T* out_write_ptr = output_values; + for (int32_t batch = 0; batch < batch_size; ++batch) { + for (int32_t out_y = 0; out_y < out_height; ++out_y) { + for (int32_t out_x = 0; out_x < out_width; ++out_x) { + // Compute the input read offsets. + const int32_t in_y_origin = (out_y * stride_y) - pad_height; + const int32_t in_x_origin = (out_x * stride_x) - pad_width; + + // Compute the range of the kernel to be applied (we may need to clip + // when we'd read outside of the valid input region - for SAME). + const int32_t kernel_y_start = + std::max(static_cast<int32_t>(0), -in_y_origin); + const int32_t kernel_y_end = + std::min(kernel_height, in_height - in_y_origin); + const int32_t kernel_x_start = + std::max(static_cast<int32_t>(0), -in_x_origin); + const int32_t kernel_x_end = + std::min(kernel_width, in_width - in_x_origin); + + for (int32_t in_c = 0; in_c < in_depth; ++in_c) { + for (int32_t mul_c = 0; mul_c < depth_mul; ++mul_c, ++out_write_ptr) { + // Convolve. + T sum = 0; + for (int32_t k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) { + const int32_t in_y = in_y_origin + k_y; + assert(in_y >= 0 && in_y < in_height); + for (int32_t k_x = kernel_x_start; k_x < kernel_x_end; ++k_x) { + const int32_t in_x = in_x_origin + k_x; + assert(in_x >= 0 && in_x < in_width); + const T input_value = + input_values[batch * in_strides[0] + // batch + in_y * in_strides[1] + // y + in_x * in_strides[2] + // x + in_c]; // in chan + const T kernel_value = + kernel_values[k_y * kernel_strides[0] + // y + k_x * kernel_strides[1] + // x + in_c * kernel_strides[2] + // in chan + mul_c]; // chan mult + sum += input_value * kernel_value; + } + } + *out_write_ptr = sum; + } // mul_c + } // in_c + } // out_x + } // out_y + } // batch +} + +template <typename T> +void DepthwiseConv2dNativeVALID(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + const T* __restrict kernel_values, + int32_t stride_y, + int32_t stride_x, + T* __restrict output_values) { + const auto out_size = + GetConvOutputSizeVALID(input_shape, kernel_shape, stride_y, stride_x); + DepthwiseConv2dNative<T>( + input_shape, input_values, kernel_shape, kernel_values, stride_y, + stride_x, std::get<0>(out_size), std::get<1>(out_size), output_values); +} + +template <typename T> +void DepthwiseConv2dNativeSAME(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + const T* __restrict kernel_values, + int32_t stride_y, + int32_t stride_x, + T* __restrict output_values) { + const auto out_size = GetConvOutputSizeSAME(input_shape, stride_y, stride_x); + DepthwiseConv2dNative<T>( + input_shape, input_values, kernel_shape, kernel_values, stride_y, + stride_x, std::get<0>(out_size), std::get<1>(out_size), output_values); +} + +template <typename T> +void FullyConnected(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict weight_shape, + const T* __restrict weight_values, + const int32_t* __restrict bias_shape, + const T* __restrict bias_values, + T* __restrict output_values) { + BENCHMARK_TIMER("FullyConnected"); +#if USE_EIGEN + const auto in = + ConstMatrixMap<T>(input_values, input_shape[1], input_shape[0]); + const auto weight = + ConstMatrixMap<T>(weight_values, weight_shape[1], weight_shape[0]); + const auto bias = ConstRowVectorMap<T>(bias_values, bias_shape[0]); + auto result = MatrixMap<T>(output_values, weight_shape[1], input_shape[0]); + result.noalias() = (weight * in).colwise() + bias; +#else + const int32_t batch_size = input_shape[0]; + const int32_t num_inputs = weight_shape[0]; + const int32_t num_outputs = weight_shape[1]; + assert(input_shape[1] == num_inputs); + assert(bias_shape[0] == num_outputs); + for (int32_t batch = 0; batch < batch_size; ++batch) { + for (int32_t out_i = 0; out_i < num_outputs; ++out_i) { + T value = bias_values[out_i]; + for (int32_t in_i = 0; in_i < num_inputs; ++in_i) { + value += input_values[batch * num_inputs + in_i] * + weight_values[in_i * num_outputs + out_i]; + } + output_values[batch * num_outputs + out_i] = value; + } + } +#endif +} + +template <typename T, typename TBlocks, typename TPaddings> +void SpaceToBatchNDRank4(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const TBlocks* __restrict block_shape_values, + const TPaddings* __restrict padding_values, + T* __restrict output_values) { + BENCHMARK_TIMER("SpaceToBatchNDRank4"); + const int32_t input_batch_size = input_shape[0]; + const int32_t input_height = input_shape[1]; + const int32_t input_width = input_shape[2]; + const int32_t input_depth = input_shape[3]; + + const TBlocks block_shape_height = block_shape_values[0]; + const TBlocks block_shape_width = block_shape_values[1]; + const TPaddings padding_top = padding_values[0]; + const TPaddings padding_bottom = padding_values[1]; + const TPaddings padding_left = padding_values[2]; + const TPaddings padding_right = padding_values[3]; + + const int32_t block_size = block_shape_height * block_shape_width; + const int32_t output_depth = input_depth; + const int32_t output_batch_size = input_batch_size * block_size; + const int32_t output_height = + (padding_top + padding_bottom + input_height) / block_shape_height; + const int32_t output_width = + (padding_left + padding_right + input_width) / block_shape_width; + + const T pad_value = 0; + + for (int32_t out_b = 0; out_b < output_batch_size; ++out_b) { + const int32_t input_batch = out_b % input_batch_size; + const int32_t shift_w = (out_b / input_batch_size) % block_shape_width; + const int32_t shift_h = (out_b / input_batch_size) / block_shape_width; + for (int32_t out_h = 0; out_h < output_height; ++out_h) { + for (int32_t out_w = 0; out_w < output_width; ++out_w) { + T* out = output_values + + (((out_b * output_height + out_h) * output_width + out_w) * + output_depth + + 0); + // Check if padding cell are being handled. + if (out_h * block_shape_height + shift_h < padding_top || + out_h * block_shape_height + shift_h >= + padding_top + input_height || + out_w * block_shape_width + shift_w < padding_left || + out_w * block_shape_width + shift_w >= padding_left + input_width) { +// This may not execute correctly when pad_value != 0 and T != uint8. +#if USE_TYPED_MEMSETMEMCPY + std::fill(out, out + input_depth, pad_value); +#else + std::memset(out, pad_value, input_depth * sizeof(T)); +#endif + } else { + const int32_t i0 = input_batch; + const int32_t i1 = + (out_h * block_shape_height + shift_h) - padding_top; + const int32_t i2 = + (out_w * block_shape_width + shift_w) - padding_left; + const T* in = + input_values + + (((i0 * input_height + i1) * input_width + i2) * input_depth + 0); +#if USE_TYPED_MEMSETMEMCPY + std::copy(in, in + input_depth, out); +#else + std::memcpy(out, in, input_depth * sizeof(T)); +#endif + } + } + } + } +} + +template <typename T, typename TBlocks, typename TCrops> +void BatchToSpaceNDRank4(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const TBlocks* __restrict block_shape_values, + const TCrops* __restrict crops_values, + T* __restrict output_values) { + BENCHMARK_TIMER("BatchToSpaceNDRank4"); + const int32_t input_batch_size = input_shape[0]; + const int32_t input_height = input_shape[1]; + const int32_t input_width = input_shape[2]; + const int32_t input_depth = input_shape[3]; + const TBlocks block_shape_height = block_shape_values[0]; + const TBlocks block_shape_width = block_shape_values[1]; + const TCrops crops_top = crops_values[0]; + const TCrops crops_bottom = crops_values[1]; + const TCrops crops_left = crops_values[2]; + const TCrops crops_right = crops_values[3]; + + const int32_t output_depth = input_depth; + const int32_t output_batch_size = + input_batch_size / (block_shape_width * block_shape_height); + const int32_t output_height = + input_height * block_shape_height - crops_top - crops_bottom; + const int32_t output_width = + input_width * block_shape_width - crops_left - crops_right; + + for (int32_t in_batch = 0; in_batch < input_batch_size; ++in_batch) { + const int32_t out_batch = in_batch % output_batch_size; + const int32_t spatial_offset = in_batch / output_batch_size; + for (int32_t in_h = 0; in_h < input_height; ++in_h) { + const int32_t out_h = in_h * block_shape_height + + spatial_offset / block_shape_width - crops_top; + if (out_h < 0 || out_h >= output_height) { + continue; + } + for (int32_t in_w = 0; in_w < input_width; ++in_w) { + const int32_t out_w = in_w * block_shape_width + + spatial_offset % block_shape_width - crops_left; + + if (out_w < 0 || out_w >= output_width) { + continue; + } + T* out = output_values + + (((out_batch * output_height + out_h) * output_width + out_w) * + output_depth + + 0); + const T* in = input_values + + (((in_batch * input_height + in_h) * input_width + in_w) * + input_depth + + 0); +#if USE_TYPED_MEMSETMEMCPY + std::copy(in, in + input_depth, out); +#else + std::memcpy(out, in, input_depth * sizeof(T)); +#endif + } + } + } +} + +#if USE_EIGEN +template <typename T, typename Tidx> +void SparseDenseMatMulCSR(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t num_rows, + const int32_t* __restrict nnz_shape, + const T* __restrict nnz_values, + const Tidx* __restrict outer_index, + const Tidx* __restrict cols, + T* __restrict output_values) { + BENCHMARK_TIMER("SparseDenseMatMulCSR"); + const int32_t num_cols = input_shape[1]; + const auto in = + ConstMatrixMap<T>(input_values, input_shape[1], input_shape[0]); + const Eigen::Map<const SparseMatrix<T, Tidx>> weight( + num_rows, num_cols, nnz_shape[0], outer_index, cols, nnz_values); + auto result = MatrixMap<T>(output_values, num_rows, input_shape[0]); + result.noalias() = weight * in; +} + +template <typename T, typename Tidx> +void SparseFullyConnectedCSR(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t num_rows, + const int32_t* __restrict nnz_shape, + const T* __restrict nnz_values, + const Tidx* __restrict outer_index, + const Tidx* __restrict cols, + const int32_t* __restrict bias_shape, + const T* __restrict bias_values, + T* __restrict output_values) { + BENCHMARK_TIMER("SparseFullyConnectedCSR"); + const int32_t num_cols = input_shape[1]; + const auto in = + ConstMatrixMap<T>(input_values, input_shape[1], input_shape[0]); + const auto bias = ConstRowVectorMap<T>(bias_values, bias_shape[0]); + const Eigen::Map<const SparseMatrix<T, Tidx>> weight( + num_rows, num_cols, nnz_shape[0], outer_index, cols, nnz_values); + auto result = MatrixMap<T>(output_values, num_rows, input_shape[0]); + result.noalias() = (weight * in).colwise() + bias; +} +#endif + +template <typename T, typename TIndex> +void Gather(int32_t params_rank, + const int32_t* __restrict params_shape, + const T* __restrict params_values, + int32_t indices_rank, + const int32_t* __restrict indices_shape, + const TIndex* __restrict indices_values, + T* __restrict output_values) { + BENCHMARK_TIMER("Gather"); + const int32_t num_indices = ShapeSize(indices_rank, indices_shape); + const int32_t num_params = params_shape[0]; + const int32_t slice_size = ShapeSize(params_rank - 1, params_shape + 1); + for (int32_t i = 0; i < num_indices; ++i) { + const int32_t index = indices_values[i]; + if (index < 0 || index >= num_params) { + std::fill(output_values, output_values + slice_size, 0); + } else { + std::copy(params_values + index * slice_size, + params_values + index * slice_size + slice_size, output_values); + } + output_values += slice_size; + } +} + +template <typename T> +void Im2Row(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + int32_t stride_y, + int32_t stride_x, + int32_t out_height, + int32_t out_width, + T* __restrict output_values) { + BENCHMARK_TIMER("Im2Row"); + // Give the shape values nicer names. + assert(input_shape[3] == kernel_shape[2]); + const int32_t batch_size = input_shape[0]; + const int32_t kernel_height = kernel_shape[0]; + const int32_t kernel_width = kernel_shape[1]; + const int32_t in_depth = kernel_shape[2]; + const int32_t in_height = input_shape[1]; + const int32_t in_width = input_shape[2]; + + // Compute the amount of padding needed to get the desired output size. + const int32_t pad_height = + ((out_height - 1) * stride_y + kernel_height - in_height) / 2; + const int32_t pad_width = + ((out_width - 1) * stride_x + kernel_width - in_width) / 2; + + // Cache the strides for address computations. + const int32_t x_stride = input_shape[3]; + const int32_t y_stride = input_shape[2] * x_stride; + const int32_t batch_stride = input_shape[1] * y_stride; + + for (int32_t batch = 0; batch < batch_size; ++batch) { + for (int32_t out_y = 0; out_y < out_height; ++out_y) { + for (int32_t out_x = 0; out_x < out_width; ++out_x) { + // Compute the input read offsets. + const int32_t in_y_origin = (out_y * stride_y) - pad_height; + const int32_t in_x_origin = (out_x * stride_x) - pad_width; + + // Compute the range of the kernel to be applied (we may need to clip + // when we'd read outside of the valid input region - for SAME). + const int32_t kernel_y_start = + std::max(static_cast<int32_t>(0), -in_y_origin); + const int32_t kernel_y_end = + std::min(kernel_height, in_height - in_y_origin); + const int32_t kernel_x_start = + std::max(static_cast<int32_t>(0), -in_x_origin); + const int32_t kernel_x_end = + std::min(kernel_width, in_width - in_x_origin); + + // Padding top. + if (kernel_y_start != 0) { + const int32_t num_lines = kernel_y_start; + const int32_t num_coeffs = num_lines * kernel_width * in_depth; +#if USE_TYPED_MEMSETMEMCPY + std::fill(output_values, output_values + num_coeffs, 0); +#else + std::memset(output_values, 0, num_coeffs * sizeof(T)); +#endif + output_values += num_coeffs; + } + for (int32_t k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) { + // Padding left. + if (kernel_x_start != 0) { + const int32_t num_coeffs = kernel_x_start * in_depth; +#if USE_TYPED_MEMSETMEMCPY + std::fill(output_values, output_values + num_coeffs, 0); +#else + std::memset(output_values, 0, num_coeffs * sizeof(T)); +#endif + output_values += num_coeffs; + } + // Valid values. + { + const int32_t in_y = in_y_origin + k_y; + const int32_t in_x = in_x_origin + kernel_x_start; + const int32_t num_coeffs = + (kernel_x_end - kernel_x_start) * in_depth; +#if USE_TYPED_MEMSETMEMCPY + const int32_t offset = + batch * batch_stride + in_y * y_stride + in_x * x_stride; + std::copy(input_values + offset, input_values + offset + num_coeffs, + output_values); +#else + std::memcpy(output_values, + input_values // Reusing the restricted pointer. + + batch * batch_stride // batch + + in_y * y_stride // y + + in_x * x_stride, // x + num_coeffs * sizeof(T)); +#endif + output_values += num_coeffs; + } + // Padding right. + if (kernel_x_end != kernel_width) { + const int32_t num_coeffs = (kernel_width - kernel_x_end) * in_depth; +#if USE_TYPED_MEMSETMEMCPY + std::fill(output_values, output_values + num_coeffs, 0); +#else + std::memset(output_values, 0, num_coeffs * sizeof(T)); +#endif + output_values += num_coeffs; + } + } + // Padding bottom. + if (kernel_y_end != kernel_height) { + const int32_t num_lines = kernel_height - kernel_y_end; + const int32_t num_coeffs = num_lines * kernel_width * in_depth; +#if USE_TYPED_MEMSETMEMCPY + std::fill(output_values, output_values + num_coeffs, 0); +#else + std::memset(output_values, 0, num_coeffs * sizeof(T)); +#endif + output_values += num_coeffs; + } + } + } + } +} + +template <typename T> +void Im2RowVALID(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + int32_t stride_y, + int32_t stride_x, + T* __restrict output_values) { + const auto out_size = + GetConvOutputSizeVALID(input_shape, kernel_shape, stride_y, stride_x); + Im2Row<T>(input_shape, input_values, kernel_shape, stride_y, stride_x, + std::get<0>(out_size), std::get<1>(out_size), output_values); +} + +template <typename T> +void Im2RowSAME(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict kernel_shape, + int32_t stride_y, + int32_t stride_x, + T* __restrict output_values) { + const auto out_size = GetConvOutputSizeSAME(input_shape, stride_y, stride_x); + Im2Row<T>(input_shape, input_values, kernel_shape, stride_y, stride_x, + std::get<0>(out_size), std::get<1>(out_size), output_values); +} + +// We use macros instead of template functions with templated functors here +// because it's a lot less verbose and easier for the compiler to optimize. +#define POOL_OP(OP_NAME, DEFAULT_VALUE, UPDATE_EXPR, RESULT_EXPR) \ + template <typename T> \ + void OP_NAME##Pool(const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t stride_y, \ + int32_t stride_x, int32_t kernel_height, \ + int32_t kernel_width, int32_t out_height, \ + int32_t out_width, T* __restrict output_values) { \ + BENCHMARK_TIMER(#OP_NAME, "Pool"); \ + const int32_t batch_size = input_shape[0]; \ + const int32_t in_height = input_shape[1]; \ + const int32_t in_width = input_shape[2]; \ + const int32_t depth = input_shape[3]; \ + \ + const int32_t pad_height = \ + ((out_height - 1) * stride_y + kernel_height - in_height) / 2; \ + const int32_t pad_width = \ + ((out_width - 1) * stride_x + kernel_width - in_width) / 2; \ + \ + const int32_t in_strides[4] = { \ + input_shape[1] * input_shape[2] * input_shape[3], \ + input_shape[2] * input_shape[3], \ + input_shape[3], \ + 1, \ + }; \ + \ + T* out_write_ptr = output_values; \ + for (int32_t batch = 0; batch < batch_size; ++batch) { \ + for (int32_t out_y = 0; out_y < out_height; ++out_y) { \ + for (int32_t out_x = 0; out_x < out_width; ++out_x) { \ + const int32_t in_y_origin = (out_y * stride_y) - pad_height; \ + const int32_t in_x_origin = (out_x * stride_x) - pad_width; \ + const int32_t kernel_y_start = \ + std::max(static_cast<int32_t>(0), -in_y_origin); \ + const int32_t kernel_y_end = \ + std::min(kernel_height, in_height - in_y_origin); \ + const int32_t kernel_x_start = \ + std::max(static_cast<int32_t>(0), -in_x_origin); \ + const int32_t kernel_x_end = \ + std::min(kernel_width, in_width - in_x_origin); \ + const int32_t count = (kernel_y_end - kernel_y_start) * \ + (kernel_x_end - kernel_x_start); \ + (void)sizeof(count); \ + \ + for (int32_t chan = 0; chan < depth; ++chan, ++out_write_ptr) { \ + T value = DEFAULT_VALUE; \ + for (int32_t k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) { \ + const int32_t in_y = in_y_origin + k_y; \ + assert(in_y >= 0 && in_y < in_height); \ + for (int32_t k_x = kernel_x_start; k_x < kernel_x_end; ++k_x) { \ + const int32_t in_x = in_x_origin + k_x; \ + assert(in_x >= 0 && in_x < in_width); \ + const T next = input_values[batch * in_strides[0] + \ + in_y * in_strides[1] + \ + in_x * in_strides[2] + chan]; \ + value = UPDATE_EXPR; \ + } \ + } \ + *out_write_ptr = RESULT_EXPR; \ + } \ + } \ + } \ + } \ + } \ + \ + template <typename T> \ + void OP_NAME##PoolVALID(const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t stride_y, \ + int32_t stride_x, int32_t kernel_height, \ + int32_t kernel_width, T* __restrict output_values) { \ + const int32_t kernel_shape[4] = {kernel_height, kernel_width, 1, 1}; \ + const auto out_size = \ + GetConvOutputSizeVALID(input_shape, kernel_shape, stride_y, stride_x); \ + OP_NAME##Pool<T>(input_shape, input_values, stride_y, stride_x, \ + kernel_height, kernel_width, std::get<0>(out_size), \ + std::get<1>(out_size), output_values); \ + } \ + \ + template <typename T> \ + void OP_NAME##PoolSAME(const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t stride_y, \ + int32_t stride_x, int32_t kernel_height, \ + int32_t kernel_width, T* __restrict output_values) { \ + const auto out_size = \ + GetConvOutputSizeSAME(input_shape, stride_y, stride_x); \ + OP_NAME##Pool<T>(input_shape, input_values, stride_y, stride_x, \ + kernel_height, kernel_width, std::get<0>(out_size), \ + std::get<1>(out_size), output_values); \ + } + +POOL_OP(Max, std::numeric_limits<T>::lowest(), std::max(value, next), value) +POOL_OP(Avg, 0, value + next, value / count) + +template <typename T> +void Memcpy(const int32_t rank, + const int32_t* __restrict input_shape, + const T* __restrict input_values, + T* __restrict output_values) { + BENCHMARK_TIMER("Memcpy"); + const int32_t size = ShapeSize(rank, input_shape); + for (int32_t i = 0; i < size; ++i) { + output_values[i] = input_values[i]; + } +} + +template <typename T> +void Softmax(const int32_t rank, + const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t reduce_dim, + T* __restrict output_values, + T* __restrict scratch_values) { + BENCHMARK_TIMER("Softmax"); + const int32_t size = ShapeSize(rank, input_shape); + if (rank == 2 && reduce_dim == 1) { + T logits_max = std::numeric_limits<T>::lowest(); + + // Max. + for (int32_t i = 0; i < size; ++i) { + logits_max = std::max(logits_max, input_values[i]); + } + + // Pre-compute exp. + for (int32_t i = 0; i < size; ++i) { + scratch_values[i] = std::exp(input_values[i] - logits_max); + } + + // Sum over the last dimension, then divide the exps and write out. + for (int32_t offset = 0; offset < size; offset += input_shape[1]) { + const int32_t end_offset = offset + input_shape[1]; + T sum = 0; + for (int32_t i = offset; i < end_offset; ++i) { + sum += scratch_values[i]; + } + const T rcp_denom = static_cast<T>(1) / sum; + for (int32_t i = 0; i < input_shape[1]; ++i) { + output_values[offset + i] = scratch_values[offset + i] * rcp_denom; + } + } + } else { + assert(false && "Generic Softmax not yet supported."); + } +} + +// Returns the start position for a slice in a single dimension. +template <typename T> +int32_t StridedSliceBegin(int32_t range_mask, + const T* __restrict range_values, + const T* __restrict strides, + const int32_t* __restrict input_shape, + int32_t dim) { + const bool is_explicit = 0 == (range_mask & (1 << dim)); + if (is_explicit) { + const T range_value = range_values[dim]; + return (range_value < 0 ? range_value + input_shape[dim] : range_value); + } else { + const bool is_reverse = strides[dim] < 0; + return is_reverse ? input_shape[dim] - 1 : 0; + } +} + +// Returns the end position for a slice in a single dimension. +template <typename T> +int32_t StridedSliceEnd(int32_t range_mask, + const T* __restrict range_values, + const T* __restrict strides, + const int32_t* __restrict input_shape, + int32_t dim) { + const bool is_explicit = 0 == (range_mask & (1 << dim)); + if (is_explicit) { + const T range_value = range_values[dim]; + return (range_value < 0 ? range_value + input_shape[dim] : range_value); + } else { + const bool is_reverse = strides[dim] < 0; + return is_reverse ? -1 : input_shape[dim]; + } +} + +template <typename T, typename TIdx> +void StridedSlice(const int32_t input_rank, + const int32_t* __restrict input_shape, + const T* __restrict input_values, + const TIdx* __restrict begin, + const TIdx* __restrict end, + const TIdx* __restrict strides, + int32_t begin_mask, + int32_t end_mask, + T* __restrict output_values) { + BENCHMARK_TIMER("StridedSlice"); + const int32_t MAX_RANK = 8; + assert(input_rank < MAX_RANK); + + // Compute the address strides for each dimension. + int32_t dim_addr_strides[MAX_RANK] = {0}; + dim_addr_strides[input_rank - 1] = 1; + for (int32_t dim = input_rank - 2; dim >= 0; --dim) { + dim_addr_strides[dim] = dim_addr_strides[dim + 1] * input_shape[dim + 1]; + } + + // Resolve the masks and get explicit ranges for each dimension. + int32_t dim_begin[MAX_RANK]; + int32_t dim_end[MAX_RANK]; + bool dim_is_full_range[MAX_RANK]; + for (int32_t dim = 0; dim < input_rank; ++dim) { + const int32_t stride = strides[dim]; + dim_begin[dim] = + StridedSliceBegin(begin_mask, begin, strides, input_shape, dim); + dim_end[dim] = StridedSliceEnd(end_mask, end, strides, input_shape, dim); + dim_is_full_range[dim] = + dim_begin[dim] == 0 && dim_end[dim] == input_shape[dim] && stride == 1; + // Make sure that the dim_end is always bigger than dim_begin, this + // simplifies the boundary checks below. + if (stride > 0 && dim_begin[dim] > dim_end[dim]) { + dim_end[dim] += input_shape[dim]; + } + + // Our termination criteria for loops is that we hit the end exactly, so + // we need to ensure that we don't step over the end with stride != 1. + const int32_t length_mod = (dim_end[dim] - dim_begin[dim]) % stride; + if (length_mod != 0) { + dim_end[dim] += stride - length_mod; + } + } + + // Find out how large the blocks are that we can copy contiguously. (All + // dimensions on the right for which we fetch the full range) + int32_t last_sliced_dim = input_rank - 1; + int32_t block_size = 1; + for (int32_t dim = input_rank - 1; dim >= 0 && dim_is_full_range[dim]; + --dim) { + block_size *= input_shape[dim]; + last_sliced_dim--; + } + + // Initialize the read pos for each dimension according to the begin offsets. + int32_t read_pos[MAX_RANK] = {0}; + for (int32_t dim = 0; dim < input_rank; ++dim) { + read_pos[dim] = dim_begin[dim]; + } + + while (read_pos[0] != dim_end[0]) { + // Compute the read offset for the current position. + int32_t read_offset = 0; + for (int32_t dim = 0; dim <= last_sliced_dim; ++dim) { + read_offset += (read_pos[dim] % input_shape[dim]) * dim_addr_strides[dim]; + } + +#if USE_TYPED_MEMSETMEMCPY + std::copy(input_values + read_offset, + input_values + read_offset + block_size, output_values); +#else + std::memcpy(output_values, input_values + read_offset, + block_size * sizeof(T)); +#endif + output_values += block_size; + + // Advance the read position. + for (int32_t dim = last_sliced_dim; dim >= 0; --dim) { + read_pos[dim] += strides[dim]; + if (dim == 0 || read_pos[dim] != dim_end[dim]) + break; + read_pos[dim] = dim_begin[dim]; + } + } +} + +template <typename T> +void TransposeRank3(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict perm, + T* __restrict output_values) { + BENCHMARK_TIMER("TransposeRank3"); + const int32_t in_strides[3] = { + input_shape[1] * input_shape[2], + input_shape[2], + 1, + }; + const int32_t out_strides[3] = {in_strides[perm[0]], in_strides[perm[1]], + in_strides[perm[2]]}; + const int32_t out_shape[3] = {input_shape[perm[0]], input_shape[perm[1]], + input_shape[perm[2]]}; + + int32_t write_offset = 0; + for (int32_t it0 = 0; it0 < out_shape[0]; ++it0) { + const int32_t read_offset0 = it0 * out_strides[0]; + for (int32_t it1 = 0; it1 < out_shape[1]; ++it1) { + const int32_t read_offset01 = read_offset0 + it1 * out_strides[1]; + for (int32_t it2 = 0; it2 < out_shape[2]; ++it2, ++write_offset) { + const int32_t read_offset = read_offset01 + it2 * out_strides[2]; + output_values[write_offset] = input_values[read_offset]; + } + } + } +} + +template <typename T> +void TransposeRank4(const int32_t* __restrict input_shape, + const T* __restrict input_values, + const int32_t* __restrict perm, + T* __restrict output_values) { + BENCHMARK_TIMER("TransposeRank4"); + const int32_t in_strides[4] = { + input_shape[1] * input_shape[2] * input_shape[3], + input_shape[2] * input_shape[3], + input_shape[3], + 1, + }; + const int32_t out_strides[4] = {in_strides[perm[0]], in_strides[perm[1]], + in_strides[perm[2]], in_strides[perm[3]]}; + const int32_t out_shape[4] = {input_shape[perm[0]], input_shape[perm[1]], + input_shape[perm[2]], input_shape[perm[3]]}; + + int32_t write_offset = 0; + for (int32_t it0 = 0; it0 < out_shape[0]; ++it0) { + const int32_t read_offset0 = it0 * out_strides[0]; + for (int32_t it1 = 0; it1 < out_shape[1]; ++it1) { + const int32_t read_offset01 = read_offset0 + it1 * out_strides[1]; + for (int32_t it2 = 0; it2 < out_shape[2]; ++it2) { + const int32_t read_offset012 = read_offset01 + it2 * out_strides[2]; + for (int32_t it3 = 0; it3 < out_shape[3]; ++it3, ++write_offset) { + const int32_t read_offset = read_offset012 + it3 * out_strides[3]; + output_values[write_offset] = input_values[read_offset]; + } + } + } + } +} + +template <typename T, typename TIdx, typename TDepth> +void OneHot(const int32_t input_rank, + const int32_t* __restrict input_shape, + const TIdx* __restrict input_values, + const TDepth* __restrict depth, + const T* __restrict on_value, + const T* __restrict off_value, + const int32_t axis, + T* __restrict output_values) { + BENCHMARK_TIMER("OneHot"); + const int32_t num_elements = ShapeSize(input_rank, input_shape); + // We can assume axis >= 0 in this implementation. + const int32_t prefix_dim_size = ShapeSize(axis, input_shape); + const int32_t suffix_dim_size = num_elements / prefix_dim_size; + int32_t write_offset = 0; + for (int32_t i = 0; i < prefix_dim_size; ++i) { + const int32_t read_offset_pre = i * suffix_dim_size; + for (TDepth d = 0; d < *depth; ++d) { + for (int32_t j = 0; j < suffix_dim_size; ++j, ++write_offset) { + const int32_t read_offset = read_offset_pre + j; + output_values[write_offset] = + (input_values[read_offset] == d) ? *on_value : *off_value; + } + } + } +} + +template <typename T, typename TIdx, typename TDepth> +void OneHotLastDim(const int32_t input_rank, + const int32_t* __restrict input_shape, + const TIdx* __restrict input_values, + const TDepth* __restrict depth, + const T* __restrict on_value, + const T* __restrict off_value, + T* __restrict output_values) { + BENCHMARK_TIMER("OneHotLastDim"); + const int32_t num_elements = ShapeSize(input_rank, input_shape); + int32_t write_offset = 0; + for (int32_t i = 0; i < num_elements; ++i) { + for (TDepth d = 0; d < *depth; ++d, ++write_offset) { + output_values[write_offset] = + (input_values[i] == d) ? *on_value : *off_value; + } + } +} + +// ----------------------------------------------------------------------------- +// Simple unary ops +// ----------------------------------------------------------------------------- + +// We use macros instead of template functions with templated functors here +// because it's a lot less verbose and easier for the compiler to optimize. + +#if USE_EIGEN + +#define SIMPLE_UNARY_OP(OP_NAME, _, EXPR_EIGEN) \ + template <typename T> \ + void OP_NAME(const int32_t rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, \ + T* __restrict output_values) { \ + BENCHMARK_TIMER(#OP_NAME); \ + const int32_t size = ShapeSize(rank, input_shape); \ + auto values = ConstRowVectorMap<T>(input_values, size).array(); \ + auto output = RowVectorMap<T>(output_values, size).array(); \ + output = EXPR_EIGEN; \ + } + +#else + +#define SIMPLE_UNARY_OP(OP_NAME, EXPR, _) \ + template <typename T> \ + void OP_NAME(const int32_t rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, \ + T* __restrict output_values) { \ + BENCHMARK_TIMER(#OP_NAME); \ + const int32_t size = ShapeSize(rank, input_shape); \ + for (int32_t i = 0; i < size; ++i) { \ + const T value = input_values[i]; \ + output_values[i] = EXPR; \ + } \ + } + +#endif + +// Second macro param is value expression, third entry is Eigen vector +// expression. +SIMPLE_UNARY_OP(Abs, std::abs(value), values.abs()) +SIMPLE_UNARY_OP(Acos, std::acos(value), values.acos()) +SIMPLE_UNARY_OP(Asin, std::asin(value), values.asin()) +SIMPLE_UNARY_OP(Atan, std::atan(value), values.atan()) +SIMPLE_UNARY_OP(Cos, std::cos(value), values.cos()) +SIMPLE_UNARY_OP(Cosh, std::cosh(value), values.cosh()) +SIMPLE_UNARY_OP(Exp, std::exp(value), values.exp()) +SIMPLE_UNARY_OP(Elu, + value < 0 ? std::expm1(value) : value, + // Use branchless version of Elu: min(ReLU, e^x - 1) + values.max(0).min(values.exp() - 1)) +SIMPLE_UNARY_OP(HardSigmoid, + std::min(std::max((static_cast<T>(0.2) * value + + static_cast<T>(0.5)), + static_cast<T>(0)), + static_cast<T>(1)), + (0.2 * values + 0.5).max(0).min(1)) +SIMPLE_UNARY_OP(Log, std::log(value), values.log()) +SIMPLE_UNARY_OP(Log1p, std::log1p(value), values.log1p()) +SIMPLE_UNARY_OP(Neg, -value, -values) +SIMPLE_UNARY_OP(Reciprocal, static_cast<T>(1) / value, values.cwiseInverse()) +SIMPLE_UNARY_OP(Relu, std::max(value, static_cast<T>(0)), values.max(0)) +SIMPLE_UNARY_OP(Relu6, + std::min(std::max(value, static_cast<T>(0)), static_cast<T>(6)), + values.max(0).min(6)) +SIMPLE_UNARY_OP(Rsqrt, static_cast<T>(1) / std::sqrt(value), values.rsqrt()) +SIMPLE_UNARY_OP(Sigmoid, + static_cast<T>(1) / (1 + std::exp(-value)), + ((-values).exp() + 1).cwiseInverse()) +SIMPLE_UNARY_OP(Sin, std::sin(value), values.sin()) +SIMPLE_UNARY_OP(Sinh, std::sinh(value), values.sinh()) +SIMPLE_UNARY_OP(Sqrt, std::sqrt(value), values.sqrt()) +SIMPLE_UNARY_OP(Square, value* value, values.square()) +SIMPLE_UNARY_OP(Tan, std::tan(value), values.tan()) +SIMPLE_UNARY_OP(Tanh, std::tanh(value), values.tanh()) + +// ----------------------------------------------------------------------------- +// Broadcasting binary ops +// ----------------------------------------------------------------------------- + +template <typename T, typename OP> +void OpNoBroadcast(const int32_t left_rank, + const int32_t* __restrict left_shape, + const T* __restrict left_values, + const int32_t right_rank, + const int32_t* __restrict right_shape, + const T* __restrict right_values, + T* __restrict output_values, + OP op) { + BENCHMARK_TIMER(op.name, "NoBroadcast"); + const int32_t size = ShapeSize(left_rank, left_shape); +#if USE_EIGEN + auto lhs = ConstRowVectorMap<T>(left_values, size).array(); + auto rhs = ConstRowVectorMap<T>(right_values, size).array(); + auto output = RowVectorMap<T>(output_values, size).array(); + op.apply(lhs, rhs, output); +#else + for (int32_t i = 0; i < size; ++i) { + output_values[i] = op(left_values[i], right_values[i]); + } +#endif +} + +template <typename T, typename OP> +void OpInnerBroadcast(int32_t left_rank, + const int32_t* __restrict left_shape, + const T* __restrict left_values, + int32_t right_rank, + const int32_t* __restrict right_shape, + const T* __restrict right_values, + T* __restrict output_values, + OP op) { + BENCHMARK_TIMER(op.name, "InnerBroadcast"); + const int32_t output_size = ShapeSize(left_rank, left_shape); + const int32_t inner_size = ShapeSize(right_rank, right_shape); + const int32_t outer_size = output_size / inner_size; +#if USE_EIGEN + if (inner_size == 1) { + // Apply the same value to all elements. + auto left = ConstMatrixMap<T>(left_values, inner_size, outer_size); + auto output = MatrixMap<T>(output_values, inner_size, outer_size); + op.apply(left.array(), right_values[0], output.array()); + } else { + auto left = ConstMatrixMap<T>(left_values, inner_size, outer_size); + auto right = ConstRowVectorMap<T>(right_values, inner_size); + auto output = MatrixMap<T>(output_values, inner_size, outer_size); + for (int32_t col = 0; col < outer_size; col++) { + op.apply(left.col(col).array(), right.array(), output.col(col).array()); + } + } +#else + for (int32_t idx_out = 0; idx_out < outer_size; ++idx_out) { + for (int32_t idx_in = 0; idx_in < inner_size; ++idx_in) { + const int32_t offset = idx_out * inner_size + idx_in; + output_values[offset] = op(left_values[offset], right_values[idx_in]); + } + } +#endif +} + +// Increments indices according to a shape. +// Returns false if indices can't be incremented because they point to the last +// element. +// +// E.g. if shape is (2, 3) and indices is [1, 2], indices is incremented to [2, +// 0]. +inline bool IncrementIndices(int32_t rank, + const int32_t* shape, + int32_t* indices) { + int32_t i = rank - 1; + while (i >= 0 && indices[i] == shape[i] - 1) { + --i; + } + if (i < 0) { + return false; + } + indices[i] += 1; + for (++i; i < rank; ++i) { + indices[i] = 0; + } + return true; +} + +// Returns the offset in a values array given its shape and indices. +// E.g. if the shape is (2, 3) and indices are [1, 2] the offset is 1*3 + 2. +inline int32_t Offset(int32_t rank, + const int32_t* shape, + const int32_t* indices) { + int32_t offset = 0; + int32_t mul = 1; + for (int32_t i = rank - 1; i >= 0; --i) { + offset += mul * indices[i]; + mul *= shape[i]; + } + return offset; +} + +// Like Offset() but with broadcasting. +// E.g. if the input_shape is (2, 1, 3) and indices are [1, 2, 2] the offset is +// 1*1*3 + 2*0 + 2. +// The indices_rank can be greater than the input_rank and then the first +// indices_rank - input_rank indices are ignored. +// E.g. if the input_shape is (4) and indices are [2, 3, 1] the offset is 1. +inline int32_t BroadcastOffset(int32_t input_rank, + const int32_t* input_shape, + int32_t indices_rank, + const int32_t* indices) { + int32_t offset = 0; + int32_t mul = 1; + for (int32_t i = input_rank - 1; i >= 0; --i) { + int32_t index = + input_shape[i] == 1 ? 0 : indices[i + indices_rank - input_rank]; + offset += mul * index; + mul *= input_shape[i]; + } + return offset; +} + +template <typename T, typename OP> +void OpGenericBroadcast(int32_t left_rank, + const int32_t* __restrict left_shape, + const T* __restrict left_values, + int32_t right_rank, + const int32_t* __restrict right_shape, + const T* __restrict right_values, + T* __restrict output_values, + OP op) { + BENCHMARK_TIMER(op.name, "GenericBroadcast"); + const int32_t output_rank = std::max(left_rank, right_rank); + const int32_t kMaxRank = 8; + assert(output_rank <= kMaxRank); + + int32_t output_shape[kMaxRank]; + for (int32_t i = 0; i < output_rank; ++i) { + int32_t left_i = i - output_rank + left_rank; + int32_t right_i = i - output_rank + right_rank; + output_shape[i] = std::max(left_i >= 0 ? left_shape[left_i] : 0, + right_i >= 0 ? right_shape[right_i] : 0); + } + + int32_t output_indices[kMaxRank]{}; + do { + output_values[Offset(output_rank, output_shape, output_indices)] = + op(left_values[BroadcastOffset(left_rank, left_shape, output_rank, + output_indices)], + right_values[BroadcastOffset(right_rank, right_shape, output_rank, + output_indices)]); + } while (IncrementIndices(output_rank, output_shape, output_indices)); +} + +#define BROADCAST_BINARY_OP(OP_NAME, EXPR, EXPR_EIGEN) \ + template <typename T> \ + struct Op##OP_NAME { \ + const char* name = #OP_NAME; \ + T operator()(const T lhs, const T rhs) { return EXPR; } \ + template <typename X, typename Y, typename Z> \ + void apply(const X& lhs, const Y& rhs, Z out) { \ + out = EXPR_EIGEN; \ + } \ + }; \ + template <typename T> \ + void OP_NAME##NoBroadcast( \ + const int32_t left_rank, const int32_t* __restrict left_shape, \ + const T* __restrict left_values, const int32_t right_rank, \ + const int32_t* __restrict right_shape, const T* __restrict right_values, \ + T* __restrict output_values) { \ + OpNoBroadcast(left_rank, left_shape, left_values, right_rank, right_shape, \ + right_values, output_values, Op##OP_NAME<T>()); \ + } \ + template <typename T> \ + void OP_NAME##InnerBroadcast( \ + const int32_t left_rank, const int32_t* __restrict left_shape, \ + const T* __restrict left_values, const int32_t right_rank, \ + const int32_t* __restrict right_shape, const T* __restrict right_values, \ + T* __restrict output_values) { \ + OpInnerBroadcast(left_rank, left_shape, left_values, right_rank, \ + right_shape, right_values, output_values, \ + Op##OP_NAME<T>()); \ + } \ + template <typename T> \ + void OP_NAME(const int32_t left_rank, const int32_t* __restrict left_shape, \ + const T* __restrict left_values, const int32_t right_rank, \ + const int32_t* __restrict right_shape, \ + const T* __restrict right_values, \ + T* __restrict output_values) { \ + OpGenericBroadcast(left_rank, left_shape, left_values, right_rank, \ + right_shape, right_values, output_values, \ + Op##OP_NAME<T>()); \ + } + +// Second macro param is value expression, third entry is Eigen vector +// expression. +BROADCAST_BINARY_OP(Add, lhs + rhs, lhs + rhs) +BROADCAST_BINARY_OP(Maximum, std::max(lhs, rhs), lhs.max(rhs)) +BROADCAST_BINARY_OP(Minimum, std::min(lhs, rhs), lhs.min(rhs)) +BROADCAST_BINARY_OP(Mul, lhs* rhs, lhs* rhs) +BROADCAST_BINARY_OP(Sub, lhs - rhs, lhs - rhs) +BROADCAST_BINARY_OP(SquaredDifference, + (lhs - rhs) * (lhs - rhs), + (lhs - rhs).square()) + +// ----------------------------------------------------------------------------- +// Reduce ops +// ----------------------------------------------------------------------------- + +// We use macros instead of template functions with templated functors here +// because it's a lot less verbose and easier for the compiler to optimize. +#define REDUCE_OP(OP_NAME, DEFAULT_VALUE, UPDATE_EXPR, RESULT_EXPR) \ + template <typename T, typename Tidx> \ + void OP_NAME##InnerReduce( \ + int32_t input_rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t index_tensor_rank, \ + const int32_t* __restrict index_shape, \ + const Tidx* __restrict index_values, T* __restrict output_values) { \ + BENCHMARK_TIMER(#OP_NAME, "InnerReduce"); \ + const int32_t inner_size = \ + GetReduceInnerSize(input_rank, input_shape, index_tensor_rank, \ + index_shape, index_values); \ + const int32_t input_size = ShapeSize(input_rank, input_shape); \ + const int32_t outer_size = input_size / inner_size; \ + for (int32_t idx_out = 0; idx_out < outer_size; ++idx_out) { \ + T value = DEFAULT_VALUE; \ + for (int32_t idx_in = 0; idx_in < inner_size; ++idx_in) { \ + const T prev = value; \ + const T next = input_values[idx_out * inner_size + idx_in]; \ + value = UPDATE_EXPR; \ + } \ + const T count = inner_size; \ + /* Used by mean reduce. */ \ + (void)sizeof(count); \ + output_values[idx_out] = RESULT_EXPR; \ + } \ + } \ + template <typename T, typename Tidx> \ + void OP_NAME##GenericReduceRank2( \ + int32_t input_rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t index_tensor_rank, \ + const int32_t* __restrict index_shape, \ + const Tidx* __restrict index_values, T* __restrict output_values) { \ + assert(input_rank == 2); \ + assert(index_tensor_rank <= 1); \ + BENCHMARK_TIMER(#OP_NAME, "GenericReduceRank2"); \ + const int32_t output_size = input_shape[1]; \ + std::fill_n(output_values, output_size, DEFAULT_VALUE); \ + for (int32_t dim0 = 0; dim0 < input_shape[0]; ++dim0) { \ + for (int32_t dim1 = 0; dim1 < input_shape[1]; ++dim1, ++input_values) { \ + T* out_ptr = output_values + dim1; \ + const T prev = *out_ptr; \ + const T next = *input_values; \ + *out_ptr = UPDATE_EXPR; \ + } \ + } \ + const T count = input_shape[0]; \ + /* Used by mean reduce. */ \ + (void)sizeof(count); \ + for (int32_t i = 0; i < output_size; ++i) { \ + const T value = output_values[i]; \ + output_values[i] = RESULT_EXPR; \ + } \ + } \ + template <typename T, typename Tidx> \ + void OP_NAME##GenericReduceRank3( \ + int32_t input_rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t index_tensor_rank, \ + const int32_t* __restrict index_shape, \ + const Tidx* __restrict index_values, T* __restrict output_values) { \ + assert(input_rank == 3); \ + assert(index_tensor_rank <= 1); \ + BENCHMARK_TIMER(#OP_NAME, "GenericReduceRank3"); \ + int32_t out_shape[3] = {input_shape[0], input_shape[1], input_shape[2]}; \ + bool reduce_mask[3] = {false, false, false}; \ + const int32_t num_indices = index_tensor_rank > 0 ? index_shape[0] : 1; \ + for (int32_t i = 0; i < num_indices; ++i) { \ + reduce_mask[index_values[i]] = true; \ + out_shape[index_values[i]] = 1; \ + } \ + const int32_t out_strides[3] = { \ + reduce_mask[0] ? 0 : out_shape[1] * out_shape[2], \ + reduce_mask[1] ? 0 : out_shape[2], \ + reduce_mask[2] ? 0 : 1, \ + }; \ + const int32_t output_size = ShapeSize(input_rank, out_shape); \ + std::fill_n(output_values, output_size, DEFAULT_VALUE); \ + for (int32_t dim0 = 0; dim0 < input_shape[0]; ++dim0) { \ + for (int32_t dim1 = 0; dim1 < input_shape[1]; ++dim1) { \ + for (int32_t dim2 = 0; dim2 < input_shape[2]; \ + ++dim2, ++input_values) { \ + T* out_ptr = output_values + out_strides[0] * dim0 + \ + out_strides[1] * dim1 + out_strides[2] * dim2; \ + const T prev = *out_ptr; \ + const T next = *input_values; \ + *out_ptr = UPDATE_EXPR; \ + } \ + } \ + } \ + const T count = (reduce_mask[0] ? input_shape[0] : 1) * \ + (reduce_mask[1] ? input_shape[1] : 1) * \ + (reduce_mask[2] ? input_shape[2] : 1); \ + /* Used by mean reduce. */ \ + (void)sizeof(count); \ + for (int32_t i = 0; i < output_size; ++i) { \ + const T value = output_values[i]; \ + output_values[i] = RESULT_EXPR; \ + } \ + } \ + template <typename T, typename Tidx> \ + void OP_NAME##GenericReduceRank4( \ + int32_t input_rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t index_tensor_rank, \ + const int32_t* __restrict index_shape, \ + const Tidx* __restrict index_values, T* __restrict output_values) { \ + assert(input_rank == 4); \ + assert(index_tensor_rank <= 1); \ + BENCHMARK_TIMER(#OP_NAME, "GenericReduceRank4"); \ + int32_t out_shape[4] = {input_shape[0], input_shape[1], input_shape[2], \ + input_shape[3]}; \ + bool reduce_mask[4] = {false, false, false, false}; \ + const int32_t num_indices = index_tensor_rank > 0 ? index_shape[0] : 1; \ + for (int32_t i = 0; i < num_indices; ++i) { \ + reduce_mask[index_values[i]] = true; \ + out_shape[index_values[i]] = 1; \ + } \ + const int32_t out_strides[4] = { \ + reduce_mask[0] ? 0 : out_shape[1] * out_shape[2] * out_shape[3], \ + reduce_mask[1] ? 0 : out_shape[2] * out_shape[3], \ + reduce_mask[2] ? 0 : out_shape[3], \ + reduce_mask[3] ? 0 : 1, \ + }; \ + const int32_t output_size = ShapeSize(input_rank, out_shape); \ + std::fill_n(output_values, output_size, DEFAULT_VALUE); \ + for (int32_t dim0 = 0; dim0 < input_shape[0]; ++dim0) { \ + for (int32_t dim1 = 0; dim1 < input_shape[1]; ++dim1) { \ + for (int32_t dim2 = 0; dim2 < input_shape[2]; ++dim2) { \ + for (int32_t dim3 = 0; dim3 < input_shape[3]; \ + ++dim3, ++input_values) { \ + T* out_ptr = output_values + out_strides[0] * dim0 + \ + out_strides[1] * dim1 + out_strides[2] * dim2 + \ + out_strides[3] * dim3; \ + const T prev = *out_ptr; \ + const T next = *input_values; \ + *out_ptr = UPDATE_EXPR; \ + } \ + } \ + } \ + } \ + const T count = (reduce_mask[0] ? input_shape[0] : 1) * \ + (reduce_mask[1] ? input_shape[1] : 1) * \ + (reduce_mask[2] ? input_shape[2] : 1) * \ + (reduce_mask[3] ? input_shape[3] : 1); \ + /* Used by mean reduce. */ \ + (void)sizeof(count); \ + for (int32_t i = 0; i < output_size; ++i) { \ + const T value = output_values[i]; \ + output_values[i] = RESULT_EXPR; \ + } \ + } \ + template <typename T, typename Tidx> \ + void OP_NAME##GenericReduceRank5( \ + int32_t input_rank, const int32_t* __restrict input_shape, \ + const T* __restrict input_values, int32_t index_tensor_rank, \ + const int32_t* __restrict index_shape, \ + const Tidx* __restrict index_values, T* __restrict output_values) { \ + assert(input_rank == 5); \ + assert(index_tensor_rank <= 1); \ + BENCHMARK_TIMER(#OP_NAME, "GenericReduceRank5"); \ + int32_t out_shape[5] = {input_shape[0], input_shape[1], input_shape[2], \ + input_shape[3], input_shape[4]}; \ + /* If true, reduce the input across that dimension. */ \ + bool reduce_mask[5] = {false, false, false, false, false}; \ + const int32_t num_indices = index_tensor_rank > 0 ? index_shape[0] : 1; \ + for (int32_t i = 0; i < num_indices; ++i) { \ + reduce_mask[index_values[i]] = true; \ + out_shape[index_values[i]] = 1; \ + } \ + const int32_t out_strides[5] = { \ + reduce_mask[0] \ + ? 0 \ + : out_shape[1] * out_shape[2] * out_shape[3] * out_shape[4], \ + reduce_mask[1] ? 0 : out_shape[2] * out_shape[3] * out_shape[4], \ + reduce_mask[2] ? 0 : out_shape[3] * out_shape[4], \ + reduce_mask[3] ? 0 : out_shape[4], \ + reduce_mask[4] ? 0 : 1, \ + }; \ + const int32_t output_size = ShapeSize(input_rank, out_shape); \ + std::fill_n(output_values, output_size, DEFAULT_VALUE); \ + for (int32_t dim0 = 0; dim0 < input_shape[0]; ++dim0) { \ + for (int32_t dim1 = 0; dim1 < input_shape[1]; ++dim1) { \ + for (int32_t dim2 = 0; dim2 < input_shape[2]; ++dim2) { \ + for (int32_t dim3 = 0; dim3 < input_shape[3]; ++dim3) { \ + for (int32_t dim4 = 0; dim4 < input_shape[4]; \ + ++dim4, ++input_values) { \ + T* out_ptr = output_values + out_strides[0] * dim0 + \ + out_strides[1] * dim1 + out_strides[2] * dim2 + \ + out_strides[3] * dim3 + out_strides[4] * dim4; \ + const T prev = *out_ptr; \ + const T next = *input_values; \ + *out_ptr = UPDATE_EXPR; \ + } \ + } \ + } \ + } \ + } \ + const T count = (reduce_mask[0] ? input_shape[0] : 1) * \ + (reduce_mask[1] ? input_shape[1] : 1) * \ + (reduce_mask[2] ? input_shape[2] : 1) * \ + (reduce_mask[3] ? input_shape[3] : 1) * \ + (reduce_mask[4] ? input_shape[4] : 1); \ + /* Used by mean reduce. */ \ + (void)sizeof(count); \ + for (int32_t i = 0; i < output_size; ++i) { \ + const T value = output_values[i]; \ + output_values[i] = RESULT_EXPR; \ + } \ + } + +REDUCE_OP(Max, std::numeric_limits<T>::lowest(), std::max(prev, next), value) +REDUCE_OP(Min, std::numeric_limits<T>::infinity(), std::min(prev, next), value) +REDUCE_OP(Sum, 0, prev + next, value) +REDUCE_OP(Mean, 0, prev + next, value / count) + +// ----------------------------------------------------------------------------- +// Dequantize ops +// ----------------------------------------------------------------------------- + +template <typename T> +void DequantizeMinCombined(const int32_t rank, + const int32_t* __restrict input_shape, + const T* __restrict input_values, + const float* __restrict min_range, + const float* __restrict max_range, + float* __restrict output_values) { + BENCHMARK_TIMER("DequantizeMinCombined"); + const int32_t size = ShapeSize(rank, input_shape); + const float offset = + std::is_signed<T>::value + ? (static_cast<float>(std::numeric_limits<T>::max()) - + std::numeric_limits<T>::min() + 1) / + 2.0f + : 0.0f; + const float range_scale = (max_range[0] - min_range[0]) / + (static_cast<float>(std::numeric_limits<T>::max()) - + std::numeric_limits<T>::min()); + for (int32_t i = 0; i < size; ++i) { + output_values[i] = + ((static_cast<int32_t>(input_values[i]) + offset) * range_scale) + + min_range[0]; + } +} + +template <typename T> +void DequantizeMinFirst(const int32_t rank, + const int32_t* __restrict input_shape, + const T* __restrict input_values, + const float* __restrict min_range, + const float* __restrict max_range, + float* __restrict output_values) { + BENCHMARK_TIMER("DequantizeMinFirst"); + const int32_t size = ShapeSize(rank, input_shape); + const float range_scale = (max_range[0] - min_range[0]) / + (static_cast<float>(std::numeric_limits<T>::max()) - + std::numeric_limits<T>::min()); + const float range_min_rounded = + (max_range[0] == min_range[0] + ? min_range[0] + : std::round(min_range[0] / range_scale) * range_scale); + for (int32_t i = 0; i < size; ++i) { + output_values[i] = ((static_cast<int32_t>(input_values[i]) - + std::numeric_limits<T>::min()) * + range_scale) + + range_min_rounded; + } +} + +// ----------------------------------------------------------------------------- +// AddN op +// ----------------------------------------------------------------------------- + +template <typename T> +void AddN(const int32_t rank, + const int32_t* __restrict shape, + std::initializer_list<const T* __restrict> input_values, + T* __restrict output_values) { + BENCHMARK_TIMER("AddN"); + const int32_t size = ShapeSize(rank, shape); +#if USE_EIGEN + auto output = RowVectorMap<T>(output_values, size).array(); + std::fill_n(output_values, size, 0); + for (const auto input_value : input_values) { + output += ConstRowVectorMap<T>(input_value, size).array(); + } +#else + for (int32_t i = 0; i < size; ++i) { + T output_value = 0; + for (auto input_value : input_values) { + output_value += input_value[i]; + } + output_values[i] = output_value; + } +#endif +} + +// ----------------------------------------------------------------------------- +// CONSTANTS +// Note that for now, endianness of the target machine needs to match that of +// the one training was performed on. +// ----------------------------------------------------------------------------- +const int32_t dnn_hiddenlayer_0_bias__0__cf__0_shape[1] = {20}; +const union { + uint8_t bytes[80]; + float values[20]; +} dnn_hiddenlayer_0_bias__0__cf__0 = {{ + 0x93, 0xa2, 0x71, 0x3e, 0x73, 0xa8, 0x38, 0xbf, 0x57, 0xda, 0x8d, 0xbf, + 0x99, 0x04, 0x97, 0x3f, 0xed, 0xf1, 0xef, 0xbf, 0x0f, 0x0f, 0xa0, 0xbf, + 0x44, 0xad, 0x97, 0x3f, 0x4f, 0x01, 0xd0, 0x3f, 0x1a, 0xab, 0x30, 0xbf, + 0x26, 0xec, 0x01, 0x3e, 0x2b, 0x49, 0xfb, 0xbf, 0x01, 0xc6, 0x57, 0x3e, + 0x84, 0xf7, 0x8d, 0x3f, 0xaf, 0x32, 0xd1, 0xbe, 0x95, 0x54, 0xc9, 0x3f, + 0x3c, 0x57, 0x30, 0xc0, 0x90, 0x1a, 0xc9, 0x3f, 0x38, 0x7f, 0xb7, 0x3f, + 0x5d, 0xf7, 0x2a, 0x3c, 0x53, 0x6b, 0x0b, 0xc0, +}}; +const int32_t dnn_hiddenlayer_0_kernel__1__cf__1_shape[2] = {325, 20}; +const union { + uint8_t bytes[26000]; + float values[6500]; +} dnn_hiddenlayer_0_kernel__1__cf__1 = {{ + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0xb2, 0x3e, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, 0x78, 0x07, 0xb6, 0xbf, + 0x70, 0x5c, 0xad, 0xbf, 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x58, 0xb0, 0x8a, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0x70, 0x5c, 0xad, 0xbf, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x68, 0x06, 0x9c, 0xbf, 0x78, 0x07, 0xb6, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x58, 0x05, 0x82, 0xbf, 0x70, 0xb1, 0xa4, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbf, 0x70, 0x5c, 0xad, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, + 0x90, 0xb3, 0xd8, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x58, 0x05, 0x82, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x90, 0xb3, 0xd8, 0x3f, 0x72, 0x5c, 0x2d, 0xc0, + 0x80, 0xb2, 0xbe, 0x3f, 0x88, 0x5d, 0xc7, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x05, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0x58, 0xb0, 0x8a, 0xbf, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x68, 0x06, 0x9c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x58, 0x05, 0x82, 0xbf, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb2, 0xbe, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x68, 0x06, 0x9c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x5d, 0xc7, 0xbf, 0xa0, 0xb4, 0xf2, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3f, 0x84, 0x5d, 0xc7, 0x40, 0x18, 0xb4, 0x65, 0xc0, + 0x80, 0x08, 0xd0, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x68, 0x06, 0x1c, 0xc0, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0xe0, 0x05, 0x0f, 0x40, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0xa8, 0x5f, 0xfb, 0x3f, 0xe0, 0x05, 0x0f, 0x40, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0x5b, 0x93, 0xbf, + 0x70, 0xb1, 0xa4, 0x3f, 0x98, 0x09, 0xea, 0xbf, 0x68, 0x06, 0x9c, 0xbf, + 0x60, 0x5b, 0x93, 0x3f, 0x80, 0xb2, 0xbe, 0x3f, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0xa8, 0x5f, 0xfb, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x68, 0x06, 0x9c, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x88, 0x08, 0xd0, 0x3f, + 0xf0, 0x06, 0x29, 0xc0, 0x88, 0x5d, 0xc7, 0x3f, 0x70, 0xb1, 0xa4, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x5c, 0xb0, 0x0a, 0xc0, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x58, 0xb0, 0x8a, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5d, 0xc7, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, 0x78, 0x07, 0xb6, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x90, 0xb3, 0xd8, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x68, 0x06, 0x9c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x06, 0x9c, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x90, 0x08, 0x50, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x5c, 0xb0, 0x0a, 0x40, 0x70, 0x5c, 0xad, 0x3f, + 0x54, 0x05, 0x02, 0xc0, 0x58, 0x05, 0x82, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x70, 0x5c, 0xad, 0x3f, 0x5c, 0xb0, 0x0a, 0x40, + 0xa0, 0xb4, 0xf2, 0x3e, 0x58, 0x05, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x58, 0xb0, 0x8a, 0xbf, 0x68, 0x06, 0x9c, 0x3f, 0x98, 0x5e, 0xe1, 0xbf, + 0x68, 0x06, 0x9c, 0xbf, 0x68, 0x06, 0x9c, 0x3f, 0x90, 0xb3, 0xd8, 0x3f, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0xd8, 0x5a, 0x06, 0xc0, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb2, 0xbe, 0x3f, 0xf6, 0xb1, 0x31, 0xc0, 0x88, 0x08, 0xd0, 0x3f, + 0x78, 0x07, 0xb6, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x05, 0x0f, 0xc0, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x98, 0x5e, 0xe1, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0xb3, 0xd8, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x68, 0x06, 0x9c, 0x3f, 0x68, 0x06, 0x9c, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0x24, 0x0a, 0x77, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0xbf, 0x90, 0x5e, 0x61, 0x3f, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, + 0xa8, 0x5f, 0xfb, 0xbf, 0x60, 0x5b, 0x93, 0xbf, 0x60, 0x5b, 0x93, 0x3f, + 0x88, 0x5d, 0xc7, 0x3f, 0x80, 0x08, 0x50, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x5b, 0x93, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb2, 0xbe, 0x3f, 0xf6, 0xb1, 0x31, 0xc0, + 0x80, 0xb2, 0xbe, 0x3f, 0x78, 0x07, 0xb6, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0xe0, 0x05, 0x0f, 0xc0, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0xb1, 0xa4, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0xe4, 0xb0, 0x17, 0xc0, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb2, 0xbe, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x06, 0x9c, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0x5b, 0x93, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5b, 0x13, 0xc0, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb2, 0xbe, 0x3f, 0x70, 0xb1, 0xa4, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0xa6, 0xdb, 0x99, 0xc0, 0x80, 0xb2, 0xbe, 0xbf, + 0x98, 0x5e, 0xe1, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, 0x90, 0x5e, 0x61, 0x3f, + 0xf0, 0x06, 0x29, 0x40, 0x78, 0x07, 0xb6, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x05, 0x0f, 0x40, 0x80, 0xb2, 0x3e, 0xbf, 0x70, 0x5c, 0xad, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x88, 0x5d, 0xc7, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x58, 0x05, 0x82, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0x5b, 0x93, 0xbf, + 0x60, 0xb0, 0x8a, 0x3f, 0x98, 0x5e, 0xe1, 0xbf, 0x60, 0x5b, 0x93, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0x88, 0x08, 0xd0, 0x3f, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x98, 0x09, 0xea, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x58, 0x05, 0x82, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x88, 0x08, 0xd0, 0x3f, + 0x72, 0x5c, 0x2d, 0xc0, 0x88, 0x5d, 0xc7, 0x3f, 0x78, 0x07, 0xb6, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x05, 0x02, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x5c, 0xad, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x5c, 0xb0, 0x0a, 0x40, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x51, 0xde, 0xda, 0xc0, + 0x80, 0xb2, 0xbe, 0xbf, 0xa0, 0xb4, 0xf2, 0x3f, 0x78, 0x07, 0xb6, 0x3f, + 0xf0, 0x06, 0x29, 0x40, 0x60, 0x5b, 0x93, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, + 0x70, 0x5c, 0x2d, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, 0xa8, 0x5f, 0xfb, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5b, 0x93, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbf, 0x70, 0x5c, 0xad, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, + 0x58, 0x05, 0x82, 0xbf, 0x70, 0xb1, 0xa4, 0x3f, 0x70, 0x5c, 0x2d, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x5c, 0xb0, 0x0a, 0xc0, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x74, 0x5c, 0x2d, 0x40, 0x60, 0x5b, 0x13, 0xc0, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x7e, 0xb2, 0x3e, 0xc0, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x90, 0x5e, 0x61, 0xbf, + 0x07, 0xb3, 0xcb, 0xc0, 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, 0x14, 0x09, 0x5d, 0x40, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x88, 0x08, 0xd0, 0xbf, + 0xa8, 0x5f, 0xfb, 0xbf, 0xa8, 0x5f, 0xfb, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x54, 0x05, 0x02, 0xc0, 0x98, 0x5e, 0xe1, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x5d, 0xc7, 0xbf, 0x68, 0x06, 0x9c, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x78, 0x07, 0x36, 0x40, 0x88, 0x08, 0xd0, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0x3e, + 0x84, 0x5d, 0x47, 0xc0, 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0xad, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0xce, 0x88, 0xd6, 0xc0, 0x80, 0x08, 0xd0, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0x5c, 0xad, 0xbe, + 0x64, 0x5b, 0x13, 0x40, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x05, 0x82, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x90, 0x5e, 0x61, 0xbf, + 0x90, 0xb3, 0xd8, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x18, 0x30, 0x84, 0x40, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x98, 0x09, 0xea, 0xbf, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0xad, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x74, 0x5c, 0x2d, 0x40, + 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x70, 0x5c, 0x2d, 0x3f, 0x00, 0x08, 0x43, 0xc0, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0xe0, 0x05, 0x0f, 0xc0, 0x01, 0x08, 0xc3, 0xc0, + 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0x72, 0x3f, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, + 0xf8, 0xb1, 0x31, 0x40, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0xbf, 0x98, 0x09, 0xea, 0xbf, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0xb2, 0x3e, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x74, 0x5c, 0x2d, 0x40, 0x80, 0xb2, 0x3e, 0xbf, 0x70, 0xb1, 0xa4, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, 0xf6, 0xb1, 0x31, 0xc0, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x70, 0x5c, 0x2d, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x9a, 0x09, 0x6a, 0xc0, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0xa8, 0x5f, 0xfb, 0x3f, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0x3f, + 0xa0, 0xb4, 0xf2, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x90, 0xb3, 0xd8, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x58, 0x05, 0x82, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0xe0, 0x05, 0x0f, 0x40, 0x58, 0x05, 0x82, 0xbf, + 0x90, 0xb3, 0xd8, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, + 0x7e, 0xb2, 0x3e, 0xc0, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x58, 0x05, 0x82, 0xbf, + 0xa0, 0xb4, 0x72, 0xc0, 0x20, 0x5f, 0x6e, 0x40, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0xad, 0x3f, 0xe4, 0xb0, 0x17, 0xc0, 0x80, 0x08, 0x50, 0x3e, + 0x58, 0x05, 0x82, 0x3f, 0x90, 0x08, 0x50, 0x3f, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0xac, 0x86, 0xa2, 0xc0, + 0x70, 0x5c, 0xad, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, 0xa8, 0x5f, 0xfb, 0x3f, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb2, 0xbe, 0x3f, 0x68, 0x06, 0x9c, 0xbf, + 0xa0, 0xb4, 0x72, 0x3f, 0xf6, 0xb1, 0x31, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x78, 0x07, 0xb6, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x58, 0xb0, 0x8a, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0x3f, + 0x58, 0x05, 0x82, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, 0xe4, 0xb0, 0x17, 0xc0, + 0x80, 0xb0, 0x0a, 0x3e, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0x5b, 0x93, 0xbf, + 0xa0, 0xb4, 0x72, 0x3f, 0x70, 0x5c, 0xad, 0xbf, 0x58, 0x05, 0x82, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0x68, 0x06, 0x9c, 0x3f, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x5c, 0xb0, 0x0a, 0x40, + 0xf0, 0x06, 0x29, 0xc0, 0x78, 0x07, 0xb6, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x6c, 0xb1, 0x24, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0xf0, 0x06, 0xa9, 0xc0, 0xa0, 0xb4, 0xf2, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb2, 0x3e, 0x3f, 0x98, 0x09, 0xea, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xb1, 0xa4, 0xbf, 0x68, 0x06, 0x9c, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x98, 0x5e, 0x61, 0x40, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0xb0, 0x0a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x58, 0x05, 0x82, 0x3f, 0xfc, 0x5c, 0x3a, 0x40, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0x3f, 0x70, 0x5c, 0x2d, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb2, 0x3e, 0xbf, 0xe8, 0x5b, 0x20, 0xc0, + 0x60, 0xb0, 0x0a, 0x3f, 0x84, 0x5d, 0x47, 0xc0, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0x5b, 0x93, 0xbf, 0x88, 0x5d, 0xc7, 0x3f, 0x90, 0x5e, 0x61, 0x3f, + 0x70, 0x06, 0x1c, 0x3f, 0x68, 0x06, 0x9c, 0x3f, 0x80, 0xb2, 0x3e, 0x40, + 0x98, 0x5e, 0x61, 0x40, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x90, 0xb3, 0xd8, 0xbf, 0x80, 0xb2, 0xbe, 0x3f, 0xe8, 0x5b, 0x20, 0xc0, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x5c, 0x2d, 0x3f, 0xa8, 0x5f, 0xfb, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x68, 0x06, 0x9c, 0xbf, 0x70, 0x5c, 0xad, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x90, 0x5e, 0x61, 0x3f, 0x9c, 0x09, 0x6a, 0x40, 0x80, 0x08, 0x50, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, + 0x60, 0xb0, 0x0a, 0xbf, 0x90, 0x5e, 0x61, 0x3f, 0xf8, 0xb1, 0x31, 0x40, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0xb0, 0x0a, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb2, 0xbe, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0xe4, 0xb0, 0x17, 0x40, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x98, 0x5e, 0xe1, 0xbf, 0x80, 0x5d, 0xc7, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0x3f, 0x90, 0x5e, 0x61, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x84, 0x5d, 0x47, 0x40, 0x40, 0xb0, 0x0a, 0xbe, 0x0c, 0x5e, 0x54, 0x40, + 0x74, 0x5c, 0x2d, 0x40, 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0x3f, 0xa8, 0x5f, 0xfb, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x5b, 0x93, 0xbf, 0x68, 0x06, 0x9c, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x9c, 0x09, 0x6a, 0x40, + 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x68, 0x06, 0x9c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, + 0xf8, 0xb1, 0x31, 0x40, 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x08, 0x50, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x08, 0x50, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x90, 0x5e, 0x61, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x90, 0x5e, 0x61, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x5c, 0xad, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5b, 0x93, 0xbf, 0x60, 0x5b, 0x93, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0x3f, 0xe8, 0x5b, 0x20, 0xc0, 0xa0, 0xb4, 0xf2, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0x72, 0x3f, 0x64, 0x5b, 0x13, 0x40, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0xb2, 0xbe, 0x3f, 0x80, 0xb2, 0xbe, 0x3f, 0x60, 0x5b, 0x93, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0xa8, 0x5f, 0xfb, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5b, 0x93, 0xbf, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0x3f, + 0x98, 0x5e, 0x61, 0x40, 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x0a, 0xbf, 0x68, 0x06, 0x9c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x90, 0x08, 0x50, 0x3f, 0xfc, 0x5c, 0x3a, 0x40, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x08, 0x50, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0xbf, 0x90, 0x5e, 0x61, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x90, 0xb3, 0xd8, 0x3f, 0x68, 0x06, 0x9c, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, + 0x62, 0x5b, 0x93, 0xc0, 0x60, 0x5c, 0xad, 0xbe, 0x70, 0x5c, 0xad, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0xb1, 0xa4, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x29, 0x31, 0x9e, 0xc0, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5b, 0x13, 0xc0, 0x90, 0x08, 0x50, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0xa8, 0x5f, 0xfb, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x68, 0x06, 0x9c, 0xbf, 0x68, 0x06, 0x9c, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x90, 0x5e, 0x61, 0x3f, 0x98, 0x5e, 0x61, 0x40, 0x80, 0x08, 0x50, 0xbf, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0x5b, 0x93, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, 0xfc, 0x5c, 0x3a, 0x40, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0x72, 0xbf, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x58, 0xb0, 0x8a, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x74, 0x5c, 0x2d, 0x40, 0x90, 0xb3, 0xd8, 0x3f, + 0x60, 0x5b, 0x13, 0xc0, 0x6c, 0xb1, 0x24, 0xc0, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x5d, 0xc7, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x98, 0x5e, 0xe1, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0xdf, 0x05, 0x0f, 0xc1, 0x60, 0xb0, 0x8a, 0x3e, 0xa6, 0x5f, 0x7b, 0xc0, + 0x90, 0x08, 0x50, 0x3f, 0x90, 0xb3, 0xd8, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0x3f, 0xa8, 0x5f, 0xfb, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, 0x70, 0x5c, 0xad, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x98, 0x5e, 0x61, 0x40, + 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x74, 0x5c, 0x2d, 0x40, 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0xec, 0x5b, 0x20, 0x40, 0x80, 0xb2, 0x3e, 0xbf, + 0x58, 0x05, 0x02, 0x40, 0x60, 0x06, 0x1c, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0xb0, 0x0a, 0x40, 0xa0, 0xb4, 0xf2, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x74, 0x5c, 0x2d, 0x40, + 0x80, 0xb2, 0xbe, 0xbf, 0x40, 0xdd, 0xc0, 0xc0, 0x80, 0x08, 0x50, 0x3e, + 0xfc, 0x5c, 0x3a, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, + 0x90, 0xb3, 0xd8, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0xad, 0xbf, + 0x60, 0x5b, 0x93, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, + 0x84, 0x5d, 0x47, 0x40, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5b, 0x93, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0xe4, 0xb0, 0x17, 0x40, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x05, 0x82, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0xad, 0x3f, 0xd8, 0x5a, 0x06, 0x40, + 0x60, 0x5b, 0x93, 0x3f, 0x90, 0xb3, 0x58, 0x40, 0x80, 0xb2, 0x3e, 0xbf, + 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x90, 0xb3, 0xd8, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x70, 0x06, 0x1c, 0x3f, 0x60, 0x5c, 0xad, 0xbe, + 0xd8, 0x5a, 0x06, 0x40, 0x60, 0xb0, 0x0a, 0x3f, 0xa1, 0xb4, 0xf2, 0xc0, + 0x70, 0x06, 0x1c, 0x3f, 0x68, 0x06, 0x1c, 0xc0, 0x68, 0x06, 0x1c, 0xc0, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0xad, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x58, 0x05, 0x82, 0xbf, 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0xf0, 0x06, 0x29, 0x40, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, 0x68, 0x06, 0x1c, 0x40, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x0a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3f, 0x68, 0x06, 0x1c, 0x40, 0x80, 0xb2, 0x3e, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x78, 0x07, 0xb6, 0x3f, 0x60, 0x5b, 0x93, 0xbf, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5b, 0x93, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, + 0x5c, 0x34, 0xec, 0xc0, 0x80, 0x08, 0xd0, 0xbe, 0xe0, 0x05, 0x0f, 0xc0, + 0x56, 0x05, 0x82, 0xc0, 0x58, 0x05, 0x82, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x68, 0x06, 0x9c, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0xe0, 0x05, 0x0f, 0x40, + 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x06, 0x1c, 0x3f, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x64, 0x5b, 0x13, 0x40, 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x54, 0x05, 0x02, 0xc0, 0x60, 0x5b, 0x93, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0xb3, 0xd8, 0x3f, 0x20, 0x5f, 0x6e, 0x40, + 0x88, 0x5d, 0xc7, 0x3f, 0x78, 0x07, 0xb6, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x90, 0xb3, 0xd8, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, + 0xa0, 0xb4, 0x72, 0x3f, 0xc3, 0x32, 0xc5, 0xc0, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb2, 0xbe, 0xbf, 0x6c, 0xb1, 0x24, 0xc0, 0x60, 0x5b, 0x93, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0xb1, 0xa4, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, + 0x88, 0x5d, 0xc7, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0xd8, 0x5a, 0x06, 0x40, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5b, 0x93, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x58, 0x05, 0x82, 0xbf, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0x5b, 0x93, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x58, 0x05, 0x82, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x70, 0x5c, 0x2d, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, + 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0xac, 0x86, 0xa2, 0xc0, + 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x23, 0x86, 0x95, 0xc0, + 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5b, 0x93, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x70, 0x5c, 0xad, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x98, 0x5e, 0xe1, 0x3f, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0xa0, 0xb4, 0xf2, 0x3f, 0x80, 0xb2, 0xbe, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0x80, 0x08, 0xd0, 0x3e, 0x88, 0x08, 0xd0, 0xbf, + 0xa0, 0xb4, 0x72, 0xbf, 0x5c, 0xb0, 0x0a, 0x40, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb2, 0xbe, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0xf0, 0x06, 0x29, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, + 0x5c, 0xb0, 0x0a, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x8a, 0x08, 0x50, 0xc0, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0xbe, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x58, 0x05, 0x82, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x70, 0xb1, 0xa4, 0x3f, + 0x60, 0xb0, 0x8a, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x88, 0x08, 0xd0, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, 0x58, 0x05, 0x82, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x08, 0xd0, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x88, 0x08, 0xd0, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x58, 0x05, 0x82, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x06, 0x1c, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x78, 0x07, 0xb6, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x78, 0x07, 0xb6, 0xbf, 0xe0, 0x05, 0x0f, 0xc0, + 0x80, 0xb2, 0x3e, 0x3f, 0x90, 0x08, 0x50, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x60, 0xb0, 0x0a, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, 0x78, 0x07, 0x36, 0x40, + 0x80, 0x5c, 0xad, 0x3e, 0xec, 0x5b, 0x20, 0x40, 0x60, 0x5c, 0xad, 0xbe, + 0x78, 0x07, 0xb6, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x8a, 0x08, 0x50, 0xc0, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x0a, 0xbf, + 0xe0, 0x05, 0x0f, 0xc0, 0x70, 0xb1, 0xa4, 0xbf, 0x78, 0x07, 0xb6, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x78, 0x07, 0xb6, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x90, 0x5e, 0x61, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbf, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x90, 0x5e, 0x61, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0xa0, 0xb4, 0x72, 0x3f, 0x58, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x98, 0x5e, 0xe1, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0xad, 0xbf, 0x80, 0x08, 0x50, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x78, 0x07, 0x36, 0x40, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x58, 0x05, 0x82, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, + 0xfc, 0x5c, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0x72, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x78, 0x07, 0xb6, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0xb1, 0xa4, 0x3f, 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x5e, 0x61, 0x3f, + 0xa1, 0xb4, 0xf2, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, 0x5c, 0xb0, 0x0a, 0xc0, + 0x5c, 0xb0, 0x0a, 0xc0, 0x90, 0x5e, 0x61, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb2, 0xbe, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x74, 0x5c, 0x2d, 0x40, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x68, 0x06, 0x9c, 0x3f, 0x70, 0x06, 0x1c, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x90, 0x5e, 0x61, 0xbf, 0x78, 0x07, 0xb6, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5b, 0x93, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0xad, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, + 0x70, 0x5c, 0xad, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0xa8, 0x5f, 0xfb, 0xbf, 0x58, 0x05, 0x82, 0xbf, 0x98, 0x09, 0xea, 0xbf, + 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, 0xa0, 0xb4, 0xf2, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x8a, 0x08, 0x50, 0xc0, 0xfc, 0x5c, 0x3a, 0x40, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0x40, 0x58, 0x05, 0x82, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, + 0x68, 0x06, 0x1c, 0x40, 0xe4, 0xb0, 0x97, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0xf8, 0xb1, 0x31, 0x40, 0x80, 0x08, 0xd0, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0xbf, 0x78, 0x07, 0xb6, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x90, 0x08, 0x50, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x58, 0x05, 0x82, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5b, 0x13, 0xc0, 0x70, 0x5c, 0xad, 0xbf, + 0x88, 0x08, 0xd0, 0x3f, 0x60, 0xb0, 0x8a, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0xd0, 0x88, 0xd6, 0x40, 0x70, 0x06, 0x1c, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, + 0x58, 0x05, 0x82, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x98, 0x5e, 0xe1, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x90, 0x5e, 0x61, 0x3f, 0x6c, 0xb1, 0x24, 0x40, 0x70, 0x5c, 0x2d, 0xbf, + 0x88, 0x08, 0xd0, 0xbf, 0x88, 0x5d, 0xc7, 0x3f, 0x78, 0x07, 0x36, 0xc0, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0xf8, 0xb1, 0x31, 0x40, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5b, 0x93, 0x3f, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, + 0x78, 0x07, 0xb6, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb2, 0x3e, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x70, 0x06, 0x1c, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x68, 0x06, 0x1c, 0xc0, + 0x90, 0x5e, 0x61, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, 0x70, 0x5c, 0x2d, 0x3f, + 0xa8, 0x5f, 0xfb, 0x3f, 0x9a, 0x85, 0x88, 0x40, 0xd8, 0x5a, 0x06, 0xc0, + 0x90, 0xb3, 0xd8, 0x3f, 0x70, 0x5c, 0xad, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0x5b, 0x93, 0x3f, 0x80, 0x5c, 0xad, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x88, 0x08, 0xd0, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x88, 0x08, 0xd0, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x78, 0x07, 0x36, 0x40, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x60, 0xb0, 0x8a, 0x3f, 0x70, 0x06, 0x1c, 0x3f, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0xbf, 0x70, 0xb1, 0xa4, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x58, 0x05, 0x82, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0xd0, 0xbe, + 0x60, 0x5b, 0x93, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x70, 0x5c, 0xad, 0x3f, 0x60, 0x06, 0x1c, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0xa8, 0x5f, 0xfb, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x90, 0x08, 0x50, 0x3f, 0x58, 0x05, 0x82, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x88, 0x08, 0xd0, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, 0x88, 0x08, 0xd0, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x78, 0x07, 0x36, 0x40, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0x68, 0x06, 0x9c, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x90, 0x5e, 0x61, 0xbf, 0x78, 0x07, 0xb6, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5b, 0x93, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x70, 0x5c, 0x2d, 0x3f, 0x70, 0x5c, 0xad, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb2, 0xbe, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0x78, 0x07, 0xb6, 0xbf, 0x70, 0x5c, 0xad, 0xbf, 0x58, 0x05, 0x82, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, 0x78, 0x07, 0xb6, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x78, 0x07, 0x36, 0x40, + 0x58, 0x05, 0x82, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x5d, 0xc7, 0xbf, 0x90, 0x08, 0x50, 0x3f, 0x70, 0x5c, 0xad, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x74, 0x5c, 0x2d, 0x40, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x68, 0x06, 0x9c, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb2, 0xbe, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x60, 0x06, 0x1c, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x78, 0x07, 0xb6, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0xc8, 0xdd, 0xcd, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x05, 0x82, 0x3f, 0x90, 0x5e, 0x61, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x07, 0xb6, 0x3f, 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x88, 0x08, 0xd0, 0x3f, + 0x70, 0x5c, 0xad, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x5b, 0x20, 0x40, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3f, 0x70, 0x06, 0x1c, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, 0x90, 0x5e, 0x61, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x0a, 0x3f, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0x06, 0x1c, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x68, 0x06, 0x9c, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x5b, 0x93, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0xe4, 0xb0, 0x17, 0xc0, + 0x80, 0xb2, 0xbe, 0x3f, 0xa0, 0xb4, 0xf2, 0x3f, 0x54, 0x05, 0x02, 0xc0, + 0x60, 0x06, 0x1c, 0xbf, 0x9a, 0x09, 0x6a, 0xc0, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0x5c, 0xad, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x68, 0x06, 0x9c, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, 0x90, 0x5e, 0x61, 0xbf, + 0xd8, 0x5a, 0x06, 0x40, 0x80, 0xb0, 0x0a, 0x3e, 0x88, 0x08, 0xd0, 0x3f, + 0x70, 0xb1, 0xa4, 0x3f, 0xa0, 0xb4, 0xf2, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x5c, 0xb0, 0x0a, 0x40, 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x70, 0xb1, 0xa4, 0x3f, + 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x58, 0xb0, 0x8a, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0xa0, 0x30, 0x11, 0xc1, 0x60, 0xb0, 0x8a, 0x3e, 0x1e, 0x5f, 0x6e, 0xc0, + 0x80, 0x08, 0x50, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0xa8, 0x5f, 0xfb, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0xe8, 0x5b, 0x20, 0xc0, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0xec, 0x5b, 0x20, 0x40, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0xe4, 0xb0, 0x17, 0x40, 0x70, 0x5c, 0xad, 0xbf, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x70, 0xb1, 0xa4, 0x3f, 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x88, 0x5d, 0xc7, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, + 0x58, 0xb0, 0x8a, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x5b, 0x93, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, 0x58, 0xb0, 0x8a, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5b, 0x13, 0xc0, 0x70, 0x06, 0x1c, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0xf6, 0xef, 0x00, 0xc1, 0x70, 0xb1, 0xa4, 0xbf, + 0x94, 0x5e, 0x61, 0xc0, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x72, 0x5c, 0x2d, 0xc0, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x72, 0x5c, 0x2d, 0xc0, 0x60, 0x5c, 0xad, 0xbe, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x88, 0x5d, 0xc7, 0x3f, 0x68, 0x06, 0x1c, 0x40, + 0x78, 0x07, 0xb6, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x68, 0x06, 0x1c, 0x40, + 0x90, 0xb3, 0xd8, 0xbf, 0x70, 0x06, 0x1c, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb2, 0xbe, 0x3f, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x70, 0x5c, 0xad, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x58, 0x05, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x58, 0x05, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x78, 0x07, 0xb6, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0xe0, 0x05, 0x0f, 0xc0, 0xb1, 0x31, 0xab, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0xf6, 0xb1, 0x31, 0xc0, 0xa0, 0xb4, 0xf2, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0xfb, 0x5c, 0xba, 0xc0, 0x70, 0x5c, 0xad, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5d, 0xc7, 0xbf, + 0xe0, 0x05, 0x0f, 0x40, 0x80, 0xb2, 0xbe, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0x09, 0xea, 0x3f, 0x68, 0x06, 0x9c, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x0a, 0xbf, 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5b, 0x93, 0x3f, + 0x90, 0x5e, 0x61, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x8a, 0x3f, 0x90, 0x5e, 0x61, 0xbf, 0x58, 0xb0, 0x8a, 0xbf, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x58, 0xb0, 0x8a, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0x50, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x58, 0x05, 0x82, 0xbf, + 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x58, 0x05, 0x82, 0x3f, + 0x58, 0xb0, 0x8a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0xe0, 0x05, 0x0f, 0xc0, + 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0x06, 0x1c, 0xbf, 0x5c, 0xb0, 0x0a, 0xc0, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5b, 0x93, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x94, 0x5e, 0x61, 0xc0, + 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0x5b, 0x13, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, + 0x78, 0x07, 0xb6, 0xbf, 0x88, 0x5d, 0xc7, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x90, 0xb3, 0xd8, 0x3f, 0x78, 0x07, 0xb6, 0xbf, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5b, 0x93, 0x3f, 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0xd8, 0x5a, 0x06, 0xc0, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0xad, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x68, 0x06, 0x9c, 0x3f, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x54, 0x05, 0x02, 0xc0, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x5e, 0x61, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x78, 0x07, 0xb6, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x5c, 0xad, 0x3f, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0x3f, + 0x90, 0xb3, 0xd8, 0xbf, 0x58, 0x05, 0x02, 0x40, 0x60, 0x06, 0x1c, 0xbf, + 0xe4, 0xb0, 0x17, 0xc0, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x90, 0xb3, 0x58, 0xc0, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x29, 0x31, 0x9e, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, 0x8a, 0x08, 0x50, 0xc0, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb2, 0xbe, 0xbf, 0xa0, 0x09, 0xea, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x0a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x58, 0x05, 0x02, 0x40, + 0x80, 0x5d, 0xc7, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x68, 0x06, 0x9c, 0x3f, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb2, 0x3e, 0x40, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, + 0x88, 0x5d, 0xc7, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x70, 0x5c, 0x2d, 0xbf, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0xd0, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, 0x98, 0x09, 0xea, 0xbf, + 0xe4, 0xb0, 0x17, 0xc0, 0x90, 0x08, 0x50, 0x3f, 0x78, 0x07, 0xb6, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x60, 0x5b, 0x13, 0xc0, + 0x60, 0x5b, 0x93, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0xad, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x08, 0x50, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x64, 0x5b, 0x13, 0x40, 0xa0, 0xb4, 0xf2, 0x3e, + 0xa0, 0xb4, 0x72, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0xa0, 0xb4, 0xf2, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x90, 0xb3, 0xd8, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x90, 0xb3, 0xd8, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, + 0x84, 0x5d, 0x47, 0xc0, 0x6c, 0xb1, 0x24, 0xc0, 0xa8, 0x5f, 0xfb, 0xbf, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x5d, 0xc7, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x17, 0x30, 0x84, 0xc0, + 0x60, 0x5b, 0x93, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbe, 0x88, 0x08, 0xd0, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0x5c, 0xad, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x64, 0x5b, 0x13, 0x40, 0x60, 0xb0, 0x8a, 0x3e, + 0x58, 0x05, 0x82, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0x5b, 0x93, 0x3f, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x1c, 0xbf, + 0x70, 0xb1, 0xa4, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5b, 0x93, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x5d, 0xc7, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0xfc, 0x5c, 0x3a, 0xc0, 0x90, 0x5e, 0x61, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0xea, 0x5b, 0xa0, 0x40, 0x80, 0x5d, 0xc7, 0xbf, + 0x58, 0x05, 0x82, 0xbf, 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x5c, 0xb0, 0x0a, 0x40, 0x60, 0x5b, 0x93, 0x3f, 0x78, 0x07, 0xb6, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x5c, 0x2d, 0x40, 0x64, 0x5b, 0x13, 0x40, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0xe4, 0xb0, 0x17, 0x40, + 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x58, 0x05, 0x82, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x70, 0x06, 0x1c, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x68, 0x06, 0x9c, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0x3f, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x06, 0x1c, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x70, 0xb1, 0xa4, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, 0x98, 0x09, 0xea, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0x78, 0x07, 0xb6, 0x3f, 0x9c, 0x09, 0x6a, 0x40, + 0x70, 0x5c, 0xad, 0xbf, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0x5e, 0x61, 0x3f, 0x58, 0x05, 0x82, 0x3f, + 0x98, 0x5e, 0xe1, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x64, 0x5b, 0x13, 0x40, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x58, 0x05, 0x82, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0x3f, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0x06, 0x1c, 0xbf, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x5e, 0x61, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0xb3, 0x58, 0xc0, 0x68, 0x06, 0x9c, 0xbf, + 0x68, 0x06, 0x9c, 0xbf, 0x70, 0x5c, 0xad, 0x3f, 0xe4, 0xb0, 0x17, 0x40, + 0x18, 0xb4, 0x65, 0x40, 0x60, 0x5b, 0x93, 0xbf, 0xe0, 0x05, 0x0f, 0xc0, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3f, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb2, 0x3e, 0x3f, 0x5c, 0xb0, 0x0a, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0xe4, 0xb0, 0x17, 0x40, 0x80, 0x08, 0xd0, 0x3e, + 0x58, 0x05, 0x82, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x68, 0x06, 0x9c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0x5b, 0x93, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb2, 0xbe, 0xbf, 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0x3f, + 0x90, 0x5e, 0x61, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0xd0, 0xbe, 0x90, 0xb3, 0xd8, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x08, 0x50, 0x3f, 0x90, 0x5e, 0x61, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5b, 0x13, 0xc0, + 0x70, 0xb1, 0xa4, 0xbf, 0x00, 0x08, 0x43, 0xc0, 0x58, 0x05, 0x82, 0x3f, + 0xfc, 0x5c, 0x3a, 0x40, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x54, 0x05, 0x02, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0xd8, 0x5a, 0x06, 0x40, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5b, 0x93, 0x3f, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, + 0xa0, 0xb4, 0x72, 0x3f, 0x60, 0xb0, 0x8a, 0x3f, 0x78, 0x07, 0xb6, 0x3f, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0xe0, 0x05, 0x0f, 0x40, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5b, 0x93, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0x5b, 0x93, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x06, 0x9c, 0xbf, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5b, 0x93, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, + 0x90, 0x5e, 0x61, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x06, 0x1c, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0x3e, 0xe4, 0xb0, 0x17, 0xc0, + 0x58, 0x05, 0x82, 0x3f, 0x78, 0x07, 0xb6, 0x3f, 0xa6, 0x5f, 0xfb, 0xc0, + 0x40, 0xb0, 0x0a, 0xbe, 0x5c, 0xb0, 0x0a, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x78, 0x07, 0xb6, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x58, 0xb0, 0x8a, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0xe4, 0xb0, 0x17, 0x40, 0x60, 0x06, 0x1c, 0xbf, 0x90, 0x08, 0x50, 0x3f, + 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0xbf, + 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0x3f, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x70, 0xb1, 0xa4, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x58, 0x05, 0x82, 0xbf, + 0x90, 0x5e, 0x61, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x05, 0x82, 0xbf, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0x08, 0x50, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0xf0, 0x06, 0x29, 0xc1, 0x70, 0x5c, 0x2d, 0x3f, 0xa8, 0x5f, 0xfb, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0xfc, 0x5c, 0x3a, 0xc0, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0x3f, 0xe4, 0xb0, 0x17, 0x40, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x06, 0x1c, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0xb3, 0xd8, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, + 0x58, 0x05, 0x82, 0x3f, 0x80, 0xb2, 0xbe, 0x3f, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0x08, 0xd0, 0x3e, 0x62, 0x5b, 0x13, 0xc1, 0x70, 0x5c, 0x2d, 0xbf, + 0xe4, 0xb0, 0x17, 0xc0, 0x60, 0xb0, 0x8a, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0xad, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x68, 0x06, 0x9c, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, 0x58, 0x05, 0x82, 0xbf, + 0xa8, 0x5f, 0xfb, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0xec, 0x5b, 0x20, 0x40, + 0x40, 0xb0, 0x0a, 0xbe, 0x78, 0x07, 0xb6, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x68, 0x06, 0x9c, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x88, 0x08, 0xd0, 0x3f, + 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0xad, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0xfc, 0x5c, 0x3a, 0xc0, 0x56, 0x89, 0xe3, 0xc0, + 0x70, 0x5c, 0x2d, 0x3f, 0xd8, 0x5a, 0x06, 0xc0, 0x88, 0x5d, 0xc7, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, 0x70, 0xb1, 0xa4, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb2, 0xbe, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x5d, 0xc7, 0xbf, + 0x18, 0xb4, 0x65, 0xc0, 0x88, 0x08, 0xd0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x58, 0x05, 0x02, 0x40, 0x40, 0xb0, 0x0a, 0xbe, 0x58, 0x05, 0x02, 0x40, + 0x60, 0xb0, 0x8a, 0x3e, 0x58, 0x05, 0x82, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x5e, 0x61, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x07, 0xb6, 0x3f, 0xa0, 0xb4, 0x72, 0x3f, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x58, 0x05, 0x82, 0xbf, + 0x80, 0xb2, 0xbe, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0xe0, 0x05, 0x0f, 0xc0, + 0xdf, 0x05, 0x0f, 0xc1, 0x70, 0x5c, 0x2d, 0x3f, 0x98, 0x5e, 0xe1, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x5b, 0x20, 0xc0, + 0x70, 0xb1, 0xa4, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x88, 0x5d, 0xc7, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x88, 0x08, 0xd0, 0xbf, 0x1d, 0xdb, 0x8c, 0xc0, 0x78, 0x07, 0x36, 0x40, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x64, 0x5b, 0x13, 0x40, 0xa0, 0xb4, 0xf2, 0xbe, + 0x58, 0x05, 0x02, 0x40, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x08, 0x50, 0x3f, + 0x70, 0x5c, 0x2d, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x90, 0x5e, 0x61, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x60, 0x5b, 0x93, 0x3f, + 0x60, 0xb0, 0x8a, 0x3f, 0x68, 0x06, 0x1c, 0x40, 0x80, 0xb0, 0x0a, 0x3e, + 0xfc, 0x5c, 0x3a, 0xc0, 0xd4, 0x33, 0xdf, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, + 0x90, 0xb3, 0xd8, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5d, 0xc7, 0xbf, 0xe4, 0xb0, 0x17, 0x40, 0x80, 0xb0, 0x8a, 0x3d, + 0x78, 0x07, 0xb6, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0xf2, 0xbf, 0x6c, 0xb1, 0x24, 0xc0, + 0xe4, 0xb0, 0x17, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x1c, 0xbf, + 0xa0, 0xb4, 0xf2, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x68, 0x06, 0x9c, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0x09, 0xea, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0x09, 0xea, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x90, 0x08, 0x50, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x06, 0x1c, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0xa0, 0xb4, 0xf2, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x98, 0x5e, 0xe1, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0xf2, 0xbf, 0xf6, 0xb1, 0x31, 0xc0, + 0x60, 0x5c, 0xad, 0xbe, 0x98, 0x5e, 0xe1, 0xbf, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0xe0, 0x05, 0x0f, 0xc0, 0x98, 0x5e, 0xe1, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x0c, 0x5e, 0x54, 0xc0, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x94, 0x5e, 0x61, 0xc0, + 0x5c, 0xb0, 0x8a, 0xc0, 0xf8, 0xb1, 0x31, 0x40, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x5b, 0x93, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x68, 0x06, 0x9c, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0xe4, 0xb0, 0x17, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x06, 0x9c, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x68, 0x06, 0x9c, 0x3f, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0xd0, 0x3e, 0x70, 0x06, 0x1c, 0x3f, 0x58, 0x05, 0x82, 0x3f, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0x5b, 0x13, 0xc0, 0x90, 0x08, 0x50, 0x3f, + 0x60, 0x5b, 0x13, 0xc0, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb2, 0xbe, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0xbf, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x5c, 0x2d, 0xbf, + 0x90, 0xb3, 0xd8, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0x5c, 0x2d, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, + 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x58, 0x05, 0x82, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0x72, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x09, 0xea, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xf6, 0xb1, 0x31, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb2, 0xbe, 0xbf, 0xf0, 0x06, 0x29, 0xc0, + 0x5c, 0xb0, 0x0a, 0xc0, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0x2d, 0xbf, + 0x90, 0xb3, 0xd8, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x06, 0x1c, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0x72, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, 0x18, 0x30, 0x84, 0x40, + 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x78, 0x07, 0xb6, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb2, 0x3e, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x18, 0xb4, 0x65, 0x40, 0x68, 0x06, 0x9c, 0x3f, + 0x68, 0x06, 0x1c, 0x40, 0x60, 0x5c, 0xad, 0xbe, 0x6c, 0xb1, 0x24, 0x40, + 0x68, 0x06, 0x9c, 0x3f, 0x78, 0x07, 0xb6, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x5e, 0x61, 0xbf, 0xa0, 0xb4, 0xf2, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x06, 0x1c, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x58, 0x05, 0x82, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb2, 0x3e, 0x3f, 0xe0, 0x05, 0x0f, 0x40, + 0x08, 0xb3, 0x4b, 0x40, 0x90, 0xb3, 0xd8, 0xbf, 0x68, 0x06, 0x9c, 0x3f, + 0x60, 0x5b, 0x93, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0xbe, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0xf0, 0x06, 0x29, 0x40, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb2, 0xbe, 0x3f, 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x8a, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0xbf, 0x90, 0xb3, 0xd8, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0xa0, 0xb4, 0xf2, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0xb0, 0x0a, 0x3f, 0x90, 0x08, 0x50, 0x3f, + 0x60, 0x06, 0x1c, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x90, 0x5e, 0x61, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x5e, 0x61, 0x3f, 0x90, 0x5e, 0x61, 0x3f, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x64, 0x5b, 0x13, 0x40, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0x2d, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x5b, 0x93, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x78, 0x07, 0xb6, 0x3f, 0x80, 0x08, 0xd0, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0x5c, 0xad, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x0a, 0xbf, 0x90, 0x5e, 0x61, 0x3f, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x06, 0x1c, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0xbf, + 0x90, 0xb3, 0xd8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x70, 0xb1, 0xa4, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x90, 0x08, 0x50, 0x3f, + 0x60, 0xb0, 0x8a, 0x3e, 0xa8, 0x5f, 0xfb, 0x3f, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x70, 0x5c, 0xad, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, 0xa0, 0x09, 0xea, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x98, 0x09, 0xea, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x58, 0xb0, 0x8a, 0xbf, + 0x08, 0xb3, 0x4b, 0x40, 0xa0, 0x09, 0xea, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0x08, 0xd0, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbf, 0xa0, 0xb4, 0xf2, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb2, 0x3e, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0xa8, 0x5f, 0xfb, 0xbf, 0x80, 0x08, 0xd0, 0xbe, + 0x90, 0x5e, 0x61, 0xbf, 0x60, 0x5b, 0x93, 0x3f, 0x6c, 0xb1, 0x24, 0x40, + 0xa0, 0xb4, 0xf2, 0xbf, 0x88, 0x08, 0xd0, 0xbf, 0x60, 0x06, 0x1c, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x06, 0xa9, 0xc0, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x94, 0x5e, 0x61, 0xc0, 0x58, 0x05, 0x02, 0x40, 0x74, 0x5c, 0x2d, 0x40, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0xf2, 0xbe, 0x58, 0x05, 0x82, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0xd0, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x98, 0x5e, 0xe1, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x0a, 0x3e, + 0x70, 0x06, 0x1c, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0xbe, + 0x90, 0x08, 0x50, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb2, 0x3e, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0xbe, 0x80, 0x08, 0x50, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0xac, 0x86, 0xa2, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x06, 0x9c, 0xbf, 0x80, 0x08, 0xd0, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x56, 0x05, 0x02, 0xc1, 0x80, 0x5d, 0xc7, 0xbf, 0x68, 0x06, 0x9c, 0x3f, + 0x80, 0xb0, 0x0a, 0x3e, 0x84, 0x5d, 0xc7, 0xc0, 0xbd, 0x87, 0xbc, 0xc0, + 0x70, 0x5c, 0xad, 0x3f, 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb2, 0x3e, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, 0x90, 0x08, 0x50, 0x3f, + 0x58, 0xb0, 0x8a, 0xbf, 0x90, 0x5e, 0x61, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x98, 0x5e, 0xe1, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, 0xa0, 0xb4, 0xf2, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0xb0, 0x0a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x0a, 0xbf, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3f, 0x12, 0x09, 0xdd, 0xc0, + 0xa0, 0xb4, 0x72, 0xbf, 0x78, 0x07, 0xb6, 0xbf, 0x60, 0xb0, 0x8a, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3f, 0xd8, 0x5a, 0x06, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x9a, 0x09, 0x6a, 0xc0, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0x98, 0x09, 0xea, 0xbf, + 0xa6, 0xdb, 0x99, 0xc0, 0x88, 0x5d, 0xc7, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x68, 0x06, 0x9c, 0x3f, 0x80, 0xb2, 0x3e, 0xbf, 0x58, 0x05, 0x82, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x5c, 0xad, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0xbe, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x08, 0x50, 0x3f, 0xa0, 0xb4, 0x72, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x0a, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x90, 0x5e, 0x61, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, + 0x70, 0x5c, 0x2d, 0x3f, 0x90, 0x5e, 0x61, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0xd0, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, 0x70, 0x5c, 0x2d, 0xbf, + 0xa6, 0xdb, 0x19, 0xc1, 0x68, 0x06, 0x9c, 0xbf, 0x78, 0x07, 0xb6, 0xbf, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0x3e, + 0x88, 0x08, 0xd0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x58, 0xc0, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0x70, 0x5c, 0xad, 0xbf, 0x9a, 0x85, 0x08, 0xc1, 0x78, 0x07, 0xb6, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0xa0, 0xb4, 0xf2, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x5c, 0xad, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, 0x60, 0xb0, 0x0a, 0xbf, + 0x68, 0x06, 0x9c, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x06, 0x1c, 0xbf, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb0, 0x8a, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, + 0x68, 0x06, 0x9c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, + 0x90, 0x5e, 0x61, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, + 0x90, 0x08, 0x50, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, + 0x58, 0x05, 0x82, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0x60, 0xb0, 0x8a, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x90, 0x08, 0x50, 0x3f, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0xa0, 0xb4, 0xf2, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0xb4, 0x72, 0xbf, + 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb2, 0x3e, 0x3f, + 0xa0, 0xb4, 0xf2, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0xe8, 0x5b, 0x20, 0xc0, 0xf6, 0xef, 0x00, 0xc1, 0x60, 0xb0, 0x0a, 0xbf, + 0x78, 0x07, 0xb6, 0xbf, 0x58, 0x05, 0x82, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x58, 0x05, 0x82, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x17, 0x30, 0x84, 0xc0, 0x80, 0xb0, 0x8a, 0xbd, 0x98, 0x5e, 0xe1, 0x3f, + 0x80, 0x08, 0x50, 0xbe, 0xa0, 0xb4, 0xf2, 0xbf, 0xea, 0x5b, 0xa0, 0xc0, + 0x88, 0x08, 0xd0, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x0a, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0x50, 0x3e, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x0a, 0x3f, 0x78, 0x07, 0xb6, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x68, 0x06, 0x9c, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0x08, 0xd0, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0xd0, 0x3e, 0xa0, 0xb4, 0xf2, 0x3e, + 0x60, 0xb0, 0x8a, 0x3f, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x0a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x0a, 0x3f, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0xbe, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb2, 0xbe, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5d, 0xc7, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0x50, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xb4, 0x72, 0x3f, 0x80, 0xb0, 0x8a, 0x3d, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0x50, 0xbf, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x5c, 0xad, 0x3e, + 0x80, 0xb2, 0xbe, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x5d, 0xc7, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x0a, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x88, 0x5d, 0xc7, 0x3f, 0xa0, 0x09, 0xea, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x5c, 0x3a, 0xc0, 0xa0, 0xb4, 0x72, 0xc0, + 0x80, 0x08, 0x50, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, 0x70, 0x5c, 0x2d, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0xb0, 0x8a, 0xc0, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x08, 0xd0, 0x3e, 0xa0, 0xb4, 0xf2, 0xbe, 0x90, 0x5e, 0x61, 0xbf, + 0x12, 0x09, 0x5d, 0xc0, 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x0a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x80, 0x08, 0x50, 0xbe, 0x90, 0xb3, 0xd8, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xb0, 0x0a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x80, 0x08, 0x50, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0x40, 0xb0, 0x0a, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x78, 0x07, 0xb6, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0xa0, 0x09, 0xea, 0x3f, + 0x60, 0x06, 0x1c, 0xbf, 0x58, 0xb0, 0x8a, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, + 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5c, 0xad, 0x3e, + 0x98, 0x09, 0xea, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x0a, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb2, 0x3e, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x60, 0xb0, 0x0a, 0xbf, 0x90, 0x5e, 0x61, 0xbf, 0x80, 0x08, 0x50, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, 0x70, 0x5c, 0xad, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0x50, 0xbe, 0x70, 0x5c, 0x2d, 0x3f, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x5c, 0xad, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0x08, 0xd0, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, + 0x80, 0x08, 0x50, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0xa0, 0xb4, 0xf2, 0xbe, + 0x58, 0x05, 0x82, 0xbf, 0x60, 0xb0, 0x8a, 0x3e, 0x60, 0xb0, 0x0a, 0xbf, + 0x40, 0xb0, 0x0a, 0xbe, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0x5c, 0xad, 0x3e, + 0x60, 0xb0, 0x8a, 0xbe, 0x70, 0x06, 0x1c, 0x3f, 0x80, 0xb0, 0x0a, 0x3e, + 0x60, 0xb0, 0x8a, 0x3e, 0xa0, 0xb4, 0x72, 0xbf, 0x80, 0xb0, 0x8a, 0xbd, + 0x90, 0x08, 0x50, 0x3f, 0x60, 0xb0, 0x8a, 0xbe, 0x80, 0x08, 0xd0, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x0a, 0xbf, 0x60, 0x5c, 0xad, 0xbe, + 0x60, 0x5c, 0xad, 0xbe, 0xa0, 0xb4, 0x72, 0x3f, 0xfc, 0x5c, 0x3a, 0x40, + 0xa0, 0xb4, 0x72, 0xbf, 0x60, 0x5b, 0x93, 0xbf, 0x80, 0xb2, 0xbe, 0xbf, + 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0x08, 0xd0, 0x3e, 0x78, 0x07, 0xb6, 0xbf, + 0x58, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0x80, 0x08, 0x50, 0x3e, 0x40, 0xb0, 0x0a, 0xbe, 0x67, 0x06, 0x9c, 0xc0, + 0x58, 0x05, 0x82, 0x3f, 0x6c, 0xb1, 0x24, 0x40, 0x60, 0xb0, 0x8a, 0xbe, + 0x70, 0x5c, 0xad, 0xbf, 0x80, 0xb2, 0xbe, 0xbf, 0x88, 0x5d, 0xc7, 0x3f, + 0x60, 0xb0, 0x0a, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x06, 0x1c, 0xbf, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0xb0, 0x8a, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0x5c, 0xad, 0xbe, 0x80, 0xb0, 0x8a, 0xbd, + 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0x08, 0x50, 0x3e, 0x70, 0xb1, 0xa4, 0xbf, + 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0xb0, 0x8a, 0x3d, 0x80, 0x08, 0x50, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xb0, 0x8a, 0x3d, 0x60, 0xb0, 0x8a, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x5b, 0x93, 0x3f, 0x60, 0x06, 0x1c, 0xbf, 0x80, 0xb2, 0x3e, 0xbf, + 0x70, 0x5c, 0x2d, 0xbf, 0x80, 0xb0, 0x8a, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x5c, 0xad, 0x3e, 0x68, 0x06, 0x9c, 0xbf, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, 0x60, 0xb0, 0x8a, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x70, 0x06, 0x1c, 0x3f, 0x80, 0x08, 0x50, 0xbe, + 0x80, 0x08, 0xd0, 0xbe, 0x60, 0x5c, 0xad, 0xbe, 0x60, 0x5c, 0xad, 0xbe, + 0x80, 0x5c, 0xad, 0x3e, 0x60, 0x06, 0x1c, 0xbf, 0x90, 0x5e, 0x61, 0x3f, + 0x60, 0x5c, 0xad, 0xbe, 0x80, 0x08, 0xd0, 0xbe, 0x80, 0x08, 0x50, 0x3e, + 0x80, 0x08, 0xd0, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, 0xa0, 0xb4, 0xf2, 0x3e, + 0xa0, 0xb4, 0xf2, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x60, 0xb0, 0x0a, 0xbf, 0xa8, 0x5f, 0xfb, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, + 0x60, 0xb0, 0x8a, 0xbe, 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x88, 0x08, 0xd0, 0x3f, 0xa0, 0xb4, 0x72, 0x3f, 0x70, 0x06, 0x1c, 0x3f, + 0x80, 0x5c, 0xad, 0x3e, 0x80, 0x5d, 0xc7, 0xbf, 0x70, 0xb1, 0xa4, 0xbf, + 0x70, 0x5c, 0x2d, 0x3f, 0x58, 0xb0, 0x8a, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x8a, 0xbd, 0x60, 0xb0, 0x8a, 0x3e, 0x80, 0xb2, 0x3e, 0x3f, + 0x40, 0xb0, 0x0a, 0xbe, 0x12, 0x09, 0x5d, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x5c, 0x2d, 0x3f, 0x80, 0xb0, 0x8a, 0xbd, 0x80, 0xb0, 0x8a, 0xbd, + 0x98, 0x5e, 0xe1, 0xbf, 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0xd0, 0x3e, + 0x60, 0x5c, 0xad, 0xbe, 0x60, 0xb0, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x06, 0x1c, 0xbf, 0x80, 0x08, 0x50, 0xbf, 0x70, 0x06, 0x1c, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0xb4, 0xf2, 0xbe, 0x80, 0xb0, 0x8a, 0x3d, + 0x70, 0x06, 0x1c, 0x3f, 0x58, 0x05, 0x82, 0xbf, 0x40, 0xb0, 0x0a, 0xbe, + 0x80, 0xb0, 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x80, 0x08, 0xd0, 0xbe, + 0xa0, 0xb4, 0xf2, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x08, 0x50, 0x3e, 0x60, 0x5c, 0xad, 0xbe, 0x20, 0x5f, 0x6e, 0x40, + 0x68, 0x06, 0x1c, 0xc0, 0x68, 0x06, 0x1c, 0xc0, 0x60, 0xb0, 0x8a, 0xbe, + 0x60, 0xb0, 0x0a, 0x3f, 0x2e, 0xdc, 0xa6, 0xc0, 0x70, 0x06, 0x1c, 0x3f, + 0x60, 0x06, 0x1c, 0xbf, 0x58, 0xb0, 0x8a, 0xbf, 0x70, 0x5c, 0x2d, 0x3f, + 0x60, 0x5b, 0x93, 0xbf, 0x80, 0x5c, 0xad, 0x3e, 0x60, 0xb0, 0x8a, 0xbe, + 0x40, 0xb0, 0x0a, 0xbe, 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0xd0, 0xbe, + 0x90, 0x5e, 0x61, 0x3f, 0x80, 0x08, 0xd0, 0x3e, 0x70, 0xb1, 0xa4, 0x3f, + 0x90, 0x5e, 0x61, 0x3f, 0x60, 0xb0, 0x0a, 0x3f, 0x60, 0x5b, 0x93, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0x58, 0x05, 0x82, 0xbf, 0x70, 0x5c, 0xad, 0x3f, + 0xe0, 0x05, 0x0f, 0x40, 0x60, 0xb0, 0x0a, 0xbf, 0xa0, 0xb4, 0x72, 0xbf, + 0x90, 0x08, 0x50, 0x3f, 0xe8, 0x5b, 0x20, 0xc0, 0xfc, 0x5c, 0x3a, 0x40, + 0x80, 0x08, 0x50, 0x3e, 0x80, 0x08, 0x50, 0xbf, 0x90, 0xb3, 0xd8, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0x40, 0xb0, 0x0a, 0xbe, 0x70, 0x5c, 0x2d, 0xbf, + 0x90, 0x5e, 0x61, 0x3f, 0x8a, 0x08, 0x50, 0xc0, +}}; +const int32_t dnn_logits_bias__2__cf__2_shape[1] = {1}; +const union { + uint8_t bytes[4]; + float values[1]; +} dnn_logits_bias__2__cf__2 = {{ + 0x6c, + 0xee, + 0x8d, + 0xbf, +}}; +const int32_t dnn_logits_kernel__3__cf__3_shape[2] = {20, 1}; +const union { + uint8_t bytes[80]; + float values[20]; +} dnn_logits_kernel__3__cf__3 = {{ + 0x96, 0x59, 0x45, 0xbd, 0x84, 0x25, 0xf6, 0xbd, 0xe9, 0x7c, 0x89, 0x3e, + 0xa0, 0x66, 0x3c, 0xbe, 0x37, 0x01, 0x89, 0x3d, 0xe4, 0x97, 0x22, 0x3e, + 0x21, 0xf8, 0x84, 0xbe, 0x92, 0x74, 0x8d, 0x3e, 0x30, 0xde, 0x0f, 0xbe, + 0x88, 0x44, 0x3b, 0xbe, 0x42, 0x78, 0xb8, 0x3d, 0x0a, 0xf2, 0xf6, 0xbd, + 0x48, 0x60, 0xd6, 0x3f, 0x2c, 0x86, 0x83, 0x3d, 0x92, 0xcc, 0xb6, 0x3e, + 0xab, 0xc5, 0x4b, 0x3e, 0x6f, 0x6e, 0x1f, 0x3f, 0x07, 0xcd, 0x32, 0x3e, + 0x5d, 0xb3, 0xb2, 0x3d, 0x31, 0x53, 0xf6, 0x3d, +}}; + +} // anonymous namespace + +// ----------------------------------------------------------------------------- +// INFERENCE +// ----------------------------------------------------------------------------- + +int32_t input_from_feature_columns_input_layer_concat_concat0Shape[2] = {1, + 325}; +int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1}; + +void Inference( + const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,325 */ + , + float* __restrict logits_MatMul_merged_with_dnn_logits_BiasAdd0 /* shape: + 1,1 */ + , + FixedAllocations* __restrict fixed) { + const int32_t input_from_feature_columns_input_layer_concat_concat0_shape[] = + {1, 325}; + +#if OP_LIB_BENCHMARK + Singleton<PerOpTimings>::get()->Reset(); +#endif + + // dnn/hiddenlayer_0/MatMul_merged_with_dnn/hiddenlayer_0/BiasAdd + FullyConnected<float>( + input_from_feature_columns_input_layer_concat_concat0_shape, + input_from_feature_columns_input_layer_concat_concat0, + dnn_hiddenlayer_0_kernel__1__cf__1_shape, + dnn_hiddenlayer_0_kernel__1__cf__1.values, + dnn_hiddenlayer_0_bias__0__cf__0_shape, + dnn_hiddenlayer_0_bias__0__cf__0.values, fixed->alloc0); + + fixed->shape0[0] = 1; + fixed->shape0[1] = 20; + + // dnn/hiddenlayer_0/Relu + Relu<float>(2, // rank + fixed->shape0, fixed->alloc0, fixed->alloc0); + + // dnn/logits/MatMul_merged_with_dnn/logits/BiasAdd + FullyConnected<float>( + fixed->shape0, fixed->alloc0, dnn_logits_kernel__3__cf__3_shape, + dnn_logits_kernel__3__cf__3.values, dnn_logits_bias__2__cf__2_shape, + dnn_logits_bias__2__cf__2.values, + logits_MatMul_merged_with_dnn_logits_BiasAdd0); + +#if OP_LIB_BENCHMARK + Singleton<PerOpTimings>::get()->WriteTimingsToInfoLog(); +#endif +} + +} // namespace ui::internal_onedevice::beta_model diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h new file mode 100644 index 00000000000..efac7f4755b --- /dev/null +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h @@ -0,0 +1,40 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Code generated by tf.native. +#ifndef UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_BETA_H_ +#define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_BETA_H_ +#include <cstdint> + +namespace ui::internal_onedevice::beta_model { +struct alignas(16) FixedAllocations { + float alloc0[20]; + int32_t shape0[2]; +}; + +extern int32_t input_from_feature_columns_input_layer_concat_concat0Shape[2]; +extern int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2]; + +#define CHROME_KNOWLEDGE_INPUT_FROM_FEATURE_COLUMNS_INPUT_LAYER_CONCAT_CONCAT0_RANK_BETA \ + 2 +#define CHROME_KNOWLEDGE_INPUT_FROM_FEATURE_COLUMNS_INPUT_LAYER_CONCAT_CONCAT0_DIM0_SIZE_BETA \ + 1 +#define CHROME_KNOWLEDGE_INPUT_FROM_FEATURE_COLUMNS_INPUT_LAYER_CONCAT_CONCAT0_DIM1_SIZE_BETA \ + 325 +#define CHROME_KNOWLEDGE_LOGITS_MATMUL_MERGED_WITH_DNN_LOGITS_BIASADD0_RANK_BETA \ + 2 +#define CHROME_KNOWLEDGE_LOGITS_MATMUL_MERGED_WITH_DNN_LOGITS_BIASADD0_DIM0_SIZE_BETA \ + 1 +#define CHROME_KNOWLEDGE_LOGITS_MATMUL_MERGED_WITH_DNN_LOGITS_BIASADD0_DIM1_SIZE_BETA \ + 1 + +void Inference( + const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,325 */ + , + float* __restrict logits_MatMul_merged_with_dnn_logits_BiasAdd0 /* shape: + 1,1 */ + , + FixedAllocations* __restrict fixed); + +} // namespace ui::internal_onedevice::beta_model +#endif // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_BETA_H_ diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc index fd3add9ccd8..c4318783699 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc @@ -14,7 +14,7 @@ #define USE_EIGEN 0 #endif -namespace ui::v2 { +namespace ui::internal_onedevice::alpha_model_v2 { namespace { // ----------------------------------------------------------------------------- @@ -15349,7 +15349,7 @@ const union { int32_t input_from_feature_columns_input_layer_concat_concat0Shape[2] = {1, 173}; int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1}; -namespace internal_onedevice { + void Inference( const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,173 */ , @@ -15419,5 +15419,4 @@ void Inference( Singleton<PerOpTimings>::get()->WriteTimingsToInfoLog(); #endif } -} // namespace internal_onedevice -} // namespace ui::v2 +} // namespace ui::internal_onedevice::alpha_model_v2 diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h index 733d0bd4e74..97b53666cc2 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h @@ -6,7 +6,7 @@ #define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_V2_H_ #include <cstdint> -namespace ui::v2::internal_onedevice { +namespace ui::internal_onedevice::alpha_model_v2 { struct alignas(16) FixedAllocations { float alloc0[117]; float alloc1[115]; @@ -36,5 +36,5 @@ void Inference( , FixedAllocations* __restrict fixed); -} // namespace ui::v2::internal_onedevice +} // namespace ui::internal_onedevice::alpha_model_v2 #endif // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_V2_H_ diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc index 2eeaf29c573..acf43d8b6fd 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc @@ -19,15 +19,24 @@ #include "base/feature_list.h" #include "base/logging.h" #include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h" +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_beta.h" #include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h" #include "ui/events/ozone/features.h" #define USE_EIGEN 0 namespace ui { +namespace { +const std::string kBetaVersion = "beta"; +} + +namespace alpha = internal_onedevice::alpha_model; +namespace alpha_v2 = internal_onedevice::alpha_model_v2; +namespace beta = internal_onedevice::beta_model; float OneDeviceTrainNeuralStylusPalmDetectionFilterModel::Inference( const std::vector<float>& features) const { DVLOG(1) << "In Inference."; + if (features.size() != expected_feature_size_) { LOG(DFATAL) << "Bad count. Is " << features.size() << " expected " << expected_feature_size_; @@ -40,16 +49,20 @@ float OneDeviceTrainNeuralStylusPalmDetectionFilterModel::Inference( } } float output = 0; - if (base::FeatureList::IsEnabled(kEnableNeuralPalmRejectionModelV2)) { - std::unique_ptr<v2::internal_onedevice::FixedAllocations> fixed_allocations( - new v2::internal_onedevice::FixedAllocations()); - v2::internal_onedevice::Inference(&features[0], &output, - fixed_allocations.get()); + if (config_.model_version == kBetaVersion) { + std::unique_ptr<beta::FixedAllocations> fixed_allocations( + new beta::FixedAllocations()); + beta::Inference(&features[0], &output, fixed_allocations.get()); } else { - std::unique_ptr<internal_onedevice::FixedAllocations> fixed_allocations( - new internal_onedevice::FixedAllocations()); - internal_onedevice::Inference(&features[0], &output, - fixed_allocations.get()); + if (base::FeatureList::IsEnabled(kEnableNeuralPalmRejectionModelV2)) { + std::unique_ptr<alpha_v2::FixedAllocations> fixed_allocations( + new alpha_v2::FixedAllocations()); + alpha_v2::Inference(&features[0], &output, fixed_allocations.get()); + } else { + std::unique_ptr<alpha::FixedAllocations> fixed_allocations( + new alpha::FixedAllocations()); + alpha::Inference(&features[0], &output, fixed_allocations.get()); + } } return output; } @@ -59,43 +72,57 @@ OneDeviceTrainNeuralStylusPalmDetectionFilterModel::config() const { return config_; } -OneDeviceTrainNeuralStylusPalmDetectionFilterModel:: - OneDeviceTrainNeuralStylusPalmDetectionFilterModel() { +void OneDeviceTrainNeuralStylusPalmDetectionFilterModel::Initialize() { // Common configurations: config_.include_sequence_count_in_strokes = true; - config_.max_neighbor_distance_in_mm = 100.0f; config_.max_dead_neighbor_time = base::Milliseconds(100.0f); config_.heuristic_palm_touch_limit = 20.0f; config_.heuristic_palm_area_limit = 400.0f; config_.max_blank_time = base::Milliseconds(100.0f); config_.nearest_neighbor_count = 0; - config_.biggest_near_neighbor_count = 4; - - if (base::FeatureList::IsEnabled(kEnableNeuralPalmRejectionModelV2)) { - config_.min_sample_count = 3; - config_.max_sample_count = 6; - config_.neighbor_min_sample_count = 1; - config_.output_threshold = 0.90271f; - expected_feature_size_ = 173; - if (base::FeatureList::IsEnabled(kEnableNeuralPalmAdaptiveHold)) { - config_.nn_delay_start_if_palm = true; - config_.early_stage_sample_counts = std::unordered_set<uint32_t>({2}); - } - } else { + if (config_.model_version == kBetaVersion) { + config_.max_neighbor_distance_in_mm = 200.0f; + config_.biggest_near_neighbor_count = 4; config_.min_sample_count = 5; config_.max_sample_count = 12; config_.neighbor_min_sample_count = 5; - config_.output_threshold = 2.519f; - expected_feature_size_ = 323; + config_.output_threshold = 4.465f; + config_.use_tracking_id_count = true; + config_.use_active_tracking_id_count = true; + expected_feature_size_ = 325; + } else { + config_.max_neighbor_distance_in_mm = 100.0f; + config_.biggest_near_neighbor_count = 4; + + if (base::FeatureList::IsEnabled(kEnableNeuralPalmRejectionModelV2)) { + config_.min_sample_count = 3; + config_.max_sample_count = 6; + config_.neighbor_min_sample_count = 1; + config_.output_threshold = 0.90271f; + expected_feature_size_ = 173; + + if (base::FeatureList::IsEnabled(kEnableNeuralPalmAdaptiveHold)) { + config_.nn_delay_start_if_palm = true; + config_.early_stage_sample_counts = std::unordered_set<uint32_t>({2}); + } + } else { + config_.min_sample_count = 5; + config_.max_sample_count = 12; + config_.neighbor_min_sample_count = 5; + config_.output_threshold = 2.519f; + expected_feature_size_ = 323; + } } } OneDeviceTrainNeuralStylusPalmDetectionFilterModel:: OneDeviceTrainNeuralStylusPalmDetectionFilterModel( - const std::vector<float>& radius_poly) - : OneDeviceTrainNeuralStylusPalmDetectionFilterModel() { + const std::string& model_version, + const std::vector<float>& radius_poly) { + config_.model_version = model_version; config_.radius_polynomial_resize = radius_poly; + Initialize(); } } // namespace ui diff --git a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h index a058d71a2d9..1690c5d2e43 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h +++ b/chromium/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h @@ -18,8 +18,8 @@ namespace ui { class COMPONENT_EXPORT(EVDEV) OneDeviceTrainNeuralStylusPalmDetectionFilterModel : public NeuralStylusPalmDetectionFilterModel { public: - OneDeviceTrainNeuralStylusPalmDetectionFilterModel(); explicit OneDeviceTrainNeuralStylusPalmDetectionFilterModel( + const std::string& model_version, const std::vector<float>& radius_poly); OneDeviceTrainNeuralStylusPalmDetectionFilterModel( @@ -32,6 +32,7 @@ class COMPONENT_EXPORT(EVDEV) OneDeviceTrainNeuralStylusPalmDetectionFilterModel const NeuralStylusPalmDetectionFilterModelConfig& config() const override; private: + void Initialize(); NeuralStylusPalmDetectionFilterModelConfig config_; size_t expected_feature_size_; }; diff --git a/chromium/ui/events/ozone/features.cc b/chromium/ui/events/ozone/features.cc index 0297d76a018..8e557b7c939 100644 --- a/chromium/ui/events/ozone/features.cc +++ b/chromium/ui/events/ozone/features.cc @@ -38,9 +38,15 @@ const base::Feature kEnablePalmSuppression{"EnablePalmSuppression", const base::Feature kLibinputHandleTouchpad{"LibinputHandleTouchpad", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kEnableFakeKeyboardHeuristic{ + "EnableFakeKeyboardHeuristic", base::FEATURE_DISABLED_BY_DEFAULT}; + extern const base::FeatureParam<std::string> kNeuralPalmRadiusPolynomial{ &kEnableNeuralPalmDetectionFilter, "neural_palm_radius_polynomial", ""}; +extern const base::FeatureParam<std::string> kNeuralPalmModelVersion{ + &kEnableNeuralPalmDetectionFilter, "neural_palm_model_version", ""}; + const base::FeatureParam<double> kHeuristicCancelThresholdSeconds{ &kEnableHeuristicPalmDetectionFilter, "heuristic_palm_cancel_threshold_seconds", 0.4}; @@ -58,6 +64,7 @@ const base::Feature kEnableInputEventLogging{"EnableInputEventLogging", constexpr char kOzoneNNPalmSwitchName[] = "ozone-nnpalm-properties"; constexpr char kOzoneNNPalmTouchCompatibleProperty[] = "touch-compatible"; +constexpr char kOzoneNNPalmModelVersionProperty[] = "model"; constexpr char kOzoneNNPalmRadiusPolynomialProperty[] = "radius-polynomial"; } // namespace ui diff --git a/chromium/ui/events/ozone/features.h b/chromium/ui/events/ozone/features.h index 6abe3e4f082..2cdfab53f23 100644 --- a/chromium/ui/events/ozone/features.h +++ b/chromium/ui/events/ozone/features.h @@ -35,6 +35,9 @@ COMPONENT_EXPORT(EVENTS_OZONE) extern const base::Feature kEnablePalmOnToolTypePalm; COMPONENT_EXPORT(EVENTS_OZONE) +extern const base::FeatureParam<std::string> kNeuralPalmModelVersion; + +COMPONENT_EXPORT(EVENTS_OZONE) extern const base::FeatureParam<std::string> kNeuralPalmRadiusPolynomial; COMPONENT_EXPORT(EVENTS_OZONE) @@ -56,14 +59,19 @@ COMPONENT_EXPORT(EVENTS_OZONE) extern const base::Feature kLibinputHandleTouchpad; COMPONENT_EXPORT(EVENTS_OZONE) +extern const base::Feature kEnableFakeKeyboardHeuristic; + +COMPONENT_EXPORT(EVENTS_OZONE) extern const char kOzoneNNPalmSwitchName[]; COMPONENT_EXPORT(EVENTS_OZONE) extern const char kOzoneNNPalmTouchCompatibleProperty[]; COMPONENT_EXPORT(EVENTS_OZONE) -extern const char kOzoneNNPalmRadiusPolynomialProperty[]; +extern const char kOzoneNNPalmModelVersionProperty[]; +COMPONENT_EXPORT(EVENTS_OZONE) +extern const char kOzoneNNPalmRadiusPolynomialProperty[]; } // namespace ui #endif // UI_EVENTS_OZONE_FEATURES_H_ |