Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A few rendering problems with alpha channel #1544

Closed
kdschlosser opened this issue Mar 10, 2020 · 60 comments
Closed

A few rendering problems with alpha channel #1544

kdschlosser opened this issue Mar 10, 2020 · 60 comments

Comments

@kdschlosser
Copy link
Contributor

kdschlosser commented Mar 10, 2020

Operating system:

Windows 7 x64 SP1

wxPython version & source:

4.0.7.post1 msw (phoenix) wxWidgets 3.0.5
pypi

Python version & source:

3.7.5 Stackless 3.7 (tags/v3.7.5-slp:f7925f2a02, Oct 20 2019, 15:28:53) [MSC v.1916 64 bit (AMD64)] (cpython)
https://github.com/stackless-dev/stackless

Description of the problem:
There are a few issues I am running into. I have attached an image to show the problems that are taking place.

  • I am not able to draw any text that has an alpha channel. Well let me correct that. It draws the text, however the alpha channel is not used. You can see this in the attached image. Every color used in that image has an alpha channel. I did a screen capture with the frame placed over a white background so you would be able to see the problems. The white is not part of the example code below.

    I have tried with GCDC.DrawText and also GraphicsContext.DrawText and neither of them support an alpha channel.

  • I am drawing a simple drop shadow using wx.Region to subtract the actual text from the bitmap where the shadow is drawn. The appearance starts out fine but as the text continues there ends up being areas that get improperly clipped, this issue gets worse as the text continues. you can see this problem in 1, 2 and 3 of the attached image.

    I have studied this fairly closely and if you look at the text you will see the text being antialiased to black. I am not sure why this is taking place, I have set the background text dolor to wx.TransparentColour

  • I may have mentioned this before, I do not remember if I have or not. This problem only becomes evident when using an alpha channel. When rendering a rectangle the filled in area of the rectangle can be seen under the outline. This can be seen in the image item 5, you may need to zoom into the image to see it better.

    If you look at item 4 you do not see this issue because the filled in area has an alpha channel of 0.

    There needs to be some kind of a check to see if the pen has an alpha level is 0 < alpha < 255 and if that is the case then the filled in area needs to be adjusted to be smaller. I am sure this same issue also exists with the other functions as well. There is also another issue regarding the use of these types of functions. If you take this example

    MemoryDC.SetPen(wx.Pen(wx.Colour(0, 0, 255, 255), 4))
    MemoryDC.SetBrush(wx.Brush(wx.Colour(255, 0, 0, 255))
    MemoryDC.DrawRectangle(0, 0, 150, 150)

    The resulting rectangle is NOT 150, 150. it ends up being 154, 154. I do know this problem has existed for a very long time and to correct it would cause a lot of problems for projects already using wxPython. Perhaps the addition of a global flag that can be set that would tell wxPython to render to exact sizes.

I may be doing something incorrect, tho I do not think that I am.

image

Here is the frame as seen without the white behind it.

image

This example script will ONLY run on Windows.

import wx
import ctypes

from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# )

GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


class Frame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(
            self,
            None,
            -1,
            style=(
                wx.FRAME_SHAPED |
                wx.NO_BORDER |
                wx.FRAME_NO_TASKBAR |
                wx.STAY_ON_TOP
            ),
            size=(600, 300),
            pos=(400, 400)
        )
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, evt):
        width, height = self.GetClientSize()

        text = 'Alpha test'
        font = self.GetFont()
        font.SetPointSize(font.GetPointSize() + 16)
        font = font.MakeBold()
        font = font.MakeItalic()

        bmp = wx.Bitmap(width, height)
        bmp2 = wx.Bitmap(width, height)
        bmp3 = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp3)
        dc.SetTextForeground(wx.Colour(255, 255, 255, 255))
        dc.SetFont(font)

        text_width, text_height = dc.GetFullTextExtent(text)[:2]

        text_x = (width / 2) - (text_width / 2)
        text_y = (height / 2) - (text_height / 2)

        dc.DrawText(text, text_x, text_y)

        dc.SelectObject(bmp2)

        dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 255), 6))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 255)))
        dc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)


        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp2, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)

        gcdc.SetDeviceClippingRegion(region1)
        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 140), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        gc.DrawRoundedRectangle(3, 3, width-6, height-6, 5)

        gcdc.DestroyClippingRegion()

        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 120), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 0)))
        gc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp3, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.SetDeviceClippingRegion(region1)
        gcdc.SetTextForeground(wx.Colour(0, 0, 0, 130))
        gcdc.SetTextBackground(wx.TransparentColour)
        gcdc.SetFont(font)
        gc.DrawText(text, text_x - 4, text_y - 4)
        gcdc.DestroyClippingRegion()

        gcdc.SetTextForeground(wx.Colour(0, 255, 0, 150))
        gcdc.SetTextBackground(wx.TransparentColour)
        gc.DrawText(text, text_x, text_y)

        gcdc.Destroy()
        del gcdc

        dc.SelectObject(wx.NullBitmap)
        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)

        self.draw_alpha(bmp)

    def draw_alpha(self, bmp):
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )


app = wx.App()

frame = Frame()
frame.Show()
app.MainLoop()

@Metallicow
Here is an example how how to overcome the alpha issue with shaped frames, this example only works on Windows. I would think that there are similiar API functions for OSX and Linux that can be used to achieve the same results

@Metallicow
Copy link
Contributor

Hmmm well whatdayaknow... it sorta works. Still has the BLACK issue on wxPy4.1 tho.... I'm beginning to think that whatever changed in wxWidgets is what may be causing the performance issues on wxPy4.1 also, but am not sure...

wx40alpha

wx41alpha

brushwithalpha.png
brushwithalpha

Collapsible Content - Click to expand
#!/usr/bin/env python
# -*- coding: utf-8 -*-

""""""

# Imports.--------------------------------------------------------------------
# -Python Imports.
import os
import sys

import ctypes
from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

# -wxPython Imports.
import wx

wxVER = 'wxPython %s' % wx.version()
pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
versionInfos = '%s %s' % (wxVER, pyVER)

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# );
GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


class Frame(wx.Frame):

    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.FRAME_SHAPED |
                       wx.NO_BORDER |
                       wx.FRAME_NO_TASKBAR |
                       wx.STAY_ON_TOP
                 , name='frame'):
        wx.Frame.__init__(self, parent, id, title, pos, size, style, name)

        self.delta = (0, 0)

        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnExit(self, event):
        self.Close()

    def OnLeftDown(self, event):
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = ((dx, dy))

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            self.Move(fp)

    def OnPaint(self, event):
        width, height = self.GetClientSize()

        text = 'Alpha test'
        font = self.GetFont()
        font.SetPointSize(font.GetPointSize() + 16)
        font = font.MakeBold()
        font = font.MakeItalic()

        bmp = wx.Bitmap(width, height)
        bmp2 = wx.Bitmap(width, height)
        bmp3 = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp3)
        dc.SetTextForeground(wx.Colour(255, 255, 255, 255))
        dc.SetFont(font)

        text_width, text_height = dc.GetFullTextExtent(text)[:2]

        text_x = (width / 2) - (text_width / 2)
        text_y = (height / 2) - (text_height / 2)

        dc.DrawText(text, text_x, text_y)

        dc.SelectObject(bmp2)

        dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 255), 6))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 255)))
        dc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp2, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)

        gcdc.SetDeviceClippingRegion(region1)
        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 140), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        gc.DrawRoundedRectangle(3, 3, width-6, height-6, 5)

        gcdc.DestroyClippingRegion()

        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 120), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 0)))
        gc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp3, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.SetDeviceClippingRegion(region1)
        gcdc.SetTextForeground(wx.Colour(0, 0, 0, 130))
        gcdc.SetTextBackground(wx.TransparentColour)
        gcdc.SetFont(font)
        gc.DrawText(text, text_x - 4, text_y - 4)
        gcdc.DestroyClippingRegion()

        gcdc.SetTextForeground(wx.Colour(0, 255, 0, 150))
        gcdc.SetTextBackground(wx.TransparentColour)
        gc.DrawText(text, text_x, text_y)

        ## vippibmp = wx.Bitmap('Vippi50.png', wx.BITMAP_TYPE_PNG)
        ## gcdc.DrawBitmap(vippibmp, x=0, y=0, useMask=False)
        brushbmp = wx.Bitmap('brushwithalpha.png', wx.BITMAP_TYPE_PNG)
        gcdc.DrawBitmap(brushbmp, x=200, y=10, useMask=False)

        gcdc.Destroy()
        del gcdc

        dc.SelectObject(wx.NullBitmap)
        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)

        self.draw_alpha(bmp)

    def draw_alpha(self, bmp):
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )

if __name__ == '__main__':
    print(versionInfos)
    app = wx.App()
    frame = Frame(None, size=(600, 300), pos=(400, 400))
    frame.Show()
    app.MainLoop()

I'm guessing you ripped the constants out of win32 to make it purepython. Not sure what you would need to do that with linux or mac...

With the stacked frames approach I made I eventually got to a point where I rage quit about 10 times because flattening out the font into a png made the stroke look worse and worse as I went.
Then I came across the left edge wasn't rendering the first pixels on each line for who knows what reason. Hardcoding -1 didn't work either... I figured I could paint the logo into a walrus sitting on a couch, and combine it with my animated python powered apng and rerender it so it might be able to be ripped into a throbber for a animated splash screen. Needless to say, I kept getting frustrated by the little problems.
shapedwintest
part of the issue is that the font isn't free so that's why I was rendering it as a png.

I think with your approach combined with 2 stacked frames it will be possible to do a fancy animated splash with alpha. I even spent the time to write a custom fade in timer for it that looks nice.
... But yea I might have to add +1 or +2 to the frame size to work around the edge issue. That likely will uglyfy the code a bit by scattering extra variables everywhere which most folks will then ask "Why did you add this hardcoded +2?"

Also with your approach live painting directly from krita onto a frame will now support a alpha background, which is nice.

@kdschlosser
Copy link
Contributor Author

I do not believe in the phrase "It cannot be done", I will however accept "With out present state of technology we cannot do it." LOL

I knew there was a better way, I have been scouring over the Windows SDK.. actually I have ported almost a million lines of it to Python (pure).

This is the real bonus of the way I have gone about it is if you wanted to draw an alpha frame that had a colored background, then you draw a png on top of it. If that png is not 100% opaque you would normally end up with the color from the background changing the colors in the png. The mechanics are there to stop that from happening.

You like how i made the hole through the middle of the frame?

both the text alpha and the overlap of the pen and brush can be coded around. doing so is expensive and it would be nice to have those issues fixed at the lower level of wxPython/wxWidgets.

I did want to let you know that a fade in and out is super simple to accomplish.

click the arrow to see the fade in/fade out example
#!/usr/bin/env python
# -*- coding: utf-8 -*-

""""""

# Imports.--------------------------------------------------------------------
# -Python Imports.
import os
import sys
import threading
import ctypes
from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

# -wxPython Imports.
import wx

wxVER = 'wxPython %s' % wx.version()
pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
versionInfos = '%s %s' % (wxVER, pyVER)

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# );
GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD


def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


class Frame(wx.Frame):

    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.FRAME_SHAPED |
                       wx.NO_BORDER |
                       wx.FRAME_NO_TASKBAR |
                       wx.STAY_ON_TOP
                 , name='frame'):
        wx.Frame.__init__(self, parent, id, title, pos, size, style, name)

        self.delta = (0, 0)
        self.fade_event = threading.Event()
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        
        self.transparency = 0

    def on_close(self, evt):
        self.fade_out()        
        evt.Skip()

    def OnExit(self, event):
        self.Close()

    def OnLeftDown(self, event):
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = ((dx, dy))

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            self.Move(fp)

    def OnPaint(self, event):
        width, height = self.GetClientSize()

        text = 'Alpha test'
        font = self.GetFont()
        font.SetPointSize(font.GetPointSize() + 16)
        font = font.MakeBold()
        font = font.MakeItalic()

        bmp = wx.Bitmap(width, height)
        bmp2 = wx.Bitmap(width, height)
        bmp3 = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp3)
        dc.SetTextForeground(wx.Colour(255, 255, 255, 255))
        dc.SetFont(font)

        text_width, text_height = dc.GetFullTextExtent(text)[:2]

        text_x = (width / 2) - (text_width / 2)
        text_y = (height / 2) - (text_height / 2)

        dc.DrawText(text, text_x, text_y)

        dc.SelectObject(bmp2)

        dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 255), 6))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 255)))
        dc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp2, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)

        gcdc.SetDeviceClippingRegion(region1)
        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 140), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        gc.DrawRoundedRectangle(3, 3, width-6, height-6, 5)

        gcdc.DestroyClippingRegion()

        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 120), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 0)))
        gc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp3, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.SetDeviceClippingRegion(region1)
        gcdc.SetTextForeground(wx.Colour(0, 0, 0, 130))
        gcdc.SetTextBackground(wx.TransparentColour)
        gcdc.SetFont(font)
        gc.DrawText(text, text_x - 4, text_y - 4)
        gcdc.DestroyClippingRegion()

        gcdc.SetTextForeground(wx.Colour(0, 255, 0, 150))
        gcdc.SetTextBackground(wx.TransparentColour)
        gc.DrawText(text, text_x, text_y)

        ## vippibmp = wx.Bitmap('Vippi50.png', wx.BITMAP_TYPE_PNG)
        ## gcdc.DrawBitmap(vippibmp, x=0, y=0, useMask=False)
        brushbmp = wx.Bitmap('brushwithalpha.png', wx.BITMAP_TYPE_PNG)
        gcdc.DrawBitmap(brushbmp, x=200, y=10, useMask=False)

        gcdc.Destroy()
        del gcdc

        dc.SelectObject(wx.NullBitmap)
        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)
        
        self.bmp = bmp
        
        self.draw_alpha(self.transparency)
        
    def fade_in(self):
        
        def _do():
            for i in range(self.transparency, 256):
                self.transparency = i
                self.draw_alpha(i)
                self.fade_event.wait(0.02)
                if self.fade_event.is_set():
                    break
            
        threading.Thread(target=_do).start()
        
    def fade_out(self):
        self.fade_event.set()
        
        for i in range(self.transparency, -1, -1):
            self.transparency = i
            self.draw_alpha(i)
            self.fade_event.wait(0.02)
            if self.fade_event.is_set():
                break
        
    def draw_alpha(self, transparency):
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, transparency, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(self.bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )

if __name__ == '__main__':
    print(versionInfos)
    app = wx.App()
    frame = Frame(None, size=(600, 300), pos=(400, 400))
    frame.Show()
    frame.fade_in()
    app.Main

I have not tested this code, But it will give you the general idea anywho.

@kdschlosser
Copy link
Contributor Author

also you can ditch the use of GraphicsContext all together and only use GCDC, the output is the same. I had it in there for purposes of testing the text alpha. the black issue might go away if you do that.

@Metallicow
Copy link
Contributor

Yea timer fade is simple enough. Mine is basically like that with range 256 but it skips a bit slower each iteration to make is a slowing curve, tho I don't use thread, just a sleep call. Then I invert to logic and run it about x5 faster on destruction. The curve makes it look a bit nicer than just a hard number for the timer. It still causes issues with wxPy4.1 tho atm.
I did actually write a starter for a pie menu im working on which is similar to the cutout. Instead of cutting out, I invert the logic to make slices in a drawn graphic, then convert the graphic to a mask and replace the green with black or whatever color I want when I have my bitmap to splat on the frame mask. You have to keep track of the regions to tell if mouse is over a slice section(button etc)
piemenu

@Metallicow
Copy link
Contributor

@kdschlosser If you really want a single file test piece, you can use my python powered logo apng to test.
I know there isn't a lot of files that can be ripped into all the tests but it does 99% of them.
apngdis/apngasm can do most of the take apart and put back to gether work along with pillow.
AnimPythonPoweredLogo120fr512x128
This one renders slow on browsers, since a gif.

If you really want the apng source files in blender I can send em to ya, tho I would bet 99% of em will cause a BLACK screen on wxPy4.1 if rendered tho

Animated3DPythonPoweredLogo120fr512x128px1_60secAPNG
.

If you are reading this line, you will notice the difference between gif and apng in your browser.
My opinion is it should look the same way with alpha in a basic demo with like the brush alpha png i provided.

@kdschlosser
Copy link
Contributor Author

OK so I dod some messing about with your animated PNG file. It is not that hard to create a wx Animation or splash screen from it. The apng specification is an easy specification. There is actually a python library that will break the apng apart into separate png images. go figure on this.. the name of the library is "apng" LOL..

It also provides you the frame header data. so you know how long to wait between frame renderings and how to clear the old frame and ly in the new frame. That is the part you would have to figure out how to do with masks. It did a quick and dirty. it didn't come out 100% correct but the animation portion of it works nd it works just fine. You would need to create a thread to handle the renderings.

The hardest part of the whole deal is going to be the masks. because apng has some goofy ways of handling how a new frame gets drawn. it can either get drawn over the old, or the old needs to get blanked to black with an alpha of 0 first then the new frame gets drawn. or the hole animation takes a back step by a single frame and the new frame gets drawn on top of the frame prior to the one that would normally be getting written on top of. and with the animation above the whole thing does not get drawn each time. only the spinning bit does. so you need to mask off portions of the main image in order to be able to draw it properly.

It is something that is very achievable. I have gotten fairly close to getting it right. But seeing as how apng is just about a dead image format anywho I do not see any kind of real need figure it out. apng never really took off because it had to many downfalls like not being able to reuse already added images. While it was a great attempt at improving upon the animated GIF it never really became widely adopted

@kdschlosser
Copy link
Contributor Author

kdschlosser commented Mar 15, 2020

Here is a partially working example of the apng. There are some rendering anomalies that still need to be sorted out. read the comments before running the script.

Example Code
path = r'c:\PATH\TO\APNG'

# you need to install the apng library
import apng
import wx
import ctypes
import threading
import tempfile

from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

temp_dir = tempfile.gettempdir()

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# )

GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


# frame_control.depose_op

# no disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.
APNG_DISPOSE_OP_NONE = 0 
# the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
APNG_DISPOSE_OP_BACKGROUND = 1 
# the frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
APNG_DISPOSE_OP_PREVIOUS = 2 

# frame_control.blend_op

# all color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region.
APNG_BLEND_OP_SOURCE = 0 
# frame should be composited onto the output buffer based on its alpha, using a simple OVER operation
APNG_BLEND_OP_OVER = 1 

splash_png = apng.APNG.open(path)

bitmaps = []
app = wx.App()

import os

print(wx.version())

first_frame = splash_png.frames[0][1]

apng_width = first_frame.width
apng_height = first_frame.height

dc = wx.MemoryDC()

frames = []

last_frame = None

class AnimationFrame(object):

    def __init__(self, index, png, frame_control):
        self.index = index
        self.png = png
        self.width = frame_control.width
        self.height = frame_control.height
        self.x_offset = frame_control.x_offset
        self.y_offset = frame_control.y_offset
        
        # These 2 variables are what are to be used when determining 
        # how to draw the next frame.
        # I have not spent enough time tinkering with it to get the values
        # of these 2 set up correctly to render the frames properly.
        # this is the reason why you are seeing the black in the animation.
        self.depose_op = frame_control.depose_op
        self.blend_op = frame_control.blend_op
        
        delay_num = frame_control.delay
        delay_den = frame_control.delay_den

        if delay_num == 0:
            self.delay = 0
        else:
            if delay_den == 0:
                delay_den = 100

            self.delay = delay_num / delay_den

        # I was lasy and didn't spend the time to figure out how to
        # collect the PNG data and send it dorectly into wx without the need for
        # making png files and opening them using wx.Bitmap. I know there is a 
        # way to do it. I really do not remember how off the top of my head.
        save_path = os.path.join(temp_dir, 'Frame_' + str(i) + '.png')
        with open(save_path, 'wb') as f:
            f.write(png.to_bytes())

        bitmap = wx.Bitmap(save_path, wx.BITMAP_TYPE_PNG)
        new_bitmap = wx.Bitmap(self.width, self.height)
        test_bitmap = wx.Bitmap(self.width, self.height)

        dc = wx.MemoryDC()
        dc.SelectObject(new_bitmap)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)
        # gcdc.SetPen(wx.TRANSPARENT_PEN)
        # gcdc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 150)))
        # gcdc.DrawRectangle(0, 0, self.width, self.height)
        # gcdc.DrawBitmap(bitmap, 0, 0)

        region1 = wx.Region(new_bitmap)
        region2 = wx.Region(bitmap, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.Destroy()
        del gcdc

        dc.SelectObject(test_bitmap)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)
        gcdc.SetDeviceClippingRegion(region1)
        gc.DrawBitmap(bitmap, 0, 0, self.width, self.height)
        gcdc.DestroyClippingRegion()

        dc.SelectObject(wx.NullBitmap)
        gcdc.Destroy()
        del gcdc

        dc.Destroy()
        del dc
        
        self.bitmap = bitmap


for i, (png, frame_control) in enumerate(splash_png.frames):
    frames += [AnimationFrame(i, png, frame_control)]


class Frame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(
            self,
            None,
            -1,
            style=(
                wx.FRAME_SHAPED |
                wx.NO_BORDER |
                wx.FRAME_NO_TASKBAR |
                wx.STAY_ON_TOP
            ),
            size=(800, 400),
            pos=(400, 400)
        )

        base_frame = frames[0]

        base_bitmap = wx.Bitmap(base_frame.bitmap.GetWidth(), base_frame.bitmap.GetHeight())
        second_frame = frames[1]

        dc = wx.MemoryDC()
        dc.SelectObject(base_bitmap)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)
        gc.DrawBitmap(base_frame.bitmap, 0, 0, base_frame.width, base_frame.height)
        gcdc.SetPen(wx.TRANSPARENT_PEN)
        gcdc.SetBrush(wx.Brush(wx.Colour(254, 255, 252, 255)))
        gcdc.DrawRectangle(0, 0, second_frame.width, second_frame.height)

        dc.SelectObject(wx.NullBitmap)
        gcdc.Destroy()
        del gcdc

        dc.Destroy()
        del dc
        self.alpha_bitmap = base_bitmap
        self.bmp = base_bitmap

        self._thread = None
        self.frame = 1
        self.close_event = threading.Event()
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def run(self):
        while not self.close_event.is_set():
            frame = frames[self.frame]
            self.draw()
            self.close_event.wait(frame.delay)

    def start(self):
        self._thread = threading.Thread(target=self.run)
        self._thread.daemon = True
        self._thread.start()

    def OnPaint(self, evt):
        bitmap = wx.Bitmap(*self.GetClientSize())
        dc = wx.MemoryDC()
        dc.SelectObject(bitmap)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)
        gcdc.DrawBitmap(self.bmp, 0, 0)

        gcdc.Destroy()
        del gcdc
        dc.Destroy()
        del dc

        self.draw_alpha(bitmap)

    def draw(self):
        frame = frames[self.frame]

        alpha_bitmap = wx.Bitmap(self.alpha_bitmap.GetWidth(), self.alpha_bitmap.GetHeight())
        dc = wx.MemoryDC()
        dc.SelectObject(alpha_bitmap)
        gcdc = wx.GCDC(dc)
        gcdc.DrawBitmap(self.alpha_bitmap, 0, 0)
        gcdc.DrawBitmap(frame.bitmap, frame.x_offset, frame.y_offset)
        dc.SelectObject(wx.NullBitmap)

        gcdc.Destroy()
        del gcdc

        if self.frame + 1 == len(frames):
            self.frame = 1
        else:
            self.frame += 1

        self.bmp = alpha_bitmap
        self.Refresh()
        self.Update()

    def draw_alpha(self, bmp):
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )



frme = Frame()
frme.Show()
frme.start()
app.MainLoop()

@kdschlosser
Copy link
Contributor Author

kdschlosser commented Mar 17, 2020

@Metallicow

Here is the APNG support you have requested. You should be able to modify this script to work with any apng file.

I embedded the apng file above into the script.

I did not do a whole mess of performance tweaks and there are things that could use to be cleaned up and changed. It does provide full apng animation support. It works without any anomalies in the rendering.

apng_script.zip

@Metallicow
Copy link
Contributor

Had a few min to look over your code. Many strange things occured but I managed to code yet another hmmmOpps() function into SourceCoder. I can manage to get a screenie this time before hard crashing.

Just to note, I dont use the apng lib, I have used the official apngasm/dis from the kickstarter onwards and other image libs to handle most of what I do with them.

1st weird thing. This is just plain stupid. Probably an issue with apng lib itself.

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\testuser\Desktop>tmprun.py
You need to install the apng library in order to use this script.
pip install apng

wtf...
Ok... reading the file your code. checked location.... looks ok but could be better...
Let's do a runitanyway in SourceCoder...
wow it actually worked... sorta until crash...

  1. probably a problem with your thread logic or whatever weirdness apng lib cant import

apngchokes

...apparently both your examples start this way. The apng starts running at the speed of light but over time it slows down until it finally crashes.

@kdschlosser
Copy link
Contributor Author

how long does it take until the crash happens?

I am not using the any of the events in wx to handle the drawing operations like EVT_PAINT. so there is nothing that is changing hands to other thenreads other then the main thread passing off to a worker thread and that worker thread is what is handling the drawing. so there shouldn't be anything that "starts off at the speed of light and slows down".

I have had the animation running for hours and not had it crash. I will start it up again and let it run and see what happens. I will also have it log how long each iteration of the animation it takes.

the apng package does not use any external software to extract the frames inside of an animation. this is the reason why I used it. I had started to write this ability myself. but once I discovered the apng library and it was done fairly close to what I would have done I decided not to.

@kdschlosser
Copy link
Contributor Author

In your test did you run the script exactly how it is right now?
or did you modify it in some way?

@Metallicow
Copy link
Contributor

Nope. no mod. I'd lay a guess if I run it in x64 just the crash might occur faster.
...maybe how the apng lib is loading and sending info... idunno
It seems like the thread is outrunning the draw function, but cant be sure.
It takes like 4-8 sec to crash, depending on if it was precompiled before running.

@kdschlosser
Copy link
Contributor Author

OK so I did run the test and after 27 iterations I did produce the error you pointed out.

That Error has got to be a bug in wxWidgets. The same operation is being performed over and over again and does not have an issue for 27 times then all of a sudden out of the blue it is saying that I have not selected a bitmap before I am creating a GCDC..

and the thread is running at the same pace or really close to the same pace.

4.180239200592041
3.711212396621704
3.713212251663208
3.713212490081787
3.712212324142456
3.713212251663208
3.712212562561035
3.713212490081787
3.728212833404541
4.181239366531372
3.9932281970977783
3.713212490081787
3.712212562561035
3.71421217918396
3.713212490081787
3.712212324142456
3.727213144302368
3.714212417602539
3.727213144302368
3.713212251663208
3.7282135486602783
3.72821307182312
3.822218656539917
3.714212417602539
3.7582149505615234
3.728213310241699
3.729213237762451

I would need to compile a debugging version of wx and also python to be able to track down the issue.

@kdschlosser
Copy link
Contributor Author

OK so this is the portion of the wxWidgets code where the problem is stemming from in wxWidgets.

wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxMemoryDC& dc)
{
    ENSURE_LOADED_OR_RETURN(NULL);
#if wxUSE_WXDIB
    // It seems that GDI+ sets invalid values for alpha channel when used with
    // a compatible bitmap (DDB). So we need to convert the currently selected
    // bitmap to a DIB before using it with any GDI+ functions to ensure that
    // we get the correct alpha channel values in it at the end.

    wxBitmap bmp = dc.GetSelectedBitmap();
    wxASSERT_MSG( bmp.IsOk(), "Should select a bitmap before creating wxGCDC" );

I am in fact selecting the bitmap before the call to construct the GCDC. It turns out that the bitmap for some reason is not returning True from IsOk(). I need to figure out why.

I did however discover there is a missing method in wxPython. there is no method MemoryDC.GetSelectedBitmap. I would love to be able to make a comparison between the one that MemoryDC has selected and the one I have created.

I still need to do some more testing and find out why the bitmap is not ok. and this seems to be happening always on the 27th loop of the animation on the 24th frame of that loop. That is remaining a constant.

@Metallicow
Copy link
Contributor

We have this little python game we play sometimes...
Actually i kinda invented it. I like to call it pyTink but phonetic for "think". verbal vs interpreter
Well, there is a piece of hardware that needed to be retired. I didn't want to just destroy it like all the rest, so I made it into a bork machine....
When we had game night, most everyone there knew at least basics of python.
I then installed python on it and said guess what!?! nor more computers vs games tonight.
Anyhow we managed to make up some [if, elif, exec, etc] cards all python based....
The rule of the game was simple: defeat your opponents and be last man standing.
Card deck on table with obviously "dangeous" ways to play them in it.
Each player is givin 1-3 variables to input into the interpreter to play the game. No other players know what you put into it LOL.
Then let the cards fly and have the python god to be doomed sort out who is winner.
Interpreter is of course the game rules so its answer is final..
After many iterations one night we had me vs linuxguru as last surviving players that night.
I had 1 card left and he had all 20. Obviously I had no chance in my mind...
so I just flopped next top card over on table and it was CHEESE!
that's like a wild card with another card played. I just so happened to have the "1-liner" card.
so after like 10 min of arguing who would win, we input it into interpreter. 1 card was obviously if True: run()
It did some weird things and has never worked the same since.
I still can't stop rolling on the floor laughing everytime I ask him a question nowadays.
replies to anything, I ask as "Is that TRUE?"
...somehow I got a extra long exception named after me in his codebase hahaha. it violates all pep8 rules

@kdschlosser
Copy link
Contributor Author

I found the problem.

There is a memory leak. It is not in wxWidgets or wxPython bit it was in the script code. I was not cleaning up properly. I needed to release and delete some of the windows handles. But also I needed to call wxBitmap.Destroy() and delete the python object.

This solved the memory leak problem.

The error that is produced by wxWidgets is misleading. Because wxWidgets uses wxBitmap.IsOk to determine if the memory dc has selected a bitmap the error gets created if IsOk returns False. The issue there is that there is more then one reason why IsOk would return False. One of them being insufficient memory.

I have modified the script to fix the memory leak, and I also modified it to provide a proper error if there is no enough memory to render the animation.

apng_script.zip

@kdschlosser
Copy link
Contributor Author

But that should solve the issue with the crash. I was unable to locate an issue with the speeding up problem you encountered. One of the things you have to remember is when dealing with any graphics animations any large rendering that takes place by any application is going to cause a slowdown. even if the rendering is being performed by a completely separate process. I think there is only a single thread/process that actually handles drawing to the screen. so anything that is to be drawn has to "wait in line". This would cause a slowdown. I have not looked into the inner working of the Windows GDI so I am not able to tell you if that really is the case. But it does make sense.

The animation should have a total of about 3.7 seconds for a runtime. I would have to go and check and see what each frames delay time is and add them up to see how accurate the script is actually running.

@kdschlosser
Copy link
Contributor Author

I just checked the delays.
each frame has a delay of 0.016666666666666666 seconds.
and all frames added up ends up being 1.9833333333333312 seconds.
so my time of 3.7ish is not correct.

I am going to have to add in a high precision timer to time how long the drawing takes and subtract that from the delay. and if there is anything left over then wait that time. and if not then continue on to render the next frame.

I will add that now. Tho the speed looks good how it is I still want it to be proper!!

@kdschlosser
Copy link
Contributor Author

OK so I have gotten the animation time to be better then it was.

I am down to 2.264129400253296 seconds total animation run time.
I am not able to get it to be exact with what is in the apng. This is due to how long is takes wxPython to do it's rendering operations and also how ,ong it takes for the windows api call to do it's thing.

264ms off over 118 frame is not horrible. If I used Cython and converted the script into c code and compiled it into a python extension I am sure we could get the times to be perfect. I think that a 264ms error over 118 frames is not awful considering the speed at which each frame should be rendered.

apng_script.zip

@kdschlosser
Copy link
Contributor Author

It was taking a HUGE amount of time to crash on my machine because I am running 64gb of ram. when I tested it after you reported the problem I had a project that was open in my IDE that is massive at close to a million lines of code. So my IDE had a grip of RAM all gobbled up. so there was far less available. When I had the animation running for a really long time to test it I didn't have much open. so most of the 64gb was available. and at 20meg or so being consumed each loop it would have taken a really long time to run out.

@kdschlosser
Copy link
Contributor Author

here is a newer version that fixes an issue where the apng will not render properly if frame 0 has an alpha channel
apng_script.zip

@Metallicow
Copy link
Contributor

Adding wx.MilliSleep(8) to the end of the update animation method fixes the draw rate.
Github garbles the filename when attaching files. The apng should be 120frames a second. Gifs have problems running at full speed in all browsers.
The last script you uploaded seems to run fine by adding the millisleep line.
I have other versions that have fewer frames but that one is 120 a sec.

@Metallicow
Copy link
Contributor

I recall that I had an animation where I had my snakey icon bonce around a screen, and had a few different ways of doing it. Regular timer in wxPython would only render at like 15 on MSW. this is like a limitation. But using pygame and thread got it to run faster on windows. Tho I scrapped my idea when I started figuring out how bad crashes was when using pygame embedded in wxPy. Tho on linux the icon bounced around at the speed of light. But yea, I recall I used a thread to bypass the frame draw limitation.

@Metallicow
Copy link
Contributor

https://commons.wikimedia.org/wiki/File:Animated_PNG_example_bouncing_beach_ball.png
This one is still showing as black background.
self.apng = apng.APNG.open(file=r'Animated_PNG_example_bouncing_beach_ball.png')
the calculation for the delay I think needs more work, but at least the page tells how many frames and a delay.
The python apng has a background on the first frame, the bouncing ball doesn't. maybe that has something to do with it...

@Metallicow
Copy link
Contributor

hmmm I think I might restart over from the one that was working fine and incorporate the latter stuff in bit by bit. If you got the unanimated one to work with alpha, then somehow something broke inbetween.
@kdschlosser This is how you would load the png from data, the "supposedly" not lazy way.

import io
img = wx.Image(io.BytesIO(png.to_bytes()), wx.BITMAP_TYPE_PNG)

@kdschlosser
Copy link
Contributor Author

I have the black background issue fixes.

I am not sure where you are coming up with the 120 frames a second. You have each frame set to a 16 millisecond render time.

16 * 120 = 1920 = 1.92 seconds

If you add any additional delays or waits you are going to slow the rendering time down even further. Right now it is getting as close as it can to the 16 milliseconds per frame as it can. the rendering time is longer then 16 milliseconds. it takes on the order of 17 milliseconds to render each frame.

The only thing I do not like about wx.Millisleep is the inability to exit the sleep. the program has to wait until the sleep is finished. This is not a good thing when dealing with threads. If a thread needs to be shut down it usually has to be done at the snap of the fingers. This is the same reason why you do not want to use time.sleep() in a thread. using threading.Event.wait() you have the ability to exit the wait from another thread if needed by calling threading.Event.set(). The same threading.Event instance can also be used to keep a loop going inside of the thread. so if you want to have the thread exit you can do so with 2 commands. one to set the event, and the second to join the thread so your program will not continue until the thread has shutdown. This is the way to do it in order to not have PyDeadObject tracebacks take place when closing an application GUI.

Here is an updated version of the script. I fixes the issue with the background not having an alpha channel. It also removes the writing of the temporary files.

You can run the script from a shell and supply a command line argument which would be a path to an apng file you want to load instead of the embedded one.

I changed the embedded apng to the bouncy ball.
apng_script.zip

@kdschlosser
Copy link
Contributor Author

Oh and the calculation for the delay is exactly to the apng specification that is written here,

The delay_num and delay_den parameters together specify a fraction indicating the time to display the current frame, in seconds. If the denominator is 0, it is to be treated as if it were 100 (that is, delay_num then specifies 1/100ths of a second). If the the value of the numerator is 0 the decoder should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound.

Frame timings should be independent of the time required for decoding and display of each frame, so that animations will run at the same rate regardless of the performance of the decoder implementation.

I have done this to the best of my ability. Because of how long it takes to render the bmp to the screen (something I do not have control over) I time how long that rendering process takes. I subtract that time from the delay that is provided in the apng frame. if the remaining value is > 0 then I have the program wait for that remainder. This is the best I am able to do in order to get the delays as close as possible. I am handling as much of the rendering of the frame as possible ahead of time so the only thing that ends up needing to be done is setting up a region that defines the alpha and copying the buffered frame to a new bmp and that new bmp is what gets drawn to the screen.

I might be able to squeeze it for better performance by reusing some of the windows structures and possible setting it up so that I do not have to keep on creating and destroying windows handles.

@Metallicow
Copy link
Contributor

@kdschlosser I ripped everything out that wasn't needed with apng and started over from the sample that worked on wxPy4.0. I think this should work. Please test. Bouncy ball apng works now with alpha.

testapng.py - Click to expand sample

sample apng image used with alpha

Animated_PNG_example_bouncing_beach_ball.png
Animated_PNG_example_bouncing_beach_ball

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Shaped Frame apng sample with alpha"""

# Imports.--------------------------------------------------------------------
# -Python Imports.
import os
import sys
import threading
import ctypes
from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

# -wxPython Imports.
import wx

# -apng Imports.
try:
    import apng
except ImportError:
    print('You need to install the apng library in order to use this script.')
    print('pip install apng')
    sys.exit(1)

# -Platform check.
if not sys.platform.startswith('win'):
    print('This script will only run on Microsoft Windows.')
    sys.exit(1)

wxVER = 'wxPython %s' % wx.version()
pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
versionInfos = '%s %s' % (wxVER, pyVER)

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# );
GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

# int ReleaseDC(
#   HWND hWnd,
#   HDC  hDC
# );
ReleaseDC = user32.ReleaseDC
ReleaseDC.restype = INT

# BOOL DeleteDC(
#   HDC hdc
# );
DeleteDC = gdi32.DeleteDC
DeleteDC.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


# This class sets up the objects needed to be
# used in a better fashion then the apng library has.
# it keeps the code quite a bit cleaner.
class AnimationFrame(object):

    def __init__(self, index, png, frame_control):
        self.index = index
        self.png = png
        self.width = frame_control.width
        self.height = frame_control.height
        self.x_offset = frame_control.x_offset
        self.y_offset = frame_control.y_offset

        self.depose_op = frame_control.depose_op
        self.blend_op = frame_control.blend_op

        self.delay = frame_control.delay
        self.delay_den = frame_control.delay_den

        self.bitmap = wx.Bitmap.NewFromPNGData(data=png.to_bytes(), size=-1)

        if not self.bitmap.IsOk():
            raise MemoryError('Insufficent memory to render animation.')


class TimerThread(threading.Thread):
    """TimerThread to regulate animation."""
    def __init__(self, event, windo):
        """Default class constructor."""
        threading.Thread.__init__(self)

        self.windo = windo
        self.close_event = event

    def run(self):
        """Run the TimerThread."""
        windo = self.windo
        frames = windo.frames

        while not self.close_event.is_set():
            try:
                windo.Refresh()
            except RuntimeError:  # Crash on closing.
                break

            frame = frames[windo.current_frame]
            delay = frame.delay

            wx.MilliSleep(delay)

            if windo.current_frame + 1 == len(windo.frames):
                windo.current_frame = 0
            else:
                windo.current_frame += 1

    def stop(self):
        """Stop the TimerThread."""
        self.close_event.set()

    def stopped(self):
        """Is the TimerThread stopped?"""
        return self.close_event.isSet()


class Frame(wx.Frame):

    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.FRAME_SHAPED |
                       wx.NO_BORDER |
                       wx.FRAME_NO_TASKBAR |
                       wx.STAY_ON_TOP
                 , name='frame'):
        wx.Frame.__init__(self, parent, id, title, pos, size, style, name)

        self.frames = []

        # self.apng = apng.APNG.from_bytes(base64.b64decode(APNG))
        self.apng = apng.APNG.open(file='Animated_PNG_example_bouncing_beach_ball.png')

        self.bitmaps = []
        self.current_frame = 0
        self._thread = None

        # creating the helper class instances
        for i, (png, frame_control) in enumerate(self.apng.frames):
            frame = AnimationFrame(i, png, frame_control)
            self.frames += [frame]

        # the base frame or "preview png" is always going to be frame 0.
        # this is designed so that an apng can be loaded as a normal png
        # in the event that animation is not supported.
        # This is also where the animation starts and is going to be
        # what is used to set up the animation width and height.
        base_frame = self.frames[0]
        self.base_frame = self.frames[0]

        self.SetSize((base_frame.width, base_frame.height))

        self.delta = (0, 0)
        self.BindEvents()

    def BindEvents(self):
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def StartThread(self):
        close_event = threading.Event()
        self._thread = TimerThread(close_event, self)
        self._thread.daemon = True
        self._thread.start()

    def OnExit(self, event):
        self._thread.stop()
        wx.CallAfter(self.Destroy)
        # self.Close()

    def OnLeftDown(self, event):
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = ((dx, dy))

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            self.Move(fp)

    def OnPaint(self, event):
        width, height = self.GetClientSize()

        bmp = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)

        gcdc.DrawBitmap(self.frames[self.current_frame].bitmap, x=0, y=0, useMask=True)

        # Draw frame rect with transparent brush for sanity check.
        gcdc.SetPen(wx.BLACK_PEN)
        gcdc.SetBrush(wx.TRANSPARENT_BRUSH)
        gcdc.DrawRectangle(0, 0, width, height)

        gcdc.Destroy()
        del gcdc

        dc.SelectObject(wx.NullBitmap)
        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)

        self.draw_alpha(bmp)

    def draw_alpha(self, bmp):
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )

        # cleanup to avoid a memory leak.

        DeleteDC(HDC(hdcSrc))
        ReleaseDC(HWND(hndl), HDC(hdcDst))

        ##bmp.Destroy()
        ##del bmp

if __name__ == '__main__':
    print(versionInfos)
    app = wx.App()
    frame = Frame(None)
    frame.Centre()
    frame.Show()
    frame.StartThread()
    app.MainLoop()

@Metallicow
Copy link
Contributor

Metallicow commented Mar 19, 2020

The python apng has 120 frames and should be 1/60 second so 1000/120 = 8 milliseconds is how i calculated it. so in order for it to look proper one rotation should take 1 second

@Metallicow
Copy link
Contributor

The animation speed is sporadic on your last example and speeds up and slows down, but yea you managed to get the alpha to look right.

@kdschlosser
Copy link
Contributor Author

OK so I have trimmed the fat off of the Windows API function calls. When I use the apng you made above the rendering time is down to 6.89748699999998 milliseconds. So now the program is able to properly render the frames at the 16 millisecond speed that is enbedded into the frames.

apng_script.zip

@kdschlosser
Copy link
Contributor Author

The delay the browser is using is the exact same delay that I am using. The problem was stemming from the rendering time being greater then the delay. This would make the animation slower. I reorked the Windows API calls so they reuse portions of it between frame renders instead of creating each piece new every frame. This has reduced the overhead quite a bit.

I checked the apng file you attached with an apng disassembler and it too states that you have a 16ms delay set for each frame. So I know my calculations are correct. The specification states that the rendering portions of the code should not affect the speed in which the apng can be displayed. I have done as much rendering as possible before the animation starts. I wish there was a way I could remove having to copy the bmp so we can draw it on the screen. But there is no way to achieve that with the Windows calls.

If I set this up to not use the alpha shaped frame. and set it up to be a panel instead I could simply write the data to a ClientDC instead and I would not have to have the additional overhead caused by the copying of the bmp.

I am not sure how much that would speed things up I do not believe it would be all that much.

I would love to extend the wx.Bitmap class so it can handle APNG files. There would need to be a whole lot of Monkey patching taking place in order to handle the drawing of each frame. for each of the various widgets that you can use a wx.Bitmap with. I am fairly certain that the bitmaps are not "drawn" by wxWidgets but instead has the data copied out of it and placed into a Windows bitmap buffer and that buffer gets passed to a Windows API call to be drawn. Because of the nature of that process there are not going to be repeated calls unless there is a need to refresh the information on that portion of the screen. So once it is drawn it does not get redrawn at some kind of an interval.

So those redrawing mechanics would need to be added to each of the available widgets. The question is how is the data contained within the wx.Bitmap retrieved when the bitmap is to be drawn on the screen. If I knew that bit of information I would be able to refresh and update which would trigger a collection of data to occur from the wx.Bitmap instance. at that point I would be able to return the proper frame.

All of this could be handled internally to the bitmap if the bitmap had a "parent" that would be the widget. I do not know if this exists. I do not think that it does.

I am going to poke about the wx.Bitmap class and see if I am able to isolate the mechanism that gets used to get the data,

@Metallicow
Copy link
Contributor

Metallicow commented Mar 19, 2020

Ok. checked out your sample and made afew modifications to fix the sporadic timing issues im getting... The reflection on the ball is still incorrect tho. still not semi-transparent.

  1. zero speed just runs as fast as it can render so it is actually valid, so changed > to >=. placed sanity check in print statement. if it wasn't in a thread it is throttled/limited to 15ms how wxPython/Widgets deals with windows, so the way you got it bypasses that just fine in a GUI without causing any issues.
    def run(self):
        while not self.close_event.is_set():
            start = perf_counter()
            self._update_animation()
            frame = self.frames[self.current_frame]
            stop = perf_counter()
            delay = frame.delay - (stop - start)

            print('debug %d' % delay)
            if delay >= 0:
                print('delay > 0: %d' % delay)
                self.close_event.wait(delay)
                # wx.MilliSleep(delay)

            if self.current_frame + 1 == len(self.frames):
                self.current_frame = 0
            else:
                self.current_frame += 1
  1. In order to get it to work right with the thread timer the delay_den should be wrapped as a float in order to accomodate python 2 otherwise it always ends up as 0.

  2. change your import to be Py2/3 phoenix friendly for perf_counter

try:
    from time import perf_counter # NOQA
except ImportError:
    from time import clock as perf_counter # NOQA

Also tested with this also. The antialias on the edges look grainy and flattened. no semi transparency.
https://commons.wikimedia.org/wiki/File:APNG_throbber.png
APNG_throbber
If you can get the edges to look proper on the 2 with the transparency, then I think everything else looks fine with the python powered one with this iteration.

Edit also I think the perf_counter probably isnt needed anyhow.... cant see any reason why to do the extra calc...

@Metallicow
Copy link
Contributor

with the float fix, this would be the optimized run method.

    def run(self):
        frames = self.frames
        len_frames = len(self.frames)
        _update_animation = self._update_animation
        close_event = self.close_event
        while not close_event.is_set():
            current_frame = self.current_frame
            _update_animation()
            close_event.wait(frames[current_frame].delay)

            if current_frame + 1 == len_frames:
                self.current_frame = 0
            else:
                self.current_frame += 1

@Metallicow
Copy link
Contributor

Metallicow commented Mar 20, 2020

creating a value or func/meth to tell if all the values are the same would be useful also...
how useful would depend on the users use of the value, if even used at all.
for example any apng with all the same delays could be scaled as anything to look quote "right" to the user, tho an amination that takes a minute might be scaled elsewise and look ugly....

@kdschlosser
Copy link
Contributor Author

wxPython 4.x only runs on Python 3.x so adding the float() is moot and does not need to be done.

That sanity check of >= does not need to be done. It is an additional function call that gets done if delay == 0 and that call does not need to be made. That call does take time to process. so why even do it when it is not necessary?? All it does is adds additional time to process thus slowing down the animation. Now it may not seem like much but if you keep on doing that over and over again it will accumulate.

I can also add in a "frame skip" if needed. it would do this when the returned value of running_time % animation_loop_time is >= a frame's delay time in the animation. The skipping of the frame only means that it is not going to get drawn to the screen. The only frame that cannot get skipped would be frame 0.

This would keep the animation run time correct. I use a similiar process when I am rendering my security cameras using wxPython. The other option we can add is turning off anti-aliasing when rendering. This should also speed up the rendering.

I have been writing an apng addon for wxPython I am removing the use of the apng library and adding in the full specification for apng into it. I am also breaking down the png specification so a user would be able to set the metadata for each frame if they wanted to.

It is also going to handle sequencing of the frames, this is something the apng library does not have the ability to do. The specification does not mandate that the frames be coded in any specific order.

The delay is not something that should be "scaled" the delay would always remain a constant. Right now we are only rendering the apng in the exact size it is made in. This is going to change as well. we will scale the size of the apng when a size event occurs. so the smaller an apng is the faster it is going to render and the delay adjustment code is going to handle that change in rendering speed as needed. the displayed size coupled with the users computer speed is what is going to determine exactly how fast we re able to render each frame. because both of those factors are ?'s the only thing we can do in terms of producing a correct animation speed is what I have done above, other then adding in a frame skip. The frame skip is something that we can add in as an option that can be turned on or off. The other thing we can add is an event that will signal the application that there is an overrun occurring this way the application then can make a decision as to whether or not it wants to resize the apng or to turn on frame skipping. In the event object we can add in the amount of overrun that is occurring.

I am writing this thing to also allow a user to create a new animation. a frame in an animation can be any file type that wx Image is able to convert into a wxBitmap. the apng would also be able to be saved to a file or a file like object.

The png and apng specification is really not all that complex. Most of the png specific bits are handled by wxPython/wxWidgets all that has to be done is we have to separate the IDAT chunk from a PNG and place it into an fdAT chunk inside the apng. there needs to also be an fcTL chunk added for each frame which provides width, height, x offset, y offset, blend op, delay and dispose op data that is needed to know how to render each frame.

I will create a repo for the apng library and you can help out with it if you like. Probably best to move the conversation to there anyway. This topic is way off track. I will probably close the issue and possibly reopen it. I have some code examples of what can be done to band-aide the problems that I will post in the new issue.

In the next day or so I will open the repo. I will post the link to it in here. then we can continue the conversation there. Even tho the apng specification is almost dead it is a great way to go about providing animation support for any image type that wxPython supports. This is because it is a really simple specification. Adding support for other animation types can be derived from this model.

One thing I have not checked is how an animation gets loaded using wx.Image. I know that wx.Image supports multiple "layers" or images in a single object. Does it load each frame if i load an apng?? gonna have to try it.

@Metallicow
Copy link
Contributor

Metallicow commented Mar 22, 2020

Phoenix has ran on Py2 and 3 since it was launched... I ported the whole demo... Im running all your code on Py 2 too lol. The sample on Py2 is ending up doing integer division is more specific with the float part.
Edit: when you are calculating this in the animation class, this will always do true division on Py2/3.
Then when run in thread.wait() it will get a float instead of 0 or whatever
self.delay = self.delay_num / float(self.delay_den)

@kdschlosser
Copy link
Contributor Author

I didn't say Phoenix. I said wxPython 4.x
I didn't think that wxPython 3.x was being developed anymore.

either or it is not a big problem to add the float().

I did however run into a road block. the components needed from wxWigets to be able to add custom animation decoders have not been added to wxPython. So I am not able to do what I wanted to without having to write a new control. The reason why I wanted to add a new decoder was because the rendering of the animation to the screen was handled in c code and it would be faster. a good comparison is racing a top fuel drag car (c code) against a pinto (python). Plus everything is already in place to handle sizing events and the running of the animation. It would simply be the best and easiest way to go about it.

@Metallicow
Copy link
Contributor

wxPy 4 is officially Phoenix launch. If it doesnt say phoenix when you do wx.version, then you are using classic. There was some versions in wxPy 3 that worked but they was still alpha/beta testing. If it isnt on PyPI then it is probably classic.

@kdschlosser
Copy link
Contributor Author

well I'll be damned. last time I had checked (don't know when that was) Phoenix was not available for Python 2.x I just checked and it is there now....

So my bad on mentioning 3.x I have them all mixed up. the 3.x is because last I knew 3.x was the latest release that was available for Python 2.x That is no longer the case.

so the float does in fact need to be there you are correct.

@kdschlosser
Copy link
Contributor Author

OK so here is the repository that adds APNG support to wxPython. It is not tested and is a WIP.

https://github.com/kdschlosser/wxAnimation

What I am doing is extending wx.adv.AnimationCtrl and wx.adv.Animation and adding a python version of wxAnimationDecoder. I have also added the methods for AnimationCtrl and Animation that have not been included in wxPython. I am setting it up so that the original classes will get used if an animation is an ANI or GIF. otherwise it will use the pure python version of it.

There is actually a problem with the original C code, It uses wx.Timer which cannot be run using a timeout value of 0. I would have to check but I believe that wxTimer has resolution down to 1 millisecond. So all frames will have at least a 1 millisecond delay. I am going to alter the code so it will do a proper update without having to have a delay of 1ms for a frame that may have a delay of 0.

I am also going to add in code to correct the rendering time vs delay time.

@Metallicow
Copy link
Contributor

iirc wx.Timer was the one restricted to like 15ms on windows. MilliSleep or Thread should work around the limitation tho. Might ask Robin again but I recall that was the case when I ran into it the first time.

@kdschlosser
Copy link
Contributor Author

I like the approach oif using threads anyhow. because it will not tie up the main thread. I have corrected some of the initial problems with the code I posted. I have to figure out why it is not rendering correctly. and I also have to add in support for transparency. as it seems from looking at the code that the original animation code does not support the rendering of alpha.

@Metallicow
Copy link
Contributor

It appears that pillow finally is getting apng support/imagegrab pushed with 7.1.0.
python-pillow/Pillow#4354 (comment)
Might be something to look into when it gets released. Tho pillow 7.1.0 won't support python 2 so it you are looking for that then might have to backport or use alternate apng lib or wxAnimationDecoder stuff when it matures.

@Metallicow
Copy link
Contributor

@kdschlosser

I managed to get this to work with the alpha on wxPy 4.1 by doing this. I didnt have the BLACK problem.
... now to figure out how to get rid of the PIL dependency and do it straight in wxPython to create a proper alpha image.

        ##from PIL import Image
        pil_im = Image.new(mode='RGBA', size=(width, height), color=(0, 0, 0, 0))
        bmp = wx.Bitmap.FromBufferRGBA(width, height, data=pil_im.tobytes())

tested on
wxPython 4.1.1a1.dev4883+75f1081f msw (phoenix) wxWidgets 3.1.4 Scintilla 3.7.2
Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:37:02) [MSC v.1924 64 bit (AMD64)] on win32

Collapsible Content - Click to expand
#!/usr/bin/env python
# -*- coding: utf-8 -*-

""""""

# Imports.--------------------------------------------------------------------
# -Python Imports.
import os
import sys

if not sys.platform.startswith('win'):
    print('This script will only run on Microsoft Windows.')
    sys.exit(1)

import ctypes
from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

# -wxPython Imports.
import wx

# -Pillow Imports.
from PIL import Image

wxVER = 'wxPython %s' % wx.version()
pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
versionInfos = '%s %s' % (wxVER, pyVER)

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# );
GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


class Frame(wx.Frame):

    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.FRAME_SHAPED |
                       wx.NO_BORDER |
                       wx.FRAME_NO_TASKBAR |
                       wx.STAY_ON_TOP
                 , name='frame'):
        wx.Frame.__init__(self, parent, id, title, pos, size, style, name)

        self.delta = (0, 0)

        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnExit(self, event):
        self.Close()

    def OnLeftDown(self, event):
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = ((dx, dy))

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            self.Move(fp)
            # self.Refresh()

    def OnPaint(self, event):
        width, height = self.GetClientSize()

        text = 'Alpha test'
        font = self.GetFont()
        font.SetPointSize(font.GetPointSize() + 16)
        font = font.MakeBold()
        font = font.MakeItalic()

        ##from PIL import Image
        pil_im = Image.new(mode='RGBA', size=(width, height), color=(0, 0, 0, 0))
        bmp = wx.Bitmap.FromBufferRGBA(width, height, data=pil_im.tobytes())
        bmp2 = wx.Bitmap.FromBufferRGBA(width, height, data=pil_im.tobytes())
        bmp3 = wx.Bitmap.FromBufferRGBA(width, height, data=pil_im.tobytes())

        # bmp = wx.Bitmap(width, height)
        # bmp2 = wx.Bitmap(width, height)
        # bmp3 = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp3)
        dc.SetTextForeground(wx.Colour(255, 255, 255, 255))
        dc.SetFont(font)

        text_width, text_height = dc.GetFullTextExtent(text)[:2]

        text_x = (width / 2) - (text_width / 2)
        text_y = (height / 2) - (text_height / 2)

        dc.DrawText(text, text_x, text_y)

        dc.SelectObject(bmp2)

        dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 255), 6))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 255)))
        dc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp2, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)

        gcdc.SetDeviceClippingRegion(region1)
        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 140), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        gc.DrawRoundedRectangle(3, 3, width-6, height-6, 5)

        gcdc.DestroyClippingRegion()

        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 120), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 0)))
        gc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp3, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.SetDeviceClippingRegion(region1)
        gcdc.SetTextForeground(wx.Colour(0, 0, 0, 130))
        gcdc.SetTextBackground(wx.TransparentColour)
        gcdc.SetFont(font)
        gc.DrawText(text, text_x - 4, text_y - 4)
        gcdc.DestroyClippingRegion()

        gcdc.SetTextForeground(wx.Colour(0, 255, 0, 150))
        gcdc.SetTextBackground(wx.TransparentColour)
        gc.DrawText(text, text_x, text_y)

        brushbmp = wx.Bitmap('brushwithalpha.png', wx.BITMAP_TYPE_PNG)
        gcdc.DrawBitmap(brushbmp, x=200, y=10, useMask=False)

        gcdc.Destroy()
        del gcdc

        # dc.SelectObject(wx.NullBitmap)
        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)

        print('OnPaint')
        bmp.SetMaskColour(wx.TransparentColour)
        self.draw_alpha(bmp)

    def draw_alpha(self, bmp):
        print('draw_alpha')
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )

if __name__ == '__main__':
    print(versionInfos)
    app = wx.App()
    frame = Frame(None, size=(600, 300), pos=(400, 400))
    frame.Show()
    app.MainLoop()

@kdschlosser
Copy link
Contributor Author

@Metallicow
Embed any images into an example if you could please 😄
If you run the script below and enter the filename of the image it will print out a base64 encoded image.

# Python 2.7 and 3.5+ supported

from __future__ import print_function
import base64

try:
    file_name = raw_input('Enter File Name\n')
except NameError:
    file_name = input('Enter File Name\n')


with open(file_name, 'rb') as f:
    data = f.read()

encoded = base64.b64encode(data)


lines = []
line = "    b'"
for item in encoded:
    try:
        line += chr(item)
    except TypeError:
        line += item

    if len(line) + 1 == 78:
        line += "'"
        lines += [line]
        line = "    b'"

if len(line) > 6:
    line += "'"
    lines += [line]

print('IMAGE = (')
print('\n'.join(lines))
print(')')

copy and paste the output into your example and then add the following lines of code to it.

from io import BytesIO
stream = BytesIO(base64.b64decode(IMAGE))
stream.seek(0)

You should be able to pass the "stream" file object to most classes/functions that deal with handling images.

One other thing I did was I wrote a cross platform high precision timer for Python and this timer has microsecond resolution... We cannot use anything that is built into Python to handle stalling a thread or the program in a traditional manner because these mechanisms are not stable and can return before the wanted time or after it. the one that I wrote will return either on time of 4-5 microseconds after the desired time. But not before it and never 10-12 milliseconds after it. This fixes the inconsistent animation speeds.

I was able to overcome the black by using masks. @RobinD42 extended the Python access to the c functions and methods So now they can be properly overridden. I have not had the opportunity yet to update the code to work with the new changes.

@kdschlosser
Copy link
Contributor Author

One thing I would like to know and maybe @RobinD42 can answer this. Is there a way to draw the frame like what is being done in the examples? To make the frame transparent without having to use a shaped frame? This is because a shaped frame is not able to be created properly using an image that has an alpha channel. If a frame can be drawn like what is being done above but for other OS's we can write an animation handler that will deal with APNG files and then separately write a shaped frame that will handle alpha channels. The shaped frame portion needs to be able to be cross platform.

@Metallicow
Copy link
Contributor

Metallicow commented Jul 19, 2020

@kdschlosser I was having issues with the base64 but I tried this and it worked to get rid of PIL dependancy.
and I just use the PyEmbeddedImage module from SourceCoder with a thumbnailer to quickly embed an image with an import.
Sorry, the image was the same as I posted above, but here is a sample with it embedded. I know... Github mangles the filenames when you attach them... :(

        bmp = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)
        bmp2 = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)
        bmp3 = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)

shapedwindowalphawxpy41

Collapsible Content - Click to expand
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""ShapedWindow with Alpha that works on wxPy4.0 and 4.1"""

# Imports.--------------------------------------------------------------------
# -Python Imports.
import os
import sys

if not sys.platform.startswith('win'):
    print('This script will only run on Microsoft Windows.')
    sys.exit(1)

import ctypes
from ctypes.wintypes import LONG, HWND, INT, HDC, HGDIOBJ, BOOL, DWORD

# -wxPython Imports.
import wx
from wx.lib.embeddedimage import PyEmbeddedImage

wxVER = 'wxPython %s' % wx.version()
pyVER = 'python %d.%d.%d.%s' % sys.version_info[0:4]
versionInfos = '%s %s' % (wxVER, pyVER)

UBYTE = ctypes.c_ubyte
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
ULW_ALPHA = 0x00000002
AC_SRC_OVER = 0x00000000
AC_SRC_ALPHA = 0x00000001

class POINT(ctypes.Structure):
    _fields_ = [
        ('x', LONG),
        ('y', LONG)
    ]

class SIZE(ctypes.Structure):
    _fields_ = [
        ('cx', LONG),
        ('cy', LONG)
    ]

class BLENDFUNCTION(ctypes.Structure):
    _fields_ = [
        ('BlendOp', UBYTE),
        ('BlendFlags', UBYTE),
        ('SourceConstantAlpha', UBYTE),
        ('AlphaFormat', UBYTE)
    ]

user32 = ctypes.windll.User32
gdi32 = ctypes.windll.Gdi32

# LONG GetWindowLongW(
#   HWND hWnd,
#   int  nIndex
# );
GetWindowLongW = user32.GetWindowLongW
GetWindowLongW.restype = LONG

# LONG SetWindowLongW(
#   HWND hWnd,
#   int  nIndex,
#   LONG dwNewLong
# );
SetWindowLongW = user32.SetWindowLongW
SetWindowLongW.restype = LONG

# HDC GetDC(
#   HWND hWnd
# );
GetDC = user32.GetDC
GetDC.restype = HDC

# HWND GetDesktopWindow();
GetDesktopWindow = user32.GetDesktopWindow
GetDesktopWindow.restype = HWND

# HDC CreateCompatibleDC(
#   HDC hdc
# );
CreateCompatibleDC = gdi32.CreateCompatibleDC
CreateCompatibleDC.restype = HDC

# HGDIOBJ SelectObject(
#   HDC     hdc,
#   HGDIOBJ h
# );
SelectObject = gdi32.SelectObject
SelectObject.restype = HGDIOBJ

# BOOL UpdateLayeredWindow(
#   HWND          hWnd,
#   HDC           hdcDst,
#   POINT         *pptDst,
#   SIZE          *psize,
#   HDC           hdcSrc,
#   POINT         *pptSrc,
#   COLORREF      crKey,
#   BLENDFUNCTION *pblend,
#   DWORD         dwFlags
# );
UpdateLayeredWindow = user32.UpdateLayeredWindow
UpdateLayeredWindow.restype = BOOL

COLORREF = DWORD

def RGB(r, g, b):
    return COLORREF(r | (g << 8) | (b << 16))


class Frame(wx.Frame):

    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString,
                 pos=wx.DefaultPosition, size=wx.DefaultSize,
                 style=wx.FRAME_SHAPED |
                       wx.NO_BORDER |
                       wx.FRAME_NO_TASKBAR |
                       wx.STAY_ON_TOP
                 , name='frame'):
        wx.Frame.__init__(self, parent, id, title, pos, size, style, name)

        self.bitmap = brushwithalpha.GetBitmap()

        self.delta = (0, 0)

        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnExit(self, event):
        self.Close()

    def OnLeftDown(self, event):
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = ((dx, dy))

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            self.Move(fp)

    def OnPaint(self, event):
        width, height = self.GetClientSize()

        text = 'Alpha test'
        font = self.GetFont()
        font.SetPointSize(font.GetPointSize() + 16)
        font = font.MakeBold()
        font = font.MakeItalic()

        bmp = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)
        bmp2 = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)
        bmp3 = wx.Bitmap.FromRGBA(width, height, red=0, green=0, blue=0, alpha=0)

        ##BLACK bmp = wx.Bitmap(width, height)
        ##BLACK bmp2 = wx.Bitmap(width, height)
        ##BLACK bmp3 = wx.Bitmap(width, height)

        dc = wx.MemoryDC()

        dc.SelectObject(bmp3)
        dc.SetTextForeground(wx.Colour(255, 255, 255, 255))
        dc.SetFont(font)

        text_width, text_height = dc.GetFullTextExtent(text)[:2]

        text_x = int((width / 2) - (text_width / 2))
        text_y = int((height / 2) - (text_height / 2))

        dc.DrawText(text, text_x, text_y)

        dc.SelectObject(bmp2)

        dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 255), 6))
        dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 255)))
        dc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        dc.SelectObject(bmp)
        gc = wx.GraphicsContext.Create(dc)
        gcdc = wx.GCDC(gc)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp2, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)

        gcdc.SetDeviceClippingRegion(region1)
        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 140), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 150)))
        gc.DrawRoundedRectangle(3, 3, width-6, height-6, 5)

        gcdc.DestroyClippingRegion()

        gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 120), 6))
        gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0, 0)))
        gc.DrawRoundedRectangle(103, 78, width - 200 - 3, height - 150 - 3, 5)

        region1 = wx.Region(bmp)
        region2 = wx.Region(bmp3, wx.Colour(0, 0, 0, 0))
        region1.Subtract(region2)
        gcdc.SetDeviceClippingRegion(region1)
        gcdc.SetTextForeground(wx.Colour(0, 0, 0, 130))
        gcdc.SetTextBackground(wx.TransparentColour)
        gcdc.SetFont(font)
        gc.DrawText(text, text_x - 4, text_y - 4)
        gcdc.DestroyClippingRegion()

        gcdc.SetTextForeground(wx.Colour(0, 255, 0, 150))
        gcdc.SetTextBackground(wx.TransparentColour)
        gc.DrawText(text, text_x, text_y)

        ## brushbmp = wx.Bitmap('brushwithalpha.png', wx.BITMAP_TYPE_PNG)
        brushbmp = self.bitmap
        gcdc.DrawBitmap(brushbmp, x=200, y=10, useMask=False)

        gcdc.Destroy()
        del gcdc

        dc.Destroy()
        del dc

        region = wx.Region(bmp)
        self.SetShape(region)

        self.draw_alpha(bmp)

    def draw_alpha(self, bmp):
        print('draw_alpha')
        hndl = self.GetHandle()
        style = GetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE))
        SetWindowLongW(HWND(hndl), INT(GWL_EXSTYLE), LONG(style | WS_EX_LAYERED))

        hdcDst = GetDC(GetDesktopWindow())
        hdcSrc = CreateCompatibleDC(HDC(hdcDst))

        pptDst = POINT(*self.GetPosition())
        psize = SIZE(*self.GetClientSize())
        pptSrc = POINT(0, 0)
        crKey = RGB(0, 0, 0)

        pblend = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA)

        SelectObject(HDC(hdcSrc), HGDIOBJ(bmp.GetHandle()))
        UpdateLayeredWindow(
            HWND(hndl),
            HDC(hdcDst),
            ctypes.byref(pptDst),
            ctypes.byref(psize),
            HDC(hdcSrc),
            ctypes.byref(pptSrc),
            crKey,
            ctypes.byref(pblend),
            DWORD(ULW_ALPHA)
        )

#----------------------------------------------------------------------
brushwithalpha = PyEmbeddedImage(
    b'iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAF5uklEQVR42uTXha4k2w2F4eVd'
    b'0HBwOMNhZqKXifC+0X2AK6IIw8zMzIzDfKC7anvll6YkjTrMZOmT7bWHwerTPvPppw8ljSgw'
    b'7uMkcsor5vgFzqOgwT3s4RFmOMAOCo7Q4hBztFN2DbsYsYUvYo0fo8dN/ASfx6/RYkQDQ91y'
    b'PtAC5/F87GGJbVzFc3F2yu5hjV/hafwIuYiw/j/qIp7amvevzfS5MfOKrGUpIdtd25QxIiTq'
    b'xPby7qJv77MPrMYMx5ijwng4ZRWJzqS2W3Rokj4M4ylZrSSPtc7oo60hM08xJx5gQIM9/AA3'
    b'MUcHTXmLXWwjcd/2cWaq1myZySKpNkKZtmytcSBpzXJH8i+YV2mdZF7Sz8lqhjqW6ec7Yg+F'
    b'rtN+wbySvPRYq6RDy/fo66bvDupq+L7t37D/9s56PNZfV4G/q86e2IlpbDFDA6Ns/Pi9Ihb0'
    b'FgPmEXF2sT3fCcVFhR6/WWdEt7xH3+ftFv0VCp2mB2YhJf0ulqgYscIa7USUm1K6nXl/nt6X'
    b'EC3aUDQRCikgQ0y2ZcYBI9aSa0hhexjHejtrrtJelBInI8o+eWepBAfr3ZJeiAYPcROXcA+/'
    b'wjH2UHAC38ZL0eJnuI8lfjO9r3EDC9zGdXRT9mM8mIyTmLohqsAo015hDtWT72dxGvsw9vAi'
    b'PB/buIQfo+JD+MAi4qH+vyrwUryNg/XSWvO5aZ/GsinFVMPFGkSRxf7W4trOYnaDdcABjAKj'
    b'nXQYcYhj202mlzVzq465N9a6U2suFXFUmW1L1pD2QlLBTQT5MiL6CN2QdA0dlmhtbduUdCoe'
    b'5+u0jkM+iNCY1ebn6WzPMpNIg61iSiyhOJZULf/W6TVx2IJ3MYuIgd6MNasko6Bn7KYj2yAw'
    b'IGFFHJL8xjW/zP5OfB+HHC3rr68GM4wT/4nDtNlbzHAK57CPPXQb/1/W05zTvsIRMiJMLwoF'
    b'tV9KuVya8gLmXfKx69uL9BaGph4wyhN5RTspUER0s7bZX866M20pM5FHSBQtikIOMlZRzKr0'
    b'xMi6ktWlPWbm2nZJFmo2Hd8eavEUtrGH+yho0MIYsMICM9zGEomHqEisMaCZulEnHQxNlRNN'
    b'PWBoo3s6VE9WQTfNdzHDJfQYkPg0buLj+OEi4kj/fxU4g130lmYRobBFrZnbEnFQ011IjHFM'
    b'foQODRIV66kHWnS2ktpH1sz1WPPOMIxz+uHRargcUteU8oi3M8xzS8e89W0ppyz3zFlK3A3F'
    b'btrnS2iMiJTU217YCuaaLAwdvQt7rohKJGd2Ugz0EhFrkpLpBcvA/pB9X9JC9oGtzplt2tuy'
    b'ZuQJm6IbAU8qMjQdktCanmjYzkdT3hJR9nMc3072VRz9lX8fxvbJ3a0rts8x9xGqtnYtzZ0+'
    b'ZXnGn+EJ27uSFk1TDppSRsmnMn0hIqa/Ay9LKQP5sRTH7A1v4/QT2VJDi2BjDoaVCBjJVSOc'
    b'ac2Zu1LKse3fRMRiPQxd27ZnS4n7UnQRaiTt8PaIvmAv5HP6wM57TMdXpob1WKulyuHKriku'
    b'EWNEyHIv68j2KKlHK/uIfiyFQk7mTlabdiOZyC3Z3HZPP7ZUCfRbPFmxcUhiY57eqGnfuLwN'
    b'hmmOyXrj4CSeLP+heTpWm5W4hts4idNTXvBrfAvfwHdwhNz9/ztUeijtMFxlPBHprchclAiR'
    b'ZbGTvKqmbXpoJL9B9hA9FriDX05+i2u2fmXnXtrLrPm8mn627LltjpebccxLmdk4PXPjVUTc'
    b'HGo9FYpFrdnZrl3THEjq2M/VdMNsxMQR7JTtTkRsNZ0N00xiszJCK2fdsdmUPbnR5uPguVIo'
    b'io5Yeoh8z3ZMn6CMnAzQxqeTaqmnO6wj+qgwPR4xm7pAfwO+f7Jv13zKqvrzNcN5vAx7w1jP'
    b'1sy31pqvop+y1UlupJg+JbJZhQb20ChSypJLRCSb2QsxY1TbiukbUwHLpqsgpm9nMrMnWOiI'
    b'iIf00yGtSilHbdfcpB+UEgfklfdZhA7p5oDebttmRd5MluRDU8ohb/fTfrAeaxnGuh+hIynO'
    b'S7pXQs+y9QvmBYfsVV2JKyGt2bdQIm1uXZ2+VC2lxC7zzNIRT7fSOmDvgi8JQ0/WHz5Q3njT'
    b'5gFCwAhMtXF4/rEVaPEcXECPa7iJ6zD0vItn/I84ANdu3bf+dRUbuxHwRl7gyXx7PWxNB/z1'
    b'K+mlPL7F0nn2villX6GU3StiIKPFQY3wahjvnT+19wxfOn6J+OZPf37tuGbeef5zLhyz5527'
    b'D7vfsWvXwJFtb3fGf2ufbklDl2h0P2ZmZsbMTJQ5d2zIU+fOzMyZmZmZmS8NSuo+Zy9rqo13'
    b'yh5j9P9W1VOn3lcM/Wx884375x9/8vRz8GW4xGt4vfS4bp8/Z9+fc14et+2tbfZMnY9EdVnG'
    b'6DZnksCCYCIYoDZRtUOrR0yss62iTZlaSnXTLq0dhjZlhmKrLtrRGtoFAEfJAc9wxBTXeAzY'
    b'sCniWeRD8Yis6FzXv4Xfjqe3wlq9Og/xDfjS+3fOH+DihbBan4M7OMMOEMBAECyYdAMyccDA'
    b'HihSBCc0ZSgITrChmGULT8U/x6Nwk+TD/dl+ZuT5SK6wJTmETWxjjMcv+kmuM/I8HF/0drvx'
    b'L26fn2ADBDsADCyDb1j0e4d8RXh/4aLtDkvbC5w58Rgflpuyw2e9EJb/D/m0vIr8fxDXwJkT'
    b'zzBPgvr/Ig3/n8UVvPx7Y3yqN3GGfbiX9v2l/cbRftMtX1EO5Aar9ou072CP+2NkQ8gGkqdX'
    b'x/X5pv/aOn+1Of8gVizY3nnrteIc34STDPkC3Edw03Y5rNtXvBDWNueDW+60znARzmd7FhnV'
    b'HRm0ACVhBSyAJDngGiuO2g1b21VdVQ+dPcfa9p52KcVQQaDtfs75QHsGZQRlwxoOYuBAJj7C'
    b'0YnViYPYyFM8S3w81+334o/h8Ip9rGDga534nouz/b3qvdbn4rQsZEGLAEFQwBAbioZgYsG+'
    b'VUAQEezUBLQEA2gBbcER/zzJh9iow3E74jkOmLjGqccjfIxnuMYeE/8AfwWHL/qCywJgANyZ'
    b'M/gK/Bi+Cp+NBzjHSVZMPMJHgNfK5+F+/ty//70A/FSCHYYTcADM/wthBUUwUC+Ld8EdPMAZ'
    b'7mOH1zHCOR6M9kvG7Ocv7VdGzysTGxK9r1YURzzwgvYc90LKFNus6ymHLczZf9dt+x3m/M1q'
    b'K/tw9fabD1Z8Eb7QiT3ewMQBZ21fzNC++FZanzeSZ+s2v3gkG86qZ+qO2LdA20CSgxrVgYFg'
    b'4hoHBLBhdnbTTnrTvojztml7rt0Vak+Rje4QVKVUuy/RwgQMFDAxJRNHbGETT8lj8VHkA2P8'
    b'hbGM3yb55//+0dP5igF14Hbb6uInjuv2HXP2C2f7OW3fwQOSxIS2AyWlSTJbSbQlAVBMFMOJ'
    b'IhiJoYhJAGhTEqakYSK4wd/HB1B2h+P66d/LhiC4wWMnHuET/Bv8TfxrPPrqz3tv8z/OBX4Y'
    b'P4bPxWsY2OEMj/Ac93CJd3G37dOdnxJUEQTv4uvw0/AP8Nfwl3CFYF6+87pXS0sABQzsMLHg'
    b'TXwDPhdfiC/FJXY4f0HY0u5T95d2Ge1dBMGKBSPtEAMbWUSxRqsGnZJiYBmxNVmWW7KMe2OM'
    b'Ba93zp+T+nJENMyrbbu6OD9b6B21xwVu0l6ZvZqtOftakuPFfvdvZnve9jpJtjnfa51h6+yK'
    b'RIKJtN0hgJUc6Q2KYMMSWeksbTvb7ttqexfmbBJDDUqp7iFMUSG0lSRDEkygJRIzckgsRlbt'
    b'loSTcCVjzDlHpbgsDwKvTt9948GtyLeP1sw3kj5MvZ0xdjiGoFgkaxkB2UTDKLCpAWLDpgoI'
    b'YCkDaEkwACSiUdUqo6TtXXweuZs44IgVxYYCjrjGDRbc+9Sp6nv4GI/9z3ODv4XPwX1c4R4W'
    b'3ODQepe+2/q8toe2j+fs3Z2fSvAeLvHL8AX4HEz8M3w9TtczOMcHt9I6vkJawcCKBa/jDUy8'
    b'hs/HL8Fn4c1QtY+OpX2wzF6k3YtWVhTFDMJOMiW0TRU7wBALSYAgEoiTqLbud2PZjYUk7LRf'
    b'jHchyRDHJJrAgmOo2afd5r9M578e3J/6IFyUbd3m5Zy9PzvPcYWJlGJre4bhRLBiYqMb9ph0'
    b'KQXtvlSr7NourUUNCm0bVUwMbRJNRnf7ZdzGsoxtt9sNSFKxBBLqVCe3SOsM1fYklp4n483e'
    b'SvjFyWfr8tnT53/mrbdf/3tvv3H/CB8+flYvZ2Lfuixfi4ez3tna18wu1QVJUkSZ7QCakXHk'
    b'vxPuEA2zAJXEhpBSrSGadiJEooC0DSSBoDij560zrCh2WDERHLDhgIkrPMKKD3GND25nV+ur'
    b'JI4P8Vdwhi9rO9uO1i393LZfiDfn7KO5zTttL1rrZ+qSMIDgS/FL8EtxjY+xIbjCNf4W/jge'
    b'4c/fyuoTr07wRfg2/AA+xgO8jf+ADV+EN0d7tsy+sbRv0YUAOsJwIkDGiF0IToBAaQAAlRRB'
    b'xJazfXu2GxgEnWHDIQkkyXSqQ29Uw6a9dtz+dub85+ptaLtbt/n2LW8cjut7W/uGk1yigpQF'
    b'MFAU04mGKYzkaWnb/Ug+KRumCpZt23Zt39S+VmgpdEbWjFS7SzKXZRkXd86MMZqkgLwI1Uoi'
    b'rSI4YmLO2WPiDEcEA01ynLMfH9f11xyO26998/V7/wEr/J1/9m8LAMjtnapzfNWc/eHDun73'
    b'cd2+Yt3mQ4y2OwwUaY1qIqqSbAGQJrSSKAptS3Dqt4UBUEYIQYOJIigmVnxSbtTY5gxWvP6p'
    b'q0ornuEG/wF/BR/iER7jb+LvffaDuxO8nIEFy+tv3Ls/Z7+g7ed39jtn+43qTtu32r5d9tqt'
    b'p96Gq7ZPPhOFFQRv41vwy/E+VjzCa7iPO1jxr/FP8ddwxK/Hv3j1stA5vgbv4yE+F1+E6dQ7'
    b'4KuX2Tu7zn2q0fuoGmEUglOdcgySLBkZAIhZgoSXUxTDyJrdMrrfxRhJUMVMbJjgJKsXLJio'
    b'dsWadftrWeff1j7EwE3bF8f1X/j8+vgV1Tnbszl7DiPZZvvf3oi+wjrbOz31m5iRa3Fs7ehe'
    b'XeM6HEVgzo5w15yjbbFmdj+WMfbn+/Pbxyi5rdPZwrJboOV52GMgJCJhJiYGgSmnOkzsOrtl'
    b'ZKDl5njc/sytnH/tum2//63X7z/DfHmWZcFbbb/t5rj9wqubww/O2fvYiamSmARd+l9qBTLp'
    b'SOJThxMtC2ALU1LQ6sun9wMLYEMBA8UVnrQ+oft1m4uX33/FEc+w4gr/Cv/e6fkP8Tfxwa2w'
    b'NgAEwT1c4rPw5XjzdjA5OwnKl2q/tNzTXrR2WBDclEfaK3TnMyvBgs/Cz8fPxTv49xh4E/fw'
    b'AE/xb/EEG74ZfwDv4l+iXr1O/yv4O/hc/DAW3MPA6etyR72f9g6KgWAMTRtQNkkRERAI1HiF'
    b'oFvGIEklklCkQpKhhUpoJ4ojBjZJsUju4BIf4VL7pbjYjXFz92L/7zp793rdupmpLshIjljD'
    b'hgOSuBoZKw5YxUGVGbPRDi3JRNsujuvdbc6hBoodHd2WznU9LLvdsj/bjdlalmWMZSQjaTUs'
    b'GWkkAgSSRY22B7qURBa6JVkwsmRgtNZgv1++TvzkvOlf+3f/5oO///D9dybwclpL2yVxPUbS'
    b'2iUimmQip56BiQHY2iwoNkzJ1CakBBFTmxIEA21Py+fqEpkI0nZBRJOsbe+ru+Uy8XgZ4/k2'
    b'52t4iufYcOPEM1w59T/GB3iCf49PTrICAw/wDj4Xn4d38LYT9+c2d62L6mvY1IojZlvYyo2S'
    b'6LbN485nRgJ4Hd+AX4VLDPxjXOI1rLiPJ/gIBzzB52Di6/EXcIZrr84Ob+AuTp+Pe/gA57ic'
    b'I5+0+Rna+2FBMHGcUjRIMhM7jCSV0IZyqoE2WoiEIGG3a8eYlnEQF7SaUGrWTGRLUjox265J'
    b'0BtEXWTb9tb5xbbtS7UX6oLusAzGnq0jo7txPJBtzg1HJ+CI4CbSMsOhtetszLnvnGedXbRR'
    b'Cx3tqW4tiKCAGnNOwwCd0s501thGMiLJMpZxplmbripIWbWbWsvHFDmvwlF7xBLOsfXUezrb'
    b'f9F1u862ffOyjCf4F15O/yN7bwEtS3Jdie59IjILLj3u16TuFpHZsj32zNhDf+bPZ2ZmZmZm'
    b'ZmYeZuYxs2XZkoVN9PrhxarKjDj71311Vq1YtVqyJLfM0S86s7Ky6mZlRuw4uE+EQtzvcvqR'
    b'sdR3FfenQpFLoRZuVeWImeoE2eZZwELaKAKMG7BCa1IPddkU6mq8aXF+AiSCBaA3nkdrvbAk'
    b'HcQKW4BiLCJYNWE0e41ddhmewR8E8OMA7q6B6ixskIexvRYOpecDsPbjO6Jj6lKKuDtGBPwq'
    b'4l97ECsCF4+7cSylZgD4paASsgGOfxzArw+EfxHATbRhBKGjR7/fqHavBrC9FCrhoy8hvMHi'
    b'Ac7iQR2GhPd1YXyfdO7fkas/RSAp1BFGMGRIIrbZKIEEGgkhcsIkVxwLCcnMmRORjDC6Avzi'
    b'X1boRKGa1AisHGhUxHAt6BqTMIN7gmtKVwZkMUkumgTYDmEvKdVzlUxCKfKL6uriqgqIJYQR'
    b'xBDJyQtJE1XPcpfc9+SaYANONUIXEOoZ2hiraPE6gM0CychEEikl66f9RsoiRZI0jqANzbfU'
    b'6EsApwTOEFAYzosUhvwxTEuDu68A+Lrdr8IProbxv73z6OwYTWtsWEnAVUnvWyyHv3ko9Ve7'
    b'dEiwkEjaAHIf4JkBJDaSGQCEqq6Q8CxeO0AZMYIUAshAVhKFoAvCBgCR2cB7C1aIWCtttifD'
    b'WNqA1EiwxhTAUWgcfzZi0D771P78VRIFoZU0OY7z2N5sFuwu+n68Poi+B2BuxotIDxtbz32t'
    b'vmoWf8+/BGxVHYBvAPCPhHSFWB2eaz1rAVLHscqcxwNCANePhzr4owDOvwSw2o2Z8ejnkTr0'
    b'WQLXIXwNgWdCZSKEtBOz1RjIEcAUzbXVGiGIfR7R5YpkBWSHCJoMlU+IWcdWTCFAgXTPHGWs'
    b'dQZgP/TOnTSsADhswxwcUFwrQCjJhboxxB85JIKrkBsnkBSBUZ3cZ5I6VZ/KPUEK1UwWv8oa'
    b'oFLzYAPEKMQLsHlXIAgThDKMAODdpGfusgQYoUyahQSZCBiIOYAjgucARkEFwnkcNwkP3PUa'
    b'pate/XVEjmyp/n3S55e233p4qptX9leS3iD5soSBwALQKGEqhbrHeM7CRMQYNqspiCJAELKk'
    b'GcmFgIERJxaR/yZtxcsMsJBCeGXZmBlq470cI0B3JYW9jph0OXVjqRWANeffiwX+4bXZ5Acm'
    b'yX7AyGWA0u2QpG7FnLkd2yE+vxtjGCphxPKFWumumRmngPbclQAsm3AiC/DMvxglLO5Ewf8N'
    b'AP7yyON6FCA1b9Qzxo150NgPpvGQXoqAuv8tgGsIsPpyPZIpeuvezyZ9uK/+z5v0bgBCYxgO'
    b'MWgGouP2GAFSYY6vSLZkly+QbBRQm992FdIUpEVENUL8NgBiBFCiek1j7Rnm98A0D0O8GjuY'
    b'xb5DKJASyAopg9t4qpVL3Vg9LcfKIkc2e0vSUF23IHWSG0FBbqo+rvsUEuSSoLINbxAcBCUY'
    b'IGvijhR3pjUqjyRyfEYRKxCTDm7JVinnY3bdIOL3h5frIYGVgMJI1SGwwuZ7lhLOAFWAmcR+'
    b'WQ0XJA8kncX9GK7duPJJAI8aD2Hb2IynJ5944tpfdna2+AtqrTdIDu4+R5zjrpm7LyFM4res'
    b'BGzjyRCGdgJq1OqwgaEi4qkCiI4J1HhwpfUKarOdN4Axk8BS66pZUEubAD/L6ZGRl/1hn+z1'
    b'LtnDRHqTy/tsE63eNWOd7cK2s3jPovcNe8XYJJSn6F1zrRcBWL8ower9AP4yAH9b8xCuNowU'
    b'OfpJ3CQBQDyEZUhV/0cwP5y+w2k5rXHUCFzL1f+G7P6XhkrQA5gm42Sj68NDMSwpp+pCtZyW'
    b'FVjBbNX1GSBqqY54wCsABxCeDRtJBjSPoMoEIVECHU53TxJFJnOFtBQqRrwAgEh/QUgDBcII'
    b'0hgAKcEj5SOFhAQXvEqfAfCGpGsh3vUuvwWgr9UHuRPCnuRJLrn7MoyxDmiytX0J0wCgEmqp'
    b'wve/IjEAPAWQ4r0KcIj8QAdwAfIhzX4COf9hGj8OMrxd2E2Fau2IWMdZ1S+U3xlA9dOaJN7/'
    b'/mffe3p68bdeXCy/w13XJO3V6rP4e1arZwCjGdWELiCeH0juhiKEmguXoPgtTnKZkh0DqLV6'
    b'HoexI7AQUFNOebkcQs3DIj4zaxwr3mxzn2xJ8JzEIm0Aq2bjpE8pZWOct82DLDF39hEBxk2Q'
    b'qcff2Q9wYhwfGttm3zCFsIn3WjWxYOMvJsBi9BThCn8ngN8cYJMQP7ghJ1yFVFUBlLhhT4Zr'
    b'9k8A+F8D3EYAOHv9nr7CNrYPAfj7I/RhD0DlpLtDs1MYZ0h2n5fdbARxCLIHmQFMok+xaRzH'
    b'4hCecPfnJfXydZfmcjdJ2RxGCQQ8kdWMSEBHQaGaKCLUI04pcGubLBv5d8IyEmyzXFNBI4RR'
    b'kAE8jvt8JunRWOsoYBVgDADzMChjY7zXdUkzueQSIJVa/dRdZ5KWDUhUEB77A8gwFHMEMUKa'
    b'AZgESJ2RfCs8m+dI9j00+yzI+/XkbJkO9wFAbycNR8rNO9H4VV/1grnr60qpf+ujR6e/bg0a'
    b'NySY5MldGYDcPQdA0V2ZhJNWSTBisBzRIuCKJCtJhfqskJyrmQ2SgBjbQQUzumuQ1K9WY+vJ'
    b'OwWABkSI2J/mFEAGEkgkZ32y1TSls2T0GHO5Ufm6xrM8j57ieZw18VqKz3YN4LWZD+fRFzv0'
    b'VWe/eAArblqEH/xPW+bTeCihNz8Vrz/TPKhV6OIVwPcA+G8B/OQaoM7xs9f2w+376+IhfiZy'
    b's17Kt64Esyv24+EeBrDOAMwhHAi6rqqnpPXW6+G4Go+8+C2XjiBlCB2ITInaxh5RlMvITTca'
    b'RKdhTGBHblUyycXw2FkTfFgkVEGSsJRkW7sDsXi8lQBhVryJKEO0UNcihH0G6TqAaTgYbEOK'
    b'4HdLqT9V3R9IWoUdL9z7YSMhj0mc+nJ40+azN2B8nUBBgNnjHpHZbW7cTyelrwHrHZOmP/CB'
    b'd92utb5nDVT/2GKx+uZSfO7udJcFWytCrHJ3JUa4g6RW7VO8ZgtcrRTmLgvg83AyFDKYFjb3'
    b'4ZGZPTw/XywBLBvm1yHmynkTc3V2ZdovCSQJN0nsdcks0/ZIXAVwBRGegJASG2/ipMkxfLAT'
    b'fBsA1wQSx/NqwijSDvngo/i+418sgMW4Wf94qIC5iRVxr/40jdchjHJ/y2t9DUKh8Sj13bvD'
    b'oPhbjl+/91uOnrrx8uufeqV8JSljDvdm2n3v5HzRrY+X9VYlp3gfHeJh33rm5oGEvVC5Dnw1'
    b'/vm11G+R6wDymwFYPaT98DwxMMIBhr1sCxxo3lPYqnKogCKJTKwInJvxQsIk1KxF2JjOIbgg'
    b'BzaBno14T0BT36g4dKEzYhbimVoVbGujkJO0DQ8XkSCMAZPn1fVnVueLP+jVPylplfbnArCq'
    b'ZxeLdLA31NPzs7Wk5Lustd/woeccAH7k4y9xvQ8AbHraAc/Sgtgf/t6fiPfeMQmLpdQn1+rg'
    b'P7UGrL+iVj8Ila5uJrdQq4okkfA4TncZCQFsr7l9ph6SFwEaiS3IAXAzG9f9gsT9lOxTOec/'
    b'+uqrb70UwHA/AIqNZ67NIeRTB3Oy4fOKBfWrG4qhq/Fe14DeMrahcoI7jKiKc5cxPxfNOTnO'
    b'WzVxkHcDsFa/GGxYbFJg/gUAvwbARYPWV1X9FoiJpKX8cT+VtLJkVwDAS/3c6vTid9ax3PHq'
    b'ES+EF+LzAtDFw30UvPavLFZD+RIoadqs9zYZ1aLzbcgR2Wy15qrae+3k/LG7mMQ1gFcIPJXM'
    b'fpMRzwPsQjqZtrYABjtDfHEbWCg01C7N76yMgUvgHqSX+y5nCQTkbbJyc5/b0AABUC01RHz0'
    b'KdlezmnPzA4a+4RIdiSntda0bqYNNa5L8JDWEkmmZGdm/GF3/RESd0nqySdv3IvvmcXA7z/x'
    b'uTfufPCFJ/tPvXRnXt2P1/sZwPLjn33dP/Tup+pObYFVPFOP3iZcj829LwD46umF3gEapAMJ'
    b'/yagbw+bUyQQK7lvs2zkrlFScvd+3bO7PBYgC1DKCkk3nksiGRILmFIqOaeFmZ2lZPfM+PLG'
    b'zMHvJfH9P/ETn2uBHW1bx1Lp81E6hwbwFwP4VZHONo37zwakVi0pYvQU24qIqG/DfRrATA1B'
    b'wE9GfzG+s4/Pn/9CBSwitoH6fyWA3xhovCircWLJniY5D7dvkSS5liQ6S+laHcu9YbF6Y1ys'
    b'zEsdmnypDgAb5EeA12mI0H8ootdfWgPX8Db2s/2wk81imyMe5UYEoN5CCFoAPkDyRQI3SC5A'
    b'PAFgwY24/TkJnwG5ovF1kB8kedB3+VmHrkFYAbgqYNaoC3vYNKkByQZUwACzJj+N8eFgMQgp'
    b'SDqD9GqX0kNAXRsLE1HJCQFKwnZwutzDSArZuk0m3azvu/l8Pr0CSIvFcD6d9jNJmk4n+xcX'
    b'i+Oc8yTnNKnrtj534u7aTFR5SonufhqsmA9J2GTSB/EecqgK00ihSqHe/wSAZ+OcT4ZKvWpi'
    b'gv5srN6vNbFG51spIYIjGwA7XYOW/wwBaw/APwHgL4nX4w4neom+aBayi0Y1MoRBW1IkVSMD'
    b'GiR0ABTikIfR/pzkT5H8gXAc/dSjl+9c4EtvKaSpXw3g1yPyX+Oe9s11I15PASjmzNgu1s14'
    b'OWv4ziLJcSuVfTTydu/F8+gaOvb7vyABq9GF37cGnH+fxgMobu5mv6fxCskegLn7IFe1ZHuS'
    b'SlkOd8c1WNWxnsu9AkAAzKQJalOjzw/RpwE+pwD+bQD/9xq0LKJ5zwOE/t5Idj5EDLbGc+fN'
    b'4BTJ0cgpyZNNdRCEpMNOECAVF94Q8HGRczP7OpJXUmIHcCJgCokBGGDoGU10IBt3doHgiJUa'
    b'ELiN62qIArdqmyqk+wYcJzMJMEAJAOSR7W80uUSjy6Ud4rsxJeMaqA7XADOfTLpDkhyG8bzr'
    b'8qyUWi+38Xw8JcskN9QKAMKYXCPXrpAoEpZmnJlZjSjxRzF5DIDHPT1rMhZWABigNmkmz0VI'
    b'y38UkVcak3oeoSwPEAB2t8/jzaGs3uoz4vMa7p8IX1qzGF9/VYCWGqDKzUJZmxzWs4Zj3+M3'
    b'dJ+HOZRxDuL7Svy+/xrAR1/85CsXX6ZQYFemfa8Nyd63S/pLgn5p3pznAhKiMSSu8BybgKSt'
    b'NAhtLQPCAqQxGB7iel81PpaszgmehMobRAPwX7hG9yC2O7v76Fst2XvypPs11uUPmtl1uQYA'
    b'ExA9yc5rHbCZCJOyGs7KarwYF8OZ14rGLrCKLgBsPBceA/2oVR3ivVdc+s/c/V1jqc8DsJCg'
    b'PhHG/Zsx4Pro3KZ4kFMSHUlLxsttBtkLyC50AmomREAOrkC8Ffl7NxSrshCituBmJAC2vvmG'
    b'91tN0QW0almjGqK1MTT2jHMDVpTOmmO1VWub54HWsE0Sl9JS3+fZdNrvrfenXZdzre7rbVdr'
    b'VUpGd4kE3BFsBFv1qEobg3AA0woxEdYAaHFOAjQPkTKMzSA23OSnzSIBBIg26ShDo9YuA7Ae'
    b'RX/9Xp8Xit+jAJY47zzuAb4E4GKAzV8VNta9RqVCSync2Hi8kUDQlNpiXMsszmNsxwbAHoYW'
    b'8FsBfGwNWP7lVvc5mnSHAp514f0u/QWSfjWAw8gC8CbcZRXhE05CEJICyIT4LQIFMebFWXM/'
    b'LxAAPVSvk5QGEItVqQBwvyGyHPMvRON6XPwTlux2nnQfCa/ITNLC3UXiAGDn1WXJZl7qalgM'
    b'D4fF8tRLPZfLANhOJro3cR+t7j2LG5pjv4/XE4K/Opn9HyPqj4fI7CS/StIzAMAIQiR4lcZs'
    b'ZCY5vdwm4whyZYRITqqk4pqQoEusW9VOexCuhGsfDhgEMxKXXYRJLVDFrqTgfaoQGG9YAzJx'
    b'pvR5CA1z9IvoDTdSG0/TBgSGChWAk5Jxf38+j4jy1Wo1nJFkrdUAWinVSCbGbyDBHTANWxYO'
    b'JZR47aWUDACN7QPN8yIgq9WXgPo4prgzQ3jglqR9pxkrySthSiCA+aOcOgBPd64jAebEvoD7'
    b'Ij8h4JE2tL4vrcHqdXxprQ8V9G68bgMrZ40NqK2P0GYUoGFWGGMbQIzaAO9ZI5GF1+/Lnmvd'
    b'8Wo82O/z9Srdcsco6GUJz4M4BNRtwyEEbm1aRGFDNd2MC0TU/lkjJIwCZsV9ihifq1rTjme3'
    b'i/4LLpcwBQf0BMBfH5zQ83E5TLnR7Usdyql16d2W0i2S0zIMp+NiWNRhPA2jujfc0bazwqk1'
    b'hjexIhY9hU3sTMLHXf4H3fXj2kSox0NkJvAUyZHQV5Pco3GWzGzdEwlyAzYeeWJGkIN7t6qe'
    b'q3uwQKKhSZJDcAWvlJEkUYxUJEObkRafgZGRf0ZDQ5Cl+F2tOCZBktr0m7pVr6QTA+4GYAmA'
    b'N8CQmv3cHAPJnHNKXZdhRjczzmaTPQl1MulCSgBJBEUMGba07Q8Om4wCqAqg7TWaWW6ShvMX'
    b'tHG2UmPsh7v/kVl6MSX7HMmD82Q/kaSvFdg58XRw5p+N5L1K/KCAfSfPtJFcvjdsOR9767s/'
    b'9uZP43ixK+9/Nge/0zcC+BUAGE/gGqSj8O4eIGKnJCCeawW3tM1Lkm9FMYyHIB4AeEDgDOAx'
    b'uAWqiyZ+SWvpSl/2XAsTSOS9flWf7L0CbkC4pY0tdmrZ5MVHSzbxWi2uoaacq5fqqc/ndSwX'
    b'2HqW0QFbgQHFxbcp+Vd3PLk5jqdfKIDFJgr5NweFsTUPZrY8OX8E8ooZrwW96ru8eDcuh5WP'
    b'pYaEwlY/j94jWrNC1eaBTeL1I5ccwBOR13Xu0puShviegSSNuAXwNoi5kT1JJaPscaMZw9gd'
    b'DykYJFmFfFFKV13JJUqAIDYlAxQgVQB6AI6C9C0LMIIx0lGNcCM90QwEjXSCng0TAslAd8Bc'
    b'0ljqsroIIMfqWCMB+iJyxlpAU3SPrrdRJWtKVieTfmLGYT6f3uj77uDSWxigSgCZW0I6mrTl'
    b'7d0FmQDQ9pkpKc6LY23TzrW2zWM7kqwiB0u2ZErLanaP0LMOzpw40WYs3B2MSwcvnHjU1MEU'
    b'gP8lKpR3a9Aam3y3/ejvBnCtoWeZhl1z2Hvqhs5fv7eMzxzNbl09gnQgaQphvtmqozGkDI4g'
    b'Ksn7MDtZ3HkwhKTWAbBGMjttYp4eBeXL8SNyhS+9WaPNfEsEY78/m13Pk+5KMIAmEiOETtJM'
    b'ComLKBH7tQKwKsOoqO2YqhSBzsg7xUd2Q1O0W8f0F0ryc+sW/XD0Hw86j29AiNLTw72PXzw4'
    b'ed7JSw9gp+ofkfuhVx+aXKUaoNRWMUZsLQbXeXPuLLx7xV3xOdwIT0duJLUJiTmBGcgDAisS'
    b'C0S57mTWpUQjufXGEWjLyIGSshndK0lu4zUBKGisCoFC0l2y6sqSTGhpseAbsOKQaEMyjtlY'
    b's1m57JII4gaEPYUFPYmGpE6qNSo1e6NuXSBSKNr0iDaWage4avTkrrJYLJc5587Mxum0vwLA'
    b'zZgBJslHM5sCQICPojqwwuEAd4XBHQZglNSuttHRNrW9AShFPNMYLxT2xEO4H9GSE7pGYCRQ'
    b'CewTmBYyJWGP1B0TpgCuVfK/0sbz+Lk1UAHA2IzT4EbHh2JsPtuo1UTELK3BKsXn7gEoi7ce'
    b'psatP20mdGlDX7q9mYGo3f6sghwgTSB04/kiNwn81tTu/G4AP3JFehHA6SNS+OIbA2DfA+B9'
    b'B0d77xnH8pxXv1JKPZKUt6ExYS+NT40Aa7PQpOottxrG5jqjvW3N0wGtZBzv/0KSsPbC+/Zq'
    b'POh3Bep/NMDriQChR49evXsHUq8oD4WgtGjiRq5Gv46IB4nvD0MtTsLY92AsdUbgKay7NgPw'
    b'2wEcADqREHQYbWwROoICoUTOck77yXiQknVGBoc3k0K0iL8PAT5Wx2Is5hLUxDYpSNc8qsxs'
    b'JzghgtWI0chy2ZNxOcnppDNbmrE3IJFEqW4gJgSD1kPVBbq7VRfGWuUOAgpwwqmA11NQJQdg'
    b'nJM8B9C1YnsD/K2huDDKdU0m3cHR0f7XkdR8PvlgVC4eSZsCMhfgkhOwyx4qcHg2RQSmQ0gB'
    b'WtxV98KbGOC5y9wJA7YOjERgDjAHcXr1nI5By0maKRjGIinao2zauROfHcnfn6S9ZbL/wYG7'
    b'a8DyHUfQfsQofXuA1pXG3gcAQ7xOjfe5zWkM0jt00VuAqbsA3ajirZOoNiwI3w/g90doxxjv'
    b'L79I4Jq86z1Pv+/ifPHnrxbDN5dSnvLqN9z9QK5p8MYHUDWdLCRWAGtIXwZAiOcxrEbbMbvk'
    b'6G3sIRo73VnjHa2/kGxYOW4KG7C5s6O6nSDiPx6+fKf8NLYFRre3USfqMBbF33xsdwg9/tkm'
    b'xio3g2wLXATOSRqJzsyUzA7MeNmzkRMjO647QhUkCWmr+tXBK1fFE4iVBBb37EJyKUUUuuI2'
    b'KJJRi4Xaue6PPSw5GYl2IDT2oWC3DLsViwtjqUOpXlyeBCyCmvhzAH4oG0cpVI2IRQvwr43h'
    b'fYqQwqJnMz5WW4JUbkjJ1Pfd01eu7P9NXZffzZT2NxKCDkUYQqyCEACu2DR1jCELlXFLNtEk'
    b'Bje9zYnkDp94QB/owes6IidUs45ASZIELCp54cCYNgByPpK/a93/SxEPbg7l0cd+8BP6PDaf'
    b'FyLW6utjEb2CmGzRrTWqN5JrW+ZqV2Xy6Go62kToJjxiFf2t0ESi9BbO4tiDG1/1wvnbzYUm'
    b'reZArpvjWN5/cbb4zeenF99SS70mKEOYCMHvTw4kCsga4QkbNZBwgtxKVdym/2hxvswIaTK6'
    b'NVt/G0mrtgnpvxDDGuIHozbRzmpipXD/wQl2W3l4KnwJrQHGvyhKkz8Xq1+bbGxNWkJBNJKd'
    b'XXZjCbuTzKzPyY6ScUIymEOZWrAUgFWptlh3l8bqIiAa6QLMJTOyJrJMczoX4Mm4zGaLRC6M'
    b'rNyohEQ85Ogaq3cE9l3ac9derZ7CXkaXXBIC1E4EKBaDNxK5G+ezbLiO2Hin7kV/FJPjwf2T'
    b'i7duHO2tAJxOJt0zy1I/OJ30713XO/yLJ49VRN2SNAFgcrWqFSS13sclCDIoKXJO9jYVeIDY'
    b'aqd8G9vFilDDoxXsWJljTvdG8mWFJ/iyExoE/n4Q/6kDryOe8a3h8SavQWtsC5xHgdA/J6LB'
    b'P9AQ1HXtPdwBq9UOoO3WS0zxejcucNG8h7ZEVmzvNcGxdwF8/Nr7n71DsxOanYEb4rym32po'
    b'Yp6V9FVyjcvF6gMnj06fWC6GfUls0rdK0FobhIogDaSxrbKziiT0FRtb5Po72YBWhzavsL1P'
    b'JHc8284/8mP/swHQ5zNcHj15Q/h52HbVggAqNcfTDmLXLxG0CODpiKD/cIDXQayY0+i5iZCv'
    b'Dc9QIpkRkpeRQ9+lm+t+xWg9CYv3ScBEaDuK3W05Vg7VR5fchc6MbqT6ZKtJTkOXrBo5RoWC'
    b'Bcgzhn2A7ogJ15Y/Xw2lriRdHYvfcNdeGEjNJQooLpWIqVm4a1ncX62u1/Yn3aQlc9stMtuo'
    b'N97E1awX0uHEpXuSTkkuZrPp8mB/+nXz2eRr1+rht0rak/sem0AxCIY4gBZgpJCaGCeiBntA'
    b'W+WZDX+YAph2KygTzY5IkYSl5EzmZ8nuVFICOhD/IYQ/aJBdG+tnAyDaj++vAeu0TWAPqerv'
    b'CbVw3kgstQETxeuzZtx4dCM5jTjCCVpOKKGGhOyASmQ65Hb852m/He8gVzQ7T31+KfXdp/Ns'
    b'8qOW0yMae5BXaPYMjbdCIrwR17rfOgcAnNdaff0wu5OHZ7eG1XCgePbBAjkyXiuStMMDO4Z6'
    b'viSxIjkiFlMCZwmo8R3m0hRAChGaRLxH1vUYYpOJYLM+IwAL3NX3m9f6+QxaAVLtKvfnx+r2'
    b'OwHcbNIsPrYGLMeX0AKcviO2t8JudhjANW88jAnAWcuOGO8f2SYfznOyvb5L+8msR0PER8BI'
    b'bnP+PGSt0X0U4BJT7gwJdDMWAgbQw6fmal3/wJmAe4RGukJKQCWxHEqtEq7UWp+pVUeAcvwp'
    b'SqoCqksmYSlocNcb1XWniU6uEsY1gNVGjGcMdFwMRQBqJl1QD+DMpYWEZ2v1m2P1Vd/n/evX'
    b'Dg6evHXt6GK54iRnWx8joCafSCAISZCgeI+SFEH8CPYCd2kkkMwobEuJsULqQq6K4w3vuWQi'
    b'qkAXITdbli4f15TMidcA/J8Efq82EpXfGorWwOT46dskVMC/NwJE9xubXgEZgA9nAFWAagoI'
    b'dJKZxinCbICQxLdFROIGxOsw58khVea0JDledgEnBE+ZeG453099PrMuFyY7IvluJptZSkc0'
    b'diAPEXUCG4P4WRv7NA7jug7j4urZ8fm8lBr21xiPEd8nIYBYvsOrJYDFjOfkZccFgZTJkdym'
    b'Jyme0bgdk1ByaequA2kbCvEYsP6K+PJ3B2/5vaaIKBsOmwoAZdLr53G6zp8XKTOfi2PPBGXM'
    b'RVDOfPpLAq1YSRFG+gDCp2P/RkO4n5uKOD3aQEpCRrOUOElmV7ouXw21MIHMJHpEQkMY1t2l'
    b'GpNRlpJC8gmDNBKxBSxg+7AFERaXvQD0BoEBLifgQ/URUoLrptyf2qopUitSu6RRwqq6vyFg'
    b'Fc9dJPcRTdJAICkirRmSQDB1TozMkkqVUKrfGatrNZayvz8b7fIeJHtKwNXZtLcumwz0vb1p'
    b'V0tF5CDKSFfElJkZjKTLVaoPBF6prrdq9UcAECX1fWVMkB5LDALdjT2ITuAeFcUkpEqJEM5F'
    b'zGTmpcsfr8n+KwC/q7UrhVH9S2kpbJ3/MC8T07PNQYvYNLTST40gY4LbatSI90iAktq0m1ao'
    b'ABiqroVwEzalhm32EUFnti51+SFzumJmicmmNAONlckuaBZR5kBsuwDevdaOJlcupeS1Letw'
    b'GIp7dbp7jgDsFEvLSpEmFtciBAABGogN0SJJj+/1BLBJyheIIqEPyb+PhPEsiAATQyV8KWIt'
    b'2OQwvRyZ0k9HysJvi5SToRWvf56AFwEgJKH/IQzD1+KmfyZ0+BSEfL9vDVj1Z2A7m0YP0n28'
    b'EP12gONTjTdmIOmWrCc5NbOpJc5yzlcs2YzkNtiSxqzIu6rVzd0RUo+HQblhX4dF1KHAAOpY'
    b'4dSoyI0No8LdxuIuYG7uT0Paa0qgW6hcjDjk0YWVS/cBFQqpUXFJadpUdCbIAiCHdygLqMG4'
    b'QJeG4j5URz/W6kaWrs+HtfphqMWs1avckzHREgbSCiGY8VyOPRpSog0Aegv7iKQHLv1eCH+S'
    b'RidgH37/u+pJtgMAH4D0HIBfCeCazDonlyTOBZya+zyVegvguYg7bvZ/eUp/wI2fDLBCANWX'
    b'2w4A/LrU53/Dcn4BxkQ2NjOEryVUKUTALEJCgTQigAwKCYXgbiVvJmvtO77tJCynB5bSQyab'
    b'pS7PmQ00W5A8BekR06UmZEgxbvcbL+UUABtJeiVXdfdaSr27Wg7XhtUw9yoKGuVaSorQE6yi'
    b'V0jBLgF2BCuQJfSQ0ladlCzntBCQ3bXnQC9oElIx5TIQQEhYnwigaj0+i+hPAng1Vp4fCybO'
    b'059noEUAz0UljzMIGcRtCPcAvQVgCuFHKf0fL7761p98/umbbCK22YCR7wYsjj89rQh3KDis'
    b'UQ9u0vg+M/uqdX8Pje/q+u753OWjKAWfmnxlC15vllotiN1KE0YpSaEabP8vBphhG+6AtGOn'
    b'uQ/ikZPFh0IAHapfp/wgyPhCcVL7MYdUoyBGDqT0+MIuJtaOETum06Y7jLKURuY0OHHBx1HQ'
    b'WnZdulFKTSSyXKSZD8tBwzAmI6tvJMJFdT+RNhPAXWl9zjZ8IicbUrJHAj4O4YdIftfXfuhd'
    b'wWoAPzvc6wAcBovH08Ew0Am8RmhC6ZiuP0LpkwAeyPjq/GzxE+EswGw2RaiAP5O4wUPL6e9I'
    b'ff7zafYMyEMowIFIjX3VPk+QZCJpQqsHooZkNTCluL5wDkSwZpSbVJ5NztO0f2RmSxgrsQGp'
    b'BpwQjY1nctawMPjWsB/b6MsomntRS+3HoRyVsczdvUha1uKAdGzESsBowDwKx/ok2WBGhO00'
    b'QJBRt5LJoW451KtDqVN3JUFN4cw4mdQlYH1PXNSkNWI3/QdCVXwDwB9BRO7ee/B5JRVeefK6'
    b'8JVvLW3Hfw/gWyAtAE5A3IR0D8AI4QTSH4Pr98Xvf4Lcem+GAOXDhswsh3R5P6rkvPzia3cF'
    b'AE8f7glffOONp24YgOmDNx9cSlXP5z5/W875fZbsSRqfIniEbVUWde6qpfqeu3pJFMT4KhFb'
    b'cIjdtqgqtLUBSQBR47cXAu4S5Z7hMpUy0VhbYGvdaPEH4tlHaSxEPTuCDLMSWnEBaKQ7QtZ3'
    b'izzr30p9V4Rt9DeN3AOQ2tgzuXMYCiSV1XJcjONlbsL4oJR64S6G9zJi3TAmMqR8vEjiDwD8'
    b'Pd/+Kz7MxWJJAHZ6tM8m/eovOn149nccXN0/Dhf/n4y4vXMA9eD4LNSu6NFee/2+foa5ru8r'
    b'i9Vvti7/pSAOIUzaBPidkBihjWeTnMk6miUa405bMWMHyOUqqc9TJgNT6kl2TJYQQb8ClPr8'
    b'Vury/XgmETKyXZYmktJOapViO2/ssafRh3gvIcwfRIQaSD2kPblK0BHdp/Aqt55PHW4AitYT'
    b'mUAOnOm4abm6uwQtxzJdjWWyGut8KGVaN+E8lLZFYEHiMWC9CKCtxFEb9bBGUOb/BeCPBViN'
    b'8Z7H57gGL3yeyOO6K41cvXlF7ygXOvnXy+xfAvQA5LMArkB4COgiHtZbdP/uUNmeDAMeI+pZ'
    b'ZJDwtwGQkUEe6uQPBWD/0Bq44kE0Azy2X/fB5/QFVtyDMNhfCwrnX1vGcpvkHogJhOLus2Gs'
    b'Ry51UkhLELFt9LjdTkKx8GZtjKJcXaysSQ6EwsjFJh8aYWhKxtA6sNlyxz/8Ni/NCBrVHGza'
    b'rtTFyln/cqzyMzPum/FAwp4ZMxBTD2AtleNYQKCOY/VS6rB+fSH5yqtQxori9X6t/gqBqy58'
    b'tjPOg43gf/jmr3tPW8CUf/wHP7lVtQgmSzaHVN198c2//hsFgGugwg67RNsUgPXlANUH4vn+'
    b'KoSt06tPGInzceYUwoB2v+VUJ4c86XI3n+7laR+BzhJII0lJFtTVBqCnWXhLoca2VS3ZMu5C'
    b'AsCmu1cf1h1yDxYMoLHd5ebc3DJ0EJgRsOb1BTdR9C9d7tvm2B7DIZWlHFdljGpRjSYSBnal'
    b'sXp2aVqqT44vVlcvVsNhdW00DYABkFCohG/FhT4AkGJSsbn4i5i0PxUq4fcDWIWUVd4GqIi3'
    b'oytp95v2JYJYatJhroD4Bpn9Z5vr4Q0QPYQKKK4dK7r/CLT14MUcVgIwJdk3hVQZvTQRw4ex'
    b'6tyX8M8C+tMvvX4Pb8NHtHusjYPS137guWVwXb034nT+8qbEWIqinLYayrVhKE80VWFA0pvY'
    b'IUXydAaYANEFLM4XHJYDvVbKtb3Tghjf8bgnAl1OSFuwejvUaUL+mi1J0AhuT20Fu9hluLbN'
    b'atqbnPbzqRAIZolzkpPGCYhgRNXGlu4XpdRHq2F8dRzKXZcuJa1XT8+XP15KOXXXOcKgHPX4'
    b'ziTcbVM4Pg/La9v0G77p/S3NTtv05YBVtBRg9W2RyvJVMa5ab3KbzLtS9dLYAgGoQhhIKk36'
    b'vcnR/CjlPIcxgzCCxsQkIWoaiQFiRtDCeB+FhCgzc5DcDf8RVOUavNRTuVY7hB3WRMqXBrQ6'
    b'RrFhAz4TZqKPU3oZwMsErhjwHIGnuVEpJwRmWQr2W7Sxi93OMyIAVhcuVuPs+GK5d74ceuxK'
    b'/KQbN0b3izg+NOjHMNhebVyeBuC/CXvWZ7FpvgYt/zwrToreJjVq5xzbDQyLjp1ztQY1BIA8'
    b'iyjaoGT/IcCNZ46RyiBF/hUz3d+Q6yySetPWsAktI3VETWpDW85+EqAMhBgs6ePCGrSET738'
    b'xj1vGQva37LLYNDQwl587QfepTDQfxjgE7FKXovr7mv1/dVq/AtKrc9DsAYdjGCOJGaSRByX'
    b'u7gGLKwWK2yJ9diiCBip/9ue2EpZcd5Pw/RsyZBzAo1QE4geRnCPCHVnTiVN+rHbm1jOadow'
    b'LySQhvgkiJh1oEuDXMNqGN8cS3lrGModdw2L5fDZ1Rq0Sq3LUvy01rqQcBH3cxldcY9rc+/H'
    b'FhzivLI7rgK8+LFPvSZ8eY1X96YK6elXh+Pn3SHN5waougD2DMkFeDyfmQQK0OZcGS2lNO1n'
    b'edJNBWSSZslUa4UZw6IIuTyqgQfbLImUE1IyWE4yoy63KVmN8ZIkFMQCIflKrvMwlod9auvl'
    b'bvnVPxsA9ZMEPkPgPoFlvEeTbhjwNRPp2SCyPGhiAFPjhaw7TgLGMcSWLuWxuK3GMqtSB0nF'
    b'FYu3aMbhErDebKJNbYdqpTSS1muhHv5uAJ+O/KQ7a8DSTrLk+yJuaQip7V6TRb5rXLTGfZoa'
    b'sMSOlHa6BqyLQOgbAK7B+A/L7C+H2s8qgJGZ8gd+6VIfy36pPpEU85ZDSrY0ckXygmQlAZLa'
    b'ltKO8uESpiFmX0hYQvqdAn73a3ceHLfXG13xuv0t00YqdOzmSsW9fu9zt0lyRcDc/dtq9b9G'
    b'0vNhkHcz24jSDZm/u3Mcil0CVRmL1+qBPSICsWLLVpLKydCnhGyG3SYI7YtWyTMzTOaTsJNK'
    b'csUvp7b4CFYYK/tc2OVl6GVdSmlihhySAGLLKEdfvfowlnq6WKxevwQ5l0avvqzuSwB2frF6'
    b'fbkcTi4WwzKKhVoTM1Rj/4TEPYArAOckVhJGSccx/o536hCWkHoXEuqHP/yc4j3df+2LLulm'
    b'a8AyADfDK/k1kp524YZL1yHNXJgK6qvrMNz9eUs+GE3NAk4yWU5zRL6pJJqZ3ENgiufi1QmJ'
    b'NMpdBEBLtgGslGRGzA5mdbY3V+6y0xC/jwqFfHD3R6qioKmEAdJFzNkHoVGdR5TAmzH3h1D3'
    b'EM0O3N8dLKS3G495jFe05+bdcd/QFj3VjO/SAJsawgIJKJeA9f3xxqRBO98ptvhWXPRJeOMO'
    b'ALwYoHXW2LBuhw7/NSGpZAA/DOBHADyMiXs9HvA+4kIa5se6M7FT8yMujq4dnoFwmn0rkv1b'
    b'AMKYyWnMGY/zB1Z/sbrXWn0+jnW/eO3kShJY3VNKNk777iznVMwIgpQ0damrG16qy21y92Jm'
    b'Z4JegfBRkp9+cHx2GtfaNmvUk5bf/TB+760GtNVk47c2ROtyns1m/Xv2Z5N3T/ruphmPCPYk'
    b'Ot9KMtAaqDgOo41DYXCgQxIgEQggYRjsQ6MLAIGRyMlAbKUvgaBcilMAUY1KyJQMs/2pAErG'
    b'ClqFIuommYFWLDHDzM3oDBHOwiPaqJYW4pUCeAe5l9WqnJyfr16/uFi85pKXsV7IVRxKtXp1'
    b'r10ZPeircbXhM1uQGM3sNCU7zzlPo7TVtFY/cfdjd52SuJdSGlKySdjRLqmaD931MMahP/nk'
    b'tSHG+b0Y2w8a8OoBqGVniIX56tF84jFpP1Rd7x1K/WZ33QTBSCGihB4EAtwNoRrv0uAQEM0y'
    b'k3VsSMtAwmwjaRGMT23laEII6RqMqHNM55M6mU/rdG9ac5cLSQtKbYVkdQapSnodwomkHwwn'
    b'26dDqvIdezXC0bJ34H4l7skmWj5K0DVsuzfi/qApU69Wumr2Ee/t74ZOxb5inzk+yACj0C8j'
    b'SzpoWQJpvzp+yN8O4M+Ex/A8JCxEeyJoNZ6Li83xHU/HjDkFkSU9nsQS9hrC+rPGdtSi8H7c'
    b'lBvHD066ftIvJwezbzFpP6LsDgBR2rrhDcUfxiTpq/vUJZMrVVcHyCRZrW6CTt19kFglTSRU'
    b'QKqupbuPkuCPP1eKuzLJa2GfGHaq8A7RvekJQN8A1rUGxIlNu4dI2s45se/y5OhgfnNvb3pt'
    b'Pp1cIZndXV69uFQkZWwkEkgydwckIwAGzIBUvBTMHJIxTPTuToiolIAAq5zgRkCbb1JQFlu2'
    b'bRJj6rK6/Rm6vamQE0GmgCogxBQoyt9H5WeSBGkKsGyNEdginSAyO415lq4eTvuj+eH8uVrr'
    b'SkA1YwdakruPwzguz5eLxelFXQ2lGpEkVJB9Skw0KzmnnmaIe+F9jys5p9vrvncJTsHooGBA'
    b'Zaiz5/G8Fmdni4W7LkHuYp2k/RMA/uz1p2+8DODOGrgKNi1HD/CEaSNFnQF4hcRbOdnDSv86'
    b'dz0Nci8M1J2ADCgTMBAtUFGABTRB7sUAwCw3pb1C92vMjIwNoEC17T9L5mamlAwAKBcEL+FJ'
    b'/JTcPwbhJwS9CuHHWpriL8BLhUN3NULGrRjn92JsHwQ4PRWvUyM55UZiYhxrraUtu6hFV/Qa'
    b'r4ccXx4gtS0xPQuQmgC4G2reZ+Mh/akQD9narxoD3R7Jq2a8QdJysmtGfljAYOSSxgqhlOqd'
    b'u89cqnKdu3wJbN3oFUAlNpHgJK8CuCahz5N805LdJFgFOWg9XAMkwphV6kNWX8pYJZyOY30M'
    b'TtU9eMKFGGT1YjHAjOauXpAls0oyvKOokpKAJEHxegai9Si2qu2sfSAEJpZsH8KkugNAnUz7'
    b'UwBoHkrqunyj7/Ktvfn01qRL09mkn+QuT5MZmweZIyivkOCwKhqHsRCY1OxWS61jrWdM6ULu'
    b'ApAUn2NIBwTZdanrpv0k9zlZShYOCGBjJ/Eo5CkzZgHaqtGgIVEDUE0yIy1Qkdg0Ia5XkoO0'
    b'oItxIMAqtqQohSQQ6WMwJIIJEvqUpiEpbtSg4FTtolzM0Wwidyel5s9vTleomAVYORklp0CS'
    b'cpcDsJSQzLjXqNrXAHmoj1FlOpXVavyWyaT7uyP4+F9aA9cPrkGrDzvNMwC+OebF4mSxQp/T'
    b'TUnXSD4i8UDCw5bemMRAYCJwCmzbVmIOQOu2NGXulLuBnNE4hcjqJQHxbZJtNpKZxZrhFrUN'
    b'KXcfyDF1+TFPvbr8vUb7bkGfCpXvEYCTpjqQonPXDxMg1bbzeO/1Rri4FlH+X9uYclobXm7B'
    b'6fNUbGd0tAb/NtXnceAogTEu/GETrfuGNjf7pKnTd+dSsjLhY9yoesLlwU1ScSb5q43869Nl'
    b'zFG262Y26XLqI3RnNBIgNhqMO6v7hMDUpQzBck7KyWTJcp+zpWTIyVjdVasTZv2C2DdQAGqV'
    b'kpEiUB1Kk5TOJuSr7n5fwFjX24vluHaTjv1iGA5q8X0BKTiXZOQS4Bj2jHMEEyXJx68lmIJJ'
    b'UUIBMfCxU25Vm2A7Rk3AebJ0mS94aCT7Ls+nfTdzaFyN9dKgrFKrgtkzkfBkyff3Jrh25WBy'
    b'sD/rISgmIUnUlJKRgLsKAA/bzgpEt7xYGaFJrV5Wy2FYLocHi+XwWjeb3PfqCVIONzgE9Bjr'
    b'wez64c1+NjmElEhm5tQTMBotvJD0dSNoETKSLJnJXTQDCHn1quY6q9cqVzWjkcYwsSiMxTlQ'
    b'hQ1xYXx0J7vZQwyU0CQ0b4O8fDWorgokJxFN2GmSqgvrLkDepREpAbEAuXuVtipdMaM1qFdT'
    b'ssluOE7OSQB+H4B/S67jB2/c/1BIFtcBXIv+BMJmGa8fBag92yQ+L3KyU3e9K1gO5K6WbcKM'
    b'7FzKTYmvwYwKoSlLOgjmAxcRlaIAGldhTJ9GcKoixekidemtyaT/2Oxw/n93OX8fiBOSOxJU'
    b'SE5ffizk9QDyDwRn3YcQXvro3sSbtV70VlPpdgSBJrwqQDEA60UCx03Bwj8l4Ddzk0t4l8Kn'
    b'nbgRN/9zJvxBbqLe7zZIybcenU2T8dvN7K/N2Z7NKV0C1l5KNuMGzIQYhIKyu0Aybdg4jXuz'
    b'SZrP+lSrO0nkbKxVhWR19+IAHi6Hw+JuRqq6GIuvCHk2G/e7dM+EcyZbuHQhlw212vlyWJyc'
    b'L88ulsOVWj0JSA3aazWMDMDWdNJhuRoLojXGwxmBKcijuLktbbGvr3/W5ZRzSpaTcb1NJBVe'
    b'MK3Gsjxdru6NpToAmXG8ce3wyu2bV68SUICBB1o97mZkKT5IColP1cwuwXA2jOXRpbtfgnmt'
    b'q9UwvuauT5yfL79zGMoZoMnBravVjAuQ+wCeoNmvs2RfK/crlvM8ij80niawloqY1DmI9kTS'
    b'aIS7FM+QjIv0Wl2Cp8fQZiZJNNrl6wasYrchR23d6a2TAIj9OBZWGi8VdTVKpSLsdLstzhdY'
    b'XSgVblbrpFuB9Lh/ZdPhjYOmqfpOkfD4aSSRAHjX5QLgPwbw/66lrNNYqOZhp3l/9MPoNzZa'
    b'S1RFDrqXxma5F6Bm0WOiYhqvGeedNBO1tc8WI+XSJKVEbJsIUEFPHIswVySXuUsvzg7m/1/X'
    b'5U9aTm8mswcg6usbbzc/+MQ1vQOsKQcAngvw/gtCyJk1TL5PtBTaDbMH4hw2dsJdxx8R5qls'
    b'wn8SN/Vq9LcI/D8AusgplAlvAPhBACcEHsbNvxrSiJnx8NlbV6+6fDIUZ3G3LieSVE5mIBOx'
    b'9Zw9HszJLF2+13WpJTtnMkNcqcxAAI9RbSw1F1fKZl5cDFeTslEkdW02GbpkvXwjUZTqvVOa'
    b'GmcE2CWre9Oea9Aqa/BarcbiAQTWGAFtDVZtWMYk4rVmJPYI9rTg6Amw7Lvczyd97rIxaAN8'
    b'84sieCpK/ZnR1m3aZ2B/f5ZvXjuadV3KAFirgyRSSp2kUF+kUqSwow0p0XLOEyPTWMoyQCMJ'
    b'WjHZ0sEHVf5xJz+d+vxY1L94dLYCsLhUoa8+c/MFANdJTstidd2SHZJ4GuQ+tmTqdNBq1C5M'
    b'URZfYaRmVGZmqGwUoJQsSYCZwYwmUNw0CxQISGjiu3aAigRAC6CK4wFrCogjIyXcJUgEGqW0'
    b'LRsrMXZh7omlTjXpHKQkbW0iJBWA7XGdFpfjAQ7VXeddl06iCvHDJnxiaOKrrjTpbH1TJfx2'
    b'vD4JG+aVhoroGKGyt5JKsy0xyUvqs0OwOhYh3o91Q7XW1HLEtZJIP+2HYTksZ/uz+xdni2Hd'
    b'HxfuTfPJCsbjBgjwiTsPiLdpXySQKfpxEwLx6fjN1oRLHMZ9UuOAehjHnwxQP2zJ/Row9/is'
    b'ZwJdo5cu40FcIXEV4PsALQEuSVxu75F4Pm0kiMl6O6VxJIiU7Jokr64jEtcB9C5lkjVuTiJp'
    b'BJpYRnalupnLzNjmwhkIS2YW0dx2OozsEh0gkgESFWXteGXWj5MuVymKeLtGGpVEA9Ff0poc'
    b'RbClJDw8vTj73Ov33hpKTWOp1urZjbFvHnMqkUxBpeFm9Eix11qLu5SqumwGGkkQQYMMQTGd'
    b'iJiw+WA+PZzPJnltVGffZZJEAIIEoFZ/3EjC1i1s09bnNJGEWl1OQEInaEryIJF1NYyni+Vq'
    b'eX6x+pa+y1ciUf31SKcaaXYNwK8n+fUAvjpPJ0dg1MUTTNAIsApYSV6Cc91DKpZXjeGaohEU'
    b'yQAdBlRsw8Oigg8RLT6H1tyFaPE1cWi3VpnQgp0s8r2bnKRoBInWhE13iNTjLlS5RhgDiCAJ'
    b'42PDvhQFbbd6eJUU3mrIXaddl14H8N2R6TG2MXYxyV6KiZfC0DyLCXoWqtKLTQ3Cg7Y6U5M5'
    b'0jUeuWWedIWk5NqPGELkvlPLm7VD7LestVqTeK+xlMFyGlerYcYuieQHYHwDxPc29qSuMW0k'
    b'AGokwuUayNrMDrz75lXh7VtqM0R646MQdtriIe25RLQ2tKkJCr/Regabe5f4x3/0f/7PWx0z'
    b'JIrrJG4APNp4wI2WbCDw0DbcTn3OaZpSmgBaERxozJJQXbeM7BiDFxtPVyZpQWvpipwnSA4i'
    b'ySMqO35cJEgy5ny9qNVW1bMAukSLZHaHuN/ni8O+P6MhJASIgBfXisQsmc1Tsj0jLSJqNYyl'
    b'3Ds+u3j1rYers8WAYL3smzJQFvtGMgRAhpXG0GXjtO847TJpTWx4OxqD6CqiBJWSaTqbYA1Y'
    b'7LoMQapVjvhkrdUtjCq+ATFutSIhqJwUoTnahi2sPVvL45OLZXUfa/VHOae7CHpjEH+C4IPJ'
    b'/uyv3r9++OsAvJfkUxL2AdnO4KmXPSTURsoUtv8kNdhChTgYBnaGmogIZSCjIQxYrfTDHUEL'
    b'0dTeRRJEvHJHuVjBx6I4KVqcEvcIkuCCqhcnSk124WDtumRd1ynnRMnr2dny3jiWFYBq2wgM'
    b'+rqdhjEe7v7g4GB+D8APRZXol9cq4arNCYzJNms8wtcj2v2DIVUtQz2a79iN2sbWGN1N+z0A'
    b'zwbLgUWx2NWWNFLwmCe2E4DpgCLlE2X73ca3aPYTND5kSj8qaCVpX9KtANdnQk1dBfgOQcv0'
    b'Rwm8Hvdm34jps9eOBIANwLCRPEt8/rQ3+juQJ6xdB8AlYP3J5mYxpImpkXsgeoK0ZEaymHGZ'
    b'Nq7SLueULagztpzaEEvVIYBJlLxKEQC3k67TGDVaVSG+RO6KeQ4H6qrUOlTnstQJAc26XFa1'
    b'9tnMr84miy4ZwuaTcrJJGHJdQswzWHVVyWVmyV26WA6Lz7x29821eljDZGUEMo0gGTX9kWIo'
    b'JyMTyW426abzST9PRppZO+EREcdMtiWgA43ockJe99l8ipSzEIZpxQRNyQAI7kJjUEHgQYTN'
    b'IOAK3nUprQ1y53fvHt9zhV0mgny7nAqAk9Sli9x3DyzZ/W46+VDq0nOpy0cxQOdNVoN2Br1t'
    b'p3+MkZYVlPEmwSD+g1q0CYAKqEFsYAika9CQkhpJS29bqKt9XYcRdTXIx1p3Su23oCWvPnqp'
    b'SxcuqvE8T3o98cTVp/s+9wCwWo3re/fo08vlcLdWnYzjeJ+kB3jlWv3xpLV1K6W++dRT178v'
    b'pNWHa8AasWnWAFYrQV2P7btDgroZsYlPxn0fmhiymQF0wBFgZMkiZIJJUocmAb6JIlEUgVCI'
    b'ncWSjUE13dHo4KZ59UQCadJXGB4JWMhVJFHSjECJ22ihjZ9t4ovtk9nsU12yu8nsihmvHE4n'
    b'05PF6uJwNolMA5QAq+MmrOf1SN8LVpd3tl0C1h0EtzTJwYwlSlPlsD1ZDEIlM+ScLG+qgmYj'
    b'EXYsgUCM017ArKGwxQ6fEwEBzaiW0DRB21hqoLr8fBh5MVYjgC5ZcYGznMqsyyOpAYF/zUyp'
    b'JGoVqpE9gPnjgEEyqFgjzHY5DJ94+c5rJ2eLV6rLQ7KZkhTBJEhR8n2azA7n0/76uh9EkQm6'
    b'C0ZaTDiENMbqUjIypYSUTF2fkbt1zxlt6JIZFRMfrbG91ArfWriB2IMkna0B9mKx8vr4HLFR'
    b'qIZNICAeMNniUpqznIbUpbPcd/spp6eY0h6J+Xalbq1AkuJlm9YTLV6otRaJ7buxEQG2SBIt'
    b'0t9agUphvmpaiJRxLwES7XEfi4aTc/fqFcGSKldBRIhHDNNQS10uFss3R8fdw6O9w2s3jp5a'
    b'b29io3YvSqmL+/dPfvziYvmZ4+Pzjy4Ww8sALuLPLSUdS7iQ9ADAYh0F/3bR77u85JPGQdPH'
    b'tsQ57wkp69qUfBgOLMY9v1hha5JYTfZmNyW9AOhpAHOvPgtEz+HdQ6i3hJSjWK4F2MmrG5Mh'
    b'BAl5rQDgedJVmj22d8JVjADczzM5SZt6A9NEds2ycirhc4DuCVgRSABtb9JZI00hft95ANTD'
    b'sF99Z6jC9SsBWC/HHx9ILEieR/JkNrKjrTuQUrKu7/M859R1OXeXrwOkAMIheNCAyKUkoQeC'
    b'vGsLTAKEXUHem/226jpKdbik49XYJ2IUgInZOQj2KY3JKAZISRoZNgsAK4cKgJFgpuEomR0x'
    b'HkbY0qxUX915ePKZN++fjtV9UtzDk44xdkIXZDftuyt7s8mTEbUcVACWFZM30TqHYjwZc96k'
    b'SeSuewxYliyudWughrvQgDdqdZXiijqEj1+7BCOxBtXh9HSxqO5V2kL86NJJzP5RwILrniZd'
    b'mu/N9ixZfhwB3iWkLr+LGzLBvZCy2pL13uwTaFi3dpaSeN5xHvAFi5iqWYKixVE2Z3GLdQJ2'
    b'YTKUYHhdt9VYxtW48k0Gw3l1nV2Cj+ToUjp0l9Val4vlcG+5Gn/g9u1r77527eC9lzUR1/1q'
    b'rb40s24Yxofn58tX1+r0T73xxv3fOQzlYZOp0JZ9fxi2qOGJo7elFmo1B36e/FIEkB0ekF/X'
    b'VF96PfJyj7FBBx09eb0ImJ6fnF/tJt3f2OX8m0BcCzAzgEYiB2MHQwwO5RmpYR9FsOOFqRiR'
    b'hxgOhngOjPMNkSUiJhBkCL1NbcqHEiLfUEVCmfWdA2jrDQ7xm34owkDe/IpIWH/sR/7npaAa'
    b'g/9YwBnIaqQEjMGthL2+6y7d6Tmn3HVpvZsnJNDYLAShtnp6LNuKF6VUR042N7NEIsXkD0lM'
    b'cR7cJYfkLoz3z5eHfbbFWD3PcloZOXIj7S0VNMKbsKc0ZdjOqmsVIQcgbWbcTFDfjPuyYTGp'
    b'LsHOF6vFmw9OT1alZEvp0Iwk2F2ewCg71XVpMp/0EwAmgGlzDtUw8LlcDMRMaaMCTqa9+kmH'
    b'ZBYZ9IxTBAnxcWkDVLWaGQO4PGxpeHh8fnJ8en7irhFS2a5usVAEUK0i6PHYzGqe9Pv7B/Mb'
    b'lm0v5cf6+xUmO4JxCjKkqwY4Yv8dqs6N3bar7rXyF41EHJPHmW0Cd4jb41CGcRgfDavh4SVY'
    b'efVFrX5SSz0vpVbVOqHwmEtsLOXB/v7cbt+++oH5fHr1sjy+JAeInK1f26/eLKWcPHx49qOv'
    b'vPLWb22L7EZHTMCTkBzOAYwBWl/MfcAR2UpfvyFqaH44VMYFgDuRMfIHzvYmDxCeycOrhzh5'
    b'eDIh+YGu7/6ibtL9tST347sOYmtojffNCtAbUgJpaMLaogVAtY0GoX0yakAPoBBZAPGUaiIr'
    b'yMoAKQJnJO+Hd/BTUVbsz0Zw6jvesqBEMAOYGLEXYr1V9/PiegXGV5NxRXIiYO/xTHd1tfo0'
    b'JevMrI8ioSagGplpzBBGEKNttT4NoYKMkWHQkZySsTqFVhcA5ACk6nYw6+skJwzVfdblLGiS'
    b'aE5iGpmhExKZQBRyALOwL0BxDJJyqAtuZp255K5yerFaL2aLs1LrmHLKltbdODGaddkgQYm0'
    b'bNaBNAsXWEOh0BiOCQ8gqqWim3SYz6bhFwUCiyAhuhieP5iROSfW6pIgM0sPHp0tzs4XS3dV'
    b'uXpIDpAI4RNROUXACgIi5OJq7nI+mPb7Bk1MYqpaSGUBZAMswwBusRb6IsEqsmv0xdJVb5cf'
    b'KQBLeltpjE617A1bhCPbYgsuaRCAYSinci1qrUuvXgOwVu7KdSxv1HVL63b9+sGv2NubXbdN'
    b'tEm3WpXT6Vqdjxg1X2+P7907/uGmKkvYLdGqO3tN6lU5+uIrJ88DmH59pKp9KDxf3sRTXQ27'
    b'15/aP189XIOWAFyC1TyCML+G5GW/HcwdBqGAsEaWdcTwYmgNDE8ud8kZ464adg3ZRKunc2fx'
    b'if1JvFYNS7+AGrZeEbgXQP9SANYjfIVadiFkhaZgC1TDgDs4pC5Hac7NRVrIjNXMpmZMcTeW'
    b'kSowyhUiJqpDY9yzMp9NFCtWBlBPz5dGoiOZI5hvy8JI0MZaazaupn3HOVkB1SZMogdQ16pS'
    b'R8DWkeJbl+nakJ5J9I+vj4wgP3Rj3WQFDmPRJbvh8dlidny+uCUgT7uczMicDAaCzLQIzmFs'
    b'Ay5INJY4AgFUhAB3J83Q5QzFSKOxtUVREtw9cmIMvo0XJY5Pzv3STuXVp4B6I2TJAqQAKcp5'
    b'EUnalo7KgAaAy0kyX7eObjNBg6teGO0UkG1AHmy4YTxyZQ1oo6QUOA8PLdAaG3ecGCi2bc0s'
    b'iZNauoiwbiG8B3FG2whCLXqpqepDQR2BKcmbmzQuLN310F0XQes8EOjMrLt58+jda7C6lTYG'
    b'bLs0sptZvgS2Wn1cb5cPH55+arkcHgHwkKBWMTZrW1Y9pKHx1/6KDwkAX/+pV/SFIr5vPn/7'
    b'qbsvvvkrw9D+7stj0S+a72Y8t+cjN/e3BiHhUxGE+vWSnh5Ww2Vw8I8ZLdN4M2xYTwhKwZ3l'
    b'cbPmXUqTPqWcDJa2BmUIbSO+2Lgqvg0FlEKl9BAGaiwsDuDHg8Xle5vk5a9I4x/+4f9p1dDK'
    b'UFCt0sJdgwMvG/nplAwk+3lO62bT9aZMp91h1+UrOaWDTZJqxOKARoIBQEWAs2V33OS0tXQs'
    b'jN5FZ0xONg/2oik6qdhv42G62KotNHn/+HwlKbs0J/k4ElmSrYbi60iAca0OXqzB7RzG6Ww2'
    b'vboJ1WCX+LibgdaEakdYFOhtIDYEBRhVdwjA0eEe5vMJckogt/Mc7g53gQTCABzfKzw6PsfF'
    b'YkCAlz6PeUhNB4Da4OYIYWmJyn2XUk4V5MSM55bzwC5Prc9HUXXagBh0gDU8VSEKMX6aiiTf'
    b'ekxJKqC6hZlAqO3uF5oIUniBA492f50kb23wwUxavXoZh7IYlqtH52eLu1595e4nXnUMaeHy'
    b'CiHlLs1feO72t83WqqAZTZJfAtU6POHWYrF6cMlmulYJ33jllTt/dLEYPhdq310AZwQepU1c'
    b'3Y26YXm4F4sDf/U3fWAPQWnTSBwOIXutN8pYnxmXwzeWYfxAGctzcj/wqqvYNErqSTiwLW8V'
    b'sM3V/Gjvf173/11C/vTrd0P9wx6Arwk1chaR4leb8ANsY5OAeZ/TdG/S78/6vE8yt+HvrV9E'
    b'8Zxbhxf5+Wt9RvN4XUg6Qx008oTkT3HDk/eHAbj2Z430Httou6+/HGDLOxG2saIhV6E45FW6'
    b'P7g/gJAn5KGkgzDcPkFgDqKXFFinqkh5kDREjbsRQIUwCFoBqAFYFUC3UwQiNfu7dM2nu2Wr'
    b'4/NXms8xjr0F4PD60d7Bo9NFrfKhVn+9VD929ycE5ZysW/eDy+7kRvqAOtMGrBIi/roBJoJw'
    b'xdaFLVi5UEqFwy/VQBgAlYriQkq2OddDhpfg1bf257U0hcUWqFpzdLRADrQDSluJhopN5EhO'
    b'3FFqqQZSZpQzHQFR514ySQLhcISdD4lmfagXDqECXtw3xlVgA1gSEhmc3iQlWJMOgjCYbLbc'
    b'wS3FnRI83lG8CzahpWLIVWpHuySJNCQk61KfD7v9WXLXUu5XJA0QIXklgPl8emPW5dtZ6uvo'
    b'g6CSifnF6eIRIin45Pjss7XUEzO6hCJpkZIt5tP+mdm0f369ff9k0r3rcmyY2ez6lcfi+zTG'
    b'1aPgTKO7d2UsB+NidasMZX+9P1f1XpLtLMgiodgONLuIEBGZmZN8yot/k2V78f3P3Hrgkn/6'
    b'tcfAdRRG/2BbxecCtMYY9+8jOSd5oYiFJBkq7tsiAkmwfdHmSe0WHm5+Q4l9xbM6JXn8uAPH'
    b'kQT+8sV8oqjItGXzANA3UpcJuBtz+WxGBp/ZFw9emY3tQLEJCu+ZgVccuOGuFYjl6Vjq9b1p'
    b'zslm7lubVG4Q20OdA8kUuWiriBMiAO3YB3b5dmpTYZfRl4jk7Oi7lBMPQuQOGovwZgRh25WD'
    b'Ge48OD0w4zwnW47SS6XUNIx1Ul3LlMy8ei/3aymnK9mYuLWlQy5wa5TZgpcHUDm8VJRSEOkp'
    b'8OWAi9WIsUuPQcxSgtcKmoFAgJej1IoaQNbYoXfz6bQb89FaFmQkQUeUbooVv2iUOwFZcgoE'
    b'SesxY4ANAYehi+DOpkw4EdJXssQegJq/2I4PGBgDuFE0jG1IqAAyNjIYBTGyo+3tFl9uvV1S'
    b'SFs1fFmKW5OUrE99Ngrz+MseUpmymc2mk2uUUlmNhcYkSSnnibuvj5TjxWK4X4ZycsmK4dJ8'
    b'GMvddT/ru9xfPdr7Ne9+9tb7AhT6ZmyeRgeAI0mspU7LcjxcXSyfLGPZl8saoHJ3dWRLmgfS'
    b'LMIcNIOUoo5fPyxWvxHAs9O96fcj2ack/ViM6z/V5CAehrT1dQCmKacbs73Zu9bbPZKWyMkc'
    b'6A2wWPwotdVC4sqa7IE43CrxppCsaSwILxCMxpRynCgab4K4KlAivg7CrwXwj03ICYFeQPxO'
    b'OIEOsR8sIqfaSLXDSvq+CvxEzNcfD9LAe18IvPgHf/h/XEJbZHWH3LdxULgv6U+7cFchhl6Z'
    b'XDoL05WUbHZ5syZ9vmJmE5KCNAg4yzntSzp21wMzXu4vatVdErx6Zf8ICORtfkxsbTezOzoa'
    b'nh40Zb+HADOLwLyHAYRsGA1Xn371bpeSXUlmVyThYjXcO7tYPVgOYx3Geipo/eynv2o27b86'
    b'kXsbu5cSFKBJkhFlH2EbrLWijgUqDkpAIHbKhsgrgkKaUkhPjcsUgtCacMXWx0/sNCFae74i'
    b'1gZE3WzpjfxVmUzWd5YnHfN8YpZTTxCJUDIz4e1sSWgvlUTYsgiqvRqpxk+mBLkUzwEecDOC'
    b'zGF4rwZ0EBIlhShmoBlDPW2CfD3iZGvApVMuucYo4hdGsFg5KEbkpfc5z7qc9kCJ4bGmWbfh'
    b'PNOqVl+sAev184vlS7X6W6X6G4vV8NHzxfB93/LVL2Ay6//u4GW/1fCydU2e3koSVb07f3T2'
    b'1OJscZPxbMK2Km2AyOTKlqxgW30o9GzXtrAqyQJClh4H+d6d7k9/tJ9N/hCA7xRw91OvvhXx'
    b'XNhvONWeBXCecvrq+f78L8hdfgrAkoD3xP5EOHB3A0AEaO3GujVNaI+ziShPqVqXrZHJWpnY'
    b'gS0NEwC5yFbwyAJGApPWJrYbSlOkEweOHXjMJRa1Q78bEUqCt2m5uE6BoKuNL3WhAqrBBXWo'
    b'TZSvQr1bjqWeVHdhrP1qOY4glpFIfGrkCchTEo8IDpZ4SDC7dP7s0zdS3HAHELo6juKheMN6'
    b'ykYCO21EytrYtJZheziOc+YA0OQFApGw+tq9R13gQ4KUSvXzsdZH4V3Js777tZO++02JHI2M'
    b'ElmAhBR8WfQwlJfqKKXKS0WIYmxzSlQdTiLmFbbgslUhW1UvGqkvyiq6m9eisIIQBjMJMIBi'
    b'rK8xv+WESUi+bhFikQRRAPH5jQpEbEOKaut7yYy5I7iVtIROkAzwUrUiUASMEqpLhUJnxlk2'
    b'a+2NRS5HoLRisYxg1iJAMkhuDoMzNER31Vp95fAiR4FEo3Vdlw4hFDgKAiwhVSOtVB8hjO5+'
    b'P+e0SmY9WadjTfsp2VzS3XDLf3NTMHfRVEIuEB4tTi6uXByf34QEMwMJeFUSwryyZdpAxTbF'
    b'S/Kq7LV2NKsppwB3dDRWS9bTuG9mYcPFBbGd/EMzgd8i+enLc/tp/2w36TLJ+5IGCqsKnQro'
    b'SYZaCpL8gt5cGoGUIj0DIY9TEM4FLQ1wiKMAgTwSMQVaSkZEVWZ07XcT2Gtf73qko0jwFNJx'
    b'4/i4HXxa90P9PcNOy0OtdIFVOhdgkOYhy8+NfAwM2ngwys3ZJIi7sHLXAtDMN4CxCKBYVeAk'
    b'/vj5+97zVH0bQrDvawo/KAbGJM7LzbaLbRjRkZuCAosWxGKbY79tDsBvb/KfdhtXxn0C7zPh'
    b'KqWOwDW5stzDVgO6O+VCGQsuLlao7kAEc8L4eNuWyqoOmG1llGaeN7LLbpMax9tO2wmmabER'
    b'WysPDHJt2aYIB5ngSoSo6l5LZZKRmdtkSRJq/rYaza+qEfTiOy0AlZuZRkyN6EkrguIvowO7'
    b'kjUxAAzwlOQB1ZbItP2dMUHi9yAAy7dLsFRJ0l1CoE8268bqZd3HsdRlxKaJm3CU3uGF5CxM'
    b'E8kllzREPb5lTnbl6sF8GjUKBOBR9BRevZsID2Fb9m11sTxYnq3Vv2HsARRJmcaqxu5oZiMg'
    b'ypUCuCQ5vXpHM0+Wt9Hh2yrWgHKfH032Zj+Q+vyjkTR89slX3xLCvNGAy2E/6X/jun9H7vIH'
    b'CA4k98zsmiUbTUoqPphEgwjA4ikqNISKcFhvH2witk8jmUCakyOITxBg5BlSG6fVNICJu+AX'
    b'g6hKCIkTu3Y8Rd9eTxL2IaG6anV/WtU/Ukv1UspnIexH1adFS9HM//O7/qvPuXDmUolVYhB0'
    b'RnDd8UMk/oCE1wCMzx7Mh8bgXRHnNzzXar7c14Cln2HgIRHtp15+S3hn2zwexjOFfA+kXwfX'
    b'13qtTxE8IGGS6NWxXKzWfUCtDhgQ0e9KIFLaJs+BYCM1v72fWPj8v5L46ZuaL5AgIprRubVp'
    b'YUOlOumArsvWdbJ+A60pGy8P9SlRBETuunEi3pdokTIwTgCUSU1Irjt6I+LnUoDiJBeAJrlm'
    b'l3u8IZ8HJQi790xQ+5JkUwsUXl21uK8IVofgrs6FPiTbzkNlDMfuAOIC5Cs+liiygE8CeDE4'
    b'zD8xnU9mAP4RAH/pNupd8DKOR8Ni2FudL/dqKROvnizZGLn8RrLS6JIoV26rQEliJD6Uxk4Y'
    b'qjQqSE9dPrVp9waNnwD5cYCfI3l/XQNzQCz+AVxj7vKz84P5X9xNug+lnG5vHgG79esEwCgp'
    b'Vz9K0iGksBPGjQ6TjkAHUURWASZiP6K/T4w8CC6tF0F8lsBNr7UH2FmyS4DcI3c9gK3ZXjt1'
    b'HpsyY6GNKD4TIXpVgI/uj9x1Wmp9tZb6CXfdgXTu7i/Kda+h4znl//pn/8u7Ah6GJLAXX3ge'
    b'N+uPB3f7dwEYn/88lY+9y8IvrGYhfj4JoB+M55S+TdU/5MV/g0Hvh5DHYeT5+VJeK0MZCucC'
    b'nQx90jYdQAtYX3LjTwvljRgkaRflo0ozEPQ3ygnMVq3rMrvk1iea5eCdgDJZZ13uwgYmAS1Q'
    b'CGxt7BShCqHQqI7kLFnOCBqzaNwuZFtwawvy9oD6QEKxATEpXsdN5I5gyjZmNQDVtdkaCZdU'
    b'3LuYMbmhwZakUeQKwAnJ1+pY7oe95IcBfDTin87XgNUB+DUA/g0IM681j6txb3F6cVSG0ru7'
    b'EQDNamNrdRI1JKpK4yj3Tq4M0sMrnGpjtxJAuAzJqk37hczuCPi0pBOX0vHpRVu/0BrWTnST'
    b'7snpfPqu3OVkySYkFyAeEFxM96ZGYWryPQoQsRIev18EFJEOIDWeOxPgITXNSFSC89As3gLw'
    b'Z8bV2Bl5RGIv5/QRM3sXyD4AxxvjRAtUEe4R+wSBtrfjTC7Aq/tyrH4ZcvKGV38k6ULSAsJS'
    b'0mkAtgA84P/0Z/6LP9AYuFIT6jCE1f53BHnf+NzzTwpfwdaitk4vhK9cSw1rwQDgaDDekuuc'
    b'0N87Xqx+82qxemq1GLMUTs/QhiLK3KDNsS4nJtIiXQe7yMOGTfNLafr8sqfUGMgi3dtJKq6x'
    b'iiDMwgdotC6TXZbNJmCyRGicmmmeU5/JUJ0F9whjIBi2JJm0iIIeA8iqnOZG9p2REatmRjAh'
    b'fnt4c+PVsDXGCwbiKoCEtgntbSNBvc2tiPgJeRj1S3UfQY4kpy4hysEnhxhSm1fJL89z4Vz+'
    b'OJ3ntRjP3xX2qos1UI0N59Kvk/SPj8vhqTVQXYYqTLx6eMHpaLzHcsGrJ5oVGn0DWhjDA+ch'
    b'daVSvauuJInxWzVmG92sGrk04z0Ar7t0Wc5svyH18+iKbpas76f9tSB7VMppReOnzewejXsA'
    b'fC2BtRxU2klSxttUd0ZTgn4K4JTkPQIfHVfDNZJ7OdlXXYIVySskD0FMInQBBPgFxrAan7dD'
    b'Tc5qHK6u1aWn9rImZa1+FsO6/Q1seL7GS8D6aBix0VQDOY9jr0Re0O8MMbr+DEGLn4eEXvjS'
    b'GvevHbytYnX60h3hp2+7kbx9nU8IYCb3v3BYDH/N4tHZVw+L1U3F6gjJQowP0TZ+jFFG8y6b'
    b'NSERTWssn++A2KVICI6/I5CIa6iAHCSQDJHRD1py9JnW58QuJwgj3Ffc0FIsJlDpyJyNkush'
    b'Aa+1nkQIxJJm9FpfW0+EhQPVSZA8NNKy8XYin4jYHzGYLwkwtqML7eDbrRqEVl0gyZ2E6U2P'
    b'F+G5LkEpHZ5lJkkW5XDoTYjO6L5wx1LQa2Uor4Qa+L+ECrg7BvMaqP7StVH9X6ql3roMVYhF'
    b'IEkhUQUvlyQD6QzhMP6iWbKVWXJh603uhlI7r54KgGJ00BwGB1hIDCQXAO4ulsPQ2G/VBHN7'
    b'vGb0TOOKG7B+i+QnSd61ZPdJPg6/uPnMzZP4TGrqDqRmmxsO+nc15J0rAg9B3CXoi/PFIQCY'
    b'2Qe7nH5lxF5eA3k1JbuK4P3fWk4BNgXIwnmMGja/gWRy+WBgV10ryZ1gWg7jvUtHniTtqJvW'
    b'vg6Awo+2FYubwMtwU+JRSFcFP7PG4Hw+ajx7vssP1IQlvBEP7Eqc28fxFQCcbWoh7rIwigfz'
    b'it0K0sSE4BzceCPl29JOq236Ra09gG+j2dflPveT/dmJ3A/G1TiXK1ZS1gZ7DIqRSmwFYBGJ'
    b'5O50bNkOKEbVv10oUvMZvb13kMROlSeIoCxxtJwLLBGZpCVGgOiAzjrllOGqcF8hUqiWq+FH'
    b'lrV+GmP9OIfxx+E6pXwRxUhLPKfxufc+7QDs1c+82gm29673PDUj+S7Cfh0T/xwDnhJ4FKFY'
    b'fcTbFAHJoK33yKWBoCFKdIXgZFF8IzX6bgtWiNcgSclHgJlgHylDJoDcsQBLqIm2blo5OMhM'
    b'Lh8lHL7dUlDH8qHl+fLPq7Umd5eZLQCkCISmolRcXMfYuHppycYIY0kuT2Y2AFg9LuIr+GOJ'
    b'SkgghyiYW824oHFJYAHwrLGrpoYJ1OP4qpm8kKsXNEE4p8KR9eMxV09f+eQrD599/7P18wgE'
    b'/ELLo8JQf/dHPtmBTNN3P9kB9aeGsfwQhW8V/N2SXqDjCog9CRlSH37DDGgKITjwUOWogJwW'
    b'KqnZKNIipzVHSYFbEq63leKja6cSvDKAr2rQN8ePHmI1Oo4s7IufoUS1D+A9TWrBV4dH5kZT'
    b'HXkW+zmkuR8LwLoJIhGsILpYzS2203DDzkj2IOeQFEynFYQRSIwIYAmj3M/KWN/wWt+S60UA'
    b'x+5+YeRXAfiQgHnqsk32OAXgaTG417qQu9zVxRQihCqi0uUAitx7mc246z0JYxADb8J/zy8o'
    b'T2lnu3saBcsZZlRKqQDwlFiQE5QSk1l1qZO7uWEmswQhwX1FaSLSWGuF6xDVH7DU+xAeQXoU'
    b'Eez7CG8VALz4qVdbi+WFAQ/mk/5uTKS75+6/cUJ8I8GJCzXqV50B8ATsBeIQYC+gQnBADJSv'
    b'EFil8JrBd4I+JEEkGOCUBDmJQoESUpWinCuxfT6gJ+Po0vEwjBdGFNfWI+jrnEDstOHoias/'
    b'RnIPwrOSzyBkAb1XPwx1O8rAbYDLUjq3ZGfuPpE7meyh4fHrPJlP/nQ+mL9+58HJv1jdD0gu'
    b'IGkYS0g3OG+K7XY7alAfW2vyZtEwSlzE9gTA6+HxvNFIr6d3f/iTny+CfPe1NcDAJgGckPbr'
    b'2eJbVet7fChfr+XwHQCnIHrVOo08EEIiaB6LrpolV5HeYSC3HmkmG2F0m01O0/7sHnP2kJgZ'
    b'3XautSLapUr4TwXVxTmisGcA1esBVvgZqoFPR2zFNwCwSPB8tknBGZubFtHznNLYgUxGzkFm'
    b'MqLqyY5EerwFUrjbE4l4WMwgUnyPSXJoI5IC0iVY1bV6EB6NY398HIeQ3hegmRBFISMq/dir'
    b'13E5XBtXg7y4IHkkeZ9LOgNwj6vx0JI9ReI6yUTSSWYarckG3o1kj52ANkJhGoNiLNAAmoE0'
    b'MpvMCIlMmd6njGwoclVJppw6EedeVb3UU3c3pNTD+BaMHYClaHswTjmMP4Hqn8VY/gzH8nHU'
    b'+gpcJ82K5h/+mvfoC/A/WUgELwR9ym/elrtqWnGP1+hCeu7jixqKZlrz3YqNS3A0raEqqjTz'
    b'kOSyE16BTXQ2mECcivwMgd8n4uMXD85eEnQi4QzA8sErb5W3+V0WMVj/eESTnyOuN3VZJJck'
    b'VzQsQY5mduLuh1Gp+RiEy7U32Zv+4fnh/P8g7QxEWdMD/V33Hp39Y8NYJhFfNDZ5s2hsNI+a'
    b'GEPfAZHcBFJfhMbxKObri2FrfhgL/TA5mK/a38Y37uunsecq+iyCZnNE1l9NB/O/SaV8laqe'
    b'3LyvHorcXSK8vrG3mwRvFsp8A0aS4qrcpv3KZpOlzfoVQEfM4R3JCg3P/FkG8B+iaW1JnXfA'
    b'yG4R1/J8ZKHn5mENzY0K/qcN5YxlO7CUrtA4AbZaEEOy6hAMDw0ViQEwMN6PgprxnQTEQAek'
    b'nJ6BlMpY78h9nwgSfvLgcitgGkmlNMNClozp8noMqcuLOhZ6qQ8kPfTqr8r9ITYJ1BnSDOAT'
    b'kj8hs5yIfdE2Yn6yazAaAW9SYBygB+hKQI6I8hphBAmEm5lvPsvNpJRSor2eEu9CepPGJVK+'
    b'ReOBuxZgvSfgdaR0JWhopoI9C4IwTuF6E2aO6o9Y6otwfxRgVXbCU3ebdgYUYwGaNZLC0JZ1'
    b'z2YjAI7VFwQuIpx9CXIvAKcS6uO3Wjzv3ISHGMBY3DQSzCALk6kJFu5LskUxPtp8J+8T+gMC'
    b'fuenXnzjbowFJ9EDmOjqfkSvo0RPlJ6xsX5HGsotuI8EroSjYFHcCwBn9XPPKdQz7IF8YGNB'
    b'ANvvBfDbVufLhy/eO14A2/Y/AfiuAMFvB3AQvW8M4ZPmvk3j3r4ZYPRWk652FiD1qYZcsALA'
    b'GqTauZRa8NeT13dj7YhoazCrIZ19CMCfA+BJtJVtyFOACuoSYFsijQbJGgqg7Z9QQA1qEYg4'
    b'LIPZCGlbpt9Xo9h3x6q+YEoVgO2AM3dseXYpYRE/TfsygcuiPwHgLwsJ6z0A5mFTqiBkZr0l'
    b'u2Y5XW5nQXif0DRJJbbhSmYKCWX3AXjzmuHdKvElFSTlGsbV8Nky1odyVwyQvegHIV11iJVP'
    b'CGCRHrhU5HrN3e9A+sNyffTuZ1/3G/sz3Ttb4MbBfAPG0hWShRbOM3Ai4iZp+4DOCFTQehBT'
    b'CKOAQyTuA3yK2Z4ibcpN+fWrlvJ1UAOF+zSbUbpX3S9ysoWZXcD9AYwzJ2+I3Pfq56XUB+5y'
    b'MB58smc16TPGcoQNr/tdLldv4GL1f3M5/ClIjz781e9efhmugBwD++uDJmWvWaUP2oriTXmr'
    b'Nnk9Y8P8OgoYGq/WBNAC2kZE9+66AyJBGgE4p/1rIj8k4obAfSfui7wK4Ly6f6pW/09efuWt'
    b'JKDG9awAHLbFb0NaEYCpjfXb0jB+E12HYYdRSIELkWcgalRprjI+pGCAcu3yb/c+/1+TR+dR'
    b'2xN4YBSA3TZpqkbfbCSsWaPdGKIqc/TDmD97AWinAWRvrQFq/DyVk1vgUvTP2wK0JnGPrgJ4'
    b'T8zVQ9ubPofqt1XqMyB7kPsksqpbgBC34YABQhH2IRCCy2B0VGVFjHBo99s0MqZU0pW9uzaf'
    b'XNAMAHL7u2xnoeQfe/m3fSVDFDqSN0j+5SC+leQHmWzPzKY0pgAmhI0q9hsjoVBBdA1FRsOX'
    b'hYSQuqLHJIgk2R0XapMszXE53BlX48MAwDHO6ZvBkXfUn00siHRHwCck/SEAf/qtT72Wck63'
    b'LhNRLdnBtcPZ7eOLoa4Trp+DtKTZHojOUnpa0gKuUxj34DoHVGh2HaSB3LOc3gUiQ6ggYdmu'
    b'MKWrcr+HYLVhsr26Gn6iDuVTkyv7Ly8fnByBnCLZbZFHZaz3S6mPJBUYjyBcbg81m9xuyifd'
    b'BzAP9/5/Ya++dW+t+jm+tMZ2G/fs2dheDUfJjZiMi5icFhKB4loUfd6oQ4rJmwGcNJO0xHVf'
    b'XPazSaeImfpLwgYLSSbgiqQ3xrH8z6+8ds8b6e+gUX1q7B/Ete6z+rNprNdYfUb5BIIhSsGD'
    b'DZMJWSGIkNUu3/Euf5fI3w7iT6zTdU6/QnNoN8dWIU2151jTBWD8aSyhRGxDZUwgegC3IHxN'
    b'Y765lg7mB6r+BImpSv2whENIeScMmlDEAhrRAqbUJmsoDLp0EoLRmdJo0/6cXb7clmQkI1Ex'
    b'ISQrssZ3kt979off6Rs8j0H0keFidQTyiMavp/G9BJ4AueVWIrcc60musNmFl0AhTZn1iCCz'
    b'qErTkrBs/EAuNZnDjt1UAMX7kZHyuFLyxeph0A4vAGREUUwCe+1qRVKJuCC5hHRM8l4ibhm5'
    b'ahgi9pvqvlXVM5OtAPByX+4nMO5bzisfSg3Jh9blrFL7sMv1AA2EYcuAwRIMCFXup6r1jTTp'
    b'f3T16PRCwgruZyL3nLw6TvsLV9wJ4LQJIXh3rNSTpmz6Jy7BCsCPf/XVA8fPvDG6NT3FFm9X'
    b'A6cR9dFsFftsJ+ium3sNWDnUl18J4JskfbpW/7BLV+X6Y9X9j7/2xv1bERwclYKAxgaUQ/WZ'
    b'QXraqj9npU4fA5Yrg5TQ5qFvk8rlyZae07mMpyDvB8vmbwHwR6tr9RUCrd12tZEQWwaT3CRt'
    b'64v4XtvGYL35IEetykMIB/H9TwR4vTcdzFYa69cAejbGu4U7Nu4T43nJ4i87djlGGN57ogB0'
    b'kAUQmPPFdG96L88nTuk6ABd55uRDCnsiqsKW944CVoiVfy2AbylDySAOSF72GDiYtrXpEDzb'
    b'cePaVrHloAq7TnW4BEbSFoy2boRUGsYBj8gBQ7vCSC60N08qQzkbh/EehNOog9gTuELiEDHh'
    b'gnH8XiarER0AhRt6NG5X7G6nnHiOrYfKqqYEEstidZ5nkyjSCqtDuWpdjtp2mLXGWBqX4Qo/'
    b'D6A+idi44+XD01d9b3ZeH52W6o56sAcAezy9qLo0uoZHNpweOaSej4XE8wcA/ME1WC2+UoG/'
    b'X3QKVpz/xcTjhWSFkJy+PrzNLum0Vn/3usOlP/Pq6/cWAK6HlHcYgBVqD4aw/3Shmn3YSr20'
    b'X00opSb6xLUFKxZPNspYZHwA8qLhqVpFtPx/BuCsuupXGLAYv+u5YDV9KhbbN8O2FaksOI9t'
    b'3Vkk2ASQelPuzXjnQQb4WCKNIN8nA/T37GD+fgzlmwBdkXAFrg7uUZ/OCew+eyrKWDkkCyO8'
    b's0sVpZI5rWjmkPL0cL7IfedGRgQ+liLvgVgKWIk8oYIO6SsAWL8RwFeVobyXxFHcjPcBvAmi'
    b'Z9iddvIRKWmMwLzqxeGusDuBkKxWp5nJLls2gnS5Q4rKNjlZo94lAGhsXU4iC9iW1RqWw6IM'
    b'5SGl/UhLgBEdpNoEiELSGyDPDehJGMAloItE5oYPnLsrVgTVNVHLIY1Fue7o3eeRSkrQUS9g'
    b'dkxjF0BjIR195nw1fry4f7q4jsIuEpIhTmOy7sUKeaspoX4/Ch/8njVYPcLPTiMAfYlBwmwA'
    b'ardlAM8FEN8P0PmMu16o1c9ffOVODXWvpWOZhlQwi88PcY8+AGBi1a+w1J4h1Yu8ALkKe9Uj'
    b'ma07Q2WFb+mNIs8veKt+P4CxuoSvTGP0HMGeTzVxjSl+37MRj/VjTViKABw0C9hhvK6hihNR'
    b'cZqPnQVhtJcMbXgFMe9uHL1frmsg30VgX9IRpH2AM8p7CFmSQeFJJEcaHQxanWgJzKlLY0rW'
    b'kdxbb7csFxHasgQRtQ/5AMSDkOD6dxKwCKAL1/BfVYZyG8QewWsgbkC4IQjQjoGcMEim6lkC'
    b'a6lWS0njcjQaRVKWkgDZ5dayQVUud7iEbtKV1GXausU3GwEHiZCsGMekkNzKMF7UUlde/diI'
    b'fQMjQwWRuw4GNa6C5uQNA28FseECYUsJQPBGXTG0bBOkNXlQIUVhHucJUtcQordlGIRAXBIV'
    b'tIHGz4F8OfiCfsuD8+VdATfC+/p0Q81TYrIehmruAGbh+v4MgD94uX3+9rUBvzDbNEIpAOBB'
    b'TL5XJenTn3vjqQDvd8U9OIh+GFLJXjMJEe9NKF1j9at0mYxF5BhGYRdZGjWrxv0dmzp896Nw'
    b'6P9eXSf4yrcewK8OMr9bofL30XPwqn9/UC8tm5JuBwFyB43kWeO9AAcMTe4neHyOGKdDa//L'
    b't65Mg258k48rPAUiM7iywqufonRgqPesPXDDgCkve6THUQrnGABiJFgJnIc6fkryzciDXIHU'
    b'Ow1YEwB/AYC/2au/H9KZAMXxp4lwQ5OSy+ROr/4YpMblQLnom2MgKdplD8CSRCMkKPYrSaQu'
    b'ByWzsiWzbtJ3lk1RynvcRlEjctGE6rWeefXVuBzvSDIG2HADsIdGLAjcIXBGggSvA5gZgcbF'
    b'PMbqdth4e8ZGrYuMfDwMG2KO95oJowS1YQLxCmw9PoXkQ5j9GIk/APL/BfDg/vmSIa6/L66j'
    b'Tbe4FZP2IFbe1wJgfwci2RcAbh7O9c6pKl982bC9+VQ/wzzQ/SaI0j+19tTGQvChmMhPNx7f'
    b'o4aRdtaq7E0YxxGkKwSmwbifEBOtmch1hzZ41YQZ/ASA/7a6XsZXvh1GNZ4PhMp2IyQnNTbK'
    b'e7GdNerhaROkHVJWmCFCYmy21ggVI5rWaBGTlOyqgJuQbscxRcqRR8rRioQyeEhgRmifpCwC'
    b'hCnMQMiI80TzEBbGsIkNMfaPQSxEdgCO3ynAYoinfx6AbwXwAa8eCA5HBKG5b6KH61hVhhG1'
    b'VvpY6FXwWmFmYrIIGww7uRmjoi0gYQNitgGvKm7Ol1JO6udTzzkxfBFF7mPILLWNZ3PXSu4X'
    b'ZShvevUxEj/3GYR0RriAj3FjM8qdcR4r9jImy/U2pqxRQ32nsEYJoCggrVnl826CapOS0Q6W'
    b'FclHAE5g/AMAP0byT/k4vvZwrB5i/vPxvRVAeMbwdOPxVEhW3xuqwjkAfQlgxc8DGr4TvoLm'
    b'+nP0EtsAMaSGy6ysgavgy2tcg5R2ryl+87uev3nla168++jGraO9J911MFa/IWni0l517bv7'
    b'oQAaueK6x+VxMZTaVmRqJvEJIl1mf9pnCf35alCccy9YH/6L6noLX/k2A/AXh/3u6aYEvjcL'
    b'6UUDsKcNxbhH3wMwj94hgK4BJ2/OVyNVskkb2ot5cMWMk+A6EwA38oFtnFSP530IKjl6CibS'
    b'0cglEXUFQDeiM3BpRgdxTvKC5AJkBLSj/kwBi6E3f0MYAJ9HiOG11H2Sh5Lvq+qgjGU6rsZc'
    b'h0J3p6pTEmgGM2pbjrz6VrFrywMrStXkPrvcEfS33k36mro0WE4DSSIi4hG12sKLwdCBk9xL'
    b'pOgMl2EAdSwn2+ozxBmBFcFHAH6cwH0Qs4nxiRgYFwBuNy58a4zkJzEAarsyR78SgLUfq2Fu'
    b'3OzeBAyS3FYMOgF4GmrPXRA/oerfGZQ/F2vAUoDok2FQH+M7n21W3jmA10MC+B4AD79IsGLT'
    b'rfk7R7HN8d1xLIJvw0YSQLpEqFKhtgzx3k897iGVNBIrXn/zgfAza4xruxkS5tPP3Ti6tSr1'
    b'fdV1VZKN1W+OpT4lIOVk9yc5vZrMTkisJFDQBIJcml92AjUnu5fMjgWkUv06pDxUvy2py8ke'
    b'zvvux9bg+N8ZOexIl7VNYD5erNiCehPegQYwvOkC4J9oyuQHSPzKsBUfhjS9v5M0jWhjdN9R'
    b'95aNd9vjHDXxaX37upWcm+9KzbhN2cwaQEKofBOtuzE8mMKMxEVM786FmsgliSnBFYnBwCEZ'
    b'T5PZg5TsTQYYxziZ/0wAiwCeCKB6X/QXIEwEQS4OF6t+XI1P17Hc8FKnLiVIsJS28RqSCBeF'
    b'oHFrqhfQTNKWCliWzFNONXXZU5cYYOcCiiVzkh0EBL2zoDCIN5GyXn0ZxaOqV1/VUldNUOMZ'
    b'gTcDJD5B4sdnya7Hb4t0IrwQYLDrEUuNTaB9wENMZoVeP40JnZqJ3zeDoAb4PYhBdQbpMwD+'
    b'WEhIP7QGqzE+d6tRB8f47qdisu4DOA3p6icBvPjTAFVuBuQ8nm2J7/mHwzZ51KzkqfF+Wuyj'
    b'sfOwoa5m9Elz3z4Z9fh+X0wgvfHim3ob0GybNO2Fn77xIy882Wnzt6+5693V9bxLz7n8VnVd'
    b'WfcDl+YkzMgFwdcEFQn7gIoLXXXvJVmpQp/T3S7bXSNrdc1OFqsPR+jJctql1/Yn/U/Q2DW5'
    b'dJ2khwJOIJwIGAANAAYJKwE9oAxt8ydr68lrFrxHAM7XoLVCeEgjBu1XNergpLFhIRoDBL0Z'
    b'l8sm5tAb58R+I423pedrO5bbkInoloyC0DqPcnSQcAYwx2dhpAEYGePGyBWBIdJZFo+Byng3'
    b'ijfDyETAYv69O78DtoTHKw2EB+7+oTqWZ8bleGW97Wup5tU7ScnImpJJ2LKtUO6Ui9oWIhVJ'
    b'KoydIBRFREEIJGnyjfqY+7wio0KPoQToLbd1EEkjg2M7SMe8+rmkhaov3f3Mq4cUgLPGQ3e9'
    b'mXhDM+nfivcvwtB9I0AuA+hi4BzHtjaJ3AUIkVhykGg8iZMGUCvJlwA9BLCQ61UADwN8FH//'
    b'pTVYDYjWDK6zRhp6FKrCncvzw6t494uQqgqAo1A1/gYAYwDrEwjAi9+8jGufxudSIy1MmmOK'
    b'89V4UhcIKTS+92G8XqH1JMb9jD5r1JZ7XA6LOP+ocWJ4bK8G2OvsYnWYkt1MxvfklCZ9l95F'
    b'4ErLBiDhZKy1j6DTW0HMaFEvMjjGtmD+DNcdQO/SJCfrxlJnArou2fM5p29g2HGaGp/nEI61'
    b'GRdD/J0gqMMhQBflEs4lLaPQS2mkmGXYHz/zwadvLLBxQ15bjmVysRymVVKtbgIY381I2DZA'
    b'GWAAibgaq5pcWYttH71reLMY29wcHxu65otGGrREJhB7AvahLegVQA3jBCtjnDd5pArjehVQ'
    b'CS5FyCFRnFCoBnURXRCOApC/57v/azTN3iZjurxNHhJBTvO0/8iVF578teNq/NVe/clxOVz3'
    b'6n0tZQ+CgawM1gQICVCgMBAGdpOLJLE1ssdWW29ZA2Zmnro09vPJKne50CyD6AnmUAWNBBV2'
    b'K98W2/T7qv4ZAOcCHl2CgdxfaoIXz2PyZACrUKOOCdg8W1vnDTFRnmuM7GNjyLyI95cN+0SK'
    b'PmNK8T24DT7+jutxzgMIHwfwpmp9MySnOw2ILAD8ULiqT0Md7GOFdABqxPNn4+8dI4j812Dl'
    b'XwJt9FOx6n519OtxPc81K783UmYXPZwJuNeMmXmcf7UB2Bz3aQwp8t+MuLALCOmNl97sm3t8'
    b'a8fOoviub0DYbkR6LCqHCLruK5tinn0ymyfjlZwSD/Ynx0ZzkqXPeZkSa1yQjaVOqisBwlhl'
    b'gjqKZonszHLO1sb1pShmUgEa2SQxC23wsiFQMe5Xjok1KOiTBQwQRkGr4nro0kDgtImN2gua'
    b'pY/Hs7ax+v5qLF91sRqfLO7mruxSJ8Fcj/cnkCghxQUruld3ALDdfMNWOt5JvK47C9oy5srd'
    b'iVml8Zqkx1kNArbsFoC88WgGbxiM8bdJXBAoJKvx8XYw8OLxlrhIZiIxSWQx8hDAMwhtIwCr'
    b'SU1Z9ybQbhEr+2ljZLsF8pBd+kjqu29gTjcEXpG0R3KU1CEEqOAIiu9n2khiMkkM0DKQbsZC'
    b'MwdEKUAzzonGbtqvulk/5C5f0GyAtJK0lPRIruNa/VOQXqXZIPdPy/W6oGMIS8TKtWMMr9G3'
    b'bS+bPk8A5G51aY++G7kNtCpia1iP19Z38d42D48BTksfxjZ1RO2CEcdLgFWrMqk5vwuw8QDg'
    b'xZfoCWSA3jPBwHAtrjHHsbNQjyP/smlxX5qEXHubKPhJgBUhXAh6DcKPuPuLtfob99+4fwjg'
    b'g1u1ecdYu2PwBchJnDMBFDxX8D5nBrtHV0rdH8c6HceSAGg2nVxcv3bwaDbtS6yJPpSaPGyd'
    b'7kokjdi09Xk1m5kIVncjiGTmBITQY4yGZKQZYUQ7xmx070tVJ8KMHDtyICCPzNhgJ1y4dCrp'
    b'QoDvFGIBY0EiwCqlWj2vVdK9Ur0P0kJKoqQaVcgp4AKSK4zlEtJYa26ecYrO+DttY2x3qY+X'
    b'iTyBUAnNYZwTEBQSHSkIgSWMsCAkEqJQaKxB/l8BJZLnJE9IJiNlxkVoTaMRUyOvk9z+fgD7'
    b'l4A1DfS+0qg6ewDYlMh+LozNBjIj2Rw5P0UjmVIPhrGNrAhkji4BpNEk5IiPQltaT0I8YNnW'
    b'fBW13SzZae67T6Y+fz/NfjxKp5/ECraQFGIzFgGq9xETJhhJiZ2mk3PhS2gBMvoK5onVAKrd'
    b'wYIApy+hNTa7Lz9sgQhQiH4j7JTfFsD/fPN3WvWhBfHpznsWvUBA9Uq54K6k6q/WWj9VxtKd'
    b'r0toNc6MefTc/CYiSoCBUISLTAjsAdqDcODVu5SN41Ami8UwK6Wau5MkzC67aTLJ49HVw1XX'
    b'ZQgK/KCLAsUkAIoxOJlPbbI3zbVUhMcaBHC5DUJnWTJYUFQTUMvuX8ZickfELMNIJUBQ0FJr'
    b'0yAMETytdhES0Ck80QRKsLhqsRqwWI2du8wlg0SXEJpJNeMSwErCPUCDACyHkkKy7ZvFbhJz'
    b'yJqFpgtMyI1KbgRg7qCLJs90JQIBQqCMbCRhIiLcd50BQZ8yIgp4wKyY2WDGgWbVkiUjJ0xW'
    b'jJwaOUdI75eA9U8HKO0DOGwGSWnjThpV8CpSuomcbiPZzIw73HMygHG+KoBiOS0ldLEqRfzR'
    b'Nn1lRfJEkXQK8ryb9t/dz/o/vt5+p5ndv/vim+cAfH5lXzt5UO1rb0BKAJjGIvxy+3KaIWK4'
    b'mhSXJ2J8/NpY3NgUGd1vVEAE6NTGDjOJ8fSGJPfq9A1/99NlLJ8bh7K6OL0Ib9c2I2DWfH9t'
    b'GFdptJSMMjOQ7OXK7prUWrthGJO7OAwlS2KXE0iaGdHlDBqQUtLe/sy7PqO6KElCoKIE98vu'
    b'cEGzg3nqJl2sXAFEZCN/M7CTSqQbIBrDuCzU4iY5JLaUaFElYythSZJ2GUdinwIYB1yBcath'
    b'xGosZAgJBCoUxnLJhlKzA4rX90ieClicL4fWGH+lid1r1bjW3NA1nO/IpUyseqa2ixDJuO72'
    b'2mNLwRS/tynt5iRrU4S2IIqokAA3GS3JupRyl2nk6yGU+CVg/dZmoMwR4mLrJm5AYAES6PJ1'
    b'GPcAJhpzeL8ySGonk5+RuyXJU5cd5IIbV+bCXVcgTWg8C87t7wfwo2Gvea1BZzVg1ahsX14R'
    b'mtv7M+EXZ8uItiDKOxQi0KZosKFGWe4Url01nqZJY2S/aOKxDsKhcD0oSSar5fDhcTW+5/zk'
    b'/P0Ib1XznUPsFxLXAE42Qo6lnIxmTLXUXoJJSu6eSnEawZQSjg7m7LsMs5CIBNTqG4nICJop'
    b'GAEZ5c5R3Omux+cNxbEYR7ZKeFte6G2akhlSl5DyuicDBeackJPBJdCFshqU+4zcd0g5CxE5'
    b'iJ1GwNG0uFiOpfJssUKpToJqy74h7KqhDtYwHp1uPc/kkkA9Oz4HJt0l6nXYAHK/6y1uYtu6'
    b'MLBTq/Eg1zrfpqo1tyaAnHGpRLQAdA8NyzcvA7oINoVGgqmSZLKSu/ywm3THNDtmsHhcAtb3'
    b'txStTfc4jkYnH0A6ctokCKd0FeQERBcsDBbX540ZsgB6A8ADmN0LY+nVxuj6U0Fw9icAvNLE'
    b'jSC2fPczN4Vfbl+o7UWE99eGR++zAO4d7U0rfu4bG9XC3lqsEgBft72jlJ4s1a+NY3nypdfv'
    b'fT2ADyGkfBKZYDWzZEa77DmlKaDeyJ7EhNoG7ZoEbjDIcAlSXYAGY+K0JK8pmWJlf9zV2EtD'
    b'rYIkuYSxVFQBq1J4thhRa4XH9yi+tLV2mplA0LgBRFSnu8NXA8aLFRKJBKDfm2L/+qG66QRk'
    b'fEHMWsZ+FO8TAQfk7gApjcVxsRqtVg/ONohAjSIdru2FAdU1CeFtpSbeSlJXi4eg97jtMn6y'
    b'YVrw4LavufqkK3XPGt6qJu6hgnCCjF8SGPCFJAmxKdMiGKuZDZbSMq3BKuf8chTruCCAS8D6'
    b'P8OwOo1BVZsLTo3B07c2BLMJiIycrsBsDjK1huJ4kjUM3sdRxHIO8iwA6Sej//HYrqKjlZwe'
    b'vvKW8Au3tcZmNPvcSQv5QrFHfDteo49821fFPnJ4Bf+JUN0+HVVhXmudCmvg0ldYfUxNBP8k'
    b'JKWj6Nfi2p6M4xaTpMp9JqBbrsb5mkr4Q6vVeCTJYOwMHHO2M7l6M3YE+2ScRs5aNmMikNoo'
    b'IcVcZEyVWOwZCRNo7FhIZmAcC0/0FoAC2OI8QgBWQ8G90ws8lmyKA6G+BOAB4E7FPUcdC1Ud'
    b'ciGuCXSHFY/sjIxuNlE36z2lBHeHZYNcjGuTXC73SmBUEGhXyUYgFQ87V9iCFaENDhFxCwgo'
    b'SPNK5PYVALUUTwSsgVsKUhBKEpA3t0VB3t111Wd9rROwrXLUzHqgxi2lGhWR0bGVrBCIFo1h'
    b'kDcbLafz1Hevpy4tSS4Ijpsir3jvJWD9kRhU0ya4TNEZPe24QS9Ajkh2BHIGsyswm8Wfrwho'
    b'3UZ8EytU/17k9D0hTb3VGMi5Gzbhpxf6EozWNxATIb7zwfGjM/08AKsubATzxq0/QVQiaqhJ'
    b'vKHMnTXsnPOY6CeNI6NDkAnO5pMnD472P3J4tP9rj67u9wBeDYn1RyKK/CdbYPwKgdZNAH93'
    b'SHeThiivbxa7VbxGk7fWLnDTsdSry+WA88Wqk4RkViU9MONqHOtBTpyT1pPYGvA3gMUU0kwz'
    b'7rc6BrfeaAlSKw4QZtyobM1nzQgjPy9H9NliwN2TC5wvhyj3FSfFJmIEEWlkQFxLMGmBLrBU'
    b'wB1GAhYgmgx52kvu8R0OxOwOrniHtE37khmQDEoJShbIGZdDOoIeZzfqPsKMRgAotTaeaG7v'
    b'a5xt2G2BXElK/TAapc9DxxE2OTIOwYRAUKMb6c3TEghRgRjEkKeTV/KkOyV5DrALyqdnST5i'
    b'qIT/DYBbzcRZRlcTVLZTgSM8c4SDNkOy9yKn57ChAI5CAOjg/iaq/yHU+nshfRrz2ZsBUgCg'
    b'LwRMjeEVzcQ/i+MvRCzOB0LCiJwu/PbI63r55wK02qonDb3L1eh9Q21y1nBU/erYxvtQE0bS'
    b'AXjYfK6b7027wyv7t+Z7sxt9n3uXHnj1h5ZsfnC49zgZN9Tr72z4wPUVkLQYgPqPxrMIMMak'
    b'saWNjecpxe9cNedM4n2XtC/JJWEciq/G8iaAQuCJUurEjJ2tG4lktExuAnNJmiDg7ecPpKaw'
    b'YbSQVKIDLpHYSkxAC2zEFnjchYfnS9w/PscwFpTqAIAAmsdbxPe0di5uEAKUYLWpvhXnRRJ/'
    b'yDXtD4HQ0jG132kmkfBsppwpoq10gtba1vKzyX0loALMgsY41wGaIAbEWYM/Uc0IRmJirq4v'
    b'taMUYpkooG3SLt6Tihcy8rJXEHFayGnkwpIN3aR/vVsDFoB9AS8Y8AikM4gYLwHrEAB3c4Ma'
    b'DqGDGJhdY5QP/mrc2dzt9HVI/CqQT8D1OUgvofpPAjh+DFTSQwAV+3MBwE8DVCkG8nPBL/2+'
    b'+PtPxN9+PgB20hh1EyKgLVJY/mcAyzVoOb4yjbsAFfdp3vBwz2O7t8PgkOO9m9H3dlRBb6LG'
    b'HSFOTGd9f3C0d2Vvfz7JOVnuUnbXSLJIWrr7g5RMayBTlGH/twHcCaCr77CU1ccz+E3B5b4f'
    b'ktxqJ51j1aQNnTVS/LUmX61vJNIyDGO+WKzm7nqYjBcp2Y1x3AKWbYNPpYmEngbJxQaA2EyS'
    b'nckTbrbm/M0xp5EwWsM7F2AFxmccLj1WDe8+OtOllFWHwgAptCODO+SqnbtQHZCouCIJ23OZ'
    b'DJYMNEIN6zAhxUnibiXsZAIZLjdAZvSt3kmo0VFjt5IoIMdaa5IwQhoEXPYCqQqwRvLNAjKE'
    b'KmlEcLmZNJ24T1JjxuNWANt2CpAFhXlcFUTKiGIpDSQR68FA2gMSoLGbTCfnKadboYVEEDH2'
    b'YyFeRuDolzBB2xYA1Kg/Y0twtltl723VvZjwTUjFUQQPfkcTE3YlBvnejvTl8UNeDbvN/YhS'
    b'v/xRF18BKYutdNCEgtwG8O7mGm/GOXsxSbuWmK91/+7ch0kbHJlymk9nk8nh0d7eZNr3XZdT'
    b'F1GRNFotLkm2biCxdNebKRnW5z4MUrn/pSkH9U7Zs3LQm/zaqATzRDyzefP8F7H1RiIvDXVJ'
    b'in3Gc+QmZ881DOVgGMYjSeeSPmVmexcXy8O+y9dSsp6EpZRaKS5JygSC4JEKICKI7U7st2pj'
    b'SzZuJNE2toUFNl5DLIeC0/OlTs4WXCxW0K7u2BjhI63HE+nZVVCruWRePTEZIjYMMIqMP69Q'
    b'I8kAgi1ytU0Et28qzhAoGRFiouILBDI6qoHnKdmd1XKYF9dSwGA53QgqpoKwgYn0xiYViwA7'
    b'd79OwA3I0OM2EqgGekCWADlDwkuWHAx7JQkk8/mkezCb9OchYNwh8cjAZ0k8SXJm5BsEbkfG'
    b'xSSYVMvjHlVzOgBqehsDgptXDoSd9kXwHukL2qLCaxQXdSMkqeebyOpnYhJcadIU+iax9kFT'
    b'PXrRqLA1wOu/B/CZdxiwCGDakJ9NQ/K7Grl7z8XrNtixBzED2ERrSwB7buu6hasXWJKoAJBS'
    b'ytNZv7d/ML++Bp9J1+d1SzlFK6V4gBbdFcND7q7jDR2NlrP59G6A+P8Y1DLlx37wE7t2jc9X'
    b'/knt7/6OX/P12klN+tsA/Ip4Tm0FYjTANG3sbuauJOg0zlF0A9iDuD4MY7o4W5wKOPLqK6T0'
    b'g+719bPTxYf359OnJ5PuqhknySyTNBCQRIJBckhvlaJk1rieQEhJoXsGO0dBCALuCskPWwkI'
    b'zZeV6hhL1cVywIPjc65WQ9jFAvAaYkoSIulGetxQt1KhWhPN4ArBg0CknAXOaQuhWzExLr8B'
    b'qhZHFEGjW6ht09oYoGPGSrMViBXJE4DHy4vlpEAus4EpTZFsFg6LqQBzorrgbdUqSUOtfh7P'
    b'9ZqkjkCOIsUG0CSJ4Sl019Alc5qJwKmZeU7WTfv86Ghv9j052Z3wCt8GMAOxNNoLMf8TpDdi'
    b'sWUrZV0C1l8EYNasfvsBAg8aSelefPjiekpLfHktNZP8hSZ6ftZIWE/E+083qmEOYHoQWwdQ'
    b'EPUDm3MeNef9EQC/dQ1YF++wN2wW13et8YS9H8QHCT4raN7QJmeAPYmeZjnIBUmSqUsJJPOk'
    b'S169WjKQ9G6SU2dp3Yx9l/pkdrmfk5kxPLRR6YfhzQq7CVBKVQzS+7XWl7suX53OJksA/xaA'
    b'31NL3R+G8flP/uSLtk0WDltZbCtCbm4WhzG2iBVx9k3f/MFDkt8B4IVa/RbJad/nSjIjbEok'
    b'Vd3HQIxFGF1b3qzapPfMBO0tL1bTYSgdqN6ruvOL5WtnZ4sX9/dmTx5d2XsqmU3NgojRlUB6'
    b'qCpjFDVQAEEQv7ELUsZBQulzSquhROTlhjVh2uUqoId0HURHMLlk7jISJsEU6mCtrsVywNnF'
    b'0hdny86rwprvJoEGiARSSm5mqqWY18qUs6dJpzSboLpbWaxYVoNF0n8gwtuvFyRlyapcCNNA'
    b'BaHALNbqOUAbDGkqtMGtAAbCU0oLEAu5lpIwrMZJBY6r8ZGRB07mQOeJA8WBToALyAKSS3Jp'
    b'lCucRJoKmAWsRo1QFQnHIUiM8Rts2ndd2PU8J7PZpH9w42j/tZSs5YV7HsmeYU5zJFtAWmis'
    b'J6ieAFwD+QjEI0lvXQLW/9JGJsf+YstUGJVmI7bnJyNWank9JX2Jk/3pUPU+0DAepqZ2Xd9U'
    b'efnqhvt8BMDGVjVvXOhnjbqxjO+5G9xP/83T3/SBE7xzjT/5x38oQ5GMS/Rm9hyAb5HwfhLv'
    b'yZPuyuxw78Z6O8l916c+d+uA1z25HAC6Sde5u+ShNhjp1R0x0zczEWZhuWhsso3NI+5KSOBe'
    b'XbXUKvexlLpKKY25S+7VH6wB609ERZfXz88unr/31qO/28zOHtx7FM8DZzs2yjG2E0Rr7vsp'
    b'gO7Klf1zkrPw+JiA/YP9+d353vQCwLzvspBoOacnzGySkkXQKBjdIBi4jWK3Wtw3QdS1u+wX'
    b'yxWWi2G9W0uXE+azae6yZTNWkheSzIUEoHIziVfhLbQAKgqYxG07QySzSziTtKq1FgBZgq4d'
    b'zMpYfc/IqwCmAjoIOYSxvpZyvZY6G4ZSVquxLC6WNqzGBNqyDONerd7R6CQh90RA3aQr3bQf'
    b'utm0VEOuQgYkr27jYtXXsaAuR1OtW4QCKTPzDQZ6sJaEtgeIpJNwhAG7rR4Vr0FCb5PbWWg2'
    b'EDhz6RSuuv4NAPDWEnjNgKdJXHdQiOcBYuZgcWLhwF4F6NIA4S0Ab0laNEKINdWyuqbA6wzA'
    b'0eHebObu+8FQOp9Pe10/2v+MkUHljasADtnnHjl1IGcgOhQXpGNIpyCryGOXn18C1p9omS+3'
    b'EcoBAo1d4iSMuR9DuNA//K4nhi8CqCZyPV1K/Ugt9Xl3v+2uqaQEYJDrKhixOa6jUsp75dpz'
    b'VwAaSnyPSAzuGuNaThoPWG6MyyfBG/VfrwHrzjssYdlP/rEfqgD2aPZk7vP7ST4P4qqZPdvN'
    b'Ju8/un3tg3nSTVNOfesyCjsAJClAiBKQJBhgBiDF77zcShBazQBszS6xFeRSGUsZVuNZ16XH'
    b'4n8ZyqsgylqlPAXwE+7+6oN7x9Oz04tvLWO5dX62SG9T0NR3k8Ibe1RuVMnx2rVDAUiCMgTL'
    b'XT4xcpxO+7f6+eTHuz53RvtWJnvGjNlICugYKR7bxZHA6nzFlO3QBRK6sRpKPl8su9WymCCY'
    b'Wem6NPZdhgvFjHdpPM9dHi6BhmTn0iyZOQkZOd/E7DQeKGBB4DMCPibpbh3rUpDctayukyqd'
    b'P3ntMGyHET8mcLlYvY/A1LJpcbZ4YhwL3MW6ofWelKEcjsN4YMlqnnSrru98uj+tab11oRtr'
    b'nZTq2TfqeufuCdCooWq8WE7rMOYIvRABNeU3uevsJFkhNCq9KMEa8NqmtQCtlxElyuyfAXiw'
    b'VrtX8UxfC+qhl89CeBC2ppUrDowOFAG3BOzFfTmNz50hNKbWBt2ErXw2BJv03O3r++s/+esl'
    b'TZPZwWzSTY/2Z2ck9wHM4zOFffcUunQTZhMSQ4DiQu53vPii1jpx19VLwPqxnTr204YDp0b3'
    b'6BcBCAFk+C0R9/PWGrx8N0ZK0jWv/l5J73PXu+W64u6HkVh6JNcleD0tKBN0SbhUXQBUSVlC'
    b'uKxlEjKNj5LZK+PG5X0nbmCIqWAg9msRPf/H4xytgcvfQTvWE5/7/k+8sJacfkPq81N1LMe5'
    b'795tOb23DOOhme2tgWueutSF6uaMtR/AFoISmZKBibTwuIAC+EXkGKk5PpY6mIQy1nN3Pzbj'
    b'yswmAi4B5FjST45jOT87OZ9enC/3S6np+OHp0wC6Jo1m9TY1AGkRES5hb6eycBeT6kLuq+AL'
    b'8xu3rj6wZEsze8KM39r1WbP59LTr8oUlCxCENc+rrBOeZcY9AJkpvV/ErJbqY6kgaQIEslRp'
    b'gcRUqh6Y8VhAN4zVYsI7ueEnt8juJxkLA0aCD0i8KOG3C/j4+29dGRC2z4YoDz/2uTfTe5++'
    b'kQBkB2YEDuS6sVquPjiuyu1+2p149WeG5fAuGnMtfiSIk/n0rOs7gLhR3A9L1UF179yd1YVS'
    b'PWhWNHp1L8uhr6uxr0NJLSg1YQwMuBUk0jgabaWt/Q0uKEFIAnITr5qJGEYCY80rgCKOj+eA'
    b'Hi9uAMaQPP8vAD8er1ePgImAU48SYY3XumtYNGp0Nd0aeukhsGL4wHO3n12uxq8fxvJeM+b5'
    b'dI1YfVdIvCcWr0MQc/bds+jylMYUJr2hllLqWDt3zwImkrpLwPrJBsrbVRbxI4RIcI0+bXij'
    b'/9Bzt67+rmmfP5bMTuO9K2FAf0LSh736Ey7dgDQDkGv1Pa8+v9xKmrp7J9dEQscgS5MESZRA'
    b'AtWSbbK4SUgqJH7MUvq+89OLnwxp6iSYJp4NlfCNQPkTAAzQEn7mbQLgWwA8c+eTrz6Zunwz'
    b'9fmZlO1pkJevb9eh1Mn+bM/M6O7yWiu5SYCLeBcaYQYwC8kImnZz04KuUAID5NRmTLR7ktxV'
    b'aymPbRQpmXn149VqfHHtXSwAJqXUh6vlcP/eWw+fXy5Wt9fvzQBNIPRok8iJoQWtUI0mkg6x'
    b'fZ+VCOZK8k4Ux0SX056ZaTLth77vJinbZL43G6ez3iylISVTw4q6iHt5vrxYuSCCPESy6zA7'
    b'CAu0XFJx2VgqATiNDqG4dFxqPR+KD5CkGPiNCWPSJGJfxCL2uwH8PwCOv/bpG8PZUPRFZCnM'
    b'4hZnSROvfmXdnwJ5YMlSBEXuCbhS3J+r1d/lriOXDl3aH0u95lIf3E8XJAYv3vlYrCyHblwO'
    b'h5AoV5JkURDFYRRcFgnBlYQTrCDGsA0UQBWgh+ZRvPohzaqkfRAml9FYo+DvKInT+fT7Th6e'
    b'vhQLjAB8OoKMg4setdFqxl3qpPs7lEuxTTsaWm0+x6997zNJUh8vexBfxc15s8CJp0GMzPmD'
    b'6vKHQUwkTdx1pVafe62dXFlykzZewp/YiWi3Bk1r9BR/QA3qEsDd6wfzP35lf/YHJ11+M4zm'
    b'zwD4MKKyrqTeqx+5tOflEqjq1N0ntfhkIyIDAVAmVwIIS6wpp8FIgezD8yJyGxn8spn9GynZ'
    b'd91988HdhrcnxX5F095BwEJjRzv83A984rm1pPUrU85Ppy49m7r8nm7a37ZsvYQYdBBA5D73'
    b'NCaCluU5uSwiaRB45VusQjRt8yskMPQEQYDaJCgCdPehjvXRajW+7KWcpJxvzOYTq9XvuPsJ'
    b'gJdOHp09cXpy/u61WvDh7YAj7yEAicbaeKQEgu5inOtmdEltuEIFOHZdShL6ftINkvbM2JOk'
    b'JUPOqa6lrHEym5xNJt2CZJaUQGSYTQB0Y/UOxAy0PlxvcAeL+/bXktz88ernw3J4hV2e0di7'
    b'+8Uw1uO4rkkTGtMFgL0aqs/3R4zeHQBnSar44pu96+aVsMMxA5pLmLkeS1K3x1o/UKuecOlI'
    b'617dnxirvxeCkVia8RxCJ6Az430KCy+1q2PZd/duUx1c2Uudkqw0FrmyGRcpp1MjLyAlko8l'
    b'wqDQecvMHgDwlNNxrf5U1+cC4UMppxMz3rWUzlOX7iaz10h+CsRnCZ6CqJLsEz/yKSHabqXt'
    b'AKefSSUlRm/39TXvfSYz7NXxnG5FDYdr6vPfIOC2hJnIozKM12qpbZziFrAUvQ2CnCAmf9NL'
    b'AwzznGzZpfT6/mzyXQez/kemfXcW5Phf25YH9+qTWuuldXNaN6Xbs0vJi3cgani35NWTAMs5'
    b'LXKXzlNKoxnBZDKyA5mNrKGX/y4A/8Gd1+691d6cZqt3WB1sAWuvqQn3jXc+9ZpSTldpnNM4'
    b'y3133Us9z9P+2dzn6yQnNOsJ5A6Y99KeAVlAmN+BmHDk1ogRCo3kAh3Y5l8lSBIpAzIBk1TH'
    b'Uu9eFtWQ1ypHf6kG9tMOw1DOaqlzCfcFvXz/7qMn61i+TtI+mmZkq7IxbIzc5QmP1Z1N/pcD'
    b'QMpJXd/V6WyyoJEkOgmZpGC0lBJTspK61Kece8uJKVsimdxVQ6qagDQB8G2RXMbtEdwVHtFy'
    b'TnBZpOPlMN5xlwWV9JWIxVs03u0HAVwfB/Bng0v+IYBy42Cuz1e1+uHJeWtEwlqTiMBudC6/'
    b'UV23Sq3vK9Wfra4bJxfLCLLG8yHp911KZ+TG3uPS1WBBfS0Z7xvtmMQpgdEjrELuBrIYeWHk'
    b'wy7bJ7uUPk7yEQlFvNgQuu4qBG+RnEDYBzEHcIPgtQgZ6AWdAfgxkp8MraOgaR//4U8K0QKk'
    b'vtK5tVhLXdZoaz0AIKcPos9/HbrcA+hrqc/6WG64+0yuHFTr+RKwfrABo7RLKxL70+bYNkM+'
    b'wl0067sfunYw/7/W2zvJ+DzJd8Vkvg5g5tXnpdauljqRq5c0iUlhMShLiL/KydxyqsG/09MY'
    b'NltEcVMex4r5WwH8jjVgnQFgANPPVkvb1TyMjU39u6+PQXsG4F3nD04PSXbZuD9L9q4M7js0'
    b'BqKWAKkUKaG7q5QsJQu1y71UWk6CkAglCFnEUuSSAOTuEl4cq5/VxSoPw/jUOFZV9zflWlT3'
    b'tKYVuVXH8bkAgW1RiMYhoIapo7TVWEgKcVmhogiCWUpnTKxmTLnvLhcakJyA7LpJzv30Ukns'
    b'kl22ZLzceHWX5Js4IQKggaCEDDVZGwRqdbkEI1FK9cXFysfVGqjIBbt0AGAVwMToJwhNoCl4'
    b'+ocAfF+YDC4asGI8u9uxj5PzxR6EFYAiaHG0Px+PzxZ7BI5uXtl7tlY9P9b6goR9Sd35cpgI'
    b'uA7ocVgOwR4br1yOQg0GoYI4z2Zv5GSfzSm9GpanrWoFYULjo2z2Sk72uXV/OZm92dBStyC6'
    b'uzi3rK6tFNw1sYFvNsdb0MLT731G+NltubFnwq4dfATA3yc9tnU/JfdbvpE6razG5O4EgEvA'
    b'unyQbChmcnTbib5u42iGhnbY9qb96d6k/+7DvemfyGZLM/6GiIa+2kQyV0kp9PVEMzECDkMV'
    b'KSRPmgj5WVPd41HzN48B/HAYC1/LT14vP4ckd11T4+1ahArcbnIIvxXAjVTqUR7rtW3MsmQg'
    b'iwBSSI1IbuC2yGQKXagSCMSGQxChLNBlPKOUIfQgTgW8IuGlWuvBeL788GoYz8ehvPbg+Mzc'
    b'JUgJxEzuc4g9pT1Js0aSHmM7NeNm9SM90jZWJFZA6OVgYaR6ID5PY5p0naXMSTfp9yazyazr'
    b'c59yyuAmhMNrrQAioyuRxvhKUIJBgrsMca9CstIwFg2rsbh7lKxUrqXeg/SqTfsUY+W8casv'
    b'onDGy7H9UQB3A6jaxoja//sBvD6M5dDMPuTuL/Vdfqq6X4cwCHASVwHsEexDIoagCnAiKcc1'
    b'FElOIJfqqu4eNqQzl+5LeBnS5xSSxdF8ei8WJBntUU58vcvp0ib8upHnTVEKNdJQ2/ihb3x/'
    b'SwbQNc/To3++xrOcHD8/2iQqlf+jkr4OwJFcVoZxuu5zuYykXwLWi82Kmhp6WwVIsJWoGsC6'
    b'CJH7tVjFvvM9T17/rj6nEoGFf2FMZG8KNqwaz5S3kl28dxGrSW3qyyGOjfE9JxG5/WcAfGYN'
    b'WIufQ0aGPvo0fus+zxZz7c86AKTrV+dS/mpz3Yh1MAGaAdwkABMLAF0AjgRUAhlCB2KIdXOI'
    b'z04JJQFVZncAzBkFDiJG+i6Au6h+Q6U88/Jr9/7UxfnyDNDTAJ8gcT1sBxSg+G40HGZE7Jux'
    b'NzIj4MRDXW0/wxgjEYM02sZWZV2frZ/0XcpmAKjw6YMMwA1GzmQpqNpMksc1+brJXQLQkbAw'
    b'LBsJnJxc+CVoNVTCE7ifSPpxAGdpPs1hs3opVMCPhnt9yJ9f3TmYdPnvzjn9GhLXIwJlGvdV'
    b'BLK2kopiEUWWtppIT2AqgDFuLboC1Ly6D6X4hctPILwu6JGEoQl0voiF+KNPXz9aS1/2amu8'
    b'/kKO4mZr0eM1vOmfb/zqLCf9PGK5vR428Pciyo/JdQPQNLj4Ti8B66NN1ZfUGt+bzibt4qIR'
    b'ue/Gjf6TUaHlo+vwhtcbTmgPcJrGa27pYKLIQCOlLKOfhR3i7g61zaNGXX01zrtYA5b/POC8'
    b'Io/P+3DRdgDN9qbfmkr96yDtUXqGwg2Rr4G4AOAQ9gFIxjcADJevuYm4btWyEcIMQCbUO/k6'
    b'wIHQHMIcxLnIBwLOKd2E612l1pc/85nXv3sYxhuJ/LAZnyZ4QGI/JFoKwNu4pBmTMEUQxm5+'
    b'XRCHhLvdwK7r0PUZ070pAMjM/BKwGJwojf8g7G8UjWbJSBoR+UQuuVyicXtdJA1AqtW5XA5l'
    b'HEeUIgnqVKtU6jmEJTZS+6lq/f2q/r39zSufConqFMBFkvSFiAXN+FyX0q9LOX2rkU+SvE5g'
    b'0oz7GhVf9rTN8wzNgBvto4nrNQABvlB1HwO4arg9lyAWpfo9r/6qb9JPHkYYzmcDZB+u7WX1'
    b'C0S26AvxpkVLO+fr84CeCqmfB/OnlRC5y7UW2yJgfglY3x3gY22RxMY9nANs9pqQhnsBVC9F'
    b'/70AXonP1IjJ+mK9CNyNzl17/n5GHoqn3v2Ufo7oiV8g8TWJ/JUkX2BKLxDaD1a5O1Z9aCL1'
    b'P9WU8T9ws87czwGMNdmQqt9y44RApusOgLs12QGFI2zsVq8HoB2mWt8FYCrpjw9D+R0/+alX'
    b'fmM2+xYS10geEtwjMY+HH8izbRVQididGYgukXbZ2sTbhgzkMUjND+YMj+2GDjgbIMAVbr5o'
    b'3DTACGgX/rTVMKNmY4R4Q3Kvi8XqeLEY6MB1uBe5C64iqYOrk/upqp9pKCc+jA/l/n9B+h0Q'
    b'7uw/f1tfBJcaSe4l41dN+u4fS8m+muAeiGlIoxVxroQCYgIFdTCRjcwB9IFR8AAoD5tVImiS'
    b'anUfXSruj0MyPiPXi9K6b7JHPhmhBss1WPk7RnMU7e3m2M8xUKGhV7oa2xISVok5odgCwCzU'
    b'/fESsL4PiMEcamGD4g+bCPIx9lcNqdyboRL+UNzw+wDqzb6r+Mo3fp5E3thG+8KJvdj9vi8B'
    b'GNvGNUjuG/lrEvl3kHgewJQgBb1lpDcl6j8T9/NGU0l52dzbSfQuznst3n8utm/EuX18xzyO'
    b'/Tvu+synPv3qEzdvXPlL3rjz4K8y49PNL8oCHFBS3Kd4zwEkhSmAG6mDl50gIhCMLQBNZhPs'
    b'Hc6Y+wyACDK74EnnDv0wwIjSkQQIjc1KgVkUDZBQh2E8O1+szmtV8rGcQ3oUIPA8pD1IRa5R'
    b'pUpj3fNhvKtSHdKn4n7+hwD+MIDzNWhVRPsCC6WRnHc5/ZV9l/+alOybAKSwOy2NvCIh7mLY'
    b'FZsUo4j4HwGUNtxD2ubYqVYVM0LSIOkY4Kq6f3et9Y+W6n8GwP01UC2/0pJMIR0/t60lAj0I'
    b'1e/pCIGaBkDl6FFOH2hYUWY5Ai8nDfn/pJnYj+LESRPtOsbrZRzLTYj9PJCw/izYjtqYMcVr'
    b'NmWMWsMjdspos0lHUZv+s2NXU3TuAl9zLibT/usT+euN/FYSt2KlkKDzTWY7kqDHKQ1GzuL9'
    b'/abgaWTB48kArRJS7FsNZ9arIcHmAKqrAP7bIOp7BODhGqz4vvc+U9bb76vSN5v4JLjl24rQ'
    b'ZxoFA5HCK+eAvFmVw/gNio3Bq2mWDO5CHStAIkVRB5CwRBJsqIND3mBLvumgUSFZqazbMIyr'
    b'YVzvjH4h6ATkwJwGGk/rxeoY0qdU6rNyfaOG8diHciyvI4QnYqG8HWaJbwlzwo+evfjm8a6k'
    b'1dp4Yp+SninVM1kTgROSMwXIV2kFKOpqqhg5CQnL4puG+GlsxtWUQJvWPC5X40uCHkr4EUjf'
    b'69L3xELDp5+4NhZ8RZui/1w2Cyw5akxQ10K62os+a5loY7zvN+fcyE3KzRjeLW8m8qRJKGbz'
    b'pYzPvBH6d5sMya8wQl+JOJevj/2HcZyB2B+P9/fd7KqIpyGcirxJaNlw1BeBk3i9ivcKhEcg'
    b'EoRK4CSedjbpMyFZWhBWn5jx/mw+fd90Pv1mI7+Wm3uzL2EkcYPAkYATh+5GHt1zLoHgPgmP'
    b'iaZ4cJNGyv1YvH5fgNEbzYR4PlIp/hsAfxrAWTP5fA1WpwB+aDWW/51dfiIn+yrEbyaR1EzZ'
    b'lm+egKuJs4uOQOtWjpXkHJaDpvMJUxDPmRnXHYJQS5WZYasquVRr9ZSTrRslqFZXGWtdLoc6'
    b'lrIUtKC4AHFh5LEbHwE8A/By2pu+Mrz54DM+lhdV/Wsh/XMxoBX2KjTcSU8GA+oqjO7nX9Ae'
    b'FAm4ko5cuivg/YCy0aYgEiQBRkEedjUjkASUYOMcm/vYCzKELj3WuvDqj1x6q7q/COmjAv5Y'
    b'zJmTq4d7BQBf/LHPoGl8/uveI/wiay0paGvTbsgL1JbQj9dDPNPziHu8cakS/vONIX0/AGnZ'
    b'FLT0+MDQSFrXAHiogq8GaJzH5Fp+xVTCmLABVn9RSBmfbNStD8b1jCI/WM2ekPEKgiqDwBDu'
    b'ZPsCnhc1Nj2P16BwYfK7uNxC6vtu3N+b9pPpZGZGdH13JOlNkIdGXAU4C5f3SKIjOERVkkHS'
    b'YxugkSkkgwcA5qH+PYxJ14ddUPFs3gXgraDO+R1xTAD4U598Rbv36eH54vmc0l8w7fLfbuQz'
    b'JPqof1cEWFSlSWTQDG+ahwIYnOkMExOJaEZDN+lkRk73puynPYIeB3rcImxfckhOo2zd4j0M'
    b'q6GUUnFpSA9v4ADjysgFwHuEPkvgYzWlT4N4C8BLAdh18bk3KgCLhelfBfCNYVy/F8eGCF/4'
    b'rmDsuIdgXW0kLUZXbPcBfDC+6+tJPk/iEGBv5FwQEYuJhJ6RYC/oEYAFhIpNgvAzWyldGgU8'
    b'iuf1ZmxfjfnyIwg+//HNh/757Ey/SIFrGsBzhAhbajjlbiPMIQ1x5xj9ID73OPn5VwfQnO3Y'
    b'sRjbZcuXFDebO2EJQvQ1WOkrCFaTiG16AcBvCAnEGqqZW4jQCDd7wskrInIEJUJNSaJdthYG'
    b'SKmpbNOqhZRWjMq7Xc5pvjfdI1EmXV4ks5UBpZt05GaQawMG3AO3pGwZ0onCLkjymqSXjbwZ'
    b'A/rlEJNvxf4KwBgSwDIm4f8V55YGqD5fmzw4W1zvc/rNRn6Q5LNmfDKRt8y4Z8YZEOWyAHOp'
    b'eNXSoTHqLvVmNiNhBA2t/Y+A0Xj5e3OXLlVixG8XAI9wBYThmcNqPB5Xw4NhVc4FzCp0QDKJ'
    b'dAIPCTwy4Pso/RiFj1N6cdXnBwC0Bin/PDaoeYTO/DshqT5qBvlPIErHBbBffM3Xv28XsPC5'
    b'R6d6/spBHqt/8LXT818X+ahPxwTqECErsZ+bhf1ho3Hcbiifl9FPALwWYPWgCbG4iyi+EmD1'
    b'eXLdo75jSIe/iICrNWccBljdAnAl+mETVtUye8xDar5+CVhEtC8gNiOA6OesNavhXx0BZs81'
    b'AzQDaF3RU5F9vQQt4+FOeEbb1H69iK4BK1BwQLVJZzAjfdJnSxsGxTqf9GfrbZeSyZKdR/rQ'
    b'jWDFBAP0FEGZUfjyVMAipKyXQjL46qbyDeK3HcZg/08Q8UQNUH2xzT7ywedmf/ajn/6aad/9'
    b'+mmXfxUfXx+mJPcU2Qa1+rAax5PqugCQjZyTnJLsjdhHFMtlcJQJWKVknnKarlXiyd7hfGZG'
    b'yFVopBdfrhar18ow3nPpBFVvwDg6eRvkdRhE4S5d30PoByh8Kn67Hr12179Ix4cBeNLM/hXL'
    b'6SOW7KNe/TlAn6VwDuCzAn4XpMWHvvrd4ThC2WUhWQPXDMCfFyyqz8akmgLIjYoyBuDUeH0z'
    b'JtscQGlooY+DBeGzsf0EgE8iWHLXQHW++1t2eh+pbasAxkg1Qv1FAlxXQlt4Ou711UbKOmjs'
    b'4ZO3ieRP/D3f+9/iF0BjIyp+e/zoDzeVaY6aslIlfrS7cV7NbslsP362Iss9NfQIvpOVLrQt'
    b'gEtEAsBsHGiWzahsqZqxzCZdmnRpmVMaU04TAHtm1pvRSqlDJG4jbn8h6IJeEfAagamRNSbD'
    b'CpHmE/v/Ztizzg+/vAA/RiDerxLw60up89fuPTqScFXS5JKcaKx+AOnRurDCa+fL4XNDqQ+N'
    b'OBKwdNdbABYkphKWEcRatQEDdV3+YE4266fd+2fz2Vd5rfeXi9XHVqvxc5LOr+3P74WX8YTE'
    b'I5IrMVZP4R6xrUhcnr15pXyRtQQYW3vp7qN+cffRe6eHe/9G7tNXaagfM+M8mT1H4r5X/34A'
    b'J5AePPn0zWXjkNmPbQGwFHBSqh8sSvnAqvpzLk23lmppSqAa+TDqq67Pq0/FJOsDoAQgBfvB'
    b'ZxBsIWGreiXAahFSVdu4w3gwi0n8AUTgZDz/lwC8uAasBX5htxTz9cNNcZn9xjZ+PbbT2NqO'
    b'MwwBWL8gWt+U9ToKSWsvUNmaRErGPgComs3c7CAAp2U4gJoJEd12UF3tfrCmXUxzOpv0XX9Z'
    b'GGK9zZZojMokZkw0A4BOUtSGNAdQwZgDwCmxVTc/JeDVRM4QGexhUP+DAB4GUH057VqA+98T'
    b'KzRDYqvHy2Fi4IGg8pjtoPrr1/emfxLAH/3h1+6d4PQiARjvn16MzdSakDZHvEpd+loAgvuj'
    b'3HffamYH42r4s179rqQLCatr+7NtOEz0cXL9EAD03qN97Rhj0bCf5saRcCWOTxE02o/Ol3vJ'
    b'eH3dP0zyI4znR9AEoVa/8OqQdF+1fhQgjq7so1HxAaCLLgCjS/NV9cNVqVeK+0RR3CIAd0Hg'
    b'pLiPVZoX122XzgNsDwCcB0j9rsjCYGO/GtdAVfD2rSUbqFHy7etjUp/Hb38zwO/3rwHrLn7h'
    b'txmCOCB6qIVbjeIgzuma4HIhsmt+IQDWbkmtK1Gt5S+PHLCW+2iyUwyBTnY1WQ9QYgtILXtj'
    b'9Da8UXAQiAKWojCaNAJaduTd+f7s2v7B3tWuS33ku1VLJttUs1lYMid5EIKdSNYIh65hbL8Q'
    b'sCLQIyL3jfy9AP4/AK8c5uQ/A9fxrwHwDzY1IJ9CUB1fjOWAQFL8zuK6f7YafvTO2fI/Lu5v'
    b'vo3USUCpqeK8XItjt7u++3V7R3sC8NbibPGNtdRPry5W/1swRepGm4HQAn+0r7p1NccE/XND'
    b'PeiatKyjBlxmzWe7xTAWAEbwqqCvISgSItBFhrYDKu5wSedyP5nNJudNuE5p7JNds9il4loW'
    b'935ZKquUAI0AV5JqcY3F/aBKR8n4xlidMe4ehH3q/224pRhAVfGFWx/mjQ+F2vjXhW329XAY'
    b'PBnX+pNhq7v7i0QtZDzXm6EB3I55/VxsY07juRgLDA3ktV8wElabMhSo/NcA+OYAr6fQqnVh'
    b'HG1W5hp1/0eA3TbtpfUGSkbA4liJLkRvzmXu8uLgcH57Np9eI2khRQHAUEoZc5fnJGtIWCmK'
    b'GdCMbmbZpYWZEcQxgZkLC5d+m0v/zuxw7y1dLAWAX4J0ZfFb/8KoE3jS2FkeNOEgs2WpHaRL'
    b'SeJ4NZZ8crHi/fPlDznxOy2n7yf5gOTDHQngenhnc6hAvxLQ7TjnmTj2nwIYIk/yOUjPQLrS'
    b'qOlDc98fvXD1oDfyW418AcBBMu4nsnV3aye+ri/VJxGDcUXCoUsl6oemsVaTsDCyRgn7AuCi'
    b'DuWl+d40YdOs/b4GCCeR+lE3fFZ6dFHKlepKVUKVuuruG8mYJy5dLe6nAO4HC8R3R0L+xRqk'
    b'vuhnFvf0r437iwCr/cZYfw3A6wGE/9UarO7hF1ezxlbIhsts1kjbB9F7AatfSDas3eC8/Xig'
    b'f2cwQxwBsJ1whNaIjmhs3rfYtvslegtqitc0M63B6tp0NjkkaZasSzn141Aedn2ehQ4oAYPc'
    b'B3cvAG6SNpEEM6uR+AsIcuBOJeZwXYZO/uFa/f998+U7Z42adBGry6vf/LXvWaFpTdrUXwbg'
    b'rwRwGp87knQqYE7w9mIYtRrL3rrPz1djWo4Fw1jTWEtvZqPIu57st9Ps95L8OCKRu+HsfibU'
    b'lBcgfe1mXz226hw/F9LRBFLYI5ADOCMqf7sYzKY5pYiGnxmwfml+NOnv7fX5zMihcXL0TXBw'
    b'WQxjJXkFwgcEJYKKUmqJwZJJMpGoEo7rWO4C0FrCyo2xXDsmgNpMluFyW10XF2PZW9Y6iyo7'
    b'CcC5hGOXLBlfn6T0g8er4Q+Gner1LwGo0IzhXwfgn2/U0/cAWDS86fsBWD8UQcL3Q8L6xdjY'
    b'bnfvlUu3i+ubM37htF3DuIVn7W4YNr2Nr2kj2BvQyQ0IHe4YPfPOTet2jPCJpPWTLuW+OxKQ'
    b'ui5nXzcJZTLtr0g6t2SnJJcCish5rTostQiqEmAp2VSDsBqLlosVaqlPQ3AaL/f/ahp/3Wxv'
    b'+unF+TKMxFva3+89u1j+1GzS/6QlW0Yg6l8P4DcJqKV6KrU+V6svFmO5tliNHyjVrbpsKCVX'
    b'93UX1l0E5IDnlIqEBOA6hW+D8BDEBwEsmuKwQUynGxBuALoKYA9q0yx0A4Bikq1AnkEa4rMe'
    b'Pcf5Fsbs3gCrwH5kSj++bwEqtQmvmQ2lEsCQzA5JPEMQggygATJoKx0BQJFQAdy3nMZJl4WQ'
    b'opqVexFdTThCiUG2JGF9Ni/y4iJcMkmVxk8b+cYk2Z+ZpPRDa8D6XMRTCV9aUyMBd02WSFtQ'
    b't62i3dpXhV+cTe3ve+1soZ33cG3aZ/7Pf+Q/+nyiWo4tG1cw87Sv+Llp1u6TuNHR/txEfkNn'
    b'/AtDZDwjcNjGiBmJHSNuaUIhuti3eK1GMsMuE+tsPimP6y93Oa/71IyrS9ACWeJ8F7GCuFdK'
    b'6by6hrGglrox4ErmEmp1rru2ceMSJMFLVdi1HgdRjsN4hpBQ+i7fAqHrVw5+x81rh6+K+PXD'
    b'WPNY69xde8U9LYfxaDXWq9VdxZ3usqiobC4pvJVOQFHQkyAwFB9gPMt994Yle4vkSUyYK6Fu'
    b'7wE6hDCHtN8Sr7US6FbtIweEwyGOMaS14z5ZCsCZGjgFN6W1bs6nrx1OurskV/GZGl2XEqKE'
    b'GYBv2glpIAmXMEiCADPymMTnCJZpn0sAw5W4vqOmpL7FNXm8DokWq6jFN1RXJ6y70Lt0h8T/'
    b'kWjfk4xvJPLuOhyiBlh9OdLEteDg+hsAnDcEkKVRCa8CGCKz4Z8GcK97z1PCL72Ww9b3DU1F'
    b'Yuw33qUb4Y2bIFrcwM+W5fBarPglAExfQdqJHNsU+xY9Szga5QsnBdgdEjMCvZEisE9yImnh'
    b'UolV/UzACTaAkBO52maEN+71kBImYZcpiNXYkk1BTveu7N8wswNAs5zSC5KqCxOv3l+C1Go5'
    b'zmutKGOhAHh1ymWCQFIggVYFNcIUWX6Z8lqTgAmEmymnKwYIpLuQ3P3ojQcn/8D5UF6bz/qz'
    b'odbpMNaZS52EXOXmLlT3VF2dJFQXJFlOVqLQQcrJVu5alVLhQJLUU3aUpAJhAmJEeGlCspoA'
    b'2hXXFX3X8wZII4DoET5AnhiAKrHnhoLbpOSAAfRFqXsHk+4kFpxlVATKAjyZJQDvCqmSArwp'
    b'/UYIUwcypIcCXoegaZ/3Gp43xLWftjmw8d5Z422+C2BCYJlIs0RAWjpwKkkkOyPvcnNegNWX'
    b'3VJ4x2aNc6S0Oa8NwcDH471fkq1Z8F7IAL4jRP+vCSBqc33Kjv3nfpxTg0Dvo2U53H2HQWs/'
    b'Vp2PNJQTXfMQU/Q9AblIk1rrCsRIMHEDNGdGzIMyZQBDtdLjB/87QTzaz1lNWaJVdMQ277JU'
    b'PPvB595jepwOdIvQLUi3vSqXWpK7pzrWNAyjD6sBtTrdnV7dLreSYp5BYEgGUeQhyotvtwZT'
    b'lDAnmaqkc0A3qrQXcRE6vljeWnk9IImxeFdqzQLAJqcw7DnWJboAc1dHorqrjPKzVa1BiMjw'
    b'hXr26gcppym2xVQ1g2CfhzBOzeuhea34+4htJVmNmCShN2ECacJgPqBIGmpnBMGIeMbgLuaU'
    b'9gWtJE1copFJQiWQBQlg2CKVsUl3emXaZcWkX7aSdvNsUzPOxuhhK4zfHY4KAtdA0oAZyEcE'
    b'via8uMLPvF2NdCIDUBtm3SvNNXrjZLJfotIVAfTxfD6XQyy9FjepNBxYs8a7U+LYN8WNXoZ3'
    b'7v8KL8nJO+zuvBX9II73cV3tYKzxd28GoUASpLhOVOF0a8wUxqaq8U9AeHHHE9U2333vPV//'
    b'vvTYg+P+XlR/n4RDkANMxZJNSBSvYt1ULrao7qyoTOzBhIeou9gBSC7fHCWj/MO2DH0XNRl7'
    b'AfsVulmk5FvIoJKk5VBmfNxQSLoRVFSvh5SkkIjIhM3BEeKFGc+wVYk4kDiSYABYx1It2Ssp'
    b'pULj9bCxzAAlIIA24EsCtyo2eQ6pgoxHEC2ivw2wTjgwqCdolBIIUJCIKmB04YGg1wimS+8l'
    b'yVmUGLsNcgLJHRoFzLgdk3IAswCYV/ucBt/wZFGASeolJYSU5655GORJgJJIcs8lpGROclhv'
    b'zxi8XPG9hUCJOKs/FVIa3wHjcm0cO0MDlorXbaDln4jzfik2Nfjzco4YmP04wHaVbAIZ21LU'
    b'FQh7QkSqluXwewGc5Gm/egeQ9ENBEfJEI+F4gBRjv81lPG+M5NPYDgjgis8ZgHkTkNdKjvpi'
    b'BmC4lv8SkOGRgiyldbeTWrxzx2WKymjJ8moxeKk1iH9FSVu7iyUThKotqASVlBFGo0t1rNVG'
    b'V/Km9peRUiRFpg1nZ6FxlMCoXg6XsiQ2tjq6i8k4GHgMYHmxGkdLaSQpCTeDt6nGdXRlGN9t'
    b'Mztl5HWRkFlCTqZkW9oYlOpwl5txkcw8JesBMJnRjNVdNRFXKJC19l49qbgJSsGGTpEemdYm'
    b'6KpL2cgxuLgmAK4pWBm0ucZZsOU5ARtLnQ/LYXD3qZf6oWEovTY2wmRGBxCOWVZBJKi2kKsk'
    b'A8GQIhGgNe4fzB/N96ZIKY1hGliEWvYqYjx+8hMv6x0weezFNjel9FZtCE3MzbAh/5JVB8ew'
    b'4+1fGt3/dOM67pqblxEh8g1gtHExBmCIrPj/HsAP52l/+jPQ578+AkF/ZRh6I+J16zG6ADBt'
    b'xPrasEgozmmzu4fmWi8CeAHg3wXw26789IwSXEtWAmAhgf6tqv631XF8gqDRKEvmEIpDi+Aj'
    b't+MHp/Pzk/MDr25BKazgfopyZtEkgsyK2KAqpVWpGMYaggeN2JKvV+O2co2nZMVIB0EXTFKC'
    b'4II0Vs95I9XJJQJ4lJK9ebZYlfmk2wPANWhd5C7fA3Eg1/vk2pOUN78p1cm895RSb6Sb0bqc'
    b'LCeT0RA1IpGSXR73nKyErOQCaMYUKQSklAIYIBfG1YAyjOvtCBccRDGz5Xw2uT/t8vEkpWEo'
    b'BSEZPg3pBgAPz98EkMlVJZXq3ks4X54vUi3VFAAUFY9r8AO2Eo3i/iNi5IykCzICJoCxMJT5'
    b'3uz+GrAe5pziM6gAPhYBnP972JzK936qzemMYq5RZPhaVf1pHEgfiu+6EUD4dKOmvtGkaTFi'
    b'3P7ftUq4+CUMWgbAcnOz+8aAuojtcQNmk+gpwOCkyT7PACrGkraSS5eFL77NQ2J7vvHohFEc'
    b'jP0SHbHtGy9P4/KNxMnI9WoCSMdYJT8JQE+98OQX65n8iNf6d9Sx/CYv9QlJXUppTJbO3TXS'
    b'eGGwJRIT3Jaz/dm5uy/KRq0Z3X2SNjmGy2DcpG+M4mk1jpdG873L1y5UQRsoBEGyQMoE0XdJ'
    b'UapcY62dgAQSkiKci9Uh1aqczUpOtiJxjopR0AiJh/NJPblYvRLAfbOMpVhKHaBzGt1ok5SM'
    b'k2nvs3k/m/Y9kpmlZDRSUR9wKxJGFeuzWnVGqpKkkX0i9804hWRbUtNQ1PtpT0m6OD731fli'
    b'2eV0ujefvrLePkhkBUCXJOEFQFcQ4QaS9sbVwHGs7puaX1OXFtgYx7VR9ahkVqp7ZzAI4QUV'
    b'JMkAmCBCoMs7CBBgkGhm1aP6Mgk/P1skALaOt7sb9EArABZ92lQUb50NzwWjwAWAjz1IfPXz'
    b'gBab7SMAV5t519rdUrN/Gu//Um6ORooqcVNa78+MG/SfCFtAayPBPbiGvj+AYNKolcJYWsMr'
    b'43OrFsjajPtYXSyC5g4bAGV8b6se1kaFZHTsFKyYRF82g8rCLsMvRmSX+0fqWP6FMpRfC2gS'
    b'UlJdt1yrH9A4pi6f02xIsEeW2K8BS7P1Ch0VcYawn6mUOhnGsjcM42S1Gi5Wi+HWCNyr0OjC'
    b'zep+W1IO1YTJTEaORmQAHiXLLckAIJxWkNHkcGSaJ+NA8CKYTs+4KU6zOLlYesMcawDuNRS0'
    b'Q0hng7vDq8/LUNXN00mX08TInkYzUmjVZ7IwSs8zYqYIZLk6Qa1UQxoQjgSQ5Pxoz2qpU3kt'
    b'ICYki4BysRpA8LaRzwIYBbmR+6Qx2aT2vZallOmwGk/d/VzuuYw1OKtQqyu7e+euLFcioct9'
    b'ADCzgcYaF9YkvhPuIiA3Y4kC01ouV3v9pHvU9/lhSumkich+X6TftHGBT0VM4IcRHvco2vr6'
    b'T6PmdE1MFnY8gY7dylW/tJsAKMeNS1sa0pjoWx6kx1sUCCtBJTL1TwgcR/b+s0GAdlKrn6Vk'
    b'vuPyTo26doKxnDXgw5DGHjWxJzcag/s8ugFI7cNtpLB2dWKcy6Zb9BJAtv8FV6sAtX4+/bZh'
    b'sfqn5P5hkiPAKmECKQEQDYXg0qtP6aop2ecAJpIZ3Fyjuz61BqeHy+UwLFfD3mo5Pr9+/YKE'
    b'maALd00vLlbddNJdShufA3EVwgHid1Z3ViGbkSnDjBa5cggBC+ZSdYckH5PZ/WS8A9rKXd3J'
    b'chGpMVgh1OLo5wCq12p9n/tSfWW0E4CHkDojBwN7M6ZgZ3dJI8iQSNaNWJHcC501uftlNwAu'
    b'B8mgY0ZIZMFWIXe4C5P5hGspq1sO5cZyrC8ZsZSjknoOwMNEzkB0Li0IZkmL6j6r1R+APJn0'
    b'Ge6ynPMZAI1j2aulzglWM7olFkmWMkSyGlkjSBZgXHUjJYVE5gRkZkNa93Eo6Lv8ehQMUUjn'
    b'd9aqYCs5PRGS1TdGsO1BaCbXvwBgKbaTEBTOtom+IWVFT80i47/EwYoAlOPGrhr+nQAY3gKx'
    b'H2WMTIQgnpLbVWCPwLtBPBGA9TB6cLrDm5s/BXAtHm4Kkfo0aDPuQzrZ8AZxBLAHbqtlTOP6'
    b'bjQicpvhb9HZqK4WA+DbAIwBUq9HrtePRea7Pl/mfKyS//S4HL6WBEm7K+k6oByfGkEuAA4g'
    b'BrnPLKfPSPhuEn8UwKuSeH6x/NoHD07/0jt3Hnx749282lzjoxjY8+VqnM+mPb2qSEopWVn3'
    b'R4m2JDDLxmlQ4jAiuvM4Vlu3ZTKeZeMxwFMJWJP2DQCGxkt3GP2ZrZQLnMTi8OjSUJ27PAXw'
    b'tKTrY6kJy2GZ0kV3eDC3rssCUaO2jUV0UiU4gUBA8KoUKqIjEA5AA1aNIUlSHUdfnS9X7r6S'
    b'2YqkCegdSnD9cKJ9yKFpNhui7NhI2rzL6aH3+X5nrF7lYymz1XKYu8sgTWmUgUUAAi5HCNYG'
    b'mu6kZvWSunhf7k4a5aUCxJkZX7Rk39ekyrwI4M5z7382AdBLn3xlH8CvCvvr+5vSdu/6IlS4'
    b'vSbm6kqj5aQdjQFbAPtlCYsZAIIuN0ci7xjGR4HbsaZQDa8BKhKqgPONaCyncFPAnoij+LxH'
    b'r41B/EqsQAFGONtWhCl1Ea/vAjhpCrS+ji7fb2KifrqqN+22zRNkY6j3D3/gXdo9NwbbPxEs'
    b'EL3cH8EsgEoWU+ARiSXIuxAmchVL9jEAnwPxP//AD34iaifiVzx5+/qftwarvfgtt+Lvv77N'
    b'/4tcyADeNxbL4dHh/uxVCU8DetaFodR64RIGoCOZU/AVG3GeUzoHdB/AiZkND04vQmUPFsxI'
    b'Oo7tw7ifxw3QJwBPW7JeUg/ihtG6TaEEdTmlSUrWZWMlSTYBmIE7LpdJyiQQhn+5K24VEZBF'
    b'VYmkR/ku1eIOYMg5v9bPJp9ZrcZSSv3UONaH41i+jcAnQb5F4EfDU3ibwB8T8L++68bRuaQZ'
    b'Dfs5pyObTa6DrMMqf/0avD6Sst0DUMtY342NFFhq9WuQJuD2d1caz81YfP2emT2i8czdD7ou'
    b'f4rkwt2vd33+gyDfdGBZN/zN39zcWx1c2b8dYHXN3Tu55u5ayL0D8P5T9/Gp55/89OnHX667'
    b'E6+R+ttiKLUxc6CRjhcA6viZ1/FLvIn/6x/9j7+TfPzwRpLJyM7ImZFPYgNAeTtGgRHCCKIS'
    b'ELkBAIkdIJOEyBMboxpL3oCbasBMf9njnCUi8jx6VJJGACHuxyT7qfCa3PvwV7/bvxLR9MGt'
    b'9VeEaH8vJCEH0NPIsCE98OoDAG/A8I9FTcaPfuxTr7Zlmj4UKsIHg855v/F6luinjeTVhap2'
    b'MOm7myRmABi0xbqUtrJxbkZks4cAHj08WyziOxOAvhn8hijZHgCFhrTuxjaeJ1SR3OcOwDME'
    b'nzXjkutuZgdXDucHVw7mKSfzZJb0uAGSBgAu9x47zd1TGLklVxDCw6MUWHgIoDKWRc7pc2a8'
    b'qNV7d7+U9H5oLPXrS6mvAkhRAeeJhl74J3V6ca+15exQ6VorPcUxtEnwzcKl555/sr704huD'
    b'zSa/yvamf37uMkDug5gAnIIgyL7vuymAvaHUCYge0gLCMLjvx0KUSfYgOgSSy7GQ+8cX58s/'
    b'uDhb/I6n9qZvNACEs4dnJah1/n1Jjjje/JY343k+EQv5P7kZa7/c+Du/5795BQDDfesQiksX'
    b'BCbg1vhtBDOIXW6jqCISXOgtEmJbV7MQGECKgElwl49Dqafjuiu8PQiVNEDsrNm+GDSzL60B'
    b'a/kOu0mfA/B3A/ibYnL38d4Q+zlUt1VTemgvJtD/EA6HB2uw0s53H0bu018K4DlEZZYGoFN8'
    b'T9/YUbqc09OSDsKO4rYNh+ADAovqXiTU1TAagIvGMTEGwJ/0ySqAmYADSfsxv53EAGAxn06W'
    b'i9UAAmPEPnzAXe+2ROu77uGsT3nad/vTLncbfi8bgZi+gI/LsXh1gFBE6Ed9PlGCEfB1o1wV'
    b'REX8PLkAoHZ9dzqZ9meWLAAVD1er8eqlDWoYy4+4+7mERxEucyUYPF96/tbVsy+qTuWX1vIb'
    b'xN84A37DYijPArgC4jpiLEfAao0ahB7HrMpFci9MJxYSJrCtxAGHfDkuxzfG04s/cdDlF2Ms'
    b'cXWxqu7+U5eSHcl/TNIEQidoCWEuCU0tyncFv/8/vjFp/HLj7/m+/+6Nplimc1vZBRXQHoLP'
    b'iWTe0a8Z9doaD0a8UoNqkgMYXBogFRdGlw/DWB+4fCXhTNIxgNMGqOI1XgXwRjzAt9J04m9T'
    b'OFUfeO/T+lLplkMK+i0NQDEGyXuaCkBnTdDeE3E9fyDiZ+4CUAtWreQWMWV/a9g4lvE30NRu'
    b'nMQK7TmnauSKxPWUzCCwoehlrQ5u3l9KWAaz6fFyNb4V31W7ZPvSNmMhkSCBAsIJxvch52Sz'
    b'6jpwaVarz1MyTbrcd5vA0GSkUkoGCSQUnxPdDYJIFIJG0mgoUAwAYwkB2yPOKUVicpKUzGxY'
    b'A9Vbue8emjED+NGLxeqRXPckPT+W+oO1+mckfSCk1pbJc5w8cVV4ZxvDrvd3YSx/DoAphD1g'
    b'C1gEYRCs8ehlEfF8mWmkJPBt6AbCU17q8cXdzG0waPXql+31Wuprl2PEqye5erkeCroNYSXp'
    b'xRh/zwcTyT8Ri6SAXwasTwAYBJz1yS5AblQXKbt0VUKOh5cRTYIFOBEKMZuwWG0liSAoNaE7'
    b'ggsaJRyPpb68GsdX5FoIW2qPJaIGYgDFW42U9TCOr9J0EvawoE5tUxyiwk98367UgzWwhWSF'
    b'/y5EcsbfPGkq0BYAn41jCqAqwGOD+r8H4N6rb31eyls8Oj7vAHwkmDS/PUAJAVipoRQZQ9q6'
    b'P5v2itX0yF3m7pbMCo0DgK7UagC8y+l+MvvY6fnyFMAspLT9bEwkHxI4DwP2IYGZpElUfN5z'
    b'aR7Ju4MZRyNP+5yuJuI6wUR+fldWx+27YnM/afRkNpIcEQGZcRoijCGBpJmpn3YP+kn/Y6Hu'
    b'EMAPPXp09mPV/WUJe5JebSSKCSL+qJ4tWml+v8locAAPh+Vq8XkWDDUq1l4bv2c5vSfNp7+2'
    b'25/9Suu7Z1Kf9wHMuVEJu0aaSpA8vKM1mDVCxSYBMX53jHUoOklAroWdXvwkqscYRa1jLZIe'
    b'SZoB+EavDt/knh4DeJekEcJnoxTcBwKo/plN4OovA1buchKAgyBey5BmIHsBFNCjLbqpUA3Y'
    b'8JwTjCoqRS5rB0ts0UzYBQEks6s55zKWehIewmMiqmSQtwGcN3akEdAbEN4IQcFCCjqM/WkA'
    b'lQew/VgT4jB/4bknzgGIYH3w8PQbZtPJ35Vzej+gU5ArkgdGfhAEJb0u6ViuByQPzezpUutP'
    b'LpfDfzOW+p2S7i9LVUNZctTEn0lSv783/cA41l/v0nVIi7HUh4jCEmbs3BVsqKgA5pM+71X3'
    b'68Tj60BEkksQEo1mNnY5jSSX7n5Sqt+a9t1Ty2F8TOzXJzsGkEEQYI5cxD2XbgC02aS757W+'
    b'kbu8n81O13j/U+fDqM7smxPx3lo9IWLCw+u3lRQYYjUkRGPMRiFwyd1pmzbKPUW0uwAYyA7A'
    b'crY3/amU05+KRecHIs1pvHJlfwFgdffecdxT/BRirKyBqjZq+FGo2LcDtFZha/yefjpZrkFL'
    b'iOcdoPcNwUbrABbx+Rp5j8s0m3xVmvZPM6fbNPZy70H2khje2BRjHWgrhQvaJvsAccoWvAmC'
    b'cClUw4paPaL165YJl9vcwRk3UhqSpc5kFYCpymqtEziSpByqfphCfrnxD/7g/3DSBIQCJGLQ'
    b'WzAtctsVslZs1FaeCQgj0aKUmm2RNqJxdT9ejuWl6uol1UigsCai/YLgamvgJgIAw22erGsr'
    b'VEefd12ekDiVdCVU2SkJElxZsktweG/X5ZO+S+pyVu7SkZn14SRYyH0A0JF2tdTyaDWUl8dS'
    b'X3X3jwl4KGFW3G8DuN7ErF0AqO6+V4rfdGnm7gdyTSVkQSnUuXN3zeXaB3ECcJU3HPAy8iqA'
    b'GQL9zVg3UfKPgWcZUaKejJ8h+dLJ2WLVRkL3ya4oQiYInJrxXja7a0Q9OthbkqQ2gan7IHEx'
    b'jBMCHybwNDzc+gSM29UIRoSfOJokhEgR70TJMsHMKgELkEKYAcZLj9tsb/a/0PgDIbWOABax'
    b'FQAEWLWNa7BSANMHAnxeCEA7QrCvNrl9D0N98jVwTRvb4fsAXInPjbHAVeY0dgfzK2naHzDZ'
    b'zHKabHCZcf0RIC0BJORiJKkjGoNZo9UAiXh2DdA5xjrgfDHANQAYvfpdxPkhaV+T5OtW5TqX'
    b'dMWLX573hqQ7kj4I4P8B8B9tQoB+ueXmQaDlNyIgI92lJAGBUiCpJtput8Wp2G0MMbkD0JsZ'
    b'QT5kgknYVjkV1ENYbtUlwkL83gMxB3mFZNeWxU/JFPu5Vu9iP5FMMXzg7lbdc601p6HsTfqM'
    b'/T2OlqgoEjoppU5WY+nXW5XqxeVHEr4q4rJ+fQDTQyMfuZQbb+FTm0GnOYC5pCkUmQFEgsNc'
    b'vk/wCoiRRkfwbyWzKYibNOZkNkpIkbwLAXMjSzJWs7QAUCW9uAYrxGSeI+xuRn46J/t4Mr5G'
    b'oItS+FcBHJC4KfkhjROKOB/Gw2z2LKQna6mT7QSKjGJSMhIQYkY20jTAOE0QTBAJEEICUUGu'
    b'AIyXgNn13R9d999K40cBnL754psFX6A1AHwQye/fETYmBv3R6/EMFrEdEIHF8Zk3mmT95wOw'
    b'bgKocf4IoMA9q9TOV4MzpxGuZDkBZNhp4YFO1i7AlBTAJLlw2QPIsE0AQoSkklXS0ogRyR7A'
    b'63kkvi9rqWMUJymSilcvNGYQmeHESDldDRCrpZSDJpbslwErVs5xlzNIWw8ge1BhcAcg0CG0'
    b'LersFTAm8Q6lcewEOICgckp2Xe4LowUAySFoy2QAjAQcJBGl80lu03/M2AHo5UoKbxkjoS3l'
    b'ZP1sQkhwd9WxWC3V1k0AbByrTs+W/d58oskEntYNgEqtPpaKkCzRsB6o8chVI4dILO4Qv8+l'
    b'qaBkJCuV3OM7CCe4TVOJiX82XatqkSXQucTVUCYE2HX5rO/z6K4+JJXi7hMJH5MUcVv4cQB/'
    b'GsAPAjhZlnp8rc+Tp25ff18p9YVS67PuflSKP+/Vb0i6JuHKWOs0kTNKMxBd7lKVQLlD2q4a'
    b'lAKdwNiCzQO1raGZjOxIXIC8MPK+5fS5vs/fM5n0v98eR//j4pVPv/aFwCoFyNwIj+pfG4D1'
    b'qOFgD457fK6pvJwBXENQwER/FsDfBuDdJA9oTCQryGxmmcaLlBMtJ1lKnnJWnvaJyTKAJPKy'
    b'xyinAFDxqyUwpC15dbg7AQruhJmQzOACIFetA8a6hPsZiq9aOpkAqkehTh969aWKRoRJAWGL'
    b'9er35ErB4daohL8sYc0EqKGUUYjDghRmFXojPQVNBxD7bKKcjUDnwECA8b2VaFULiGTOyfac'
    b'HF1yAgZYL/mFhJkRUwgHAs4t2VUA+1r38OKQRiEkdhCyZCNp3k/6PvUppS4j5WRyYVgOMrB0'
    b'fQdICR5+Z9+k/E9BX63Gu4vVcDaWek3ApIkqLtEVfYmgcWk8fw5hQnBw95m7JoJgxgrQAbEh'
    b'miOE1HUJ1fU0gCeSMeWUSjKTJAfk7phKGt1FSWk27T+z7v/Nq28++NEm6dyvzSdqgHVP0qy6'
    b'33bXdXe9KySNZ1w6cBdJGKROCHskwN2IABdgFBR+Y2LXAdYcC1U1aGUsd6n00z7lnL/KjHtB'
    b'8viJZ9/79GsA6hq42uvNAUJXA6y+MSovvzcWgicRYNQEGaNxhPQA7kS/NyxXFcAb3aR78fD6'
    b'0V/cbwpPeMqJAIxmETeIaJQZCdLMaG3Aa6P7wZLBqyA53CWvFWenFz6cr1TlxWgk6TQDDYR0'
    b'BvJ+Z9ZBKk0M3Ahgj8YcpJJ9sjQnacNyuCNp1MYxMsh1LOksbHYNod8vtywATa5S3oq9krcJ'
    b'rPGau9WRI6V1GbmGvYAVBInIEsao7ZZITBBjAEBNZEqUCyhRequQdkigB9ERcTY2aiQBax8c'
    b'zUSjzOipy8zrnvpkZsnD5+W0x8hYw52TvVQ4qpIlAzaYdXqxPCu1du66LSBd9jDVsLW/hW3C'
    b'YvAxHANhQFUU3eSShoFiBmQS4vvgjN8ZFo6jLqelkXR5gBoeS1XVvVuD09n+fPJm33Vv5WRv'
    b'pmT/GIBHa4DyL5C28Kjr8k+Q9FKqV69P1OoTF4XChZk6QeZVncIuHHFeQitBBcLKHZQi627b'
    b'25iVbXRWyqlOZ5NF13eWkt0KG9+7wm6UAJRXP/P6PRAen52EDfBK2KieD4B6OsDoNNQ4C1Xw'
    b'BGH7ajjNGGrg6bu//r0BRLj47I9+6vcw2Z+TcvpammUAhWSl0SAlmlHuLm3YVwF3Wk4kHDBu'
    b'IIwdhE7BJ7b5rDFRIolL6X25WI1JZiJWFkwQtepCwIuQvz6gnu11GU3w7qsAyrgae6/+GQi3'
    b'Jf0bcj10+WmofUXQItTcFJ99NX77L7dt+oYEfQEyuwArRffmHBMironIgXQR4kACmgAswOZ1'
    b'E1yXQJo2Nilr6g3GPg3RIig1ETAQIFvrZ1xQrSqRddtNiJyS5JDgYLKUk2WvVXKXnKruJJgK'
    b'qgheEWSNmU4C2yy4GsbYAoABUBWI1+FyN+NKrg5gBkSBwcjC2qQVDSmlVZftTTP7sFyVpJEc'
    b'+i6fzibdxXqLnNNbTQzYvwbg4U+GdBKN3/jBdwltCy9p3+cf6rr0eq0+B5Dd9ezFMF6D+567'
    b'rlWriIh1mRlSMm7txYIQ/1xGH0ugE6PYw9bZwjjqlqzM5pPTyaQvJCeNWWF5+uisXOb6lbHc'
    b'SjntSzrz6gtJN8M2+KEAoFshYY0xQScBWm82YQkrAH1zX84CzFaN1Dbvp/3NtaH/LOX0qoBp'
    b'2BRTpA4B7iYE2EqQYF6qhY1JpDlpg6BzI1J1uaQDAO4hShbj8tGqvFjcg/8/xE3hfgQhD88e'
    b'7qlRBS2uWcNiiNd4JkrUdQ3n225Q9qph1/3lFoMhRRR62/TFpLXsOAW7GNnxHhT7fVsgXpKK'
    b'oAIBYEgr277FjNgVCBlZYdxMbsDir7QhQSYJdayABsizLCVYTiAJL1Ve/XGX4F7rIJcZN/Yu'
    b'kq0dz8Ns00qTHr0AOBXQIYA6VvASVMUkUQmeS0gNAdsqbGydGZY5pRuzafegy/l+zsmMfEBi'
    b'TnIOICL/cRHR3q+8cv/YDq7us7EP+qfuPPAmeXYWkp+9cPPKlMCHaPY0iSdS4rW9xKsE5wC6'
    b'WJpcm9uRJHGHgx3xvQt3Py+rsS9jmcc9EiSLrJtqKS3WYPWw67uTUA09gGS4OL24L2lIyZ68'
    b'cfvW9M6rd1/0Wm+TeE7C1wA4ahhmn4/f3Mf+jSDLe5Pkq7nLz9Za6e4rCBcRuvJ9AE7W0pUA'
    b'7AXg/dpnPvjcR+L1clgOBklMtqfqS5CJZJZ7oZFedSH5KNcyJFx4LZJAM84qKXJTFKO6ZEYz'
    b'oOv6rsg4luJh/N+yKvzHYV9Du7DvxgRGP4vPDdFnYbcb47zUFHvBC9/yQXyFGnf2Ff3nLWA9'
    b'igvM0dHcNDbHsFOgdNcA0kVvjjVSEhHnkwZ5B3YjJDbBds1Xxn7shnFFELQ9GrAS70MEjKql'
    b'AiBSBwiQkfDqm16qSqkj3FeqvmLfnRNcksFhJCCCaANoYU2cVWkobc58KIhm1udqhgeA7YUK'
    b'7WFAzeFgYvCE1/m0X3ZdetfedHJuxhWApYQnAHkY+5cuTQjckPBn3nh0+j4gwG/TrMkKmDbs'
    b'ANcBpIvV+CSAX0Xy8nOTbDalIRGghCo4wiaZYKxUJN8KLqIAyNyA8BvJ0phT6sfVgFpqFIJN'
    b'Qz/pLtb9fkiP2UgDkMfV6NqoeUhdPrCUnqip3np0//gzJDsJz9PsBaOuynUYUe1haMZ1xES2'
    b'ZJe2qJJzfn/u8rcKWi5OFz/s1V8KXvWX10B12tyPd0ew7o1QpxzApT1thUgeXl0sB7lqmCSo'
    b'KkioAH12MFsAsMXZwgBUEqluFsiJkfsCYJBv4uj80uY5VPeHAG41xWvvRAqZnjvadwA8fXDq'
    b'ePvmMa4YPYKnsddqG/E6fwWZGhKAa9GvNrxzbzVgeSVU+3m8frIh+DSExzuk4XtfaWkwV+n1'
    b'hhI5GZC8WW2bQVEtjnsAWjQ2tfCnjTcNiIvf5aYKjcRdKgIQNq6EOIdhSWg0v9jDNl2rfSfK'
    b'Z2H7tlfVQQJQBbKWgmE51LIall61ILAicFKH8S3U+qoRgtl1kIdyjZI/ClveAc1u0XglpJKH'
    b'cl9E2lJFAHhdDQXCKODE+m5KYvChdGnS1/hdNulzT3DV5XQrJ7OQ1JK7nnP5srpuuXSfwJNR'
    b'muz/XVPF/Kq4n/NGmtqWr4rjYwwqP5xNtBhLF5P/6YimVoiQJsggMO73VsNORmYzT8ZspJF8'
    b'C+QZCDMY5vPpo8XF8ohmmkz7i5TSEHzvRID7OJSNpLB5phWSS2Kkdx2Y8Rtzl6/IddPdn3TV'
    b'Z91V7TGgckg5LfpJn9bb+brvWbKnIpTjQq7FZDbZr7X+Wa/+U2uwar2OXRDohe0MC4QK1th+'
    b'bDKfljgWgcahyknzUBc1mcUhgKcn5yPIWylZDU2ZbhZOEa9xz6PyEATgtwI4v1bla6D6QlqK'
    b'muf3ZkPdfYgAr+Y3vBGBo/4VoBuex998V6jlQXQAhl3xyZBaJ01yfXw2pMOQqBvP7e+OyPyv'
    b'GEMqf98P/Y8/1Vwom30AsIZ/PMUxb7bWAhGBmTbbHA6nEKyiK8wnkAf3jEepBoZqSgIJuyoi'
    b'sc30MaNAMA6KZMhbVJMGVEmWlLioQ7lYg5XXsSxABu8QKqQFhZfN/RPglmjtaQgviRtbFM0G'
    b'AlnAlJuJd+LVHwH8TDCKxm+FBcAmEAXCwI3xuYdw/sS7bq3efOnO8NTzt5+l2d+eiCskH7rA'
    b'6n5z3bvqGiOI9kLSmwB+9GSxIoAanTHZDhvmhysA9puFIU+77AJ6SDdduA4oSbDGQNkqKiSp'
    b'ZFSXks+6pJzTAyNfgrAAYbMuT9x96q4u57TaoQ0igHG1WFVJyyB3TJAOJD3hrvNSyiu11AOv'
    b'6tx9LtceoOsSjszsJPe5pvz4v0lKacrIUoycumUwop7UWl9cA8rfH6k7ZX51X42W8A1Bsf2E'
    b'pCfrWG+Ex3bPa9336hnkxGs1ubqyibdLqkphpaCZFXeHXKnKVxAmkg5y39XUJdGMOSeH0YdS'
    b'h3XA6/2Lxeq4KVDyXwH4kWtVwk/fGN7Q/yBCOU7CI7iKZP8H8Yw/HoGjn1urhBf40lvLZjGL'
    b'sfMcwrkR/aBJebKdQsp9vN5r6ju05f8GYBs32cV7j+J3/d6vRKWfLOlhS7vRqHUp+rQh8vYd'
    b'VS83NwQCFuF5rDuxpUIUjRCg+LYC4JzJspEdzfZIpAioSCCm22sIF3sAWyToBEgBBIRI0Ia7'
    b'JLmpSOPZkOtQOrknAL3M9sEItZA6Se931zMEzgSNEKYknwHxJIEpVF8N0/stlz4J4NcS/Byo'
    b'90L4UQh3AVwVdAbhfjzI48vXLPw4gAqivPHp187e/5EP9GUo/1Kt5YrEk+reSbotaSlgBHAm'
    b'oJd0RcB3ni5WEdSJ1KgMUW49OLUi4r6JE8vLsTCZEVAnoZBIAJtwhGYRIRVMAyhebTGqToFl'
    b'l1MlWOddPgNQzQxm0M7zT9GrV38MWO46BTCLOKV+3Q9qqc/V4tndq6D9lNKNruuVunRyCVNm'
    b'NqFxvlXzgSLXKvITH4W0UddgdRPAPwzgX2nyPBH35SfDo3YwrsZvenjn4d9JM3n1g3Ecb+Sc'
    b'yrAaDyAYCQcpxOct2QiAjgpsPKwVRCfhCgnVUlnHitQnr9XLOJZhKDV7qYlAL+BOxMN9NsDq'
    b'i201VKjrMbHnDSXyaUhWPx7bL7dNw7i/F0G0T4bN8JkGpLp4P8U+Gy3Jt0wqMV93HAST+F5v'
    b'hIxrkfv4etyX8k7bsLqQhvodlEXsl9gS4a2LC0yNsXE/wKQ04Qe7BkcXmUUEZQrcNioYw86T'
    b'IiNLIaUIFqKUdqUDIqUEuSD3cevKrC65qqq7xnLm47hAqZVkJzJRGgUmEBnCBK4OUhATIuUu'
    b'+/6V/Xf3s8nEq9d+2n+gljrGxPrqsm4ppfeZsYL8y+V+IoFe66mke2WsDzZecz0A4F790eLs'
    b'4k9DGF7/zGvvufn0zW8HuAhP6kEkf2cCDwgcxr3+vWuwWgHIjejOWHVfbmikrzQDbmxUDavu'
    b'SGbF5QHqSgSSCEMEaMdxARSjpmF1DatSJylZvz/p0CQNK4DjTgzE8yZfc5ztzxYP3np4UYu/'
    b'Lql69d/o7h+QlOTKgjLJw/n+/GC6N51sB7229L8Io35BgDei4nbu8gOEShy9x06L6/kUhMna'
    b'DvXtAl6umwjxk5TsrbopEPtaROeDYAXECI4e5T53Vy8grVYjABwIWEWAoUNKIgaAEpHG6hjc'
    b'BwAl6JP/SADml9J6kjMS9wGMAI8BGcAJoKmEJYkMcJ+E2cOzSSNJe7OvBjDaXkEYaDUdTCcC'
    b'exFfX8lvbMNNdsqMjbHPlt03jpUm9tAaYDuL96fRK4Dr4S19Lby+eidVws/GhHcBk8ZuZALU'
    b'XLgCsBSWIoRruydwEBfvzefZpK+7A0XkIMBogECIhLDN7DeQFUDHALyAvNbMvm3y+ORm45FG'
    b'L0ijfN1LOdFQHsj9uHkYAulxWRfBSnGGEIFnB2tF42j/qb3D+Q2aWQCpIwKXJAmCx0QfI+dy'
    b'Gd7QM0krEBXCiFDlVhfLH5NQD6/uf32wEqyqayL3I5dGAQtJdNdC0Evu+pFHF8uuYWm91iSP'
    b'e4DI1TgelYG2InoMftTYlr1J5xdD6Y14muQRQ00Ij+s5jZmAkawQzkgc3zzY+1w2W5JbO1CJ'
    b'ogrHAIamOniN1yfxt++s460Wkr4dwF9D8rmu725M5pNbucszkoVkcvdkyWYxqpykgxi8+gWA'
    b'hVyPLFnpJl0HQHEfjsM7+HuC4mf1+st3difCQbBk/IZQuaJOwRb026bcZStjyXEfXZFEr5B2'
    b'XKoMttciPSIwcSAc3RiL9NIo/RcCfsu1qhW++GZdl795b3/2b6dkvfRY3b5Zq/fr44O74j7r'
    b'UdqULepns0kQXkZIx2WP9KQAhhzj4kdDlUxpf3pBcBRxA8YbAp8T8UQlX2iCb/cb4KrxetIC'
    b'X3vdDaBNdwWcuJ7jAPHfE3a9e3gHG3/bD/z3/04jAk7jYrvmwqcIBCWQHaiMQM6IaKeRhyEe'
    b'15jkOUjkJpHTJwYqCzBHtB07VeyK8TpAigE0cSLa5l7rqOqFJIK2t8Ld5b7QWI+DFqQtrFoB'
    b'DCGpXIvXMxrne0f7s+l8ethNOnZ9t7l2YtMUkmaAQYjte4AGmnWhwiSEtBAVaI7XEtZHJ9P+'
    b'3SmlJyUsBTHlNI+yXsdefenS4NKhhP/t4fli3pQq6xspNv4+DkOsP2zYRtlUEC7NYFa8PgmQ'
    b'miOAlKEax/M9JzgAePTE4d6fnvX5B408bewT503RhFkTfb1qXPTn8Xf42ufeeE/u8j84mU2+'
    b'o+u7W2bWgaC7C8I0HCsRY+KnZkZ3PZL8HkGncZ67/FTKaQHglUYbeCvUjP9lDVZxfNsY/QDA'
    b'3wLgbwbwXKPanDeFSksD7I5NG4tUAryuxGLL+MyiSMtRGqK+2ejSA23So/796znf12p0fHGN'
    b'JPv5fPIXzfdmf8Nk2n0I4K0mJnKgMeysWxuR+klnzfjNDUC0HmwCOLa++34QxmQvw3iN5Ezk'
    b'FMSnBT7pxNdebkXMYvwMu/boZtyhcQY0VcPRRa9xPOYVHgZo/u8BpAPewcbf8gP//b+KoOcN'
    b'tO6a+oNASE4NspYdkXS5I0Z2MZmuxGSwlFOO/MRuM2DV79DaSkLg2rYRjY2q1TAD3ST3qg0g'
    b'eRjSq1wOoMDlqPVC7iu4WlHaYp+N/aeCUNd3/eG1w72Dq4f7lqL+sNHiHEmCpBrBk5WkWbLO'
    b'aEmQe/WKmARlLMsIcRgoHUoa3DUSMCZjlZ+646y6s1Z3Sd8j4M3ji+UMQancGNdze93N8S5W'
    b'1dlOsYJxR12oDXjVnR50N7g4nE3udck+czid/B8p2RuM72kaG7f7rk3TJH1VKf4bVxerX8XE'
    b'p0jehtBHYOUsxtdKLiJARK5QKVCZbCX3s5BaJwTp69eXKmf87U9FDuUfWgPWxRfwgO2HlPWP'
    b'xTjc5U1j61gKb3l8DldIzsITbQFOCwdsJZ1GPcReG2nvcu587IYofGnt6sHh/G/p++6bL+PU'
    b'zPi1ZAQYk0JIUsTWLuzTaW/x/FYNF1qK11cALGlWYHyDZvcAPQ/jDYAHJC5AHivZCWhLN/QC'
    b'J2JDJR0qX/OMx+Y4Q9I+be7jPMZebT6X49p/OMrrfyrGl95JwPqfdhAzxP24gOjNSvV29QZr'
    b'c1F9E+KQo0/MrNPW86AObS5bbIOyA62RnYhYheZEuRSR60DMhsATD7XQIRWUupD7CNeqEV/b'
    b'Woehbm3DucfJbLI8uHZwsAaukOpAr46o8owYyFXavnBJFhNPiAct6QJSsWSElCQwJXvCzLxK'
    b'XqsPxf3MpeNa/YGkHzlZrI6axWPS2BhTCzaNzWoWv6GP83ILKtHRfDaur3mGsXJGfxg2h98G'
    b'4Eeev3a0xBfX9kKq+bYIMcCwHADiJoQnQPRBsZ3NjLXUR+7eSn2Qawi++NclnUfoyBg0QcP+'
    b'0d6nwvP0o2uwOv9pgiD3I4n6HwKwCPUnNVLVeVPgYQDwxtL9dQeWDnytSzeqBAFXBbzehO2E'
    b'IwT3gyL7f7tJG/Glt6cmffd1+wezv7Hr84dJPmfrFuKXIdRwSRVA6brsiGfajA2BvADAkJjP'
    b'QY6NF7mDceO8IDPNXmGyn4LxjoA/AuLl0QyNLXQCoAYIHcXxNjD5vAlofhj3YNqA/oNmvH4y'
    b'Xp++80b3+EPR500QqTWr8zanrwEvtgb1ZlKlJkYoIb5bEprXrbSAACuQj3sABWBRnl3augIj'
    b'Z0bi5sSQzeKmtWJYqCuRML2Krh1xdzflaBhWw2q1GBarxeog5RzSn1RLNUkjyRQcbh2AjsZK'
    b'slq2aUh6GaSFMX8FwL3U4zKM527mJFUlpEn3AFVjqB0/vgardlEwAH2zbR0iaqRcxHtTVe/i'
    b'jmYEMVyjHh4DGKOjAbMMQM39mDbP2fHFtxWAB21RjQhFKTQuwRhLwnkt9RRkTikdScql1CIX'
    b'AFlwphmkc0u2n7vumeP7J//j6aPT/2v/6IXz5nd/vqamItP/AuB3HD157eD4jQfTAPZHiNih'
    b'o6euG8llMIyMn33pDiL5eh/AGESSD7iVcNUFPbUiyv5/BqD+XbfwZbS3Vi+/9fG+TB50U7vS'
    b'5bwfzw5efUnjlADcsQTRFeA8YysZrkCuSAzxOkc62EZNF+aACLJc7gtawnUBqcr9GZodIdmC'
    b'xv++r/7ykGz5NgwrabeEXjtHYosGA37WWg40vhrIebux8iMGyDwG8CK2kwZ0atOHOD96AESI'
    b'ranLTiJLuAHpupokapIttaxAgvGeBI8w97iBCNoTCVCr/uRG4lO8rk2FY2/eG+L4SbPyuqSF'
    b'qiZru1M/P5x3Eqyf9lNLluWqAFIt1TevvdCs1lIHud8HuKeNK/+iFi+SjxLqsFy94cVtMpus'
    b'v6lbcuMZfXG5Gh9M96f3UP0nH14sT/NYr4E4D3XjQc0pCl4gW/V9T3Ytfgea2oJLX6zUDCoH'
    b'mACYCMZxSrqA6yLk2J5ED3Jm077NblBTUmr1JQJWDTD4AMKcsG6TlNOkjOVC0inBFNWlF17d'
    b'JN2i2ZxAL6i6q5LIcp2dHZ//vtVi+Wfc9eDdX/XCo+P7x/z0Rz+Dp7/mBQCwq8/eFKI1dlc1'
    b'RuRWXfX1+Y8AnDYpTFcB7DUG+bo3n1oyPg9idNdDdxmAD2JrrQBr9UHSPUl/4rlnbz0BYHH8'
    b'8h3Ed+wFYGRFYDKAlaA6eeaWQTgAdAPAeyR8W7qy99Sqyx+GtI/qbuE5DwioQoT+OEYmdBVg'
    b'MgKCgYgQF2YQ4a1j15ARpLATHksaSJ4HNVGFfIqib3XoWQgfx3J41IQrPGyKuZ40mR6vRUec'
    b't2jmO37WQCtUwn+kCRKbNytsiu009hHgdqWxmagx/p1Hb3VWre0PELYi53u5AcMuuOM7IdzG'
    b'oWIB9NDExo1a5Qxiuz0AnarLa/VaqrOJfweELcC5BEmoVapeIHkTY6KdSPwxujexJiPJRKOT'
    b'dDODoBXAMaqoZAWrpqQXIx/vhyC8R9IRiIHgZySdALhUA/PhtYMn1uB39VKycPeHe4d7nxDw'
    b'A5J+4N7r96+IeBLknoArIm+Dm/ssYAryush3CYh4Jd2l8GZNdr+cXCzQLkBEIphAzEDugTC4'
    b'zuW6D+kUgoPoN9H7TEj2FI1zCMVm/QMAnwbw+wD86OLOQ/8ClWn4oQ89LwAMyeTvD+rqKISx'
    b'lQBWy/PlhbufA4hqM7hVq/eQJjROaqn1/OT83vJi9aKvAVzSnwq65AXJhzT2NNsnOWXiwa33'
    b'Pv3+iGxfNuR8NcbvPkISaOw992Mbqg4mOxXE91JOaV34NoHoo5TdcwRecNdcYXgPG+tHb964'
    b'8t1Rp/MCwiPV+pRXfxZAvbh/soJBaX9+8ZiLbBi/GcIBiH0JWbVOBDg2eYjMxoM+p57GOp30'
    b'7Pq+mm2dGS6h0vgoJwPAEdBBFMzY1lmEkEIpoTafcUiAsdUqEqSQpFgisHIhAZDOGqC/ANA1'
    b'CxcAvBFlxmpEsr/UqHyxuP3sAdZf2ngGr22N5XGsUU1msTI93RTj9EbtOI0f8RYivWD/6gGC'
    b'p+nDYdvosF2F1EHaC5zqwg6VSFavfhEqRQXRk+xV3ctYWFYD5fIt8JBtwVRCWkJaQaqovlCp'
    b'n4zrtkbq62KbojPeb4Fs0qjCNXpptqmppfhZAN8F4Eea91J8drBk/RqgfsPNp258K4Dy4M7D'
    b'l1fL1Y8++dzt77n76t3j5j7PRHQApwD2QZiA2TDp9gBc64YyB3kgoCe0gnB+cXIeCwQziFtb'
    b'/vKgVEHUzIPrWMVfUa0vhr0t0Xjdpv2vp9lNSedYd5X6OV+NP6RSX2zc6G1jdHzw69+nz33u'
    b'jW86OJj9xTduXPkGRFGM5XLoptN+FeMCy4vl3B+DphcAV0leGYfC8+OzB8vFCnLNJY0ASqzk'
    b'H49g0BOarTtfTznNLNnN1Odnrj5z86sDsPYB1Bh73gRBogm4PYhndNGo0F30CB5NAWA6WCxW'
    b'm0huRUgLOTHj7QjfSQBOuy5XSLOoOE1JvRrzSIzDqVefkAElktzldTV6HQslQaW6ABiRSIqg'
    b'mXHIXV5OZ9NXVeqPHV0/PDdswekJkDdA7EE43CxKRhoTjSuAb3kpKy/1BlzzkNY7hLYZ9lft'
    b'2C7vq/oDABMmm6l6CrDfa8b9W1FurURU/5txz+9GbF7UKPjZAawPNupT1xjL5wgDXmNwfCYM'
    b'mIwe6iHUGOKWa6AK1ye+FsC3RRkv2wJV2DliUIyQNhzypY6p77jeVjMWy3kGaPCxLoaziwdl'
    b'KM9g8/kCMMMuu21BBlIK17AglQCstwJs4zws43dZo0Km5v1hx4PE+MwkevuZuwGGx8GG+S8B'
    b'OI/XRKggJF964cPPfy2AXxn36sde/MRLn/Tqrx4c7aMBzBfCu/Vc48L/FIDaPIsqYgowngnq'
    b'2fFZhZAIvFfrHvQtVdDLEH4IwCMQMwh7kOaSBoJCsiPr0g3m9AzIKVzHPowf11hflftJTPIH'
    b'DRc7G9BakOQ6Zu03z/dmv2ayN/u6yBaI1ZsWavYKgGXy6hqc7p+fXNi4Gu6AnDHZbRLX5ToC'
    b'kCSdyHWh6q9L+vQGsLhnyT6VunwvT7qn1tt35T4/Nb96EDTRWwBaNAbhPbTR2pFXmDapRaVR'
    b'n3tA+xBuCZjG92UA3TCMxs1iuUegD1WPRkiuLImQKEBQm8IGSoiXAoNEGggmzOqqQ4GP5XLf'
    b'11t5dcUHtnbHW8/e/LfWY+O3m9mTwaT6LRI+sFquPjCO5YqRXe6y1t2NvA/iLoSZar3q1a+p'
    b'1oOYX4IgtEVmCREcm3smSVcBqKwbAIi2BFQ6s5NFqctZTuEJxMuxACwiYPajMd71swVYaWfl'
    b'7Bq3eoo+jeMJ4VJt+hQRIXt488qCQAJ5m8SfG+9VCRNIXYisM0iMz4QqiEHSFFCuQ0mWUm/J'
    b'XKWe+lg+OS5W37veXvPqfzGEc0GnAp6D2VWY5XjSjLyzKaQC6ZHcp6geKRCRhxfXGsAybQb0'
    b'1VgtrjUekEmsHm80trIhAuIeRdR3CnB5FNvWQCkAXNthHMBeQ53yMQD319KVdjLn/0IA3xrS'
    b'QwfgOwMwhpB8n4vrQ6xwC4TEd3G2cJLfSPKDluyWJEI49Vo/6+7Hct2RayVpDuC6JEo6AXkR'
    b'k4ogPQJvB0j7IRVfbUqhfa4p+vDQchoODvd+xXQ+fb8lSynZoZOjwotJcubyk6HUl3w5PhwX'
    b'q+uPQZu0PO2Uum5K4pBmR5Km2ETJL1Zni3uqfh/APWabUFrS7NVu2uPx2MjpJqTh4PbVvq7G'
    b'681CS2zapHnWjNL0lWDYXTCNHlKIJhDyTsgGXKphSY2ajMH3JpkktgD+dmxyUst5qM3WXbXU'
    b'WhbD4LUqvOUWf6N6qUPKtkopPXrimVv/x2Q+mZJ8zMoq6bmzk4tnF4vlxDdOlnJ45eBO7hLl'
    b'snEsl6lJ1wxAv6EtskaPLyEhSjH3EGpfxG/PQQ4AxnEYRwAOwsaxvpQvY+nIvdH9VQEXxfXa'
    b'YZ8R5oNXYlx8D4DVzwZgIRp3Jo81j6DunJPbwXHzXU/MJF32p+T6FhC/icA8+KwOTZhBmgqY'
    b'ElhCqqGHG+QJm/dzMCJQhAMsPpYHeZIflOXgBvY0dpDoQq615mEoi+Vy6OVukh5s1EEkSGdy'
    b'fQbSBdwfxQr1Slz3zZh8r2LT7jYkaa/H/qqhDbmI17U13LeA1HpXWoPvGqiEtjWOhgasWtaB'
    b'vy4krGcBsPnbxzG4LAArx3slOlaLlUi+l+RzNF4Nb6bkWkgatLk3S5evICTflPQ8c9dJSLlj'
    b'E/aQmjyxOYhQp3kCg8FxxmTWTfvJbG92ONubboo9GM+NnDpQKlABXHNgacBDS/YkwJS6dA1A'
    b'smStJOIAMo3JzOilFstJ3HxnhSDVei73pYofq9YqQYTQ78/71fli7Kd9aWyRbAzGpFlbBj4R'
    b'oLaxbCHxazvmibdvkqQAK0AwtQzS+rwfivcEuYKXrcLH4nLRixOQ5FoRWF72lNOyn/aP1qE1'
    b'H1/f2zdJ3gIwdffr9+8++trFWsUupRpJXKqnJJFz6kC6V1ddt2ys+/PZxIxh4IWCDigJynHZ'
    b'BoLxnguCXK6o2B5R/5I2wD9Ary+L/7gRtUrfCWAxul4Zqv/ke4/27gHQzw1gff4mvH2zW8/f'
    b'PpLrL5D060KSCFsCpub+Hvp2mTECioKVYTNHjaD2QsIknQXAoCyGfjrt03I1GoEORKru53It'
    b'61jvnT46+d9r8RWMzwYhoCC9JfcX4frc5X5cd2mBJnrfSE19A0qKrTW2Ljb2HG+Bavf+rUV5'
    b'4UtvcwB/A4DfFGr3rPXEtpQyAVrT5trl1UdBGcIs4psSYpaFanbZq1xVEuTqJKVaK2r1Isgh'
    b'MjxQwQDPQwg1ci8Lk/WWk5GQ0TxPu9xNutr1nZEmEAONOcq59THoJ7nLo2VjSsktp2nKaRvy'
    b'DRKQAvh36STCfOxObWySLvcq91HVV3K/QLCcRnByGyjrjTG+0DgQDGpl7IcEtrcTpoPWMaMm'
    b'/CZi+xj3zuSeonDH5/XwS0IYtQOxoGgMGqOlqhYJOIUESROSnlM6W3upP7uOPfsJMwOCEqeU'
    b'+pH7bz38hsVidVjGkqW4W0SNIOcEsyWlYTbpu4O92SzUWMRlNPRyzQ9mbBthkCQ8RMSqjRfX'
    b'IZ2O9eGi1nMJ/yuJ31dcHwdQP3BlPwMYfzYBq21sAIpPvPCk8NO3pyVdTrhvCgkhEdwXlKzU'
    b'fbr2AWWACr5zI1AFiEQMNFtl6IwRpBZesiMAF8NqZD/pfL01QRXCOJn0q9c+99rfVcbyOWjL'
    b'/OggI+UAS0iRlAqzw73qJ+eXW19v2QBCO+K4C9Bf4Phu488AsPYjD+4vDMCKfEFMmnis9DZB'
    b'vDXqIpbNJFCo703A76ZRAqQt8yrdH++lGnFy0RS3orTjgMHOajmJRlgyWUpKOSGKfsiSiaTi'
    b'GcirD3KH5ZSSmaUuW550k5RTLwCBV2z4zBCNYLySoCBflDtUqyu8wAHAoeaBlsx2HChqI/xJ'
    b'jo1pw3adLp+HGRSS0DRGuHBIWruz5W3yXcH4IagAVpJO3f1RjhhDVL9QrVmuuRk95Xyxlqze'
    b'mO3PXs453wFxAOCpcSjfsJawnl4ullZKTe6C3AmjiawycwkuwECkac6wnNh1CZ0ZXVGkt/lF'
    b'ibSiAFHBqwR3VZd8dK9VKhBcUGgeWDGl+9rUSvzTTfQ7flYkrD997/e+k6RgHwHwV0QZ+Kt0'
    b'T6x+QOEgotvRcGPVxhA4GjGQeMvAR2Ew7reDSIiS6+gJVIREJAGTbP/Ty595/bve875nGcF0'
    b'k7ixF5/4yc89+OCHX7j42Ec/fQFAl0D14eef5Pq1I9ouUL3dfgDQV7ZFResID7gdEkBL3IfY'
    b'pgbEEuI8SeHNRO/VF7XUcE+3gLVtvm6RMg46NPctIJJbMCTUBtxaMua+kyWTmVUQbmkDWGZ0'
    b'M6s0c0uG3OcsYCMRCbJkXe7ypUQ2tZSmkgzY5hdyK5IQLT0jg2AeKhWhlIXEKQcgL34KqEoa'
    b'IAx50pUYH2zskxexX23dmhCdp8PjFqDVzjhRrpZ4ErElJGukK+5KWFJDkC8JZGnCBAYAo1x3'
    b'J12+S7nD1Rkpktnl85TyajLt7qUup2QWn0Nfa33+4f2TZy/Ol9Na6rzWOllvrZIOM4m0x91I'
    b'bO8gCSMhCS4hGQgGD3TcbNDigssWfBimILKDUAFdgJxHUK0A/GRwdf3fTS4hfqEAFhCev/CE'
    b'/dN0fz9dU4ZkFeEMZGsXIwqFkQFCoQ3MQvIyxmSLslTdDpgYgNInu9eZHQM8JNFG6d8Pm9Sj'
    b'qJz70cb+pJdeelP4+dMIoI96fH9V8/p6SwXSqDh9E0vUefUbEYPVywV3XynSkUDuAZi4e4l0'
    b'D3chSZ5CWVz5xj6SQXQAA+BQGrWqgEiNlMWNhGWWck7WJctdNkuJYYcCjQDokESj8iah3EAy'
    b'ymtBLoSaJS8VQoBq9XUTAwLIKJZoAJu8zgqheq0LSQsIj7fdtB+bQGaL/a4xwMvMWkLElhcK'
    b'Cq+3Su1AhAeSXWPfomIL6POYU4jIcT2H8ZTgKGDQWCq7tNJQqtf66tpg/qoZkcxkJAFMSYrk'
    b'GYmewCFJxRi4IaGcn11cPX54+qS7d+s+SDiD4JLcSVWid7JzaSowi+wRhWEEGNgqgigBTIBQ'
    b'w47pO71uE3iFBaAlUorEeHwSwO8E8KOZfB1f+caMd675No5GOAvj9yRKh0IQWrAKA+NpaAKH'
    b'EPYEJKg9HVKwjZJCeIm9ZRUFeTf+7iMJPQk0tqlbccO/PozsD+K1nnjyGt6m8Qurx5+/3Xnj'
    b'gfAzaQFEcc23Qy28saPe7NjOYkUMllMIOfIazaFrcAiGPZKZRgrbkjeUU1Fw0imNlmguJGg7'
    b'OB0RTR8hKF3Uc4RtjOaW+44pp8FSSiRlxsxkCEoUhY0qrhcmKUMiLJGkMRGh2oVhPCQbIYUh'
    b'Og6JKhWqDrgzzF8Mx93GeWOYQGydQW0Rh7Hx+pq7q1Wn433SKB9KByCHZBFSvjxoROzz2NjV'
    b'2AsrhBWlB1p3dvnhMI7H00m3RJ8XAMbZwXxFQiSLkWN8QU9iAWgu4YBgFySWN0ObSHK/sjhf'
    b'PhWEiQ8l1FpqhHgpCZTMBKISXJIykRWSF2CPZnlbwFu+2IKSVBVRsTGWakjYDmFo7tWmBkEd'
    b'z9l35wAUWsF3/mwt6rneeYh3qDE9cVWPgYH490D85zA+50IxeR9BbImKIhcUIV6petwmAFJT'
    b'MsfQLGFqireSSEarJEQgQ3iBxlNF6o6EAsAbI7qRyLFKPfxp7E/bydWQ5z0b/UYTbNg+xAsA'
    b'p2sAjEBJjIjo4DWIneCLa4qJ9UNx3X9rqCwPEK2JAcNOjidtw65Zw85gJOcmJKcMkkfkdtht'
    b'tlmYqi7JRSCKq5IpiKxreAb3mgDiZUhxZin1qctc98dYZV1iynndjTRjGIBLPEsprM/jaiQJ'
    b'yiUaC40ZIMmG74wWnwKZCLkEEuwIZ4WKgAAygEZjprFHmA/lqiAYVXxym8nQdCHyK301rpp7'
    b'mRp6mQziQkAWOAURRTyQ4ncFvx9jnqMGv8gI6XUBjyrxSGO5WIPV1pkz67uR5MrWneRAwkgu'
    b'gug7kRE6ROwDvIEgDyBxHcaLrs8PLi6WB+MwTkqp5tWzpK3kF5djoNGphMiHZReeDkFAPF/B'
    b'H29jdsUzj3kT3SIpWjoFONtcC4OpAW9EQOnFz5LJZMY/8RP/eztZ2/20M6FSyz7a5LXN0ny6'
    b'SReAZtqUiH8Ort9M6VdAumrCDESlVAm4hDnAHlSC4vtIaxavFjwYOw03FtzAMunSeSgiD8NA'
    b'bXGNbCKdPW7s7wfwP4b3cb+6/7qxlGcB1CbmbH/L9xRG7wCtLsBo2diUPI6n6DWOnUfqwv8H'
    b'4HvXoHWML7I1YRfvDZ7yp5uYuA5BwdxULvHmt+ZhOZyHLesFSbchHADIgiAwwTiBWZJEr3Xp'
    b'rrBrwQCxBfOG92gEoIi+T5aN3aRHN+n6fjYpllOXu0RLCZYMKWeEV85pRjO2HrNAIpJRMlkK'
    b'cSkKQkoOLw734ElsuftdhDvgLoShK0IFqtdaowDGMcnHYBRSIrpZbwBE11ZKlXEEUC8BK9Jx'
    b'jgDc3EpJG6CaiZuFFoQhIJUNzxHRsiNhNOklSK+O5KVUddaA5FsAhr1pXwOwLoxchPoHggck'
    b'bm4WRr4HRMeIHYz4QAK47e4vnZ5cnD28d/zsajU8XWvdIziC8GE1dnF1DrKArGAY2nKaIqU+'
    b'FrALJArasLqCNJBbyTIk7BXcH4FcAXoI2hS1fhLGI04nj+dQaC2/C8DLmRS+ki2yGC4B630A'
    b'0FQTyZHeMd8lOWvfj8l8aJu0ketQ3Nyc9iHdgvw2hFuRpLkUVBMggBHkF/7WMAsiHr/CayMo'
    b'EwzUV8NGoziNSORymtO9nGwAgCZOLpFYtBIQgLuhb39cUrcax78BwPub3Dc2xuxZAJQ3UtPh'
    b'DqDnhvGhi/6oSRz994Kls16/cUVf6koSWQETgr0gxXtjrT4fhvEFr74XEtPw8otvjABeP7l/'
    b'cipoSvJZgs8JugLhAOQNdenrkdPXRLn+h+IWBB2Cwx2SVpDOY04mENNGopshVl1LaZK6bP0m'
    b'+PMSsmjJbN0RWw+uMF2+h01TLbV4qXXL3Ep44xBMIKcRPwavTkmkEWaU0UDjprmHu1Nex+La'
    b'vA4plQLxkOQFAY8vZ9dnAjAEXRDIDtIYFXOuQgqpApTUiex9Oxa3NQvMAG8M7t4ULlCSXjLp'
    b'ewXcgXSe92cX8e4iFrF6ZW+2IHlqG6AybKS3GwSeiYXoRixQNxuVXBFh/pKkP1PG2o9juTas'
    b'hnfX6oevvvRmJHWjRxj2Q1VTIwwocmrvxXM/BRFxhlQstCX60MQAfhbAHbtxJRZI7Hqoh680'
    b'WDWLcn8JWP9FXGxb/WK+S+wVUsf9mMB7JxfLBGBqxkOjPSY9o+Egmd3qJ/1hQzBnkMg22JQg'
    b'FFsQCPVZIhlnRFxg20JtgJpiFIK0MuOJgLFLeZWSLaJ64ZDMCskFiRXJMyN/AsD/7dLtoYx/'
    b'l9GeAZCjPmDr2u52AkT7+D1jU2a9awaTxVZxDkn+K5L+GIBlAJbFd1kbntDYWsbmuxDvP9Wk'
    b'P6VW2g1JYIwcvIckH378Y58bH919FJWF4zrJJ2B8krPJvwLyaUgJ0ssAnhG2uXgjqp9AekPV'
    b'PxmrwjeDSDGJzkE+sR0P0gLAMc0OUk6rdSeNbjmPuUsTy2lhySx3eS/33dSSJQCoY61lHKtX'
    b'L1AoboQhVvhIcp4IMK8u90r51hQGGnUJlEaKLsi91tV4RomWrI/8yBVJwXUC4JjEqIjyTjkR'
    b'YIpKSIckrrS8W9HpYBf7FlWuKZfk8nVjgFQY31AprLL0FjfS1UchfDI9feNHASztYnmKmEMN'
    b'E2y9ebSfAOyFzfK5sLPuBVh5gFWOvMrPRlT52Y/94CfGz0OnpJ0QmXfFd7XS/3HDumBNrGRt'
    b'PavRhzauzW5eVbyPTOrnyDGFS8D6PQByc0NL/FBDoFozWRaIVJGzxcpA9gT2SR4B2Dd7XMft'
    b'yMwmRobNJGQoCEayQelGWomm1rWs5lh7FnfsnJC7issryEqwVPdh3TszG9yVXc4+5zckvSVI'
    b'7noiZzuY9l3JOSlnW+aULO5Ba6dKTUEENkGHir7H8O6AiNQPJYAw4x8G+QclvXXlysEcwFHc'
    b'u/2m2Oc0tsfRPRaGRZz3/oaDzGOAlUbVZYjlPxHSXWo410vwzPc/9BMvfgOI/5TATI6HoO6q'
    b'+NMudXIfVKrJVVTqPUifA3CGZN+wtQHtsovG/WiuK5Ekc2Lq0hxgCTvHNOcEGo3J+sfARV72'
    b'TkAGmWiWQbCho+Z2HLgTgkkiCdGMltM21BSbINLV4y/Jlon4JqCg1qWK35H0GuT3abbfdTlD'
    b'uErju8AIrREoKNEsQSoiZwJ6lwgy1+oopVa5y3cokQh4AhYmXSiYI+R6mI/2fwB9/gTIuyTv'
    b'PLzzICi18eJXP397tZNR8kKASxA3biWbz4UaeR7PtKzBSl9GqBFbs0XThV9oLQDrtzc1x1Ij'
    b'Fh43koVaF/fFclgovFphMN8HeZSMN21DA9KnZJ2RqbGkxlhqDVJNayQnCNHUnNXuN6kOUjjC'
    b'VcpmBVxVl7n76BIVUgsBF+ANlU7qu1zn027suly6nMr69SmNAwOQGs+IGnXY4j6AxgOjzUgk'
    b'khOSJe6haPwYye+GsNjbn6VGattvuZwaVctD7ZzE9+9FHxrb3NikSs0a9smPNfQpZw2v2cWd'
    b'+ycPJH3I3b+hVJ+Mpby2WpWzUurt4o/jtR6t++tydwCfDG/gLY0lbGfATihJQmybfU9d3s/z'
    b'ydS6PKVZMqNA5q2Zh2Dsbx9+E3GVoHYBE6WYcARaHjQEcIVZQJCKkWYEE2mmICoay5kP5TW5'
    b'HqjWT+YuP9916TZAp7EXkFWqAxgFwhNvIbxxVVp3ZI80nPaKQYJGEHQjCqTiIC3BBRYBK5Hn'
    b'AgYJI4iLcTU+AvADEbf06TVotVJSD+CgtRc2DBRtE365hWTVFjkI/quGKnWyU1Vj6HKqUeQy'
    b'gZzEDe8JTEkmM04JZEpEe6+l7R4bja9p+gLPR80xqQFRCYOAJaRHLpwKIM2UgAypq1KSNAkS'
    b'tRwqRgVQXBj6Lh93XRpIrsL9PiN53hjhewCPWpWMZG9kZ8YNT5NxRbAPNerCSIC4MZtN27Jo'
    b'F7Fl9JaHrO5U0O4CrKbR2cQUoaWkCZWii45WNV0Ldy7oplz9WCpLqdeWk3Ll/GJpy+V4UVHP'
    b'SX6XyAnA12CQFqtvDVUwSANxAeAIUfuvCRF4M0D1GsgMcELS1m2AsQNg2k52ugginiNFSbDw'
    b'CTOsQTUK4wpQ6ykWyHgJk7u28Ed2TiiCuGBSBJJxIumG15oh3fLleLK8WL3a9x1gmFNAXNte'
    b'NXuqOKeizEVzKUsyAUQ0Ix0kDJIJlfE3HTBQY3E4wKUMF5AnOeYRWe/c3KsjbaQlP77zkEcb'
    b'j3pb90/45fZFAda7m5V0aKhE1JSk7tv4lkvAGko1AIqCAXvxfpJkUhjkSIOAt8nToiD9dHFQ'
    b'ijPZHNc2Tghwubkr1+ocq9umgrJfdVftcioCuBpGWzeB6ggSYDXIaaxmvAC0HEtZSRrMLAEa'
    b'jBxSTucE7iHAhWSOCaIwtjnJGi6ugWQiUAGmRrXrF4vVMJtN0PBkrRr17qhZIA5j/0qrdjY8'
    b'2US0HdqUvoklGmKfATKl1DoBcEsKQ2K2NEWedXnup2mZHHpzLOUVFX4GwABiiIDA65Fm9VRE'
    b'NR81FXg+crmdT/sHpfpHzPiadRnM6Tsspz2Q+yQ97JBpK00IIhAQJQ+jOLU1XkogUtCfQJKB'
    b'dJIAA+TVSmvcHiG39oeg0kbOKR05OC3DeCz5QGivDEW5TyR5YMY90noak5EmwiRQErwRAEU6'
    b'AQX4OLW1xHoiK0jfABeyhD2BEbOFQUBK5CQqnl+VNuEUa9BS8wwdv9y+qJabRNAuXms3iXSH'
    b'uG5cjeW4CW1IitWIIUl4FQg6iFZUEmPblOxqFMDYarsjKXbRApaqBAZ2uQQPw/NoZKFZEbWx'
    b'Y1UfSWLa56pgCY31vgsbW3HXuBoKRnPLyYZktkqJ1UepyxupK/LdJtvaGLSORCEZUhBWwQA5'
    b'IVEB3A9pbTadTYiWi0hKMZDnJPtGWpkGKNxsi4DsqGC+Y8Qrsc92u63cUyr6TfxNHwtJra5k'
    b'5tmlVc6XWnE+XGPqE6fnyz9dan0Yf+vlGBMfa8bF0Nix/vCVw70pgaMe+Akke45dfr/6TKS0'
    b'H96otlo4owvRmtQfxWU71KQfEQBpUWg27Zhe39aFlASkIOKC0WishGVOuqMszYOiZZHMJKAH'
    b'OXGzHsY+kX1jGXUhrl7bZ0cBgYsqTFZJG4zI3BxPCkZYl1SBEt+BatYJMgd/jWtrpxpje9IY'
    b'4/0HPvWK8HPf+PNVDc1NRRG0hrroFr216fiky7eqi4wCCSH6p8Ake/sfT+xmD7NVFGNHkU8W'
    b'2IAmArfN2dI2IEtykJ4M1R2DQ6NAUJsKwu6O04uVTfp8YcYVpETjCchTuZZOnZjpOOxgd/uO'
    b'n8gpvUzjGcAkYE6wB3CV4JwMiYjcDzX4mbjmGcE+fteoSDVqagYelaHsj6vhdu7yS6nLn2Xi'
    b'qwFWV8PwGgUzgPhMaaQsb4qmsqmaY+358V4axmIAuurKFmEgLg2SUxIDvMfZtLvl0PX3PfdE'
    b'/cGfeDF95MPPDwD0Pd/zsbuNcbZdxHDj6oGBfEJmzyLZ+5Xtq2X2AskJoAIyAxGIGhHi4U1x'
    b'MCa+4jvZBDOKDiHAbAtNFme0SdGtCA7GfojC2y0siCXILtLDJtJlV6GZOdGB7EnmZnRRYIp9'
    b'xfG0vS6gEhxhNhLoDOjib1pIkTCi2hY/wWqkQAr4DpdmAVKLMDPcB/Ag+sk3v+/ZAUD52Qau'
    b'FvejL3++AtZZEwDZcpzXHQ6nMW50WQ5ljOjbWwCuC0gh8kSkLhIBa2ysYrOURqtC0yI/LCYS'
    b'AcWgAVvPEZo4QwCwUDolGhk8DnKP6rwEWJKhjGPNAPYEmYReUjgYcNh4ZT7dRO46gOUHXrh9'
    b'8vEX71ykZPyV3/zBm8ePTudNae4k6YdJ7kO44tCcQgdiDqGb7U27xcUyTab9rdXZ4hvKMD5h'
    b'OZ/YLJ2QfBea6OImr20S3WLbqn9tHbkxbEqpOc7okbqEGQFTZCFviv94CqqZMAUxJTP9yCde'
    b'FojRjPWlV++Ozzx3G2fJHG/TCpDSycVTyvYMzPZFS9iOIaaQkKxlM4Gg2BoBUiICRSGAkFFI'
    b'IdcgEnlzM4DYSG3xzc3aJYlmBIRmnFgw/kcIlRS4ZjJ2BDoFCHKLdY3iudmTEN9BMIA4nhEy'
    b'ATCAs0nOyBR8G18IFLkfK1k28DlEcn48v9OGJPL1yMY4XgPX6mcBtFrW4AMA+01dBsTrh9/x'
    b'K796/Hki9R1eegk/GQfO30b9U3RrAiS1GosjIsMl3AR0LSRxb7S92sQFK/ouc1AkNkFoXjdA'
    b'GS3OaUrgcxsZL8SgUrgLS3W5gCFoMkYCRYCvhtHDRuECiqRVTPyXAqg+G/vHjb3mFMAFSeWc'
    b'9r7tmz4waYL02vJoR41adyXArC+r8d3L8+U31VKupZxOgrvpUe67O+t+TuOUZIBgE9UeEk1z'
    b'HaUtQR/nq4l4X7Xl2R6enM8Aviuitw+HsYzj43qAOgKRINwlcc+Mn75Yjr/n5HzxR77pw8+f'
    b'A7gErNqAn+86RQQcVvLbl8avs/Xvk/F9IK9SmIM6gBiSH4wQG7UKhEiXiQQlUkJrGIgXFEk3'
    b'k4wUCZHtCe1rmRQeBzJBoiBuRcntcAtNLcaiMQs0BAgREe71NvpQayKRUKP+XIfIedxN7Wqc'
    b'QtVdHhSFF17ri+jyZwQt9lKKwqTbsJk7ESN1L8Dr0VdY0ooS/ZgA+GCM54cBmt8UoPWDDS/+'
    b'62vgcvzsNwPQxZx6NjcJwnsIySq2tpPUXBqVsBtr7YOxUACbgMgdVZCtHN8OXAhxjF+U+1ZA'
    b'WC4ZjPqBVQ4ggqNd6z6CHC4naFCOFEVQ7P58ugKwEHBhwBnIBY3nDx6dRXQ6VgE6FcBFAAiD'
    b'RrawGZRtukBDqjeL9+dlGG+vLlZfXddSlaSe5LlXn5K0MpS5XIcpp1dIM5Dvi1CEVtplm3Ta'
    b'RCCfNyCl+LsFwIPWNU5y6lIU/JD25hNLZrfclTc8eD6trtkwlNPVML4k1xLAsAYrtRWRm8UL'
    b'ZbHicn82n5f6fghfPXH/6up6ClI4XdCxKQPXUBcgpG5FooK1KxbAdtLb9kXgDSQQakxMbAeN'
    b'x6AgoBb2LL4xqGjIIDVUHM8W3xrftUsI1gJV0E/CRRik5FJqNVJid+zSAyQ9yn2tEM9zL6V4'
    b'lmjjoaYBDGfN/ftKNEbK17dH4Vs0GsW9iLSv4Xj5zQDmwd2OP/PdH7P3P3PLf7ZsaLffdUsA'
    b'2JRve/pSwvpM4wlsyc1yMzlPY6KMzUQpQ6keJa8ilQDdzmoTwnbsbJrHVqEmoF2R4g9WCB7y'
    b'UwwWuQTPyRDemC4GmksbkHVpcGm87CFyjSBWEJYIoybJAcSS4CmNy2T2MCd7y4wnJE/NeJrM'
    b'jj/36t2HANQUNlh8x7d91SxUyOtNPccnGm77PS/16mqx+si4Gp5R9Xks/hZDm1E92mgsk/n0'
    b'7rq/RWNH8lojJe03OYy+w86gZgsEeDXHxovlUAlMQd6SFKqrapWSquCSC3qruu7K/RMn58s/'
    b'+t5nb/1+AI/WgPUEgI8EgF4E+8YdSdcgvVvS0wCedOAZCE+KUX7NNYcQIRjhVW7gINqOE0Wg'
    b'0NpLgSbLXZuuRsmLMg+AGOqq5JRqAtkTG4IbAGGoq6GakSFNhXTeLjxsLPltE2IxbC6cYVQ3'
    b'V2vJaFVJIjTYAcRQx/I4mFSu+3K/J+n44GB+1hjbL0KauheLzv0IGD1dS1cV72x7LiiM/hwA'
    b'XwPAQrL7cQDvA0BEzcsms+M/i2h7AtAasPQOA5PaCIQWHwK0CKCPeffeS8B6sMMYeBIXijjR'
    b'm9Vgt1Kyr8ZSBEwgXBe04RsXUgRp7vJst2Q8JjQkydEYD71RLV1RiSWlJEhUxBy1pGsKw4y7'
    b'RgEF0AAwrhujGS8EjAQWKaXPmfGUZE7G85zS/ZTs7rq/CuDcyBOuO4AliQXJRcQITa/fOLoB'
    b'4Fr0INrDvle/OixWX1eG8bla6jWvvh/ShdFsBJTkyjRWCRbBpaWf9ndz3z1ab+/HnegbEMzR'
    b'384ZwkbKs5bauboTgAU9SorgWpTqdvm8huqSdAbhHqDPDkP5wadvXfljcNx59Y173wjiWwTc'
    b'gHAN0v0oAbaxcQjXBdzU5voshBrXZr/xapJCSOBCCtXQWskbAtu4NJHbo4p9GSHQCUFGCnQZ'
    b'CxwDqQJw4WNhIg964ySTZoBCLRShhiU4WmRcNJ5r7NQvqIq4RAZHGImwz4lhVmB8BjWoW2F0'
    b'kiON5yQfwuz+cHpxIeAxnbOEhaQHLl1MunRse7MLAGcCjgU8ErTZjw5g+OSnX3N8+Y3RnwmQ'
    b'+nUAfmWA41mMs4vo7wpigPtxb/6bsOveXYPU6TsMUm0jYqwHoC4Qvz2usQZoTQDcvgSs15sA'
    b'tvNGPMSuW31XgopjqVTvAjMOSaYIlbIgOSzSVsIyYGtDcoSWF0J+SCFwaCstKN6vEjznVAEF'
    b'c4Amrg0gNJAnl2qEzFeSlUSlcWVmI6J6bt93o5EdiROS981418gzgAsS5yTfAnA3Vrw3ow8k'
    b'J5bs1tWrB7cjsPKW3G+Mq/ED42p4vo71ilxTr3VPUqZZiUmMaIzYLUmimRVBzH13une099Lj'
    b'84FZ9EljjO/aVagFriZurkS/5L+/ENADug6hB2mAOFbnUKpKlUtaVveLBLx4MJu86u4vSXpz'
    b'rR5/u4SvA7EPgZBK0IugWbRa77AAIOw5GZKFqWALUNqlmyZcQA21LcfiRRC7TQgpSyRFuogL'
    b'AibjCYSXUT0j2RWM5Wpn7DowmVQZjpoASqFR/dpK4wi1LULnW+oeI5lIdpISjTnG6NZ2KsUH'
    b'JL/s2kjvI8lzmr1K8sLHEqqgioQDyA8kXKDL3wducmABLRXSjULyEnCqDXgsmoKx3OWqbxav'
    b'OYBHa4ArbYxlUG7/BZHm9UZoS8820tSqkaZ+CsCfAvBjAVZ3AHhIVe+YJBXNQkO5Gdf2kagc'
    b'/mbYkb87JLvFGrA8VNVfdwlY/3uTdDnfCWfITWfcOESzneDGsto8HIW38BDAgQQjYfFwLXow'
    b't7XRDGGa3LJExhcnq/EdJUjxzUhLyQSyrLcjQAQ9spXqk+o+rxt7Ebqch0mXlJKdA1gKUbSS'
    b'8GRGANaW3o6/tTDjCcFzEksAJRKM30jJzkguzejz6eR94zB+9fJi+e5kduS1HkVxh97Mxsst'
    b'AKHRQ8CYSMm8DGNvKdXp3vRkuj8/A1HDu5qbKPjW/tc19g02nqY7sdgkAGWt4iUST4UDIBnZ'
    b'Q+iHUn0xjqlGDhOkItcqG8eQuFbjWB6ruhK6iM1sJWpBimhzdYCs9d7GVq16tN0XPCZueAtB'
    b'MBZBAWo/TbRO5VADWUQ+EHAfyX4IwBsgznkxZIM+QvKrupT2CSQgjF8B8AE8W2qbxoAuSSXi'
    b'CCu2vFYUgImkTKIHmFKO9ZQBwgIjDlBhtoBCEyExUhxBFK81Qd7DkQUlgIOggi6/yGT3BC6b'
    b'Wp1QgJGAi9iPXELca8wEBFAYmSnx3o8CeOjSKxrrtU+/fOcvBfAXhY36XmhON5qULmtYJBZx'
    b'fBKOp38yxlR9h1RANlvGYv+eKGn3baGpMMDrEwD+UKQyfSpGxPkTz968QvLbLgHrw/EFl9sP'
    b'NT8mN+Rtk133ebOfGtVkWI2lkuwgBT0vppLUSmrVfVmrTlwyl87Ce3MowI0cIF1MJ/3MpTCA'
    b'i6S5y/sup7M1UEjEsyQXkMZQBR0AL4FqNdYjd+9IImdbJbOSkq1ISlJfXR2kYsYI/gTIrZG0'
    b'q+59rcoA3IxjTra63BrtrOvSaSItG5MBM7jPo+5iD1xuMVoiI+2kiyUdDJpHmlGSqXq2lKol'
    b'K12fLySUybqkU87pHGTf3N/aEga2r5v7joYGZ+/R6cU+yRcIzFwQpb3qdbpaFQzDmN1DiQYd'
    b'QDVA4CYyflNCHpFXqgBGthG9b6NFtUcD4BQwpEb5AwuJKpfHuXvt58IW1J4fTJ9YCniTOX0M'
    b'5B8F8ZMi30iPzqNmpv4CM/vzui4/Z8mmII1bUGdIuEqIlC0ywDPAJpTEFH8fwTdvjZTX/NCt'
    b'RC8X3LXB/ogHrHDREEzqwVQiF4NKR9sqRsYz9t1nRC4QjgpBAaaAgBodTYBwz6gL2jjGjECW'
    b'8El3//T62fdnZ4tvHMfyQqn+SlMV++kAhL4Jo1gGUM1Dm/htAP6PADi8Q5IVm/0nQw399VH7'
    b'4b0ByIq/X+KaPhOdEWr04zefvrHyUp+7BKxg48R7QiR7tmGanCN6g8j7jTG6j5uAeO80/sgT'
    b'y6FEeSjApRWJiaRxffxT7noE4FwSQQwA90gcEXiEjSjst28cFZIzRGlzkoeCruaUOpJ71X1f'
    b'4amptU4klMe77nmxGq/X6hMjFQE2bmbVCCfp7koxOEIlYY3Jp1BZzaUcQKZkrACZDNaZeSKV'
    b'jWZGGGAEq6BK0tlEn5uZIOUoZUWkdF5q3ZOrp1FBWJcspVXO6Th16W7O+a1u0r2Wc3pAkjte'
    b'w2mz3zUxNK06f/HmmtwNxLshTMxocN2S1I9D2Su1dvJIg5PacJISVXWaRYhsA3ZDUmrRKzZC'
    b'HI/WSlltZwXkEBQAn8BGnycAUjseu1O5fkq1frfNJh8H+eJo9vrkYnkHq3GJoJU2s78y5fQ3'
    b'rPt6n5lkCuDqQFjDA9lKjdJO0nXsQ4K2+83Pbfza7pFQ75LB5XI3yUEhykC0tw6U0DDTwJHs'
    b'nH13EkHXfZPy4ZENMQqoiHxEhr2yrVouaVWr8/j0ol4shjwM40FK6R6JhxJsuRoE4EbM62Wo'
    b'equmarZCqvnHALz4DhrWGd3i7z8VXse/PFRBAKiNdhM5u9GjivxqsfrJcTX+6I0nr98chvHq'
    b'JWAh2pVQIW5d9gcXy1moXzeNvAHAXTomMQmReLh8j8S+S2+560EYtUlyJulUwMKlBwDGOpTL'
    b'/YcSVsFuMIMgElOSB7eu7IchHXW+N62R5vBeErcJzkDsk3yGZEIwGUiaKnjMA505lDq9WKxm'
    b'1T27ZK1dPnYMEsPlb2gCW0lWGr0JAmTce2Wj8sZIbwRpMXlJFBolyAkUA6uRlYTR0iivM5qt'
    b'XEirUmYga3Cvp2B8KMFNPySzi9yn+5NJ/+psNnm9S2kV8yMLSAjVGOHVFRBSHc7JTVn5V+48'
    b'PJbrGQDvJ5mz8Ya7z7z6TQLT6t6FmbwgUARSEz8Utkm1kSfNu9itdczmSLQWrBRqoNrJqBw7'
    b'DjJAa/s/j5VjpOtE7t/Paf8HYfwRWwwhMQR322oUNm1G8sNm/Gcsp6/JKe3TOCPZayvZtwBK'
    b'hdBLQGicAQZjYA19Nw9W8XGSkFglyQHBPUluUbgWgrZ/AQJ2AVyK+9GlU+R0KnAUVLTDXBKS'
    b'0YRNfF3EjCVJVqrj/GI5OT1fdMNQchj8l2EmOPPqey5NS6mt46wHMDbJ6/9cUHOfR8jCO2Wr'
    b'6sKbfiMkqt8Qhv2rDcnl2OTQzuOaBKCu27Fc53K3MtaJuz/y6iP/j+/+r3eDyQ5J3CD41fHH'
    b'liQ6CYcNxcp5SD43N2qHJMAYaqGgEjaCCLwMgsB4n8AIshBIJB5yY+w+vbU/rwAWewdzBnB+'
    b'FYn3AniK4E0Qe40alKP3sbVwAKSzi+X+eptdyu7KEoAtZYm2yNVsqLjdRvo29jnE82RgZwYj'
    b'U1RHCl0iPO/Nak1iZHy1A4zfadWVHBKApABSj1RuIwFyMONKxEXfda8d7E0/BqJCSF1O58NY'
    b'r1b5JD/OdbQlgD4Y1BOIarSUk7356p0H90v150rxQ0Am6RoB78w+NOnSFYIzuSMmjTV3olHJ'
    b'ZA0GxSZU21Z0aNM+habFHQ61i4C3XPS67FIvYCKggkyN584Enav6j3r1P5n3pi/bavwUgU8Q'
    b'uIfVuDupDCHpk/zVJH997tI3W7JrZjZ5XFys7+apz8mriwRDLYeZUWElk4QwvgtkCMVBxOVK'
    b'AexwF8yIzXeR7o4WYWop616FNuwhkA0SXK7Abxe5ZJcfKNmZAAgh8YeGQzDUfZTovSSV6t0a'
    b'qLrlauRyOeRYOYPQEQt3Lx75nGY8k8BxLB0iXCG2vyVKdL3+DhrWGYvplZi/vwbA39IEfo4h'
    b'OQHAUZOKZnLdoDG5+6lcp5I6uY7c/YzE5b5Kqce7gDUB8EwYw35F6L0dgNrQ5FoTQ8J4PW8Y'
    b'Rj0AhI0NRg2w9HGRbLiufzeB7wPxylOH++XgaA/xt78h1NQnmijw/UZlbW1rCSEGni9X10vx'
    b'LChJSJLQNkl0wYDNtkopiNxMcUoyFjMKoBmQjIxQDJGBb6E1oFEb3IESSzedqA3zU2bwWnl4'
    b'oSJhFjGJh0heG0iem3HhUuTxcgGyQDKSIwgZOQZFDnOy43VfTrruzTv3j98E8G65pu4+CPBs'
    b'dnPWpW/MtKtG9HI5CcmVEa3x3kVBg5a7KtgxEFcIcgtxaCFKrdpYQboBI6CNQwNI0oarPCIq'
    b'DdIAcgQ5E2Gofter/xlJP9h1+QTAcAG8Ecbgu4h7GNvuaDUWRN0+EM+Q/CuN/CCNbmbn3Wzy'
    b'rE36r5nM+qteKiynZOtG0mopnvqOlrZc3ZB7w3vLtpAqWyyWR10/F9RIkF4rympULRUAVEqR'
    b'itOrU5vohxoquUAMyumYXX6kqC1g4BGB/XitdmwD4HI12MPj8+QSVqvRklEgUat72NMowVPi'
    b'SYTiXJjZ/TWwHQFYBG33/xhqYP0ZqICMbWvPjio/+OsA/GUNMJ1HrNkcEWjd2Mjr5VYuaGPP'
    b'zpL2IIySR0YNUUtZlbEMGW0LlsrmS7vYTprgrQxgiD42OW/7zY31eN+jqzkegZHBGx0cTzEO'
    b'rr92fHbywaO988aV+wBAit417tjc2HdyqEidyydj8SQpudTF6gh3zzumYqlRcmLExapk40ZV'
    b'g1HoiC24MWQ1sp3nIbiNElzIinOFLSAwgEdSSIZNC89TdqEK6AL8d+1AHvxKeryVLFZkJrMT'
    b'I5ddHq+txnItCircNeA8GWcmXSljdSSNicwEEAAaUERHQJEYblvIXIo4dIXmtOmEWoXRA6oA'
    b'0gkw3vN40yR0AnpBiUCS4nygiCySKoFjr34PwisAzlOXv+oCW1LDR1FXchkqxaM4fvN40pVZ'
    b'qW8iUsUMnAN6FdLd5HprGMbv6JM9gdGmZpYgGaQEiDCrpRSxkCCMQAaYmnxFbcFpJ/5VLVMS'
    b'IZIKBM80Y54k1LFQDi/+uBEubcvXQo6+u8+c7gEwA5/gxvE0CFthgMRWhQtGJXVG2jgW63JS'
    b'rQ73ilJ9O/eScUHaaU52v1Y/lDSLGKt/Kjxvy5+hrYrNvjUkCn9uxHu9N56PAzhuMjLi2HaM'
    b'T+W6ELQK4sgrkoYoO7axEpMTuVdLKWMsaVfC6gF8W1jwvyXQ8qABM49eAq3RHGObjtG8N0bX'
    b'jtfgNIDnlfAEvBVxIK994Nlb90hmAM+HaHkYIuUTcU0hZTUxS/H97ppeLIdri2G85u5TCWlr'
    b'SAdCkgLZhtkDbTS1G1mMVG/sw3uEOJs7DPOBcmKNVTMEfkqBMhJbboHPE/UtcAvsNQ47iZFk'
    b'IVBIPg5+TWZnAVyIzACPb6nrcAZQiKRVvUXhk4m80Sd7X2d8Lmo4yMgJgN4sYt+iO8JdH/Ya'
    b'b2zNgfBsADQaQxKIm9TENzWhChKU4lMrAURUZI5433MId7AJmBxSMoxA63326AcNvTQaCueU'
    b'XTX20XXpzMhzI8ex1IO+S9O+79633s5Tzl0yAwBa4kTaLni2BfAIuCfpIDYgRzZlFoGopRxx'
    b'9UB4Pr2WotVixZSzea2+bpXYqJAAWMrIWrXwbBcQjOCUQFKTFsdYgCMMp7IJablYrtL5xaob'
    b'S700sPtYne6eNkBl5xuJiq/X6ldILvo+/+Tp2eK/CKqgk3fA+8emG4Anwpj+VQC+OexUA4CH'
    b'jXBiTQyh3L0n0EkYFN51BEhJqkaagCSJtdQVAWpTJu5iF7CuRE28v7pRv3IAyxIhObVpOtGJ'
    b'QMxG7RMAa0CL0dV0a3jifzRq9b8SBsATM3oM0MO4ntsBYkdxLGiBA7QCuav7/nIoV1bDeFSq'
    b'TyXAJbbFKwBsU8lCkeE2OMhoiegI0qUWaNiamBk7khhWoa3UJolVoEtQnC60wKXWwuqI3abv'
    b'WrXdjIXh6o+YLQV41WR2uhzG85ALOgALQp82Uh359DTZezLtCtiI8KSRhFrCTgDRpdgVERgm'
    b'aoenOkCzNiBCASZsA4RrfFcXv93ipp0TGJoxNU4m3QKgEM2lKQF5xLMNpfatrSQ+W3dj1UiU'
    b'rssXtVTNZ5OTcTV2s2l/fW/WT1JKzMl6kh3N+rgWb4AWAipJpxFhlw0tkgz1j20UIQB5dSkA'
    b'J8J0ktxRq0PVVd0LzYah1vt1rCk8t1BTgYZA3wDVOZvyejG2p6XUbrEa1j6xYX6xWB2WUhGL'
    b'7IrEw+rCJUpHWMC/1QCV3uEwhRthNvrGMKi/d5vIHdfappN59Y7kFUmnIUBMIXVxz1LcfZLo'
    b'IZhLYy11KXeut4t1v+PVF9yVsCJT+z8NYKgNP5M1eYVjA0ZDAz6pAa2WTK4Fq9IY4RdxM+9G'
    b'IYVgSogcNuDBB9/1RBtZPW/SYm5Gf7aRwro2ElXSnoCZu3pBXXWfIhhNxlL3NmojEiiHCDM6'
    b'pEO4DgR4BBRS298nqsEZkkgBAATkQvHHW+XisiLfGrKbGv2xAVwoAlYN6LtiFYrvHBr6XCK2'
    b'Li3jeD/tOxk5kBjOl8OCgra88MR9k06MdtEbv2aS7EkDJyD6lFNxqYdrGlBcQIwOUPGnqtSF'
    b'RNoOVEX3pjdJ8KoAexgvSK7GUhLAqYAUUlZqUr9yS38cC1RQIwcAhqoVzosVgMVQKoysLhVE'
    b'tHYixyp1AHwNUoVEgZBXq5FwP5pO+2dms8l8bz5JOSV2OdnW07C1VclrqReWkoEIuxg7uF/e'
    b'L4s8UGgrTTaGeal49YHG+DqwjmUh4pGE06gBed2r7637yt1rdR9r8fuQgv0DFwIeETgEtvfp'
    b'ekOXfV/SNQHm7mel+GptfD8MJ9MA6KLL+bPr1/925Acev4NAZbE9CHD6ZgB/XmwXTST+PD5z'
    b'EdvOayXAjKgBIWkXAI1klgSvdSVhpHE+LIYHtZQ3fd3kGgFYABba1kc4/78QgV5sSn5bbNsg'
    b'xuO2skfLZNDGC0UvAVSncf6jQOUXAXwqgOthE1e0WAPW7s3O2++LILp4qM8FcN0Mx8HtZnXy'
    b'8Dh7dc8COglTyWcuJEgmyFB1y933QpzIITEhLDZ0IFUPdQ8iEUhGtmV8KEDFBYfon5d7IuyA'
    b'AVJs1OudsvS1kbScQAmVM+omxj2NmogU1CSyrwB9lsIjI703qk/pN077/G6XOgmJQGXiqdHO'
    b'FBKQS7Ox1KmAXoR2U2yaEvkrADmRRrIms4cwXiAkPwEzQBN3zFfjOHWFsXWD8xZxWK1FjGqD'
    b'AIJFRo3RSMBO7Fg4Z4GWgbWAGAkuAZwY6Wac912+MZ/1e33X2WzakYK7VFIyARhAxjPZSqB7'
    b'm8DgSDcKsJLH1cS4irzNpaQFjbZu9wBcqmWvCboEu2+Wa+7VVyArpDIM44MyVq+lziWdC3iV'
    b'G/tcFyri9SYNahnhDTe1mTv3JE3kmh2fLfIwjm90Of+h/fn0//v0y2++EnMH73A8VQfgMADq'
    b'7w+TUYSYICNANfok8msrAEPUSYhQnrEFwCb9yb36uSQOy+FeLeXY3YukCm0ZeU/fDrAQF/aX'
    b'hrT1DICrDVBMW6kp+vnuyhtA9lZD26ImofGt2L4SE+04eoAhEED1RRsA24IRsb8MtH86gPfd'
    b'Lr1H0nMu1BDHQ3tTj7G8z4Xk8jmErsbAjZLk2YVSN5JTJmCSwJ0MbQGoQuyBoYpiV58UcBH3'
    b'rMZ1liZp2IngvApVvHFwBP6hFHfFyvYw+oBQBxturgLo4xQeqwiJvHtl0r1fwN/T9fkmyZXA'
    b'EoJfgAVsNYwQYDLL2nqAFOYtzQF4l9OjSApHSnaSzFbrnpKRIKYSZi7Nq/v+WHxe3CfVlSWZ'
    b'uzPorBkqXwopJbXqXbzHnVxMBkgtJbjLw/7EUO9l3Gp2WBK4AMFk1qVkqe/ydDbpMZ/2TkMl'
    b'betvkLYR727JJlHDMSQ+tsKx2uCYANolhIeX27BpLUupeVwNXqsvBT2QayFAJu276zBKtA0h'
    b'tbw79odGm1k1NQbvIjSaRuvJEj4L4HsB/H4Sn/iJT79a3kFGBUbPkev3twUtzawZk2oWzXnw'
    b'r12Jxfs0ylp17XON5SfR2BOE17qs1YdxNV4C1RvubnIZAI+x7GHn/q2fD7DQRFZ/MHTVW6GK'
    b'HTWhCldi4p0GyClevxnBaMsmKGzzo4JZMYBsaIHpK5QRnhGqqqQDl65JuOHQ8xLei+rf6O4f'
    b'gOuqS12VKKC4gM1kYNWWJy4obyULSYpsPQ+SisQI0dGOS7yx8TedXBEYGADZOjhIhrSJi9hW'
    b'QKm4CoDa9BTbsxCDosirPknhe2JgfdKNy1t9d1XA1wh4r7BlOH1OwI3GOTBXeIoVgBrbcwD9'
    b'3t70mGajS0eS5i7smbGDdGjGDFx2pQjq7Yp7Pxbvo2yWIeCojSfdMYw16EDG2/KQdBvEqHHb'
    b'w6bHkDiBLegTThApWb/uk67L073ZpMxnPYwMNRYKT60aAtGOQIaLIFrAFFoPklEOOFwX2Ejw'
    b'K0hTd6ekYbUc31qthnshIk9JXKmuTxhwEVrA9bivswCntxp+rCebuMdHiMIwzfuvRVzTS0G2'
    b'95PjcnjxHc79+xCAvzZCFWoj+Y8AVg1QXW1iq8zdH8o1hoeSYStMIG3dOkHuxc9qqYsyjute'
    b'7kUe7iR+6xDf9yi4uf4LAD8VgPXTthQ3NMd+Wzn2dKcogjcVpKcBZNaEOZxHH9O0F372Gt97'
    b'62r36bceHrxw/ejQ3V/w6n+eu3+7u25U16GA0aEkaQ+AqgBBEwkpwrbDtsStTS/y8GoEyrqA'
    b'uih1b8vJC5hDjvhwY6BWTLKxAR5H4F8ttaScHEBlrNpVaozzO56ysO9Qj7eKwfyQ0v8F4FU3'
    b'Hvd9p/jM5GgDRtcVdsBYxacBWKPCBS3gGBHKEiKd376yf+jSjbHUF7ANNuYNAFdIZAnmLkSu'
    b'aNLjLehyUxOsyjDmd8mGiCnzkHbYRIwYQWyCgL2rriQp3t7aQ2utzgCeEs9otcMQITObdjlN'
    b'ZpNuMZ32zCnNUrIsqYQNpbj7SLPL4/NaqiWzFLmCCO0TEaE/KhklqQxlWZfDfXPNBG1UPGEF'
    b'yN11uZ1DyIrK6QSSkbfD2TSJfrexAT0ZE3VspPBHTSzkNI4tAsBei9d/EMB3rkFLP8NFnmHD'
    b'/hoAf3HkGd9oimfUFqgaBt4JgPNa6ylcyaUJhJ5EJmk0mwKAu6+81uW4Go/HYVzI1UtaoI3G'
    b'D+EnTFPfE9hC/sCrvx2/hBpdOvTF6ldW6S934WtduuZE58JCwETSHoiTIAWchwQxVdjyA7wy'
    b'wlYlYNUwZS6D3sYITBwoLi0cmwcHwFopYneQbHuQF4YtqG5DASJ2LbZo2DNO4/iioZs5DxD7'
    b'Y0EXcrfvu2Vjj7iCCPY7iEVFIVUhaJAv+wWQIg9sr4l54/WDWQ/AXDiEdFXAdUbeqUuz6upK'
    b'9elQah+VINwlk5rfvUPXHudddkRlZQsyKylOd9fjjqBsATBUqYbIkwiMABVsBmodGiRo5EU2'
    b'e2jGArAzo0uKiQKBdMv5ME26JzSWGYmZjGLOU7lOLFtSNIpJXk2LldNFlxaNHZdELABB1Efg'
    b'kGCK55VJjAFabDzfk0b9O26Sgm8GWK3i2EHLNBu0MP/DGqxW+PKaRe9CPf2LQv17oQHGkOxx'
    b'HZFS08RqHse1jWUs5xEneJ3kNdsAleQ+1uqLMoyX/y3dVeXexXe3FO3nwXL6+0IVVHTwBz71'
    b'f+OXSCOAqy785vIYrPRel+YVkEuoG1Vx6dCquA5qUO2EXcUjcbfbCaBaBbkb4mHVmCSpSvdd'
    b'eMulDsCegC4+lrxRE71WC9XH4qutlWItWfXql1sH4PE+mm2NHtTJWMUAOI3+3eEx+rG+7wJc'
    b'kZqwlXn0aRPvdhh91pz/HABvwkd4ZW/qwa3fQ5qb8YjgEwJmknqX8vlqnNbqKTyAkCJFKhoJ'
    b'RfUHxwasKgG1akm88JDWIvxCTrBETa4xzm97YVOWnYRHCthFpt0NsOAOJ7/RuErT/npUsHZL'
    b'FlWSCLkvCXQairHUHpEt7q6F3EPyQGJkeCgcUWwK3BKUoIcAHkTa4pW4zyWel4U0dRLH3h/3'
    b'/zgkqafjeu/HwvWpCAf6PWuwGvHltxT9OoC/IvpZA0i1qSNwK451CJBqNKjTcRgvSF63ZM9K'
    b'mqpKtdaLMpbFukvVL9sKAONzanjtPxG/5wcDjCui/VIDrD2X/i4Bv2F0PV+kgyo9qtL1mKx3'
    b'Iw7pqqC0OR/u0iLiirpQ6zoRi1jzJ42IrMajc6dZMWxZPcR47EUaQgZhDRgQLWuoMAAaJITx'
    b'XcVSCiPntnmTNlWa1WmI7cMY5B+N1fcTAVjY4RC/2YDTYWOw72NrTdpF8zs2asBa0JpKmADY'
    b'k3BT0HVFmffGxldJeDB3pDC+B7CEY+NtmwhQAWBt27UJektu2NiwHETh9lyOBGoyPjLSuAGI'
    b'2rjrZTnN0rTfW287ppQB0JJlL6WyOrEaO7gYtX9c7sWLrwAlBlgqnhMBI5gFhR1yG1B5ZmSP'
    b'8Ao2Zb/uAfAAg2caG9FZnCsAy+jfHf3ProHqHF9eY5uP2VB/Px2hC18P4Nl4vRd/N4A+VMAA'
    b'nbBbL+M7nqqPC574mZf6TBmLr/tCLrg74zO1Aec3YlH9ztAG3mxjEX+pAZZFcNvfAeB5F/ar'
    b'NHdo4cK14s4iOYDJ5XsOZddG9HapCuir1DF0aAFDpJrksE1R2E6UkeQxJC+SslHF5XEN9Oqp'
    b'UecSiTnAKbhVF6tc55AeatPPY0oWAJ4n3QQBgtHjNRi9NqD1KCbHJ2Ig/NCNST+04SsCrvtm'
    b'tdwXcPVCOsR621Tu2euAmyMwz8C0BDV1Ssbm72uS09ylfWnjqVWwkQY4XCSzYJNAX6rvuzSR'
    b'Ipxkh/yvLTUR95sSUlMgJwcQQjvRt21OJBFqYagxAZqFYDHy2IhzI89aeyvJZJPuME/7WzTr'
    b'mKyHu+jeaTkC1TOMgDGcx3LVWlRVAEnYStmZ4FRQxaYtBJwSqAFc1chu51mtYvG4Hp+5G9d2'
    b'uznvrTCw/wkAP3y5vwarnymF8l48cyJ40yNk4UaA0jfE+0EWGXbs2Ma1P4iI9xuxWD9anC2+'
    b'r4zlx73Wubv/3XLtNc6h/QDoz0WA62+NLJcHP10tRP7uH/9ffyGpdPppUgV2GxN5+zCn/0DA'
    b'ME92DUDv0oFvgGhVXL1LNx2bMuMOpeo6NVIOHQJYSJgIOHaJDhwRWAkYXZq60Ico8CqIxap6'
    b'DC6soueyGi2uNEFb7+s1SdcaVtGKCO8gcRYT+o6Ac5J7ANRN+6ZW4duWsGfjjb0TK/fHQ8T+'
    b'WN93jaF+s4rvkVcdeFrAYzvUSup64GAAukYl7HJOHYQrgoJ/O0rtC3tRnbmTZGZkaHFiOCMS'
    b'+cjIBwDMoWkUM73svYReUEI0AbbDrWUkBpJDnB9SH0TCJXiTICxEa1QJJzDG+QNAT8SpkWdG'
    b'njT3o2Oy6SVYpb67QqCHBIw1YSwECSRD4CThYhQRGENajKR3UlAJsClNP0cAloB7iTyN45Mw'
    b'cLNJV7veSLxvBHi9FHad7wVwJ4DqZ9oSQqoOKeo7Is4qMkewDAmrxjWcxbn7jff6agDVW3H9'
    b'fygqh//YwzsP7kqaA/i7gku+j+9+KTJbfksT2iF8ES3j50djdLWhCNEnzTkevW/Vk3iN2Frc'
    b'zEqAnfHbq3RKIi+qVwYHsaAFwf1EZBJI4PXi/kjiRTZ2gkTxNKoXPBIwNyJJuBt5d5Oo4PkI'
    b'wkMAvqpu23izeIDD+XIlaIRQQVwB+BQjRzKiwwuIgUAG+dh+FAb/8yi+kd39AcBHq/PFarI3'
    b'64CItQrgau5FO3HRqCEnAVZo7V775ArAnMC1CFZcTckE4LzP6UhS3k4c4UgBtBCmIDpok3AO'
    b'KIU6reoyQN4CadkwR+xDcpCMoMwcYNTmnyaAqV2eSFxIOIe0iJSkGwyJNoSqHXJl5gasLECB'
    b'TfhHUeQxNnmKS5CwnA7XvQ9eemAYDRKRrYZbnmG3UnxulLQkMA2ggiBvVFRrStIP2iwiF4nM'
    b'W/65UN8be9qz8d4bIX28FZP7d4TKOIQX8GcCUnXHG/ibQ0I6imu93zCwfBKAxWL6cngOx7jG'
    b'm3GdHw1J/icRAeAP3rx/DIABcv9Dw9Twp2IBvfv5hJCfrxIWAXTND7m2LegYYnOA1WHsD/GZ'
    b'VZx/ENtJA2xdk/sIAN6R1xO51xlvZfJKIq/GqtuRG3I0Aam4TgQkjwh3lx464NV1CWBPuFRc'
    b'uAAwcyhj85lPuaCLWtvcqVMAj4aL5QMIK20BVlex4Rh7jgibExF6fFw3mQgI5BiVah7J9aKk'
    b'EwA/Lul4drgXSd+4Er+za+xQBoAhWT2KQfF6DJKfWquEaibGNQHfLOBJAbcFfIOTX4NN0F8X'
    b'OYBJjfgvwNVU71FTZro9Fm2XS7lCqrt5mbtN28q4kV9HKiKljUDw5KPDLkkz6cEsChJOEmDE'
    b'WMVpJBzgKm3UwTeMXADI8XfO0qy/mXJ+ju5zagPEAVIObfPdtkVZWb0XMEBwQS6gMFzyAu4T'
    b'23CTOyFh3o4xy1B/WtX6AMCiqRozCWnqv4pzRwD4GYJVbqTwecy3PyeM6GOTB2gArjUlvyZx'
    b'zjJA7fmGSeW7APwIgBcD6M7XYLU7DG7GZz4R37fTfkEAVsRxxY+JWI+vDRHTmtykSSM5oSnv'
    b'jYa4r2vO266wsY9M7k+TPTE1u5mImYAiQRa2Iw8WVAB0YQAgh+4LeEhhUqS3qsSJWRaw79Kf'
    b'EfBjLvHOcphfn3TX76/GHsD+6myxnOzPxuXJxVkQUs4kfFUYMI9IGMALABbAE4yZyCALySVC'
    b'KgIhuRZyfVLS96n6R/euH54gKKobVscPx8CzGGQW9oE3w7P0J9dgdQfYtifCNvE16nIv4Aiu'
    b'96vWqZvNgl98BuGmuLFnaZd9M0qYBWi0x3aKURCRODnIfSHAW69UlIsPqYyM4JEaEkuF0SAG'
    b'BkCIYNTdUr2CBG4PCEaYmQMQYiEgsSQ4GnGxsWPxIsZItpw8T7qbZnaV7hmKzINat+ZzBJKi'
    b'OgNZHcIoaNXQLpWGY/9+FAEJbyWssRUdx2IyjWc4xuevxgLzv4cBugZIvdPazLsDsG41GSdf'
    b'G8fPAaiZa0/Fdb3VqLEXIU29Ej0+80W0X8CANYsb9pHmZs121LxF40KtjYi9AtDaLw4Qg6+b'
    b'9sSWCgUwoZ8QNyfATQbwhWQlAW5SJ8ClLUf3kkQ1cBil7yc30bv9tB99f/6v5vsnH//02YXO'
    b'7x5z7+ZRQgDnyZ2HHcl9EkcgbwH41khxmmEDACcCVkREBJMVAGIwnJG8ALECuO44NeNVAY8g'
    b'fa9cPyT5x/ZvXHnQAPZ+2B7eHzaCO01E9AnCSBur38UatOKn4+ui/xoE0CvZkeJaJDwh6FCN'
    b'1CQXJQWxH1p6ESnAKmw5bdM289s1wr02oRsGCZF5zHgWItpKSvE32ICFYJJM0FYJhLZR8AGS'
    b'UeIw+PxBwoyjma1IDkYMifaIxBIxDtOkZ550tyjtsdYALDEi4dlIWowI0kGuRRS3qDvVui8I'
    b'Kgz9D+OYopc4d4z9yDbALDxl/0vYgVZ4e+8o3gEA60L1tC3fXCx8cfzVIExUQ+d0GAD7ZgAZ'
    b'YnvW/L6veMv4uWtC2BJif9LcmD0EsDQeqxyva1s2P453TZ5jvnygliwjAgE7ss/GuQNGwAy0'
    b'CpUtmV3k3zpVAXoy9iIfwex4YnySrqM66X7P2He/B8T9evNo/103rxS88FQwFaCa2UrveXr0'
    b'6jg/Of/6e2/c/4fKUJ5093nK9iDl/Po4jE8Pi9WBEGSE0p6E5WYl1tm2ErCACMR8MwbuG0ET'
    b'8mIMdLQSZxjW78dge6Fhj30FoWa03sSQvr5aOZ1Cuk3hKoUjL3WP5AxENsACiGrw9FtDdsXY'
    b'd221wp3if80Eo+QgLMIAxghSRUx+E8GWc79hbCBaSmbGBqBCnPMq96gKiC3xG03JQJIe3yUS'
    b'7LsVDAQ5gggpF06z0bp0QNIQ3MkQCHfA3SAwfrkhDP2QCGiGiE8iaIKGyDvcCwfL/cZOdhFb'
    b'i20AARbhIft/AfzhWGR8F6gCpIR3ppUYA2zI9XKMrx9pEpr3m1zfT8a1DQBap4Kjab/YJayr'
    b'AL4qvBPfFKrhfhNV7Q04YafMlZrjis7cZYvH0AFIBkxnxqMEdAaSBOOP51BP5EIF4U0lhgFE'
    b'FIdAWZl9sph9QsBgZgtL9pDEKKEE/9Iqd915LfXaw7sPv3UNWF81rIabZrZa93N3n6z7LOa5'
    b'q/ocALn5rnPw8feuFqcXKwBXBNyH9GYYMTOAPxBu7NXRk9e1+wwb0C6IY035JsSq6GsJiwDS'
    b'lVtXr4yr4S8dh/ptq2H4VgrXNtcHI5kEVEgtwLV1KEiia296AAsZINfmCKJleHWv7hophf6m'
    b'Gob4BO4kx4IUweArNwgUNp1h/EaV5JW1OstQVb0KiAuZdAkkVF3BXSNLNkxm/XHK+cSMZ2G8'
    b'N+vyzTybzJjMosT9DNUNpbKpk1hD7itwuUvHBs4RHPQEIyYJHmB1LujYyLNmrCqeiYWh/Y0A'
    b'qz8a9p1gMdkBqq9YC5NLS4Me9uDofZP2tYp97QLVLxXAyiEN/GWhOl2LftB4vEpLWdNSM7fe'
    b'obZGYupyMBhqTqCbkNNM5hzeKEMs2gq0itkV/0NxFwQkI5elltNSz0GuLKelmY0kQaObsSL4'
    b'3Gim1WI1H5bD9OL04sjdLaU0ClItNUfV6cv9iZkNACoTL8xsQbKQHIOmpK4/X2NV+34A3xvb'
    b'0zVQDZ83QTV++3uffSIqwjSBrEHCGH1P0vvG6r9mHMqHyji+X2O5JaGXe0g28dmod9dEOdW3'
    b'YZTVznWofV/tAwpPhiDBJTCAKYSfkEzUcMWHkgcxpCwRBgVdSXXV6tVr5bgqHMcRkmApicnE'
    b'nLjuCPGPcRFD7vJb/aR7Pff51SA/dOvy9TTpnmOyuZlNSe7DPaHUhLruLoNUBT6U9CalacOR'
    b'PAnAmsbYTIKWRrbsvJPYX8bi8Sgk4h8NyeXNkIb1jql9X34AaQegb649ts3j/Dls+ec4oHOI'
    b'VeWZ6HGDsGiAaWzQP3q0nSTgbtJZRB5OAaYcrKEI2pdMmCBE0Ri4C5LM5ag15lEkqS2G0ZfV'
    b'AXJiRmOpsQoBJEVjNbNSq+dxGPs1WM3KWPqU00Cjr/cnlqymlGoQ64PGy05LVmIS9hAm1Wsm'
    b'eNHP+o/fOLzx0r3X7r0I4PeEl+88pCoi2vveddtiJYzyX7oRsTDXQLAJe+gj2/6Gu941lHqz'
    b'uN+UNPVas6pXkA5oYPAGe3W2NMfBBdze6xS3tDWwt9u3d/1Vr4oGAPCQ1YCW64oBVxUBWogP'
    b'BOYhCka417qCwyFVGlZmJnclPe5lirF2NHSgJSYzxH0Za91PyT5ryX5k3e+ZWQJ0RdUPIGVl'
    b'PMsufzWqnsIazOEymFUQD1H9LPB0aWRMboigxZglAMTr0thpz5t4o1fCVvVaSFdvtup6ANXP'
    b'dtMOpfnQHNfni4P8pSVhhTgaE+rvBfCbQhqoO4ieG3A7a+Jq1NzgmicdYyWdYENmf5jBSQfk'
    b'6o6eNANY3XHZsxnCyq7qDgdkJJZj8fPV6PHHRaMTrIgirABkZhWA11rzsBrnl4BlZpWJVS4y'
    b'AG0zeeKbCKWURhproyL4JcgB0Pxg/iP9tP/YlRtX/u9P/sgnPxm/Vd/0K74Kn3r5TQfANVAd'
    b'hJPiuXCRv0vAPoFewFHER80aTvAoIII8lnpjqPVQruSS+WrMKjVDSg3VTajYSnIRpAdwEKRB'
    b'or7QgFegT1NpR74BqraQKAkqKF3kcDNaKJXmkkuoRiZ3L4hwiKj5VyW45LUWH2qty1rqqgxl'
    b'VauPknIbItJIjCtANU96khy6af9D0/3p/2I5vWkpndN4heQByPnsxtGvh/RrtBy+CUPZl+SQ'
    b'SkhF9wAklDpvDO059i92qqKfR78XYPbxsBG9Huy6wcYQIPXzKDj75xqcfr5Huk8bFsNviwlm'
    b'TTpCBZAbQ19UiAIQICZhkDss576WOq+13vBSr5o0n+Zsky4xm8EhMGZfcZdLCBOHLreLYcTo'
    b'choRc85BejsnIbgk1rHkS65wEE4zB+EhdblvpK6OIB5LWTkVS1YAhNcKtZaa172bzqf3JvPJ'
    b'y2ug+t2T2eQPpJw+8+Pf9eOV5PQbv/lDYUfAQVCOvC/Aah/haYxOBDA1cVJTKKilCbp7P5Q6'
    b'K9Wz3LNXN4xlIgmNehN7wQvlzohtCsgV1A5otTYuRawTGVn46y4E6JNACoUwbsNO1VAG/WDI'
    b'YGY09+1zJgKp5KpevY7jWGupqmNNpVS1tk5LZgKz3GmJBeDCzB5YtjfM7H6edB/tZ5M/lvr8'
    b'KZJLkH2Y9FOadL8xTfu/kNKzKpUcy9NwGYAiaQjVL4TMqPEXcW2xfwHgYUjHD8IO+emQqD66'
    b'E7Tp+OX2Cw6wCOBG5Pv9zSE5zCKSucb73aarc1dXxzKtpU6inLxqcZOcZiaQlZGc2SWbzvou'
    b'JxJm2NZqHkuFhYQFQkN1X9V41czJpvyXSJokeXXUsaRthV/QBSmlVECoDKWrpSZ3t9zn0vXd'
    b'SFIxceXFbRzHbn18Nd+f3zm6cfTja+nqByzZ9xB8mdBeHuuVCPF4AsBhk/jaRU8BWsGs0JRl'
    b'C86iRq0GgvZ4NZabq7EeInh2fBgmcHHHKBxNlMtDeROJHLAdYCW2pThqrYPXGmXoJXcVRF0Z'
    b'MyaAMUFlO5AltvawMHEHC4Q2e0yQMA5lMayG8zLWMq7GJMkaG54henxDSOm4C0lhM3oYNsGf'
    b'iP7yC7/6a0prw4kF4QaEDOmOVsNfo+Xwl6DUWdzXhMgZjHsPAH1IxHdiG+Ek+LPhjXt9x94n'
    b'/HL7hQlYLU97U9brfV79WyTdcvf9WvxKrfWojPXA3TtIFpVCRKNDcCYDojhqUMdOjEzTPiGn'
    b'xGwmB0AjStAwgOBQNypi2Gu0tcsEQBGBYS64u7nL5IK8CoKCZldenbVWW/dkZko51XX3nHMF'
    b'gTpWjsOYLVnNfV4eXT96fd2/P3f5lWS8RyFResGq3wJQGsLEvSbGR42qZ9E9JtCkYVmYNKRv'
    b'lJCHUg7HUg+q1MlhkFNjyage3wEBUoCEyz1CRnbQm8HeEMwqIU3FOQFg0SS1JeGBtimOqLVl'
    b'SYi3GPazOKV69dVlmafFxfK+Vx+iwFaJZ1XJUNGCr5/ETK4OxmMaTyC8aclesZQubVev0+hy'
    b'LV/4lg/uAegQrAiroaQtYeHD0wNtvKd/O6RnJZxu7rGmzQLiEB6G6vfxkKb+TADkxVfS/vPL'
    b'gPVz30zSXK731lq/vYz1m8tYXnDXFUjz7YMnBShWNjqJaillYdNIzgDsmVlOiezMkM3MJayq'
    b'wwhWScXVpPlTCKCKQ5QrTBhAlHai3LeVRkn5BrsqvbqRlCVzkrC0AS1JLGMxuZi7PO4f7T86'
    b'vH746mQ6ufRU/QSFNyB1Jr0A6ba59htXc+xv1eOLph7jJHpb99EbFTqKs+pxYdlSa18lhgpo'
    b'cIeX2qPWDO04M6QUajchlLA3JYVKtgEFIcCdASpt6BTlcknaymPxZjw/I8FYEBj2rtYQ5o1N'
    b'rSqYQF1YurSs1ROkHEGkZLKcctokFru7BBEYvPoq9XmZcmLqkpF2QuMxgDdoHEDqxvO3cwB9'
    b'DdDK4+nFEOpucLrjQ5JuA5jD9fhvgDwgsZLrvqQfhvAxSD8u6Wx2+9oSwGzx5gOs96MKFEps'
    b'f7n9DBr/nx/+n/Bz3IhoZ289nAN4/8HV/W/y4u+utV6C1a0m74wiM6RMIrjD4SAs9TkBjBgh'
    b'HmCTM5gJRAFZgARqFbw6xCZtJKKi5YFECmGDFAS6O+UOuRBz0Jms1g0QSZLR6AySupxzMTMf'
    b'hqH34qmf9qvZ/mxxeO1wMZlN3li//gEAn4xg0LcoPW2ub7fq3xjBn0dvE0913KzaJwAU73WN'
    b'ZJVaxgYJY/G6V6rP3NVXVyeEXch1gVJ71ToHgJj8EbUd5bgI9+qDXOdyLwB7QJ0UMTpSBeLb'
    b'ALXilbtGQTDSLFkfQAcac6jXtSGSEdCGOIRiKI4yrEgbLKeaJl2GWYkS8ea11oiEpyUzCCOA'
    b'3t2ZkvXjalwEDXVBFKIFeQrglMYh6nH54RNX+ybOz+pilVqnT/RrEK5unBtagXxI4hGA8zSb'
    b'jPGMHpaL5QXBPs0nJQDwU0FGdy+Sh+PZ/XL7chp/38f+ty8XYNSyLLTb06H4F0kR04IVIz3g'
    b'V4D4doDPErgSEoWF8Td3fZ4AYCBNiUTXzrpuH0RHsgcxBbgHMjH+YltLLj7PNrRRAnbq4UGS'
    b'VCpdDjjoiohqQZY4ApS7ExJdrpzzmHIqdayPQx3W4LSYzCfLtVR1sgass37SL2n8TNBq/GgM'
    b'4r1uKL8KwF8f+X2RlrQN+RgbuhqiiY1qJtnYrOAjmjCA8VKVrn5Y3fc37wVTpSS5ZmU1vAtC'
    b'D4gUpgASoApgEX9hVsdyps1nKMGbrIRMwsLWVYPXCyQZ0eAVhEMhkwoimZunH3ZKiSBDqrNQ'
    b'zUcmo+WcrE9IXWdMNoBcRXiIAZh49TEWDY8SW6GByuVKtdZRriWk8Njh4rJbTh1JrsEq7IKw'
    b'nWTkWlejBW/ZHoRrknoQZxDukJRNOsZzmDbSre1UmzkLw/v3hV3r5ej1KwtcvwxYbLcNpW6r'
    b'z48tIVkDXIyOaEK0BrA2+YDkN3BToPF9IG5vvpdCVDreABEOaOxJQ4QbWDfpQaNF1PQkIo9T'
    b'e80xQVqPn8C3QWJBcHcpAh7lJhe9OuQOkhWGStjWYZlyGuTiZfDodG96bmZ1bVB/Y+9w7zR1'
    b'qaSULgDcCxvH7w3AUjeU9wD4xyO371pDl1uaFBs16TUe+6c7qR41nkENUHpR0oOx+nPV/UmX'
    b'OgiDAIfkq2HsWf0pSEd0XWt4tM62lMvuuU2BGpZDSFKwJhLaFROVwBRk5iZ27QCbNoQYZRLG'
    b'UPtHSfJST2r1s3BmnLv7EkBuKKlXllPp92a3umk/S31ONCONNXUpm9mcZj0IxiOl5TSFNAKA'
    b'FAawUpeAFgBXIDxip5S6dHWyP8sA1CwEQ0tJ42NJ0PY+V+vzspG84jgyGhrkJjRnFYb+V4LL'
    b'6jvjNZvP/bKa+BUIHGVDmXsl9qNYKbp4rUg1OAsx+M3mwewGoeltM8iJwqj4DOIcYAYxJbkX'
    b'Ol2icUKyAzDSGPW0UGop3k364EmCi3KAiWyNwRKJpv5yi6BSAJUU70pu7iIkBFkbASquhQCY'
    b'cxYIlKHOLFnZO9pbrPvp/uH+cTfpxpRT2sm/mgQwWTeUGqren22i/dEWUY39izaivaE3Pm0C'
    b'FFdN5ZRPADgHeMuMS4mPwin3gMDp2XLIMMsCSakq4RxAdB1yNV5pPI9CLEz9tMf56UXYteQE'
    b'oygqcsivUXZLK0nnkpa1+Ou11M+5++XrEUIviaH2EoBFh6TgmYcDuAfycWkumN20Lr87mz0b'
    b'jAo9jZB0Gk+Nwcfeyf2MZAfSCFWQNfc5uOSVyP+fva/qcp1nslZJspN0J53DzC8zMzPj9fde'
    b'fTQMt3M5/AdmroaZmZmZ4SE43AxhtKWasr3XWbXyMFPUay9JbsWJbXmrVCpV2Ua91bBqtXVV'
    b'kQ+hfMdMwiaeFwLbRlU3C6Rl1KBNKGd4xschQW/i/wZl+9TMG5YS1uK0rY7O9XK4mbiCeo7O'
    b'ZTEvDxhJ7i4fBKYeImVheRl4eDJ0ImEZIlqR2prkHyBrPynldxPZOtlSWqqsw6UMfW2ASxKq'
    b'OqhhnyZW8pRMWddivpao9FSQGcelqCPrGA6RSt0qoxVLgpdL+Z65kdxEpjwEZ8nKMT9baTW6'
    b'YqLQTdO0L2Q1xD0aqtD+A+UB9B4hrIjjDXhO+BYY0nrBLto7pZ+aqF38rNx6XIcrkmv4HAGW'
    b'meshcjG4xN54WrSd6cg6tjj2qAnbNTBVVeUG6tqkIChpb47vOIDEnW3e2F58/u4RtvcQWaq7'
    b'xB+We31apKDXrR5ufbh9+shRrCLDH1iZknyesbQLC3tNc5SnAKGOpKQclBdUHG5h8JihHhYJ'
    b'TAvnanAJwECwg/dhANxGfoDP38TvXaYnQFjar1RAp7gseBekqTb+l0MSSCHeDpQV8DrIqoMX'
    b'dQ9Tw8X5OqFDeCW5HZl0BqfJ2QtE9G5D9E4qRiXENJN6xAidEJminkBf4shaT7bMSb8EytE3'
    b'GRwhdDBmHIFbFHAXxzwU52bE+YyETm6tjTZxGRbZjfN+ls3n1jo38Yk/kKnfv9ZXa/cIWV0j'
    b'S3tE1FfTg4nSLY0EfSGrmVEJ9/dV8GsV8GK2lV7E4b72cY57gX3c/7g/GPOjOEoMOEcUksqf'
    b'ycUTnQ69/CybJ5dS9L93ABcxWB5TJElaQkPuFiKQZ4DD/yMwVVKRJs0EZXUchIXPoZwogvLq'
    b'3RgoYuwLJspGaw/lm4L/wP+naM+Pj7SWhIWbbVrKevqKikl2UQU0OKKME0vJQUVy3sWD0CHn'
    b'+/hcH6TlBVYR1TFIbhfno8kpENARQ/RaQ6VVd9PEypsjVnQsVToqR2Q9dFbWKJcnhsu2avsO'
    b'AD06Vv4SyFa5QZDTkp/Q2a2zQ+fcIIbgpVz5Afe2I+WDkEcqpqveu00ndefdtk+STZ/6LS/l'
    b'ypMDoXOrhN8gRMWPQ6JJQVgXBFaNxh1lwjAXn9nxhdTXdOVxkFsTDub+N6TOKLDK22yqpLoc'
    b'mOOYlvC0F4KgF4iUt9pESbFeIar2WoLKlHSZq3YaidoPO8ezuwrp6m5Iw1ZJgzMVX3KZHoWw'
    b'TkOaauEFeTk6yCvUNGBNjRQzQYSYu65epC2QlhUw2o0FB1IZDue5VSPdUZzzlOAM6qcy8RVF'
    b'RGvoSOcBzxUJWbLkDZGDxGVJAHOEaFhQJanFeSkdEeL9UUV01YpSnEnZusQxVhmDtXYqbSbe'
    b'uf2kntwgolzaNjLZ/OqcGwoZbRLRCB4N5lIeJLXk6o27btxkZiFo7hs22drJ0uYme/mFk9nT'
    b'+ZJzq8HmpZea2I70CcHXo59Epf+J6GO53iS/QFpe9Tm9fccqI1yrpFA9RdQzAZ0scq1kj4o0'
    b'9ZQyKvcsQyy4XIev9i381ikG9gHKyo3LMi0mj+neZUg7x4tc6ShOCmZ6ZFf1PeU9cYLPRIZR'
    b'IVaHSIrnLWGqKEltyD2kJLYVtZLnKvsqHpjKL1VqnUvJWkwFiQwSdE9RaaRg+UyJtVW7gqBi'
    b'HkdE5GH2UK7MhCz0JO82DzX7ztlamvhdJtqOzPMYYtsQZbV67V7r7Ki8zmrvTyb1rvNOpCm/'
    b'86q3vrLvEleQ2VAAkpKWtcQ85bTcwjGB5P6f0M811Ko0/KFBUgc5KUIZKVMPBIlVSnJA3WML'
    b'JABpKELSzyVTklFUix465PoM9VwN9iMVB7EvYLXQMEW+TI9CWB/DVHAFN+5lmI7kIJmZenA9'
    b'wZZSZo65Wh56VaxCQKWSr+GBHQgmjujPRlmYoLPVVFDGUwW0x1CQSaz0VrZF1jbJ0ipcxAiY'
    b'We/7gB6qspW2ksFOCLqtAoh7XtrhcOQuM1cjG5kJEflW4tckP+HkN23tdn2W590YOa/iAkq7'
    b'an0wGC5Ja8BVYIhhjHHUOnZop7gnL3vdpadZ97BMKpr1P0L6+A44eVwVBKXG0MSuTQ1yIBI8'
    b'o6LeVUSTP6xuFUlJaKzOudimpiS/uVa4o57hOvYxAxkrIhsDeqM/A8v0CIR1CdLORRAVQ4I6'
    b'UFtEdnHDnSBVbVps+JBUbJmz8SRticw1Kc9nIb5Snb+hVhrX1Qoko+M5U8bhp1OS18nSCclr'
    b'WNIDL3IEIZEkZ62xasiLsHR3cK6UMfOcI/cE8n08kvoefKevceTd82eOXJJ2l4hMg4hWTx1r'
    b'F8R9363N/e0qSk7sIigmsXy+0W4ORp3+IMa4J2TVf1ZE92WaCvbg53wN/utbanUaW19MtGz0'
    b'dE6qxukQ0iisgjCcIocpwIrwWNtjKWmuQKKkrIlaWJkp6a+H3z1Fu22oUQ7wP6MMgwOQPbaE'
    b'tdRh/Yvkh7V7DPWwToYsvxlDvM2RD9KVmlFKxim8AKywtGfY1wQ2Ta525FMe2agHmanVlLoa'
    b'pRzqRaKYh2Nk7aly+iYkx1y5kyEyDtFPCKbMkfFwCccwS5yZWEpC+4KJHJgwx4Pi90v94FUX'
    b'T92W3OWRPyF50fkbkZmg0Le2CmJ68+bm/v15HjaqmIDC5GeO5YPhZBsdrwPS7Yh09cx3sGVK'
    b'oLr4GhaDZpa5FokaltUKIaR1JlMzrAin8lQaWUqIvzhRpjjarxovkoaaemYApm1KgkIZ58yB'
    b'dcENRV7YZYCpJL77iU//l4Q1VFFYGlBys2GecORRjLwfQ1iPIW6V7mGJUqJKJ1BbbeST0XQ1'
    b'adRWI7ONXEwrOWdjWnlk7V0gQ55oWxs1ejVAmnWO0aHzGVi1F1KWg+eXzDBXrj1Yzln9lga2'
    b'jYwR0IHw/KeldBXi/b6e7hXXePFYe5KHeJyI3kpkLkv71TvexRGTBTsLp0Tmn8jQvYm366l3'
    b'65vDCUl9WnirJEu7e/v9MrjDkrCe1VSrhXiUyx0QdCGSeTNX+tfVAlJuYwGnaQwNQSIjZdJh'
    b'BRRJxa6EqYMK02WUxHQTUtKmNv5VkY+NWt2rvIlCVYIcUz+cc5mecqLf/I8fOyBLdY48Uy97'
    b'zpV3gjzmoRNDvEVU1M2QY7xt4Fhf0rgkNSoDjUYx8FubCyEoZbpWhBrUc6CulpERBBTTQ+YG'
    b'diZbZp6UEpPhkXVuoGIZFr9tV/KIvYPOyh8kuKgi2G4INk+2V2sh8kmSKUXi7JqztklUbhD2'
    b'pcOFyNES5SRJ8lBMaxPnbqbe/aF39u6N4XjLEE0rMqOZGmXjo+23PH6szWaZnm7TiKQMrU/m'
    b'fWzo/VyEiSNzgZX0jue+rQKT5lytOrKg6LNdDJJNpVA/wOfXsZVmqqShmjKWDXr7DT43Qj5W'
    b'08RlepoT/ca//+jtcu8VURMuCqqVEpgTcOV6diI4kNp/S74uzaYxxB3BXoxx0zj3KrZ0yq7U'
    b'y1VGdIRUExXyGo5lKKcLVtSkIooMUZ+o1Uij7GtG0GWcR0eyyhhwpIwsXbtRc0RUc5aOFUSV'
    b'OJtYopzLtkxFGeek1LtaPfFdLqM28+ZKmvyed/YXSMjvwe4wU9KhLXDt7htFOVx+zUWjyOsx'
    b'HfYviewJJVL5iuA0jGy/yMZ8lql4/lSuyrFAGdnejb6zATKJVEpBvDW3Nij3xpn2ZQ6iYxXr'
    b'MoUedkU58GP03T4wV5ur82dmirdMHs7XahzZO2frfMd+hCNJASbfQUCCZgzRxhCGkLwK47cu'
    b'ER0y1q3xdO6MvaNOipR4q4zwDPKGIEFO2rBOidm5WnlxAlLKyhMQ+1+GTjTGZ6Ogr7xwtgWT'
    b'Zj3dd9bOnKVDkbmNvYUs5QSeRNkSjVPvqvhVznZq3o29s11j6DqRuWWJDh7oDGZ6+4gQlYFL'
    b'5yJdu3rX9fWicOW1l+IjbUHS+e5ej9QxBpZE9vBbxCyea1PwTvj//zg2XI+Jy7iOpQ8q5Wrn'
    b'jGBVWZ7/G/raf4HA5ur8D7c6Z9HfHM7bVW2nqm1Eea4kr2V6hpInojVBo5yBwRWIJMtFXnmT'
    b'zGOIs5DlOzHGPSkPOcQDZh7jQe1Jm6vE9hULNi+B89yS96ykKC8gJSHxggO6HTVlbGAki0rH'
    b'9QqQ1QDn2FT/b5adFB1qpZZsOaIpsznKhhPvXEJEMUb2BSknriAxGyzRqJH6ubN0mgzJFNAO'
    b'LdEErpavYwvF/OWHWySkxUaSkNUVyT4Nt853wYLZP8aqodbbWa1TUZ/JhchYS2pPU3glAhG+'
    b'UAhK36c2yOozgs9jwFoDQfTwmVX0jzr6gV7hy6CiuAzScYJJPcTHIpYIZCCojlmm5zx562wp'
    b'6WClzzFXMUmIKCfm0Xw6P8hlpZBjXDdsNpl5CmJwgg2UX2UiB+N4BidqiSEySuQeonPVVUfM'
    b'lR3KRG91wGeMMrc4KuigbVSug5uok9pkalLv2pYoNNJkaC01K52Ycc5SnjprvbMxca4rdZbq'
    b'XNqflfKt8poRmQfn+nPB5h/97X+vJam/Yp09LPWPpvX0tRz5Qoyla5TzUi5eqh8W7AqZZTI9'
    b'jI9GMMr2zAIGOQGLRo7aRXKmph2PbysMJLoXgARHgENfOQs//5/GdR/Cc95HuzXlLnqCcqq2'
    b'ko1UOPU3wsL85F/9+b9dhQPBItET9LdOz23oq6XSPYNzlYgtLkGqY5KHHvPQjTFMHdkuM/vh'
    b'cGLQYTqCAPH6aulBwblLxrtXkbNnsL8vCnJKE6cs51voeDeUPoJVYNQm2pxCeaoCUXrACXJt'
    b'P6Mc3HWONht7RBSdpRVp8SqW87XqaTHly2zl0C9aS9PUub60iZboOBFt4dw9NTJ/r+Df/uCv'
    b'/zN33p0TfDytJV9N0uQNUq4bSULkWQwxSN6JkX895OF3BPcW/7r06gtOK4mV7i5DXf9fS12k'
    b'4PVGcRU+aoA8W9Sb6fQC0Z8t3oMU+qkvYPp3WVBXW8MMpCyjNuvX8zzk3rvKjTTsm5B3lerg'
    b'jwV/I/inv/yzf801qeuBYnF7zeJU8eEIS7fXx59eclsmX3mR5GBiITnxjCObmOd5KSlFTiLH'
    b'mhwRwqGs0aiZyWR2HFOgv8NI1yjbMm+WMbMqj59eck+1pKWCIqxhmfgBSGV1tZl1BSTlQBoj'
    b'RWBerchMlR3LRBHWQNA7ubbay2NsM/NKPUnO1RPvE2e7ks+IKBRtbeX9IaPKXU0b08pcbTy9'
    b'KPh/f/g3/3UXV5LZy4p6YQrBbC5m87xwvStFM3fOyhd4l+ehGfL8ixxjcc5bhgwrRe1Jte3J'
    b'gexJ2e4MlD4l1/ZA+oXWRrsoD5XdT6aW4i0QccyoPWs5pK3nC3mRgsUU+/PYy/paQV3pKJu4'
    b'l1oycvhcP8vyifcuVw4k835vmK61m6/uHPSvF+0Gg/GRfZlyT8bTOvrYImEmBklbtqs2usyq'
    b'rIxNVRvdFvlT93+1VLpPYoyGQ8ylzGXOPEX02siR61EIhwwXksbuWnt1o98bbard6KFycBc3'
    b'JL9abTO2UIgbAiGsq31U71AriUeUOD9GpzyuggJMVMeaIh+q9h288HWRrJLt/mh64Wj7Zj31'
    b'n1hNE1NM80pzeDItuHuZKx1HqoJZToBLgv8tZFVMGc5K+f2Cjzjv3lJfqZ2VPHGSyJIn+U4i'
    b'YklBUk3qWYx80ZD5REHemze2stMXT63gWluCNq75ogoLNVD70MLCCD8H9O7/VeAY8lwwAzK0'
    b'v62kMEwdUQfB6YAVQl4lmQlxxeeIqJq47+/F1O/tuJY+rquFAS1D3eNejmbT+bxWT7vDwXjC'
    b'xgTpl5n0TyMzgVaz2agxm72d7YN/nE7m14nID/qjf5AB92oIsThXQxFJCiRAxHf6AmqwORBc'
    b'QT4o2gCpMiIlXM/9eMYR29ky9Q7Ml8T15BL98l//QBf6ph5HDvDDPTWVUstLZhPvNrx3t4r2'
    b'vd4owDXGbeVSZr/sYERsLJ11h1ofkfoxNV27jG05KyCpMR7cISxRr6lQSyP1AtsFS+Q+vnOI'
    b'89YE47OHW3vrncHelROHR6m3n0+dOy5K98QS9ZhNIDITkKBTK5SDBQO/7T/9+7u+M89Dcc2v'
    b'xypUkZ9srNaPpPW05b0r/py11sPv+Fxy4ir8V+k9VKq9GOJ/seFO+8haQYhevQA15H5BUhwr'
    b'40YG4sNMlWqK+NaQW0FQU6Btdd9TnHuOY+uCHXXdHRWvbyKYPwsSFxVQEugnBP8L9xq/W0dU'
    b'Bkk91FHgWIhqitiujL2jniXNZvPb2L6Vdg4GfzMcjrcn41k3hLATI5/DvXud8gpxGedkENK/'
    b'YQvQJtqewO+9G/11hM9tCVaVa+udooxjXcwWbuOZEDw1/CJmKDM9RXy8BLaUsCrvBN4i2GeM'
    b'sc5sWs7ZTpL4HhGMROUBWaL+4cOtaZr64fZ25wYewK5g4o62M5BBQH5KuVNOlPK9hc7QEHil'
    b'LJ+o0Y4FNaAjuKlGr74ivM6VE4f2ru50t19/7vjAW/t572zbEjWYuRONcVKeo9M59du07Uwm'
    b'23B2Hry5/f1ZHpoY5d+JUfII3CHX4ChQIMlZiPe2JhkxWSZb1E2bmYtGL2MuO2qirquuVlFT'
    b'JR1BclJTkCrpbSJWvcQO98Wrc+dq68dE+1jC8Rw4oqZZAXlf7XvbFomrqAchruwZ1FUl0FH9'
    b'P8F53JeeutaRTN+yNPEnRII6pWzxxqPhZBgrMxtIipKIEufdIWvtYZnuDZnNmU5nsClt29PJ'
    b'7EII8WSMMcX92FNOI1npP2sGCTo0ElxU0+8VEJtF+7qqM9BUz6MuOIpB5SR8e41wnn8V/JHS'
    b'BZvHTVZLHZbJy+gpHI9YS2ytzQuyknzKhg8RlQ9y4p3dS1K/5VwpbT348lecffCB+9dvClEF'
    b'tY/LgRwuKJ/dEZ3hKI4dw4PbUvqnpgpn1cZLtI38JlYjD+HhXldK510hq85bL50KzOaTROYd'
    b'RHS6mAoaKqkmxx60niKGDaUfyq7e2rn/xsbeDwpZnQZZfUy5u8mLcgwxsdYy9GCR2XgpC4xj'
    b'QzNLhBHyjknInesdDyf5SrMRlbJ9RUkJVt0fwvd5IAWC+t8c5VQ5n9NeNk8qNyYNlAf43g6O'
    b'B0V06ygHtL2F+9MT4toT0po+TSSlQ5Z9HET1SuWRdaDcszjB4Ua9dtQ6uyotEkNlP9gZ9Mdz'
    b'KXtGfEVrbeqcbTprGzLVu5XnYbS706HRaGKyMjp4ZEkefSnR5KJWp60yXSDt8G/h3vfV57vo'
    b'l4fVgGCVq6UmjrPCP+P+7uNzl3FOeohjwWV6xORDjIeJDFvnhtYSk6FgiBwzN6yzbIlY8ttp'
    b'6u+r19O/TtPkX62l/v07vZmQlVHJAl3BPUrXUpKR0svchfZt4Dg60xAv0AZE8XvwgP8D5zyJ'
    b'dmOgK8jeceWMt0TvsWQ+TUTN4jOq45xWehBWZJXt7Pfz7mD0z7e3Dn4oD3EVTuLepezECBui'
    b'o7W2byqdVU2QqtW7TOq2HO2lgCjAA6LiOziZjqepkFW24KZ4Bde8pn6XnvYu+l3K1dRITxOj'
    b'VhKr+qq6T6x0LBN1LoM8VRJGkTs8MxZEIa2Dp0haBKxCof4uGH1ewIAzVZJkHfelVZR94moY'
    b'aAaCA5nadZ2jFkeTee/a8v9DMXImeqye6Kbu6fWGfzccTO4KMb4jhngEktshQQSskuqDDvSB'
    b'PFHEH1D36v7oafwxRWg1JR1jQIIdYZWcem6HQFJ/KthSzywsyerxJe+qsOqmiq3Hjshklmjo'
    b'nLU+cbettV3v3X8LWf2N5P8lx7vqpdIp4sbv4WE8iId9XvkEOopO6fFAeyCnKR76VYxc/yD4'
    b'd+W1tIZpo1d+jKbvfcW5BiSir4H8Burhv1zQUaPiNZxnvHPQpxNH1/7gP++/9TOhCl76nXh5'
    b'D9D2BL6nT1R0QLZsOClyhNCbStnd8VgiCeGjhmQpSnk1rdVaUo9qukdKh1ZDPtP3T0tdako3'
    b'Ra6nerQwSETkRuvB1DlHqq1R0nBb6fPgHhvbS6AzFNJ6UkanehUPZPXtWB2eY+DIlVHnaUUQ'
    b'KQajeYF+fzSKIdaJzEnn3dlaLW2L9DSdz7JJrzvsdruD6Wg4beV5+EKM8ZQ6Z6rUCCMVHs0B'
    b'miS0rVsEeS7ug81U3SmJDZ8tU6okNlZ1o/SYX8T1n4aubKR+j136w3r05L13NsSYEVFIvAvW'
    b'2Ym1dizHk0Y97Tvvbjpn5865FSLzbrxA/VeePFSKt/dtd8daEkB+n2AHBHUMq20rSs8yVKL1'
    b'bZDJvUpX1dP2L+rFJiEpRrkGsvrfELEHqiMdERygnkHqamJ0q504svZTYgz6OzHGInjppwRG'
    b'WdbnahtQi+EuniN7GNfOpWZNpfMjS5RWJEUTKs5B1HTO6qkAAaw7v44zCPBC3WhXPkrXFJSE'
    b'R2o6mSiSYAXSkW+UXm1LLV4McM1dYIr6BGT1pFcA1QLBX2EaeKGoA7ywBauv6lMhpBERrdQb'
    b'tZOWKJ3NsoEc2xJl+64Q1YHU/XyeTTny/7B3DjDWtMsWTm9hrE/6bduWosvoOryxdW0eR0fR'
    b'sRD9toXP9pgb32xj5lQn602e9MHMHA3+t5KVnr2nt7tXV9VbtaqK/sJxdUQMueMRSgpbtZ++'
    b'WxEJx6TpgoJC5hJ+nxZG3VE6aRDkXDZMQ1E3UJ63DymTf9d58E+G3fj8frjqks3P73951ghq'
    b'Jvxaw8bgsJbFaouGLQ/UadtzdrsSjvgOgsC10rylH3VSru28kVb0S46hFWez4SLdNwTxs1Ed'
    b'XGcMbXlMy008xg1/bPhDw05UymeRV6jqtYbl1U0YOj45dOZL+WL5rJH0g0r87jCUhCSueF3y'
    b'mPqMrDM2uTlrCeBsGCLafU7ktMv+Grfvp2TblK0kxjhFGJ5UgFXKGAkmqolEUOoEE1ja1BDn'
    b'43W7LVRRX1aCt1pDMr8O762I1yi41VgjrMYKj6kYlWQVyt+OjoUbDD0o7lzEfMC4Tva8PL7N'
    b'lUqtbsQ0ZqHfhNVVfWDJ+ONWqJtqNFqd8t4vMRT1Ot0qjyjp9ysLFfdZUUHfq/v7tU8vyN9Z'
    b'QcQiYtVWCXqUyaQ5Liyaj0K4X8aCxxHDC9p+vLwiYG/BMx9/PQzzWoYRQzsIgi3mZYUeVtFu'
    b'H7OTsRgEsQE1DR+XZO2oW6kDWUUtpgOz012R4C3Ms5gRRLXcK/jNhr9weQpcEXsQUlR1sA2K'
    b'GLvf23viX0rlWtlC379RPVgvdLj1nhVaoh0m9DjT2VTaSGvIyCtckQpfo2jfTSGciRjCyKrt'
    b'QltObEaeKEFdJ91HSzjlVSTcE5EDvoEJL+/rd6iBuDoYLiIkaUHJ4ixem6uEBdQ+VfC/spHW'
    b'sht6MSh2u8jjfqyOVgwX6/Un8ZslML5sSI89rttvhfjog0NTVrSbt9AvI3J6wNChfTZjseYm'
    b'kE9e+3Dw7Ga8XlZoGBajwpLwcNv4jlvYn6H7IvZPMBTFimwb3lwaQ2+zytWOaB9vMFoi25Ge'
    b'DfXOLZQ5aSfgcVWAN42gpm07aSfrPK5Q89Hkr4WGMSMt3YZxvLoSwFhmbyO0+3UsrgQ98x+D'
    b'Lk+Bg2hAHtyWd3Yf+4dytbHDYro/1wkRGHLIQ3QJRZFsRn9P22M6LcFbbtvJYuFHyq7wrwRB'
    b'EJJhK9Sct3orlxPaye+WRIWDeQAe1yI8MdYasfE2xtYOrKRdrcfNoeo/hZCzLrSQSG7Ci2lj'
    b'DBv3FzEq9Fza4kJTt3cZ/kzfcWiulKBL3+tx7XtC+9b1vnuFU4bdyu+8+8G7B2ct7Aub7VOo'
    b'h9ql95gFYfca5kTIUyKoikLQbVjJmwcpVbVvCuTTRE3cJMg9LrjfNsMpO9jGsF8n2rG68V3M'
    b'yLPL63f8IBIWeoPRgtcOf3MsLBQNiclI6rtBEJyx+qspI68J86wadl/MhYDIMVC6tseQo6cV'
    b'XdYGOf1WDCfs36ngMABBVPA+CzO5c9kTw1P/Z57VA/Ym7taBUkFFc1YhTAHhScxwVrMQU+Zx'
    b'nuvq6bzQegnP2Q7hCVfv6e/mIM8OKlIgXFjkUjnCihiTv9py3wGFsWY8ERS6UhNMIR/0xLJ4'
    b'rhkk0ekZzDuSQtHoIlDTY4rysFrLyFklDLvkuV7qLm5Y3e137x8eUVN5p5hC9oPy4I+4yUzW'
    b'88c8YAatXF26SBXgYQ9K8qdDIegnKFpOu9ISbeP4HrOR36MOr6jHkEXOi+TMEJ/nRdn9Dvju'
    b'0+64RC3eqPb/uuG9pWuxvAVvHPv2x2FLjdVYHbJw58dGVMNhngZX8gC5nZYhavGhnq72KvSg'
    b'Xa2Vp6uQU6hDq/vc2FS+PDwx+8p8uXaPDtwUlqwDQx+Wq2OYqpIzDAVBkLe9YmFTrU12PmuE'
    b'FTZ2B0ZeceRosoZu/Z2BgmrAAxrbFjxQGWtxuFKkUET3QUX1NPJSbbcCC9G5Nr6LlvOuEBrW'
    b'MHqqABWMAJLCBoWKUrBYIgGfUphWkfebNlxruAaDIqb13VyAFqtOEccJw0mdxCURVdS4SpfG'
    b'd75JhNgpwswi1NwU8XoCbBMoDelw3RPwVls4XhaAmLbs7eTY/ja8tm5DDcdZCqvjbxiOKxwc'
    b'1n7efoUlrL7qr8y7yoW1VUEQ1Ki1HtF3CoyYfsk4plWx65E4DVxuDJIiZ42sXjKyugyFgR0Q'
    b'EOx3YSvc8QkQ0IyFIX2WuvuGrQx+NLC5/zER3CanPiEMoEyD9VIJjpoSUlzFQ7jBC0EjSmwI'
    b'LcpYYS0KdUfQ8B4TILkMB49iJbWEi1EncookyxRWd5capd42vAxi2IUxXWf1ekNCzlATOeXQ'
    b'FF4zolpYhjcX4wUBpBHaqJLqR+RlXYEK/wXUqPW6jgqswl4WKXEoufCPBaeoa2MSvom8YBU1'
    b'f5vd35DA6YOnlzCMLY+svAUfj/04xpwUl1dFUrZdc7bD8Ec6GK/RwdbUwT+t4tTXTRrGLXH/'
    b'garYe/X5urEAUEX40oPp1UcNnzXkTEV0SCtdlxrughZXBxQp4yAKhngczBmVHolHq8EZQqKo'
    b'chGKqnN4v3W8/xmETWmWUXDBA4qZJWEeq4NleFtVbAsraNXR0r2IQd4HEt8BLiotoSm5l+V6'
    b'190IgelppYSsXiMp4tyqEphT6CYoavW6W/dfApnjC/R3H1abu3EhakOiOwdi75eX2K1j5KeG'
    b'p/Q77YNu/PV63h+KrI7oN/C2hCXoQWnbBklpu8ZM039RttDSiTwpja6PjKxm9ZlO4qoYGDrR'
    b'ZNuEsGAFLUD/bXgPMjeThg/RynI/q6V1X7T4M4GTyVkCxFZ3//slMiVMljNcrYF8WljUMMOY'
    b'dpEVq+ix3yJIpOYS0JCsiQvyFlZgyh1GmpzjJClW3BtR2Xb5hhycyFHGeYEGeGGnRFhvQGOt'
    b'hpCww1BH/nFanpkLs2eQO1tASNdtKCEHSE9s0NCnY/Mj7TuBwti3MWx4HJ/H23IJi8S1DqR0'
    b'Z4UJtyAABYmTRla8Wl0l76iJkz6hA60luFWsg4avGiakzR6aI41Jd9VG4+vlOji70fgK70kG'
    b'0qCcDYgnqpcU4KTuQoKYGuJZhIUNtJ4E1M6CTDDrvFqsC3KLFZgywzxNc9nSM/z8fG2+dxly'
    b'VL+OLTKUZr0a+1pB4ifotWJL4+0DjDi0P8NRHhuyny8UpuAfAJPX7W3ZFpydf369Dta8BlXT'
    b'x1xjqZFVnaGjikPvU+6kx8mwuHwEck7fVgHfnJGVO7BoGT1+QK7/Zr3+hYY+kVYM4UNoWSS+'
    b'M5B5Zv5sAXkrzs+r4wRpIJdVQ5tNXWigFqiK12tyyIKrdUK1+zTeUy7Eb1OlgfLDIqhV0Idf'
    b'VYvRa/70ysl4wgog3xEoHKwZWS1EDogbDfdKwfJyaIGXoF6wV8vfR0hUUcPKFCRidFuJbCC0'
    b'QUxtmUVBapfe1y4sycd1fxVhaBnh1RRWIGcNAQd+gMTSKHPIumJQl+TFYzMQVyw60to+0L24'
    b'gafvDMCr9P16nrBWTw/ciIoHHwnmKiU9L0Dl95RO0jHlqUaNqCorvnIa8DetyfeAEyMBr28X'
    b'EtBdOKnalDbWfccw7aUhZAxpEHbFeXfw6GKo2Uq4MJB6W8yVGVm1Nggx0Xbpt79Qt0/IE59Z'
    b'n6ty3oKvPfeZDfvZVILwBLTAJwx5kdZZI6rcKuUNO+GlsQQhwN+okzJD7iXqabIoFQi0Jcm2'
    b'N5AXxRYkqiZcJoLaqt99h6EPva9vGs75vj1PWGvROnSVvUIkNa1czTkk1deEgawSJC56XkY2'
    b'8rz8qHqE0j3qT7xQq3tb0ctIFYaaCOtFrRw2fGjoCWstHuBxFPa177nn2vV50nuSYltOvxY8'
    b'LhFRDRp6MMAkQbkjrMSOGX6gxvEcaty8rRMLDpfWeA7LmycpzaoUSe3UCu0maKZ3GDqhvtFG'
    b'ZX8sUnj7tOFbhlnvZXnC8ubtt5GTyoCkLpIXtc0wiNkAKWrfI+8XleXhYsOiku5fZS5rJYTl'
    b'zROWN09SLJrtVKL8UpV/XAwvKg7VVKq4puEpxYUmCIsFnU2VsnzJcBj7eVsXle7evK0mSek+'
    b'dBA8JNyp22xRaqO/MoXJ4E1UjS/gNZrQYuNotJz+/uXhoDdPWN68kaSg33WNCOpRhHuhVdgT'
    b'iT7MFHo126wvg+5XFeUOCcGFfznDIGRjRITeYJ6wvHmDwsI2FXXepPBvR6QXsmSoYMx/A3mq'
    b'OHJTJRQF15AHS0DYL6u/i7r/ShHVmEocauuDsLzF//Rv/yBp0sFpQ9zwO4nlvXnTPMceEdWN'
    b'CP1uw8pfGh5PG9X7cd1fAbm0oGXGPFUWvZ4xQwUqFOwkqEH3fx2pJXgP62JUS88YabXKGnKp'
    b'+2JbBrpXTGTevIFwelXUea3hOsNl0HjPQpZ5nhX/mLicgxeViXhYbUjLdLkyBihP1BBO1iEr'
    b'LU9ufQ0w9Zruwz/6MjR5Tqt37aT+329oOL3zLcto6/DmDWRwocZ6XaUizy2GTkhaR6S4If+C'
    b'IRxo1m5REx+1WjGML+PU5QCaXgtQqTgugcYPDMcQbnpbB4T1sdzig4bnMLE5g8kebcOIa+z1'
    b'xOVtCa9qi8K9O5TcHtC2juZwThPi4I4MPK8a5HIaIK80PKyWIzMmz/F/RhPzIqr3RVTjWEX0'
    b'tg4sAW3qjFz1Hmh+D+v/eUigtKdy83XbNrf43jZvMNgOeVQpeEAFEBp17RPap4P9k1Ca0HGn'
    b'coboGDOO5NcWiEMXbFJKn/sNE5CFNlsXYaE3rKRU3aRdKHgOYLrHIA6onDvwjLhqkfyWN2+B'
    b'PJmTrr8P02loGeiHJTCUYwGSx51uag2mWNeQu3LWipBWr163qunKbxneNQTcb32SlCesKUNZ'
    b'V8AhreL0YsR7ylDG1S5wbronK28y2oIuclMirC2oRE+irCGLQa+cvB3aAgpC8xA2zMIjm8fj'
    b'40iqjyvFcVihX4kEt75JylsCY72HXOsCpn7sNeQgrl/AFJG2wZu3qMWV+9wl9CJZ3o2LHmcl'
    b'NlEUWnPhmpCFumzNUNaWNV1jmhS923ACOa3axiMpT1g9IqeYkNdVahpi/mXUvOSgg+7NGy0w'
    b'bJGG/l3IkXZgZbCGRHlTqEFNlfMQuyMElhZ6tEj0iYhqxlCCnIxMJRLeNoYhtxB3ZQ2Y0ltA'
    b'GFjVRJqqku3h/d680dLyqG4x3OByVyhdqLPuCYnzBhLnndqnhqR7EuT1ieEjef7TmMiDcA+2'
    b'Ib0rT1jDhjL6riqYMHzMcEoHVgXlDN680WLyei4z7MKFsCXE4V3VtQ2EFPKiRTczERfS3cLH'
    b'+n8NyXlvn9KyhgXkBiY0ifaMyKy0RHLdm7dAhNXtVBe0TaO6vEllBoz1zzs1BQwnfV+Da3fD'
    b'i6p7kvKW0MFUlzc1azght7thRLXSGXXevNZ6j46rSRFMBqFhEzmmRYR7RYV5hxX2FdFW4yzw'
    b'9VLeEoZDIqzdSmTmDO0VelXevC2g9y8wZFES0xQWMHasrGT5nNIOU3rsvM9FeaPREnK/T8q7'
    b'Kq80T+XNGyZOj4ikik5THZ5XTIQ0B3KqalteDil585ZQKJj7DZPq3rx3VVQYmIX4Xh1V5SXU'
    b'VpUjOus/a+8uChiGAQAAjpn8f+dlFmanDM8y3rVJDIQRKh9+fpe2K8g8NLUtzUmdS8PA9Hrj'
    b'bibNUWHFy88qKzqyLaUqKDqvsHYyFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzO7/89'
    b'HsJoAzCDV8U/WxUWMGG7MNzT8AoAfZ902jVyazIAAAAASUVORK5CYII=')


if __name__ == '__main__':
    print(versionInfos)
    app = wx.App()
    frame = Frame(None, size=(600, 300), pos=(400, 400))
    frame.Show()
    app.MainLoop()

I guess now that both the ShapedFrame and apng alpha issues have been figured out now for wxPy4.1 I can go ahead and make some proper samples and classes for my MCOW package or it could be added to lib might be better option at the moment. The win32 stuff should probably be a module import like mswalpha.py or something and licensed MIT/wxPython so others can try and figure out how to port it to work with linux and mac. But as far as I know other widget toolkits dont have alpha issues...

@Metallicow
Copy link
Contributor

Note: self.SetTransparent(alpha=254) breaks the sample.

@kdschlosser
Copy link
Contributor Author

kdschlosser commented Jul 20, 2020

heh the shaped frame thing is pretty cool. I can click right through the center of the box.. pretty spiffy!

I know I did an example up of how to draw that box. I don't remember where it is or how I went about doing it! LOL

@kdschlosser
Copy link
Contributor Author

Also there is an issue with wx.Frame.Move and the shaped frame. If you use that function it does move the frame however it doesn't release where the frame was so mouse click still act as tho there is a frame there. Changing Move to SetPosition fixes the problem.

@Metallicow
Copy link
Contributor

Not sure but I added if self.HasCapture(): self.ReleaseMouse() to the leftdown method. Not doing so tends to cause issues sometimes. Was that the problem with Move?

    def OnLeftDown(self, event):
        if self.HasCapture():
            self.ReleaseMouse()
        self.CaptureMouse()
        x, y = self.ClientToScreen(event.GetPosition())
        originx, originy = self.GetPosition()
        dx = x - originx
        dy = y - originy
        self.delta = (dx, dy)

    def OnLeftUp(self, event):
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMove(self, event):
        if event.Dragging() and event.LeftIsDown():
            x, y = self.ClientToScreen(event.GetPosition())
            fp = (x - self.delta[0], y - self.delta[1])
            # self.Move(fp)
            self.SetPosition(fp)

@kdschlosser
Copy link
Contributor Author

@Metallicow

What was happening is if I would move the window and put the mouse where the window was the mouse buttons do not work for the application that is behind where the window was.

@Metallicow
Copy link
Contributor

@kdschlosser Ah... hmmm. that seems weird. Does it happen with this sample.
MinimalShapedWindowAlpha.zip

I think I finally got it all figured out with the win stuff... If this works for you with no other issues, would you go ahead and document the mswalpha.py in the zip and make a Pull Reqz for it in wx.lib. Then I can close the other alpha issue and continue with my win7 taskbarbutton on wxPy41 and finish off the gradient pen lining.
[TaskbarButton]

... of course thanks to you figuring half of it out and me fiddling around for like 5 years, now I can start to make some really neat dynamic widgets just painting in krita and not having to worry about a single pixel alpha screwing the whole thing up lol.

@kdschlosser
Copy link
Contributor Author

Yeah I will download it and go over the documentation for ya. I have a really good handle on the Windows API stuff so if there is anything you need help with with that sort of thing lemme know.

Did you reproduce the problem with the Move function?? I want to make sure it wasn't my PC and that it is an actual problem.

@Metallicow
Copy link
Contributor

Metallicow commented Jul 24, 2020

No, I've never experienced that type of problem before that I can recall. Not sure what was causing the issue for ya...
Edit: If the sample crashed and/or didn't close cleanly and a process might still be open something like that might occur I would think...

@kdschlosser
Copy link
Contributor Author

no it wasn't that. It happens when I move the window and then go back to where the window was with the mouse and attempt to click on the application behind it. It will not let me click on that application once i get out of the are where the window was it works fine. It is almost like wx thinks the window is still at it's previous location. But the funny thing is that I cannot click and move the window from the old location. It simply does not allow the mouse clicks to make it to the window that is behind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants