9
\$\begingroup\$

I made a python script that takes an image and converts it into an ASCII-art representation and saves it as a .txt file. The logic is somewhat simple. It resizes the image to a manageable size, converts it to gray-scale, takes the value of each pixel and assigns it a character according to it. Then appends the characters to a string and wrap it to the number of pixels of width. Can this be improved in any way? Does it follows conventions and best practices?

import PIL.Image
image=None
def closest(myList, myNumber):
    from bisect import bisect_left
    pos=bisect_left(myList, myNumber)
    if pos==0:
        return myList[0]
    if pos==len(myList):
        return myList[-1]
    before=myList[pos-1]
    after=myList[pos]
    if after-myNumber<myNumber-before:
        return after
    else:
        return before
def resize(image,new_width=300):
    width,height=image.size
    new_height=int((new_width*height/width)/2.5)
    return image.resize((int(round(new_width)), int(round(new_height))))
def to_greyscale(image):
    return image.convert("L")
def pixel_to_ascii(image):
    pixels=image.getdata()
    a=list("@$B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "[::-1]) #remove the slice to get light mode
    pixlist=list(pixels)
    minp=min(pixlist)
    maxp=max(pixlist)
    vals=[]
    lst=list(range(minp,maxp+1))
    def chunks(lst,n):
        import numpy as np
        return np.array_split(lst,n)
    lchunks=list(chunks(lst,len(a)))
    for chunk in lchunks:
        vals.append(chunk[0])
    avals={vals[i]:a[i] for i in range(len(vals))}
    ascii_str="";    
    for pixel in pixels:
        ascii_str+=avals[closest(vals,pixel)];
    return ascii_str
if __name__='main'
    path=#drop your path here
    image=PIL.Image.open(path)
    name=input("enter the image name here: ")
    image=resize(image);
    greyscale_image=to_greyscale(image)
    ascii_str=pixel_to_ascii(greyscale_image)
    img_width=greyscale_image.width
    ascii_str_len=len(ascii_str)
    ascii_img=""
    for i in range(0,ascii_str_len, img_width):
        ascii_img+=ascii_str[i:i+img_width]+"\n"
    with open("{}.txt".format(n    ame), "w") as f:
        f.write(ascii_img);
    print(ascii_img)
\$\endgroup\$
1
  • 5
    \$\begingroup\$ Dumping your code through Black will give instant benefits! (as well as find a few syntax errors like if __name__='main' and format(n ame)) \$\endgroup\$
    – ggorlen
    Aug 4 at 0:25

1 Answer 1

9
\$\begingroup\$

myList should be my_list by PEP8.

Add PEP484 type hints.

from bisect import bisect_left should be moved to the top of the file.

I think it's clearer, rather than image.getdata(), to just use the np.array() call on the image file. That way you can min and max on it without converting it to a list.

I don't see a lot of value in defining a local function chunks that you only call once and has only one line (the import not counting; that should also be moved to the top).

avals should not use a range, and should just zip over the two sub-series.

You should extract from your main code a conversion function that neither prints nor saves to a file.

Suggested

import PIL.Image
from bisect import bisect_left
import numpy as np


CHARSET = (
    "@$B%8&WM#*oahkbdpqwm"
    "ZO0QLCJUYXzcvunxrjft"
    "/\|()1{}[]?-_+~<>i!l"
    "I;:,\"^`'. "
)


def closest(my_list: list[int], my_number: int) -> int:
    pos = bisect_left(my_list, my_number)
    if pos == 0:
        return my_list[0]
    if pos == len(my_list):
        return my_list[-1]
    before = my_list[pos - 1]
    after = my_list[pos]
    if after - my_number < my_number - before:
        return after
    else:
        return before


def resize(image: PIL.Image.Image, new_width: int = 300) -> PIL.Image.Image:
    width, height = image.size
    new_height = int((new_width*height/width)/2.5)
    return image.resize((round(new_width), round(new_height)))


def to_greyscale(image: PIL.Image.Image) -> PIL.Image.Image:
    return image.convert("L")


def pixel_to_ascii(image: PIL.Image.Image, light_mode: bool = True) -> str:
    pixels = np.array(image.getdata())
    direction = 1 if light_mode else -1
    charset = CHARSET[::direction]
    minp = pixels.min()
    maxp = pixels.max()
    lst = np.arange(minp, maxp+1)
    lchunks = np.array_split(lst, len(charset))

    vals = [chunk[0] for chunk in lchunks]
    avals = dict(zip(vals, charset))
    ascii_str = ''.join(
        avals[closest(vals, pixel)]
        for pixel in pixels
    )
    return ascii_str


def convert(path: str) -> str:
    image = PIL.Image.open(path)
    image = resize(image)
    greyscale_image = to_greyscale(image)
    ascii_str = pixel_to_ascii(greyscale_image)
    img_width = greyscale_image.width
    ascii_str_len = len(ascii_str)
    ascii_img = ""
    for i in range(0, ascii_str_len, img_width):
        ascii_img += ascii_str[i:i+img_width] + "\n"
    return ascii_img


def main() -> None:
    ascii_img = convert('archived/image2l.png')
    name = input("enter the image name here: ")
    with open("{}.txt".format(name), "w") as f:
        f.write(ascii_img)
    print(ascii_img)


if __name__ == '__main__':
    main()

Output

                       IxL00QOqW%Q)I...    .                .   .``',~tCd****#MW&%%%%&*d0c/?~<i!I;;:,""^`'``'....'...'''````^`'... ....''    .'`'.                                                                                                                                                          
                       ln0ZOQ0wMBO\!'``                     .      ..`:~(vma**o*#W8%%%BB%MdUr([-+>!I;;I;:,"""^'''```````^^^^"^`'`'''''.''     .'''.                                                                                                                                                         
                       ;xQZOCQm#8Zti'`'                              .'^^;+\Uq*M##*#MW8%BBB8*pUf([_<!I::,,,,,^```^"^^^^`^^^^^``''''''..'`      .....                                                                                                                                                        
                       IrQqwQ0wMBwj>',".                                `"^^:~|cZk##*ooM&8BBBB8#pUt}?+<il;:;I;:"^"","^^"^^^^````'``''. '`        .''.                                                                                                                                                       
                   .   ,\UqpO0m#Bqri."".                              ....'`^``,<(vmoWWM##W8%BB$$8bCr([_<illI;:"^^^^^^","^^^`````````. '''...   .'''.                                                                                                                                                       
                       ")XdkmZq#8dc+'""'.                                     .'^^,<(Xqa####M&8%BB$%MdUj1-<>>!I,^```^^^"^^`''.''.'''.  `^`'.. ....''.                                                                                                                                                       
              ..   .   ^{cpkmmqo#mn<',,.            ..                          .^,,,;~(u0kM&WMMWW8%BB8a0u|}-<!lI;:,,"^`^^````.    .'..`^`'   .''.....'.  ..           .     ...                                                                                                                            
