From c36d2076305c2b14a6246fce55a5c56e21f7d7d0 Mon Sep 17 00:00:00 2001 From: Peter Whitesell Date: Tue, 16 May 2023 19:00:16 -0500 Subject: [PATCH 1/4] add support for horizontal scrolling --- kmk/hid.py | 42 ++++++++++++++++++++++++++++++------------ kmk/keys.py | 7 ++++--- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/kmk/hid.py b/kmk/hid.py index 7d0d20a95..606e44c0a 100644 --- a/kmk/hid.py +++ b/kmk/hid.py @@ -5,7 +5,9 @@ from storage import getmount from kmk.keys import FIRST_KMK_INTERNAL_KEY, ConsumerKey, ModifierKey, MouseKey -from kmk.utils import Debug, clamp +from kmk.utils import clamp, Debug + +debug = Debug(__name__) try: from adafruit_ble import BLERadio @@ -30,6 +32,7 @@ class HIDModes: class HIDReportTypes: KEYBOARD = 1 MOUSE = 2 + POINTER = 5 CONSUMER = 3 SYSCONTROL = 4 @@ -37,18 +40,20 @@ class HIDReportTypes: class HIDUsage: KEYBOARD = 0x06 MOUSE = 0x02 + POINTER = 0x01 CONSUMER = 0x01 SYSCONTROL = 0x80 class HIDUsagePage: CONSUMER = 0x0C - KEYBOARD = MOUSE = SYSCONTROL = 0x01 + KEYBOARD = MOUSE = POINTER = SYSCONTROL = 0x01 HID_REPORT_SIZES = { HIDReportTypes.KEYBOARD: 8, HIDReportTypes.MOUSE: 4, + HIDReportTypes.POINTER: 5, HIDReportTypes.CONSUMER: 2, HIDReportTypes.SYSCONTROL: 8, # TODO find the correct value for this } @@ -91,9 +96,9 @@ def __init__(self, **kwargs): self._cc_report = bytearray(HID_REPORT_SIZES[HIDReportTypes.CONSUMER] + 1) self._cc_report[0] = HIDReportTypes.CONSUMER self._cc_pending = False - - self._pd_report = bytearray(HID_REPORT_SIZES[HIDReportTypes.MOUSE] + 1) - self._pd_report[0] = HIDReportTypes.MOUSE + if self._pd_report is None: + self._pd_report = bytearray(HID_REPORT_SIZES[HIDReportTypes.MOUSE] + 1) + self._pd_report[0] = HIDReportTypes.MOUSE self._pd_pending = False def __repr__(self): @@ -230,8 +235,10 @@ def remove_pd(self): def move_axis(self, axis): delta = clamp(axis.delta, -127, 127) axis.delta -= delta - self._pd_report[axis.code + 2] = 0xFF & delta - self._pd_pending = True + # the built in mouse doesn't have a Pan (P) axis. only add Pan axis to report if using a custom Pointer Device Report with at least 5 bytes + if len(self._pd_report) > axis.code + 2: + self._pd_report[axis.code + 2] = 0xFF & delta + self._pd_pending = True def clear_axis(self): for idx in range(2, len(self._pd_report)): @@ -260,6 +267,7 @@ def __init__(self, **kwargs): for device in usb_hid.devices: us = device.usage up = device.usage_page + print('init device:', up, us) if up == HIDUsagePage.CONSUMER and us == HIDUsage.CONSUMER: self.devices[HIDReportTypes.CONSUMER] = device @@ -267,6 +275,10 @@ def __init__(self, **kwargs): self.devices[HIDReportTypes.KEYBOARD] = device elif up == HIDUsagePage.MOUSE and us == HIDUsage.MOUSE: self.devices[HIDReportTypes.MOUSE] = device + elif up == HIDUsagePage.POINTER and us == HIDUsage.POINTER: + self.devices[HIDReportTypes.POINTER] = device + self._pd_report = bytearray(HID_REPORT_SIZES[HIDReportTypes.POINTER] + 1) + self._pd_report[0] = HIDReportTypes.POINTER elif up == HIDUsagePage.SYSCONTROL and us == HIDUsage.SYSCONTROL: self.devices[HIDReportTypes.SYSCONTROL] = device @@ -275,13 +287,13 @@ def __init__(self, **kwargs): def hid_send(self, evt): if not supervisor.runtime.usb_connected: return - # int, can be looked up in HIDReportTypes reporting_device_const = evt[0] - - return self.devices[reporting_device_const].send_report( - evt[1 : HID_REPORT_SIZES[reporting_device_const] + 1] - ) + report = evt[1 : HID_REPORT_SIZES[reporting_device_const] + 1] + debug('reporting:') + debug(' device: ', evt[0]) + debug(' report: ', report) + return self.devices[reporting_device_const].send_report(report) class BLEHID(AbstractHID): @@ -333,6 +345,12 @@ def devices(self): result[HIDReportTypes.MOUSE] = device continue + if up == HIDUsagePage.POINTER and us == HIDUsage.POINTER: + self.devices[HIDReportTypes.POINTER] = device + self._pd_report = bytearray(HID_REPORT_SIZES[HIDReportTypes.POINTER] + 1) + self._pd_report[0] = HIDReportTypes.POINTER + continue + if up == HIDUsagePage.SYSCONTROL and us == HIDUsage.SYSCONTROL: result[HIDReportTypes.SYSCONTROL] = device continue diff --git a/kmk/keys.py b/kmk/keys.py index 039d97765..5f66b82e0 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -52,9 +52,10 @@ def move(self, keyboard: Keyboard, delta: int): class AX: - W = Axis(2) - X = Axis(0) - Y = Axis(1) + W = Axis(2) # wheel + P = Axis(3) # pan + X = Axis(0) # move x + Y = Axis(1) # move y def maybe_make_key( From 09dca35a1eda3263ffde6d6cdae44abeac768dcf Mon Sep 17 00:00:00 2001 From: Peter Whitesell Date: Tue, 16 May 2023 19:01:11 -0500 Subject: [PATCH 2/4] add Pointer custom device to boot.py, to support horizontal scrolling --- boot.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/boot.py b/boot.py index e69de29bb..97ffbe37f 100644 --- a/boot.py +++ b/boot.py @@ -0,0 +1,61 @@ +# boot.py +import usb_hid + +POINTER_REPORT_DESCRIPTOR = bytes(( + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x01, # Usage (Pointer) + 0xA1, 0x01, # Collection (Application) + 0x09, 0x01, # Usage (Pointer) + 0xA1, 0x00, # Collection (Physical) + 0x85, 0x05, # 10, 11 Report ID (5) + 0x05, 0x09, # Usage Page (Button) + 0x19, 0x01, # Usage Minimum (0x01) + 0x29, 0x05, # Usage Maximum (0x05) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x95, 0x05, # Report Count (5) + 0x75, 0x01, # Report Size (1) + 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, # Report Count (1) + 0x75, 0x03, # Report Size (3) + 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, # Usage (X) + 0x09, 0x31, # Usage (Y) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x02, # Report Count (2) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x38, # Usage (Wheel) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x01, # Report Count (1) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x0C, # Usage Page (Consumer Page) + 0x0A, 0x38, 0x02, # Usage (Pan) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x01, # Report Count (1) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + 0xC0, # End Collection +)) + +pointer = usb_hid.Device( + report_descriptor=POINTER_REPORT_DESCRIPTOR, + usage_page=0x01, # Generic Desktop Control + usage=0x01, # Pointer + report_ids=(5,), # Descriptor uses report ID 2 + in_report_lengths=(5,), # This pointer sends 5 bytes in its report + out_report_lengths=(0,), # It does not receive any reports +) + +usb_hid.enable( + (usb_hid.Device.KEYBOARD, + usb_hid.Device.MOUSE, + usb_hid.Device.CONSUMER_CONTROL, + pointer) +) From 02fdfd92b78c1bae0d08fc9461b24caec90ce325 Mon Sep 17 00:00:00 2001 From: Peter Whitesell Date: Tue, 16 May 2023 19:02:15 -0500 Subject: [PATCH 3/4] add pmw3360 trackball module (thanks @bullwinkle3000 and @kbjunky!) --- kmk/modules/pmw3360.py | 267 ++ kmk/modules/pmw3360_firmware.py | 4096 +++++++++++++++++++++++++++++++ 2 files changed, 4363 insertions(+) create mode 100644 kmk/modules/pmw3360.py create mode 100644 kmk/modules/pmw3360_firmware.py diff --git a/kmk/modules/pmw3360.py b/kmk/modules/pmw3360.py new file mode 100644 index 000000000..117b51abc --- /dev/null +++ b/kmk/modules/pmw3360.py @@ -0,0 +1,267 @@ +import busio +import digitalio +import microcontroller +from kmk.hid import HID_REPORT_SIZES, HIDReportTypes +from kmk.utils import Debug + +import time +import math + +from kmk.modules import Module +from kmk.modules.pmw3360_firmware import firmware +from kmk.keys import KC, AX + +from kmk.utils import Debug +debug = Debug(__name__) + +class REG: + Product_ID = 0x0 + Revision_ID = 0x1 + Motion = 0x02 + Delta_X_L = 0x03 + Delta_X_H = 0x04 + Delta_Y_L = 0x05 + Delta_Y_H = 0x06 + Config1 = 0x0F + Config2 = 0x10 + Angle_Tune = 0x11 + SROM_Enable = 0x13 + Observation = 0x24 + SROM_ID = 0x2A + Power_Up_Reset = 0x3A + Motion_Burst = 0x50 + SROM_Load_Burst = 0x62 + Lift_Config = 0x63 + +class PMW3360(Module): + tsww = tswr = 180 + baud = 2000000 + cpol = 1 + cpha = 1 + DIR_WRITE = 0x80 + DIR_READ = 0x7F + + def __init__(self, cs, sclk, miso, mosi, invert_x=False, invert_y=False, flip_xy=False, lift_config=0x04, on_move=lambda keyboard: None): + self.cs = digitalio.DigitalInOut(cs) + self.cs.direction = digitalio.Direction.OUTPUT + self.spi = busio.SPI(clock=sclk, MOSI=mosi, MISO=miso) + self.invert_x = invert_x + self.invert_y = invert_y + self.flip_xy = flip_xy + self.v_scroll_enabled = False + self.h_scroll_enabled = False + self.volume_control = False + self.v_scroll_ctr = 0 + self.h_scroll_ctr = 0 + self.scroll_res = 10 + self.on_move = on_move + self.lift_config = lift_config + debug(f'lift_config: {lift_config}') + + def start_v_scroll(self, enabled=True): + self.v_scroll_enabled = enabled + + def start_h_scroll(self, enabled=True): + self.h_scroll_enabled = enabled + + def set_scroll(self, enabled=True): + self.v_scroll_enabled = enabled + self.h_scroll_enabled = enabled + + def start_volume_control(self, enabled=True): + self.volume_control = enabled + + def pmw3360_start(self): + self.cs.value = False + + def pmw3360_stop(self): + self.cs.value = True + + def pmw3360_write(self, reg, data): + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.pmw3360_start() + self.spi.write(bytes([reg | self.DIR_WRITE, data])) + # microcontroller.delay_us(35) + except Exception as e: print(e) + finally: + self.spi.unlock() + self.pmw3360_stop() + microcontroller.delay_us(self.tswr) + + def pmw3360_read(self, reg): + result = bytearray(1) + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.pmw3360_start() + self.spi.write(bytes([reg & self.DIR_READ])) + microcontroller.delay_us(160) + self.spi.readinto(result) + microcontroller.delay_us(1) + finally: + self.spi.unlock() + self.pmw3360_stop() + microcontroller.delay_us(19) + return result[0] + + def pwm3360_upload_srom(self): + debug("Uploading pmw3360 FW") + self.pmw3360_write(REG.Config2, 0x0) + self.pmw3360_write(REG.SROM_Enable, 0x1D) + time.sleep(0.01) + self.pmw3360_write(REG.SROM_Enable, 0x18) + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.pmw3360_start() + self.spi.write(bytes([REG.SROM_Load_Burst | self.DIR_WRITE])) + microcontroller.delay_us(15) + for b in firmware: + self.spi.write(bytes([b])) + microcontroller.delay_us(15) + except Error: + debug("Received error on firmware write") + finally: + debug("Firmware done") + microcontroller.delay_us(200) + self.spi.unlock() + self.pmw3360_stop() + + self.pmw3360_read(REG.SROM_ID) + self.pmw3360_write(REG.Config2, 0) # set to wired mouse mode + microcontroller.delay_us(1) + + def delta_to_int(self, high, low): + comp = (high << 8) | low + if comp & 0x8000: + return (-1) * (0xFFFF + 1 - comp) + return comp + + def pmw3360_read_motion(self): + result = bytearray(12) + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.pmw3360_start() + self.spi.write(bytes([REG.Motion_Burst & self.DIR_READ])) + microcontroller.delay_us(35) + self.spi.readinto(result) + finally: + self.spi.unlock() + self.pmw3360_stop() + microcontroller.delay_us(20) + return result + + def during_bootup(self, keyboard): + debug("firmware during_bootup() called") + debug("Debugging not enabled") + self.pmw3360_start() + microcontroller.delay_us(40) + self.pmw3360_stop() + microcontroller.delay_us(40) + self.pmw3360_write(REG.Power_Up_Reset, 0x5A) + time.sleep(0.1) + self.pmw3360_read(REG.Motion) + self.pmw3360_read(REG.Delta_X_L) + self.pmw3360_read(REG.Delta_X_H) + self.pmw3360_read(REG.Delta_Y_L) + self.pmw3360_read(REG.Delta_Y_H) + self.pwm3360_upload_srom() + time.sleep(0.1) + self.pmw3360_write(REG.Config1, 0x06) # set x/y resolution to 700 cpi + # self.pmw3360_write(REG.Config2, 0) # set to wired mouse mode + self.pmw3360_write(REG.Angle_Tune, -25) # set to wired mouse mode + self.pmw3360_write(REG.Lift_Config, self.lift_config) # set to wired mouse mode + if keyboard.debug_enabled: + debug('PMW3360 Product ID ', hex(self.pmw3360_read(REG.Product_ID))) + debug('PMW3360 Revision ID ', hex(self.pmw3360_read(REG.Revision_ID))) + if self.pmw3360_read(REG.Observation) & 0x40: + debug('PMW3360: Sensor is running SROM') + debug('PMW3360: SROM ID: ', hex(self.pmw3360_read(REG.SROM_ID))) + else: + debug('PMW3360: Sensor is not running SROM!') + debug("Finished with firmware download") + + def before_matrix_scan(self, keyboard): + return + + def after_matrix_scan(self, keyboard): + return + + def before_hid_send(self, keyboard): + return + + def after_hid_send(self, keyboard): + motion = self.pmw3360_read_motion() + if motion[0] & 0x80: + if motion[0] & 0x07: + debug("Motion weirdness") + self.pmw3360_write(REG.Motion_Burst, 0) + return + if self.flip_xy: + delta_x = self.delta_to_int(motion[5], motion[4]) + delta_y = self.delta_to_int(motion[3], motion[2]) + else: + delta_x = self.delta_to_int(motion[3], motion[2]) + delta_y = self.delta_to_int(motion[5], motion[4]) + if self.invert_x: + delta_x *= -1 + if self.invert_y: + delta_y *= -1 + if delta_x == 0 and delta_y == 0: + return + # print('Delta: ', delta_x, ' ', delta_y) + if self.v_scroll_enabled: + # vertical scroll + self.v_scroll_ctr += delta_y + if self.v_scroll_ctr >= self.scroll_res: + AX.W.move(keyboard, -1) + self.v_scroll_ctr = 0 + if self.v_scroll_ctr <= -self.scroll_res: + AX.W.move(keyboard, 1) + self.v_scroll_ctr = 0 + if self.h_scroll_enabled: + # horizontal scroll + self.h_scroll_ctr += delta_x + if self.h_scroll_ctr >= self.scroll_res: + AX.P.move(keyboard, 1) + self.h_scroll_ctr = 0 + if self.h_scroll_ctr <= -self.scroll_res: + AX.P.move(keyboard, -1) + self.h_scroll_ctr = 0 + elif self.volume_control: + self.v_scroll_ctr += 1 + if self.v_scroll_ctr >= self.scroll_res: + if delta_y > 0: + keyboard.tap_key(KC.VOLD) + if delta_y < 0: + keyboard.tap_key(KC.VOLU) + self.v_scroll_ctr = 0 + else: + if delta_x: + AX.X.move(keyboard, self._scale_mouse_move(delta_x)) + if delta_y: + AX.Y.move(keyboard, self._scale_mouse_move(delta_y)) + if self.on_move is not None: + self.on_move(keyboard) + + def on_powersave_enable(self, keyboard): + return + + def on_powersave_disable(self, keyboard): + return + def _scale_mouse_move(self, val): + return val + sign = math.copysign(1, val) + sqrd = abs(val**1.5) + scaled = sqrd // 4 + ensured = max(scaled, 1) + signed = math.copysign(ensured, sign) + typed = int(signed) + return typed diff --git a/kmk/modules/pmw3360_firmware.py b/kmk/modules/pmw3360_firmware.py new file mode 100644 index 000000000..b96caa057 --- /dev/null +++ b/kmk/modules/pmw3360_firmware.py @@ -0,0 +1,4096 @@ +firmware = ( + b'\x01' + b'\x04' + b'\x8e' + b'\x96' + b'\x6e' + b'\x77' + b'\x3e' + b'\xfe' + b'\x7e' + b'\x5f' + b'\x1d' + b'\xb8' + b'\xf2' + b'\x66' + b'\x4e' + b'\xff' + b'\x5d' + b'\x19' + b'\xb0' + b'\xc2' + b'\x04' + b'\x69' + b'\x54' + b'\x2a' + b'\xd6' + b'\x2e' + b'\xbf' + b'\xdd' + b'\x19' + b'\xb0' + b'\xc3' + b'\xe5' + b'\x29' + b'\xb1' + b'\xe0' + b'\x23' + b'\xa5' + b'\xa9' + b'\xb1' + b'\xc1' + b'\x00' + b'\x82' + b'\x67' + b'\x4c' + b'\x1a' + b'\x97' + b'\x8d' + b'\x79' + b'\x51' + b'\x20' + b'\xc7' + b'\x06' + b'\x8e' + b'\x7c' + b'\x7c' + b'\x7a' + b'\x76' + b'\x4f' + b'\xfd' + b'\x59' + b'\x30' + b'\xe2' + b'\x46' + b'\x0e' + b'\x9e' + b'\xbe' + b'\xdf' + b'\x1d' + b'\x99' + b'\x91' + b'\xa0' + b'\xa5' + b'\xa1' + b'\xa9' + b'\xd0' + b'\x22' + b'\xc6' + b'\xef' + b'\x5c' + b'\x1b' + b'\x95' + b'\x89' + b'\x90' + b'\xa2' + b'\xa7' + b'\xcc' + b'\xfb' + b'\x55' + b'\x28' + b'\xb3' + b'\xe4' + b'\x4a' + b'\xf7' + b'\x6c' + b'\x3b' + b'\xf4' + b'\x6a' + b'\x56' + b'\x2e' + b'\xde' + b'\x1f' + b'\x9d' + b'\xb8' + b'\xd3' + b'\x05' + b'\x88' + b'\x92' + b'\xa6' + b'\xce' + b'\x1e' + b'\xbe' + b'\xdf' + b'\x1d' + b'\x99' + b'\xb0' + b'\xe2' + b'\x46' + b'\xef' + b'\x5c' + b'\x07' + b'\x11' + b'\x5d' + b'\x98' + b'\x0b' + b'\x9d' + b'\x94' + b'\x97' + b'\xee' + b'\x4e' + b'\x45' + b'\x33' + b'\x6b' + b'\x44' + b'\xc7' + b'\x29' + b'\x56' + b'\x27' + b'\x30' + b'\xc6' + b'\xa7' + b'\xd5' + b'\xf2' + b'\x56' + b'\xdf' + b'\xb4' + b'\x38' + b'\x62' + b'\xcb' + b'\xa0' + b'\xb6' + b'\xe3' + b'\x0f' + b'\x84' + b'\x06' + b'\x24' + b'\x05' + b'\x65' + b'\x6f' + b'\x76' + b'\x89' + b'\xb5' + b'\x77' + b'\x41' + b'\x27' + b'\x82' + b'\x66' + b'\x65' + b'\x82' + b'\xcc' + b'\xd5' + b'\xe6' + b'\x20' + b'\xd5' + b'\x27' + b'\x17' + b'\xc5' + b'\xf8' + b'\x03' + b'\x23' + b'\x7c' + b'\x5f' + b'\x64' + b'\xa5' + b'\x1d' + b'\xc1' + b'\xd6' + b'\x36' + b'\xcb' + b'\x4c' + b'\xd4' + b'\xdb' + b'\x66' + b'\xd7' + b'\x8b' + b'\xb1' + b'\x99' + b'\x7e' + b'\x6f' + b'\x4c' + b'\x36' + b'\x40' + b'\x06' + b'\xd6' + b'\xeb' + b'\xd7' + b'\xa2' + b'\xe4' + b'\xf4' + b'\x95' + b'\x51' + b'\x5a' + b'\x54' + b'\x96' + b'\xd5' + b'\x53' + b'\x44' + b'\xd7' + b'\x8c' + b'\xe0' + b'\xb9' + b'\x40' + b'\x68' + b'\xd2' + b'\x18' + b'\xe9' + b'\xdd' + b'\x9a' + b'\x23' + b'\x92' + b'\x48' + b'\xee' + b'\x7f' + b'\x43' + b'\xaf' + b'\xea' + b'\x77' + b'\x38' + b'\x84' + b'\x8c' + b'\x0a' + b'\x72' + b'\xaf' + b'\x69' + b'\xf8' + b'\xdd' + b'\xf1' + b'\x24' + b'\x83' + b'\xa3' + b'\xf8' + b'\x4a' + b'\xbf' + b'\xf5' + b'\x94' + b'\x13' + b'\xdb' + b'\xbb' + b'\xd8' + b'\xb4' + b'\xb3' + b'\xa0' + b'\xfb' + b'\x45' + b'\x50' + b'\x60' + b'\x30' + b'\x59' + b'\x12' + b'\x31' + b'\x71' + b'\xa2' + b'\xd3' + b'\x13' + b'\xe7' + b'\xfa' + b'\xe7' + b'\xce' + b'\x0f' + b'\x63' + b'\x15' + b'\x0b' + b'\x6b' + b'\x94' + b'\xbb' + b'\x37' + b'\x83' + b'\x26' + b'\x05' + b'\x9d' + b'\xfb' + b'\x46' + b'\x92' + b'\xfc' + b'\x0a' + b'\x15' + b'\xd1' + b'\x0d' + b'\x73' + b'\x92' + b'\xd6' + b'\x8c' + b'\x1b' + b'\x8c' + b'\xb8' + b'\x55' + b'\x8a' + b'\xce' + b'\xbd' + b'\xfe' + b'\x8e' + b'\xfc' + b'\xed' + b'\x09' + b'\x12' + b'\x83' + b'\x91' + b'\x82' + b'\x51' + b'\x31' + b'\x23' + b'\xfb' + b'\xb4' + b'\x0c' + b'\x76' + b'\xad' + b'\x7c' + b'\xd9' + b'\xb4' + b'\x4b' + b'\xb2' + b'\x67' + b'\x14' + b'\x09' + b'\x9c' + b'\x7f' + b'\x0c' + b'\x18' + b'\xba' + b'\x3b' + b'\xd6' + b'\x8e' + b'\x14' + b'\x2a' + b'\xe4' + b'\x1b' + b'\x52' + b'\x9f' + b'\x2b' + b'\x7d' + b'\xe1' + b'\xfb' + b'\x6a' + b'\x33' + b'\x02' + b'\xfa' + b'\xac' + b'\x5a' + b'\xf2' + b'\x3e' + b'\x88' + b'\x7e' + b'\xae' + b'\xd1' + b'\xf3' + b'\x78' + b'\xe8' + b'\x05' + b'\xd1' + b'\xe3' + b'\xdc' + b'\x21' + b'\xf6' + b'\xe1' + b'\x9a' + b'\xbd' + b'\x17' + b'\x0e' + b'\xd9' + b'\x46' + b'\x9b' + b'\x88' + b'\x03' + b'\xea' + b'\xf6' + b'\x66' + b'\xbe' + b'\x0e' + b'\x1b' + b'\x50' + b'\x49' + b'\x96' + b'\x40' + b'\x97' + b'\xf1' + b'\xf1' + b'\xe4' + b'\x80' + b'\xa6' + b'\x6e' + b'\xe8' + b'\x77' + b'\x34' + b'\xbf' + b'\x29' + b'\x40' + b'\x44' + b'\xc2' + b'\xff' + b'\x4e' + b'\x98' + b'\xd3' + b'\x9c' + b'\xa3' + b'\x32' + b'\x2b' + b'\x76' + b'\x51' + b'\x04' + b'\x09' + b'\xe7' + b'\xa9' + b'\xd1' + b'\xa6' + b'\x32' + b'\xb1' + b'\x23' + b'\x53' + b'\xe2' + b'\x47' + b'\xab' + b'\xd6' + b'\xf5' + b'\x69' + b'\x5c' + b'\x3e' + b'\x5f' + b'\xfa' + b'\xae' + b'\x45' + b'\x20' + b'\xe5' + b'\xd2' + b'\x44' + b'\xff' + b'\x39' + b'\x32' + b'\x6d' + b'\xfd' + b'\x27' + b'\x57' + b'\x5c' + b'\xfd' + b'\xf0' + b'\xde' + b'\xc1' + b'\xb5' + b'\x99' + b'\xe5' + b'\xf5' + b'\x1c' + b'\x77' + b'\x01' + b'\x75' + b'\xc5' + b'\x6d' + b'\x58' + b'\x92' + b'\xf2' + b'\xb2' + b'\x47' + b'\x00' + b'\x01' + b'\x26' + b'\x96' + b'\x7a' + b'\x30' + b'\xff' + b'\xb7' + b'\xf0' + b'\xef' + b'\x77' + b'\xc1' + b'\x8a' + b'\x5d' + b'\xdc' + b'\xc0' + b'\xd1' + b'\x29' + b'\x30' + b'\x1e' + b'\x77' + b'\x38' + b'\x7a' + b'\x94' + b'\xf1' + b'\xb8' + b'\x7a' + b'\x7e' + b'\xef' + b'\xa4' + b'\xd1' + b'\xac' + b'\x31' + b'\x4a' + b'\xf2' + b'\x5d' + b'\x64' + b'\x3d' + b'\xb2' + b'\xe2' + b'\xf0' + b'\x08' + b'\x99' + b'\xfc' + b'\x70' + b'\xee' + b'\x24' + b'\xa7' + b'\x7e' + b'\xee' + b'\x1e' + b'\x20' + b'\x69' + b'\x7d' + b'\x44' + b'\xbf' + b'\x87' + b'\x42' + b'\xdf' + b'\x88' + b'\x3b' + b'\x0c' + b'\xda' + b'\x42' + b'\xc9' + b'\x04' + b'\xf9' + b'\x45' + b'\x50' + b'\xfc' + b'\x83' + b'\x8f' + b'\x11' + b'\x6a' + b'\x72' + b'\xbc' + b'\x99' + b'\x95' + b'\xf0' + b'\xac' + b'\x3d' + b'\xa7' + b'\x3b' + b'\xcd' + b'\x1c' + b'\xe2' + b'\x88' + b'\x79' + b'\x37' + b'\x11' + b'\x5f' + b'\x39' + b'\x89' + b'\x95' + b'\x0a' + b'\x16' + b'\x84' + b'\x7a' + b'\xf6' + b'\x8a' + b'\xa4' + b'\x28' + b'\xe4' + b'\xed' + b'\x83' + b'\x80' + b'\x3b' + b'\xb1' + b'\x23' + b'\xa5' + b'\x03' + b'\x10' + b'\xf4' + b'\x66' + b'\xea' + b'\xbb' + b'\x0c' + b'\x0f' + b'\xc5' + b'\xec' + b'\x6c' + b'\x69' + b'\xc5' + b'\xd3' + b'\x24' + b'\xab' + b'\xd4' + b'\x2a' + b'\xb7' + b'\x99' + b'\x88' + b'\x76' + b'\x08' + b'\xa0' + b'\xa8' + b'\x95' + b'\x7c' + b'\xd8' + b'\x38' + b'\x6d' + b'\xcd' + b'\x59' + b'\x02' + b'\x51' + b'\x4b' + b'\xf1' + b'\xb5' + b'\x2b' + b'\x50' + b'\xe3' + b'\xb6' + b'\xbd' + b'\xd0' + b'\x72' + b'\xcf' + b'\x9e' + b'\xfd' + b'\x6e' + b'\xbb' + b'\x44' + b'\xc8' + b'\x24' + b'\x8a' + b'\x77' + b'\x18' + b'\x8a' + b'\x13' + b'\x06' + b'\xef' + b'\x97' + b'\x7d' + b'\xfa' + b'\x81' + b'\xf0' + b'\x31' + b'\xe6' + b'\xfa' + b'\x77' + b'\xed' + b'\x31' + b'\x06' + b'\x31' + b'\x5b' + b'\x54' + b'\x8a' + b'\x9f' + b'\x30' + b'\x68' + b'\xdb' + b'\xe2' + b'\x40' + b'\xf8' + b'\x4e' + b'\x73' + b'\xfa' + b'\xab' + b'\x74' + b'\x8b' + b'\x10' + b'\x58' + b'\x13' + b'\xdc' + b'\xd2' + b'\xe6' + b'\x78' + b'\xd1' + b'\x32' + b'\x2e' + b'\x8a' + b'\x9f' + b'\x2c' + b'\x58' + b'\x06' + b'\x48' + b'\x27' + b'\xc5' + b'\xa9' + b'\x5e' + b'\x81' + b'\x47' + b'\x89' + b'\x46' + b'\x21' + b'\x91' + b'\x03' + b'\x70' + b'\xa4' + b'\x3e' + b'\x88' + b'\x9c' + b'\xda' + b'\x33' + b'\x0a' + b'\xce' + b'\xbc' + b'\x8b' + b'\x8e' + b'\xcf' + b'\x9f' + b'\xd3' + b'\x71' + b'\x80' + b'\x43' + b'\xcf' + b'\x6b' + b'\xa9' + b'\x51' + b'\x83' + b'\x76' + b'\x30' + b'\x82' + b'\xc5' + b'\x6a' + b'\x85' + b'\x39' + b'\x11' + b'\x50' + b'\x1a' + b'\x82' + b'\xdc' + b'\x1e' + b'\x1c' + b'\xd5' + b'\x7d' + b'\xa9' + b'\x71' + b'\x99' + b'\x33' + b'\x47' + b'\x19' + b'\x97' + b'\xb3' + b'\x5a' + b'\xb1' + b'\xdf' + b'\xed' + b'\xa4' + b'\xf2' + b'\xe6' + b'\x26' + b'\x84' + b'\xa2' + b'\x28' + b'\x9a' + b'\x9e' + b'\xdf' + b'\xa6' + b'\x6a' + b'\xf4' + b'\xd6' + b'\xfc' + b'\x2e' + b'\x5b' + b'\x9d' + b'\x1a' + b'\x2a' + b'\x27' + b'\x68' + b'\xfb' + b'\xc1' + b'\x83' + b'\x21' + b'\x4b' + b'\x90' + b'\xe0' + b'\x36' + b'\xdd' + b'\x5b' + b'\x31' + b'\x42' + b'\x55' + b'\xa0' + b'\x13' + b'\xf7' + b'\xd0' + b'\x89' + b'\x53' + b'\x71' + b'\x99' + b'\x57' + b'\x09' + b'\x29' + b'\xc5' + b'\xf3' + b'\x21' + b'\xf8' + b'\x37' + b'\x2f' + b'\x40' + b'\xf3' + b'\xd4' + b'\xaf' + b'\x16' + b'\x08' + b'\x36' + b'\x02' + b'\xfc' + b'\x77' + b'\xc5' + b'\x8b' + b'\x04' + b'\x90' + b'\x56' + b'\xb9' + b'\xc9' + b'\x67' + b'\x9a' + b'\x99' + b'\xe8' + b'\x00' + b'\xd3' + b'\x86' + b'\xff' + b'\x97' + b'\x2d' + b'\x08' + b'\xe9' + b'\xb7' + b'\xb3' + b'\x91' + b'\xbc' + b'\xdf' + b'\x45' + b'\xc6' + b'\xed' + b'\x0f' + b'\x8c' + b'\x4c' + b'\x1e' + b'\xe6' + b'\x5b' + b'\x6e' + b'\x38' + b'\x30' + b'\xe4' + b'\xaa' + b'\xe3' + b'\x95' + b'\xde' + b'\xb9' + b'\xe4' + b'\x9a' + b'\xf5' + b'\xb2' + b'\x55' + b'\x9a' + b'\x87' + b'\x9b' + b'\xf6' + b'\x6a' + b'\xb2' + b'\xf2' + b'\x77' + b'\x9a' + b'\x31' + b'\xf4' + b'\x7a' + b'\x31' + b'\xd1' + b'\x1d' + b'\x04' + b'\xc0' + b'\x7c' + b'\x32' + b'\xa2' + b'\x9e' + b'\x9a' + b'\xf5' + b'\x62' + b'\xf8' + b'\x27' + b'\x8d' + b'\xbf' + b'\x51' + b'\xff' + b'\xd3' + b'\xdf' + b'\x64' + b'\x37' + b'\x3f' + b'\x2a' + b'\x6f' + b'\x76' + b'\x3a' + b'\x7d' + b'\x77' + b'\x06' + b'\x9e' + b'\x77' + b'\x7f' + b'\x5e' + b'\xeb' + b'\x32' + b'\x51' + b'\xf9' + b'\x16' + b'\x66' + b'\x9a' + b'\x09' + b'\xf3' + b'\xb0' + b'\x08' + b'\xa4' + b'\x70' + b'\x96' + b'\x46' + b'\x30' + b'\xff' + b'\xda' + b'\x4f' + b'\xe9' + b'\x1b' + b'\xed' + b'\x8d' + b'\xf8' + b'\x74' + b'\x1f' + b'\x31' + b'\x92' + b'\xb3' + b'\x73' + b'\x17' + b'\x36' + b'\xdb' + b'\x91' + b'\x30' + b'\xd6' + b'\x88' + b'\x55' + b'\x6b' + b'\x34' + b'\x77' + b'\x87' + b'\x7a' + b'\xe7' + b'\xee' + b'\x06' + b'\xc6' + b'\x1c' + b'\x8c' + b'\x19' + b'\x0c' + b'\x48' + b'\x46' + b'\x23' + b'\x5e' + b'\x9c' + b'\x07' + b'\x5c' + b'\xbf' + b'\xb4' + b'\x7e' + b'\xd6' + b'\x4f' + b'\x74' + b'\x9c' + b'\xe2' + b'\xc5' + b'\x50' + b'\x8b' + b'\xc5' + b'\x8b' + b'\x15' + b'\x90' + b'\x60' + b'\x62' + b'\x57' + b'\x29' + b'\xd0' + b'\x13' + b'\x43' + b'\xa1' + b'\x80' + b'\x88' + b'\x91' + b'\x00' + b'\x44' + b'\xc7' + b'\x4d' + b'\x19' + b'\x86' + b'\xcc' + b'\x2f' + b'\x2a' + b'\x75' + b'\x5a' + b'\xfc' + b'\xeb' + b'\x97' + b'\x2a' + b'\x70' + b'\xe3' + b'\x78' + b'\xd8' + b'\x91' + b'\xb0' + b'\x4f' + b'\x99' + b'\x07' + b'\xa3' + b'\x95' + b'\xea' + b'\x24' + b'\x21' + b'\xd5' + b'\xde' + b'\x51' + b'\x20' + b'\x93' + b'\x27' + b'\x0a' + b'\x30' + b'\x73' + b'\xa8' + b'\xff' + b'\x8a' + b'\x97' + b'\xe9' + b'\xa7' + b'\x6a' + b'\x8e' + b'\x0d' + b'\xe8' + b'\xf0' + b'\xdf' + b'\xec' + b'\xea' + b'\xb4' + b'\x6c' + b'\x1d' + b'\x39' + b'\x2a' + b'\x62' + b'\x2d' + b'\x3d' + b'\x5a' + b'\x8b' + b'\x65' + b'\xf8' + b'\x90' + b'\x05' + b'\x2e' + b'\x7e' + b'\x91' + b'\x2c' + b'\x78' + b'\xef' + b'\x8e' + b'\x7a' + b'\xc1' + b'\x2f' + b'\xac' + b'\x78' + b'\xee' + b'\xaf' + b'\x28' + b'\x45' + b'\x06' + b'\x4c' + b'\x26' + b'\xaf' + b'\x3b' + b'\xa2' + b'\xdb' + b'\xa3' + b'\x93' + b'\x06' + b'\xb5' + b'\x3c' + b'\xa5' + b'\xd8' + b'\xee' + b'\x8f' + b'\xaf' + b'\x25' + b'\xcc' + b'\x3f' + b'\x85' + b'\x68' + b'\x48' + b'\xa9' + b'\x62' + b'\xcc' + b'\x97' + b'\x8f' + b'\x7f' + b'\x2a' + b'\xea' + b'\xe0' + b'\x15' + b'\x0a' + b'\xad' + b'\x62' + b'\x07' + b'\xbd' + b'\x45' + b'\xf8' + b'\x41' + b'\xd8' + b'\x36' + b'\xcb' + b'\x4c' + b'\xdb' + b'\x6e' + b'\xe6' + b'\x3a' + b'\xe7' + b'\xda' + b'\x15' + b'\xe9' + b'\x29' + b'\x1e' + b'\x12' + b'\x10' + b'\xa0' + b'\x14' + b'\x2c' + b'\x0e' + b'\x3d' + b'\xf4' + b'\xbf' + b'\x39' + b'\x41' + b'\x92' + b'\x75' + b'\x0b' + b'\x25' + b'\x7b' + b'\xa3' + b'\xce' + b'\x39' + b'\x9c' + b'\x15' + b'\x64' + b'\xc8' + b'\xfa' + b'\x3d' + b'\xef' + b'\x73' + b'\x27' + b'\xfe' + b'\x26' + b'\x2e' + b'\xce' + b'\xda' + b'\x6e' + b'\xfd' + b'\x71' + b'\x8e' + b'\xdd' + b'\xfe' + b'\x76' + b'\xee' + b'\xdc' + b'\x12' + b'\x5c' + b'\x02' + b'\xc5' + b'\x3a' + b'\x4e' + b'\x4e' + b'\x4f' + b'\xbf' + b'\xca' + b'\x40' + b'\x15' + b'\xc7' + b'\x6e' + b'\x8d' + b'\x41' + b'\xf1' + b'\x10' + b'\xe0' + b'\x4f' + b'\x7e' + b'\x97' + b'\x7f' + b'\x1c' + b'\xae' + b'\x47' + b'\x8e' + b'\x6b' + b'\xb1' + b'\x25' + b'\x31' + b'\xb0' + b'\x73' + b'\xc7' + b'\x1b' + b'\x97' + b'\x79' + b'\xf9' + b'\x80' + b'\xd3' + b'\x66' + b'\x22' + b'\x30' + b'\x07' + b'\x74' + b'\x1e' + b'\xe4' + b'\xd0' + b'\x80' + b'\x21' + b'\xd6' + b'\xee' + b'\x6b' + b'\x6c' + b'\x4f' + b'\xbf' + b'\xf5' + b'\xb7' + b'\xd9' + b'\x09' + b'\x87' + b'\x2f' + b'\xa9' + b'\x14' + b'\xbe' + b'\x27' + b'\xd9' + b'\x72' + b'\x50' + b'\x01' + b'\xd4' + b'\x13' + b'\x73' + b'\xa6' + b'\xa7' + b'\x51' + b'\x02' + b'\x75' + b'\x25' + b'\xe1' + b'\xb3' + b'\x45' + b'\x34' + b'\x7d' + b'\xa8' + b'\x8e' + b'\xeb' + b'\xf3' + b'\x16' + b'\x49' + b'\xcb' + b'\x4f' + b'\x8c' + b'\xa1' + b'\xb9' + b'\x36' + b'\x85' + b'\x39' + b'\x75' + b'\x5d' + b'\x08' + b'\x00' + b'\xae' + b'\xeb' + b'\xf6' + b'\xea' + b'\xd7' + b'\x13' + b'\x3a' + b'\x21' + b'\x5a' + b'\x5f' + b'\x30' + b'\x84' + b'\x52' + b'\x26' + b'\x95' + b'\xc9' + b'\x14' + b'\xf2' + b'\x57' + b'\x55' + b'\x6b' + b'\xb1' + b'\x10' + b'\xc2' + b'\xe1' + b'\xbd' + b'\x3b' + b'\x51' + b'\xc0' + b'\xb7' + b'\x55' + b'\x4c' + b'\x71' + b'\x12' + b'\x26' + b'\xc7' + b'\x0d' + b'\xf9' + b'\x51' + b'\xa4' + b'\x38' + b'\x02' + b'\x05' + b'\x7f' + b'\xb8' + b'\xf1' + b'\x72' + b'\x4b' + b'\xbf' + b'\x71' + b'\x89' + b'\x14' + b'\xf3' + b'\x77' + b'\x38' + b'\xd9' + b'\x71' + b'\x24' + b'\xf3' + b'\x00' + b'\x11' + b'\xa1' + b'\xd8' + b'\xd4' + b'\x69' + b'\x27' + b'\x08' + b'\x37' + b'\x35' + b'\xc9' + b'\x11' + b'\x9d' + b'\x90' + b'\x1c' + b'\x0e' + b'\xe7' + b'\x1c' + b'\xff' + b'\x2d' + b'\x1e' + b'\xe8' + b'\x92' + b'\xe1' + b'\x18' + b'\x10' + b'\x95' + b'\x7c' + b'\xe0' + b'\x80' + b'\xf4' + b'\x96' + b'\x43' + b'\x21' + b'\xf9' + b'\x75' + b'\x21' + b'\x64' + b'\x38' + b'\xdd' + b'\x9f' + b'\x1e' + b'\x95' + b'\x16' + b'\xda' + b'\x56' + b'\x1d' + b'\x4f' + b'\x9a' + b'\x53' + b'\xb2' + b'\xe2' + b'\xe4' + b'\x18' + b'\xcb' + b'\x6b' + b'\x1a' + b'\x65' + b'\xeb' + b'\x56' + b'\xc6' + b'\x3b' + b'\xe5' + b'\xfe' + b'\xd8' + b'\x26' + b'\x3f' + b'\x3a' + b'\x84' + b'\x59' + b'\x72' + b'\x66' + b'\xa2' + b'\xf3' + b'\x75' + b'\xff' + b'\xfb' + b'\x60' + b'\xb3' + b'\x22' + b'\xad' + b'\x3f' + b'\x2d' + b'\x6b' + b'\xf9' + b'\xeb' + b'\xea' + b'\x05' + b'\x7c' + b'\xd8' + b'\x8f' + b'\x6d' + b'\x2c' + b'\x98' + b'\x9e' + b'\x2b' + b'\x93' + b'\xf1' + b'\x5e' + b'\x46' + b'\xf0' + b'\x87' + b'\x49' + b'\x29' + b'\x73' + b'\x68' + b'\xd7' + b'\x7f' + b'\xf9' + b'\xf0' + b'\xe5' + b'\x7d' + b'\xdb' + b'\x1d' + b'\x75' + b'\x19' + b'\xf3' + b'\xc4' + b'\x58' + b'\x9b' + b'\x17' + b'\x88' + b'\xa8' + b'\x92' + b'\xe0' + b'\xbe' + b'\xbd' + b'\x8b' + b'\x1d' + b'\x8d' + b'\x9f' + b'\x56' + b'\x76' + b'\xad' + b'\xaf' + b'\x29' + b'\xe2' + b'\xd9' + b'\xd5' + b'\x52' + b'\xf6' + b'\xb5' + b'\x56' + b'\x35' + b'\x57' + b'\x3a' + b'\xc8' + b'\xe1' + b'\x56' + b'\x43' + b'\x19' + b'\x94' + b'\xd3' + b'\x04' + b'\x9b' + b'\x6d' + b'\x35' + b'\xd8' + b'\x0b' + b'\x5f' + b'\x4d' + b'\x19' + b'\x8e' + b'\xec' + b'\xfa' + b'\x64' + b'\x91' + b'\x0a' + b'\x72' + b'\x20' + b'\x2b' + b'\xbc' + b'\x1a' + b'\x4a' + b'\xfe' + b'\x8b' + b'\xfd' + b'\xbb' + b'\xed' + b'\x1b' + b'\x23' + b'\xea' + b'\xad' + b'\x72' + b'\x82' + b'\xa1' + b'\x29' + b'\x99' + b'\x71' + b'\xbd' + b'\xf0' + b'\x95' + b'\xc1' + b'\x03' + b'\xdd' + b'\x7b' + b'\xc2' + b'\xb2' + b'\x3c' + b'\x28' + b'\x54' + b'\xd3' + b'\x68' + b'\xa4' + b'\x72' + b'\xc8' + b'\x66' + b'\x96' + b'\xe0' + b'\xd1' + b'\xd8' + b'\x7f' + b'\xf8' + b'\xd1' + b'\x26' + b'\x2b' + b'\xf7' + b'\xad' + b'\xba' + b'\x55' + b'\xca' + b'\x15' + b'\xb9' + b'\x32' + b'\xc3' + b'\xe5' + b'\x88' + b'\x97' + b'\x8e' + b'\x5c' + b'\xfb' + b'\x92' + b'\x25' + b'\x8b' + b'\xbf' + b'\xa2' + b'\x45' + b'\x55' + b'\x7a' + b'\xa7' + b'\x6f' + b'\x8b' + b'\x57' + b'\x5b' + b'\xcf' + b'\x0e' + b'\xcb' + b'\x1d' + b'\xfb' + b'\x20' + b'\x82' + b'\x77' + b'\xa8' + b'\x8c' + b'\xcc' + b'\x16' + b'\xce' + b'\x1d' + b'\xfa' + b'\xde' + b'\xcc' + b'\x0b' + b'\x62' + b'\xfe' + b'\xcc' + b'\xe1' + b'\xb7' + b'\xf0' + b'\xc3' + b'\x81' + b'\x64' + b'\x73' + b'\x40' + b'\xa0' + b'\xc2' + b'\x4d' + b'\x89' + b'\x11' + b'\x75' + b'\x33' + b'\x55' + b'\x33' + b'\x8d' + b'\xe8' + b'\x4a' + b'\xfd' + b'\xea' + b'\x6e' + b'\x30' + b'\x0b' + b'\xd7' + b'\x31' + b'\x2c' + b'\xde' + b'\x47' + b'\xe3' + b'\xbf' + b'\xf8' + b'\x55' + b'\x42' + b'\xe2' + b'\x7f' + b'\x59' + b'\xe5' + b'\x17' + b'\xef' + b'\x99' + b'\x34' + b'\x69' + b'\x91' + b'\xb1' + b'\x23' + b'\x8e' + b'\x20' + b'\x87' + b'\x2d' + b'\xa8' + b'\xfe' + b'\xd5' + b'\x8a' + b'\xf3' + b'\x84' + b'\x3a' + b'\xf0' + b'\x37' + b'\xe4' + b'\x09' + b'\x00' + b'\x54' + b'\xee' + b'\x67' + b'\x49' + b'\x93' + b'\xe4' + b'\x81' + b'\x70' + b'\xe3' + b'\x90' + b'\x4d' + b'\xef' + b'\xfe' + b'\x41' + b'\xb7' + b'\x99' + b'\x7b' + b'\xc1' + b'\x83' + b'\xba' + b'\x62' + b'\x12' + b'\x6f' + b'\x7d' + b'\xde' + b'\x6b' + b'\xaf' + b'\xda' + b'\x16' + b'\xf9' + b'\x55' + b'\x51' + b'\xee' + b'\xa6' + b'\x0c' + b'\x2b' + b'\x02' + b'\xa3' + b'\xfd' + b'\x8d' + b'\xfb' + b'\x30' + b'\x17' + b'\xe4' + b'\x6f' + b'\xdf' + b'\x36' + b'\x71' + b'\xc4' + b'\xca' + b'\x87' + b'\x25' + b'\x48' + b'\xb0' + b'\x47' + b'\xec' + b'\xea' + b'\xb4' + b'\xbf' + b'\xa5' + b'\x4d' + b'\x9b' + b'\x9f' + b'\x02' + b'\x93' + b'\xc4' + b'\xe3' + b'\xe4' + b'\xe8' + b'\x42' + b'\x2d' + b'\x68' + b'\x81' + b'\x15' + b'\x0a' + b'\xeb' + b'\x84' + b'\x5b' + b'\xd6' + b'\xa8' + b'\x74' + b'\xfb' + b'\x7d' + b'\x1d' + b'\xcb' + b'\x2c' + b'\xda' + b'\x46' + b'\x2a' + b'\x76' + b'\x62' + b'\xce' + b'\xbc' + b'\x5c' + b'\x9e' + b'\x8b' + b'\xe7' + b'\xcf' + b'\xbe' + b'\x78' + b'\xf5' + b'\x7c' + b'\xeb' + b'\xb3' + b'\x3a' + b'\x9c' + b'\xaa' + b'\x6f' + b'\xcc' + b'\x72' + b'\xd1' + b'\x59' + b'\xf2' + b'\x11' + b'\x23' + b'\xd6' + b'\x3f' + b'\x48' + b'\xd1' + b'\xb7' + b'\xce' + b'\xb0' + b'\xbf' + b'\xcb' + b'\xea' + b'\x80' + b'\xde' + b'\x57' + b'\xd4' + b'\x5e' + b'\x97' + b'\x2f' + b'\x75' + b'\xd1' + b'\x50' + b'\x8e' + b'\x80' + b'\x2c' + b'\x66' + b'\x79' + b'\xbf' + b'\x72' + b'\x4b' + b'\xbd' + b'\x8a' + b'\x81' + b'\x6c' + b'\xd3' + b'\xe1' + b'\x01' + b'\xdc' + b'\xd2' + b'\x15' + b'\x26' + b'\xc5' + b'\x36' + b'\xda' + b'\x2c' + b'\x1a' + b'\xc0' + b'\x27' + b'\x94' + b'\xed' + b'\xb7' + b'\x9b' + b'\x85' + b'\x0b' + b'\x5e' + b'\x80' + b'\x97' + b'\xc5' + b'\xec' + b'\x4f' + b'\xec' + b'\x88' + b'\x5d' + b'\x50' + b'\x07' + b'\x35' + b'\x47' + b'\xdc' + b'\x0b' + b'\x3b' + b'\x3d' + b'\xdd' + b'\x60' + b'\xaf' + b'\xa8' + b'\x5d' + b'\x81' + b'\x38' + b'\x24' + b'\x25' + b'\x5d' + b'\x5c' + b'\x15' + b'\xd1' + b'\xde' + b'\xb3' + b'\xab' + b'\xec' + b'\x05' + b'\x69' + b'\xef' + b'\x83' + b'\xed' + b'\x57' + b'\x54' + b'\xb8' + b'\x64' + b'\x64' + b'\x11' + b'\x16' + b'\x32' + b'\x69' + b'\xda' + b'\x9f' + b'\x2d' + b'\x7f' + b'\x36' + b'\xbb' + b'\x44' + b'\x5a' + b'\x34' + b'\xe8' + b'\x7f' + b'\xbf' + b'\x03' + b'\xeb' + b'\x00' + b'\x7f' + b'\x59' + b'\x68' + b'\x22' + b'\x79' + b'\xcf' + b'\x73' + b'\x6c' + b'\x2c' + b'\x29' + b'\xa7' + b'\xa1' + b'\x5f' + b'\x38' + b'\xa1' + b'\x1d' + b'\xf0' + b'\x20' + b'\x53' + b'\xe0' + b'\x1a' + b'\x63' + b'\x14' + b'\x58' + b'\x71' + b'\x10' + b'\xaa' + b'\x08' + b'\x0c' + b'\x3e' + b'\x16' + b'\x1a' + b'\x60' + b'\x22' + b'\x82' + b'\x7f' + b'\xba' + b'\xa4' + b'\x43' + b'\xa0' + b'\xd0' + b'\xac' + b'\x1b' + b'\xd5' + b'\x6b' + b'\x64' + b'\xb5' + b'\x14' + b'\x93' + b'\x31' + b'\x9e' + b'\x53' + b'\x50' + b'\xd0' + b'\x57' + b'\x66' + b'\xee' + b'\x5a' + b'\x4f' + b'\xfb' + b'\x03' + b'\x2a' + b'\x69' + b'\x58' + b'\x76' + b'\xf1' + b'\x83' + b'\xf7' + b'\x4e' + b'\xba' + b'\x8c' + b'\x42' + b'\x06' + b'\x60' + b'\x5d' + b'\x6d' + b'\xce' + b'\x60' + b'\x88' + b'\xae' + b'\xa4' + b'\xc3' + b'\xf1' + b'\x03' + b'\xa5' + b'\x4b' + b'\x98' + b'\xa1' + b'\xff' + b'\x67' + b'\xe1' + b'\xac' + b'\xa2' + b'\xb8' + b'\x62' + b'\xd7' + b'\x6f' + b'\xa0' + b'\x31' + b'\xb4' + b'\xd2' + b'\x77' + b'\xaf' + b'\x21' + b'\x10' + b'\x06' + b'\xc6' + b'\x9a' + b'\xff' + b'\x1d' + b'\x09' + b'\x17' + b'\x0e' + b'\x5f' + b'\xf1' + b'\xaa' + b'\x54' + b'\x34' + b'\x4b' + b'\x45' + b'\x8a' + b'\x87' + b'\x63' + b'\xa6' + b'\xdc' + b'\xf9' + b'\x24' + b'\x30' + b'\x67' + b'\xc6' + b'\xb2' + b'\xd6' + b'\x61' + b'\x33' + b'\x69' + b'\xee' + b'\x50' + b'\x61' + b'\x57' + b'\x28' + b'\xe7' + b'\x7e' + b'\xee' + b'\xec' + b'\x3a' + b'\x5a' + b'\x73' + b'\x4e' + b'\xa8' + b'\x8d' + b'\xe4' + b'\x18' + b'\xea' + b'\xec' + b'\x41' + b'\x64' + b'\xc8' + b'\xe2' + b'\xe8' + b'\x66' + b'\xb6' + b'\x2d' + b'\xb6' + b'\xfb' + b'\x6a' + b'\x6c' + b'\x16' + b'\xb3' + b'\xdd' + b'\x46' + b'\x43' + b'\xb9' + b'\x73' + b'\x00' + b'\x6a' + b'\x71' + b'\xed' + b'\x4e' + b'\x9d' + b'\x25' + b'\x1a' + b'\xc3' + b'\x3c' + b'\x4a' + b'\x95' + b'\x15' + b'\x99' + b'\x35' + b'\x81' + b'\x14' + b'\x02' + b'\xd6' + b'\x98' + b'\x9b' + b'\xec' + b'\xd8' + b'\x23' + b'\x3b' + b'\x84' + b'\x29' + b'\xaf' + b'\x0c' + b'\x99' + b'\x83' + b'\xa6' + b'\x9a' + b'\x34' + b'\x4f' + b'\xfa' + b'\xe8' + b'\xd0' + b'\x3c' + b'\x4b' + b'\xd0' + b'\xfb' + b'\xb6' + b'\x68' + b'\xb8' + b'\x9e' + b'\x8f' + b'\xcd' + b'\xf7' + b'\x60' + b'\x2d' + b'\x7a' + b'\x22' + b'\xe5' + b'\x7d' + b'\xab' + b'\x65' + b'\x1b' + b'\x95' + b'\xa7' + b'\xa8' + b'\x7f' + b'\xb6' + b'\x77' + b'\x47' + b'\x7b' + b'\x5f' + b'\x8b' + b'\x12' + b'\x72' + b'\xd0' + b'\xd4' + b'\x91' + b'\xef' + b'\xde' + b'\x19' + b'\x50' + b'\x3c' + b'\xa7' + b'\x8b' + b'\xc4' + b'\xa9' + b'\xb3' + b'\x23' + b'\xcb' + b'\x76' + b'\xe6' + b'\x81' + b'\xf0' + b'\xc1' + b'\x04' + b'\x8f' + b'\xa3' + b'\xb8' + b'\x54' + b'\x5b' + b'\x97' + b'\xac' + b'\x19' + b'\xff' + b'\x3f' + b'\x55' + b'\x27' + b'\x2f' + b'\xe0' + b'\x1d' + b'\x42' + b'\x9b' + b'\x57' + b'\xfc' + b'\x4b' + b'\x4e' + b'\x0f' + b'\xce' + b'\x98' + b'\xa9' + b'\x43' + b'\x57' + b'\x03' + b'\xbd' + b'\xe7' + b'\xc8' + b'\x94' + b'\xdf' + b'\x6e' + b'\x36' + b'\x73' + b'\x32' + b'\xb4' + b'\xef' + b'\x2e' + b'\x85' + b'\x7a' + b'\x6e' + b'\xfc' + b'\x6c' + b'\x18' + b'\x82' + b'\x75' + b'\x35' + b'\x90' + b'\x07' + b'\xf3' + b'\xe4' + b'\x9f' + b'\x3e' + b'\xdc' + b'\x68' + b'\xf3' + b'\xb5' + b'\xf3' + b'\x19' + b'\x80' + b'\x92' + b'\x06' + b'\x99' + b'\xa2' + b'\xe8' + b'\x6f' + b'\xff' + b'\x2e' + b'\x7f' + b'\xae' + b'\x42' + b'\xa4' + b'\x5f' + b'\xfb' + b'\xd4' + b'\x0e' + b'\x81' + b'\x2b' + b'\xc3' + b'\x04' + b'\xff' + b'\x2b' + b'\xb3' + b'\x74' + b'\x4e' + b'\x36' + b'\x5b' + b'\x9c' + b'\x15' + b'\x00' + b'\xc6' + b'\x47' + b'\x2b' + b'\xe8' + b'\x8b' + b'\x3d' + b'\xf1' + b'\x9c' + b'\x03' + b'\x9a' + b'\x58' + b'\x7f' + b'\x9b' + b'\x9c' + b'\xbf' + b'\x85' + b'\x49' + b'\x79' + b'\x35' + b'\x2e' + b'\x56' + b'\x7b' + b'\x41' + b'\x14' + b'\x39' + b'\x47' + b'\x83' + b'\x26' + b'\xaa' + b'\x07' + b'\x89' + b'\x98' + b'\x11' + b'\x1b' + b'\x86' + b'\xe7' + b'\x73' + b'\x7a' + b'\xd8' + b'\x7d' + b'\x78' + b'\x61' + b'\x53' + b'\xe9' + b'\x79' + b'\xf5' + b'\x36' + b'\x8d' + b'\x44' + b'\x92' + b'\x84' + b'\xf9' + b'\x13' + b'\x50' + b'\x58' + b'\x3b' + b'\xa4' + b'\x6a' + b'\x36' + b'\x65' + b'\x49' + b'\x8e' + b'\x3c' + b'\x0e' + b'\xf1' + b'\x6f' + b'\xd2' + b'\x84' + b'\xc4' + b'\x7e' + b'\x8e' + b'\x3f' + b'\x39' + b'\xae' + b'\x7c' + b'\x84' + b'\xf1' + b'\x63' + b'\x37' + b'\x8e' + b'\x3c' + b'\xcc' + b'\x3e' + b'\x44' + b'\x81' + b'\x45' + b'\xf1' + b'\x4b' + b'\xb9' + b'\xed' + b'\x6b' + b'\x36' + b'\x5d' + b'\xbb' + b'\x20' + b'\x60' + b'\x1a' + b'\x0f' + b'\xa3' + b'\xaa' + b'\x55' + b'\x77' + b'\x3a' + b'\xa9' + b'\xae' + b'\x37' + b'\x4d' + b'\xba' + b'\xb8' + b'\x86' + b'\x6b' + b'\xbc' + b'\x08' + b'\x50' + b'\xf6' + b'\xcc' + b'\xa4' + b'\xbd' + b'\x1d' + b'\x40' + b'\x72' + b'\xa5' + b'\x86' + b'\xfa' + b'\xe2' + b'\x10' + b'\xae' + b'\x3d' + b'\x58' + b'\x4b' + b'\x97' + b'\xf3' + b'\x43' + b'\x74' + b'\xa9' + b'\x9e' + b'\xeb' + b'\x21' + b'\xb7' + b'\x01' + b'\xa4' + b'\x86' + b'\x93' + b'\x97' + b'\xee' + b'\x2f' + b'\x4f' + b'\x3b' + b'\x86' + b'\xa1' + b'\x41' + b'\x6f' + b'\x41' + b'\x26' + b'\x90' + b'\x78' + b'\x5c' + b'\x7f' + b'\x30' + b'\x38' + b'\x4b' + b'\x3f' + b'\xaa' + b'\xec' + b'\xed' + b'\x5c' + b'\x6f' + b'\x0e' + b'\xad' + b'\x43' + b'\x87' + b'\xfd' + b'\x93' + b'\x35' + b'\xe6' + b'\x01' + b'\xef' + b'\x41' + b'\x26' + b'\x90' + b'\x99' + b'\x9e' + b'\xfb' + b'\x19' + b'\x5b' + b'\xad' + b'\xd2' + b'\x91' + b'\x8a' + b'\xe0' + b'\x46' + b'\xaf' + b'\x65' + b'\xfa' + b'\x4f' + b'\x84' + b'\xc1' + b'\xa1' + b'\x2d' + b'\xcf' + b'\x45' + b'\x8b' + b'\xd3' + b'\x85' + b'\x50' + b'\x55' + b'\x7c' + b'\xf9' + b'\x67' + b'\x88' + b'\xd4' + b'\x4e' + b'\xe9' + b'\xd7' + b'\x6b' + b'\x61' + b'\x54' + b'\xa1' + b'\xa4' + b'\xa6' + b'\xa2' + b'\xc2' + b'\xbf' + b'\x30' + b'\x9c' + b'\x40' + b'\x9f' + b'\x5f' + b'\xd7' + b'\x69' + b'\x2b' + b'\x24' + b'\x82' + b'\x5e' + b'\xd9' + b'\xd6' + b'\xa7' + b'\x12' + b'\x54' + b'\x1a' + b'\xf7' + b'\x55' + b'\x9f' + b'\x76' + b'\x50' + b'\xa9' + b'\x95' + b'\x84' + b'\xe6' + b'\x6b' + b'\x6d' + b'\xb5' + b'\x96' + b'\x54' + b'\xd6' + b'\xcd' + b'\xb3' + b'\xa1' + b'\x9b' + b'\x46' + b'\xa7' + b'\x94' + b'\x4d' + b'\xc4' + b'\x94' + b'\xb4' + b'\x98' + b'\xe3' + b'\xe1' + b'\xe2' + b'\x34' + b'\xd5' + b'\x33' + b'\x16' + b'\x07' + b'\x54' + b'\xcd' + b'\xb7' + b'\x77' + b'\x53' + b'\xdb' + b'\x4f' + b'\x4d' + b'\x46' + b'\x9d' + b'\xe9' + b'\xd4' + b'\x9c' + b'\x8a' + b'\x36' + b'\xb6' + b'\xb8' + b'\x38' + b'\x26' + b'\x6c' + b'\x0e' + b'\xff' + b'\x9c' + b'\x1b' + b'\x43' + b'\x8b' + b'\x80' + b'\xcc' + b'\xb9' + b'\x3d' + b'\xda' + b'\xc7' + b'\xf1' + b'\x8a' + b'\xf2' + b'\x6d' + b'\xb8' + b'\xd7' + b'\x74' + b'\x2f' + b'\x7e' + b'\x1e' + b'\xb7' + b'\xd3' + b'\x4a' + b'\xb4' + b'\xac' + b'\xfc' + b'\x79' + b'\x48' + b'\x6c' + b'\xbc' + b'\x96' + b'\xb6' + b'\x94' + b'\x46' + b'\x57' + b'\x2d' + b'\xb0' + b'\xa3' + b'\xfc' + b'\x1e' + b'\xb9' + b'\x52' + b'\x60' + b'\x85' + b'\x2d' + b'\x41' + b'\xd0' + b'\x43' + b'\x01' + b'\x1e' + b'\x1c' + b'\xd5' + b'\x7d' + b'\xfc' + b'\xf3' + b'\x96' + b'\x0d' + b'\xc7' + b'\xcb' + b'\x2a' + b'\x29' + b'\x9a' + b'\x93' + b'\xdd' + b'\x88' + b'\x2d' + b'\x37' + b'\x5d' + b'\xaa' + b'\xfb' + b'\x49' + b'\x68' + b'\xa0' + b'\x9c' + b'\x50' + b'\x86' + b'\x7f' + b'\x68' + b'\x56' + b'\x57' + b'\xf9' + b'\x79' + b'\x18' + b'\x39' + b'\xd4' + b'\xe0' + b'\x01' + b'\x84' + b'\x33' + b'\x61' + b'\xca' + b'\xa5' + b'\xd2' + b'\xd6' + b'\xe4' + b'\xc9' + b'\x8a' + b'\x4a' + b'\x23' + b'\x44' + b'\x4e' + b'\xbc' + b'\xf0' + b'\xdc' + b'\x24' + b'\xa1' + b'\xa0' + b'\xc4' + b'\xe2' + b'\x07' + b'\x3c' + b'\x10' + b'\xc4' + b'\xb5' + b'\x25' + b'\x4b' + b'\x65' + b'\x63' + b'\xf4' + b'\x80' + b'\xe7' + b'\xcf' + b'\x61' + b'\xb1' + b'\x71' + b'\x82' + b'\x21' + b'\x87' + b'\x2c' + b'\xf5' + b'\x91' + b'\x00' + b'\x32' + b'\x0c' + b'\xec' + b'\xa9' + b'\xb5' + b'\x9a' + b'\x74' + b'\x85' + b'\xe3' + b'\x36' + b'\x8f' + b'\x76' + b'\x4f' + b'\x9c' + b'\x6d' + b'\xce' + b'\xbc' + b'\xad' + b'\x0a' + b'\x4b' + b'\xed' + b'\x76' + b'\x04' + b'\xcb' + b'\xc3' + b'\xb9' + b'\x33' + b'\x9e' + b'\x01' + b'\x93' + b'\x96' + b'\x69' + b'\x7d' + b'\xc5' + b'\xa2' + b'\x45' + b'\x79' + b'\x9b' + b'\x04' + b'\x5c' + b'\x84' + b'\x09' + b'\xed' + b'\x88' + b'\x43' + b'\xc7' + b'\xab' + b'\x93' + b'\x14' + b'\x26' + b'\xa1' + b'\x40' + b'\xb5' + b'\xce' + b'\x4e' + b'\xbf' + b'\x2a' + b'\x42' + b'\x85' + b'\x3e' + b'\x2c' + b'\x3b' + b'\x54' + b'\xe8' + b'\x12' + b'\x1f' + b'\x0e' + b'\x97' + b'\x59' + b'\xb2' + b'\x27' + b'\x89' + b'\xfa' + b'\xf2' + b'\xdf' + b'\x8e' + b'\x68' + b'\x59' + b'\xdc' + b'\x06' + b'\xbc' + b'\xb6' + b'\x85' + b'\x0d' + b'\x06' + b'\x22' + b'\xec' + b'\xb1' + b'\xcb' + b'\xe5' + b'\x04' + b'\xe6' + b'\x3d' + b'\xb3' + b'\xb0' + b'\x41' + b'\x73' + b'\x08' + b'\x3f' + b'\x3c' + b'\x58' + b'\x86' + b'\x63' + b'\xeb' + b'\x50' + b'\xee' + b'\x1d' + b'\x2c' + b'\x37' + b'\x74' + b'\xa9' + b'\xd3' + b'\x18' + b'\xa3' + b'\x47' + b'\x6e' + b'\x93' + b'\x54' + b'\xad' + b'\x0a' + b'\x5d' + b'\xb8' + b'\x2a' + b'\x55' + b'\x5d' + b'\x78' + b'\xf6' + b'\xee' + b'\xbe' + b'\x8e' + b'\x3c' + b'\x76' + b'\x69' + b'\xb9' + b'\x40' + b'\xc2' + b'\x34' + b'\xec' + b'\x2a' + b'\xb9' + b'\xed' + b'\x7e' + b'\x20' + b'\xe4' + b'\x8d' + b'\x00' + b'\x38' + b'\xc7' + b'\xe6' + b'\x8f' + b'\x44' + b'\xa8' + b'\x86' + b'\xce' + b'\xeb' + b'\x2a' + b'\xe9' + b'\x90' + b'\xf1' + b'\x4c' + b'\xdf' + b'\x32' + b'\xfb' + b'\x73' + b'\x1b' + b'\x6d' + b'\x92' + b'\x1e' + b'\x95' + b'\xfe' + b'\xb4' + b'\xdb' + b'\x65' + b'\xdf' + b'\x4d' + b'\x23' + b'\x54' + b'\x89' + b'\x48' + b'\xbf' + b'\x4a' + b'\x2e' + b'\x70' + b'\xd6' + b'\xd7' + b'\x62' + b'\xb4' + b'\x33' + b'\x29' + b'\xb1' + b'\x3a' + b'\x33' + b'\x4c' + b'\x23' + b'\x6d' + b'\xa6' + b'\x76' + b'\xa5' + b'\x21' + b'\x63' + b'\x48' + b'\xe6' + b'\x90' + b'\x5d' + b'\xed' + b'\x90' + b'\x95' + b'\x0b' + b'\x7a' + b'\x84' + b'\xbe' + b'\xb8' + b'\x0d' + b'\x5e' + b'\x63' + b'\x0c' + b'\x62' + b'\x26' + b'\x4c' + b'\x14' + b'\x5a' + b'\xb3' + b'\xac' + b'\x23' + b'\xa4' + b'\x74' + b'\xa7' + b'\x6f' + b'\x33' + b'\x30' + b'\x05' + b'\x60' + b'\x01' + b'\x42' + b'\xa0' + b'\x28' + b'\xb7' + b'\xee' + b'\x19' + b'\x38' + b'\xf1' + b'\x64' + b'\x80' + b'\x82' + b'\x43' + b'\xe1' + b'\x41' + b'\x27' + b'\x1f' + b'\x1f' + b'\x90' + b'\x54' + b'\x7a' + b'\xd5' + b'\x23' + b'\x2e' + b'\xd1' + b'\x3d' + b'\xcb' + b'\x28' + b'\xba' + b'\x58' + b'\x7f' + b'\xdc' + b'\x7c' + b'\x91' + b'\x24' + b'\xe9' + b'\x28' + b'\x51' + b'\x83' + b'\x6e' + b'\xc5' + b'\x56' + b'\x21' + b'\x42' + b'\xed' + b'\xa0' + b'\x56' + b'\x22' + b'\xa1' + b'\x40' + b'\x80' + b'\x6b' + b'\xa8' + b'\xf7' + b'\x94' + b'\xca' + b'\x13' + b'\x6b' + b'\x0c' + b'\x39' + b'\xd9' + b'\xfd' + b'\xe9' + b'\xf3' + b'\x6f' + b'\xa6' + b'\x9e' + b'\xfc' + b'\x70' + b'\x8a' + b'\xb3' + b'\xbc' + b'\x59' + b'\x3c' + b'\x1e' + b'\x1d' + b'\x6c' + b'\xf9' + b'\x7c' + b'\xaf' + b'\xf9' + b'\x88' + b'\x71' + b'\x95' + b'\xeb' + b'\x57' + b'\x00' + b'\xbd' + b'\x9f' + b'\x8c' + b'\x4f' + b'\xe1' + b'\x24' + b'\x83' + b'\xc5' + b'\x22' + b'\xea' + b'\xfd' + b'\xd3' + b'\x0c' + b'\xe2' + b'\x17' + b'\x18' + b'\x7c' + b'\x6a' + b'\x4c' + b'\xde' + b'\x77' + b'\xb4' + b'\x53' + b'\x9b' + b'\x4c' + b'\x81' + b'\xcd' + b'\x23' + b'\x60' + b'\xaa' + b'\x0e' + b'\x25' + b'\x73' + b'\x9c' + b'\x02' + b'\x79' + b'\x32' + b'\x30' + b'\xdf' + b'\x74' + b'\xdf' + b'\x75' + b'\x19' + b'\xf4' + b'\xa5' + b'\x14' + b'\x5c' + b'\xf7' + b'\x7a' + b'\xa8' + b'\xa5' + b'\x91' + b'\x84' + b'\x7c' + b'\x60' + b'\x03' + b'\x06' + b'\x3b' + b'\xcd' + b'\x50' + b'\xb6' + b'\x27' + b'\x9c' + b'\xfe' + b'\xb1' + b'\xdd' + b'\xcc' + b'\xd3' + b'\xb0' + b'\x59' + b'\x24' + b'\xb2' + b'\xca' + b'\xe2' + b'\x1c' + b'\x81' + b'\x22' + b'\x9d' + b'\x07' + b'\x8f' + b'\x8e' + b'\xb9' + b'\xbe' + b'\x4e' + b'\xfa' + b'\xfc' + b'\x39' + b'\x65' + b'\xba' + b'\xbf' + b'\x9d' + b'\x12' + b'\x37' + b'\x5e' + b'\x97' + b'\x7e' + b'\xf3' + b'\x89' + b'\xf5' + b'\x5d' + b'\xf5' + b'\xe3' + b'\x09' + b'\x8c' + b'\x62' + b'\xb5' + b'\x20' + b'\x9d' + b'\x0c' + b'\x53' + b'\x8a' + b'\x68' + b'\x1b' + b'\xd2' + b'\x8f' + b'\x75' + b'\x17' + b'\x5d' + b'\xd4' + b'\xe5' + b'\xda' + b'\x75' + b'\x62' + b'\x19' + b'\x14' + b'\x6a' + b'\x26' + b'\x2d' + b'\xeb' + b'\xf8' + b'\xaf' + b'\x37' + b'\xf0' + b'\x6c' + b'\xa4' + b'\x55' + b'\xb1' + b'\xbc' + b'\xe2' + b'\x33' + b'\xc0' + b'\x9a' + b'\xca' + b'\xb0' + b'\x11' + b'\x49' + b'\x4f' + b'\x68' + b'\x9b' + b'\x3b' + b'\x6b' + b'\x3c' + b'\xcc' + b'\x13' + b'\xf6' + b'\xc7' + b'\x85' + b'\x61' + b'\x68' + b'\x42' + b'\xae' + b'\xbb' + b'\xdd' + b'\xcd' + b'\x45' + b'\x16' + b'\x29' + b'\x1d' + b'\xea' + b'\xdb' + b'\xc8' + b'\x03' + b'\x94' + b'\x3c' + b'\xee' + b'\x4f' + b'\x82' + b'\x11' + b'\xc3' + b'\xec' + b'\x28' + b'\xbd' + b'\x97' + b'\x05' + b'\x99' + b'\xde' + b'\xd7' + b'\xbb' + b'\x5e' + b'\x22' + b'\x1f' + b'\xd4' + b'\xeb' + b'\x64' + b'\xd9' + b'\x92' + b'\xd9' + b'\x85' + b'\xb7' + b'\x6a' + b'\x05' + b'\x6a' + b'\xe4' + b'\x24' + b'\x41' + b'\xf1' + b'\xcd' + b'\xf0' + b'\xd8' + b'\x3f' + b'\xf8' + b'\x9e' + b'\x0e' + b'\xcd' + b'\x0b' + b'\x7a' + b'\x70' + b'\x6b' + b'\x5a' + b'\x75' + b'\x0a' + b'\x6a' + b'\x33' + b'\x88' + b'\xec' + b'\x17' + b'\x75' + b'\x08' + b'\x70' + b'\x10' + b'\x2f' + b'\x24' + b'\xcf' + b'\xc4' + b'\xe9' + b'\x42' + b'\x00' + b'\x61' + b'\x94' + b'\xca' + b'\x1f' + b'\x3a' + b'\x76' + b'\x06' + b'\xfa' + b'\xd2' + b'\x48' + b'\x81' + b'\xf0' + b'\x77' + b'\x60' + b'\x03' + b'\x45' + b'\xd9' + b'\x61' + b'\xf4' + b'\xa4' + b'\x6f' + b'\x3d' + b'\xd9' + b'\x30' + b'\xc3' + b'\x04' + b'\x6b' + b'\x54' + b'\x2a' + b'\xb7' + b'\xec' + b'\x3b' + b'\xf4' + b'\x4b' + b'\xf5' + b'\x68' + b'\x52' + b'\x26' + b'\xce' + b'\xff' + b'\x5d' + b'\x19' + b'\x91' + b'\xa0' + b'\xa3' + b'\xa5' + b'\xa9' + b'\xb1' + b'\xe0' + b'\x23' + b'\xc4' + b'\x0a' + b'\x77' + b'\x4d' + b'\xf9' + b'\x51' + b'\x20' + b'\xa3' + b'\xa5' + b'\xa9' + b'\xb1' + b'\xc1' + b'\x00' + b'\x82' + b'\x86' + b'\x8e' + b'\x7f' + b'\x5d' + b'\x19' + b'\x91' + b'\xa0' + b'\xa3' + b'\xc4' + b'\xeb' + b'\x54' + b'\x0b' + b'\x75' + b'\x68' + b'\x52' + b'\x07' + b'\x8c' + b'\x9a' + b'\x97' + b'\x8d' + b'\x79' + b'\x70' + b'\x62' + b'\x46' + b'\xef' + b'\x5c' + b'\x1b' + b'\x95' + b'\x89' + b'\x71' + b'\x41' + b'\xe1' + b'\x21' + b'\xa1' + b'\xa1' + b'\xa1' + b'\xc0' + b'\x02' + b'\x67' + b'\x4c' + b'\x1a' + b'\xb6' + b'\xcf' + b'\xfd' + b'\x78' + b'\x53' + b'\x24' + b'\xab' + b'\xb5' + b'\xc9' + b'\xf1' + b'\x60' + b'\x23' + b'\xa5' + b'\xc8' + b'\x12' + b'\x87' + b'\x6d' + b'\x58' + b'\x13' + b'\x85' + b'\x88' + b'\x92' + b'\x87' + b'\x6d' + b'\x58' + b'\x32' + b'\xc7' + b'\x0c' + b'\x9a' + b'\x97' + b'\xac' + b'\xda' + b'\x36' + b'\xee' + b'\x5e' + b'\x3e' + b'\xdf' + b'\x1d' + b'\xb8' + b'\xf2' + b'\x66' + b'\x2f' + b'\xbd' + b'\xf8' + b'\x72' + b'\x47' + b'\xed' + b'\x58' + b'\x13' + b'\x85' + b'\x88' + b'\x92' + b'\x87' + b'\x8c' + b'\x7b' + b'\x55' + b'\x09' + b'\x90' + b'\xa2' + b'\xc6' + b'\xef' + b'\x3d' + b'\xf8' + b'\x53' + b'\x24' + b'\xab' + b'\xd4' + b'\x2a' + b'\xb7' + b'\xec' + b'\x5a' + b'\x36' + b'\xee' + b'\x5e' + b'\x3e' + b'\xdf' + b'\x3c' + b'\xfa' + b'\x76' + b'\x4f' + b'\xfd' + b'\x59' + b'\x30' + b'\xe2' + b'\x46' + b'\xef' + b'\x3d' + b'\xf8' + b'\x53' + b'\x05' + b'\x69' + b'\x31' + b'\xc1' + b'\x00' + b'\x82' + b'\x86' + b'\x8e' + b'\x7f' + b'\x5d' + b'\x19' + b'\xb0' + b'\xe2' + b'\x27' + b'\xcc' + b'\xfb' + b'\x74' + b'\x4b' + b'\x14' + b'\x8b' + b'\x94' + b'\x8b' + b'\x75' + b'\x68' + b'\x33' + b'\xc5' + b'\x08' + b'\x92' + b'\x87' + b'\x8c' + b'\x9a' + b'\xb6' + b'\xcf' + b'\x1c' + b'\xba' + b'\xd7' + b'\x0d' + b'\x98' + b'\xb2' + b'\xe6' + b'\x2f' + b'\xdc' + b'\x1b' + b'\x95' + b'\x89' + b'\x71' + b'\x60' + b'\x23' + b'\xc4' + b'\x0a' + b'\x96' + b'\x8f' + b'\x9c' + b'\xba' + b'\xf6' + b'\x6e' + b'\x3f' + b'\xfc' + b'\x5b' + b'\x15' + b'\xa8' + b'\xd2' + b'\x26' + b'\xaf' + b'\xbd' + b'\xf8' + b'\x72' + b'\x66' + b'\x2f' + b'\xdc' + b'\x1b' + b'\xb4' + b'\xcb' + b'\x14' + b'\x8b' + b'\x94' + b'\xaa' + b'\xb7' + b'\xcd' + b'\xf9' + b'\x51' + b'\x01' + b'\x80' + b'\x82' + b'\x86' + b'\x6f' + b'\x3d' + b'\xd9' + b'\x30' + b'\xe2' + b'\x27' + b'\xcc' + b'\xfb' + b'\x74' + b'\x4b' + b'\x14' + b'\xaa' + b'\xb7' + b'\xcd' + b'\xf9' + b'\x70' + b'\x43' + b'\x04' + b'\x6b' + b'\x35' + b'\xc9' + b'\xf1' + b'\x60' + b'\x23' + b'\xa5' + b'\xc8' + b'\xf3' + b'\x45' + b'\x08' + b'\x92' + b'\x87' + b'\x6d' + b'\x58' + b'\x32' + b'\xe6' + b'\x2f' + b'\xbd' + b'\xf8' + b'\x72' + b'\x66' + b'\x4e' + b'\x1e' + b'\xbe' + b'\xfe' + b'\x7e' + b'\x7e' + b'\x7e' + b'\x5f' + b'\x1d' + b'\x99' + b'\x91' + b'\xa0' + b'\xa3' + b'\xc4' + b'\x0a' + b'\x77' + b'\x4d' + b'\x18' + b'\x93' + b'\xa4' + b'\xab' + b'\xd4' + b'\x0b' + b'\x75' + b'\x49' + b'\x10' + b'\xa2' + b'\xc6' + b'\xef' + b'\x3d' + b'\xf8' + b'\x53' + b'\x24' + b'\xab' + b'\xb5' + b'\xe8' + b'\x33' + b'\xe4' + b'\x4a' + b'\x16' + b'\xae' + b'\xde' + b'\x1f' + b'\xbc' + b'\xdb' + b'\x15' + b'\xa8' + b'\xb3' + b'\xc5' + b'\x08' + b'\x73' + b'\x45' + b'\xe9' + b'\x31' + b'\xc1' + b'\xe1' + b'\x21' + b'\xa1' + b'\xa1' + b'\xa1' + b'\xc0' + b'\x02' + b'\x86' + b'\x6f' + b'\x5c' + b'\x3a' + b'\xd7' + b'\x0d' + b'\x98' + b'\x93' + b'\xa4' + b'\xca' + b'\x16' + b'\xae' + b'\xde' + b'\x1f' + b'\x9d' + b'\x99' + b'\xb0' + b'\xe2' + b'\x46' + b'\xef' + b'\x3d' + b'\xf8' + b'\x72' + b'\x47' + b'\x0c' + b'\x9a' + b'\xb6' + b'\xcf' + b'\xfd' + b'\x59' + b'\x11' + b'\xa0' + b'\xa3' + b'\xa5' + b'\xc8' + b'\xf3' + b'\x45' + b'\x08' + b'\x92' + b'\x87' + b'\x6d' + b'\x39' + b'\xf0' + b'\x43' + b'\x04' + b'\x8a' + b'\x96' + b'\xae' + b'\xde' + b'\x3e' + b'\xdf' + b'\x1d' + b'\x99' + b'\x91' + b'\xa0' + b'\xc2' + b'\x06' + b'\x6f' + b'\x3d' + b'\xf8' + b'\x72' + b'\x47' + b'\x0c' + b'\x9a' + b'\x97' + b'\x8d' + b'\x98' + b'\x93' + b'\x85' + b'\x88' + b'\x73' + b'\x45' + b'\xe9' + b'\x31' + b'\xe0' + b'\x23' + b'\xa5' + b'\xa9' + b'\xd0' + b'\x03' + b'\x84' + b'\x8a' + b'\x96' + b'\xae' + b'\xde' + b'\x1f' + b'\xbc' + b'\xdb' + b'\x15' + b'\xa8' + b'\xd2' + b'\x26' + b'\xce' + b'\xff' + b'\x5d' + b'\x19' + b'\x91' + b'\x81' + b'\x80' + b'\x82' + b'\x67' + b'\x2d' + b'\xd8' + b'\x13' + b'\xa4' + b'\xab' + b'\xd4' + b'\x0b' + b'\x94' + b'\xaa' + b'\xb7' + b'\xcd' + b'\xf9' + b'\x51' + b'\x20' + b'\xa3' + b'\xa5' + b'\xc8' + b'\xf3' + b'\x45' + b'\xe9' + b'\x50' + b'\x22' + b'\xc6' + b'\xef' + b'\x5c' + b'\x3a' + b'\xd7' + b'\x0d' + b'\x98' + b'\x93' + b'\x85' + b'\x88' + b'\x73' + b'\x64' + b'\x4a' + b'\xf7' + b'\x4d' + b'\xf9' + b'\x51' + b'\x20' + b'\xa3' + b'\xc4' + b'\x0a' + b'\x96' + b'\xae' + b'\xde' + b'\x3e' + b'\xfe' + b'\x7e' + b'\x7e' + b'\x7e' + b'\x5f' + b'\x3c' + b'\xfa' + b'\x76' + b'\x4f' + b'\xfd' + b'\x78' + b'\x72' + b'\x66' + b'\x2f' + b'\xbd' + b'\xd9' + b'\x30' + b'\xc3' + b'\xe5' + b'\x48' + b'\x12' + b'\x87' + b'\x8c' + b'\x7b' + b'\x55' + b'\x28' + b'\xd2' + b'\x07' + b'\x8c' + b'\x9a' + b'\x97' + b'\xac' + b'\xda' + b'\x17' + b'\x8d' + b'\x79' + b'\x51' + b'\x20' + b'\xa3' + b'\xc4' + b'\xeb' + b'\x54' + b'\x0b' + b'\x94' + b'\x8b' + b'\x94' + b'\xaa' + b'\xd6' + b'\x2e' + b'\xbf' + b'\xfc' + b'\x5b' + b'\x15' + b'\xa8' + b'\xd2' + b'\x26' + b'\xaf' + b'\xdc' + b'\x1b' + b'\xb4' + b'\xea' + b'\x37' + b'\xec' + b'\x3b' + b'\xf4' + b'\x6a' + b'\x37' + b'\xcd' + b'\x18' + b'\x93' + b'\x85' + b'\x69' + b'\x31' + b'\xc1' + b'\xe1' + b'\x40' + b'\xe3' + b'\x25' + b'\xc8' + b'\x12' + b'\x87' + b'\x8c' + b'\x9a' + b'\xb6' + b'\xcf' + b'\xfd' + b'\x59' + b'\x11' + b'\xa0' + b'\xc2' + b'\x06' + b'\x8e' + b'\x7f' + b'\x5d' + b'\x38' + b'\xf2' + b'\x47' + b'\x0c' + b'\x7b' + b'\x74' + b'\x6a' + b'\x37' + b'\xec' + b'\x5a' + b'\x36' + b'\xee' + b'\x3f' + b'\xfc' + b'\x7a' + b'\x76' + b'\x4f' + b'\x1c' + b'\x9b' + b'\x95' + b'\x89' + b'\x71' + b'\x41' + b'\x00' + b'\x63' + b'\x44' + b'\xeb' + b'\x54' + b'\x2a' + b'\xd6' + b'\x0f' + b'\x9c' + b'\xba' + b'\xd7' + b'\x0d' + b'\x98' + b'\x93' + b'\x85' + b'\x69' + b'\x31' + b'\xc1' + b'\x00' + b'\x82' + b'\x86' + b'\x8e' + b'\x9e' + b'\xbe' + b'\xdf' + b'\x3c' + b'\xfa' + b'\x57' + b'\x2c' + b'\xda' + b'\x36' + b'\xee' + b'\x3f' + b'\xfc' + b'\x5b' + b'\x15' + b'\x89' + b'\x71' + b'\x41' + b'\x00' + b'\x82' + b'\x86' + b'\x8e' + b'\x7f' + b'\x5d' + b'\x38' + b'\xf2' + b'\x47' + b'\xed' + b'\x58' + b'\x13' + b'\xa4' + b'\xca' + b'\xf7' + b'\x4d' + b'\xf9' + b'\x51' + b'\x01' + b'\x80' + b'\x63' + b'\x44' + b'\xeb' + b'\x54' + b'\x2a' + b'\xd6' + b'\x2e' + b'\xbf' + b'\xdd' + b'\x19' + b'\x91' + b'\xa0' + b'\xa3' + b'\xa5' + b'\xa9' + b'\xb1' + b'\xe0' + b'\x42' + b'\x06' + b'\x8e' + b'\x7f' + b'\x5d' + b'\x19' + b'\x91' + b'\xa0' + b'\xa3' + b'\xc4' + b'\x0a' + b'\x96' + b'\x8f' + b'\x7d' + b'\x78' + b'\x72' + b'\x47' + b'\x0c' + b'\x7b' + b'\x74' + b'\x6a' + b'\x56' + b'\x2e' + b'\xde' + b'\x1f' + b'\xbc' + b'\xfa' + b'\x57' + b'\x0d' + b'\x79' + b'\x51' + b'\x01' + b'\x61' + b'\x21' + b'\xa1' + b'\xc0' + b'\xe3' + b'\x25' + b'\xa9' + b'\xb1' + b'\xc1' + b'\xe1' + b'\x40' + b'\x02' + b'\x67' + b'\x4c' + b'\x1a' + b'\x97' + b'\x8d' + b'\x98' + b'\x93' + b'\xa4' + b'\xab' + b'\xd4' + b'\x2a' + b'\xd6' + b'\x0f' + b'\x9c' + b'\x9b' + b'\xb4' + b'\xcb' + b'\x14' + b'\xaa' + b'\xb7' + b'\xcd' + b'\xf9' + b'\x51' + b'\x20' + b'\xa3' + b'\xc4' + b'\xeb' + b'\x35' + b'\xc9' + b'\xf1' + b'\x60' + b'\x42' + b'\x06' + b'\x8e' + b'\x7f' + b'\x7c' + b'\x7a' + b'\x76' + b'\x6e' + b'\x3f' + b'\xfc' + b'\x7a' + b'\x76' + b'\x6e' + b'\x5e' + b'\x3e' + b'\xfe' + b'\x7e' + b'\x5f' + b'\x3c' + b'\xdb' + b'\x15' + b'\x89' + b'\x71' + b'\x41' + b'\xe1' + b'\x21' + b'\xc0' + b'\xe3' + b'\x44' + b'\xeb' + b'\x54' + b'\x2a' + b'\xb7' + b'\xcd' + b'\xf9' + b'\x70' + b'\x62' + b'\x27' + b'\xad' + b'\xd8' + b'\x32' + b'\xc7' + b'\x0c' + b'\x7b' + b'\x74' + b'\x4b' + b'\x14' + b'\xaa' + b'\xb7' + b'\xec' + b'\x3b' + b'\xd5' + b'\x28' + b'\xd2' + b'\x07' + b'\x6d' + b'\x39' + b'\xd1' + b'\x20' + b'\xc2' + b'\xe7' + b'\x4c' + b'\x1a' + b'\x97' + b'\x8d' + b'\x98' + b'\xb2' + b'\xc7' + b'\x0c' + b'\x59' + b'\x28' + b'\xf3' + b'\x9b' +) \ No newline at end of file From 86d19843ce23e1d7a7e91ab4ecfc1d89c011c877 Mon Sep 17 00:00:00 2001 From: Peter Whitesell Date: Tue, 16 May 2023 19:02:45 -0500 Subject: [PATCH 4/4] ability to disable per-module debug logs --- kmk/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kmk/utils.py b/kmk/utils.py index b27a04ba6..9e145eda8 100644 --- a/kmk/utils.py +++ b/kmk/utils.py @@ -17,6 +17,7 @@ class Debug: '''default usage: debug = Debug(__name__) ''' + _disabled_names = {} def __init__(self, name: str = __name__): self.name = name @@ -24,6 +25,8 @@ def __init__(self, name: str = __name__): def __call__(self, *message: str, name: Optional[str] = None) -> None: if not name: name = self.name + if name in self._disabled_names: + return print(ticks_ms(), end=' ') print(name, end=': ') print(*message, sep='') @@ -37,3 +40,8 @@ def enabled(self) -> bool: def enabled(self, enabled: bool): global _debug_enabled _debug_enabled = enabled + + @classmethod + def disable(cls, *names): + for name in names: + cls._disabled_names[name] = True