Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.mixmin.net!hq-usenetpeers.eweka.nl!hq-usenetpeers.eweka.nl!border1.nntp.ams1.giganews.com!nntp.giganews.com!newsfeed.xs4all.nl!newsfeed3a.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'insert': 0.05; 'url:bitbucket': 0.05; 'debug': 0.07; 'debugging': 0.07; 'method.': 0.07; 'none,': 0.07; 'none:': 0.07; 'paths': 0.07; 'tests.': 0.07; 'advance': 0.07; '#print': 0.09; '[0]': 0.09; 'dst': 0.09; 'guys.': 0.09; 'lawrence': 0.09; 'method,': 0.09; 'method:': 0.09; 'snippet': 0.09; 'subject:method': 0.09; 'tcp/ip': 0.09; 'runs': 0.10; 'cc:addr:python-list': 0.11; 'python': 0.11; 'def': 0.12; 'language.': 0.14; '"""install': 0.16; "'''": 0.16; "','": 0.16; '-1):': 0.16; '-1,': 0.16; 'attaching': 0.16; 'caused.': 0.16; 'dst):': 0.16; 'dst,': 0.16; 'dst:': 0.16; 'elephant': 0.16; 'event):': 0.16; 'fetches': 0.16; 'flow,': 0.16; 'hashed': 0.16; 'instantiated': 0.16; 'lot!': 0.16; 'param': 0.16; 'path.': 0.16; 'paths.': 0.16; 'src': 0.16; 'src,': 0.16; 'subject:class': 0.16; 'suggestion.': 0.16; 'switches': 0.16; 'traverse': 0.16; 'true:': 0.16; 'exception': 0.16; 'sat,': 0.16; 'skip:= 10': 0.16; 'language': 0.16; 'fix': 0.17; 'wrote:': 0.18; 'bit': 0.19; 'trying': 0.19; '<': 0.19; 'everyone,': 0.19; 'skip:p 40': 0.19; 'fit': 0.20; 'help.': 0.21; 'example': 0.22; 'import': 0.22; 'cc:addr:python.org': 0.22; 'print': 0.22; 'install': 0.23; 'entries': 0.24; 'specify': 0.24; 'question': 0.24; 'cc:2**0': 0.24; 'cc:no real name:2**0': 0.24; 'source': 0.25; '>': 0.26; 'logging': 0.26; 'nearly': 0.26; 'defined': 0.27; 'skip:_ 20': 0.27; 'header:In-Reply-To:1': 0.27; 'tried': 0.27; 'point': 0.28; 'host': 0.29; 'skip:p 30': 0.29; 'raise': 0.29; 'message-id:@mail.gmail.com': 0.30; "i'm": 0.30; 'url:mailman': 0.30; 'code': 0.31; "skip:' 10": 0.31; 'node': 0.31; 'object.': 0.31; 'piece': 0.31; 'skip:= 20': 0.31; 'class': 0.32; 'run': 0.32; '(including': 0.33; 'url:python': 0.33; 'running': 0.33; 'mac': 0.33; 'skip:# 10': 0.33; 'skip:& 30': 0.33; 'skip:t 40': 0.33; 'skip:_ 10': 0.34; 'skip:d 20': 0.34; 'could': 0.34; 'skip:s 30': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'really': 0.36; 'event,': 0.36; 'false': 0.36; 'url:listinfo': 0.36; 'method': 0.36; 'thanks': 0.36; 'hi,': 0.36; 'url:org': 0.36; 'two': 0.37; 'list': 0.37; 'clear': 0.37; 'implement': 0.38; 'skip:o 20': 0.38; 'skip:& 10': 0.38; 'skip:[ 10': 0.38; 'pm,': 0.38; 'skip:_ 40': 0.38; 'here:': 0.62; 'complete': 0.62; 'address': 0.63; 'kind': 0.63; 'skip:n 10': 0.64; 'our': 0.64; 'more': 0.64; 'here': 0.66; 'between': 0.67; 'results': 0.69; '8bit%:100': 0.72; 'url:info': 0.73; '2015': 0.84; 'route': 0.84; 'subject:base': 0.84; 'to:addr:yahoo.co.uk': 0.84; 'url:log': 0.84; 'utilized': 0.84; '\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0': 0.84; 'destination': 0.91; 'subject:results': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=6Y7u84yNZv4RPAaLi/BTZQOU8nGgBdpmBEeKBWvB3Us=; b=ApgZaL2CS054wb+t2dkA5JRVaLOvStibRbm8MsnWQDPZUJ1EL+z2uWtX+VaIkLMkbD VUAO5VULSP+CRCnjUf0E6yh9Ldp8dm7br4BShDc2dhk/p60KpYi40drlg1O7mEEusNnN abpQDIBwanvuSpWbk4LxoW6346otRJWlyhy/4UdFTGfUTA/gAmBeCFdy7MNbnqwewbxx 66H1OAwGFjfM/6WF2PPrjF4mh1ahIae+DmK7Y1zJ6TD+hnfPKeow6Lyt0BInaMQv30yb G9FxO7laDlKbd/8CzaF4jHyiXyzHzeonN2braM7gXvaJdXyvidkvrd9Bzqx6v/4CQ11u FBTA== MIME-Version: 1.0 X-Received: by 10.42.197.65 with SMTP id ej1mr1871470icb.13.1431158360251; Sat, 09 May 2015 00:59:20 -0700 (PDT) In-Reply-To: References: Date: Sat, 9 May 2015 12:59:20 +0500 Subject: Re: calling base class method fetches no results From: david jhon To: Mark Lawrence Cc: python-list@python.org Content-Type: multipart/alternative; boundary=20cf303ea680ae24d50515a1850e X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 612 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1431158369 news.xs4all.nl 2899 [2001:888:2000:d::a6]:59705 X-Complaints-To: abuse@xs4all.nl X-Received-Bytes: 37807 X-Received-Body-CRC: 4152984005 Xref: csiph.com comp.lang.python:90271 --20cf303ea680ae24d50515a1850e Content-Type: text/plain; charset=UTF-8 Hi, I am sorry for sending in five attachments, I cloned the code from here : Let me explain it here: Routing Base class defined in DCRouting.py: import logging from copy import copy class Routing(object): '''Base class for data center network routing. Routing engines must implement the get_route() method. ''' def __init__(self, topo): '''Create Routing object. @param topo Topo object from Net parent ''' self.topo = topo def get_route(self, src, dst, hash_): '''Return flow path. @param src source host @param dst destination host @param hash_ hash value @return flow_path list of DPIDs to traverse (including hosts) ''' raise NotImplementedError def routes(self, src, dst): ''' Return list of paths Only works for Fat-Tree topology @ param src source host @ param dst destination host @ return list of DPIDs (including inputs) ''' complete_paths = [] # List of complete dpid routes src_paths = { src : [[src]] } dst_paths = { dst : [[dst]] } dst_layer = self.topo.layer(dst) src_layer = self.topo.layer(src) lower_layer = src_layer if dst_layer > src_layer: lower_layer = dst_layer for front_layer in range(lower_layer-1, -1, -1): if src_layer > front_layer: # expand src frontier new_src_paths = {} for node in sorted(src_paths): path_list = src_paths[node] for path in path_list: last_node = path[-1] for frontier_node in self.topo.upper_nodes(last_node): new_src_paths[frontier_node] = [path + [frontier_node]] if frontier_node in dst_paths: dst_path_list = dst_paths[frontier_node] for dst_path in dst_path_list: dst_path_copy = copy ( dst_path ) dst_path_copy.reverse() complete_paths.append( path + dst_path_copy) src_paths = new_src_paths if dst_layer > front_layer: # expand dst frontier new_dst_paths = {} for node in sorted(dst_paths): path_list = dst_paths[node] for path in path_list: last_node = path[-1] for frontier_node in self.topo.upper_nodes(last_node): new_dst_paths[frontier_node] = [ path + [frontier_node]] if frontier_node in src_paths: src_path_list = src_paths[frontier_node] dst_path_copy = copy( path ) dst_path_copy.reverse() for src_path in src_path_list: complete_paths.append( src_path + dst_path_copy) dst_paths = new_dst_paths if complete_paths: return complete_paths class HashedRouting(Routing): ''' Hashed routing ''' def __init__(self, topo): self.topo = topo def get_route(self, src, dst, hash_): ''' Return flow path. ''' if src == dst: return [src] paths = self.routes(src,dst) if paths: #print 'hash_:', hash_ choice = hash_ % len(paths) #print 'choice:', choice path = sorted(paths)[choice] #print 'path:', path return path ============> Instantiated in util.py: from DCTopo import FatTreeTopo from mininet.util import makeNumeric from DCRouting import HashedRouting, Routing TOPOS = {'ft': FatTreeTopo} ROUTING = {'ECMP' : HashedRouting} def buildTopo(topo): topo_name, topo_param = topo.split( ',' ) return TOPOS[topo_name](makeNumeric(topo_param)) def getRouting(routing, topo): return ROUTING[routing](topo) ============================> utilized in HController. py: A Piece of code which works with self.r.routes() method: Following list of methods are defined in HController.py def _ecmp_hash(self, packet): ''' Return an ECMP-style 5-tuple hash for TCP/IP packets, otherwise 0. RFC2992 ''' hash_input = [0] * 5 if isinstance(packet.next, ipv4): ip = packet.next hash_input[0] = ip.srcip.toUnsigned() hash_input[1] = ip.dstip.toUnsigned() hash_input[2] = ip.protocol if isinstance(ip.next, tcp) or isinstance(ip.next, udp): l4 = ip.next hash_input[3] = l4.srcport hash_input[4] = l4.dstport return crc32(pack('LLHHH', *hash_input)) return 0 def _install_reactive_path(self, event, out_dpid, final_out_port, packet): ''' Install entries on route between two switches. ''' in_name = self.t.node_gen(dpid = event.dpid).name_str() out_name = self.t.node_gen(dpid = out_dpid).name_str() hash_ = self._ecmp_hash(packet) paths = self.r.routes(src_name, dst_name) if paths == None: print "PATH is None :(" return route = self.r.get_route(in_name, out_name, hash_) print "Route:",route print '-'*80 if route == None: print None, "route between", in_name, "and", out_name return match = of.ofp_match.from_packet(packet) for i, node in enumerate(route): node_dpid = self.t.node_gen(name = node).dpid if i < len(route) - 1: next_node = route[i + 1] out_port, next_in_port = self.t.port(node, next_node) else: out_port = final_out_port self.switches[node_dpid].install(out_port, match, idle_timeout = 10) if isinstance(packet.next, of.ipv4) and isinstance(packet.next.next, of.tcp): self.matchDict[(packet.next.srcip, packet.next.dstip, packet.next.next.srcport, packet.next.next.dstport)] = (route, match) def _handle_PacketIn(self, event): if not self.all_switches_up: #log.info("Saw PacketIn before all switches were up - ignoring." ) return packet = event.parsed dpid = event.dpid in_port = event.port # Learn MAC address of the sender on every packet-in. self.macTable[packet.src] = (dpid, in_port) sw_name = self.t.node_gen(dpid = dpid).name_str() #print "Sw:", sw_name, packet.src, packet.dst,"port", in_port, packet.dst.isMulticast(),"macTable", packet.dst in self.macTable #print '-'*80 # Insert flow, deliver packet directly to destination. if packet.dst in self.macTable: out_dpid, out_port = self.macTable[packet.dst] self._install_reactive_path(event, out_dpid, out_port, packet) self.switches[out_dpid].send_packet_data(out_port, event.data) else: self._flood(event) ===================> code snippet which returns 'None' number of paths. def _GlobalFirstFit(self,flow): '''do the Hedera global first fit here''' src_name = self.t.node_gen(dpid = flow['src']).name_str() dst_name = self.t.node_gen(dpid = flow['dst']).name_str() print 'Global Fisrt Fit for the elephant flow from ',src_name,'to', dst_name paths = self.r.routes(src_name,dst_name) print 'all routes found for the big flow:\n',paths GFF_route = None if paths == None: return else: for path in paths: fitCheck = True for i in range(1,len(path)): fitCheck = False if self.bwReservation.has_key(path[i-1]) and self.bwReservation[path[i-1]].has_key(path[i]): if self.bwReservation[path[i-1]][path[i]]['reserveDemand'] + flow['demand'] > 1 : break else: #self.bwReservation[path[i-1]][path[i]]['reserveDemand'] += flow['demand'] fitCheck = True else: self.bwReservation[path[i-1]]={} self.bwReservation[path[i-1]][path[i]]={'reserveDemand':0} fitCheck = True if fitCheck == True: for i in range(1,len(path)): self.bwReservation[path[i-1]][path[i]]['reserveDemand'] += flow['demand'] GFF_route = path print "GFF route found:", path break if GFF_route != None: """install new GFF_path between source and destintaion""" self. _install_GFF_path(GFF_route,flow['match']) def launch(topo = None, routing = None, bw = None ): #print topo if not topo: raise Exception ("Please specify the topology") else: t = buildTopo(topo) r = getRouting(routing, t) if bw == None: bw = 10.0 #Mb/s bw = float(bw/1000) #Gb/s else: bw = float(bw)/1000 core.registerNew(HController, t, r, bw) log.info("** HController is running I am really sorry for any inconvenience caused. I, ve tried to make it a bit clear here. I am not even able to debug the code by setting a python debugging point pdb. I need help from you guys. Thanks a lot again for your time and help. Best Regards, David On Sat, May 9, 2015 at 12:07 PM, Mark Lawrence wrote: > On 09/05/2015 07:41, david jhon wrote: > >> Hello everyone, >> >> I am new to python and trying to run an example code from mininet tests. >> Basically, I am trying to call a method in Hcontroller.py from base class >> Routing defined in DCRouting.py which runs and fetches all the required >> results in install_reactive_path() method, but it returns None when it is >> called from _GlobalFirstFit. I hope someone here could help me fix this >> bug.. >> >> I am attaching all the three files(DCRouting.py, HController.py, util.py) >> to have a look into. Thanks in advance for your time, help or suggestion. >> Thanks a lot! >> >> kind regards, >> David >> >> > I'm sorry but I'm not wading through nearly 30kb of code in five > attachments. Please see http://sscce.org/ for how to put your question > so you're more likely to get answers. > > -- > My fellow Pythonistas, ask not what our language can do for you, ask > what you can do for our language. > > Mark Lawrence > > -- > https://mail.python.org/mailman/listinfo/python-list > --20cf303ea680ae24d50515a1850e Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: base64 PGRpdiBkaXI9Imx0ciI+PGRpdj48ZGl2PjxkaXY+PGRpdj48ZGl2PjxkaXY+PGRpdj48ZGl2Pkhp LCBJIGFtIHNvcnJ5IGZvciBzZW5kaW5nIGluIGZpdmUgYXR0YWNobWVudHMsIEkgY2xvbmVkIHRo ZSBjb2RlIGZyb20gPGEgaHJlZj0iaHR0cHM6Ly9iaXRidWNrZXQub3JnL21zaGFyaWYvaGVkZXJh L3NyYyIgdGFyZ2V0PSJfYmxhbmsiPmhlcmU8L2E+OiBMZXQgbWUgZXhwbGFpbiBpdCBoZXJlOjxi cj48YnI+PC9kaXY+Um91dGluZyBCYXNlIGNsYXNzIGRlZmluZWQgaW4gRENSb3V0aW5nLnB5Ojxi cj48YnI+aW1wb3J0IGxvZ2dpbmc8YnI+ZnJvbSBjb3B5IGltcG9ydCBjb3B5PGJyPjxicj5jbGFz cyBSb3V0aW5nKG9iamVjdCk6PGJyPsKgwqDCoCAmIzM5OyYjMzk7JiMzOTtCYXNlIGNsYXNzIGZv ciBkYXRhIGNlbnRlciBuZXR3b3JrIHJvdXRpbmcuPGJyPjxicj7CoMKgwqAgUm91dGluZyBlbmdp bmVzIG11c3QgaW1wbGVtZW50IHRoZSBnZXRfcm91dGUoKSBtZXRob2QuPGJyPsKgwqDCoCAmIzM5 OyYjMzk7JiMzOTs8YnI+PGJyPsKgwqDCoCBkZWYgX19pbml0X18oc2VsZiwgdG9wbyk6PGJyPsKg wqDCoMKgwqDCoMKgICYjMzk7JiMzOTsmIzM5O0NyZWF0ZSBSb3V0aW5nIG9iamVjdC48YnI+PGJy PsKgwqDCoMKgwqDCoMKgIEBwYXJhbSB0b3BvIFRvcG8gb2JqZWN0IGZyb20gTmV0IHBhcmVudDxi cj7CoMKgwqDCoMKgwqDCoCAmIzM5OyYjMzk7JiMzOTs8YnI+wqDCoMKgwqDCoMKgwqAgc2VsZi50 b3BvID0gdG9wbzxicj48YnI+wqDCoMKgIGRlZiBnZXRfcm91dGUoc2VsZiwgc3JjLCBkc3QsIGhh c2hfKTo8YnI+wqDCoMKgwqDCoMKgwqAgJiMzOTsmIzM5OyYjMzk7UmV0dXJuIGZsb3cgcGF0aC48 YnI+PGJyPsKgwqDCoMKgwqDCoMKgIEBwYXJhbSBzcmMgc291cmNlIGhvc3Q8YnI+wqDCoMKgwqDC oMKgwqAgQHBhcmFtIGRzdCBkZXN0aW5hdGlvbiBob3N0PGJyPsKgwqDCoMKgwqDCoMKgIEBwYXJh bSBoYXNoXyBoYXNoIHZhbHVlPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBAcmV0dXJuIGZsb3dfcGF0 aCBsaXN0IG9mIERQSURzIHRvIHRyYXZlcnNlIChpbmNsdWRpbmcgaG9zdHMpPGJyPsKgwqDCoMKg wqDCoMKgICYjMzk7JiMzOTsmIzM5Ozxicj7CoMKgwqDCoMKgwqDCoCByYWlzZSBOb3RJbXBsZW1l bnRlZEVycm9yPGJyPjxicj7CoMKgwqAgZGVmIHJvdXRlcyhzZWxmLCBzcmMsIGRzdCk6PGJyPsKg wqDCoMKgwqDCoMKgICYjMzk7JiMzOTsmIzM5OyBSZXR1cm4gbGlzdCBvZiBwYXRoczxicj7CoMKg wqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqAgT25seSB3b3JrcyBmb3IgRmF0LVRyZWUgdG9w b2xvZ3k8YnI+PGJyPsKgwqDCoMKgwqDCoMKgIEAgcGFyYW0gc3JjIHNvdXJjZSBob3N0PGJyPsKg wqDCoMKgwqDCoMKgIEAgcGFyYW0gZHN0IGRlc3RpbmF0aW9uIGhvc3Q8YnI+PGJyPsKgwqDCoMKg wqDCoMKgIEAgcmV0dXJuIGxpc3Qgb2YgRFBJRHMgKGluY2x1ZGluZyBpbnB1dHMpIDxicj7CoMKg wqDCoMKgwqDCoCAmIzM5OyYjMzk7JiMzOTvCoCA8YnI+wqDCoMKgwqDCoMKgwqAgPGJyPsKgwqDC oMKgwqDCoMKgIGNvbXBsZXRlX3BhdGhzID0gW10gIyBMaXN0IG9mIGNvbXBsZXRlIGRwaWQgcm91 dGVzPGJyPsKgwqDCoMKgwqDCoMKgIDxicj7CoMKgwqDCoMKgwqDCoCBzcmNfcGF0aHMgPSB7IHNy YyA6IFtbc3JjXV0gfTxicj7CoMKgwqDCoMKgwqDCoCBkc3RfcGF0aHMgPSB7IGRzdCA6IFtbZHN0 XV0gfSA8YnI+wqDCoMKgIDxicj7CoMKgwqDCoMKgwqDCoCBkc3RfbGF5ZXIgPSBzZWxmLnRvcG8u bGF5ZXIoZHN0KTxicj7CoMKgwqDCoMKgwqDCoCBzcmNfbGF5ZXIgPSBzZWxmLnRvcG8ubGF5ZXIo c3JjKTxicj7CoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqAgbG93ZXJfbGF5ZXIgPSBz cmNfbGF5ZXI8YnI+wqDCoMKgwqDCoMKgwqAgaWYgZHN0X2xheWVyICZndDsgc3JjX2xheWVyOjxi cj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGxvd2VyX2xheWVyID0gZHN0X2xheWVyPGJyPsKgwqDC oMKgwqDCoMKgIDxicj48YnI+wqDCoMKgwqDCoMKgwqAgZm9yIGZyb250X2xheWVyIGluIHJhbmdl KGxvd2VyX2xheWVyLTEsIC0xLCAtMSk6PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgc3Jj X2xheWVyICZndDsgZnJvbnRfbGF5ZXI6PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgIyBleHBh bmQgc3JjIGZyb250aWVyPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBuZXdfc3Jj X3BhdGhzID0ge308YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGZvciBub2RlIGlu IHNvcnRlZChzcmNfcGF0aHMpOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCBwYXRoX2xpc3QgPSBzcmNfcGF0aHNbbm9kZV08YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgZm9yIHBhdGggaW4gcGF0aF9saXN0Ojxicj7CoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGxhc3Rfbm9kZSA9IHBhdGhbLTFdPGJy PsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZm9yIGZyb250 aWVyX25vZGUgaW4gc2VsZi50b3BvLnVwcGVyX25vZGVzKGxhc3Rfbm9kZSk6PGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBuZXdfc3JjX3Bh dGhzW2Zyb250aWVyX25vZGVdID0gW3BhdGggKyBbZnJvbnRpZXJfbm9kZV1dPGJyPjxicj7CoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgZnJv bnRpZXJfbm9kZSBpbiBkc3RfcGF0aHM6PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRzdF9wYXRoX2xpc3QgPSBkc3RfcGF0 aHNbZnJvbnRpZXJfbm9kZV08YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZm9yIGRzdF9wYXRoIGluIGRzdF9wYXRoX2xpc3Q6 PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgZHN0X3BhdGhfY29weSA9IGNvcHkgKCBkc3RfcGF0aCApPGJyPsKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgZHN0X3BhdGhfY29weS5yZXZlcnNlKCk8YnI+wqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjb21w bGV0ZV9wYXRocy5hcHBlbmQoIHBhdGggKyBkc3RfcGF0aF9jb3B5KTxicj7CoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgc3JjX3BhdGhzID0gbmV3X3NyY19wYXRoczxicj7CoMKgwqDCoMKg wqDCoMKgwqDCoMKgIDxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlmIGRzdF9sYXllciAmZ3Q7 IGZyb250X2xheWVyOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgICMgZXhwYW5kIGRzdCBmcm9u dGllcjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbmV3X2RzdF9wYXRocyA9IHt9 PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBmb3Igbm9kZSBpbiBzb3J0ZWQoZHN0 X3BhdGhzKTrCoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgcGF0aF9saXN0ID0gZHN0X3BhdGhzW25vZGVdPGJyPsKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIGZvciBwYXRoIGluIHBhdGhfbGlzdDo8YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBsYXN0X25vZGUgPSBwYXRoWy0x XTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGZvciBm cm9udGllcl9ub2RlIGluIHNlbGYudG9wby51cHBlcl9ub2RlcyhsYXN0X25vZGUpOjxicj7CoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbmV3X2Rz dF9wYXRoc1tmcm9udGllcl9ub2RlXSA9IFsgcGF0aCArIFtmcm9udGllcl9ub2RlXV08YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlmIGZyb250aWVy X25vZGUgaW4gc3JjX3BhdGhzOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzcmNfcGF0aF9saXN0ID0gc3JjX3BhdGhzW2Zy b250aWVyX25vZGVdPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRzdF9wYXRoX2NvcHkgPSBjb3B5KCBwYXRoICk8YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqAgZHN0X3BhdGhfY29weS5yZXZlcnNlKCk8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZm9yIHNyY19wYXRoIGluIHNyY19w YXRoX2xpc3Q6PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY29tcGxldGVfcGF0aHMuYXBwZW5kKCBzcmNfcGF0 aCArIGRzdF9wYXRoX2NvcHkpPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgPGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkc3RfcGF0aHMgPSBuZXdfZHN0X3BhdGhzPGJyPjxicj7C oMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlmIGNvbXBsZXRlX3BhdGhzOjxicj7CoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJuIGNvbXBsZXRlX3BhdGhzPGJyPmNsYXNzIEhhc2hlZFJv dXRpbmcoUm91dGluZyk6PGJyPsKgwqDCoCAmIzM5OyYjMzk7JiMzOTsgSGFzaGVkIHJvdXRpbmcg JiMzOTsmIzM5OyYjMzk7PGJyPjxicj7CoMKgwqAgZGVmIF9faW5pdF9fKHNlbGYsIHRvcG8pOjxi cj7CoMKgwqDCoMKgwqDCoCBzZWxmLnRvcG8gPSB0b3BvPGJyPjxicj7CoMKgwqAgZGVmIGdldF9y b3V0ZShzZWxmLCBzcmMsIGRzdCwgaGFzaF8pOjxicj7CoMKgwqDCoMKgwqDCoCAmIzM5OyYjMzk7 JiMzOTsgUmV0dXJuIGZsb3cgcGF0aC4gJiMzOTsmIzM5OyYjMzk7PGJyPsKgwqDCoMKgwqDCoMKg IDxicj7CoMKgwqDCoMKgwqDCoCBpZiBzcmMgPT0gZHN0Ojxicj7CoMKgwqDCoMKgwqDCoMKgwqDC oMKgIHJldHVybiBbc3JjXTxicj7CoMKgwqAgPGJyPsKgwqDCoMKgwqDCoMKgIHBhdGhzID0gc2Vs Zi5yb3V0ZXMoc3JjLGRzdCk8YnI+wqDCoMKgwqDCoMKgwqAgaWYgcGF0aHM6PGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgI3ByaW50ICYjMzk7aGFzaF86JiMzOTssIGhhc2hfPGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgY2hvaWNlID0gaGFzaF8gJSBsZW4ocGF0aHMpPGJyPsKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgI3ByaW50ICYjMzk7Y2hvaWNlOiYjMzk7LCBjaG9pY2U8YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBwYXRoID0gc29ydGVkKHBhdGhzKVtjaG9pY2VdPGJyPsKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgI3ByaW50ICYjMzk7cGF0aDomIzM5OywgcGF0aDxicj7CoMKgwqDCoMKgwqDC oMKgwqDCoMKgIHJldHVybiBwYXRoPGJyPjxicj49PT09PT09PT09PT0mZ3Q7PGJyPjwvZGl2Pklu c3RhbnRpYXRlZCBpbiB1dGlsLnB5Ojxicj48YnI+ZnJvbSBEQ1RvcG8gaW1wb3J0IEZhdFRyZWVU b3BvPGJyPmZyb20gbWluaW5ldC51dGlsIGltcG9ydCBtYWtlTnVtZXJpYzxicj5mcm9tIERDUm91 dGluZyBpbXBvcnQgSGFzaGVkUm91dGluZywgUm91dGluZzxicj48YnI+VE9QT1MgPSB7JiMzOTtm dCYjMzk7OiBGYXRUcmVlVG9wb308YnI+Uk9VVElORyA9IHsmIzM5O0VDTVAmIzM5OyA6IEhhc2hl ZFJvdXRpbmd9PGJyPjxicj48YnI+ZGVmIGJ1aWxkVG9wbyh0b3BvKTo8YnI+wqDCoMKgIHRvcG9f bmFtZSwgdG9wb19wYXJhbSA9IHRvcG8uc3BsaXQoICYjMzk7LCYjMzk7ICk8YnI+wqDCoMKgIHJl dHVybiBUT1BPU1t0b3BvX25hbWVdKG1ha2VOdW1lcmljKHRvcG9fcGFyYW0pKTxicj48YnI+PGJy PmRlZiBnZXRSb3V0aW5nKHJvdXRpbmcsIHRvcG8pOjxicj7CoMKgwqAgcmV0dXJuIFJPVVRJTkdb cm91dGluZ10odG9wbyk8YnI+PGJyPjwvZGl2Pj09PT09PT09PT09PT09PT09PT09PT09PT09PT0m Z3Q7IHV0aWxpemVkIGluIEhDb250cm9sbGVyLiBweTogPGJyPjwvZGl2PkEgUGllY2Ugb2YgY29k ZSB3aGljaCB3b3JrcyB3aXRoIHNlbGYuci5yb3V0ZXMoKSBtZXRob2Q6PGJyPkZvbGxvd2luZyBs aXN0IG9mIG1ldGhvZHMgYXJlIGRlZmluZWQgaW4gSENvbnRyb2xsZXIucHkgPGJyPjxicj7CoMKg wqAgZGVmIF9lY21wX2hhc2goc2VsZiwgcGFja2V0KTo8YnI+wqDCoMKgwqDCoMKgwqAgJiMzOTsm IzM5OyYjMzk7IFJldHVybiBhbiBFQ01QLXN0eWxlIDUtdHVwbGUgaGFzaCBmb3IgVENQL0lQIHBh Y2tldHMsIG90aGVyd2lzZSAwLjxicj7CoMKgwqDCoMKgwqDCoCBSRkMyOTkyICYjMzk7JiMzOTsm IzM5Ozxicj7CoMKgwqDCoMKgwqDCoCBoYXNoX2lucHV0ID0gWzBdICogNTxicj7CoMKgwqDCoMKg wqDCoCBpZiBpc2luc3RhbmNlKHBhY2tldC5uZXh0LCBpcHY0KTo8YnI+wqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBpcCA9IHBhY2tldC5uZXh0PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaGFzaF9p bnB1dFswXSA9IGlwLnNyY2lwLnRvVW5zaWduZWQoKTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKg IGhhc2hfaW5wdXRbMV0gPSBpcC5kc3RpcC50b1Vuc2lnbmVkKCk8YnI+wqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBoYXNoX2lucHV0WzJdID0gaXAucHJvdG9jb2w8YnI+wqDCoMKgwqDCoMKgwqDCoMKg wqDCoCBpZiBpc2luc3RhbmNlKGlwLm5leHQsIHRjcCkgb3IgaXNpbnN0YW5jZShpcC5uZXh0LCB1 ZHApOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbDQgPSBpcC5uZXh0PGJyPsKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBoYXNoX2lucHV0WzNdID0gbDQuc3JjcG9ydDxi cj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaGFzaF9pbnB1dFs0XSA9IGw0LmRzdHBv cnQ8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiBjcmMzMihwYWNrKCYj Mzk7TExISEgmIzM5OywgKmhhc2hfaW5wdXQpKTxicj7CoMKgwqDCoMKgwqDCoCByZXR1cm4gMDxi cj48YnI+wqDCoMKgIGRlZiBfaW5zdGFsbF9yZWFjdGl2ZV9wYXRoKHNlbGYsIGV2ZW50LCBvdXRf ZHBpZCwgZmluYWxfb3V0X3BvcnQsIHBhY2tldCk6PGJyPsKgwqDCoMKgwqDCoMKgICYjMzk7JiMz OTsmIzM5OyBJbnN0YWxsIGVudHJpZXMgb24gcm91dGUgYmV0d2VlbiB0d28gc3dpdGNoZXMuICYj Mzk7JiMzOTsmIzM5Ozxicj7CoMKgwqDCoMKgwqDCoCBpbl9uYW1lID0gc2VsZi50Lm5vZGVfZ2Vu KGRwaWQgPSBldmVudC5kcGlkKS5uYW1lX3N0cigpPGJyPsKgwqDCoMKgwqDCoMKgIG91dF9uYW1l ID0gc2VsZi50Lm5vZGVfZ2VuKGRwaWQgPSBvdXRfZHBpZCkubmFtZV9zdHIoKTxicj7CoMKgwqDC oMKgwqDCoCBoYXNoXyA9IHNlbGYuX2VjbXBfaGFzaChwYWNrZXQpPGJyPsKgwqDCoMKgwqDCoMKg IHBhdGhzID0gc2VsZi5yLnJvdXRlcyhzcmNfbmFtZSwgZHN0X25hbWUpPGJyPsKgwqDCoMKgwqDC oMKgIGlmIHBhdGhzID09IE5vbmU6PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBwcmludCAm cXVvdDtQQVRIIGlzIE5vbmUgOigmcXVvdDs8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJl dHVybjxicj7CoMKgwqDCoMKgwqDCoCByb3V0ZSA9IHNlbGYuci5nZXRfcm91dGUoaW5fbmFtZSwg b3V0X25hbWUsIGhhc2hfKTxicj7CoMKgwqDCoMKgwqDCoCBwcmludCAmcXVvdDtSb3V0ZTomcXVv dDsscm91dGXCoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqAgcHJpbnQgJiMzOTstJiMz OTsqODA8YnI+wqDCoMKgwqDCoMKgwqAgaWYgcm91dGUgPT0gTm9uZTo8YnI+wqDCoMKgwqDCoMKg wqDCoMKgwqDCoCBwcmludCBOb25lLCAmcXVvdDtyb3V0ZSBiZXR3ZWVuJnF1b3Q7LCBpbl9uYW1l LCAmcXVvdDthbmQmcXVvdDssIG91dF9uYW1lPGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0 dXJuPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBtYXRjaCA9IG9mLm9mcF9tYXRjaC5mcm9tX3BhY2tl dChwYWNrZXQpPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBmb3IgaSwgbm9kZSBpbiBlbnVtZXJhdGUo cm91dGUpOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIG5vZGVfZHBpZCA9IHNlbGYudC5ub2Rl X2dlbihuYW1lID0gbm9kZSkuZHBpZDxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlmIGkgJmx0 OyBsZW4ocm91dGUpIC0gMTo8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIG5leHRf bm9kZSA9IHJvdXRlW2kgKyAxXTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgb3V0 X3BvcnQsIG5leHRfaW5fcG9ydCA9IHNlbGYudC5wb3J0KG5vZGUsIG5leHRfbm9kZSk8YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoCBlbHNlOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqAgb3V0X3BvcnQgPSBmaW5hbF9vdXRfcG9ydDxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNl bGYuc3dpdGNoZXNbbm9kZV9kcGlkXS5pbnN0YWxsKG91dF9wb3J0LCBtYXRjaCwgaWRsZV90aW1l b3V0ID0gMTApPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBpZiBpc2luc3RhbmNlKHBhY2tldC5uZXh0 LCBvZi5pcHY0KSBhbmQgaXNpbnN0YW5jZShwYWNrZXQubmV4dC5uZXh0LCBvZi50Y3ApOjxicj7C oMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNlbGYubWF0Y2hEaWN0WyhwYWNrZXQubmV4dC5zcmNpcCwg cGFja2V0Lm5leHQuZHN0aXAsIHBhY2tldC5uZXh0Lm5leHQuc3JjcG9ydCwgcGFja2V0Lm5leHQu bmV4dC5kc3Rwb3J0KV0gPSAocm91dGUsIG1hdGNoKTxicj7CoMKgwqDCoCA8YnI+wqDCoMKgIGRl ZiBfaGFuZGxlX1BhY2tldEluKHNlbGYsIGV2ZW50KTo8YnI+wqDCoMKgwqDCoMKgwqAgaWYgbm90 IHNlbGYuYWxsX3N3aXRjaGVzX3VwOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgICM8YSBocmVm PSJodHRwOi8vbG9nLmluZm8iIHRhcmdldD0iX2JsYW5rIj5sb2cuaW5mbzwvYT4oJnF1b3Q7U2F3 IFBhY2tldEluIGJlZm9yZSBhbGwgc3dpdGNoZXMgd2VyZSB1cCAtIGlnbm9yaW5nLiZxdW90OyAp PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJuPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBw YWNrZXQgPSBldmVudC5wYXJzZWQ8YnI+wqDCoMKgwqDCoMKgwqAgZHBpZCA9IGV2ZW50LmRwaWQ8 YnI+wqDCoMKgwqDCoMKgwqAgaW5fcG9ydCA9IGV2ZW50LnBvcnQ8YnI+PGJyPsKgwqDCoMKgwqDC oMKgICMgTGVhcm4gTUFDIGFkZHJlc3Mgb2YgdGhlIHNlbmRlciBvbiBldmVyeSBwYWNrZXQtaW4u PGJyPsKgwqDCoMKgwqDCoMKgIHNlbGYubWFjVGFibGVbcGFja2V0LnNyY10gPSAoZHBpZCwgaW5f cG9ydCk8YnI+wqDCoMKgwqDCoMKgwqAgc3dfbmFtZSA9IHNlbGYudC5ub2RlX2dlbihkcGlkID0g ZHBpZCkubmFtZV9zdHIoKTxicj7CoMKgwqDCoMKgwqDCoCAjcHJpbnQgJnF1b3Q7U3c6JnF1b3Q7 LCBzd19uYW1lLCBwYWNrZXQuc3JjLCBwYWNrZXQuZHN0LCZxdW90O3BvcnQmcXVvdDssIGluX3Bv cnQsIHBhY2tldC5kc3QuaXNNdWx0aWNhc3QoKSwmcXVvdDttYWNUYWJsZSZxdW90OywgcGFja2V0 LmRzdCBpbiBzZWxmLm1hY1RhYmxlPGJyPsKgwqDCoMKgwqDCoMKgICNwcmludCAmIzM5Oy0mIzM5 Oyo4MDxicj7CoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqAgIyBJbnNlcnQgZmxvdywg ZGVsaXZlciBwYWNrZXQgZGlyZWN0bHkgdG8gZGVzdGluYXRpb24uPGJyPjxicj7CoMKgwqDCoMKg wqDCoCBpZiBwYWNrZXQuZHN0IGluIHNlbGYubWFjVGFibGU6PGJyPsKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgb3V0X2RwaWQsIG91dF9wb3J0ID0gc2VsZi5tYWNUYWJsZVtwYWNrZXQuZHN0XTxicj7C oMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNlbGYuX2luc3RhbGxfcmVhY3RpdmVfcGF0aChldmVudCwg b3V0X2RwaWQsIG91dF9wb3J0LCBwYWNrZXQpPGJyPjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKg IHNlbGYuc3dpdGNoZXNbb3V0X2RwaWRdLnNlbmRfcGFja2V0X2RhdGEob3V0X3BvcnQsIGV2ZW50 LmRhdGEpPGJyPjxicj7CoMKgwqDCoMKgwqDCoCBlbHNlOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDC oMKgIHNlbGYuX2Zsb29kKGV2ZW50KTxicj48YnI+PC9kaXY+PT09PT09PT09PT09PT09PT09PSZn dDsgY29kZSBzbmlwcGV0IHdoaWNoIHJldHVybnMgJiMzOTtOb25lJiMzOTsgbnVtYmVyIG9mIHBh dGhzLiA8YnI+wqDCoMKgIGRlZiBfR2xvYmFsRmlyc3RGaXQoc2VsZixmbG93KTo8YnI+wqDCoMKg wqDCoMKgwqAgJiMzOTsmIzM5OyYjMzk7ZG8gdGhlIEhlZGVyYSBnbG9iYWwgZmlyc3QgZml0IGhl cmUmIzM5OyYjMzk7JiMzOTs8YnI+wqDCoMKgwqDCoMKgwqAgc3JjX25hbWUgPSBzZWxmLnQubm9k ZV9nZW4oZHBpZCA9IGZsb3dbJiMzOTtzcmMmIzM5O10pLm5hbWVfc3RyKCk8YnI+wqDCoMKgwqDC oMKgwqAgZHN0X25hbWUgPSBzZWxmLnQubm9kZV9nZW4oZHBpZCA9IGZsb3dbJiMzOTtkc3QmIzM5 O10pLm5hbWVfc3RyKCk8YnI+wqDCoMKgwqDCoMKgwqAgcHJpbnQgJiMzOTtHbG9iYWwgRmlzcnQg Rml0IGZvciB0aGUgZWxlcGhhbnQgZmxvdyBmcm9tICYjMzk7LHNyY19uYW1lLCYjMzk7dG8mIzM5 OywgZHN0X25hbWU8YnI+wqDCoMKgwqDCoMKgwqAgcGF0aHMgPSBzZWxmLnIucm91dGVzKHNyY19u YW1lLGRzdF9uYW1lKTxicj7CoMKgwqDCoMKgwqDCoCBwcmludCAmIzM5O2FsbCByb3V0ZXMgZm91 bmQgZm9yIHRoZSBiaWcgZmxvdzpcbiYjMzk7LHBhdGhzPGJyPsKgwqDCoMKgwqDCoMKgIEdGRl9y b3V0ZSA9IE5vbmU8YnI+wqDCoMKgwqDCoMKgwqAgaWYgcGF0aHMgPT0gTm9uZTo8YnI+wqDCoMKg wqDCoMKgwqDCoMKgwqAgcmV0dXJuPGJyPsKgwqDCoMKgwqDCoMKgIGVsc2U6wqDCoCA8YnI+wqDC oMKgwqDCoMKgwqDCoMKgIGZvciBwYXRoIGluIHBhdGhzOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGZpdENoZWNrID0gVHJ1ZTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoCA8YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBmb3IgaSBpbiByYW5nZSgxLGxlbihwYXRoKSk6PGJyPsKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoCBmaXRDaGVjayA9IEZhbHNlIDxicj7CoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgaWYgc2VsZi5id1Jlc2VydmF0aW9uLmhhc19rZXkocGF0aFtpLTFdKSBh bmQgc2VsZi5id1Jlc2VydmF0aW9uW3BhdGhbaS0xXV0uaGFzX2tleShwYXRoW2ldKTo8YnI+wqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgc2VsZi5id1Jlc2VydmF0aW9u W3BhdGhbaS0xXV1bcGF0aFtpXV1bJiMzOTtyZXNlcnZlRGVtYW5kJiMzOTtdICsgZmxvd1smIzM5 O2RlbWFuZCYjMzk7XSAmZ3Q7IDEgOjxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIGJyZWFrwqDCoCA8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgZWxzZTo8YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoCAjc2VsZi5id1Jlc2VydmF0aW9uW3BhdGhbaS0xXV1bcGF0aFtpXV1bJiMzOTty ZXNlcnZlRGVtYW5kJiMzOTtdICs9IGZsb3dbJiMzOTtkZW1hbmQmIzM5O108YnI+wqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBmaXRDaGVjayA9IFRydWXCoCA8 YnI+wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGVsc2U6PGJyPsKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNlbGYuYndSZXNlcnZhdGlvbltwYXRoW2ktMV1dPXt9 PGJyPsKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNlbGYuYndSZXNlcnZh dGlvbltwYXRoW2ktMV1dW3BhdGhbaV1dPXsmIzM5O3Jlc2VydmVEZW1hbmQmIzM5OzowfTxicj7C oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBmaXRDaGVjayA9IFRydWU8YnI+ wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpZiBmaXRDaGVjayA9PSBUcnVlOjxicj7CoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqAgZm9yIGkgaW4gcmFuZ2UoMSxsZW4ocGF0aCkpOjxicj7CoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzZWxmLmJ3UmVzZXJ2YXRpb25bcGF0 aFtpLTFdXVtwYXRoW2ldXVsmIzM5O3Jlc2VydmVEZW1hbmQmIzM5O10gKz0gZmxvd1smIzM5O2Rl bWFuZCYjMzk7XTxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgR0ZGX3JvdXRlID0g cGF0aDxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcHJpbnQgJnF1b3Q7R0ZGIHJv dXRlIGZvdW5kOiZxdW90OywgcGF0aDxicj7CoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAg YnJlYWs8YnI+wqDCoMKgwqDCoMKgwqAgaWYgR0ZGX3JvdXRlICE9IE5vbmU6PGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgJnF1b3Q7JnF1b3Q7JnF1b3Q7aW5zdGFsbCBuZXcgR0ZGX3BhdGggYmV0 d2VlbiBzb3VyY2UgYW5kIGRlc3RpbnRhaW9uJnF1b3Q7JnF1b3Q7JnF1b3Q7PGJyPsKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgc2VsZi4gX2luc3RhbGxfR0ZGX3BhdGgoR0ZGX3JvdXRlLGZsb3dbJiMz OTttYXRjaCYjMzk7XSk8YnI+PGJyPmRlZiBsYXVuY2godG9wbyA9IE5vbmUsIHJvdXRpbmcgPSBO b25lLCBidyA9IE5vbmUgKTo8YnI+wqDCoMKgICNwcmludCB0b3BvPGJyPsKgwqDCoCBpZiBub3Qg dG9wbzo8YnI+wqDCoMKgwqDCoMKgwqAgcmFpc2UgRXhjZXB0aW9uICgmcXVvdDtQbGVhc2Ugc3Bl Y2lmeSB0aGUgdG9wb2xvZ3kmcXVvdDspPGJyPsKgwqDCoCBlbHNlOiA8YnI+wqDCoMKgwqDCoMKg wqAgdCA9IGJ1aWxkVG9wbyh0b3BvKTxicj48YnI+wqDCoMKgIHIgPSBnZXRSb3V0aW5nKHJvdXRp bmcsIHQpPGJyPsKgwqDCoCBpZiBidyA9PSBOb25lOjxicj7CoMKgwqDCoMKgwqDCoCBidyA9IDEw LjAgI01iL3M8YnI+wqDCoMKgwqDCoMKgwqAgYncgPSBmbG9hdChidy8xMDAwKSAjR2Ivczxicj7C oMKgwqAgZWxzZTo8YnI+wqDCoMKgwqDCoMKgwqAgYncgPSBmbG9hdChidykvMTAwMDxicj7CoMKg wqAgY29yZS5yZWdpc3Rlck5ldyhIQ29udHJvbGxlciwgdCwgciwgYncpPGJyPsKgwqDCoCA8YSBo cmVmPSJodHRwOi8vbG9nLmluZm8iPmxvZy5pbmZvPC9hPigmcXVvdDsqKiBIQ29udHJvbGxlciBp cyBydW5uaW5nPGJyPjxicj48L2Rpdj5JIGFtIHJlYWxseSBzb3JyeSBmb3IgYW55IGluY29udmVu aWVuY2UgY2F1c2VkLiBJLCB2ZSB0cmllZCB0byBtYWtlIGl0IGEgYml0IGNsZWFyIGhlcmUuIEkg YW0gbm90IGV2ZW4gYWJsZSB0byBkZWJ1ZyB0aGUgY29kZSBieSBzZXR0aW5nIGEgcHl0aG9uIGRl YnVnZ2luZyBwb2ludCBwZGIuIEkgbmVlZCBoZWxwIGZyb20geW91IGd1eXMuIFRoYW5rcyBhIGxv dCBhZ2FpbiBmb3IgeW91ciB0aW1lIGFuZCBoZWxwLiA8YnI+PGJyPjwvZGl2PkJlc3QgUmVnYXJk cyw8YnI+PC9kaXY+RGF2aWQ8YnI+PGRpdj48ZGl2PjxkaXY+PGRpdj48ZGl2Pjxicj48L2Rpdj48 L2Rpdj48L2Rpdj48L2Rpdj48L2Rpdj48L2Rpdj48ZGl2IGNsYXNzPSJnbWFpbF9leHRyYSI+PGJy PjxkaXYgY2xhc3M9ImdtYWlsX3F1b3RlIj5PbiBTYXQsIE1heSA5LCAyMDE1IGF0IDEyOjA3IFBN LCBNYXJrIExhd3JlbmNlIDxzcGFuIGRpcj0ibHRyIj4mbHQ7PGEgaHJlZj0ibWFpbHRvOmJyZWFt b3JlYm95QHlhaG9vLmNvLnVrIiB0YXJnZXQ9Il9ibGFuayI+YnJlYW1vcmVib3lAeWFob28uY28u dWs8L2E+Jmd0Ozwvc3Bhbj4gd3JvdGU6PGJyPjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90 ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3Bh ZGRpbmctbGVmdDoxZXgiPjxkaXYgY2xhc3M9IkhPRW5aYiI+PGRpdiBjbGFzcz0iaDUiPk9uIDA5 LzA1LzIwMTUgMDc6NDEsIGRhdmlkIGpob24gd3JvdGU6PGJyPg0KPGJsb2NrcXVvdGUgY2xhc3M9 ImdtYWlsX3F1b3RlIiBzdHlsZT0ibWFyZ2luOjAgMCAwIC44ZXg7Ym9yZGVyLWxlZnQ6MXB4ICNj Y2Mgc29saWQ7cGFkZGluZy1sZWZ0OjFleCI+DQpIZWxsbyBldmVyeW9uZSw8YnI+DQo8YnI+DQpJ IGFtIG5ldyB0byBweXRob24gYW5kIHRyeWluZyB0byBydW4gYW4gZXhhbXBsZSBjb2RlIGZyb20g bWluaW5ldCB0ZXN0cy48YnI+DQpCYXNpY2FsbHksIEkgYW0gdHJ5aW5nIHRvIGNhbGwgYSBtZXRo b2QgaW4gSGNvbnRyb2xsZXIucHkgZnJvbSBiYXNlIGNsYXNzPGJyPg0KUm91dGluZyBkZWZpbmVk IGluIERDUm91dGluZy5weSB3aGljaCBydW5zIGFuZCBmZXRjaGVzIGFsbCB0aGUgcmVxdWlyZWQ8 YnI+DQpyZXN1bHRzIGluIGluc3RhbGxfcmVhY3RpdmVfcGF0aCgpIG1ldGhvZCwgYnV0IGl0IHJl dHVybnMgTm9uZSB3aGVuIGl0IGlzPGJyPg0KY2FsbGVkIGZyb20gX0dsb2JhbEZpcnN0Rml0LiBJ IGhvcGUgc29tZW9uZSBoZXJlIGNvdWxkIGhlbHAgbWUgZml4IHRoaXM8YnI+DQpidWcuLjxicj4N Cjxicj4NCkkgYW0gYXR0YWNoaW5nIGFsbCB0aGUgdGhyZWUgZmlsZXMoRENSb3V0aW5nLnB5LCBI Q29udHJvbGxlci5weSwgdXRpbC5weSk8YnI+DQp0byBoYXZlIGEgbG9vayBpbnRvLiBUaGFua3Mg aW4gYWR2YW5jZSBmb3IgeW91ciB0aW1lLCBoZWxwIG9yIHN1Z2dlc3Rpb24uPGJyPg0KVGhhbmtz IGEgbG90ITxicj4NCjxicj4NCmtpbmQgcmVnYXJkcyw8YnI+DQpEYXZpZDxicj4NCjxicj4NCjwv YmxvY2txdW90ZT4NCjxicj48L2Rpdj48L2Rpdj4NCkkmIzM5O20gc29ycnkgYnV0IEkmIzM5O20g bm90IHdhZGluZyB0aHJvdWdoIG5lYXJseSAzMGtiIG9mIGNvZGUgaW4gZml2ZSBhdHRhY2htZW50 cy7CoCBQbGVhc2Ugc2VlIDxhIGhyZWY9Imh0dHA6Ly9zc2NjZS5vcmcvIiB0YXJnZXQ9Il9ibGFu ayI+aHR0cDovL3NzY2NlLm9yZy88L2E+IGZvciBob3cgdG8gcHV0IHlvdXIgcXVlc3Rpb24gc28g eW91JiMzOTtyZSBtb3JlIGxpa2VseSB0byBnZXQgYW5zd2Vycy48c3BhbiBjbGFzcz0iSE9Fblpi Ij48Zm9udCBjb2xvcj0iIzg4ODg4OCI+PGJyPg0KPGJyPg0KLS0gPGJyPg0KTXkgZmVsbG93IFB5 dGhvbmlzdGFzLCBhc2sgbm90IHdoYXQgb3VyIGxhbmd1YWdlIGNhbiBkbyBmb3IgeW91LCBhc2s8 YnI+DQp3aGF0IHlvdSBjYW4gZG8gZm9yIG91ciBsYW5ndWFnZS48YnI+DQo8YnI+DQpNYXJrIExh d3JlbmNlPGJyPg0KPGJyPg0KLS0gPGJyPg0KPGEgaHJlZj0iaHR0cHM6Ly9tYWlsLnB5dGhvbi5v cmcvbWFpbG1hbi9saXN0aW5mby9weXRob24tbGlzdCIgdGFyZ2V0PSJfYmxhbmsiPmh0dHBzOi8v bWFpbC5weXRob24ub3JnL21haWxtYW4vbGlzdGluZm8vcHl0aG9uLWxpc3Q8L2E+PGJyPg0KPC9m b250Pjwvc3Bhbj48L2Jsb2NrcXVvdGU+PC9kaXY+PGJyPjwvZGl2Pg0K --20cf303ea680ae24d50515a1850e--