....      ......       ^1cqdOOwkaOx<`,".     .                              ..    .`"I!Il</Qa%8M*o#M&%B%8Ma0v\[+<ilI;:"",,"^""`'..'''..`^. .... .``'.... ....  ..                                                                                                                                           
..                     '_xmpQ0w*&qu+^;:           ..''.                              `,;li]fO&%&8888WW8%%BB%8owv([_~>!;"""``^^^``^^`'`^'.....'. .''..      ....''..`^,;l>+{|frxnnuvr\{_l"""^`.                                                                                                              
11{{1)))1}]?---__-___-_1uZ**pqbW$#mzft\(1}[[[})|/rcCOJj?"`'                             `;-fLaaaWB$$B%%%%8W&%BB*qUx\[++?}1|/tfjxuvvvvxjf\}_<iI:"```,l<-]]-<;^"I+1fvYJLQ0QLQQCUXzzzzUQO0LX\+:`'.       ..                                                                                                    
bbbbdddddpppppppdbkaahhaoo**oaa#&W#abbdddddbkkkbbkk#$#C|;,,'                              I|L*ada#MWB$$$$%8&&W&%B8MkZCOpqwZ0CYczYcxvYUC0wkbmX|{1\xcJOZQLQLJLOmwqqZ0CXuxxf/|)1{}[]][][\vmakOx(1{{]+!:^'''..   .                                                                                              
bbbbdppppdbbbdbkbppqpddpqwmmwZOmqqwwwqqqqwqqwwZOOZmdobL\:^^.                  ..    .     I\O8MbbkbkoM%$$$$B%&W&8$$$&hq0Yurt|))))1{{{11)|xYmbhbwwm0Jznt//|(fzZbkqzf/|){}}}{{[[[[[[???)rUZqq0LCL00Q00CXr(]>:^..''.........                                                                                   
00QLLQ000OOZO0000QQLLLQQQQ00OOOOO00OOOOZZOZmZO00OZwdhbOf!::`.                             !jm&Waaakka#B@@$%88B$$8#bOYnt|(()1{}}}{}}{11{{|uQbkqCnf\||()}[{{1}}(/jf)}{}}[[[[[[]]???]]]][[{1111{{{1)\/jcC0ZZJj}!:,"^```'.....''......                                                                          
mZZOOZZmmmwwwmmwqqqpddbbkkkkhhahkbbkhbddppqwZZZmmwmqbdZfI,:`.                             ijw%Wao#&8&Wokq0COd##qJvj/()}]??]]]]]]]][[]--1fzYcr|1}[][}{{[]]][[][[[]--][[[]]]]][[[]???]]]][}}[]]???--][]?])uCwC/_;^```^`..``^`'''...     ...                                                                   
#######MMMMM####MWWWMM#*********oaao**#W%B8#bmqdpqmmppZt,`"'                              !fm%8M%BWk0XnjfnYwkOzt()))11}[}}}[[[[[[}{}]???]]]??]][]?][[]???][]]]]]]??]]]]]???]]]]??------???????--????]]?-+)zQ0u}i,^^^^.'^"^``..`````''''....   . .       ..                              .                   
pppqwwwwwwwmmmwwwqqwmmZZZZZZOZZmmZZmmmqb*%B8awqpqmZmdp0/,^,`                              !fw$@@8oLuf\\||cmmLx|()1{{}]]]}}[]]][]]]]]]]??????]]]]????[}]]?]]??]]]]]][[]???]]]?-------???????????-?]]]?-]?__?tC0Ct~I:"^`````''..'''''''............................                   .....                   
zzXXzzzXXXzccczXYUJJCCJUUUJJCCLLLLQ0OOZqhW8&hwqqwOQ0mqZjI,:`.                          .  ixp$@&pUx/|){{(tj/(1{11}}}[?][[]]]??]]]]][[11[?????????-??]]]?-__--???]?]]??---?????----?]]]]?????]]]??????---]?~?\YqOc1lllI:"`'...''```````'''````'..'''''''.''''..'''..................... ...              ....
czzzzccczzzzzXXXXXYUJJCCCJJJCCLLQ0000Zwpa&8&apqqZ0LLOmZf:":^.                        .,>}jXZ*BWpXff/)}[[}}[]???-?[]?][[]???][[]]?????]]?---------?]]?-??--_--??]]]??-------??]]]???]]]]]???]]]]?]]]]--]}}}}[[\cwokwOQCXx\}+!I::,,,,,""^^```^^''''''''''''..''''''''''``'''''''.'...........                 
YYXXXzvccXXXzXXXYUUYYUJCCJJJCCLLLQ00OZwdo&%8aqqqmOQQQZZj;,;"'.    ..              `l?jXUUXux0#kLj1))1{}[[]]???--?[}]]]]]]]??]]]?--????????????????--?????--??????]]]]]???---???]]]?--????--?]]]????????][[]]]][)/jxrxzJ0wqOYj1~!lI,"^^"^`^^^````'''`''`^'..'```''````""^`^``''''....''.'''.                 
UYXzzzzczXYYYXXzXUUYYUUUYUCLLLLLLLQ0OZmpa&%8aqqqwOCJLZwx!;I"'.                 ."-fJmLzj|11|cZJr([}{}[]]]]??]???]]]?]]]]]]???]]?--??????????????-------????]]]??-????-----------???----???--?]????]]]]??]]??----?]]]?_-}(fcLZZJn1~l;:::"^""^^""^^^^^^`^^`'```^"^```'`^^^``'''...........''.......     .     
JJYYXXYYUUJJUUYYYUYXYYUUUUJJJCLLLLLQ0Omqa&%8hmmmZ0CJQwpxI,:"'.                `?nLZUr)[{{[]]}1}]][[[]?_-]?--?--]]??????]]?--?]??---????????????-------??---????---???-----------????---?]?????????][[]]]]]]?--?[}]??][[[[[?]|nJOOLct?>!I:;I;:,"^^^^^^^^^`''''`^^'````^""^`'''.....  ....''.........   ......
JJJUYYYUJJUUUUUYYXXYYYUUJJJJJCCLLLCLQ0Zwh&%&hmZmZ0CJLZmj;,;"'.               I|YwLx([}}]][[?][[[[[??][[]]??????[]??]]?????--???-__---??????--------------__----??]]]????????????????--?]]]???]]]?-?]?-___-----?][]???][}[??]??](nCqmX\~!!ll:,::,","^""""^````^"^```''^^^``'''..'..  ....''''''...........   
JJJJUJJJUYXYYYYYXXXYYUYYUJLLLCCLQLCLLQOwa8%WhwwmZOQLQZmj:^,^'.             ^<jqpJx()1{}??[[?][[[[[??][[]--?--??]?]??-_-???---??-__----???-------------??-__-------?????-------????--_--?----???-----__--??-___?]]?----??][}{{}?_+{rJwz]>!lI;:;:,,:,"","^^```^""^^^^``^^''''`''''.....'.''''''''''...........
CCCJJCLCUYYUUUJJJJUYUUYYYJLLCCCLQLCLLQOwh8%WappqZ0QQOwqr;`^'.           ';}nm*Mmx/|()){[]][}[?????-----_-?]?-????]]?-------_----__-------------_______---------___-???-______--???-_________-----???------_____------??-?]\vC0Xx/|/Ya0(+i;:::;:,,""^"""^``'`^^^^^^`'`^^``^^`''``'''''''''``````'''''''''````
LLCCUJJJUJJJCCCCCLCJJCUYUUJJJCLQQLLQ0Omqa88MhpqmZ0QLQZZf,'^.   ..     'l|vzUOqpU\)(1[[[]???]]???????]??---???-_--------_____---------___-------___+__------___+____--??--_-???-?????--__+____--?-___-----------_+-?-------+-/XmpCcnQWm)<lII;;II;:,"^"""""^```""""^``^"""""^`'`""^```````^"""""^^^""^^`^^^^^^
CLLLCCCCCCCCCCCCCJJJCCCJUUCCCLQ00QQQ0Ompo8&obqwmZ0LL0ZOt"'`.         .+fUCx/\/t([}{[]]??????????????????-???????---_-??------------?-_++___--------___------_____----?????]]]??-?????--------_________---------___---?--?[]-+]x0baddbX}+<!IllI;:,""",,,,,,"",,,"^``^,,,,""""","^```^,,"""""""^^^",,,"^"",:::
QQQQLLCCCCLLLLCCCJCLLLLLLLQLCLQ000Q0OZwpo%8*bqwmOQL0wmL('."`.       `ijJvt({[?--?[[?-]]??????-?]]??-?????????????-_-?]?-----?????????-___--------_____-------__---?????????]]]??]?----------______---------________--?--?[[[_+?/Qa&bu1+~_<!I;;;:,,,,,::::::::;;;:,,,,:,,:;I;;;,"""^";II;,"^"""^^":;I;:::::::
QQQQQQLLLLLLLLCJJJCLLCCLQQQLCLQQQ00Zmmqd*B%*kpwmmZLOdpC1 .,'       `-rYUt}{1}[}]??]]??]]]????-??]]?-?????????????----??-------????????]]]]]]????-____---???-------?]???????-??]]]?---???????----___---??????----??-__??-?]]]]]+-|XhhCt+<+ill!I;;;:::::;;;;;:::;;::::::,,:;;;IlI;;:::III;;;;II;;;;III;;;I;;;;
00QQQLLLLCCCLLCJUUJCCCCCQQQLLQ000OOmmwqb#$8adwmZZ0COapX[ ','      :[vOUj)}))}[]]]]]-_-?????]??????????--???--__????-__-----------------????]]]]?????????]???------???--???---??]?-____---???????-__--?]]]]]]?????--??????-???-??_(YpoQ1+<!l!!II;;;;;;;;;;IIII;::,",:::,,:I;:;lIIIl!!III;:;l!!lllII;,,":;:,,,
OO000QLLQLLLLLLLCJJCLLLQQQLQ00OOOOZZmmqkM$&hdpwZZ0CmMpr~.'"'      >jUCj[}11{]?]]]]]?-----??]]?????????---??--_--???-_+_---____-------__-----------__?]?]????--------__-?]]]]?]]??--------????]]]]????]]]]]]?-------------?????]]])rZB8qz\[+~<!lllII;::::;;;;:::::;;;;;:;;;::;Il!!!lI::;;:::IIII:,,,"",,:;;;;
OOZOO0Q00O0QLLLLCCCLLLQQQQQ0OO000OZZmZwkM@%obpwmZOCwMw/i..'.     `{YUu|]{1[?-??][]]??????]]]]]]]]]?---_-????????--__++_------_____----_----??---__+__-_____--???--____-??]]]]]???------??--?????][]]???????--__----???????]]]]?--](UW$8bJt[_~il!llI;;;;;;:,:::;;;;I;::;I;,:IIII;;;::::;;II;II;:,,,,,,,:;IIII
ZO0OOOO00QLLQ00QLQQQLLLLQ000Q00OOOZmmmqbM$%obqwZ0QLqWq\i'       .:/ZJt)}11]----?]?-__-??]??]]??????-----?????????----____-???--____-?-----??????????---------?????-_+_____--------__---??????????]???---????----?????]]]]][[[]]??}\U*&*aqj+~<!llIIIII;;::::I!!llIIIIIIIII:,::;;;;;;;;;IllI;;I;;::::::::;Illl
ZZOOOOOOO0QLQ000QQQ0QQLLQ000000OZZZmwwqd*$%*kqmO0QCwWpf+^        `(0Ln|[[[?-___---__--???---------______-----??????]]]?-----???----------????????]]]----------__________________--___-------?--????-----????-------------??????]?1jCbakobx~<<>i!llIII;;;IIl!!!llIll!!!lll;;Ill!!!llIII;;;;:;;;::::::,,,::;;;
ZZZZO0QOZZOQLQQ0QLQ00O000OOOO0OZmZZmwqpdo%8#kqmZZ0UZMpr+'         -zCYf}}}]-___----????????---------????????----------??------?]]?--_----______---??-_--------??????---_______-------______--?????--------??------------??????]?-?1\fYmdmti><>i!!lll!!llllll;IIIIll!!lllllIIIll!!llI;;;;;;;;;::,,:::,,,,:;;;
ZZZZZO00000QQ000QLQOZZOOOOOO00OZmmmmwqpda88MkwmmmOJmWdr~  .   .   itYLx1)(}]??]][]?---?-??????-----???]]]]??--________-??------?????-????--______---___-----____------___________________---------_____--????---------????????]]]-~_{zbqY)!!!IIIllI;;;;;;;;;;IlI;;;;;,,:;!l;;;;II;;::;;;IIllI;:,:;;;;;;II;;;
ZZZZmZZZOO00000QQQQ0OOOOZZZZZOOZZmmwqqpdo&8WkmmmmOCw&bx_'.`.      I)Ywc))1[????][[[]????----______----?????-----_____--?------------????-----___--?----????---____-____________________--??--____--_-__-----_--------??--??---???-+1vqWO\->iii!lI;::;;;;II;IllII;;;:,"",:;;IIIIlIIII;IIllllIII;::;;;;IIlIIII
OOOOOZmZZ0QQ00000O00OOOOOOOOOOOZZZmwqqpb*8%&hwmmZ0CZMdn?^.'.      "_vwz(1{[?---?][{[]??_______----?????????]]]??-____--___--------___--_++++++++__--------_____________--__-??--?--_____----__++-[?------____-?--__-???-----__?}1)(zhowx_<<i!!l;::;;;;;;;IIIII;;;Il!!lIII::I;:l>ii>>>i!!!lII;IlI;IIIIIII;:::
ZZmmZZZO00QQQLQ0OO0Q0OOOOZZZZZOZmwwqqqqdo8%8aqwwZ0Lmoqu?. ...     ;1Xwc1[]??--???][]?----????????????????]]]??-__+______-----------______+++_---____--_+++++__-______+______--__----___++______+-]?_______----------???-------[\zOb*#Of[<<~illlIIl!ii!lllllIll!lllliiiiii!i>!I!>>i!!l!ii>i!!lIII;:;I;:;IIIII
ZZmmmZOOOOOZO000OZO0OZZOOZZZZZZZZwqppppdaW%B*qwwmOCZopu?. .      `1CQYf{{{[?---???]]?-_----------------___--___++++__---??-______-?---?-___--?????-________--------_______________---__+++__--___---------__---??---???---??---(UphZr{~<~>lI;;;;Il!llIIIl!!i>>>i!!>~+~<>>><~<<>><~<>!iii!ii>>>ii!I;II;Il!!!!
ZZZZOOZZZOOOZZO0OZO0OZZZZmmmmmmmmwqpddddhM%$*qqqmOJOapc]..^'    ^+udC\)111[??--?]]?]]?-??????----------____--_____-?????---______--???--__---__-???-+++_----_______--__-_______+__----_____---____-?-__-]1||1[-?]?-???----????-(JZJtl,!!!!lII;;IIl!!!!!iii!i<<>i!!i<<<<<<<<~+_++~<>ii!!!ii>>>>>>i!!!!!ii!lll
qqwmZZZZZZOZOOOOZZZOZZZZZmZmmmmmwqppppdbaM%B*qwwwZLZapz[..^'    >nL0n[}1}?]]]]?][]?????]]]]]?]????------__--???------?-___--------___---???--___--??__----------__-------______+__??-__-????-------?-_---\CCu/}]]?????----??-?|zb*qJrtt////tft//ttffrncYCLQ0OO0QCCCLQZmqqmZZmmZOO0CJJYzvxf\(()1{}{{{{}[[[]]]
qqwwmmmmZOOOZZZOOOOOOZZmmmmmmmmwwqpqqqpbaM%B*qqqmZ0mkdC(^`"'    -LOXj{[[?-]]?--?]?????]]]]]?????---?-??-_+__----____---__------??--_---]]]?-_+_--])tnx1?-_+_-____++------_-?---???][{1)}]?????????--____+|L0zf1[[?-?]]]?]]?]1j0hohpph*W&&#ahhbdpddbkkbkkkkbbkkkao*#MM*oabpwwwqqqkoaaoM%%&*hddkahaoooooaaaaaa
mmwwwmZZZZZZmmmmZOO0OZmmmmmmmmmmwqpppqpdh#%$#pppwmOmkkZfI"^'    ]O0x|{}[-_??----?----?]]]]??????----???-_++______+___--______________++_---_++__+_)vaaj??--][{1}]--_--_-)jcXf1[???[tzUXf1{}]?]]]?_--??+-|vmQr([][???]]]]-_[/YpMMqCXzJZhMMdO00LCCCCLLLCJUUUYYYYXYJCL0O0QLCCCJCCCCLCJYYJObM%@@$%Makbbbbbbbdppp
mwqqqwZZZmmwmZZmZZOOOOZmmwwmmmwwwqpqqwqdh#%@#ppqwm0wahZj!:,'   .)pwx|}}[?-??--??----?]]]????????????------?????---------______-----__------??-?]++|X*oj-_?1xJCXf)1}--}\nUQJu|[}]_[/UdZz/}}}[[][[]?---}/vQ0Ux)}}]--?---]1tzOk#Mkw0Q0QJOkoaOYJCCJUUUUJJJJCCCLLLCCCCLQQQ0QLLLLLLLQ0QQCJJUYJ0kWMhwQLLLCJCQOOQLLL
mwwwwmZZZmwwwmZZZmmZOOOZmwwwmmmwwqpqqqpbh#%@#qwmmOLZabJ(,^"'   .(bm/1{[?--??????----?]]]???????????----_+_____+++__------_____-----__-???----___]|YqbOt}1jJwpUt1]?]{tU0OYf)]?-_-[j0pwu[?]]???]?-__-1rUOOYx({}[]-__?1\rUp*8&opO0QQLCUYZohmYuXJJJUUUUJCCLLLQQQLLLLLQ0QQ00QLLLLLLQ000LCCCCJCp#aqQJQQLQQQQQQ0000
wwmmZZZZZmmmmZZZOOZZZZmmmmmmmmwwqppqqqpbh#%@MdwZZOQmodX1:,:`   .(kwt)1]-__??--??----?]]]????????????-----??]???--_____---__++_----______+___+~+[x0bdz/1/YmkOn|})\fvOqmYt){}[?_+1u0dLf}+?[-_-?}{)fcQqdwUnt|()(\juXUQqh#MMadwOQQ00QLJUJw#amJzJCUUYYYYJCLLLCCLLQLLLCLLLLLQQQQLCJCLQOO0QQLCCQd*hqO0OOQQ0QLLLQQQQ
ZZZZOOOOZZZZZZZO00OOZmmmZZZZZZmwqppqqpdbh*8@WkqZZZOqapY(I,,'   ^/opf1}1)[???----?---?]]]]???????----???????----_+++++_--__+++__---_++_-_+~~_{fUd*#bZLQp*&#dLzYLOmdqLv/{--?-]1tYmahOzjnzvnnuzUCJULZb*M#kpppqqqpdhooabpmOQQ000QQQQQQLLLq*odOCLQLLLCJJJCLLLCCLLLLCCCLLQQLCCLQQLCCLQQQLLLLCL0dapOLLOOQLQLLCCCLLL
ZmmmZO0OOZmmZOOOOOZZmmmmmmZOOZmwqqqqqqpbh*%@&adwqwmqapY(;``.   .}mwc\[}{}]]]?--???????]][]]??----?]?---__++++______+__---_--??----_-----[|xJ0O0CYzccunucYYXU0bMMawJr/fjxvzUL0Zwm0CUUUJJYcvnrt\)[+>?/UqbbwZZOQQQLLLLCCCLQQQQQLLLLLLLCCmhbw0CCCCCCCCCJCLLLCCLLCCCLLLLLCCCLLLLLLLLLLLLQQQQQOdap0CJLLLLQQQ0QLCCC
wmmZZOOZZmmmZZZZO0OZmwqwmZ000OZwwwwqqqpdh*8@&hqmqqZqadC|;""`.   <uCUj}}1{}[]]]]]????]]]]][]??---????-_______________---____-??-__+~+-[\cQZ0Xj1+!lllI;::;I;;I!+}|fnzUJUYzcnj\1-<!llllllllIIIIIIIII;:;~(z0ZOCJCJUCQLCCCCCLLLLLCCCCCCLCUQpdpOCJCCCCCCCJJCCCJJLLLCCCLLLQQLLLLLQQQQ0000OO0QQQOk#dQCCLQQQLLLQQLLLL
wwwwmZZZZmmmmZZZZZZmmmwwmmZOOZZwqwwqqppdko8@8odqppmqabQ/l,:^``. 'l1r/[[[]?-??]]???????---???-__-????]?--__----__------??----?]----[\nYJXr)-<ii!lllI;IIllII;;IIIIIl!>>i!lllII;;;;;;:::;;;;;;;;;;;;;;:,:<)zOpwLJUJLLLQQLCUJLLLLLCUUJCJYLqhhZJJCCCCCCJJJCCCJJCCCLLLLLLQQ00QLCQ00QQQQ000QQQQZaWdQLLQ00QQQQQLLQQQ
wwwwwwwmZZZmmwwmZZZmmwwwwmmZOZmwqwwqqqpdbaWB8#kppqmpabL/l::`..  ^</Xr[?]???????]]]]??------__---??]]????_--------????---????]]_+?tU0Lu(_illllI;:::;;::;;;Il!!lIIIIIII;;;;;;;IIIIIIIIIII;;;;;;;;;::;;II:;</QqmQYXXJLLLLCJLQQLCJCLCCCJYQphhOUJCCCLLLLLLLLCJJJCCLLQQQ000QQQQQ0O0QQLCCLQ0000ZhMqCJCLQ00QQQLLQ0QQ
mmwwwwwmZZZZmwwwmZZOmwwwZZZZZZmmwqqqqqqdbhMB8#bwqqZwkdQ\;,;^'   ,]fct_?}}}[]???]]]??----?]?-???---??---------------??----?]?+_[tUQJj-il!!I;IlI;:,,::::;;;;:;;II;;;;;;II;;:::;;III;::;;IllllII;;;IlIIlll;,l[uwwQYcYCCCCCLLLLCUUJCCJJUYQphk0YJCCCLLCLLLQLLCCCCLLQ0000000Q000000000QQQ00QQLQd*qCCCQ000QQQQQQQQQ
qwmmmmmmmmmmmmmmmZZOZmmmZZmZZZmmwqpqqwqdbb*%%WbZwwZmbpL(``;,`'  ,?fXr}{))1}[[[[]]?????????]]??????--------___--------?--?]__-|YQCr?i!!i!llI;;;;;;;;;::::;;:::::,,;;:;III;:::::;;;::::;;;IIIII;;;IllIlllII:,+fCqOJUUJJJJJJUUUUUYYUUYYYLwkh0zJLCCLCJJJCCCCLLLLLCLLLLLLLLQQLLLLQQ0000QLCCCLOb*dOLL0O00QQQQQLLLL
mwwmZZZwwwmmmwwmmZZZZZmmZZO0OOZmwqqwwwqdbk*&%&kmqqOZdqJ(^'"`.   "_jJu)11[]??]]]??---??-__--??----__-?]]]----____________-?_}rY0z}>l!i!IIII;;;;;;::;;;;:::::::::::;;::;III;;:;;;:,::::::;;;;;;;;;IIIIIlII;:::I)XZqLXYUUUUUUUYYYUUYYUYXU0pbQzUCCCCCCJUUUJCCCLLCJJJCCLLLLLCLLQQQQQQQLQLCCCLObodOQQ00QLJCLLLLCCC
wwwmZmwwqwwwwmmmZZZmZZmZZO00OZmmwqqwwqppdkoM%8bOwqZwkpC(^^;`    .:(Jc|{]+~_-?----__----__------------???--___---__++++_-++?/Q0j[il!!lI;;;;;;;;;:::;;;;;;:::::::;;;;::;;;;;;;;;::,,::::::::::::::::::::::;Il;:I-vqqZJXXXUJUUYYYXXzzzXXCZqqCcXUJCCCCCJUUJJCCCCCCJCCLQQQLCCCLLLQQQQLLLLLCCLOpbZCJCLLCJJLQQLLCCC
pqwmZZwwwwmmmZZZZmmmmmZZZO0OZZmmwwwwqpppdbaM%%bOwwZmdqC)`"l^ ..  .-cJXf1{}[]]]?---??]]]]]??---------???-__-----_____+++++]tzLz]!iii!l;;;I;;;::;;;;::::::;;;;;;;::::;;:::;;;;;:::::;;;;;;;;;;;;;;;:;I;:::;;;Il;;]j0pLXccXYYYYUUYXYYXXYQpdwCzYUUUJJJJJJJJJCCJJJCLLCLLLLLLLLCCCLQQQLCLLCCCLOdhmLLLLLCCCCCCCCCCC
ppqmZZmqpqwmZZZZZmmmmZmmmZOOZZmwwqqwwqpddbh#&8kmwwOZpwL|"^:^..    ;1Xmc()1[???---???????---??????--_------_______________{XQc\<l>ilI;;;III;;;;;;;;;:::::;;;;;;;::::;;::::;;;::::;;;;;;;;;;;;;;;;;:;;I;:;II;I!l;;<umZQXczXYYYYYYYUUUXcCqdqLXUCUUJJJJCCJUUUJUJJCCCJJJJJJCLLCCCCCLLQQQQCJJLOdhmJJCCCCJJUYJCCCJJ
wwwmOOZwqwmZZmmZmmmmZZmmwmZZZZmwqqwwwqpdbbh*&8kZmwqdhkO/,";"'.     "\LXf{-][?-----?---_-_--?]]]]?-___----______+___---_-[tmpt~>iil;;;;;IIII;;;;;;;;;::::;;;;;;;:::;;;;::::::::::;;:::::::::::::::::;l!I;IIIIII;:l|zOZYczXXXzXXXXXXXzzLqpwJcYJUUUUUJJJJUUUUJJJCCJUYYYYYUUUUUUUJJCLQQLJUUCOdk0XXYJJUUUYUJLCCCC
mmmmZZmmwmmmmmZZZmwwmZmmwmZZmmwwwwwmwqppdbh*8BhZmwwphkmt,";,`'.     ~fULr1)1}]?-??????-??????]]???--____++______-_--___?{fwdf+<i!lllIII;;;;III;;;;;;;;;;:::::::;;;;;;;;;:,,,::;;;:::::::::::::::;I;:;II;;;;;III:;>)JmCYzzzzzXXXXzzcvcJmwZUcXUUUUUJJJJUUUYUJJYYYYUYYYYYYXXXYYUUUUUUUUXXYC0dhZUYYUUYYYYUJCCCCC
wwZZZZmZZZZmmZZZZmwqwmmZZZZZmwqqqwmmmwqppdh*%$hZwqwpbbwt^^;"'.      ^+XpX\(1[?---????-_-????----------_________----___~+[tOm|>i!lI;;I;;;;;;;;:::::;;;:::::::;II;:::;;:::::::;;:::::::;I;;:;;;;;I;;:::::;;;;;;;;;;;_uQ0LXczXYcvczccvvvCwqOUvXYYYXXYYYUUUUYXYYYXXXYYXzzXYUUYUJJUUUYYXXXYUJLqhmJYYXXYXXXYUUUUUU
pqmZZmmmZZZZZZZmmmmmmmmZZZZZmmwwwwwmmwppdbh#B$*pppppdbpj,";^..   .   `t0Lv\{}[?------__-??-______--??-?_+++__+++~~~++_~~}jOZ\<>ii!lI;;::::::::::::::;;;::::;IIII;::;;:::::::;;:::::::;I;;::;;:;I;:::::::;;;;;;;;I,itY0LvuvccvvccvvvvuJqqZUvzYYXzzzXXXXXXzzXYXXXXXXXXXXXXzcXUUXYYUUYYYYUYYOpQzcvczXXXXXYYYYYY
qwmZZZZZOOOOOOZmmmmZZZZZZZmZZZmmmwwwwqpdbka#B$oqppqqpdqr:";^'.        )XJc|}1{]-__-___-?]]?-____--??--__---___________~+1xmw\<i!!lIII;::::::::::::::;;::,,:::;;;;::;::::::::;;::::::::::::::::;;;:::::;;;;;;;;;II:>fUZLnxvunuvvvvccvuUmwOYuczccccczzXzzzcczzzXYYYXzzzXXzzzXYUUYYXXXXXzzXUOwCvnnuczzzXXXYXzzz
wwmmZZZZZO000OZmmmZOOZZZZZZZZZmmmmmwwqpdbho#%Bhmqpqpkhdn!;I"'. ..  . .?rYz|[}[-__---?]][[[]?-____----__________--__+~~<+[fZw\>>i>!;II;::::::::::::::::::,,,,,::::::::::;:::::::::::::::::::::;;;::::::;;;;;;;;;II;~rJQXttxxxxnuvccvvcJmqZYnczcvvvvczzzzccvuvccczzccvvuvvvuvvcXzzcczzcvuvX0wCcvczXXzcvvvczXXX
mmZZOOOOO0QQ0OZZZZZZZZZZZZZOOZmmZmmmwwqpbka#%$hZwpwpboac>II,`'.      .l1XJ/[]?-------?]]?---______---??-__-----_+~~~~~<<_|Opr?+<>l;;;;::::::::::::::;:::::::::;;:;:::::;;:::::::;;::;:::;;;;;I;;::::;;;;;;;;;;;II;~xJUcffnxxnnnuvvuuvJZZLzxucccvuuuvccccccvvcccvvuuuuvccvvuvvvvvvcczzcvvcQwCvuuvcccvuvczzccc
ZZZZOOOOO00Q0ZZZOOZZZZZZZZO0OZwmZmmmmmwpdka*%$amwwwwqa#Xi:;^..        `~zQf}]][[[?_++__---------____---???---_++++++++~~<}Cwr]iIIIl;::;;;;;;;;;;;;;:,,,,:::::::::::::::;;:::::::;;;;;:,:;II;;II;;:::;;;;;;;;;;;Ill-cQYutfxxrxnnuuvuuvCqmCznuvvvvvvvzzzcvccvvuvcccvuvcccvvcvuuvvvvvccccvcYOpLvnxuvcvuvvzXXzzz
wmmZZZZmmZOOOOOOOOOOOOOOOZZZZwqqwmmmmmwqdkho8$hZmwwZOkWU>::^'....  .   "xLn\{[{}]?????-__?]?????--------?---_______++~~++[zQz/~lii!lI;:::::;;;;;I;I;;;;;;;;I;;;IIII;;:;III;;;;;III;;;;;;;;;;;II;I;;;;;;;;;;;;;;Ili}zLXujjxuuuuuuccvuvCqmLXuvvccvczzcccvvczcvvvccvvvvvuvczzzcvvvcvvvvvvccz0pQzunvvuuvvvvvzXzz
wmZZZZZmmZZOOOZZZZZZZZZZZZmmmmwwmmmwwwqpbkao%$hZwqwZQb&C~:,"`'...  .   `|zzx1[1[-_--????-----------------_______---___+++]jUOY[i>ilII;;;;;;;;;;;II;l?|rj)]_~ilIl!lI;;I;;;;;;I;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;I!+)XLvrjrxnnnnnuvvnnvJZOLzuczcvuvzcvczvvczcvuuvvvvcccvvccvvccccvvuuvvcczXOdQzuuvcvvvvuuuuuuu
wmZOOOOZZZZZZmmZZZZZZZZZZZZZZmmmmmwwqpddka*MB$hZwqwZLd&Q]I"""'...      .~/CU)-]?-----?---______-----------___++__------_+-1nbp/+>i!lII;;;;;IIIIIII;l>]/rruzzv\?<i!lIIll;;II;;;;;;;:::::;;;;;;;;;:,,::;;;IIIIIII;![jQ0ujxnunnnnxnvvunnYZOJvxuccvvvzcvccvuczccczzzcczzccvvvvvvccccvvvvvvvcY0wCvuuuuvvuvvvuvvvv
ZZOOZZmmmmZZOOOOOZZZZZOOOOOOOZZZZZZmwwqdko#W$$awqqm0QdWO(!^""'.         ^-UQt11}]-__-??--??-------_____-]]?-__++______-?_++\dhv{ili!lIIIIIIIIIIIIIIIII;Ili~(xvuxf/)+!ll!!lI;;;II;;::;;;;;;;;;;;;:,,::;;IIIIIIII,I)cmmurvvuuuvvunnuuunYO0CcnvzzzcvvvvvvcczzccczzXXXXzcvvccccvccvuunuvcvvXLmqJvcccczzcczzzcccc
mmZZZZZmmZZOOOOZZZZZZZZZZOOOOOOOOZZmmwqpko#&B$*pqwZ0QdMZt<`^"'           IxQQv{__-?-_?]????????------__?[]?-__+_______-?_+<1LqJtiIi!lIIII;;II;;;;;;;II;:,,":l+{\rzcf1~!lII;;;;;::;;;;;;;;IIIIIIIIII;;;;;;;;;;;;"I/JZ0urvvnnuuuuuvvvuuUZm0zrucccvvvvcvuvcccccvccccvvunnnuvvvvccvvuvvvcvnX0wwJvvcvvcccccvvczzz
wwmZZZZZZZOOOOOOOOOOOOOOO0QQQQ000OZmmwqpbh*MB$#dqwmOCq#wx~''^.           'i/OL/}[]?-__---??---????????????-______------_++<[vQLx>I!lI;III;;;;;;:::::,:;;::::::::;i}xvrt|)}_>!III::;I;IIIIIIIIIIIIIII;;;;;;;;;;I:>xQ0JurnuunnnnunnnunxYZmQcrnvuunnuvvvvvvvunnuuuunuuunnnuunuvccvuuvvvvunYmwOYnuvvvvvcvvcczzzz
ZZZZmmZZZmmmZZOZO0ZmwmZOOmdkhahdqwwwwmwpka*#B@owOOwZQq#qu-`",'             IfYCn{?]??--__--??]?---???--??]]?-____-?--?-_-_>-uQOv~I!lII;;IIIIIIII;;;;;;;;;IllllIII:I~}\rzJCj]<!lIIIllI;:IlI;IllI;IlII;:;;;;;;;Ill_zwLcrjnxxrxxnnnnxxrxYmm0XnuvvvunnuvcccvuunnnuuuuuuuvvvvunuvvvuuvvccvvuUZZLznucczzzcvcczzXXX
wwmmmZZZZZZZZZZZZZmmZO0Zk#88&%B&odmwwwwpbho*8BMahoWMbh#dY{",:`..           'Itw0j1??]?-----???--__---____-?----_-??-___-_+~-tUpC}<<!IIIIIIIIIIIII;;;IlIIIllII;;;;;;;;::I]tnccvvr(]~>ilI!i!!lIllIII;;;;::;;;;III![CbLvxrnuunxnuxrxxnnvJww0zxucczzcvuuuvvvuunnuvcvuuuuuvvvvnuccvuuuvczzcvY0Z0XuvzzzXzcvvczXXXX
qwmmZZZZZZZOOZZOOZZZZwph8B#bmbW&#dZmwmwpkaooW%BB$$@$8%%aC1":I^'.             !tvcf{1)}]__???]]?---??-___--___--_+_------??-])vbO(+>lIIlllllllllIIIII!!I;IIllII;;IIIII!!lIl_1|tnXCXf}<i>!!!!ll!!!lIIIII;::;Il!ll>1QbJnuvvvvunvvuuvcvucLqmCcnzXvuccvvvczzzvuvcczXzcvvvvvczcuuvvvvvcccccvvJwwOUcXYXXYYYXXXYXXXX
mZZZZZmmwmmZZZZO00Ommwb*$%bmOp*WWbZmwmwpko*#&%$@$$$@@@@#0/>i!,`'.            .I\Ju)}]?-__-????-----?-__---?-_--__-----????--}xbZ\_i!llllllllllllllll!!IIIl!lIIIllIIl!!iii!l;:""i|ncj?>illl!!!!!!lIIlIIllIIIIl!~)vp*wCUYzcvvvvccvcvnuzLmOJzvXYcvzXzzzzXXzccczzzXzcvvvvcczcvvcccccczzzzzcJmwZJzYUYXYYUYXXYXXXX
mmZZZZmmwwwmZZZmZZZmmwkM$8pOOqhW&b0Ommwdh*#M8$$$$$$@@@@#Zj_<>:^'.  .           [YYx)_?[?????]]????][]]]?-???-]}{{}]-?[]?-_+-1vkm|+>!!!!!!lllllllll!!lIl!!!l!i<~__-?--?-??]?---__?/ULUzuxf([_<i!ll!i>i!!llI;i?/zZh*awQCYcuuuvvcczzzvvzQbqQznvzczYYXXXXXzzXYYXXXzzzzzzzXXzzzzXXXXXYUUUUYY0dbwLYUJUUJUYYXYYYYYY
qwwmZZZZZmmmmZZZZOZZOmkM$%bmZwh&BhZZZZmdho*M8$$$$B$$$@@Mmx]+>;"`.              itYLf-[{[?---?]???][[[[[]-?])fcJQ00Cvf)[]???{fUkO)~>i>>>ii!!llllllll!lIl!!<}jJmkaaooaaooao*######o*W&8%BB8#kmLXuf)?_+<>i+1tuYLOmpbkd0CLQQLCCCUXYYYYXzz0hhwXxzUzzXXXYJJUXzYUUYXzccXXXXYYXzzXXXXXYYUUUUUYY0dpmCYUJUUUUUUUYYUJJJ
ZZmmZZZZmmmmmmmZZOOOOZd*B8pOZqk&@aZZmZwph*M&%@$B8W#M%$@*0/>i!:^'..         .   ,?vOn{}}[]__-][[]]]]]]]]??}\XZQz|(uCmZQ0Omwwpk*WoZ0ZO0OZO000QLJJUYXcvxf/trUpW@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@$&qu|}}(nQa%$BW**WB$@$$B$@@@B8#hZQ0QJYX0khdJvYJYzzXXXXXXzcczXzzccccXUUUXzzzXzzzzYYYXYYYYYQqw0YcYJUYJUYYXYUUJJJ
mmmmmmZmmmZZOZZZZ00QL0p*$8qQ0mb&@aZZmZmpko#WB@@$BB%88$@aJ(lII"'.                ,f0Xt)[[]?---?---?]]?-_-]|uwhY(+l!-/LW$@@@@@@@@@@@@@@@@@@@@@@@@@@@@@$B%%$@@@@@@$B&*bqm00mdho#M&W*abpppppdbhM%$@@&hm0wh&$@@@@@@@@@@@@@@@@@@@@$Wp00QUXXCmppCczXzzczzzcccczXXzzzzzzccczXXXXXcczzXYYzzXXzzX0dpOUcXYYYUUUYXYUUUUU
mmmmmmmmmmmZZZZmmO0QQ0p*$8qQ0ZdW@ammmmmqba*M%@@@@@@@@@$aC/+>!:^'                 -nJJf{{[-_---????]]]?-1jQ*B%J\|/xf{/Up#*aaadwZ00QQCUC00Zqdkao#MMMMMMW8%8%B$@@$8aJj)-!l<xZ0z/{1\nYCLCzr\{_[j08@@@@@@@@@$WhZLJLOmOXnrjjuLkB@$&amLLCYXYLmddLvczXzcvvvvczcvcczXXXzcvvczzcczccczXXXXcczXcccQddZJcXUUYYYUUYYYYYYY
OZZZOOOOZZZZZmmZZOOO0Zp*$8pOZwb&@omwqwqbh*M&%@@$@$$8#MMwn];;;^.                  ^~u0ct({{}}[]][[]]]-?}nqW@Baz()xmOr1[fCZwp0j[+>iiil;;;;I!><~~++___+<~]]-|Xb$@$hJ)~_<:":1zQ0v|~;:i}jYQOLz/)fL8@@@@@@@@@8dX(-?|uXv],,"`I1C8@$Md0JUYXXYCmbkLvucXXcvvvvzzcvvczXzzccvczzzvvvcccczXXzcvvvccc0kbmCXYUUUUUUYYUCUYYY
OZZZOOOOZZZZZZmZZZZZZmd*$%dZwqk&@aZmwwqbho#M%@@$@@$&dh&pn]:;;^.                   .[nUYt)()1[?????]]?{f08@@hJt(xJq0/-!<])()]>llI;;l!!llllIIII;;;IllIIIllI?rq@@Bpc1~~<i;"^:[rJ0Ux[i;"",i}fz0d*B@@8*dwa8@&QnfjcLwq0f?-~!>1J8@$*qQJJUYzcUOko0vvcccvvcczzzzzXYYXcccvvcczcccccczXYUYXXXXXYYX0dbqQUJJYYUJJUYJCCCCC
OOOOOOOOOO00000OOOOO0Zd*$%dmwpk&@hOZmwqbh*#M%@@@$$@&dkWqr_":;^.             .      ,]Cwu(}][[]???]]?-)uq$@@wx|jdoLt>!+<lII;;;IlI;Il!!lIIIIIII;;Ill!!lIlll}ud@@Bmx1_~>l;:^'`"!]tncu/}~i>+}\vqM$@@#ZufLoB%bmZJnjjYQn)-i;>(L%@$omQJJJUYXYCb#OunnuvccccvvcvvvczzccvvuvvczzzzzzzXXXXXXXXXUYY0dkbQXUJUUUJJJCCCCCCC
OOOZZZZmZZOO0QQQ00OOOZpoB%hqwqb&$kQZmZwdh*#W%@@@@@$%M8$dx];,"`.                   . ,vmUr1[1{?-_][]?-?)xQk#qz|(UZLn]~~>iI;;;Il!!IIllllIIIIIlllIIIIll!!lIl[up@@Bpz(_~!;::,,"^^`.,-|xunvzJCzYpM$@@oLr/Yb8BhOc/{(jcc)i!I,-vpB@$hOLCJUYXXczmhZYcvczcczcvczcccvvvvvvvczXXXXXXXXYYXXXYUJJCCJULwkh0zUCLLJUCCLLLLLLL
OOOOOOOZZZOO0QQ000ZmZZqaB%kwmmpMBkOmwZZwdaM8$@@@$B8W#&$dn{i;,`..                    ']xLCf((1}]]][[[]?-]1umaaCf1{t\]<llilIIIIIIIII;;;;IIIIIIIIIlllIl!!lII<(0$@@%oQn|]~l:,,"^^`...`,!-)fnXLqW$$@Bdv//Yb%%p0Q0mOJx)+lllI)Q*$@%b0LLCJUYXccQdwQUYUUXXXXXXYYYYXzzczzzXXXYYUUUUUUUUUJCCCCCLLJCQqbZCCLQQCJJLQLCCLLL
OOOOO0OOOOOO00000Q00Q0whBBaqZwkM8kOZmOZqboM8B@$$$$%WaW$pj+"",'.                     .^?U0v/}]}[[?-??--]/XYcXCdpx[<!>]|)-<llII;;;;;;IlI;IIIIIIIIlllII;IlllI>jwM$@@@%Mokpm0LUzvxf//\|||/rYw#B$@@8hLj|/Yd%%p00Zwc[~>>i!>?u*$@%#wCJJJUYYUYX0pqZUzXYXXXXXzzXXXXXzczYYXzcXYYYXXYYYYUJJUUYUCJUUCZpZLJUJJUUJJCCJJJJJ
OO00QQQ00000000OOO00Omdo%B#dZwhWBW**adqdh*&%BBBB$$$%M&%px-,",`.                       ;jJLz\)1[???]??[(zpY}i>tJw0j\rCdJ(_!!!!llI;;II;;;IIII;;IIlllII;;;IllIl~/Up*&B$@@@@@@@@@@@@@@@@@@@@@@@@@BaCx(?]xZ&$Maho*qUXXzXJObM$@@&kOJCCJUYXzccQpqOcxvcccccczzXzccczzzYXzzzXYYYYYUYXYJJUJUUUUYXUQdamJYYCLJJJJJLQLCCC
OO00QQQQQ00000OOOOmqaW%B@$8Mo#8$@@@@B*hhoM&%B$$$$@@BMW&pu],,,`..                      `?nwL|}1}[[}[[[\zLLj<!ii?uL00Czr{ilIllIIll;;;::;II;IIIIIIIIIIIIIlllIIIIIl+{|tjuYCQZpka*#W8%$@$$$$%WW#kZzj(]<l!1chB@@@@@@@@@@@@@@@$8*d0CJUUYYUXcczLqpmzxvcvvuvvczXYXzzXYXXXXXXXXXXYYYUUUUUYUUJUUYYJ0boZYYULLJUUUUCQLCCC
000QLLLLLQ00000000mbW$@@@$BBBB$B$$@@B*hhao#M8B$@@$B8M%@bn+`^,`.                       .")wmj(1{{{}-]|cpOf]!!ii!!i<<ilI;:;::::;;;:,::;II;;;;IIIIIIIIIlllI;;;;:;III;;;;Ill!iii>>><+_-__]}_<~~>!!lll!I;;</Xmko#MM*akkdqmOLYzzXXXzccczzcvvcULZZXxuuuuuczcczXXXXXYYXXXXXYYYYYYUJCCJUJCLCJUUUJLqhmJJJJJJUUUUJCCCCC
QLLCCCCCLLQQQQQQQ0mbMBBB@%addka&@@@@$8&&WWMWW&%$$B&#hM$bx+^",`.                         !vQCu|(()}_[rQhL[><>i!I;:::;IlII;;;;;;;::::;;;;;;;IIIIIlllI;;;;;;;IIIIIIIIl!l;IlIIII;::;IIllIIIlI;:;;;IlIIII::![uOahmUuuvunnnnnnuvccvvczzzccvvcXCqdCcccccvvccvczXYYYYYYYYYYUUUUUUJJJUUJLLCCJJUUJ0b#d0CUUJCJUUUUUCCCC
LLLLCCCLLLLLLLLQ0Zb#8$BB@B#ahoW%@@@$$$@@@$$$$$$$$BW*aW$dx-,,,`..                        ,1YkQ\)11{]1xLbY~li!llII;;IIII;;Il;;;III;::,,:;IIIII;;IIllllI;;;IIIIIIIIIIIII;;;:::;::;IllI;II!+}(tjrxrrrrt(}-ii-fOkadOmpqm0JzzYYXzcczXYzzXXzccYQhoOXXXYXccczzXXYYXXYUUUUUUJJJJJJCCCJJCCJJJJJUYUQdop0LJUUJUUUJUJCCCC
QQQQLLQQQQQQQLQOmb#B$@@@$$$$$@@@$@@@@@@@@@$$$@@@@%#ak#%du?"",^..                        .:x&ac/{1){|z0qviI<il;;IIIIII;;;;I;;;;;:,:;::::;;;;;;III;;;;IIIIIIIIIIIIIIIII;III;;;;I;;,;i?|rvcvxjf/(1{{1{[_<>!II<tOdpCcX0aMawUXYUUUUXcczzXYYXU0o&mXzzXXvcXYYYYUUXXYUJJJJUJCCCJJJCLLCCCJUJJUUYULd*pQUXYUJJJUUUYJCCC
00QQQQQQQQQQQLQZqhW$@@@@@$%B@@$@@@@@@@@@@@@$@@$B%&*hb*8bc?^",^.                           (kWaQx/(1tQwQr>i+>l;;IIIIIIIIIIII;;:::;III;;;I!iilIIllI;IIIIIIIIIIIII;;;IIIII;;IIlll;I>{rczr)-<!i~]1)(()[_~>i!!l:I+\zUUucwo*pYzYUUUUYYYYYYXzvcUbMwJYXXzvzUUUUUJJYYUUJJJJJJJCCCJJJJCCJJCLCCJJCJJqadOCUJCJJCCCCCCLLL
000QQLLLQQQQQL0ZqbM@@$@$$%W&B$$$$$@@@@B88B$@$%*kbbbdpo8pn+'^,`'.                         "|q&$#mcfjXbpn{ii~>lI;II;III;;;;IllIIIIll!~[fXOqqm0Xf}i!i!lllllIIIII;;;;;;lllllIIII;;!}uzx(<IIi}n0hM&&88&MaQn|}]-+>l;!)vOkohZJzzYYYYUCCUUUUYYXXJb#mUzczXzYJJJUUJUUUUJCCCCCLQLCJUJCLLLCJJCCCCCLCCp#hwQCLLLL000QLLQQQ
00QLCCCCCCCCCCLQmb#B@$$$$B88%B$$@$$$@@%okh#%8#bwqppqwa%dn-^,:^'.                        .:|Z#%BMmYXQ*p/+!!i!lIIIIIII;;;;IIIlIIIIIl>}/jf([??]]_>I;I;IIlIIIIIIIIIII;IlllIII;;:;!+|Uc[iIIli[u0pbqwZ0QQLcf)]-~<i!!lIl_|c0pq0JXXYXXXXXXUJJJUYJb#mUzzYUYUJCCLLLCLLCLLLLLQQQLLCCLLLLLLCJJCLQLCCLb#kZLJLQQQQQ0QQQQQQ
O0LCCLLLLLCCCJC0md#B$@@$$B%%B$$$@$$$@@B*hhMB8#kqqqddqo%dx_^":^'                         .;\ma#%BdQZpkC-I!!!lIIIIIIII;;;IIIllllllllllllI;Ii+]?+illi!!!llIIIIIIIlllllIllI;II;;;!+)uu/\\()11)(((()){]?-~<>>>ilIIIllI;;!]jJqmJYYXXYUUUJCLCJUCdomJUUJJJCCLQQLCCLLLLLLLQQQQQQQLLLLCCLQQLLLLCJJLbMpCUUCLLLQQQQQQLLL
00QQQQQLCCCLLLQ0md*%@@@$@$$$$$@$$$$$$@%ohaM%&*kqwwqwZh%dx+`^,^'                          ,\wkboWMM%Mmu?ii!!llIIIIIIIII;;;;;IIIIIIIIllI;:;i(JwwY\]~>illllI;I;;;;;;IIIIl!lIIIIIlIi_{\jrxnxxrrrrrrrnvJ00QCCQQUUUXcr/({-+?xdb0JXXYYUJJJJCJJJQh#wJUUJJJJCCLLLLQ0QQLLQQQQQQQQLLLLLCJCL00000QLLOkMbOOO00QQQQQ00QQQQ
00QLLLCCCLLQQLL0wkoM&%B$$$$B%%B$$@@@@$&hbk#B8#kpqqpwZk%dx_^^"`'                         .:tdhwph%@%kc|}_>!!llI;;;II;III;IIIIIIIIIIllllIII;>(nCCXUYx|?<i!lIIl!lII;IIllIIIIIIIl!ll;Illll!!l!!!i!!!i-uwdqLJOZQQOZmqddddqqkW*q0JJCCCJCCLQLCCQhMpQLLLQQQQQQQQLQQQLLLLLLQQQQQQQQ0ZOQQQQQQQLLLQOhWaqmZO0QQ0OO000000
0QLCCCJJCLQQLCC0wdbbbbhaaaaoo*#W8&&WM*abbhMB&*bqqqqmOk%qt>`^"^'.                         ,thowZq&$kY)~+~>i!llIIIIllIIII;;;;;;;IIIIIIIllll;I>}fnzOdw0QJXur\}]]-+~<i!!!lIIIIII;Ill;;Illl!llll!ii!!li|Uda0XXzcczXYUJCLQQLLQQ0QCCJCCCCCCCCCC0aMwCCCLQ0OOO00000000000000QQ000000O0000000000QLQbWaq0LOO000OZZZmwww
QLLLLLLCLLQQLLCLZpbbdqqqqqqqpdbkahhhkbpwqk#%WobqqwwZOk%m|>"",^'.                         ^\dhmmqM8bU|-_<ii!llllllIIII;;;;;;;;;IIIlIIIIIIllI;l_fJCv)>i>-\X0wm0LLCCQQJXuxxjt/|1[-+>!!!lllIIl!ii>iill?rd&wJUUJJJJCJUUUJUYYYUCCCCLLLLLQQQQQOwM8qLLQQ0OZZO000OOOOOOOOOOOOOOOZZZZOOZZZZZZZZmZOOb#hqZOZZmmwwwmZmwww
00QQQQQLCLQ00QQ0ZwmOQLCCLQQQLLQQLLLLQ00QZd#%WabpqqwmZaBZ1l^^"`.                          ^/kawZwa#*mf[-~i!!llllllIIII;IIII;;;IIlllllll!lIIIll;l_/vcx|1)xZC|>`'`:?XJ|+!i-{\rvJQCUzccvuvzXYvnnnnxj|}?)Q*kZJYUJCCJCCJJCLCCCCCCLLQQQ000QQQQ0moMqQ0OOOZZZO0Q00OOZZZZOOOOOOOOZZZmmmmZZmmmmmmmZZdabqmZOZmwqqwwmmwmm
O0QQQQQLCCLLQLLQLJUYYYYUJCCCCCCJUUJJCLLLOp*%Wadqwwmmwa8L-:`^"'.                          "f*#qwqk*%av)}_>i!!!ll!!llIIIIIII;;;;;;;;IIIll!!lIIII;:l~1fzOk&@WZu)-~_|O0-.      ;rUf1}[}}}\uqbu}]?][1fcXUp**bQUCCLCCCCCCCLLLLLLLLLCQ000QQ0QQ0m*WwL0OmmmZZOOOZZZOOZZOOOOOOOOOOZmwwwwwwmwwwwmZZZb*hpmZmwwmmmmwwwmmm
LLLLLLLLLCCJJCCCJUUUUUCCJJJJJCCJUJJJJCCLOp*%Whpwmmmwp*&C+;:"^'.                          "fo*wwqpk$8Lf[~ii<>i!!!!!llllIIII;;;;;;;IIIIIIllI;;;III;;Ili~-)ruvvcczCqM#Lnf|{<I~\waY)I'.  i|L0?      ~upWaqmOCCLLJJCJJCLQQQLLCCCCLLQQLLQQ00QOq&BdLQ0OOO0QQ0ZmmmOOOOZZZZOOOOOZZZZmmmmmmmmmmmZOOb*#aqZZmmmmmwwqmZZZ
LLLLCJJUJJJCJJJUUUJJJJCCCCCCJCCJUCCJJCLLOq*%#bpwmZZmd#&J~;:,^'.                         .:j**Zmqqk%&0r]i!i>i!l;IIII;;IIIII;;;;;;;IIIIIIllllIlllIIIIl!!llIIIIl>_]}1(truzXzXLwh#WM*bwZZd*WoU/[<:"l\0bkQzXYUUUYYYUUJCCCCCLLLLCCLLLLLLLLLCCQZoMqLCLQQ00000OOZZZO00OOZOO0OOOOO0OZZZZZmmmwwwmmmdo#aw0OZZZOmqqqwmmm
LLCCCCCCCJJJJJUYYYXYUJJJJJJJCCCCCCCJUJCC0q*%#dqwwmZmd*WY>::,"'.                         '!uW*OZppk8&mu[<<>>ilI;;;IIIIIIIII;;;;;;IIIIll!!!!lIIIIlllllIIllIIIll!!I;;;llll!><~_-}|rYCQmd#%B$$B&*kbhW$$%&*dwmZO0QLJJCCCJJJCLLLLCCCCCLQQ00QQQOdkZLQ0OZZZ00Q00OOOO0OOZZZZZZOOOOOOZZZOOOOZZZZOOOqh##q0OOZZOZmwwwwww
LLLLCCCCCCJJJCCUUJJUUUUUJJJJJUJCLLLJJCLC0qo8#bqwwmZwb*MXi::"`'.  .                      'iuW*Ompwqa*kC(??+>!llI;;Il!!lIIIIII;;;;IIIllII;I;IIIIIIllIIllIIIl!lIl!!i!l!lIII;;;I;;;;Ill!>_[1(\tfxvUOkW%$@@$%&WM#obwZ0QCJJJCCLQQQCCLLLLQQLLL0mh*qQLLQQ0OOOOOOZZZZOOZZZmmmZZZZZZmwmZZZOOOOOZZOOwboowQ0OZZZmmmZZmmm
JJJJUYYUUJJJUUUUYYUUUYYYYUUJJJJCCCCUUUUULwo%#dwmwwmqb*#Xi::,^'.                         'lxoaOwdqqba#qr1]+>!llIII!ii!II;II;;;;;IIIIIIIIIIII;IIIIIIIIll!lII!lll!!lll!!llIIlllI;IllIII;II:,:;;;;Il<]\nXYcr({)cbokmLCCJJJCLLQQQQQQQLLLLLCCLOh#pO00QQ00OO00000OZZZZZOZZZOOOOOZmmZZOOOOOOOOO0OOwo#q00OOOOOOZZmmmm
JJUUUJJJJJJJUYYYYYUUUUUYUYYYUJLCJCCCJUYYLwo%#bwmmmmqk*#X<Il:^'.                         .;jhhOwdqqkoMqf}-~i!llIll!!lII;;II;;IlllllII;IIIIIIllllllIIIllll!lIIllllll!!!lllllllllI;;III;;IIIlllllllIIII;;;Il[uw8MdOCCLCCCCCCCLLLQQQQQCCCJJJQdawQLLQ000QLLLQQ000OOOOOO0O000000OZZOOZZZOO00000Ow#8kZO00OOOOOOOOOO
CCCJUUUUJCCJUYYUUUJJUYYYUUYUJJUUUJCLCJUJQq*%MbqmZZZwboovl","^'.                         ';jaamqdddka#wt}]+>!!!!!!!lIII;;II;;l!llllII;IIIIll!!!!!!!i!IIl!!!!!!!ii>iii!!i>i!ll!!ll!!lllllllllllllll!!l!!!?rO&&bZCJCJJJJJUUUJCLLLLLLLLLLLCCQb*dOCCLQQQQLLLQQQQQQ00OOOOO00000000OOOOO0OO00QLLLOaWkm00ZZOOOO0OZZZ
JJCCJJJCLCJUUJJJJUJCCJUYUUJUUUJJJCCCCJJULmo%MhdqmZ0mdaocl"""^'                      .   .IjhamwppdhahQ/[]+>i!llllllII;;IllIIlllllllllllllll!!!lllll!llllIIllllllllll!!!!llllllllllllll!!!!!!llll!i!llll[YdMdXcYUUUUJJJJJJJJCCCCCJJJJJJJUCb#kmLJLLLLCCCLQQQQQLQQ0000QQLQQ000Q0OZOOOOOO0QQQLQkMkwQLZZOOO0Q0000
CCLLLCCLLCCJUYUJCLLQLCCCCCJJJCQLCLLLCUUULwo%WhbqmZOmdaavl:I,`'                          .IjbkZwpqp**wz(]?_~>i!lll!!!lIIl!!IIIl;IIl!!i!l!!!!lllllllllllli~++_-??-+~<>>!!lIII;;IIIl!!!!!!!!iii!!ll!i!ll!l?vq8hXvzXYUUYYXXXXXXYUJJUYYYUUUYXYwhbmLUJCCCLLLLLLLLLCCLLLLCCCCCLQQQLLQQQQQQ00QLLLC0kMhwCJ00QQ0QLLLLL
LLLLLLLLCJJJJUJCCCCCCCJJJUYUJCLLCLLLLJJJ0p#BMbqwmZ0ZpaauI;!:`.            ..            ,-vh*hkhho%WZu(]-~>ii!llllllllIIIlIIII;;IIll!!i!l!!ii!!l!!>~-)uCwba*######hm0LYvuxj/)}?_~<>i!!lll!iiii!l!>!l!!l>{Y&#CXXYUUUUUYXXzzXYUJJJYXXXYXzczOdkpLYYYYUJJJJJJCLLCCCLLLLLLQQQQQQQLLQQQQQLLLLLLCLbodZJULQCLLLLLLLL
QLCCLCCCCCCCCJCLLCCCCCJJUYUJJJCCCLLCCJUULq*$MbpqwZ0OdaavlI!,'.                 ...   ^i)uOb*M&8%$@@&Ou|}?+>ii>>>iiii>>illlIllllIll!!!!!!iiii!!<]tz0doW%%&#ahbqqo&&oZOdhaaooabqqwZQUcnt)1{]-+<ilI!>!liili[z&M0JJZdbpZQCUXULQ0QLCJYYYUUUUYUQmbdQYUUUJCCCJJJCCLCLQLLLQQ0000QQQLCCLLQQLCCLQQLCLdobmJYCQ0wbbdwZOO
QLCLQLCJJJJCCLLLLLCJCLLCJUJUUJCCCCCCJCJUQp#$MdqqmZ0Zk#*cl:I"'.                      `}cd#abqb*W&W#&aYf([+>!!i>><>ii>>iiiii!ll!!i!ll!i!l!!!<]/XwokmQQbokpqqqqqZZh#awznvvuXQhMpCXY0b&%8&##*bwOJvj/|)[-__-(Xd%*CJ0wbaokmJXX0h&8k0JUJJUYUJJYYCmM8aq0LLCJJCCCLLLLCCLLLLLCJLLLCCCLLLLLLCCCLCJUUUCpahmXcCOpko*apO00
LCJCQLCJUUJCCCJJCCCJCLCJUUYYYUJJJJCCCCCJ0p#B#dqqwZQOd**cl;l"'..           ..     ."~\QkakpqpbaM&M**qx){?~<>>ii>>>!!!iiii!!ll!i>>iiiii!ll_fJdkwUf_:,?LapwqqqwmOOk#*b0CLCCma%$h0UCwhM&8%&hmOZpo%$$%Madqqpa88amCOdppbkdmJXUZo8$MbOJUYXXXXzczCqW$%#q0JUJUYUUUUUUUYYUJJUYUJCJJUUUUYYYYYJCCL00LXzQpdwJYQmdkkhk0UUJ
CCCCLCJJJUUJJCCJJJJUCLJUJUXXXYYUUJCCCLCC0p#$MbpqwZQZk*avl:I"..      ..    .   `I-/zZkkbddppddbh*&&*qXr/)[_~>iiii>>>>>iiiii!!l!!i><<>>~{vm#Wh0urr\)(rp#dwwwwmmOZa&#hqqppqoB@$#bpk&B%%B@$*pwwpa%$@$8#a*MMMW#akk*WM*##abwmpaWWM88*kpwwmwmOQQOwbho*#kOQQQQQLLLCCL0mwwmZOOZwdkhdZ00000QLQOb&8#qLZpkbmmdkhhhhbOCLQ
LLLLCJUJLCCJJCCCCCJCCLJJJUYYYUUJCLQLLCCULw*BMbpwZOQma#hu!;I"`'...    .     ';[vOhoohkbbddddppddpdk#WW#hbpLuf\{-~<>i!!!iiiiiiiii>>>>!~/C#@@BW#*###**#88W#okpqwZmo&M##WBB&%$@@$B%B$$BB$$@$$BB$$@@$$BB%%%%%%%BB%BB8&%B8WMW8&Moa*&%$$BBBB%8WMWW#akaW8&*bdpdpmOOmd#B&apmwdoMMW%B%&#abppdkaMB@$8#ooMMooMW&&888M*##
JJJCCJUUJUYUUJCCJUUJJUUUUYXXYYYUUJCCCLLC0pM$MbpwmOQmh*hu!Il,`'...   ..    `+uqWWabdddddbbdddbbdppddbbh*M&8%%&*d0Uzx\)1]_~<<<>><<<i!<{cpW$B&*hbdbbbbkho#W&888WhdoW#aqpho**okdbhookpmwdkaahhkdppqmmwqppwmmqpdbbkkpqbaMWWW#ohbddbko##aaa*W&&88W#aha*WWWWM####*#MWM#***###ohbkh*MW&&MM&8&W**#W&8&&&8%8&MabbhM%88
YYYUUUYYYYYYYUUJUUUUUUUYXXXXXYYUUUUJCCCJ0dM$#dqqwZLZh#oc!:I,`'..     ... ,1zb#okppddddddddddbbdddddpddddbbkkko##MWW#*akdqZCzxft/())tcp&B$%W*ahkkbbbbbbkbbh*W%8&%%*pCYYXzccuxxnczcvuuccczzccvuuuvvvvvvvuuvcczXUUYXYUL0O0LLCCCCCJJCJJJJJJCQQQ0QLLCJCQOwpbkbdqmOQCLOwppqZQCUYXXYJL0OmwqwmOOOO0OOQQ00QQQ00LCQO0Q
YYUUJJJJUUUUUUUJJJUUJCJUYYUUUUYYYYYJJJUYQdW@#pwwZ0J0b*av!:;^'.......   .>/Zoakqqdddpddddpdddppppdbppddppddpddbbbbbkhhh*WWW&8&&WWWWWWWWWWM*akbbbbddbkdddbbbdbaM%$BMdLYYXvvvunxxnuuunuuuuuunnnnnnnuvccvuuunnnuvvvvvccXXYYYXzzzXXYYXzcccvvcccvvvvvvvvuuuvczcvuuvvvvuvczzccvvvcccvvcvvzXzXYYXXzcvzzzcczXYYYXXXYY
CCCCJJJUUUUJJJCCCCCCLLCJUUJCCCJJCJJCCJJJ0bW@MbqmOQCQqa*X>"^^`''`'.   '^>up##bppddddddddbbddbbbbbbbddbbbbbddbkkkbbbbbbbbbddddbkkkkhahkdpbkkbbbkkbdpdbkbddbbkbdkoW88oZCJXcccvvuuuuuunnuuvuuuuuuvvuuvvccvvvvuuvvvvnnvzXYYYXzzXXYYXzzzccczzzzzccvcccccccccccvvuvvczzzcczzzzzzzXYYYYXXUOwwOCUXXXYYUYzczXXzvzXzzYU
QQQQLLCCJJJCCCLLCCCLQQQLLLQQQQLQ0000QLLLZk&@WhdqmOL0qoWJ~,^""``^`..'.^_rq#hdpdkbddbkkkkkbbbbddbbbbbbbbbbbddbbbbbbkkkkkkkkkbbkkkkbkkbdbkkkkbbdbbkkkbbkhkkbkkkbdbaM%8kZCYXzccvccccccvvvvcunuvvvccvccvvuuuuuuuuuvvuuucczzzzXXXXXzzzzzzzXXXXXYUUYXzzXYYXXXXXzzXXXXXzzczXYYYYYUUJJUUJQZdhapLUJLZpkkdwZO0000QLCJUU
LLLLCCJJJJJCCLQ0QLLQQQLLLLCLLCCLLLQ0O0LLObW@&hdqZ0LOb#&Y>:;"^^^^^'..`ixqaobppdddpdkkbbbddbbbbbbbddbbbdbbdddbbkkbbkkbbbbbbbbdbbbbbbbbbkbbdddbkkbbbbkkhhkbbddbkhhkhM&&oZQCJJUYYYXXzzccvvvuuuuuuvuuvzcvuuuuvvuuuvcvvvvvvcczXXXXzcczzXYYXXXzczzzzzzzXYYYXzcczXYYXXXXYJJJJUUUUJJUUUJLOOCLZqbkaakpZOZqbbbdbkhahbwm
LLLLLLCCCCCCLLQLCJCLLCJJJUUUJCLLLLLQQLCJQp#$WhpmZOQZkMWY<;I,^^^`''.'!(OWodpdbbddddbddddpddbdbbkbddbbddbbbbbbbkkbdddbbddbbbbbdddbkkkkkkkbdddbbkbbbbbbkkbbbddddkhkba#B&d0JUUYYXYYYXzcccvvcccvvvnnnuvuuvvuuuvcvuuuuuuuvvcccccccccvvvvzzcczzczzzzXXzXXXXzccczXUUzccXJLLCUYXYYYYUUJL0LJXXCZdbdmQJUYYUJJJUUJJCQQLL
CCCCJJJJUJCCCCCCCJCCCCCJJJJJCCLLQLLQQCCJObMBWhpmmZOmb#8J~l!I:,"..`^;{zbWhpbkbbdddddbbbbddbbbbbddddkkdddbbbkbbbdddbbbbdppddbbddbbkbdbkkbbdbbbbbbdbkkbbbbbbbbbbbkkbba88oqCYYXzccczzzXXzzzzzcczcvvuunnuvvvuvczzccccvuuuuvvvvvvvvvvvvvczzXXXzzzczXYXXXzzzXYYYYYYXzzXYYYYYYUUYYYYYYYYzzzXYUUYXzzzzzzXXXXzzcccczzz
CCCCJJUUYJJJJJJJJJJJCCCCCCCCCCCLLLLQQQQQmkW$WhdwmZOZq#$O[l:^`^^`'.`lfmo#bwpdbbddbbddddddba*M*kbdddbkbbkkbbbbbbbbkkkkbbbbkkkbbbbbkbdbbbddbkkbbbbbkkkkbbbbbbbbbbbbbbh#W&ow0JYXzczccccvuuuuuvvvvvvvvcvvvvccccccczzcvvvvcccccccczzzzcccczXXzzcccczXYYYXzccccvczzXXXXzzzzXXXXXzcczXXXXXXXXXzccczccccczzzzzzzXXXXX
JJCJUUUUUCLLLLLCCCCJUUYYUUUUYUJLLQQ00QLLObM$WkpwmmmZmo$q/~,^^^`''':-za*adqpdddppddbddbbbkhM%Wahkbbbbkhkkbbkkkbbbbbbkbbbkkkbbkkkkkkkbkkkbbkbdddbbkhkbbbddbbbddbkkkbka#8#pQXzcunnnrrxxxnuuunnuunnxnnxrrrxnnxjjjrxrrrxxxxxxxxxxrrnunnnnnnnnxxxnnunnnnxxxnnnxxnuuuuuuunnnuuunnnuuvvuunnuvvvuuvcccvvvvvvvvvvuuuuu
JJJUYYYYYJCCCCCCLLLCJJUUJJUUUUCLLLLCCCCC0q*%&obqwmmmma%dn-";I^.  '~jw&oqpbao##*##MM*ooaaaoM%&#ohhhhhhhkkkhhkkbddddbbbbbbbbbdkkbbdbbkkkbddbbbbbddbkbbbbbbbbbka**ahhkk*8MbQvnxjft/\|||/fjft////\\\||||((|\||(((|||||\|||||\\\\\\tfjjjjjftt/tfjrrrjjjffjrrrjjrxxxrxxxrrrxrrrrxnxrrjjjrrrjjjjrrxxrjfffffjfffffff
CCJUYYYYUJCCCJJCCCCJJJJJJJJJJJJJCCCJJJJC0w*B&okpmOOOZk8hY},;I"'   !\Ca**&88#dOJJJXcYC0mh*#&B8#ahhoW&WWWW&&WMMMM#*ahhkhhaahhkkkbbkhkbbddbbkkbbkbbbbbbbbbbbddh*MMakbbbo88#mXvurft/\\\\||||()1)(|()))(((((|||||\\\\/\\\\\\||\//////ttffft////tjrjjjjjfffffftfjjffjjjrrxrjjjjjjfffjjrrjft/ttfftttfftttttffffffff
JJJUYYUUUCCCJUJJJJJJJJJJJUUUJJJCLLLCJCCCQwo%WhdwZOOOObMa0|Il!,`'...'I?\v0db0f1]??_~+--|Jk8B%Moao#W%$BB$$BBBBB%8W#**M&8&M#***#MMMWM*ahkh*#M*ahhhkkkkkkkbbkbbh*W&#ahaoM%%WqYzvxft/////\()\jxnf\()(((((|||||\\\|\\/\((|\//||\/t/\\||||\\//////ttttffft/ttffffjjfffjrrxrjft///////////\\|||\\\\\\\////\\\\\/////
000QLCCJUUUUJJUYYUJJJUUUUUJJJUJCCLLCJJJCQZh&*pZQ00QQQwohZ/Ili:^^^'''.  `}xJz[<?-~~~<<+|Ch8&abbaM&%88&&W&&%$$B%&M*oo#W%WoaoM8BBBBB8MoahoM&&Mo*WM#oahhaao*#*aaoW8M*o*WWodOUvnxrft///\|()\XZdmr\xJwmJx|(|\///||(((()11)|//|()))(||(((((||\\\\\||||\\\\\//ttt//tttttftttt/\\||||((|||(||()(())(|||(((((((|\/\|||
UUJJJCCJUUUUUUUYYYYXXXYYUUYYYYUJCLLCCJJL0mhWopZQO0CUJZaopr<~+I"^^^`'.. `/UJn?<]-<<~<+[jw**oa*MW&M*akkkbh*W%B%&#*aao#&BWooM8BBBBB$%W#*aoW%8Wo*&8&888888%BB%&WWMaJjtckomJcuxrffttttt//txXQmZ0JLw*BMqOLJzxt/\\||()11))(|||(){{{1)(((((((((||((()))(((((|\\\\\\\\\\|\||||||||||))(||)1)((())1)|||((()))))(((((((
UUUYXXXXXXXYYXXXXXXXXXXYUUUUYYUJLQQQ00Zwpb#%MkwOZ0JYUZaaqtI!!^.        "vmXt-<_+~~+>_\Uo&hh#WW#akbbkkbbddh*MWM#oahhoM&W##MMM**#M&8&W##M8B%&MM88888888%$$BB%B%Mpu)\X#WZUcunrftfxvXC0Zw0JUJ0ZZQYQbdZmqhk0zr/|(())1)(((()1)))))))(((()))))))))))11)(()))(((())((((((((((||||||)(||((((()(()1)(((((((())(())))))
OOZmmmwwwwwqpddbbbbkkkkkkkkkkkkao***#W&%%8B$&admOLUYJma#aLnnunxrjfjrrxxXo80x|{[_<~_~[cp88kdkkbppdkkkbddbbbbbbbkhkkkbkkhhhkbbbkkkkhhhkkhao*****oa*M&%B%W&B$$$B#wn|tJW&ZUvnrrrjffffruczx//fjjft\/ft|11rYQOLJYzcunxxjt\|(()((||||((((((((((((|||))((((((|||(()((||||||||||(((()(|((|||())()11111)((())))())1111
#MWW&&WWW#*###*o****ahbddppqqwmwwqqqqmmZZwh&&#dOCUXXJZa&%Moaaao*###MMM&%$WQnj\)[-__+1Uh88kdbbbbbbbbbbbbbbkkbbbddbbkkkbbbbbkkkkbbkkkkbbbbddbkhkbbh*W8W#hh*&B$$oOvffY#Wp0Jzvuunnnnxxrrjjfjrffrrjt/\//\|\fnvXUUYUUYzurf//\||||||||||||((())))))))(|||(((((())1)|||||||||(()))111))((())()1{{{{11)((()))))((((((
OOOO00QQQCJCCJUUUUUUYYYUUUUYYYYYYYYYYYXXYCwo8$8M*ooo###ohqOOOQQ00QLLQOda#dn(/([??_-}jq&&#bdbbbbddbbbbbbbkbbbbbbbbbbbbbbbbbbkkkkkbkkbbbbbbbbbdpddkhhhhkkkbaWB$hCx/tUW8pQUzccvnxnunnnxrxnnuuuuxffrrfffffffft////tttt///ttft////////tttt//\\\\\\((||||((||(()))||||||((((((((||()11(()))1{1(()(((((((())1)))111
YYXXXXXXXXYYUUYYXXXXYUUUUUUUUUJJJJJUUUUUJCL0wdppppqwwZ00QQLLQ00000OOZm*8hQr||)]--_?/U#$*kbddppdbbkbbbbkbbbbbbbbbbbbbbbbbbbkkbbbbbbbkkkbkbbbbbbbbbbdbkkkbpdh&$hJj\x0%%qQCYXXXcuxxnuuunnnnnnnxrrrxxxrrxxxrrrrrjjffffjjjjjjfttt//////tffffffffjjtttt/\\\\/\\\\\///\|||\\|((((||)111)(((())))1111{{{11)))))))(((
uuunuunnxnnuvvuuuvvccczzvvccccXXzzzccvvczXzzzzzzzczcvczXXYXYYXXYYUUJLmW$qc/)){]-??)YdW&kpkkbbdbbkkbbbbbbbbbbbbbbbbbbbbbbbbbbkkkbbbbbkkkkkbbkkkkkkkbbkkkkkka&$dXjjJd%WmLLJUYYzcccccvvuuuuuunnxxxnnunnxxnnxxxxnxxxrjjrxnnxrjjjjffffffjjfffttfjjttffftttfff//////tttttfftttttftt/\\/tt/\\/\||||((((|\\\\///tfff
xrjjjjjffjjjjjfffjjjjjrrrrnxrxxxxrrjrrrrjjrxrrrrrrjffjrxnnxjffrnnnxxcLM$Zx/(1[-+_[/m&MobdkkkkbbkkkkkbbbbbbbbbbbbbbbbbbkkbbdbbkkkkbbbkkkkkkkkkkkkkkkbkbkkbbkWBdXrn0hWo0JJUYXzzzccccvvvvvvvvvunnnuuunxrrrxxrrrxrrrxrrxnnxrrxnuxrjjjjffjjfttffjjjfjjrjffjrxjjjjttjjffffjjrxxxxjrrrrxxrjffjfffjftttffjjfffjjjjjj
jjjjjjjfftffjfjjjjjjjjjjffft//fffjjfftttfjjjt//tfjfffjjrrjffjjrrrrxxuU*B0f\()}?-](nbB#kkkhhhkkkkkkkkbbbbbbbbbbbbbbbbbbkhkdddbkhkkbbkkkkkbbbbbkkkkbbbkhhkbbkWBdXrxOa#hQUYzvccccvuuuuunuuuvvunnnnnnnnnnnxxxnnnxrrxxnxxrjjjrxnnnnxxxxrrrrrjfjrxxrrxxxxjjrxxrjrrffjjjjjrrrxxxrjfjxxrxxxrjjrrjxxxrjjjjjjjjjjjjjjj
ffttffffffffffffjjjjjfff//tt//tfffffft////tft//ttfffffrrrjjrxxxrrnnncC#BQt(11[--[tUo$Mkhhhkbbbbkkkkkkbbkkkkkkkkkkkkkkkkkbbbbkkkkbbbkkkkkkbbbbkkkkbbbbbkkdba&$pcfrZo#kQJUXcvvunxnuunnxxxxnnxxxxxxxxnuuunnnnnnrrxnnnxxxxxxxxnnuunxnnnnxxrrrrrrrxxxxxxrrxrrrrnuuuunnnnnnnnuunrjrnnnxxnnxxxxxxxxxrrrjjjrrjjjjjjj
tt/tttftttffffffftttttff//////ttttttffffffffjjfffffffjrxnuuuuuuvvuuuUm&BQft|{]__[r0#B*bkhkkbddbkhhkkbkkkkkkkkkkkkkkkkkbbbkkkkkbbbbkkkkkkkkkkkkkkkkkbbbkkbbh&$pcjnq#MkLYYzvunnnnnuuvuunnuuuunnuuuunnuunnnnxrrrnuvvuuuvcvvuuvvuunuuvccvuuvvvvuuuuuuuuvvunxrxnuuunnnuvunxxnnnnnnuunxxxnnxrrrrrxxxrjjrxnnnnxxxxx
fttt//////ttfffjjtttjjjffjxxxrjjjjfjrrxxnunnnnuuuxjjrxnuunxnuvvvuvuvQb#az||)[--?1vqM%obbkhhkkkkkkkkkkkkkkkbbbbbbkkkkkkkkkkkkhhkkkkkkkbbbbbbbkkkkkkkkbbkkkkkW$bUnuZoWhCXzccvuvuuunnvvvunuvvuuuvuxrxuvunuunxrrrxnuuuuunnnnuvvuxrrrxnuuuuuuuuuvvvvvvvvvvvunxxxnnnnxxxxxxnnnnnnnuuunnxrxnnnxxnuunxxuvcvuuvuuuvvv
///\\\\//tffffjrrrrrjjjjrrjjrjjjrrrrrrrxxxxxxnnnxrjjxxxnnnnnnnuvuunvQkhqu|\|}?][(UaMMakhhhhhhhkkkkkkkkkkkkkbbbbbkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkbbbkkbbW$kUxrCbWoLXXzccvuuuuuuvvuuuuvvunnunrjjrxnnxxnxxxxnnnuxrrrxnnxrrrxxxxrrxnnxxrjjjrxrrxxnnuuuunxxnnnnnnnxxxxnnnxxxnnnnxnnxxnunxxxnnnrrnuuunnuuuuuuu
\\\/ttttt////ttffjjjftffjf//tttttfjjjjjfffjrrrftfjjrrjjrxrrxnxxrxxxv0hhZt-1)}?-]|C#W#kdkkbbkkkkkkkkkbbbkhhkkkkbbbbbkkkkkkkbbbbbbkkkkkkkkkkkkkkbbbbbbbkkkkkhW$hCnn0a8oLXXcvvunnnnnnnnxnnnnunrrnnxrrrxnnxrxrjjrjjjjjrrxxxxxrrxxxjjrxuunxxnnxrrjrrrrrrxnnnxxrxxxxnnnnnnnnuunxnunxrjrxxxnxxxrrrxnnxxxrjjrnnnnuuu
ffffffffffffftfjrrrrrxnnxrxxxrjffjxxxxnxxxnuunrfjjxrjfffjjruvxrjjxxnLkhmt-)(}]??{nmW$#kkkbbkkkkkkkbbbbbbhhhkkkkbbbbkkkkkkkkkbbbbbbkkkkkkkkkkkkkkbbbkkkkkkkh&$aQuuZaMkYvcuxnnxrrrxnnxrjjjjxnrjrjjjrjfjjjrxjttjjjjjjjrrjjfffjjrrjffrnnnnnuunxxxxxnnnnuuunxrrxxxxxnnnxxxxxxxrxxrjt/tfffjjfjjjjrrxxrjjrrxnnnnnnn
nxxrrrrxxxrjjfjxxxrxnnnxxxnxxrrrxxxxrxxnnnnnxxxxxxnnxxxrrxnuunnuunxnYwMWU|(1}[[-~]tOM8WkdkhkkkkkkkkkkkkkkkkkkkkbbkkkkhkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkhkbdkMB*mzvZa#bXvcuxnnnxxxnnxrjjjjjxxrjjrrxnxjjrxxxrjjxxxxrxxxxxxrrrrrrrxxrrrrjjjrrrrxxjfffttffjffttfffffjjjjjjfffjjrrrjttffffffffjffjrrrrjffjjjrrjjjj
xxnnuuunnnnnnunxxxnnnxxxnnnnnnvvunxxnnnnnnnxxrnnnnnnxxnnnnuxxnuvuxrjxzp*qYt)(1]-_+?/Y*$&ohkkkkkkkkkkkkkkbbkkkkkbbkkhhhhkkkkkhhhkkkkkkkkkkkkkkkkhhhhhkkhhkka&BoZvnOhMbYcXvxxnxrrjjjrjfftffjjfftfrxxrjffjjftttjjjftfffffjjjrrrrrrrrrjjjjjjjrrrrjjjffffffftttfft/////tt/\\\/ft/tt//ttt/t/////tttffffffjffffjjjj

On path-likes

@TamoghnaChowdhury suggests

taking path as an actual pathlib.Path instead of as a str, at least for the purposes of the type hint. Maybe even path: pathlib.Path | str to shut the type checker up (I'm not sure where you can get the type annotation PathLike from)?

You can use os.PathLike. However, if we were to abide by the protocol of the inner open() call, then a PathLike is incorrect, because it can also take a file-like:

    :param fp: A filename (string), pathlib.Path object or a file object.
       The file object must implement ``file.read``,
       ``file.seek``, and ``file.tell`` methods,
       and be opened in binary mode.

But I consider all of that to be a bit much for this application, and would constrain it to PathLike for simplicity and clarity.

Simplified algorithm

I don't think you need to bisect. You can simply load the image in 'F' mode (32-bit floating-point), scale to your min and max, round to the nearest index and then index into your charset. You can take the opportunity to improve Numpy vectorisation and eliminate your loops.

from os import PathLike
from pathlib import Path

import PIL.Image
import numpy as np


CHARSET = (
    "@$B%8&WM#*oahkbdpqwm"
    "ZO0QLCJUYXzcvunxrjft"
    "/\|()1{}[]?-_+~<>i!l"
    "I;:,\"^`'. "
)


def resize(image: PIL.Image.Image, new_width: int = 300) -> PIL.Image.Image:
    width, height = image.size
    new_height = new_width * height / width / 2.5
    return image.resize((round(new_width), round(new_height)))


def pixel_to_ascii(pixels: np.ndarray, light_mode: bool = True) -> str:
    direction = 1 if light_mode else -1
    charset_str = CHARSET[::direction]
    charset = np.array(tuple(charset_str), dtype='U1')

    minp = pixels.min()
    maxp = pixels.max()
    scaled = (pixels - minp) / (maxp - minp) * (len(CHARSET) - 1)
    indices = np.around(scaled).astype(int)

    ascii_array = charset[indices]
    rows = ascii_array.view(f'U{pixels.shape[1]}')[:, 0]
    return '\n'.join(rows)


def convert(path: PathLike) -> str:
    greyscale_image = resize(PIL.Image.open(path).convert('F'))
    return pixel_to_ascii(np.array(greyscale_image))


def main() -> None:
    ascii_img = convert('archived/image2l.png')
    print(ascii_img)


if __name__ == '__main__':
    main()
\$\endgroup\$
5
  • \$\begingroup\$ The use of light_mode is pretty clever. I didn't thought of doing that. \$\endgroup\$ Aug 4 at 2:30
  • \$\begingroup\$ What do the "arrows" means? (The parts that do ->something outside of a function) I've seen them a lot but I have no idea what they are \$\endgroup\$ Aug 4 at 2:32
  • 2
    \$\begingroup\$ Return value type hints \$\endgroup\$
    – Reinderien
    Aug 4 at 2:54
  • 1
    \$\begingroup\$ I'd suggest taking path as an actual pathlib.Path instead of as an str, at least for the purposes of the type hint. Maybe even path : pathlib.Path | str to shut the type checker up (I'm not sure where you can get the type annotation PathLike from)? \$\endgroup\$ Aug 4 at 13:11
  • 1
    \$\begingroup\$ @TamoghnaChowdhury See edit. \$\endgroup\$
    – Reinderien
    Aug 4 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.