dicompyler-0.4.1-1/0000755000076500000240000000000011700133245014707 5ustar apanchalstaff00000000000000dicompyler-0.4.1-1/dicompyler/0000755000076500000240000000000011700133245017056 5ustar apanchalstaff00000000000000dicompyler-0.4.1-1/dicompyler/__init__.py0000644000076500000240000000070411677672407021215 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # __init__.py """Package initialization for dicompyler.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ from main import start, __version__ if __name__ == '__main__': import dicompyler.main dicompyler.main.start()dicompyler-0.4.1-1/dicompyler/baseplugins/0000755000076500000240000000000011700133245021372 5ustar apanchalstaff00000000000000dicompyler-0.4.1-1/dicompyler/baseplugins/2dview.py0000644000076500000240000006246111677672407023202 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # 2dview.py """dicompyler plugin that displays images, structures and dose in 2D planes.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # import wx from wx.xrc import XmlResource, XRCCTRL, XRCID from wx.lib.pubsub import Publisher as pub from matplotlib import _cntr as cntr from matplotlib import __version__ as mplversion import numpy as np from dicompyler import guiutil, util def pluginProperties(): """Properties of the plugin.""" props = {} props['name'] = '2D View' props['description'] = "Display image, structure and dose data in 2D" props['author'] = 'Aditya Panchal' props['version'] = 0.4 props['plugin_type'] = 'main' props['plugin_version'] = 1 props['min_dicom'] = ['images'] props['recommended_dicom'] = ['images', 'rtss', 'rtdose'] return props def pluginLoader(parent): """Function to load the plugin.""" # Load the XRC file for our gui resources res = XmlResource(util.GetBasePluginsPath('2dview.xrc')) panel2DView = res.LoadPanel(parent, 'plugin2DView') panel2DView.Init(res) return panel2DView class plugin2DView(wx.Panel): """Plugin to display DICOM image, RT Structure, RT Dose in 2D.""" def __init__(self): pre = wx.PrePanel() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, res): """Method called after the panel has been initialized.""" # Bind ui events to the proper methods self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) # Initialize variables self.images = [] self.structures = {} self.window = 0 self.level = 0 self.zoom = 1 self.pan = [0, 0] self.bwidth = 0 self.bheight = 0 self.mousepos = (-10000, -10000) self.mouse_in_window = False self.isodose_line_style = 'Solid' self.isodose_fill_opacity = 25 self.structure_line_style = 'Solid' self.structure_fill_opacity = 50 # Setup toolbar controls if guiutil.IsGtk(): import gtk zoominbmp = wx.ArtProvider_GetBitmap(gtk.STOCK_ZOOM_IN, wx.ART_OTHER, (24, 24)) zoomoutbmp = wx.ArtProvider_GetBitmap(gtk.STOCK_ZOOM_OUT, wx.ART_OTHER, (24, 24)) drawingstyles = ['Solid', 'Transparent', 'Dot'] else: zoominbmp = wx.Bitmap(util.GetResourcePath('magnifier_zoom_in.png')) zoomoutbmp = wx.Bitmap(util.GetResourcePath('magnifier_zoom_out.png')) drawingstyles = ['Solid', 'Transparent', 'Dot', 'Dash', 'Dot Dash'] self.tools = [] self.tools.append({'label':"Zoom In", 'bmp':zoominbmp, 'shortHelp':"Zoom In", 'eventhandler':self.OnZoomIn}) self.tools.append({'label':"Zoom Out", 'bmp':zoomoutbmp, 'shortHelp':"Zoom Out", 'eventhandler':self.OnZoomOut}) # Set up preferences self.preferences = [ {'Drawing Settings': [{'name':'Isodose Line Style', 'type':'choice', 'values':drawingstyles, 'default':'Solid', 'callback':'2dview.drawingprefs.isodose_line_style'}, {'name':'Isodose Fill Opacity', 'type':'range', 'values':[0, 100], 'default':25, 'units':'%', 'callback':'2dview.drawingprefs.isodose_fill_opacity'}, {'name':'Structure Line Style', 'type':'choice', 'values':drawingstyles, 'default':'Solid', 'callback':'2dview.drawingprefs.structure_line_style'}, {'name':'Structure Fill Opacity', 'type':'range', 'values':[0, 100], 'default':50, 'units':'%', 'callback':'2dview.drawingprefs.structure_fill_opacity'}] }] # Set up pubsub pub.subscribe(self.OnUpdatePatient, 'patient.updated.parsed_data') pub.subscribe(self.OnStructureCheck, 'structures.checked') pub.subscribe(self.OnIsodoseCheck, 'isodoses.checked') pub.subscribe(self.OnDrawingPrefsChange, '2dview.drawingprefs') pub.sendMessage('preferences.requested.values', '2dview.drawingprefs') def OnUpdatePatient(self, msg): """Update and load the patient data.""" self.z = 0 self.structurepixlut = ([], []) self.dosepixlut = ([], []) if msg.data.has_key('images'): self.images = msg.data['images'] # Set the first image to the middle of the series self.imagenum = len(self.images)/2 image = self.images[self.imagenum-1] self.structurepixlut = image.GetPatientToPixelLUT() # Determine the default window and level of the series self.window, self.level = image.GetDefaultImageWindowLevel() # Dose display depends on whether we have images loaded or not self.isodoses = {} if msg.data.has_key('dose'): self.dose = msg.data['dose'] self.dosedata = self.dose.GetDoseData() # First get the dose grid LUT doselut = self.dose.GetPatientToPixelLUT() # Then convert dose grid LUT into an image pixel LUT self.dosepixlut = self.GetDoseGridPixelData(self.structurepixlut, doselut) else: self.dose = [] if msg.data.has_key('plan'): self.rxdose = msg.data['plan']['rxdose'] else: self.rxdose = 0 else: self.images = [] self.SetBackgroundColour(wx.Colour(0, 0, 0)) # Set the focus to this panel so we can capture key events self.SetFocus() self.Refresh() def OnFocus(self): """Bind to certain events when the plugin is focused.""" # Bind keyboard and mouse events self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) self.Bind(wx.EVT_MOTION, self.OnMouseMotion) pub.subscribe(self.OnKeyDown, 'main.key_down') pub.subscribe(self.OnMouseWheel, 'main.mousewheel') def OnUnfocus(self): """Unbind to certain events when the plugin is unfocused.""" # Unbind keyboard and mouse events self.Unbind(wx.EVT_KEY_DOWN) self.Unbind(wx.EVT_MOUSEWHEEL) self.Unbind(wx.EVT_LEFT_DOWN) self.Unbind(wx.EVT_LEFT_UP) self.Unbind(wx.EVT_RIGHT_DOWN) self.Unbind(wx.EVT_RIGHT_UP) self.Unbind(wx.EVT_MOTION) pub.unsubscribe(self.OnKeyDown) pub.unsubscribe(self.OnMouseWheel) def OnDestroy(self, evt): """Unbind to all events before the plugin is destroyed.""" pub.unsubscribe(self.OnUpdatePatient) pub.unsubscribe(self.OnStructureCheck) pub.unsubscribe(self.OnIsodoseCheck) pub.unsubscribe(self.OnDrawingPrefsChange) self.OnUnfocus() def OnStructureCheck(self, msg): """When the structure list changes, update the panel.""" self.structures = msg.data self.SetFocus() self.Refresh() def OnIsodoseCheck(self, msg): """When the isodose list changes, update the panel.""" self.isodoses = msg.data self.SetFocus() self.Refresh() def OnDrawingPrefsChange(self, msg): """When the drawing preferences change, update the drawing styles.""" if (msg.topic[2] == 'isodose_line_style'): self.isodose_line_style = msg.data elif (msg.topic[2] == 'isodose_fill_opacity'): self.isodose_fill_opacity = msg.data elif (msg.topic[2] == 'structure_line_style'): self.structure_line_style = msg.data elif (msg.topic[2] == 'structure_fill_opacity'): self.structure_fill_opacity = msg.data self.Refresh() def DrawStructure(self, structure, gc, position, prone, feetfirst): """Draw the given structure on the panel.""" # Draw the structure only if the structure has contours # on the current image position if structure['planes'].has_key(position): # Set the color of the contour color = wx.Colour(structure['color'][0], structure['color'][1], structure['color'][2], int(self.structure_fill_opacity*255/100)) # Set fill (brush) color, transparent for external contour if (('RTROIType' in structure) and (structure['RTROIType'].lower() == 'external')): gc.SetBrush(wx.Brush(color, style=wx.TRANSPARENT)) else: gc.SetBrush(wx.Brush(color)) gc.SetPen(wx.Pen(tuple(structure['color']), style=self.GetLineDrawingStyle(self.structure_line_style))) # Create the path for the contour path = gc.CreatePath() for contour in structure['planes'][position]: if (contour['geometricType'] == u"CLOSED_PLANAR"): # Convert the structure data to pixel data pixeldata = self.GetContourPixelData( self.structurepixlut, contour['contourData'], prone, feetfirst) # Move the origin to the last point of the contour point = pixeldata[-1] path.MoveToPoint(point[0], point[1]) # Add each contour point to the path for point in pixeldata: path.AddLineToPoint(point[0], point[1]) # Close the subpath in preparation for the next contour path.CloseSubpath() # Draw the path gc.DrawPath(path) def DrawIsodose(self, isodose, gc, isodosegen): """Draw the given structure on the panel.""" # Calculate the isodose level according to rx dose and dose grid scaling level = isodose['data']['level'] * self.rxdose / (self.dosedata['dosegridscaling'] * 10000) contours = isodosegen.trace(level) # matplotlib 1.0.0 and above returns vertices and segments, but we only need vertices if (mplversion >= "1.0.0"): contours = contours[:len(contours)//2] if len(contours): # Set the color of the isodose line color = wx.Colour(isodose['color'][0], isodose['color'][1], isodose['color'][2], int(self.isodose_fill_opacity*255/100)) gc.SetBrush(wx.Brush(color)) gc.SetPen(wx.Pen(tuple(isodose['color']), style=self.GetLineDrawingStyle(self.isodose_line_style))) # Create the drawing path for the isodose line path = gc.CreatePath() # Draw each contour for the isodose line for c in contours: # Move the origin to the last point of the contour path.MoveToPoint( self.dosepixlut[0][int(c[-1][0])]+1, self.dosepixlut[1][int(c[-1][1])]+1) # Add a line to the rest of the points # Note: draw every other point since there are too many points for p in c[::2]: path.AddLineToPoint( self.dosepixlut[0][int(p[0])]+1, self.dosepixlut[1][int(p[1])]+1) # Close the subpath in preparation for the next contour path.CloseSubpath() # Draw the final isodose path gc.DrawPath(path) def GetLineDrawingStyle(self, style): """Convert the stored line drawing style into wxWidgets pen drawing format.""" styledict = {'Solid':wx.SOLID, 'Transparent':wx.TRANSPARENT, 'Dot':wx.DOT, 'Dash':wx.SHORT_DASH, 'Dot Dash':wx.DOT_DASH} return styledict[style] def GetContourPixelData(self, pixlut, contour, prone = False, feetfirst = False): """Convert structure data into pixel data using the patient to pixel LUT.""" pixeldata = [] # For each point in the structure data # look up the value in the LUT and find the corresponding pixel pair for p, point in enumerate(contour): for xv, xval in enumerate(pixlut[0]): if (xval > point[0] and not prone and not feetfirst): break elif (xval < point[0]): if feetfirst or prone: break for yv, yval in enumerate(pixlut[1]): if (yval > point[1] and not prone): break elif (yval < point[1] and prone): break pixeldata.append((xv, yv)) return pixeldata def GetDoseGridPixelData(self, pixlut, doselut): """Convert dosegrid data into pixel data using the dose to pixel LUT.""" dosedata = [] x = [] y = [] # Determine if the patient is prone or supine imdata = self.images[self.imagenum-1].GetImageData() prone = -1 if 'p' in imdata['patientposition'].lower() else 1 # Get the pixel spacing spacing = imdata['pixelspacing'] # Transpose the dose grid LUT onto the image grid LUT x = (np.array(doselut[0]) - pixlut[0][0]) * prone / spacing[0] y = (np.array(doselut[1]) - pixlut[1][0]) * prone / spacing[1] return (x, y) def OnPaint(self, evt): """Update the panel when it needs to be refreshed.""" # Special case for Windows to account for flickering # if and only if images are loaded if (guiutil.IsMSWindows() and len(self.images)): dc = wx.BufferedPaintDC(self) self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) else: dc = wx.PaintDC(self) width, height = self.GetClientSize() try: gc = wx.GraphicsContext.Create(dc) except NotImplementedError: dc.DrawText("This build of wxPython does not support the " "wx.GraphicsContext family of classes.", 25, 25) return # If we have images loaded, process and show the image if len(self.images): # Save the original drawing state gc.PushState() # Scale the image by the zoom factor gc.Scale(self.zoom, self.zoom) # Redraw the background on Windows if guiutil.IsMSWindows(): gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0))) gc.SetPen(wx.Pen(wx.Colour(0, 0, 0))) gc.DrawRectangle(0, 0, width, height) image = guiutil.convert_pil_to_wx( self.images[self.imagenum-1].GetImage(self.window, self.level)) bmp = wx.BitmapFromImage(image) self.bwidth, self.bheight = image.GetSize() # Center the image gc.Translate(self.pan[0]+(width-self.bwidth*self.zoom)/(2*self.zoom), self.pan[1]+(height-self.bheight*self.zoom)/(2*self.zoom)) gc.DrawBitmap(bmp, 0, 0, self.bwidth, self.bheight) gc.SetBrush(wx.Brush(wx.Colour(0, 0, 255, 30))) gc.SetPen(wx.Pen(wx.Colour(0, 0, 255, 30))) # Draw the structures if present imdata = self.images[self.imagenum-1].GetImageData() self.z = '%.2f' % imdata['position'][2] # Determine whether the patient is prone or supine if 'p' in imdata['patientposition'].lower(): prone = True else: prone = False # Determine whether the patient is feet first or head first if 'ff' in imdata['patientposition'].lower(): feetfirst = True else: feetfirst = False for id, structure in self.structures.iteritems(): self.DrawStructure(structure, gc, self.z, prone, feetfirst) # Draw the isodoses if present if len(self.isodoses): grid = self.dose.GetDoseGrid(float(self.z)) if not (grid == []): x, y = np.meshgrid( np.arange(grid.shape[1]), np.arange(grid.shape[0])) # Instantiate the isodose generator for this slice isodosegen = cntr.Cntr(x, y, grid) for id, isodose in iter(sorted(self.isodoses.iteritems())): self.DrawIsodose(isodose, gc, isodosegen) # Restore the translation and scaling gc.PopState() # Prepare the font for drawing the information text font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) if guiutil.IsMac(): font.SetPointSize(10) gc.SetFont(font, wx.WHITE) # Draw the information text imtext = "Image: " + str(self.imagenum) + "/" + str(len(self.images)) te = gc.GetFullTextExtent(imtext) gc.DrawText(imtext, 10, 7) impos = "Position: " + str(self.z) + " mm" gc.DrawText(impos, 10, 7+te[1]*1.1) if ("%.3f" % self.zoom == "1.000"): zoom = "1" else: zoom = "%.3f" % self.zoom imzoom = "Zoom: " + zoom + ":1" gc.DrawText(imzoom, 10, height-17) imsize = "Image Size: " + str(self.bheight) + "x" + str(self.bwidth) + " px" gc.DrawText(imsize, 10, height-17-te[1]*1.1) imwinlevel = "W/L: " + str(self.window) + ' / ' + str(self.level) te = gc.GetFullTextExtent(imwinlevel) gc.DrawText(imwinlevel, width-te[0]-7, 7) impatpos = "Patient Position: " + imdata['patientposition'] te = gc.GetFullTextExtent(impatpos) gc.DrawText(impatpos, width-te[0]-7, height-17) def OnSize(self, evt): """Refresh the view when the size of the panel changes.""" self.Refresh() evt.Skip() def OnUpdatePositionValues(self, evt=None): """Update the current position and value(s) of the mouse cursor.""" if (evt == None): pos = np.array(self.mousepos) else: pos = np.array(evt.GetPosition()) # On the Mac, the cursor position is shifted by 1 pixel to the left if guiutil.IsMac(): pos = pos - 1 # Determine the coordinates with respect to the current zoom and pan w, h = self.GetClientSize() xpos = int(pos[0]/self.zoom-self.pan[0]-(w-self.bwidth*self.zoom)/ (2*self.zoom)) ypos = int(pos[1]/self.zoom-self.pan[1]-(h-self.bheight*self.zoom)/ (2*self.zoom)) # Set an empty text placeholder if the coordinates are not within range text = "" value = "" # Only display if the mouse coordinates are within the image size range if ((0 <= xpos < len(self.structurepixlut[0])) and (0 <= ypos < len(self.structurepixlut[1])) and self.mouse_in_window): text = "X: " + unicode('%.2f' % self.structurepixlut[0][xpos]) + \ " mm Y: " + unicode('%.2f' % self.structurepixlut[1][ypos]) + \ " mm / X: " + unicode(xpos) + \ " px Y:" + unicode(ypos) + " px" # Lookup the current image and find the value of the current pixel image = self.images[self.imagenum-1] # Rescale the slope and intercept of the image if present if (image.ds.has_key('RescaleIntercept') and image.ds.has_key('RescaleSlope')): pixel_array = image.ds.pixel_array*image.ds.RescaleSlope + \ image.ds.RescaleIntercept else: pixel_array = image.ds.pixel_array value = "Value: " + unicode(pixel_array[ypos, xpos]) # Lookup the current dose plane and find the value of the current # pixel, if the dose has been loaded if not (self.dose == []): xdpos = np.argmin(np.fabs(np.array(self.dosepixlut[0]) - xpos)) ydpos = np.argmin(np.fabs(np.array(self.dosepixlut[1]) - ypos)) dosegrid = self.dose.GetDoseGrid(float(self.z)) if not (dosegrid == []): dose = dosegrid[ydpos, xdpos] * \ self.dosedata['dosegridscaling'] value = value + " / Dose: " + \ unicode('%.4g' % dose) + " Gy / " + \ unicode('%.4g' % float(dose*10000/self.rxdose)) + " %" # Send a message with the text to the 2nd and 3rd statusbar sections pub.sendMessage('main.update_statusbar', {1:text, 2:value}) def OnZoomIn(self, evt): """Zoom the view in.""" self.zoom = self.zoom * 1.1 self.Refresh() def OnZoomOut(self, evt): """Zoom the view out.""" if (self.zoom > 1): self.zoom = self.zoom / 1.1 self.Refresh() def OnKeyDown(self, evt): """Change the image when the user presses the appropriate keys.""" # Needed to work around a bug in Windows. See main.py for more details. if guiutil.IsMSWindows(): try: evt = evt.data except AttributeError: keyname = evt.GetKeyCode() if len(self.images): keyname = evt.GetKeyCode() prevkey = [wx.WXK_UP, wx.WXK_PAGEUP] nextkey = [wx.WXK_DOWN, wx.WXK_PAGEDOWN] zoominkey = [43, 61, 388] # Keys: +, =, Numpad add zoomoutkey = [45, 95, 390] # Keys: -, _, Numpad subtract if (keyname in prevkey): if (self.imagenum > 1): self.imagenum -= 1 self.Refresh() if (keyname in nextkey): if (self.imagenum < len(self.images)): self.imagenum += 1 self.Refresh() if (keyname == wx.WXK_HOME): self.imagenum = 1 self.Refresh() if (keyname == wx.WXK_END): self.imagenum = len(self.images) self.Refresh() if (keyname in zoominkey): self.OnZoomIn(None) if (keyname in zoomoutkey): self.OnZoomOut(None) def OnMouseWheel(self, evt): """Change the image when the user scrolls the mouse wheel.""" # Needed to work around a bug in Windows. See main.py for more details. if guiutil.IsMSWindows(): try: evt = evt.data except AttributeError: delta = evt.GetWheelDelta() rot = evt.GetWheelRotation() if len(self.images): delta = evt.GetWheelDelta() rot = evt.GetWheelRotation() rot = rot/delta if (rot >= 1): if (self.imagenum > 1): self.imagenum -= 1 self.Refresh() if (rot <= -1): if (self.imagenum < len(self.images)): self.imagenum += 1 self.Refresh() def OnMouseDown(self, evt): """Get the initial position of the mouse when dragging.""" self.mousepos = evt.GetPosition() def OnMouseUp(self, evt): """Reset the cursor when the mouse is released.""" self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) def OnMouseEnter(self, evt): """Set a flag when the cursor enters the window.""" self.mouse_in_window = True self.OnUpdatePositionValues(None) def OnMouseLeave(self, evt): """Set a flag when the cursor leaves the window.""" self.mouse_in_window = False self.OnUpdatePositionValues(None) def OnMouseMotion(self, evt): """Process mouse motion events and pass to the appropriate handler.""" if evt.LeftIsDown(): self.OnLeftIsDown(evt) self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) elif evt.RightIsDown(): self.OnRightIsDown(evt) # Custom cursors with > 2 colors only works on Windows currently if guiutil.IsMSWindows(): image = wx.Image(util.GetResourcePath('contrast_high.png')) self.SetCursor(wx.CursorFromImage(image)) # Update the positon and values of the mouse cursor self.mousepos = evt.GetPosition() self.OnUpdatePositionValues(evt) def OnLeftIsDown(self, evt): """Change the image pan when the left mouse button is dragged.""" delta = self.mousepos - evt.GetPosition() self.mousepos = evt.GetPosition() self.pan[0] -= (delta[0]/self.zoom) self.pan[1] -= (delta[1]/self.zoom) self.Refresh() def OnRightIsDown(self, evt): """Change the window/level when the right mouse button is dragged.""" delta = self.mousepos - evt.GetPosition() self.mousepos = evt.GetPosition() self.window -= delta[0] self.level -= delta[1] self.Refresh() dicompyler-0.4.1-1/dicompyler/baseplugins/2dview.xrc0000644000076500000240000000023411677672407023334 0ustar apanchalstaff00000000000000 dicompyler-0.4.1-1/dicompyler/baseplugins/__init__.py0000755000076500000240000000001511677672407023527 0ustar apanchalstaff00000000000000# __init__.pydicompyler-0.4.1-1/dicompyler/baseplugins/anonymize.py0000755000076500000240000003361511677672407024015 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # anonymize.py """dicompyler plugin that anonymizes DICOM / DICOM RT data.""" # Copyright (c) 2010-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # import wx from wx.xrc import XmlResource, XRCCTRL, XRCID from wx.lib.pubsub import Publisher as pub import os, threading from dicompyler import guiutil, util def pluginProperties(): """Properties of the plugin.""" props = {} props['name'] = 'Anonymize' props['menuname'] = "as Anonymized DICOM" props['description'] = "Anonymizes DICOM / DICOM RT data" props['author'] = 'Aditya Panchal' props['version'] = 0.4 props['plugin_type'] = 'export' props['plugin_version'] = 1 props['min_dicom'] = [] props['recommended_dicom'] = ['images', 'rtss', 'rtplan', 'rtdose'] return props class plugin: def __init__(self, parent): self.parent = parent # Set up pubsub pub.subscribe(self.OnUpdatePatient, 'patient.updated.raw_data') # Load the XRC file for our gui resources self.res = XmlResource(util.GetBasePluginsPath('anonymize.xrc')) def OnUpdatePatient(self, msg): """Update and load the patient data.""" self.data = msg.data def pluginMenu(self, evt): """Anonymize DICOM / DICOM RT data.""" dlgAnonymize = self.res.LoadDialog(self.parent, "AnonymizeDialog") dlgAnonymize.Init() if dlgAnonymize.ShowModal() == wx.ID_OK: path = dlgAnonymize.path name = str(dlgAnonymize.name) patientid = str(dlgAnonymize.patientid) privatetags = dlgAnonymize.privatetags # If the path doesn't exist, create it if not os.path.exists(path): os.mkdir(path) # Initialize the progress dialog dlgProgress = guiutil.get_progress_dialog( wx.GetApp().GetTopWindow(), "Anonymizing DICOM data...") # Initialize and start the anonymization thread self.t=threading.Thread(target=self.AnonymizeDataThread, args=(self.data, path, name, patientid, privatetags, dlgProgress.OnUpdateProgress)) self.t.start() # Show the progress dialog dlgProgress.ShowModal() dlgProgress.Destroy() else: pass dlgAnonymize.Destroy() return def AnonymizeDataThread(self, data, path, name, patientid, privatetags, progressFunc): """Anonmyize and save each DICOM / DICOM RT file.""" length = 0 for key in ['rtss', 'rtplan', 'rtdose']: if data.has_key(key): length = length + 1 if data.has_key('images'): length = length + len(data['images']) i = 1 if data.has_key('rtss'): rtss = data['rtss'] wx.CallAfter(progressFunc, i, length, 'Anonymizing file ' + str(i) + ' of ' + str(length)) self.updateCommonElements(rtss, name, patientid, privatetags) self.updateElement(rtss, 'SeriesDescription', 'RT Structure Set') self.updateElement(rtss, 'StructureSetDate', '19010101') self.updateElement(rtss, 'StructureSetTime', '000000') if rtss.has_key('RTROIObservations'): for item in rtss.RTROIObservations: self.updateElement(item, 'ROIInterpreter', 'anonymous') rtss.save_as(os.path.join(path, 'rtss.dcm')) i = i + 1 if data.has_key('rtplan'): rtplan = data['rtplan'] wx.CallAfter(progressFunc, i, length, 'Anonymizing file ' + str(i) + ' of ' + str(length)) self.updateCommonElements(rtplan, name, patientid, privatetags) self.updateElement(rtplan, 'SeriesDescription', 'RT Plan') self.updateElement(rtplan, 'RTPlanName', 'plan') self.updateElement(rtplan, 'RTPlanDate', '19010101') self.updateElement(rtplan, 'RTPlanTime', '000000') if rtplan.has_key('ToleranceTables'): for item in rtplan.ToleranceTables: self.updateElement(item, 'ToleranceTableLabel', 'tolerance') if rtplan.has_key('Beams'): for item in rtplan.Beams: self.updateElement(item, 'Manufacturer', 'manufacturer') self.updateElement(item, 'InstitutionName', 'institution') self.updateElement(item, 'InstitutionAddress', 'address') self.updateElement(item, 'InstitutionalDepartmentName', 'department') self.updateElement(item, 'ManufacturersModelName', 'model') self.updateElement(item, 'TreatmentMachineName', 'txmachine') if rtplan.has_key('TreatmentMachines'): for item in rtplan.TreatmentMachines: self.updateElement(item, 'Manufacturer', 'manufacturer') self.updateElement(item, 'InstitutionName', 'vendor') self.updateElement(item, 'InstitutionAddress', 'address') self.updateElement(item, 'InstitutionalDepartmentName', 'department') self.updateElement(item, 'ManufacturersModelName', 'model') self.updateElement(item, 'DeviceSerialNumber', '0') self.updateElement(item, 'TreatmentMachineName', 'txmachine') if rtplan.has_key('Sources'): for item in rtplan.Sources: self.updateElement(item, 'SourceManufacturer', 'manufacturer') self.updateElement(item, 'SourceIsotopeName', 'isotope') rtplan.save_as(os.path.join(path, 'rtplan.dcm')) i = i + 1 if data.has_key('rtdose'): rtdose = data['rtdose'] wx.CallAfter(progressFunc, i, length, 'Anonymizing file ' + str(i) + ' of ' + str(length)) self.updateCommonElements(rtdose, name, patientid, privatetags) self.updateElement(rtdose, 'SeriesDescription', 'RT Dose') rtdose.save_as(os.path.join(path, 'rtdose.dcm')) i = i + 1 if data.has_key('images'): images = data['images'] for n, image in enumerate(images): wx.CallAfter(progressFunc, i, length, 'Anonymizing file ' + str(i) + ' of ' + str(length)) self.updateCommonElements(image, name, patientid, privatetags) self.updateElement(image, 'SeriesDate', '19010101') self.updateElement(image, 'ContentDate', '19010101') self.updateElement(image, 'SeriesTime', '000000') self.updateElement(image, 'ContentTime', '000000') self.updateElement(image, 'InstitutionName', 'institution') self.updateElement(image, 'InstitutionAddress', 'address') self.updateElement(image, 'InstitutionalDepartmentName', 'department') modality = image.SOPClassUID.name.partition(' Image Storage')[0] image.save_as( os.path.join(path, modality.lower() + '.' + str(n) + '.dcm')) i = i + 1 wx.CallAfter(progressFunc, length-1, length, 'Done') def updateElement(self, data, element, value): """Updates the element only if it exists in the original DICOM data.""" if element in data: data.update({element:value}) def updateCommonElements(self, data, name, patientid, privatetags): """Updates the element only if it exists in the original DICOM data.""" if len(name): self.updateElement(data, 'PatientsName', name) if len(patientid): self.updateElement(data, 'PatientID', patientid) if privatetags: data.remove_private_tags() self.updateElement(data, 'OtherPatientIDs', patientid) self.updateElement(data, 'OtherPatientNames', name) self.updateElement(data, 'InstanceCreationDate', '19010101') self.updateElement(data, 'InstanceCreationTime', '000000') self.updateElement(data, 'StudyDate', '19010101') self.updateElement(data, 'StudyTime', '000000') self.updateElement(data, 'AccessionNumber', '') self.updateElement(data, 'Manufacturer', 'manufacturer') self.updateElement(data, 'ReferringPhysiciansName', 'physician') self.updateElement(data, 'StationName', 'station') self.updateElement(data, 'NameofPhysiciansReadingStudy', 'physician') self.updateElement(data, 'OperatorsName', 'operator') self.updateElement(data, 'PhysiciansofRecord', 'physician') self.updateElement(data, 'ManufacturersModelName', 'model') self.updateElement(data, 'PatientsBirthDate', '') self.updateElement(data, 'PatientsSex', 'O') self.updateElement(data, 'PatientsAge', '000Y') self.updateElement(data, 'PatientsWeight', 0) self.updateElement(data, 'PatientsSize', 0) self.updateElement(data, 'PatientsAddress', 'address') self.updateElement(data, 'AdditionalPatientHistory', '') self.updateElement(data, 'EthnicGroup', 'ethnicity') self.updateElement(data, 'StudyID', '1') self.updateElement(data, 'DeviceSerialNumber', '0') self.updateElement(data, 'SoftwareVersions', '1.0') self.updateElement(data, 'ReviewDate', '19010101') self.updateElement(data, 'ReviewTime', '000000') self.updateElement(data, 'ReviewerName', 'anonymous') class AnonymizeDialog(wx.Dialog): """Dialog that shows the options to anonymize DICOM / DICOM RT data.""" def __init__(self): pre = wx.PreDialog() # the Create step is done by XRC. self.PostCreate(pre) def Init(self): """Method called after the dialog has been initialized.""" # Set window icon if not guiutil.IsMac(): self.SetIcon(guiutil.get_icon()) # Initialize controls self.txtDICOMFolder = XRCCTRL(self, 'txtDICOMFolder') self.checkPatientName = XRCCTRL(self, 'checkPatientName') self.txtFirstName = XRCCTRL(self, 'txtFirstName') self.txtLastName = XRCCTRL(self, 'txtLastName') self.checkPatientID = XRCCTRL(self, 'checkPatientID') self.txtPatientID = XRCCTRL(self, 'txtPatientID') self.checkPrivateTags = XRCCTRL(self, 'checkPrivateTags') self.bmpError = XRCCTRL(self, 'bmpError') self.lblDescription = XRCCTRL(self, 'lblDescription') # Bind interface events to the proper methods wx.EVT_BUTTON(self, XRCID('btnFolderBrowse'), self.OnFolderBrowse) wx.EVT_CHECKBOX(self, XRCID('checkPatientName'), self.OnCheckPatientName) wx.EVT_CHECKBOX(self, XRCID('checkPatientID'), self.OnCheckPatientID) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) # Set and bold the font of the description label if guiutil.IsMac(): font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.FONTWEIGHT_BOLD) self.lblDescription.SetFont(font) # Initialize the import location via pubsub pub.subscribe(self.OnImportPrefsChange, 'general.dicom.import_location') pub.sendMessage('preferences.requested.value', 'general.dicom.import_location') # Pre-select the text on the text controls due to a Mac OS X bug self.txtFirstName.SetSelection(-1, -1) self.txtLastName.SetSelection(-1, -1) self.txtPatientID.SetSelection(-1, -1) # Load the error bitmap self.bmpError.SetBitmap(wx.Bitmap(util.GetResourcePath('error.png'))) # Initialize variables self.name = self.txtLastName.GetValue() + '^' + self.txtFirstName.GetValue() self.patientid = self.txtPatientID.GetValue() self.privatetags = True def OnImportPrefsChange(self, msg): """When the import preferences change, update the values.""" self.path = unicode(msg.data) self.txtDICOMFolder.SetValue(self.path) def OnFolderBrowse(self, evt): """Get the directory selected by the user.""" dlg = wx.DirDialog( self, defaultPath = self.path, message="Choose a folder to save the anonymized DICOM data...") if dlg.ShowModal() == wx.ID_OK: self.path = dlg.GetPath() self.txtDICOMFolder.SetValue(self.path) dlg.Destroy() def OnCheckPatientName(self, evt): """Enable or disable whether the patient's name is anonymized.""" self.txtFirstName.Enable(evt.IsChecked()) self.txtLastName.Enable(evt.IsChecked()) if not evt.IsChecked(): self.txtDICOMFolder.SetFocus() else: self.txtFirstName.SetFocus() self.txtFirstName.SetSelection(-1, -1) def OnCheckPatientID(self, evt): """Enable or disable whether the patient's ID is anonymized.""" self.txtPatientID.Enable(evt.IsChecked()) if not evt.IsChecked(): self.txtDICOMFolder.SetFocus() else: self.txtPatientID.SetFocus() self.txtPatientID.SetSelection(-1, -1) def OnOK(self, evt): """Return the options from the anonymize data dialog.""" # Patient name if self.checkPatientName.IsChecked(): self.name = self.txtLastName.GetValue() if len(self.txtFirstName.GetValue()): self.name = self.name + '^' + self.txtFirstName.GetValue() else: self.name = '' # Patient ID if self.checkPatientID.IsChecked(): self.patientid = self.txtPatientID.GetValue() else: self.patientid = '' # Private tags if self.checkPrivateTags.IsChecked(): self.privatetags = True else: self.privatetags = False self.EndModal(wx.ID_OK)dicompyler-0.4.1-1/dicompyler/baseplugins/anonymize.xrc0000644000076500000240000001646611677672407024163 0ustar apanchalstaff00000000000000 wxVERTICAL wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL 5,0 wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 0,5 1 0,5 20,0 wxALIGN_CENTRE 5,0 wxALL|wxEXPAND|wxALIGN_CENTRE 7,0 wxALIGN_CENTRE 5,0 anonymous wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE 0,5 1 0,5 20,0 wxALIGN_CENTRE 5,0 wxHORIZONTAL 123456 wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE 0,5 1 0,15 16,16 5,0 bold 0 default ISO-8859-1 wxALIGN_LEFT wxHORIZONTAL 0,5 wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 0,5 1 1 wxALL|wxEXPAND|wxALIGN_CENTRE 3 0,5 Anonymize DICOM Data 1 dicompyler-0.4.1-1/dicompyler/baseplugins/dvh.py0000755000076500000240000003247511677672407022570 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # dvh.py """dicompyler plugin that displays a dose volume histogram (DVH) with adjustable constraints via wxPython and matplotlib.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # # It is assumed that the reference (prescription) dose is in cGy. import wx from wx.xrc import XmlResource, XRCCTRL, XRCID from wx.lib.pubsub import Publisher as pub from dicompyler import guiutil, util from dicompyler import dvhdata, guidvh from dicompyler import wxmpl import numpy as np def pluginProperties(): """Properties of the plugin.""" props = {} props['name'] = 'DVH' props['description'] = "Display and evaluate dose volume histogram (DVH) data" props['author'] = 'Aditya Panchal' props['version'] = 0.4 props['plugin_type'] = 'main' props['plugin_version'] = 1 props['min_dicom'] = ['rtss', 'rtdose'] props['recommended_dicom'] = ['rtss', 'rtdose', 'rtplan'] return props def pluginLoader(parent): """Function to load the plugin.""" # Load the XRC file for our gui resources res = XmlResource(util.GetBasePluginsPath('dvh.xrc')) panelDVH = res.LoadPanel(parent, 'pluginDVH') panelDVH.Init(res) return panelDVH class pluginDVH(wx.Panel): """Plugin to display DVH data with adjustable constraints.""" def __init__(self): pre = wx.PrePanel() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, res): """Method called after the panel has been initialized.""" self.guiDVH = guidvh.guiDVH(self) res.AttachUnknownControl('panelDVH', self.guiDVH.panelDVH, self) # Initialize the Constraint selector controls self.lblType = XRCCTRL(self, 'lblType') self.choiceConstraint = XRCCTRL(self, 'choiceConstraint') self.txtConstraint = XRCCTRL(self, 'txtConstraint') self.sliderConstraint = XRCCTRL(self, 'sliderConstraint') self.lblResultType = XRCCTRL(self, 'lblResultType') self.lblConstraintUnits = XRCCTRL(self, 'lblConstraintUnits') self.lblConstraintTypeUnits = XRCCTRL(self, 'lblConstraintTypeUnits') # Initialize the result labels self.lblConstraintType = XRCCTRL(self, 'lblConstraintType') self.lblResultDivider = XRCCTRL(self, 'lblResultDivider') self.lblConstraintPercent = XRCCTRL(self, 'lblConstraintPercent') # Modify the control and font size on Mac controls = [self.lblType, self.choiceConstraint, self.sliderConstraint, self.lblResultType, self.lblConstraintUnits, self.lblConstraintPercent, self.lblConstraintType, self.lblConstraintTypeUnits, self.lblResultDivider] # Add children of composite controls to modification list compositecontrols = [self.txtConstraint] for control in compositecontrols: for child in control.GetChildren(): controls.append(child) # Add the constraint static box to the modification list controls.append(self.lblType.GetContainingSizer().GetStaticBox()) if guiutil.IsMac(): font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetPointSize(10) for control in controls: control.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) control.SetFont(font) # Adjust the control size for the result value labels te = self.lblType.GetTextExtent('0') self.lblConstraintUnits.SetMinSize((te[0]*10, te[1])) self.lblConstraintPercent.SetMinSize((te[0]*6, te[1])) self.Layout() # Bind ui events to the proper methods wx.EVT_CHOICE(self, XRCID('choiceConstraint'), self.OnToggleConstraints) wx.EVT_SPINCTRL(self, XRCID('txtConstraint'), self.OnChangeConstraint) wx.EVT_COMMAND_SCROLL_THUMBTRACK(self, XRCID('sliderConstraint'), self.OnChangeConstraint) wx.EVT_COMMAND_SCROLL_CHANGED(self, XRCID('sliderConstraint'), self.OnChangeConstraint) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) # Initialize variables self.structures = {} # structures from initial DICOM data self.checkedstructures = {} # structures that need to be shown self.dvhs = {} # raw dvhs from initial DICOM data self.dvhdata = {} # dict of dvh constraint functions self.dvharray = {} # dict of dvh data processed from dvhdata self.dvhscaling = {} # dict of dvh scaling data self.plan = {} # used for rx dose self.structureid = 1 # used to indicate current constraint structure # Set up pubsub pub.subscribe(self.OnUpdatePatient, 'patient.updated.parsed_data') pub.subscribe(self.OnStructureCheck, 'structures.checked') pub.subscribe(self.OnStructureSelect, 'structure.selected') def OnUpdatePatient(self, msg): """Update and load the patient data.""" self.structures = msg.data['structures'] self.dvhs = msg.data['dvhs'] self.plan = msg.data['plan'] # show an empty plot when (re)loading a patient self.guiDVH.Replot() self.EnableConstraints(False) def OnDestroy(self, evt): """Unbind to all events before the plugin is destroyed.""" pub.unsubscribe(self.OnUpdatePatient) pub.unsubscribe(self.OnStructureCheck) pub.unsubscribe(self.OnStructureSelect) def OnStructureCheck(self, msg): """When a structure changes, update the interface and plot.""" # Make sure that the volume has been calculated for each structure # before setting it self.checkedstructures = msg.data for id, structure in self.checkedstructures.iteritems(): if not self.structures[id].has_key('volume'): self.structures[id]['volume'] = structure['volume'] # make sure that the dvh has been calculated for each structure # before setting it if self.dvhs.has_key(id): self.EnableConstraints(True) # Create an instance of the dvhdata class to can access its functions self.dvhdata[id] = dvhdata.DVH(self.dvhs[id]) # Create an instance of the dvh arrays so that guidvh can plot it self.dvharray[id] = dvhdata.DVH(self.dvhs[id]).dvh # Create an instance of the dvh scaling data for guidvh self.dvhscaling[id] = self.dvhs[id]['scaling'] # 'Toggle' the choice box to refresh the dose data self.OnToggleConstraints(None) if not len(self.checkedstructures): self.EnableConstraints(False) # Make an empty plot on the DVH self.guiDVH.Replot() def OnStructureSelect(self, msg): """Load the constraints for the currently selected structure.""" if (msg.data['id'] == None): self.EnableConstraints(False) else: self.structureid = msg.data['id'] if self.dvhs.has_key(self.structureid): # Create an instance of the dvhdata class to can access its functions self.dvhdata[self.structureid] = dvhdata.DVH(self.dvhs[self.structureid]) # Create an instance of the dvh scaling data for guidvh self.dvhscaling[self.structureid] = self.dvhs[self.structureid]['scaling'] # 'Toggle' the choice box to refresh the dose data self.OnToggleConstraints(None) else: self.EnableConstraints(False) self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures) def EnableConstraints(self, value): """Enable or disable the constraint selector.""" self.choiceConstraint.Enable(value) self.txtConstraint.Enable(value) self.sliderConstraint.Enable(value) if not value: self.lblConstraintUnits.SetLabel('- ') self.lblConstraintPercent.SetLabel('- ') self.txtConstraint.SetValue(0) self.sliderConstraint.SetValue(0) def OnToggleConstraints(self, evt): """Switch between different constraint modes.""" # Replot the remaining structures and disable the constraints # if a structure that has no DVH calculated is selected if not self.dvhs.has_key(self.structureid): self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures) self.EnableConstraints(False) return else: self.EnableConstraints(True) dvh = self.dvhs[self.structureid] # Check if the function was called via an event or not if not (evt == None): constrainttype = evt.GetInt() else: constrainttype = self.choiceConstraint.GetSelection() constraintrange = 0 # Volume constraint if (constrainttype == 0): self.lblConstraintType.SetLabel(' Dose:') self.lblConstraintTypeUnits.SetLabel('% ') self.lblResultType.SetLabel('Volume:') rxDose = float(self.plan['rxdose']) dvhdata = (len(dvh['data'])-1)*dvh['scaling'] constraintrange = int(dvhdata*100/rxDose) # never go over the max dose as data does not exist if (constraintrange > int(dvh['max'])): constraintrange = int(dvh['max']) # Volume constraint in Gy elif (constrainttype == 1): self.lblConstraintType.SetLabel(' Dose:') self.lblConstraintTypeUnits.SetLabel('Gy ') self.lblResultType.SetLabel('Volume:') constraintrange = self.plan['rxdose']/100 maxdose = int(dvh['max']*self.plan['rxdose']/10000) # never go over the max dose as data does not exist if (constraintrange*100 > maxdose): constraintrange = maxdose # Dose constraint elif (constrainttype == 2): self.lblConstraintType.SetLabel('Volume:') self.lblConstraintTypeUnits.SetLabel(u'% ') self.lblResultType.SetLabel(' Dose:') constraintrange = 100 # Dose constraint in cc elif (constrainttype == 3): self.lblConstraintType.SetLabel('Volume:') self.lblConstraintTypeUnits.SetLabel(u'cm³') self.lblResultType.SetLabel(' Dose:') constraintrange = int(self.structures[self.structureid]['volume']) self.sliderConstraint.SetRange(0, constraintrange) self.sliderConstraint.SetValue(constraintrange) self.txtConstraint.SetRange(0, constraintrange) self.txtConstraint.SetValue(constraintrange) self.OnChangeConstraint(None) def OnChangeConstraint(self, evt): """Update the results when the constraint value changes.""" # Check if the function was called via an event or not if not (evt == None): slidervalue = evt.GetInt() else: slidervalue = self.sliderConstraint.GetValue() self.txtConstraint.SetValue(slidervalue) self.sliderConstraint.SetValue(slidervalue) rxDose = self.plan['rxdose'] id = self.structureid constrainttype = self.choiceConstraint.GetSelection() # Volume constraint if (constrainttype == 0): absDose = rxDose * slidervalue / 100 volume = self.structures[id]['volume'] cc = self.dvhdata[id].GetVolumeConstraintCC(absDose, volume) constraint = self.dvhdata[id].GetVolumeConstraint(absDose) self.lblConstraintUnits.SetLabel("%.1f" % cc + u' cm³') self.lblConstraintPercent.SetLabel("%.1f" % constraint + " %") self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures, ([absDose], [constraint]), id) # Volume constraint in Gy elif (constrainttype == 1): absDose = slidervalue*100 volume = self.structures[id]['volume'] cc = self.dvhdata[id].GetVolumeConstraintCC(absDose, volume) constraint = self.dvhdata[id].GetVolumeConstraint(absDose) self.lblConstraintUnits.SetLabel("%.1f" % cc + u' cm³') self.lblConstraintPercent.SetLabel("%.1f" % constraint + " %") self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures, ([absDose], [constraint]), id) # Dose constraint elif (constrainttype == 2): dose = self.dvhdata[id].GetDoseConstraint(slidervalue) self.lblConstraintUnits.SetLabel("%.1f" % dose + u' cGy') self.lblConstraintPercent.SetLabel("%.1f" % (dose*100/rxDose) + " %") self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures, ([dose], [slidervalue]), id) # Dose constraint in cc elif (constrainttype == 3): volumepercent = slidervalue*100/self.structures[id]['volume'] dose = self.dvhdata[id].GetDoseConstraint(volumepercent) self.lblConstraintUnits.SetLabel("%.1f" % dose + u' cGy') self.lblConstraintPercent.SetLabel("%.1f" % (dose*100/rxDose) + " %") self.guiDVH.Replot([self.dvharray], [self.dvhscaling], self.checkedstructures, ([dose], [volumepercent]), id) dicompyler-0.4.1-1/dicompyler/baseplugins/dvh.xrc0000644000076500000240000001220211677672407022713 0ustar apanchalstaff00000000000000 wxHORIZONTAL wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL 3,0 Volume (V___) Volume (V__Gy) Dose (D__) Dose (D__cc) 0 wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL 5,0 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL 3,0 100 1 wxALIGN_CENTRE_VERTICAL 3,0 100 0 wxALL|wxEXPAND|wxALIGN_CENTRE_VERTICAL 3,0 wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL 5,0 wxALIGN_CENTRE_VERTICAL 3,0 wxALIGN_CENTRE_VERTICAL 3,0 wxALIGN_CENTRE_VERTICAL wxALIGN_CENTRE_VERTICAL 10,0 wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL dicompyler-0.4.1-1/dicompyler/baseplugins/treeview.py0000755000076500000240000001762611677672407023642 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # treeview.py """dicompyler plugin that displays a tree view of the DICOM data structure.""" # Copyright (c) 2010-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # import threading, Queue import wx from wx.xrc import XmlResource, XRCCTRL, XRCID from wx.lib.pubsub import Publisher as pub from wx.gizmos import TreeListCtrl as tlc from dicompyler import guiutil, util import dicom def pluginProperties(): """Properties of the plugin.""" props = {} props['name'] = 'DICOM Tree' props['description'] = "Display a tree view of the DICOM data stucture" props['author'] = 'Aditya Panchal' props['version'] = 0.4 props['plugin_type'] = 'main' props['plugin_version'] = 1 props['min_dicom'] = [] props['recommended_dicom'] = ['rtss', 'rtdose', 'rtss', 'ct'] return props def pluginLoader(parent): """Function to load the plugin.""" # Load the XRC file for our gui resources res = XmlResource(util.GetBasePluginsPath('treeview.xrc')) panelTreeView = res.LoadPanel(parent, 'pluginTreeView') panelTreeView.Init(res) return panelTreeView class pluginTreeView(wx.Panel): """Plugin to display DICOM data in a tree view.""" def __init__(self): pre = wx.PrePanel() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, res): """Method called after the panel has been initialized.""" # Initialize the panel controls self.choiceDICOM = XRCCTRL(self, 'choiceDICOM') self.tlcTreeView = DICOMTree(self) res.AttachUnknownControl('tlcTreeView', self.tlcTreeView, self) # Bind interface events to the proper methods wx.EVT_CHOICE(self, XRCID('choiceDICOM'), self.OnLoadTree) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) # Decrease the font size on Mac font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) if guiutil.IsMac(): font.SetPointSize(10) self.tlcTreeView.SetFont(font) # Set up pubsub pub.subscribe(self.OnUpdatePatient, 'patient.updated.raw_data') def OnUpdatePatient(self, msg): """Update and load the patient data.""" self.choiceDICOM.Enable() self.choiceDICOM.Clear() self.choiceDICOM.Append("Select a DICOM dataset...") self.choiceDICOM.Select(0) self.tlcTreeView.DeleteAllItems() # Iterate through the message and enumerate the DICOM datasets for k, v in msg.data.iteritems(): if isinstance(v, dicom.dataset.FileDataset): i = self.choiceDICOM.Append(v.SOPClassUID.name.split(' Storage')[0]) self.choiceDICOM.SetClientData(i, v) # Add the images to the choicebox if (k == 'images'): for imgnum, image in enumerate(v): i = self.choiceDICOM.Append( image.SOPClassUID.name.split(' Storage')[0] + \ ' Slice ' + str(imgnum + 1)) self.choiceDICOM.SetClientData(i, image) def OnDestroy(self, evt): """Unbind to all events before the plugin is destroyed.""" pub.unsubscribe(self.OnUpdatePatient) def OnLoadTree(self, event): """Update and load the DICOM tree.""" choiceItem = event.GetInt() # Load the dataset chosen from the choice control if not (choiceItem == 0): dataset = self.choiceDICOM.GetClientData(choiceItem) else: return self.tlcTreeView.DeleteAllItems() self.root = self.tlcTreeView.AddRoot(text=dataset.SOPClassUID.name) self.tlcTreeView.Collapse(self.root) # Initialize the progress dialog dlgProgress = guiutil.get_progress_dialog( wx.GetApp().GetTopWindow(), "Loading DICOM data...") # Set up the queue so that the thread knows which item was added self.queue = Queue.Queue() # Initialize and start the recursion thread self.t=threading.Thread(target=self.RecurseTreeThread, args=(dataset, self.root, self.AddItemTree, dlgProgress.OnUpdateProgress, len(dataset))) self.t.start() # Show the progress dialog dlgProgress.ShowModal() dlgProgress.Destroy() self.tlcTreeView.SetFocus() self.tlcTreeView.Expand(self.root) def RecurseTreeThread(self, ds, parent, addItemFunc, progressFunc, length): """Recursively process the DICOM tree.""" for i, data_element in enumerate(ds): # Check and update the progress of the recursion if (length > 0): wx.CallAfter(progressFunc, i, length, 'Processing DICOM data...') if (i == length-1): wx.CallAfter(progressFunc, i, len(ds), 'Done') # Add the data_element to the tree if not a sequence element if not (data_element.VR == 'SQ'): wx.CallAfter(addItemFunc, data_element, parent) # Otherwise add the sequence element to the tree else: wx.CallAfter(addItemFunc, data_element, parent, needQueue=True) item = self.queue.get() # Enumerate for each child element of the sequence for i, ds in enumerate(data_element.value): sq_item_description = data_element.name.replace(" Sequence", "") sq_element_text = "%s %d" % (sq_item_description, i+1) # Add the child of the sequence to the tree wx.CallAfter(addItemFunc, data_element, item, sq_element_text, needQueue=True) sq = self.queue.get() self.RecurseTreeThread(ds, sq, addItemFunc, progressFunc, 0) def AddItemTree(self, data_element, parent, sq_element_text="", needQueue=False): """Add a new item to the DICOM tree.""" # Set the item if it is a child of a sequence element if not (sq_element_text == ""): item = self.tlcTreeView.AppendItem(parent, text=sq_element_text) else: # Account for unicode or string values if isinstance(data_element.value, unicode): item = self.tlcTreeView.AppendItem(parent, text=unicode(data_element.name)) else: item = self.tlcTreeView.AppendItem(parent, text=str(data_element.name)) # Set the value if not a sequence element if not (data_element.VR == 'SQ'): if (data_element.name == 'Pixel Data'): arrayLen = 'Array of ' + str(len(data_element.value)) + ' bytes' self.tlcTreeView.SetItemText(item, arrayLen, 1) elif (data_element.name == 'Private tag data'): self.tlcTreeView.SetItemText(item, 'Private tag data', 1) else: self.tlcTreeView.SetItemText(item, unicode(data_element.value), 1) # Fill in the rest of the data_element properties self.tlcTreeView.SetItemText(item, unicode(data_element.tag), 2) self.tlcTreeView.SetItemText(item, unicode(data_element.VM), 3) self.tlcTreeView.SetItemText(item, unicode(data_element.VR), 4) if (needQueue): self.queue.put(item) class DICOMTree(tlc): """DICOM tree view based on TreeListControl.""" def __init__(self, *args, **kwargs): super(DICOMTree, self).__init__(*args, **kwargs) self.AddColumn('Name') self.AddColumn('Value') self.AddColumn('Tag') self.AddColumn('VM') self.AddColumn('VR') self.SetMainColumn(0) self.SetColumnWidth(0, 200) self.SetColumnWidth(1, 200) self.SetColumnWidth(3, 50) self.SetColumnWidth(4, 50) dicompyler-0.4.1-1/dicompyler/baseplugins/treeview.xrc0000644000076500000240000000361011677672407023767 0ustar apanchalstaff00000000000000 5,5 wxHORIZONTAL - 0 0 wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 1 5,5 wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE|wxADJUST_MINSIZE wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL dicompyler-0.4.1-1/dicompyler/credits.txt0000644000076500000240000000020611677672407021277 0ustar apanchalstaff00000000000000The dicompyler Team Lead Developer Aditya Panchal Developers Roy Keyes Artists Roy Keyes famfamfam Silk dicompyler-0.4.1-1/dicompyler/dicomgui.py0000755000076500000240000011126711677672407021270 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # dicomgui.py """Class that imports and returns DICOM data via a wxPython GUI dialog.""" # Copyright (c) 2009-2011 Aditya Panchal # Copyright (c) 2009 Roy Keyes # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # # It's assumed that the reference (prescription) dose is in cGy. import logging logger = logging.getLogger('dicompyler.dicomgui') import hashlib, os, threading import wx from wx.xrc import * from wx.lib.pubsub import Publisher as pub import numpy as np from dicompyler import dicomparser, dvhdoses, guiutil, util def ImportDicom(parent): """Prepare to show the dialog that will Import DICOM and DICOM RT files.""" # Load the XRC file for our gui resources res = XmlResource(util.GetResourcePath('dicomgui.xrc')) dlgDicomImporter = res.LoadDialog(parent, "DicomImporterDialog") dlgDicomImporter.Init(res) # Show the dialog and return the result if (dlgDicomImporter.ShowModal() == wx.ID_OK): value = dlgDicomImporter.GetPatient() else: value = None # Block until the thread is done before destroying the dialog if dlgDicomImporter: dlgDicomImporter.t.join() dlgDicomImporter.Destroy() return value class DicomImporterDialog(wx.Dialog): """Import DICOM RT files and return a dictionary of data.""" def __init__(self): pre = wx.PreDialog() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, res): """Method called after the panel has been initialized.""" # Set window icon if not guiutil.IsMac(): self.SetIcon(guiutil.get_icon()) # Initialize controls self.txtDicomImport = XRCCTRL(self, 'txtDicomImport') self.btnDicomImport = XRCCTRL(self, 'btnDicomImport') self.checkSearchSubfolders = XRCCTRL(self, 'checkSearchSubfolders') self.lblDirections = XRCCTRL(self, 'lblDirections') self.lblDirections2 = XRCCTRL(self, 'lblDirections2') self.lblProgressLabel = XRCCTRL(self, 'lblProgressLabel') self.lblProgress = XRCCTRL(self, 'lblProgress') self.gaugeProgress = XRCCTRL(self, 'gaugeProgress') self.lblProgressPercent = XRCCTRL(self, 'lblProgressPercent') self.lblProgressPercentSym = XRCCTRL(self, 'lblProgressPercentSym') self.tcPatients = XRCCTRL(self, 'tcPatients') self.bmpRxDose = XRCCTRL(self, 'bmpRxDose') self.lblRxDose = XRCCTRL(self, 'lblRxDose') self.txtRxDose = XRCCTRL(self, 'txtRxDose') self.lblRxDoseUnits = XRCCTRL(self, 'lblRxDoseUnits') self.btnSelect = XRCCTRL(self, 'wxID_OK') # Bind interface events to the proper methods wx.EVT_BUTTON(self, XRCID('btnDicomImport'), self.OnBrowseDicomImport) wx.EVT_CHECKBOX(self, XRCID('checkSearchSubfolders'), self.OnCheckSearchSubfolders) wx.EVT_TREE_SEL_CHANGED(self, XRCID('tcPatients'), self.OnSelectTreeItem) wx.EVT_TREE_ITEM_ACTIVATED(self, XRCID('tcPatients'), self.OnOK) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) # Set the dialog font and bold the font of the directions label font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) if guiutil.IsMac(): self.txtDicomImport.SetFont(font) self.btnDicomImport.SetFont(font) self.checkSearchSubfolders.SetFont(font) self.lblDirections.SetFont(font) self.lblDirections2.SetFont(font) self.lblProgressLabel.SetFont(font) self.lblProgress.SetFont(font) self.lblProgressPercent.SetFont(font) self.lblProgressPercentSym.SetFont(font) self.tcPatients.SetFont(font) self.txtRxDose.SetFont(font) self.lblRxDoseUnits.SetFont(font) font.SetWeight(wx.FONTWEIGHT_BOLD) self.lblDirections2.SetFont(font) self.lblRxDose.SetFont(font) # Initialize the patients tree control self.root = self.InitTree() # Initialize the patients dictionary self.patients = {} # Initialize the import location via pubsub pub.subscribe(self.OnImportPrefsChange, 'general.dicom') pub.sendMessage('preferences.requested.values', 'general.dicom') # Search subfolders by default self.import_search_subfolders = True # Set the threading termination status to false intially self.terminate = False # Hide the progress bar until it needs to be shown self.gaugeProgress.Show(False) self.lblProgressPercent.Show(False) self.lblProgressPercentSym.Show(False) # Start the directory search as soon as the panel loads self.OnDirectorySearch() def OnImportPrefsChange(self, msg): """When the import preferences change, update the values.""" if (msg.topic[2] == 'import_location'): self.path = unicode(msg.data) self.txtDicomImport.SetValue(self.path) elif (msg.topic[2] == 'import_location_setting'): self.import_location_setting = msg.data elif (msg.topic[2] == 'import_search_subfolders'): self.import_search_subfolders = msg.data self.checkSearchSubfolders.SetValue(msg.data) def OnCheckSearchSubfolders(self, evt): """Determine whether to search subfolders for DICOM data.""" self.import_search_subfolders = evt.IsChecked() self.terminate = True self.OnDirectorySearch() def OnBrowseDicomImport(self, evt): """Get the directory selected by the user.""" self.terminate = True dlg = wx.DirDialog( self, defaultPath = self.path, message="Choose a directory containing DICOM RT files...") if dlg.ShowModal() == wx.ID_OK: self.path = dlg.GetPath() self.txtDicomImport.SetValue(self.path) dlg.Destroy() self.OnDirectorySearch() def OnDirectorySearch(self): """Begin directory search.""" self.patients = {} self.tcPatients.DeleteChildren(self.root) self.terminate = False self.gaugeProgress.Show(True) self.lblProgressPercent.Show(True) self.lblProgressPercentSym.Show(True) self.btnSelect.Enable(False) # Disable Rx dose controls except on GTK due to control placement oddities if not guiutil.IsGtk(): self.EnableRxDose(False) self.t=threading.Thread(target=self.DirectorySearchThread, args=(self, self.path, self.import_search_subfolders, self.SetThreadStatus, self.OnUpdateProgress, self.AddPatientTree, self.AddPatientDataTree)) self.t.start() def SetThreadStatus(self): """Tell the directory search thread whether to terminate or not.""" if self.terminate: return True else: return False def DirectorySearchThread(self, parent, path, subfolders, terminate, progressFunc, foundFunc, resultFunc): """Thread to start the directory search.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Searching for patients...') patients = {} # Check if the path is valid if os.path.isdir(path): files = [] for root, dirs, filenames in os.walk(path): files += map(lambda f:os.path.join(root, f), filenames) if (self.import_search_subfolders == False): break for n in range(len(files)): # terminate the thread if the value has changed # during the loop duration if terminate(): wx.CallAfter(progressFunc, 0, 0, 'Search terminated.') return if (os.path.isfile(files[n])): try: logger.debug("Reading: %s", files[n]) dp = dicomparser.DicomParser(filename=files[n]) except (AttributeError, EOFError, IOError, KeyError): pass logger.info("%s is not a valid DICOM file.", files[n]) else: patient = dp.GetDemographics() h = hashlib.sha1(patient['id']).hexdigest() if not patients.has_key(h): patients[h] = {} patients[h]['demographics'] = patient if not patients[h].has_key('studies'): patients[h]['studies'] = {} patients[h]['series'] = {} wx.CallAfter(foundFunc, patient) # Create each Study but don't create one for RT Dose # since some vendors use incorrect StudyInstanceUIDs if not (dp.GetSOPClassUID() == 'rtdose'): stinfo = dp.GetStudyInfo() if not patients[h]['studies'].has_key(stinfo['id']): patients[h]['studies'][stinfo['id']] = stinfo # Create each Series of images if (('ImageOrientationPatient' in dp.ds) and \ not (dp.GetSOPClassUID() == 'rtdose')): seinfo = dp.GetSeriesInfo() seinfo['numimages'] = 0 seinfo['modality'] = dp.ds.SOPClassUID.name if not patients[h]['series'].has_key(seinfo['id']): patients[h]['series'][seinfo['id']] = seinfo if not patients[h].has_key('images'): patients[h]['images'] = {} image = {} image['id'] = dp.GetSOPInstanceUID() image['filename'] = files[n] image['series'] = seinfo['id'] image['referenceframe'] = dp.GetFrameofReferenceUID() patients[h]['series'][seinfo['id']]['numimages'] = \ patients[h]['series'][seinfo['id']]['numimages'] + 1 patients[h]['images'][image['id']] = image # Create each RT Structure Set elif dp.ds.Modality in ['RTSTRUCT']: if not patients[h].has_key('structures'): patients[h]['structures'] = {} structure = dp.GetStructureInfo() structure['id'] = dp.GetSOPInstanceUID() structure['filename'] = files[n] structure['series'] = dp.GetReferencedSeries() structure['referenceframe'] = dp.GetFrameofReferenceUID() patients[h]['structures'][structure['id']] = structure # Create each RT Plan elif dp.ds.Modality in ['RTPLAN']: if not patients[h].has_key('plans'): patients[h]['plans'] = {} plan = dp.GetPlan() plan['id'] = dp.GetSOPInstanceUID() plan['filename'] = files[n] plan['series'] = dp.ds.SeriesInstanceUID plan['referenceframe'] = dp.GetFrameofReferenceUID() plan['rtss'] = dp.GetReferencedStructureSet() patients[h]['plans'][plan['id']] = plan # Create each RT Dose elif dp.ds.Modality in ['RTDOSE']: if not patients[h].has_key('doses'): patients[h]['doses'] = {} dose = {} dose['id'] = dp.GetSOPInstanceUID() dose['filename'] = files[n] dose['referenceframe'] = dp.GetFrameofReferenceUID() dose['hasdvh'] = dp.HasDVHs() dose['rtss'] = dp.GetReferencedStructureSet() dose['rtplan'] = dp.GetReferencedRTPlan() patients[h]['doses'][dose['id']] = dose # Otherwise it is a currently unsupported file else: logger.info("%s is a %s file and is not " + \ "currently supported.", files[n], dp.ds.SOPClassUID.name) # Call the progress function to update the gui wx.CallAfter(progressFunc, n, len(files), 'Searching for patients...') if (len(patients) == 0): progressStr = 'Found 0 patients.' elif (len(patients) == 1): progressStr = 'Found 1 patient. Reading DICOM data...' elif (len(patients) > 1): progressStr = 'Found ' + str(len(patients)) + ' patients. Reading DICOM data...' wx.CallAfter(progressFunc, 0, 1, progressStr) wx.CallAfter(resultFunc, patients) # if the path is not valid, display an error message else: wx.CallAfter(progressFunc, 0, 0, 'Select a valid location.') dlg = wx.MessageDialog( parent, "The DICOM import location does not exist. Please select a valid location.", "Invalid DICOM Import Location", wx.OK|wx.ICON_ERROR) dlg.ShowModal() def OnUpdateProgress(self, num, length, message): """Update the DICOM Import process interface elements.""" if not length: percentDone = 0 else: percentDone = int(100 * (num+1) / length) self.gaugeProgress.SetValue(percentDone) self.lblProgressPercent.SetLabel(str(percentDone)) self.lblProgress.SetLabel(message) if not (percentDone == 100): self.gaugeProgress.Show(True) self.lblProgressPercent.Show(True) self.lblProgressPercentSym.Show(True) else: self.gaugeProgress.Show(False) self.lblProgressPercent.Show(False) self.lblProgressPercentSym.Show(False) # End the dialog since we are done with the import process if (message == 'Importing patient complete.'): self.EndModal(wx.ID_OK) elif (message == 'Importing patient cancelled.'): self.EndModal(wx.ID_CANCEL) def InitTree(self): """Initialize the tree control for use.""" iSize = (16,16) iList = wx.ImageList(iSize[0], iSize[1]) iList.Add( wx.Bitmap( util.GetResourcePath('group.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('user.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('book.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('table_multiple.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('pencil.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('chart_bar.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('chart_curve.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('pencil_error.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('chart_bar_error.png'), wx.BITMAP_TYPE_PNG)) iList.Add( wx.Bitmap( util.GetResourcePath('chart_curve_error.png'), wx.BITMAP_TYPE_PNG)) self.tcPatients.AssignImageList(iList) root = self.tcPatients.AddRoot('Patients', image=0) return root def AddPatientTree(self, patient): """Add a new patient to the tree control.""" # Create a hash for each patient h = hashlib.sha1(patient['id']).hexdigest() # Add the patient to the tree if they don't already exist if not self.patients.has_key(h): self.patients[h] = {} self.patients[h]['demographics'] = patient name = patient['name'] + ' (' + patient['id'] + ')' self.patients[h]['treeid'] = \ self.tcPatients.AppendItem(self.root, name, 1) self.tcPatients.SortChildren(self.root) self.tcPatients.ExpandAll() def AddPatientDataTree(self, patients): """Add the patient data to the tree control.""" # Now add the specific item to the tree for key, patient in self.patients.iteritems(): patient.update(patients[key]) if patient.has_key('studies'): for studyid, study in patient['studies'].iteritems(): name = 'Study: ' + study['description'] study['treeid'] = self.tcPatients.AppendItem(patient['treeid'], name, 2) # Search for series and images if patient.has_key('series'): for seriesid, series in patient['series'].iteritems(): if patient.has_key('studies'): for studyid, study in patient['studies'].iteritems(): if (studyid == series['study']): modality = series['modality'].partition(' Image Storage')[0] name = 'Series: ' + series['description'] + \ ' (' + modality + ', ' if (series['numimages'] == 1): numimages = str(series['numimages']) + ' image)' else: numimages = str(series['numimages']) + ' images)' name = name + numimages series['treeid'] = self.tcPatients.AppendItem(study['treeid'], name, 3) self.EnableItemSelection(patient, series, []) # Search for RT Structure Sets if patient.has_key('structures'): for structureid, structure in patient['structures'].iteritems(): if patient.has_key('series'): foundseries = False name = 'RT Structure Set: ' + structure['label'] for seriesid, series in patient['series'].iteritems(): foundseries = False if (seriesid == structure['series']): structure['treeid'] = self.tcPatients.AppendItem(series['treeid'], name, 4) foundseries = True # If no series were found, add the rtss to the study if not foundseries: structure['treeid'] = self.tcPatients.AppendItem(study['treeid'], name, 4) filearray = [structure['filename']] self.EnableItemSelection(patient, structure, filearray) # Search for RT Plans if patient.has_key('plans'): for planid, plan in patient['plans'].iteritems(): foundstructure = False name = 'RT Plan: ' + plan['label'] + ' (' + plan['name'] + ')' if patient.has_key('structures'): for structureid, structure in patient['structures'].iteritems(): foundstructure = False if (structureid == plan['rtss']): plan['treeid'] = self.tcPatients.AppendItem(structure['treeid'], name, 5) foundstructure = True # If no structures were found, add the plan to the study/series instead if not foundstructure: # If there is an image series, add a fake rtss to it foundseries = False for seriesid, series in patient['series'].iteritems(): foundseries = False if (series['referenceframe'] == plan['referenceframe']): badstructure = self.tcPatients.AppendItem( series['treeid'], "RT Structure Set not found", 7) foundseries = True # If no series were found, add the rtss to the study if not foundseries: badstructure = self.tcPatients.AppendItem( patient['treeid'], "RT Structure Set not found", 7) plan['treeid'] = self.tcPatients.AppendItem(badstructure, name, 5) self.tcPatients.SetItemTextColour(badstructure, wx.RED) filearray = [plan['filename']] self.EnableItemSelection(patient, plan, filearray, plan['rxdose']) # Search for RT Doses if patient.has_key('doses'): for doseid, dose in patient['doses'].iteritems(): foundplan = False if patient.has_key('plans'): for planid, plan in patient['plans'].iteritems(): foundplan = False if (planid == dose['rtplan']): foundplan = True if dose['hasdvh']: name = 'RT Dose with DVH' else: name = 'RT Dose without DVH' dose['treeid'] = self.tcPatients.AppendItem(plan['treeid'], name, 6) filearray = [dose['filename']] self.EnableItemSelection(patient, dose, filearray) # If no plans were found, add the dose to the structure/study instead if not foundplan: if dose['hasdvh']: name = 'RT Dose with DVH' else: name = 'RT Dose without DVH' foundstructure = False if patient.has_key('structures'): for structureid, structure in patient['structures'].iteritems(): foundstructure = False if dose.has_key('rtss'): if (structureid == dose['rtss']): foundstructure = True if (structure['referenceframe'] == dose['referenceframe']): foundstructure = True if foundstructure: badplan = self.tcPatients.AppendItem( structure['treeid'], "RT Plan not found", 8) dose['treeid'] = self.tcPatients.AppendItem(badplan, name, 6) self.tcPatients.SetItemTextColour(badplan, wx.RED) filearray = [dose['filename']] self.EnableItemSelection(patient, dose, filearray) if not foundstructure: # If there is an image series, add a fake rtss to it foundseries = False for seriesid, series in patient['series'].iteritems(): foundseries = False if (series['referenceframe'] == dose['referenceframe']): badstructure = self.tcPatients.AppendItem( series['treeid'], "RT Structure Set not found", 7) foundseries = True # If no series were found, add the rtss to the study if not foundseries: badstructure = self.tcPatients.AppendItem( patient['treeid'], "RT Structure Set not found", 7) self.tcPatients.SetItemTextColour(badstructure, wx.RED) badplan = self.tcPatients.AppendItem( badstructure, "RT Plan not found", 8) dose['treeid'] = self.tcPatients.AppendItem(badplan, name, 5) self.tcPatients.SetItemTextColour(badplan, wx.RED) filearray = [dose['filename']] self.EnableItemSelection(patient, dose, filearray) # No RT Dose files were found else: if patient.has_key('structures'): for structureid, structure in patient['structures'].iteritems(): if patient.has_key('plans'): for planid, plan in patient['plans'].iteritems(): name = 'RT Dose not found' baddose = self.tcPatients.AppendItem(plan['treeid'], name, 9) self.tcPatients.SetItemTextColour(baddose, wx.RED) # No RT Plan nor RT Dose files were found else: name = 'RT Plan not found' badplan = self.tcPatients.AppendItem(structure['treeid'], name, 8) self.tcPatients.SetItemTextColour(badplan, wx.RED) name = 'RT Dose not found' baddose = self.tcPatients.AppendItem(badplan, name, 9) self.tcPatients.SetItemTextColour(baddose, wx.RED) self.btnSelect.SetFocus() self.tcPatients.ExpandAll() self.lblProgress.SetLabel( str(self.lblProgress.GetLabel()).replace(' Reading DICOM data...', '')) def EnableItemSelection(self, patient, item, filearray = [], rxdose = None): """Enable an item to be selected in the tree control.""" # Add the respective images to the filearray if they exist if patient.has_key('images'): for imageid, image in patient['images'].iteritems(): appendImage = False # used for image series if item.has_key('id'): if (item['id'] == image['series']): appendImage = True # used for RT structure set if item.has_key('series'): if (item['series'] == image['series']): appendImage = True # used for RT plan / dose if item.has_key('referenceframe'): if (item['referenceframe'] == image['referenceframe']): if not 'numimages' in item: appendImage = True if appendImage: filearray.append(image['filename']) # Add the respective rtss files to the filearray if they exist if patient.has_key('structures'): for structureid, structure in patient['structures'].iteritems(): if item.has_key('rtss'): if (structureid == item['rtss']): filearray.append(structure['filename']) break elif (structure['referenceframe'] == item['referenceframe']): filearray.append(structure['filename']) break # If no referenced rtss, but ref'd rtplan, check rtplan->rtss if item.has_key('rtplan'): if patient.has_key('plans'): for planid, plan in patient['plans'].iteritems(): if (planid == item['rtplan']): if plan.has_key('rtss'): if (structureid == plan['rtss']): filearray.append(structure['filename']) # Add the respective rtplan files to the filearray if they exist if patient.has_key('plans'): for planid, plan in patient['plans'].iteritems(): if item.has_key('rtplan'): if (planid == item['rtplan']): filearray.append(plan['filename']) if not rxdose: self.tcPatients.SetPyData(item['treeid'], {'filearray':filearray}) else: self.tcPatients.SetPyData(item['treeid'], {'filearray':filearray, 'rxdose':rxdose}) self.tcPatients.SetItemBold(item['treeid'], True) self.tcPatients.SelectItem(item['treeid']) def OnSelectTreeItem(self, evt): """Update the interface when the selected item has changed.""" item = evt.GetItem() # Disable the rx dose message and select button by default self.EnableRxDose(False) self.btnSelect.Enable(False) # If the item has data, check to see whether there is an rxdose if not (self.tcPatients.GetPyData(item) == None): data = self.tcPatients.GetPyData(item) self.btnSelect.Enable() rxdose = 0 if data.has_key('rxdose'): rxdose = data['rxdose'] else: parent = self.tcPatients.GetItemParent(item) parentdata = self.tcPatients.GetPyData(parent) if not (parentdata == None): if parentdata.has_key('rxdose'): rxdose = parentdata['rxdose'] # Show the rxdose text box if no rxdose was found # and if it is an RT plan or RT dose file self.txtRxDose.SetValue(rxdose) if (rxdose == 0): if (self.tcPatients.GetItemText(item).startswith('RT Plan') or self.tcPatients.GetItemText(parent).startswith('RT Plan')): self.EnableRxDose(True) def EnableRxDose(self, value): """Show or hide the prescription dose message.""" self.bmpRxDose.Show(value) self.lblRxDose.Show(value) self.txtRxDose.Show(value) self.lblRxDoseUnits.Show(value) # if set to hide, reset the rx dose if not value: self.txtRxDose.SetValue(1) def GetPatientData(self, path, filearray, RxDose, terminate, progressFunc): """Get the data of the selected patient from the DICOM importer dialog.""" wx.CallAfter(progressFunc, -1, 100, 'Importing patient. Please wait...') for n in range(0, len(filearray)): if terminate(): wx.CallAfter(progressFunc, 98, 100, 'Importing patient cancelled.') return dcmfile = str(os.path.join(self.path, filearray[n])) dp = dicomparser.DicomParser(filename=dcmfile) if (n == 0): self.patient = {} self.patient['rxdose'] = RxDose if (('ImageOrientationPatient' in dp.ds) and \ not (dp.GetSOPClassUID() == 'rtdose')): if not self.patient.has_key('images'): self.patient['images'] = [] self.patient['images'].append(dp.ds) elif (dp.ds.Modality in ['RTSTRUCT']): self.patient['rtss'] = dp.ds elif (dp.ds.Modality in ['RTPLAN']): self.patient['rtplan'] = dp.ds elif (dp.ds.Modality in ['RTDOSE']): self.patient['rtdose'] = dp.ds wx.CallAfter(progressFunc, n, len(filearray), 'Importing patient. Please wait...') # Sort the images based on a sort descriptor: # (ImagePositionPatient, InstanceNumber or AcquisitionNumber) if self.patient.has_key('images'): sortedimages = [] unsortednums = [] sortednums = [] images = self.patient['images'] sort = 'IPP' # Determine if all images in the series are parallel # by testing for differences in ImageOrientationPatient parallel = True for i, item in enumerate(images): if (i > 0): iop0 = np.array(item.ImageOrientationPatient) iop1 = np.array(images[i-1].ImageOrientationPatient) if (np.any(np.array(np.round(iop0 - iop1), dtype=np.int32))): parallel = False break # Also test ImagePositionPatient, as some series # use the same patient position for every slice ipp0 = np.array(item.ImagePositionPatient) ipp1 = np.array(images[i-1].ImagePositionPatient) if not (np.any(np.array(np.round(ipp0 - ipp1), dtype=np.int32))): parallel = False break # If the images are parallel, sort by ImagePositionPatient if parallel: sort = 'IPP' else: # Otherwise sort by Instance Number if not (images[0].InstanceNumber == \ images[1].InstanceNumber): sort = 'InstanceNumber' # Otherwise sort by Acquisition Number elif not (images[0].AcquisitionNumber == \ images[1].AcquisitionNumber): sort = 'AcquisitionNumber' # Add the sort descriptor to a list to be sorted for i, image in enumerate(images): if (sort == 'IPP'): unsortednums.append(image.ImagePositionPatient[2]) else: unsortednums.append(image.data_element(sort).value) # Sort image numbers in descending order for head first patients if ('hf' in image.PatientPosition.lower()) and (sort == 'IPP'): sortednums = sorted(unsortednums, reverse=True) # Otherwise sort image numbers in ascending order else: sortednums = sorted(unsortednums) # Add the images to the array based on the sorted order for s, slice in enumerate(sortednums): for i, image in enumerate(images): if (sort == 'IPP'): if (slice == image.ImagePositionPatient[2]): sortedimages.append(image) elif (slice == image.data_element(sort).value): sortedimages.append(image) # Save the images back to the patient dictionary self.patient['images'] = sortedimages wx.CallAfter(progressFunc, 98, 100, 'Importing patient complete.') def GetPatient(self): """Return the patient data from the DICOM importer dialog.""" return self.patient def OnOK(self, evt): """Return the patient data if the patient is selected or the button is pressed.""" item = self.tcPatients.GetSelection() if self.tcPatients.GetPyData(item): # Since we have decided to use this location to import from, # update the location in the preferences for the next session # if the 'import_location_setting' is "Remember Last Used" if (self.import_location_setting == "Remember Last Used"): pub.sendMessage('preferences.updated.value', {'general.dicom.import_location':self.path}) # Since we have updated the search subfolders setting, # update the setting in preferences pub.sendMessage('preferences.updated.value', {'general.dicom.import_search_subfolders': self.import_search_subfolders}) filearray = self.tcPatients.GetPyData(item)['filearray'] self.btnSelect.Enable(False) self.txtRxDose.Enable(False) self.terminate = False self.importThread=threading.Thread(target=self.GetPatientData, args=(self.path, filearray, self.txtRxDose.GetValue(), self.SetThreadStatus, self.OnUpdateProgress)) self.importThread.start() def OnCancel(self, evt): """Stop the directory search and close the dialog.""" self.terminate = True self.Hide() dicompyler-0.4.1-1/dicompyler/dicomparser.py0000755000076500000240000005626511677672407022006 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # dicomparser.py """Class that parses and returns formatted DICOM RT data.""" # Copyright (c) 2009-2011 Aditya Panchal # Copyright (c) 2009-2010 Roy Keyes # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ import logging logger = logging.getLogger('dicompyler.dicomparser') import numpy as np import dicom import random from PIL import Image from math import pow, sqrt class DicomParser: """Parses DICOM / DICOM RT files.""" def __init__(self, dataset=None, filename=None): if dataset: self.ds = dataset elif filename: try: # Only pydicom 0.9.5 and above supports the force read argument if (dicom.__version__ >= "0.9.5"): self.ds = dicom.read_file(filename, defer_size=100, force=True) else: self.ds = dicom.read_file(filename, defer_size=100) except (EOFError, IOError): # Raise the error for the calling method to handle raise else: # Sometimes DICOM files may not have headers, but they should always # have a SOPClassUID to declare what type of file it is. If the # file doesn't have a SOPClassUID, then it probably isn't DICOM. if not "SOPClassUID" in self.ds: raise AttributeError else: raise AttributeError ######################## SOP Class and Instance Methods ######################## def GetSOPClassUID(self): """Determine the SOP Class UID of the current file.""" if (self.ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.481.2'): return 'rtdose' elif (self.ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.481.3'): return 'rtss' elif (self.ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.481.5'): return 'rtplan' elif (self.ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.2'): return 'ct' else: return None def GetSOPInstanceUID(self): """Determine the SOP Class UID of the current file.""" return self.ds.SOPInstanceUID def GetStudyInfo(self): """Return the study information of the current file.""" study = {} if 'StudyDescription' in self.ds: desc=self.ds.StudyDescription else: desc='No description' study['description'] = desc study['id'] = self.ds.StudyInstanceUID return study def GetSeriesInfo(self): """Return the series information of the current file.""" series = {} if 'SeriesDescription' in self.ds: desc=self.ds.SeriesDescription else: desc='No description' series['description'] = desc series['id'] = self.ds.SeriesInstanceUID series['study'] = self.ds.StudyInstanceUID series['referenceframe'] = self.ds.FrameofReferenceUID return series def GetReferencedSeries(self): """Return the SOP Class UID of the referenced series.""" if "ReferencedFrameofReferences" in self.ds: if "RTReferencedStudies" in self.ds.ReferencedFrameofReferences[0]: if "RTReferencedSeries" in self.ds.ReferencedFrameofReferences[0].RTReferencedStudies[0]: if "SeriesInstanceUID" in self.ds.ReferencedFrameofReferences[0].RTReferencedStudies[0].RTReferencedSeries[0]: return self.ds.ReferencedFrameofReferences[0].RTReferencedStudies[0].RTReferencedSeries[0].SeriesInstanceUID else: return '' def GetFrameofReferenceUID(self): """Determine the Frame of Reference UID of the current file.""" if 'FrameofReferenceUID' in self.ds: return self.ds.FrameofReferenceUID elif 'ReferencedFrameofReferences' in self.ds: return self.ds.ReferencedFrameofReferences[0].FrameofReferenceUID else: return '' def GetReferencedStructureSet(self): """Return the SOP Class UID of the referenced structure set.""" if "ReferencedStructureSets" in self.ds: return self.ds.ReferencedStructureSets[0].ReferencedSOPInstanceUID else: return '' def GetReferencedRTPlan(self): """Return the SOP Class UID of the referenced RT plan.""" if "ReferencedRTPlans" in self.ds: return self.ds.ReferencedRTPlans[0].ReferencedSOPInstanceUID else: return '' def GetDemographics(self): """Return the patient demographics from a DICOM file.""" patient = {} patient['name'] = str(self.ds.PatientsName).replace('^', ', ') patient['id'] = self.ds.PatientID if (self.ds.PatientsSex == 'M'): patient['gender'] = 'Male' elif (self.ds.PatientsSex == 'F'): patient['gender'] = 'Female' else: patient['gender'] = 'Other' if len(self.ds.PatientsBirthDate): patient['dob'] = str(self.ds.PatientsBirthDate) else: patient['dob'] = 'None found' return patient ################################ Image Methods ################################# def GetImageData(self): """Return the image data from a DICOM file.""" data = {} data['position'] = self.ds.ImagePositionPatient data['orientation'] = self.ds.ImageOrientationPatient data['pixelspacing'] = self.ds.PixelSpacing data['rows'] = self.ds.Rows data['columns'] = self.ds.Columns if 'PatientPosition' in self.ds: data['patientposition'] = self.ds.PatientPosition return data def GetImage(self, window = 0, level = 0): """Return the image from a DICOM image storage file.""" if ((window == 0) and (level == 0)): window, level = self.GetDefaultImageWindowLevel() # Rescale the slope and intercept of the image if present if (self.ds.has_key('RescaleIntercept') and self.ds.has_key('RescaleSlope')): rescaled_image = self.ds.pixel_array*self.ds.RescaleSlope + \ self.ds.RescaleIntercept else: rescaled_image = self.ds.pixel_array image = self.GetLUTValue(rescaled_image, window, level) return Image.fromarray(image).convert('L') def GetDefaultImageWindowLevel(self): """Determine the default window/level for the DICOM image.""" window, level = 0, 0 if ('WindowWidth' in self.ds) and ('WindowCenter' in self.ds): if isinstance(self.ds.WindowWidth, float): window = self.ds.WindowWidth elif isinstance(self.ds.WindowWidth, list): if (len(self.ds.WindowWidth) > 1): window = self.ds.WindowWidth[1] if isinstance(self.ds.WindowCenter, float): level = self.ds.WindowCenter elif isinstance(self.ds.WindowCenter, list): if (len(self.ds.WindowCenter) > 1): level = self.ds.WindowCenter[1] else: wmax = 0 wmin = 0 # Rescale the slope and intercept of the image if present if (self.ds.has_key('RescaleIntercept') and self.ds.has_key('RescaleSlope')): pixel_array = self.ds.pixel_array*self.ds.RescaleSlope + \ self.ds.RescaleIntercept else: pixel_array = self.ds.pixel_array if (pixel_array.max() > wmax): wmax = pixel_array.max() if (pixel_array.min() < wmin): wmin = pixel_array.min() # Default window is the range of the data array window = int(abs(wmax) + abs(wmin)) # Default level is the range midpoint minus the window minimum level = int(window / 2 - abs(wmin)) return window, level def GetLUTValue(self, data, window, level): """Apply the RGB Look-Up Table for the given data and window/level value.""" lutvalue = np.piecewise(data, [data <= (level - 0.5 - (window-1)/2), data > (level - 0.5 + (window-1)/2)], [0, 255, lambda data: ((data - (level - 0.5))/(window-1) + 0.5)*(255-0)]) # Convert the resultant array to an unsigned 8-bit array to create # an 8-bit grayscale LUT since the range is only from 0 to 255 return np.array(lutvalue, dtype=np.uint8) def GetPatientToPixelLUT(self): """Get the image transformation matrix from the DICOM standard Part 3 Section C.7.6.2.1.1""" di = self.ds.PixelSpacing[0] dj = self.ds.PixelSpacing[1] orientation = self.ds.ImageOrientationPatient position = self.ds.ImagePositionPatient m = np.matrix( [[orientation[0]*di, orientation[3]*dj, 0, position[0]], [orientation[1]*di, orientation[4]*dj, 0, position[1]], [orientation[2]*di, orientation[5]*dj, 0, position[2]], [0, 0, 0, 1]]) x = [] y = [] for i in range(0, self.ds.Columns): imat = m * np.matrix([[i], [0], [0], [1]]) x.append(float(imat[0])) for j in range(0, self.ds.Rows): jmat = m * np.matrix([[0], [j], [0], [1]]) y.append(float(jmat[1])) return (x, y) ########################### RT Structure Set Methods ########################### def GetStructureInfo(self): """Return the patient demographics from a DICOM file.""" structure = {} structure['label'] = self.ds.StructureSetLabel structure['date'] = self.ds.StructureSetDate structure['time'] = self.ds.StructureSetTime structure['numcontours'] = len(self.ds.ROIContours) return structure def GetStructures(self): """Returns the structures (ROIs) with their coordinates.""" structures = {} # Determine whether this is RT Structure Set file if not (self.GetSOPClassUID() == 'rtss'): return structures # Locate the name and number of each ROI if self.ds.has_key('StructureSetROIs'): for item in self.ds.StructureSetROIs: data = {} number = item.ROINumber data['id'] = number data['name'] = item.ROIName logger.debug("Found ROI #%s: %s", str(number), data['name']) structures[number] = data # Determine the type of each structure (PTV, organ, external, etc) if self.ds.has_key('RTROIObservations'): for item in self.ds.RTROIObservations: number = item.ReferencedROINumber structures[number]['RTROIType'] = item.RTROIInterpretedType # The coordinate data of each ROI is stored within ROIContourSequence if self.ds.has_key('ROIContours'): for roi in self.ds.ROIContours: number = roi.ReferencedROINumber # Get the RGB color triplet for the current ROI if roi.has_key('ROIDisplayColor'): structures[number]['color'] = np.array(roi.ROIDisplayColor, dtype=float) # Otherwise generate a random color for the current ROI else: structures[number]['color'] = np.array(( random.randint(0,255), random.randint(0,255), random.randint(0,255)), dtype=float) planes = {} if roi.has_key('Contours'): # Locate the contour sequence for each referenced ROI for contour in roi.Contours: # For each plane, initialize a new plane dictionary plane = {} # Determine all the plane properties plane['geometricType'] = contour.ContourGeometricType plane['numContourPoints'] = contour.NumberofContourPoints plane['contourData'] = self.GetContourPoints(contour.ContourData) # Each plane which coincides with a image slice will have a unique ID if contour.has_key('ContourImages'): plane['UID'] = contour.ContourImages[0].ReferencedSOPInstanceUID # Add each plane to the planes dictionary of the current ROI if plane.has_key('geometricType'): z = ('%.2f' % plane['contourData'][0][2]).replace('-0','0') if not planes.has_key(z): planes[z] = [] planes[z].append(plane) # Calculate the plane thickness for the current ROI structures[number]['thickness'] = self.CalculatePlaneThickness(planes) # Add the planes dictionary to the current ROI structures[number]['planes'] = planes return structures def GetContourPoints(self, array): """Parses an array of xyz points and returns a array of point dictionaries.""" return zip(*[iter(array)]*3) def CalculatePlaneThickness(self, planesDict): """Calculates the plane thickness for each structure.""" planes = [] # Iterate over each plane in the structure for z in planesDict.iterkeys(): planes.append(float(z)) planes.sort() # Determine the thickness thickness = 10000 for n in range(0, len(planes)): if (n > 0): newThickness = planes[n] - planes[n-1] if (newThickness < thickness): thickness = newThickness # If the thickness was not detected, set it to 0 if (thickness == 10000): thickness = 0 return thickness ############################### RT Dose Methods ############################### def HasDVHs(self): """Returns whether dose-volume histograms (DVHs) exist.""" if not "DVHs" in self.ds: return False else: return True def GetDVHs(self): """Returns the dose-volume histograms (DVHs).""" self.dvhs = {} if self.HasDVHs(): for item in self.ds.DVHs: dvhitem = {} # If the DVH is differential, convert it to a cumulative DVH if (self.ds.DVHs[0].DVHType == 'DIFFERENTIAL'): dvhitem['data'] = self.GenerateCDVH(item.DVHData) dvhitem['bins'] = len(dvhitem['data']) # Otherwise the DVH is cumulative # Remove "filler" values from DVH data array (even values are DVH values) else: dvhitem['data'] = np.array(item.DVHData[1::2]) dvhitem['bins'] = int(item.DVHNumberofBins) dvhitem['type'] = 'CUMULATIVE' dvhitem['doseunits'] = item.DoseUnits dvhitem['volumeunits'] = item.DVHVolumeUnits dvhitem['scaling'] = item.DVHDoseScaling if "DVHMinimumDose" in item: dvhitem['min'] = item.DVHMinimumDose else: # save the min dose as -1 so we can calculate it later dvhitem['min'] = -1 if "DVHMaximumDose" in item: dvhitem['max'] = item.DVHMaximumDose else: # save the max dose as -1 so we can calculate it later dvhitem['max'] = -1 if "DVHMeanDose" in item: dvhitem['mean'] = item.DVHMeanDose else: # save the mean dose as -1 so we can calculate it later dvhitem['mean'] = -1 self.dvhs[item.DVHReferencedROIs[0].ReferencedROINumber] = dvhitem return self.dvhs def GenerateCDVH(self, data): """Generate a cumulative DVH (cDVH) from a differential DVH (dDVH)""" dDVH = np.array(data) # Separate the dose and volume values into distinct arrays dose = data[0::2] volume = data[1::2] # Get the min and max dose and volume values mindose = int(dose[0]*100) maxdose = int(sum(dose)*100) maxvol = sum(volume) # Determine the dose values that are missing from the original data missingdose = np.ones(mindose) * maxvol # Generate the cumulative dose and cumulative volume data k = 0 cumvol = [] cumdose = [] while k < len(dose): cumvol += [sum(volume[k:])] cumdose += [sum(dose[:k])] k += 1 cumvol = np.array(cumvol) cumdose = np.array(cumdose)*100 # Interpolate the dDVH data for 1 cGy bins interpdose = np.arange(mindose, maxdose+1) interpcumvol = np.interp(interpdose, cumdose, cumvol) # Append the interpolated values to the missing dose values cumDVH = np.append(missingdose, interpcumvol) return cumDVH def GetDoseGrid(self, z = 0, threshold = 0.5): """ Return the 2d dose grid for the given slice position (mm). :param z: Slice position in mm. :param threshold: Threshold in mm to determine the max difference from z to the closest dose slice without using interpolation. :return: An numpy 2d array of dose points. """ # If this is a multi-frame dose pixel array, # determine the offset for each frame if 'GridFrameOffsetVector' in self.ds: z = float(z) # Get the initial dose grid position (z) in patient coordinates imagepatpos = self.ds.ImagePositionPatient[2] # Add the position to the offset vector to determine the # z coordinate of each dose plane planes = np.array(self.ds.GridFrameOffsetVector)+imagepatpos frame = -1 # Check to see if the requested plane exists in the array if (np.amin(np.fabs(planes - z)) < threshold): frame = np.argmin(np.fabs(planes - z)) # Return the requested dose plane, since it was found if not (frame == -1): return self.ds.pixel_array[frame] # Check whether the requested plane is within the dose grid boundaries elif ((z < np.amin(planes)) or (z > np.amax(planes))): return [] # The requested plane was not found, so interpolate between planes else: frame = np.argmin(np.fabs(planes - z)) if (planes[frame] - z > 0): ub = frame lb = frame-1 elif (planes[frame] - z < 0): ub = frame+1 lb = frame # Fractional distance of dose plane between upper and lower bound fz = (z - planes[lb]) / (planes[ub] - planes[lb]) plane = self.InterpolateDosePlanes( self.ds.pixel_array[ub], self.ds.pixel_array[lb], fz) return plane else: return [] def InterpolateDosePlanes(self, uplane, lplane, fz): """Interpolates a dose plane between two bounding planes at the given relative location.""" # uplane and lplane are the upper and lower dose plane, between which the new dose plane # will be interpolated. # fz is the fractional distance from the bottom to the top, where the new plane is located. # E.g. if fz = 1, the plane is at the upper plane, fz = 0, it is at the lower plane. # A simple linear interpolation doseplane = fz*uplane + (1.0 - fz)*lplane return doseplane def GetIsodosePoints(self, z = 0, level = 100, threshold = 0.5): """ Return points for the given isodose level and slice position from the dose grid. :param z: Slice position in mm. :param threshold: Threshold in mm to determine the max difference from z to the closest dose slice without using interpolation. :param level: Isodose level in scaled form (multiplied by self.ds.DoseGridScaling) :return: An array of tuples representing isodose points. """ plane = self.GetDoseGrid(z, threshold) isodose = (plane >= level).nonzero() return zip(isodose[1].tolist(), isodose[0].tolist()) def InterpolatePlanes(self, ub, lb, location, ubpoints, lbpoints): """Interpolates a plane between two bounding planes at the given location.""" # If the number of points in the upper bound is higher, use it as the starting bound # otherwise switch the upper and lower bounds if not (len(ubpoints) >= len(lbpoints)): lbCopy = lb.copy() lb = ub.copy() ub = lbCopy.copy() plane = [] # Determine the closest point in the lower bound from each point in the upper bound for u, up in enumerate(ubpoints): dist = 100000 # Arbitrary large number # Determine the distance from each point in the upper bound to each point in the lower bound for l, lp in enumerate(lbpoints): newDist = sqrt(pow((up[0]-lp[0]), 2) + pow((up[1]-lp[1]), 2) + pow((ub-lb), 2)) # If the distance is smaller, then linearly interpolate the point if (newDist < dist): dist = newDist x = lp[0] + (location-lb) * (up[0]-lp[0]) / (ub-lb) y = lp[1] + (location-lb) * (up[1]-lp[1]) / (ub-lb) if not (dist == 100000): plane.append((int(x),int(y))) return plane def GetDoseData(self): """Return the dose data from a DICOM RT Dose file.""" data = self.GetImageData() data['doseunits'] = self.ds.DoseUnits data['dosetype'] = self.ds.DoseType data['dosesummationtype'] = self.ds.DoseSummationType data['dosegridscaling'] = self.ds.DoseGridScaling data['dosemax'] = float(self.ds.pixel_array.max()) return data ############################### RT Plan Methods ############################### def GetPlan(self): """Returns the plan information.""" self.plan = {} self.plan['label'] = self.ds.RTPlanLabel self.plan['date'] = self.ds.RTPlanDate self.plan['time'] = self.ds.RTPlanTime self.plan['name'] = '' self.plan['rxdose'] = 0 if "DoseReferences" in self.ds: for item in self.ds.DoseReferences: if item.DoseReferenceStructureType == 'SITE': self.plan['name'] = item.DoseReferenceDescription if item.has_key('TargetPrescriptionDose'): rxdose = item.TargetPrescriptionDose * 100 if (rxdose > self.plan['rxdose']): self.plan['rxdose'] = rxdose elif item.DoseReferenceStructureType == 'VOLUME': if 'TargetPrescriptionDose' in item: self.plan['rxdose'] = item.TargetPrescriptionDose * 100 return self.plan dicompyler-0.4.1-1/dicompyler/dvhcalc.py0000755000076500000240000002272111677672407021070 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # dvhcalc.py """Calculate dose volume histogram (DVH) from DICOM RT Structure / Dose data.""" # Copyright (c) 2011 Aditya Panchal # Copyright (c) 2010 Roy Keyes # This file is part of dicompyler, released under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ import logging logger = logging.getLogger('dicompyler.dvhcalc') import numpy as np import numpy.ma as ma import matplotlib.nxutils as nx def get_dvh(structure, dose, limit=None, callback=None): """Get a calculated cumulative DVH along with the associated parameters.""" # Get the differential DVH hist = calculate_dvh(structure, dose, limit, callback) # Convert the differential DVH into a cumulative DVH dvh = get_cdvh(hist) dvhdata = {} dvhdata['data'] = dvh dvhdata['bins'] = len(dvh) dvhdata['type'] = 'CUMULATIVE' dvhdata['doseunits'] = 'GY' dvhdata['volumeunits'] = 'CM3' dvhdata['scaling'] = 1 # save the min dose as -1 so we can calculate it later dvhdata['min'] = -1 # save the max dose as -1 so we can calculate it later dvhdata['max'] = -1 # save the mean dose as -1 so we can calculate it later dvhdata['mean'] = -1 return dvhdata def calculate_dvh(structure, dose, limit=None, callback=None): """Calculate the differential DVH for the given structure and dose grid.""" sPlanes = structure['planes'] logger.debug("Calculating DVH of %s %s", structure['id'], structure['name']) # Get the dose to pixel LUT doselut = dose.GetPatientToPixelLUT() # Generate a 2d mesh grid to create a polygon mask in dose coordinates # Code taken from Stack Overflow Answer from Joe Kington: # http://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask/3655582 # Create vertex coordinates for each grid cell x, y = np.meshgrid(np.array(doselut[0]), np.array(doselut[1])) x, y = x.flatten(), y.flatten() dosegridpoints = np.vstack((x,y)).T # Get the dose and image data information dd = dose.GetDoseData() id = dose.GetImageData() # Create an empty array of bins to store the histogram in cGy # only if the structure has contour data if len(sPlanes): maxdose = int(dd['dosemax'] * dd['dosegridscaling'] * 100) # Remove values above the limit (cGy) if specified if not (limit == None): if (limit < maxdose): maxdose = limit hist = np.zeros(maxdose) else: hist = np.array([0]) volume = 0 plane = 0 # Iterate over each plane in the structure for z, sPlane in sPlanes.iteritems(): # Get the contours with calculated areas and the largest contour index contours, largestIndex = calculate_contour_areas(sPlane) # Get the dose plane for the current structure plane doseplane = dose.GetDoseGrid(z) # If there is no dose for the current plane, go to the next plane if not len(doseplane): break # Calculate the histogram for each contour for i, contour in enumerate(contours): m = get_contour_mask(doselut, dosegridpoints, contour['data']) h, vol = calculate_contour_dvh(m, doseplane, maxdose, dd, id, structure) # If this is the largest contour, just add to the total histogram if (i == largestIndex): hist += h volume += vol # Otherwise, determine whether to add or subtract histogram # depending if the contour is within the largest contour or not else: contour['inside'] = False for point in contour['data']: if nx.pnpoly(point[0], point[1], np.array(contours[largestIndex]['data'])): contour['inside'] = True # Assume if one point is inside, all will be inside break # If the contour is inside, subtract it from the total histogram if contour['inside']: hist -= h volume -= vol # Otherwise it is outside, so add it to the total histogram else: hist += h volume += vol plane += 1 if not (callback == None): callback(plane, len(sPlanes)) # Volume units are given in cm^3 volume = volume/1000 # Rescale the histogram to reflect the total volume hist = hist*volume/sum(hist) # Remove the bins above the max dose for the structure hist = np.trim_zeros(hist, trim='b') return hist def calculate_contour_areas(plane): """Calculate the area of each contour for the given plane. Additionally calculate and return the largest contour index.""" # Calculate the area for each contour in the current plane contours = [] largest = 0 largestIndex = 0 for c, contour in enumerate(plane): # Create arrays for the x,y coordinate pair for the triangulation x = [] y = [] for point in contour['contourData']: x.append(point[0]) y.append(point[1]) cArea = 0 # Calculate the area based on the Surveyor's formula for i in range(0, len(x)-1): cArea = cArea + x[i]*y[i+1] - x[i+1]*y[i] cArea = abs(cArea / 2) # Remove the z coordinate from the xyz point tuple data = map(lambda x: x[0:2], contour['contourData']) # Add the contour area and points to the list of contours contours.append({'area':cArea, 'data':data}) # Determine which contour is the largest if (cArea > largest): largest = cArea largestIndex = c return contours, largestIndex def get_contour_mask(doselut, dosegridpoints, contour): """Get the mask for the contour with respect to the dose plane.""" grid = nx.points_inside_poly(dosegridpoints, contour) grid = grid.reshape((len(doselut[1]), len(doselut[0]))) return grid def calculate_contour_dvh(mask, doseplane, maxdose, dd, id, structure): """Calculate the differential DVH for the given contour and dose plane.""" # Multiply the structure mask by the dose plane to get the dose mask mask = ma.array(doseplane * dd['dosegridscaling'] * 100, mask=~mask) # Calculate the differential dvh hist, edges = np.histogram(mask.compressed(), bins=maxdose, range=(0,maxdose)) # Calculate the volume for the contour for the given dose plane vol = sum(hist) * ((id['pixelspacing'][0]) * (id['pixelspacing'][1]) * (structure['thickness'])) return hist, vol def get_cdvh(ddvh): """Calculate the cumulative DVH from a differential DVH array.""" # cDVH(x) is Sum (Integral) of dDVH with x as lower limit cdvh = [] j = 0 jmax = len(ddvh) while j < jmax: cdvh += [np.sum(ddvh[j:])] j += 1 cdvh = np.array(cdvh) return cdvh ############################# Test DVH Calculation ############################# def main(): import dicomparser import string has_pylab = True try: import pylab as pl except ImportError: has_pylab = False # Read the example RT structure and RT dose files # The testdata was downloaded from the dicompyler website as testdata.zip rtss = dicomparser.DicomParser(filename="testdata/rtss.dcm") rtdose = dicomparser.DicomParser(filename="testdata/rtdose.dcm") # Obtain the structures and DVHs from the DICOM data structures = rtss.GetStructures() dvhs = rtdose.GetDVHs() # Generate the calculated DVHs calcdvhs = {} for key, structure in structures.iteritems(): calcdvhs[key] = get_dvh(structure, rtdose) # Compare the calculated and original DVH volume for each structure print '\nStructure Name\t\t' + 'Original Volume\t\t' + \ 'Calculated Volume\t' + 'Percent Difference' print '--------------\t\t' + '---------------\t\t' + \ '-----------------\t' + '------------------' for key, structure in structures.iteritems(): if (key in calcdvhs) and (len(calcdvhs[key]['data'])): if key in dvhs: ovol = dvhs[key]['data'][0] cvol = calcdvhs[key]['data'][0] print string.ljust(structure['name'], 18) + '\t' + \ string.ljust(str(ovol), 18) + '\t' + \ string.ljust(str(cvol), 18) + '\t' + \ "%.3f" % float((100)*(cvol-ovol)/(ovol)) # Plot the DVHs if pylab is available if has_pylab: for key, structure in structures.iteritems(): if (key in calcdvhs) and (len(calcdvhs[key]['data'])): if key in dvhs: pl.plot(calcdvhs[key]['data']*100/calcdvhs[key]['data'][0], color=np.array(structure['color'], dtype=float)/255, label=structure['name'], linestyle='dashed') pl.plot(dvhs[key]['data']*100/dvhs[key]['data'][0], color=np.array(structure['color'], dtype=float)/255, label='Original '+structure['name']) pl.legend(loc=7, borderaxespad=-5) pl.setp(pl.gca().get_legend().get_texts(), fontsize='x-small') pl.show() if __name__ == '__main__': main() dicompyler-0.4.1-1/dicompyler/dvhdata.py0000755000076500000240000001136411677672407021100 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # dvhdata.py """Class and functions related to dose volume histogram (DVH) data.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # # It's assumed that the reference (prescription) dose is in cGy. import numpy as np class DVH: """Processes the dose volume histogram from DICOM DVH data.""" def __init__(self, dvh): """Take a dvh numpy array and convert it to cGy.""" self.dvh = dvh['data'] * 100 / dvh['data'][0] self.scaling = dvh['scaling'] # Instruct numpy to print the full extent of the array np.set_printoptions(threshold=2147483647, suppress=True) def GetVolumeConstraint(self, dose): """ Return the volume (in percent) of the structure that receives at least a specific dose in cGy. i.e. V100, V150.""" return self.dvh[int(dose/self.scaling)] def GetVolumeConstraintCC(self, dose, volumecc): """ Return the volume (in cc) of the structure that receives at least a specific dose in cGy. i.e. V100, V150.""" volumepercent = self.GetVolumeConstraint(dose) return volumepercent * volumecc / 100 def GetDoseConstraint(self, volume): """ Return the maximum dose (in cGy) that a specific volume (in percent) receives. i.e. D90, D20.""" return np.argmin(np.fabs(self.dvh - volume))*self.scaling def CalculateVolume(structure): """Calculates the volume for the given structure.""" sPlanes = structure['planes'] # Store the total volume of the structure sVolume = 0 n = 0 # Iterate over each plane in the structure for sPlane in sPlanes.itervalues(): # Calculate the area for each contour in the current plane contours = [] largest = 0 largestIndex = 0 for c, contour in enumerate(sPlane): # Create arrays for the x,y coordinate pair for the triangulation x = [] y = [] for point in contour['contourData']: x.append(point[0]) y.append(point[1]) cArea = 0 # Calculate the area based on the Surveyor's formula for i in range(0, len(x)-1): cArea = cArea + x[i]*y[i+1] - x[i+1]*y[i] cArea = abs(cArea / 2) contours.append({'area':cArea, 'data':contour['contourData']}) # Determine which contour is the largest if (cArea > largest): largest = cArea largestIndex = c # See if the rest of the contours are within the largest contour area = contours[largestIndex]['area'] for i, contour in enumerate(contours): # Skip if this is the largest contour if not (i == largestIndex): contour['inside'] = False for point in contour['data']: if PointInPolygon(point[0], point[1], contours[largestIndex]['data']): contour['inside'] = True # Assume if one point is inside, all will be inside break # If the contour is inside, subtract it from the total area if contour['inside']: area = area - contour['area'] # Otherwise it is outside, so add it to the total area else: area = area + contour['area'] # If the plane is the first or last slice # only add half of the volume, otherwise add the full slice thickness if ((n == 0) or (n == len(sPlanes)-1)): sVolume = float(sVolume) + float(area) * float(structure['thickness']) * 0.5 else: sVolume = float(sVolume) + float(area) * float(structure['thickness']) # Increment the current plane number n = n + 1 # Since DICOM uses millimeters, convert from mm^3 to cm^3 volume = sVolume/1000 return volume def PointInPolygon(x, y, poly): """Uses the Ray Casting method to determine whether a point is within the given polygon. Taken from: http://www.ariel.com.au/a/python-point-int-poly.html""" n = len(poly) inside = False p1x, p1y, p1z = poly[0] for i in range(n+1): p2x, p2y, p2z = poly[i % n] if y > min(p1y,p2y): if y <= max(p1y,p2y): if x <= max(p1x,p2x): if p1y != p2y: xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x if p1x == p2x or x <= xinters: inside = not inside p1x,p1y = p2x,p2y return inside dicompyler-0.4.1-1/dicompyler/dvhdoses.py0000755000076500000240000000400511677672407021276 0ustar apanchalstaff00000000000000# dvhdoses.py """Functions to calculate minimum, maximum, and mean dose to ROI from a cDVH.""" # Copyright (c) 2009 Roy Keyes (roy.coding) # Copyright (c) 2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # # Start - 20 Nov. 2009 # It is assumed that the bin width of the cDVH is fixed at 1 cGy. def get_dvh_min(dvh): '''Return minimum dose to ROI derived from cDVH.''' # ROI volume (always receives at least 0% dose) v1 = dvh[0] j = 1 jmax = len(dvh) - 1 mindose = 0 while j < jmax: if dvh[j] < v1: mindose = (2*j - 1)/2.0 break else: j += 1 return mindose def get_dvh_max(dvh): '''Return maximum dose to ROI derived from cDVH.''' # Calulate dDVH ddvh = get_ddvh(dvh) maxdose = 0 j = len(ddvh) - 1 while j >= 0: if ddvh[j] > 0.0: maxdose = j+1 break else: j -= 1 return maxdose def get_dvh_median(dvh): '''Return median dose to ROI derived from cDVH.''' # Half of ROI volume v1 = dvh[0]/2. j = 1 jmax = len(dvh) - 1 while j < jmax: if dvh[j] < v1: mediandose = (2*j - 1)/2.0 break else: j += 1 return mediandose def get_dvh_mean(dvh): '''Return mean dose to ROI derived from cDVH.''' # Mean dose = total dose / ROI volume # Volume of ROI v1 = dvh[0] # Calculate dDVH ddvh = get_ddvh(dvh) # Calculate total dose j = 1 dose = 0 for d in ddvh[1:]: dose += d*j j += 1 meandose = dose/v1 return meandose def get_ddvh(cdvh): '''Return dDVH from cDVH array.''' # dDVH is the negative "slope" of the cDVH j = 0 jmax = len(cdvh) - 1 ddvh = [] while j < jmax: ddvh += [cdvh[j] - cdvh[j+1]] j += 1 ddvh += [cdvh[j]] return ddvh dicompyler-0.4.1-1/dicompyler/guidvh.py0000755000076500000240000000713311677672407020752 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # guidvh.py """Class that displays the dose volume histogram via wxPython and matplotlib.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # # It's assumed that the reference (prescription) dose is in cGy. import wxmpl import numpy as np class guiDVH: """Displays and updates the dose volume histogram using WxMpl.""" def __init__(self, parent): self.panelDVH = wxmpl.PlotPanel(parent, -1, size=(6, 4.50), dpi=68, crosshairs=False, autoscaleUnzoom=False) self.Replot() def Replot(self, dvhlist=None, scalinglist=None, structures=None, point=None, pointid=None, prefixes=None): """Redraws the plot.""" fig = self.panelDVH.get_figure() fig.set_edgecolor('white') # clear the axes and replot everything axes = fig.gca() axes.cla() maxlen = 1 if not (dvhlist == None): # Enumerate each set of DVHs for d, dvhs in enumerate(dvhlist): # Plot the DVH from each set for id, dvh in dvhs.iteritems(): if id in structures: # Convert the color array to MPL formatted color colorarray = np.array(structures[id]['color'], dtype=float) # Plot white as black so it is visible on the plot if np.size(np.nonzero(colorarray/255 - 1)): color = colorarray/255 else: color = np.zeros(3) prefix = prefixes[d] if not (prefixes == None) else None linestyle = '-' if not (d % 2) else '--' maxlen = self.DrawDVH(dvh, structures[id], axes, color, maxlen, scalinglist[d], prefix, linestyle) if (point and (pointid == id)): self.DrawPoint(point, axes, color) axes.legend(fancybox=True, shadow=True) # set the axes parameters axes.grid(True) axes.set_xlim(0, maxlen) axes.set_ylim(0, 100) axes.set_xlabel('Dose (cGy)') axes.set_ylabel('Volume (%)') axes.set_title('DVH') # redraw the display self.panelDVH.draw() def DrawDVH(self, dvh, structure, axes, color, maxlen, scaling=None, prefix=None, linestyle='-'): """Draw the given structure on the plot.""" # Determine the maximum DVH length for the x axis limit if len(dvh) > maxlen: maxlen = len(dvh) # if the structure color is white, change it to black dose = np.arange(len(dvh)) if not (scaling == None): dose = dose * scaling[structure['id']] name = prefix + ' ' + structure['name'] if prefix else structure['name'] axes.plot(dose, dvh, label=name, color=color, linewidth=2, linestyle=linestyle) return maxlen def DrawPoint(self, point, axes, color): """Draw the point for the given structure on the plot.""" axes.plot(point[0], point[1], 'o', color=color) dicompyler-0.4.1-1/dicompyler/guiutil.py0000755000076500000240000001740411677672407021150 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # guiutil.py """Several GUI utility functions that don't really belong anywhere.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ import util import wx from wx.xrc import XmlResource, XRCCTRL, XRCID from wx.lib.pubsub import Publisher as pub def IsMSWindows(): """Are we running on Windows? @rtype: Bool""" return wx.Platform=='__WXMSW__' def IsGtk(): """Are we running on GTK (Linux) @rtype: Bool""" return wx.Platform=='__WXGTK__' def IsMac(): """Are we running on Mac @rtype: Bool""" return wx.Platform=='__WXMAC__' def GetItemsList(wxCtrl): # Return the list of values stored in a wxCtrlWithItems list = [] if not (wxCtrl.IsEmpty()): for i in range(wxCtrl.GetCount()): list.append(wxCtrl.GetString(i)) return list def SetItemsList(wxCtrl, list = [], data = []): # Set the wxCtrlWithItems to the given list and store the data in the item wxCtrl.Clear() i = 0 for item in list: wxCtrl.Append(item) # if no data has been given, no need to set the client data if not (data == []): wxCtrl.SetClientData(i, data[i]) i = i + 1 if not (wxCtrl.IsEmpty()): wxCtrl.SetSelection(0) def get_data_dir(): """Returns the data location for the application.""" sp = wx.StandardPaths.Get() return wx.StandardPaths.GetUserLocalDataDir(sp) def get_icon(): """Returns the icon for the application.""" icon = None if IsMSWindows(): if util.main_is_frozen(): import sys exeName = sys.executable icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO) else: icon = wx.Icon(util.GetResourcePath('dicompyler.ico'), wx.BITMAP_TYPE_ICO) elif IsGtk(): icon = wx.Icon(util.GetResourcePath('dicompyler_icon11_16.png'), wx.BITMAP_TYPE_PNG) return icon def convert_pil_to_wx(pil, alpha=True): """ Convert a PIL Image into a wx.Image. Code taken from Dave Witten's imViewer-Simple.py in pydicom contrib.""" if alpha: image = apply(wx.EmptyImage, pil.size) image.SetData(pil.convert("RGB").tostring()) image.SetAlphaData(pil.convert("RGBA").tostring()[3::4]) else: image = wx.EmptyImage(pil.size[0], pil.size[1]) new_image = pil.convert('RGB') data = new_image.tostring() image.SetData(data) return image def get_progress_dialog(parent, title="Loading..."): """Function to load the progress dialog.""" # Load the XRC file for our gui resources res = XmlResource(util.GetResourcePath('guiutil.xrc')) dialogProgress = res.LoadDialog(parent, 'ProgressDialog') dialogProgress.Init(res, title) return dialogProgress def adjust_control(control): """Adjust the control and font size on the Mac.""" if IsMac(): font = control.GetFont() font.SetPointSize(11) control.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) control.SetFont(font) class ProgressDialog(wx.Dialog): """Dialog to show progress for certain long-running events.""" def __init__(self): pre = wx.PreDialog() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, res, title=None): """Method called after the dialog has been initialized.""" # Initialize controls self.SetTitle(title) self.lblProgressLabel = XRCCTRL(self, 'lblProgressLabel') self.lblProgress = XRCCTRL(self, 'lblProgress') self.gaugeProgress = XRCCTRL(self, 'gaugeProgress') self.lblProgressPercent = XRCCTRL(self, 'lblProgressPercent') def OnUpdateProgress(self, num, length, message=None): """Update the process interface elements.""" if not length: percentDone = 0 else: percentDone = int(100 * (num) / length) self.gaugeProgress.SetValue(percentDone) self.lblProgressPercent.SetLabel(str(percentDone)) if not (message == None): self.lblProgress.SetLabel(message) # End the dialog since we are done with the import process if (message == 'Done'): self.EndModal(wx.ID_OK) class ColorCheckListBox(wx.ScrolledWindow): """Control similar to a wx.CheckListBox with additional color indication.""" def __init__(self, parent, pubsubname=''): wx.ScrolledWindow.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) # Initialize variables self.pubsubname = pubsubname # Setup the layout for the frame self.grid = wx.BoxSizer(wx.VERTICAL) # Setup the panel background color and layout the controls self.SetBackgroundColour(wx.WHITE) self.SetSizer(self.grid) self.Layout() self.Clear() def Layout(self): self.SetScrollbars(20,20,50,50) super(ColorCheckListBox,self).Layout() def Append(self, item, data=None, color=None, refresh=True): """Add an item to the control.""" ccb = ColorCheckBox(self, item, data, color, self.pubsubname) self.items.append(ccb) self.grid.Add(ccb, 0, flag=wx.ALIGN_LEFT, border=4) self.grid.Add((0,3), 0) if refresh: self.Layout() def Clear(self): """Removes all items from the control.""" self.items = [] self.grid.Clear(deleteWindows=True) self.grid.Add((0,3), 0) self.Layout() class ColorCheckBox(wx.Panel): """Control with a checkbox and a color indicator.""" def __init__(self, parent, item, data=None, color=None, pubsubname=''): wx.Panel.__init__(self, parent, -1) # Initialize variables self.item = item self.data = data self.pubsubname = pubsubname # Initialize the controls self.colorbox = ColorBox(self, color) self.checkbox = wx.CheckBox(self, -1, item) # Setup the layout for the frame grid = wx.BoxSizer(wx.HORIZONTAL) grid.Add((3,0), 0) grid.Add(self.colorbox, 0, flag=wx.ALIGN_CENTRE) grid.Add((5,0), 0) grid.Add(self.checkbox, 1, flag=wx.EXPAND|wx.ALL|wx.ALIGN_CENTRE) # Decrease the font size on Mac if IsMac(): font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetPointSize(10) self.checkbox.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) self.checkbox.SetFont(font) # Setup the panel background color and layout the controls self.SetBackgroundColour(wx.WHITE) self.SetSizer(grid) self.Layout() # Bind ui events to the proper methods self.Bind(wx.EVT_CHECKBOX, self.OnCheck) def OnCheck(self, evt): """Send a message via pubsub if the checkbox has been checked.""" message = {'item':self.item, 'data':self.data, 'color':self.colorbox.GetBackgroundColour()} if evt.IsChecked(): pub.sendMessage('colorcheckbox.checked.' + self.pubsubname, message) else: pub.sendMessage('colorcheckbox.unchecked.' + self.pubsubname, message) class ColorBox(wx.Window): """Control that shows and stores a color.""" def __init__(self, parent, color=None): wx.Window.__init__(self, parent, -1) self.SetMinSize((16,16)) if not (color == None): col = [] for val in color: col.append(int(val)) self.SetBackgroundColour(tuple(col)) # Bind ui events to the proper methods self.Bind(wx.EVT_SET_FOCUS, self.OnFocus) def OnFocus(self, evt): """Ignore the focus event via keyboard.""" self.Navigate() dicompyler-0.4.1-1/dicompyler/license.txt0000644000076500000240000003716111677672407021276 0ustar apanchalstaff00000000000000Copyright (c) 2009-2011 Aditya Panchal and dicompyler contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The name of Aditya Panchal may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- dicompyler uses the famfamfam Silk icon set created by Mark James found at: http://famfamfam.com/lab/icons/silk/ famfamfam Silk is licensed under the Creative Commons Attribution 2.5 License. -------------------------------------------------------------------------------- dicompyler uses the following libraries: elixir Copyright (c) 2007, 2008 Jonathan LaCour, Daniel Haus, and Gaetan de Menten. SQLAlchemy is a trademark of Michael Bayer. pydicom Copyright (c) 2008-2010 Darcy Mason and pydicom contributors SQLAlchemy Copyright (c) 2005, 2006, 2007, 2008, 2009 Michael Bayer and contributors. SQLAlchemy is a trademark of Michael Bayer. They are distributed under the MIT License as follows: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- dicompyler uses The Python Imaging Library, which is distributed under the following license: The Python Imaging Library is Copyright (c) 1997-2011 by Secret Labs AB Copyright (c) 1995-2011 by Fredrik Lundh By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- dicompyler uses matplotlib, which is distributed under the following license: LICENSE AGREEMENT FOR MATPLOTLIB 1.0.1 -------------------------------------- 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib 1.0.1 alone or in any derivative version, provided, however, that JDH's License Agreement and JDH's notice of copyright, i.e., "Copyright (c) 2002-2007 John D. Hunter; All Rights Reserved" are retained in matplotlib 1.0.1 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib 1.0.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib 1.0.1. 4. JDH is making matplotlib 1.0.1 available to Licensee on an "AS IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 1.0.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 1.0.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 1.0.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib 1.0.1, Licensee agrees to be bound by the terms and conditions of this License Agreement. -------------------------------------------------------------------------------- dicompyler uses NumPy, which is distributed under the New BSD license: Copyright (c) 2005, NumPy Developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The name of NumPy Developers may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- dicompyler uses Python, which is distributed under the following license: PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. -------------------------------------------------------------------------------- dicompyler uses WxMpl, which is distributed under the following license: Copyright 2005-2009 Illinois Institute of Technology Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ILLINOIS INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Illinois Institute of Technology shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Illinois Institute of Technology. -------------------------------------------------------------------------------- dicompyler uses wxPython, which is distributed under the following license: wxWindows Library Licence, Version 3.1 ====================================== Copyright (c) 1998-2005 Julian Smart, Robert Roebling et al Everyone is permitted to copy and distribute verbatim copies of this licence document, but changing it is not allowed. WXWINDOWS LIBRARY LICENCE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public Licence for more details. You should have received a copy of the GNU Library General Public Licence along with this software, usually in a file named COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. EXCEPTION NOTICE 1. As a special exception, the copyright holders of this library give permission for additional uses of the text contained in this release of the library as licenced under the wxWindows Library Licence, applying either version 3.1 of the Licence, or (at your option) any later version of the Licence as published by the copyright holders of version 3.1 of the Licence document. 2. The exception is that you may use, copy, link, modify and distribute under your own terms, binary object code versions of works based on the Library. 3. If you copy code from files distributed under the terms of the GNU General Public Licence or the GNU Library General Public Licence into a copy of this library, as this licence permits, the exception does not apply to the code that you add in this way. To avoid misleading anyone as to the status of such modified files, you must delete this exception notice from such code and/or adjust the licensing conditions notice accordingly. 4. If you write modifications of your own for this library, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, you must delete the exception notice from such code and/or adjust the licensing conditions notice accordingly. dicompyler-0.4.1-1/dicompyler/main.py0000644000076500000240000010763011700106032020355 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # main.py """Main file for dicompyler.""" # Copyright (c) 2009-2011 Aditya Panchal # Copyright (c) 2009 Roy Keyes # This file is part of dicompyler, released under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ # Configure logging for dicompyler import logging, logging.handlers logger = logging.getLogger('dicompyler') logger.setLevel(logging.DEBUG) import os, threading import sys, traceback import wx from wx.xrc import * import wx.lib.dialogs, webbrowser # Uncomment line to setup pubsub for frozen targets on wxPython 2.8.11 and above # from wx.lib.pubsub import setupv1 from wx.lib.pubsub import Publisher as pub from dicompyler import guiutil, util from dicompyler import dicomgui, dvhdata, dvhdoses, dvhcalc from dicompyler.dicomparser import DicomParser as dp from dicompyler import plugin, preferences __version__ = "0.4.1-1" class MainFrame(wx.Frame): def __init__(self, parent, id, title, res): # Initialize logging logger = logging.getLogger('dicompyler') # Configure the exception hook to process threads as well self.InstallThreadExcepthook() # Remap the exception hook so that we can log and display exceptions def LogExcepthook(*exc_info): # Log the exception text = "".join(traceback.format_exception(*exc_info)) logger.error("Unhandled exception: %s", text) pub.sendMessage('logging.exception', text) sys.excepthook = LogExcepthook # Modify the logging system from pydicom to capture important messages pydicom_logger = logging.getLogger('pydicom') for l in pydicom_logger.handlers: pydicom_logger.removeHandler(l) # Add file logger logpath = os.path.join(guiutil.get_data_dir(), 'logs') if not os.path.exists(logpath): os.makedirs(logpath) self.fh = logging.handlers.RotatingFileHandler( os.path.join(logpath, 'dicompyler.log'), maxBytes=524288, backupCount=7) self.fh.setFormatter(logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) self.fh.setLevel(logging.WARNING) logger.addHandler(self.fh) pydicom_logger.addHandler(self.fh) # Add console logger if not frozen if not util.main_is_frozen(): self.ch = logging.StreamHandler() self.ch.setFormatter(logging.Formatter( '%(levelname)s: %(message)s')) self.ch.setLevel(logging.WARNING) logger.addHandler(self.ch) pydicom_logger.addHandler(self.ch) # Otherwise if frozen, send stdout/stderror to the logging system else: sys.stdout = StreamWrapper(logger, logging.INFO) sys.stderr = StreamWrapper(logger, logging.ERROR) # Set the window size if guiutil.IsMac(): size=(900, 700) else: size=(850, 625) wx.Frame.__init__(self, parent, id, title, pos=wx.DefaultPosition, size=size, style=wx.DEFAULT_FRAME_STYLE) # Set up the status bar self.sb = self.CreateStatusBar(3) # set up resource file and config file self.res = res # Set window icon if not guiutil.IsMac(): self.SetIcon(guiutil.get_icon()) # Load the main panel for the program self.panelGeneral = self.res.LoadPanel(self, 'panelGeneral') # Initialize the General panel controls self.notebook = XRCCTRL(self, 'notebook') self.notebookTools = XRCCTRL(self, 'notebookTools') self.lblPlanName = XRCCTRL(self, 'lblPlanName') self.lblRxDose = XRCCTRL(self, 'lblRxDose') self.lblPatientName = XRCCTRL(self, 'lblPatientName') self.lblPatientID = XRCCTRL(self, 'lblPatientID') self.lblPatientGender = XRCCTRL(self, 'lblPatientGender') self.lblPatientDOB = XRCCTRL(self, 'lblPatientDOB') self.choiceStructure = XRCCTRL(self, 'choiceStructure') self.lblStructureVolume = XRCCTRL(self, 'lblStructureVolume') self.lblStructureMinDose = XRCCTRL(self, 'lblStructureMinDose') self.lblStructureMaxDose = XRCCTRL(self, 'lblStructureMaxDose') self.lblStructureMeanDose = XRCCTRL(self, 'lblStructureMeanDose') self.cclbStructures = guiutil.ColorCheckListBox(self.notebookTools, 'structure') self.cclbIsodoses = guiutil.ColorCheckListBox(self.notebookTools, 'isodose') # Modify the control and font size on Mac controls = [self.notebookTools, self.choiceStructure] if guiutil.IsMac(): font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetPointSize(10) for control in controls: control.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) control.SetFont(font) # Setup the layout for the frame mainGrid = wx.BoxSizer(wx.VERTICAL) hGrid = wx.BoxSizer(wx.HORIZONTAL) if guiutil.IsMac(): hGrid.Add(self.panelGeneral, 1, flag=wx.EXPAND|wx.ALL|wx.ALIGN_CENTRE, border=4) else: hGrid.Add(self.panelGeneral, 1, flag=wx.EXPAND|wx.ALL|wx.ALIGN_CENTRE) mainGrid.Add(hGrid, 1, flag=wx.EXPAND|wx.ALL|wx.ALIGN_CENTRE) # Load the menu for the frame menuMain = self.res.LoadMenuBar('menuMain') # If we are running on Mac OS X, alter the menu location if guiutil.IsMac(): wx.App_SetMacAboutMenuItemId(XRCID('menuAbout')) wx.App_SetMacPreferencesMenuItemId(XRCID('menuPreferences')) wx.App_SetMacExitMenuItemId(XRCID('menuExit')) # Set the menu as the default menu for this frame self.SetMenuBar(menuMain) # Setup Tools menu self.menuShowLogs = menuMain.FindItemById(XRCID('menuShowLogs')).GetMenu() self.menuPlugins = menuMain.FindItemById(XRCID('menuPluginManager')).GetMenu() # Setup Export menu self.menuExport = menuMain.FindItemById(XRCID('menuExportPlaceholder')).GetMenu() self.menuExport.Delete(menuMain.FindItemById(XRCID('menuExportPlaceholder')).GetId()) self.menuExportItem = menuMain.FindItemById(XRCID('menuExport')) self.menuExportItem.Enable(False) # Bind menu events to the proper methods wx.EVT_MENU(self, XRCID('menuOpen'), self.OnOpenPatient) wx.EVT_MENU(self, XRCID('menuExit'), self.OnClose) wx.EVT_MENU(self, XRCID('menuPreferences'), self.OnPreferences) wx.EVT_MENU(self, XRCID('menuShowLogs'), self.OnShowLogs) wx.EVT_MENU(self, XRCID('menuPluginManager'), self.OnPluginManager) wx.EVT_MENU(self, XRCID('menuAbout'), self.OnAbout) wx.EVT_MENU(self, XRCID('menuHomepage'), self.OnHomepage) wx.EVT_MENU(self, XRCID('menuLicense'), self.OnLicense) # Load the toolbar for the frame toolbarMain = self.res.LoadToolBar(self, 'toolbarMain') self.SetToolBar(toolbarMain) self.toolbar = self.GetToolBar() # Setup main toolbar controls if guiutil.IsGtk(): import gtk folderbmp = wx.ArtProvider_GetBitmap(gtk.STOCK_OPEN, wx.ART_OTHER, (24, 24)) else: folderbmp = wx.Bitmap(util.GetResourcePath('folder_user.png')) self.maintools = [{'label':"Open Patient", 'bmp':folderbmp, 'shortHelp':"Open Patient...", 'eventhandler':self.OnOpenPatient}] for m, tool in enumerate(self.maintools): self.toolbar.AddLabelTool( m+1, tool['label'], tool['bmp'], shortHelp=tool['shortHelp']) self.Bind(wx.EVT_TOOL, tool['eventhandler'], id=m+1) self.toolbar.Realize() # Bind interface events to the proper methods wx.EVT_CHOICE(self, XRCID('choiceStructure'), self.OnStructureSelect) self.Bind(wx.EVT_CLOSE, self.OnClose) self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) # Events to work around a focus bug in Windows self.notebook.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.notebook.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.notebookTools.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.notebookTools.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.SetSizer(mainGrid) self.Layout() #Set the Minumum size self.SetMinSize(size) self.Centre(wx.BOTH) # Initialize the welcome notebook tab panelWelcome = self.res.LoadPanel(self.notebook, 'panelWelcome') self.notebook.AddPage(panelWelcome, 'Welcome') # Set the version on the welcome notebook tab XRCCTRL(self, 'lblVersion').SetLabel('Version ' + __version__) # Initialize the tools notebook self.notebookTools.AddPage(self.cclbStructures, 'Structures') self.notebookTools.AddPage(self.cclbIsodoses, 'Isodoses') # Create the data folder datapath = guiutil.get_data_dir() if not os.path.exists(datapath): os.mkdir(datapath) # Initialize the preferences if guiutil.IsMac(): self.prefmgr = preferences.PreferencesManager( parent = None, appname = 'dicompyler') else: self.prefmgr = preferences.PreferencesManager( parent = None, appname = 'dicompyler', name = 'Options') sp = wx.StandardPaths.Get() self.generalpreftemplate = [ {'DICOM Import Settings': [{'name':'Import Location', 'type':'choice', 'values':['Remember Last Used', 'Always Use Default'], 'default':'Remember Last Used', 'callback':'general.dicom.import_location_setting'}, {'name':'Default Location', 'type':'directory', 'default':unicode(sp.GetDocumentsDir()), 'callback':'general.dicom.import_location'}] }, {'Plugin Settings': [{'name':'User Plugins Location', 'type':'directory', 'default':unicode(os.path.join(datapath, 'plugins')), 'callback':'general.plugins.user_plugins_location', 'restart':True}] }, {'Calculation Settings': [{'name':'DVH Calculation', 'type':'choice', 'values':['Use RT Dose DVH if Present', 'Always Recalculate DVH'], 'default':'Use RT Dose DVH if Present', 'callback':'general.calculation.dvh_recalc'}] }, {'Advanced Settings': [{'name':'Enable Detailed Logging', 'type':'checkbox', 'default':False, 'callback':'general.advanced.detailed_logging'}] }] self.preftemplate = [{'General':self.generalpreftemplate}] pub.sendMessage('preferences.updated.template', self.preftemplate) # Set up pubsub pub.subscribe(self.OnLoadPatientData, 'patient.updated.raw_data') pub.subscribe(self.OnStructureCheck, 'colorcheckbox.checked.structure') pub.subscribe(self.OnStructureUncheck, 'colorcheckbox.unchecked.structure') pub.subscribe(self.OnIsodoseCheck, 'colorcheckbox.checked.isodose') pub.subscribe(self.OnIsodoseUncheck, 'colorcheckbox.unchecked.isodose') pub.subscribe(self.OnUpdatePlugins, 'general.plugins.user_plugins_location') pub.subscribe(self.OnUpdatePreferences, 'general') pub.subscribe(self.OnUpdateStatusBar, 'main.update_statusbar') # Send a message to the logging system to turn on/off detailed logging pub.sendMessage('preferences.requested.value', 'general.advanced.detailed_logging') # Create the default user plugin path self.userpluginpath = os.path.join(datapath, 'plugins') if not os.path.exists(self.userpluginpath): os.mkdir(self.userpluginpath) # Load and initialize plugins self.plugins = [] pub.sendMessage('preferences.requested.value', 'general.plugins.user_plugins_location') pub.sendMessage('preferences.requested.value', 'general.calculation.dvh_recalc') ########################### Patient Loading Functions ########################## def OnOpenPatient(self, evt): """Load and show the Dicom RT Importer dialog box.""" self.ptdata = dicomgui.ImportDicom(self) if not (self.ptdata == None): # Delete the previous notebook pages self.notebook.DeleteAllPages() # Delete the previous toolbar items for t in range(0, self.toolbar.GetToolsCount()): # Only delete the plugin toolbar items if (t >= len(self.maintools)): self.toolbar.DeleteToolByPos(len(self.maintools)) # Delete the previous plugin menus if len(self.menuDict): self.menuPlugins.Delete(wx.ID_SEPARATOR) for menuid, menu in self.menuDict.iteritems(): self.menuPlugins.Delete(menuid) self.menuDict = {} # Delete the previous export menus if len(self.menuExportDict): self.menuExportItem.Enable(False) for menuid, menu in self.menuExportDict.iteritems(): self.menuExport.Delete(menuid) self.menuExportDict = {} # Reset the preferences template self.preftemplate = [{'General':self.generalpreftemplate}] # Set up the plugins for each plugin entry point of dicompyler for i, p in enumerate(self.plugins): props = p.pluginProperties() # Only load plugin versions that are qualified if (props['plugin_version'] == 1): # Check whether the plugin can view the loaded DICOM data add = False if len(props['min_dicom']): for key in props['min_dicom']: if (key == 'rxdose'): pass elif key in self.ptdata.keys(): add = True else: add = False break # Plugin can view all DICOM data so always load it else: add = True # Initialize the plugin if add: # Load the main panel plugins if (props['plugin_type'] == 'main'): plugin = p.pluginLoader(self.notebook) self.notebook.AddPage(plugin, props['name']) # Load the menu plugins if (props['plugin_type'] == 'menu'): if not len(self.menuDict): self.menuPlugins.AppendSeparator() self.menuDict[100+i] = self.menuPlugins.Append( 100+i, props['name']+'...') plugin = p.plugin(self) wx.EVT_MENU(self, 100+i, plugin.pluginMenu) # Load the export menu plugins if (props['plugin_type'] == 'export'): if not len(self.menuExportDict): self.menuExportItem.Enable(True) self.menuExportDict[200+i] = self.menuExport.Append( 200+i, props['menuname']) plugin = p.plugin(self) wx.EVT_MENU(self, 200+i, plugin.pluginMenu) # Add the plugin preferences if they exist if hasattr(plugin, 'preferences'): self.preftemplate.append({props['name']:plugin.preferences}) pub.sendMessage('preferences.updated.template', self.preftemplate) pub.sendMessage('patient.updated.raw_data', self.ptdata) def OnLoadPatientData(self, msg): """Update and load the patient data.""" ptdata = msg.data dlgProgress = guiutil.get_progress_dialog(self, "Loading Patient Data...") self.t=threading.Thread(target=self.LoadPatientDataThread, args=(self, ptdata, dlgProgress.OnUpdateProgress, self.OnUpdatePatientData)) self.t.start() dlgProgress.ShowModal() if dlgProgress: dlgProgress.Destroy() def LoadPatientDataThread(self, parent, ptdata, progressFunc, updateFunc): """Thread to load the patient data.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Processing patient data...') patient = {} if ptdata.has_key('rtss'): wx.CallAfter(progressFunc, 20, 100, 'Processing RT Structure Set...') if not patient.has_key('id'): patient = dp(ptdata['rtss']).GetDemographics() patient['structures'] = dp(ptdata['rtss']).GetStructures() if ptdata.has_key('rtplan'): wx.CallAfter(progressFunc, 40, 100, 'Processing RT Plan...') if not patient.has_key('id'): patient = dp(ptdata['rtplan']).GetDemographics() patient['plan'] = dp(ptdata['rtplan']).GetPlan() if ptdata.has_key('rtdose'): wx.CallAfter(progressFunc, 60, 100, 'Processing RT Dose...') if not patient.has_key('id'): patient = dp(ptdata['rtdose']).GetDemographics() patient['dvhs'] = dp(ptdata['rtdose']).GetDVHs() patient['dose'] = dp(ptdata['rtdose']) if ptdata.has_key('images'): wx.CallAfter(progressFunc, 80, 100, 'Processing Images...') if not patient.has_key('id'): patient = dp(ptdata['images'][0]).GetDemographics() patient['images'] = [] for image in ptdata['images']: patient['images'].append(dp(image)) if ptdata.has_key('rxdose'): if not patient.has_key('plan'): patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure wx.CallAfter(progressFunc, 90, 100, 'Processing DVH data...') if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 for key, structure in patient['structures'].iteritems(): # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys())) or (self.dvhRecalc == 'Always Recalculate DVH')): # Only calculate DVHs for structures, not applicators if structure['name'].startswith('Applicator'): continue wx.CallAfter(progressFunc, 10*i/len(patient['structures'])+90, 100, 'Calculating DVH for ' + structure['name'] + '...') # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(structure, patient['dose'], 50000) if len(dvh['data']): patient['dvhs'][key] = dvh i += 1 for key, dvh in patient['dvhs'].iteritems(): self.CalculateDoseStatistics(dvh, ptdata['rxdose']) wx.CallAfter(progressFunc, 100, 100, 'Done') wx.CallAfter(updateFunc, patient) def CalculateDoseStatistics(self, dvh, rxdose): """Calculate the dose statistics for the given DVH and rx dose.""" sfdict = {'min':dvhdoses.get_dvh_min, 'mean':dvhdoses.get_dvh_mean, 'max':dvhdoses.get_dvh_max} for stat, func in sfdict.iteritems(): # Only calculate stat if the stat was not calculated previously (-1) if dvh[stat] == -1: dvh[stat] = 100*func(dvh['data']*dvh['scaling'])/rxdose return dvh def OnUpdatePatientData(self, patient): """Update the patient data in the main program interface.""" self.PopulateDemographics(patient) self.structures = {} if patient.has_key('structures'): self.structures = patient['structures'] self.PopulateStructures() if patient.has_key('plan'): self.PopulatePlan(patient['plan']) else: self.PopulatePlan({}) if (patient.has_key('dose') and patient.has_key('plan')): self.PopulateIsodoses(patient.has_key('images'), patient['plan'], patient['dose']) else: self.PopulateIsodoses(patient.has_key('images'), {}, {}) if patient.has_key('dvhs'): self.dvhs = patient['dvhs'] else: self.dvhs = {} # publish the parsed data pub.sendMessage('patient.updated.parsed_data', patient) def PopulateStructures(self): """Populate the structure list.""" self.cclbStructures.Clear() self.structureList = {} for id, structure in self.structures.iteritems(): # Only append structures, don't include applicators if not(structure['name'].startswith('Applicator')): self.cclbStructures.Append(structure['name'], structure, structure['color'], refresh=False) # Refresh the structure list manually since we didn't want it to refresh # after adding each structure self.cclbStructures.Layout() self.choiceStructure.Clear() self.choiceStructure.Enable(False) self.OnStructureUnselect() def PopulateIsodoses(self, has_images, plan, dose): """Populate the isodose list.""" self.cclbIsodoses.Clear() self.isodoseList={} if (has_images and len(plan)): dosedata = dose.GetDoseData() dosemax = int(dosedata['dosemax'] * dosedata['dosegridscaling'] * 10000 / plan['rxdose']) self.isodoses = [{'level':dosemax, 'color':wx.Colour(120, 0, 0), 'name':'Max'}, {'level':102, 'color':wx.Colour(170, 0, 0)}, {'level':100, 'color':wx.Colour(238, 69, 0)}, {'level':98, 'color':wx.Colour(255, 165, 0)}, {'level':95, 'color':wx.Colour(255, 255, 0)}, {'level':90, 'color':wx.Colour(0, 255, 0)}, {'level':80, 'color':wx.Colour(0, 139, 0)}, {'level':70, 'color':wx.Colour(0, 255, 255)}, {'level':50, 'color':wx.Colour(0, 0, 255)}, {'level':30, 'color':wx.Colour(0, 0, 128)}] for isodose in self.isodoses: # Calculate the absolute dose value name = ' / ' + unicode("%.6g" % (float(isodose['level']) * float(plan['rxdose']) / 100)) + \ ' cGy' if isodose.has_key('name'): name = name + ' [' + isodose['name'] + ']' self.cclbIsodoses.Append(str(isodose['level'])+' %'+name, isodose, isodose['color'], refresh=False) # Refresh the isodose list manually since we didn't want it to refresh # after adding each isodose self.cclbIsodoses.Layout() def PopulateDemographics(self, demographics): """Populate the patient demographics.""" self.lblPatientName.SetLabel(demographics['name']) self.lblPatientID.SetLabel(demographics['id']) self.lblPatientGender.SetLabel(demographics['gender']) self.lblPatientDOB.SetLabel(demographics['dob']) def PopulatePlan(self, plan): """Populate the patient's plan information.""" if (len(plan) and not plan['rxdose'] == 1): if plan.has_key('name'): if len(plan['name']): self.lblPlanName.SetLabel(plan['name']) elif len(plan['label']): self.lblPlanName.SetLabel(plan['label']) self.lblRxDose.SetLabel(unicode("%.6g" % plan['rxdose'])) else: self.lblPlanName.SetLabel('-') self.lblRxDose.SetLabel('-') ############################## Structure Functions ############################# def OnStructureCheck(self, msg): """Load the properties of the currently checked structures.""" structure = msg.data # Get the structure number id = structure['data']['id'] structure['data']['color'] = structure['color'].Get() # Make sure that the volume has been calculated for each structure # before setting it if not self.structures[id].has_key('volume'): # Use the volume units from the DVH if they are absolute volume if self.dvhs.has_key(id) and (self.dvhs[id]['volumeunits'] == 'CM3'): self.structures[id]['volume'] = self.dvhs[id]['data'][0] # Otherwise calculate the volume from the structure data else: self.structures[id]['volume'] = dvhdata.CalculateVolume( self.structures[id]) structure['data']['volume'] = self.structures[id]['volume'] self.structureList[id] = structure['data'] # Populate the structure choice box with the checked structures self.choiceStructure.Enable() i = self.choiceStructure.Append(structure['data']['name']) self.choiceStructure.SetClientData(i, id) # Select the first structure self.OnStructureSelect() pub.sendMessage('structures.checked', self.structureList) def OnStructureUncheck(self, msg): """Remove the unchecked structures.""" structure = msg.data # Get the structure number id = structure['data']['id'] # Remove the structure from the structure list if self.structureList.has_key(id): del self.structureList[id] # Remove the structure from the structure choice box for n in range(self.choiceStructure.GetCount()): if (id == self.choiceStructure.GetClientData(n)): # Save if the currently selected item's position currSelection = self.choiceStructure.GetSelection() self.choiceStructure.Delete(n) break # If the currently selected item will be deleted, # select the last item instead if (n == currSelection): if (self.choiceStructure.GetCount() >= 1): self.OnStructureSelect() # Disable the control if it is the last item if (self.choiceStructure.GetCount() == 0): self.choiceStructure.Enable(False) self.OnStructureUnselect() pub.sendMessage('structures.checked', self.structureList) def OnStructureSelect(self, evt=None): """Load the properties of the currently selected structure.""" if (evt == None): self.choiceStructure.SetSelection(0) choiceItem = 0 else: choiceItem = evt.GetInt() # Load the structure id chosen from the choice control id = self.choiceStructure.GetClientData(choiceItem) pub.sendMessage('structure.selected', {'id':id}) self.lblStructureVolume.SetLabel(str(self.structures[id]['volume'])[0:7]) # make sure that the dvh has been calculated for each structure # before setting it if self.dvhs.has_key(id): self.lblStructureMinDose.SetLabel("%.3f" % self.dvhs[id]['min']) self.lblStructureMaxDose.SetLabel("%.3f" % self.dvhs[id]['max']) self.lblStructureMeanDose.SetLabel("%.3f" % self.dvhs[id]['mean']) else: self.lblStructureMinDose.SetLabel('-') self.lblStructureMaxDose.SetLabel('-') self.lblStructureMeanDose.SetLabel('-') def OnStructureUnselect(self): """Clear the properties of the selected structure.""" pub.sendMessage('structures.selected', {'id':None}) self.lblStructureVolume.SetLabel('-') self.lblStructureMinDose.SetLabel('-') self.lblStructureMaxDose.SetLabel('-') self.lblStructureMeanDose.SetLabel('-') ############################### Isodose Functions ############################## def OnIsodoseCheck(self, msg): """Load the properties of the currently checked isodoses.""" isodose = msg.data self.isodoseList[isodose['data']['level']] = isodose pub.sendMessage('isodoses.checked', self.isodoseList) def OnIsodoseUncheck(self, msg): """Remove the unchecked isodoses.""" isodose = msg.data id = isodose['data']['level'] # Remove the isodose from the isodose list if self.isodoseList.has_key(id): del self.isodoseList[id] pub.sendMessage('isodoses.checked', self.isodoseList) ################################ Other Functions ############################### def InstallThreadExcepthook(self): """Workaround for sys.excepthook thread bug from Jonathan Ellis (http://bugs.python.org/issue1230540). Call once from __main__ before creating any threads. If using psyco, call psyco.cannotcompile(threading.Thread.run) since this replaces a new-style class method.""" run_old = threading.Thread.run def Run(*args, **kwargs): try: run_old(*args, **kwargs) except (KeyboardInterrupt, SystemExit): raise except: sys.excepthook(*sys.exc_info()) threading.Thread.run = Run def OnUpdatePreferences(self, msg): """When the preferences change, update the values.""" if (msg.topic[1] == 'calculation') and (msg.topic[2] == 'dvh_recalc'): self.dvhRecalc = msg.data elif (msg.topic[1] == 'advanced') and \ (msg.topic[2] == 'detailed_logging'): # Enable logging at the debug level if the value is set if msg.data: self.fh.setLevel(logging.DEBUG) if not util.main_is_frozen(): self.ch.setLevel(logging.DEBUG) else: self.fh.setLevel(logging.WARNING) if not util.main_is_frozen(): self.ch.setLevel(logging.WARNING) def OnUpdatePlugins(self, msg): """Update the location of the user plugins and load all plugins.""" self.userpluginpath = msg.data # Load the plugins only if they haven't been loaded previously if not len(self.plugins): self.plugins = plugin.import_plugins(self.userpluginpath) self.menuDict = {} self.menuExportDict = {} def OnUpdateStatusBar(self, msg): """Update the status bar text.""" for k, v in msg.data.iteritems(): self.sb.SetStatusText(unicode(v), k) def OnPageChanged(self, evt): """Notify each notebook tab whether it has the focus or not.""" # Determine the new tab new = evt.GetSelection() page = self.notebook.GetPage(new) # Notify the new tab that it has focus if hasattr(page, 'OnFocus'): page.OnFocus() # If the tab has toolbar items, display them if hasattr(page, 'tools'): for t, tool in enumerate(page.tools): self.toolbar.AddLabelTool( (new+1)*10+t, tool['label'], tool['bmp'], shortHelp=tool['shortHelp']) self.Bind(wx.EVT_TOOL, tool['eventhandler'], id=(new+1)*10+t) self.toolbar.Realize() # For all other tabs, notify that they don't have focus anymore for i in range(self.notebook.GetPageCount()): if not (new == i): page = self.notebook.GetPage(i) if hasattr(page, 'OnUnfocus'): page.OnUnfocus() # Delete all other toolbar items if hasattr(page, 'tools'): for t, tool in enumerate(page.tools): self.toolbar.DeleteTool((i+1)*10+t) evt.Skip() def OnKeyDown(self, evt): """Capture the keypress when the notebook tab is focused. Currently this is used to workaround a bug in Windows since the notebook tab instead of the panel receives focus.""" if guiutil.IsMSWindows(): pub.sendMessage('main.key_down', evt) def OnMouseWheel(self, evt): """Capture the mousewheel event when the notebook tab is focused. Currently this is used to workaround a bug in Windows since the notebook tab instead of the panel receives focus.""" if guiutil.IsMSWindows(): pub.sendMessage('main.mousewheel', evt) def OnPreferences(self, evt): """Load and show the Preferences dialog box.""" self.prefmgr.Show() def OnShowLogs(self, evt): """Open and display the logs folder.""" util.open_path(os.path.join(guiutil.get_data_dir(), 'logs')) def OnPluginManager(self, evt): """Load and show the Plugin Manager dialog box.""" self.pm = plugin.PluginManager(self, self.plugins) def OnAbout(self, evt): # First we create and fill the info object info = wx.AboutDialogInfo() info.Name = "dicompyler" info.Version = __version__ info.Copyright = u"© 2009-2011 Aditya Panchal" credits = util.get_credits() info.Developers = credits['developers'] info.Artists = credits['artists'] desc = "Extensible radiation therapy research platform and viewer for DICOM and DICOM RT." + \ "\n\ndicompyler is released under a BSD license.\n" + \ "See the Help menu for license information." info.Description = desc if guiutil.IsGtk(): info.WebSite = "http://code.google.com/p/dicompyler/" # Then we call wx.AboutBox giving it that info object wx.AboutBox(info) def OnHomepage(self, evt): """Show the homepage for dicompyler.""" webbrowser.open_new_tab("http://code.google.com/p/dicompyler/") def OnLicense(self, evt): """Show the license document in a new dialog.""" f = open(util.get_text_resources("license.txt"), "rU") msg = f.read() f.close() if guiutil.IsMSWindows(): dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "dicompyler License") else: dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "dicompyler License", size=(650, 550)) dlg.ShowModal() dlg.Destroy() def OnClose(self, _): self.Destroy() class StreamWrapper(object): """Class that accepts messages redirected from stdout/stderr.""" def __init__(self, logger, level): self.logger = logger self.level = level def write(self, string): import inspect f = inspect.currentframe(1) caller = f.f_code.co_name self.logger.log(self.level, "%s: %s", caller, string) class dicompyler(wx.App): def OnInit(self): wx.InitAllImageHandlers() wx.GetApp().SetAppName("dicompyler") # Load the XRC file for our gui resources self.res = XmlResource(util.GetResourcePath('main.xrc')) # Use the native listctrl on Mac OS X if guiutil.IsMac(): wx.SystemOptions.SetOptionInt("mac.listctrl.always_use_generic", 0) dicompylerFrame = MainFrame(None, -1, "dicompyler", self.res) self.SetTopWindow(dicompylerFrame) dicompylerFrame.Centre() dicompylerFrame.Show() return 1 # end of class dicompyler def start(): app = dicompyler(0) app.MainLoop() if __name__ == '__main__': start() dicompyler-0.4.1-1/dicompyler/plugin.py0000755000076500000240000001165511677672407020766 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # plugin.py """Plugin manager for dicompyler.""" # Copyright (c) 2010-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ import logging logger = logging.getLogger('dicompyler.plugin') import imp, os import wx from wx.xrc import * from dicompyler import guiutil, util def import_plugins(userpath=None): """Find and import available plugins.""" # Get the base plugin path basepath = util.GetBasePluginsPath('') # Get the user plugin path if it has not been set if (userpath == None): datapath = guiutil.get_data_dir() userpath = os.path.join(datapath, 'plugins') # Get the list of possible plugins from both paths possibleplugins = [] for i in os.listdir(userpath): possibleplugins.append(i) for i in os.listdir(basepath): possibleplugins.append(i) modules = [] plugins = [] for file in possibleplugins: module = file.split('.')[0] if module not in modules: if not ((module == "__init__") or (module == "")): # only try to import the module once modules.append(module) try: f, filename, description = imp.find_module(module, [userpath, basepath]) except ImportError: # Not able to find module so pass pass else: # Try to import the module if no exception occurred try: plugins.append(imp.load_module(module, f, filename, description)) except ImportError: logger.exception("%s could not be loaded", module) else: logger.debug("%s loaded", module) # If the module is a single file, close it if not (description[2] == imp.PKG_DIRECTORY): f.close() return plugins def PluginManager(parent, plugins): """Prepare to show the plugin manager dialog.""" # Load the XRC file for our gui resources res = XmlResource(util.GetResourcePath('plugin.xrc')) dlgPluginManager = res.LoadDialog(parent, "PluginManagerDialog") dlgPluginManager.Init(plugins) # Show the dialog dlgPluginManager.ShowModal() class PluginManagerDialog(wx.Dialog): """Manage the available plugins.""" def __init__(self): pre = wx.PreDialog() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, plugins): """Method called after the panel has been initialized.""" # Set window icon if not guiutil.IsMac(): self.SetIcon(guiutil.get_icon()) # Initialize controls self.lcPlugins = XRCCTRL(self, 'lcPlugins') self.btnGetMorePlugins = XRCCTRL(self, 'btnGetMorePlugins') self.btnDeletePlugin = XRCCTRL(self, 'btnDeletePlugin') self.plugins = plugins # Bind interface events to the proper methods # wx.EVT_BUTTON(self, XRCID('btnDeletePlugin'), self.DeletePlugin) self.InitPluginList() self.LoadPlugins() def InitPluginList(self): """Initialize the plugin list control.""" info = wx.ListItem() info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT info.m_image = -1 info.m_format = 0 info.m_text = 'Name' self.lcPlugins.InsertColumnInfo(0, info) self.lcPlugins.SetColumn(0, info) self.lcPlugins.SetColumnWidth(0, 120) info.m_text = 'Description' self.lcPlugins.InsertColumnInfo(1, info) self.lcPlugins.SetColumn(1, info) self.lcPlugins.SetColumnWidth(1, 300) info.m_text = 'Author' self.lcPlugins.InsertColumnInfo(2, info) self.lcPlugins.SetColumn(2, info) self.lcPlugins.SetColumnWidth(2, 150) info.m_text = 'Version' self.lcPlugins.InsertColumnInfo(3, info) self.lcPlugins.SetColumn(3, info) self.lcPlugins.SetColumnWidth(3, 75) info.m_text = 'Enabled' self.lcPlugins.InsertColumnInfo(4, info) self.lcPlugins.SetColumn(4, info) self.lcPlugins.SetColumnWidth(4, 75) def LoadPlugins(self): """Update and load the data for the plugin list control.""" # Set up the plugins for each plugin entry point of dicompyler for n, p in enumerate(self.plugins): props = p.pluginProperties() self.lcPlugins.InsertStringItem(n, props['name']) self.lcPlugins.SetStringItem(n, 1, props['description']) self.lcPlugins.SetStringItem(n, 2, props['author']) self.lcPlugins.SetStringItem(n, 3, str(props['version'])) self.lcPlugins.SetStringItem(n, 4, 'Yes') dicompyler-0.4.1-1/dicompyler/preferences.py0000755000076500000240000004307611677672407021773 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # preferences.py """Preferences manager for dicompyler.""" # Copyright (c) 2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ import os import wx from wx.xrc import * from wx.lib.pubsub import Publisher as pub from dicompyler import guiutil, util try: # Only works on Python 2.6 and above import json except ImportError: # Otherwise try simplejson: http://github.com/simplejson/simplejson import simplejson as json class PreferencesManager(): """Class to access preferences and set up the preferences dialog.""" def __init__(self, parent, name = None, appname = "the application", filename='preferences.txt'): # Load the XRC file for our gui resources res = XmlResource(util.GetResourcePath('preferences.xrc')) self.dlgPreferences = res.LoadDialog(None, "PreferencesDialog") self.dlgPreferences.Init(name, appname) # Setup internal pubsub methods pub.subscribe(self.SetPreferenceTemplate, 'preferences.updated.template') pub.subscribe(self.SavePreferenceValues, 'preferences.updated.values') # Setup user pubsub methods pub.subscribe(self.GetPreferenceValue, 'preferences.requested.value') pub.subscribe(self.GetPreferenceValues, 'preferences.requested.values') pub.subscribe(self.SetPreferenceValue, 'preferences.updated.value') # Initialize variables self.preftemplate = [] self.values = {} self.filename = os.path.join(guiutil.get_data_dir(), filename) self.LoadPreferenceValues() def __del__(self): # Destroy the dialog when the preferences manager object is deleted if self.dlgPreferences: self.dlgPreferences.Destroy() def Show(self): """Show the preferences dialog with the given preferences.""" # If the pref dialog has never been shown, load the values and show it if not self.dlgPreferences.IsShown(): self.dlgPreferences.LoadPreferences(self.preftemplate, self.values) self.dlgPreferences.Hide() # Otherwise, hide the dialog and redisplay it to bring it to the front else: self.dlgPreferences.Hide() self.dlgPreferences.Show() def SetPreferenceTemplate(self, msg): """Set the template that the preferences will be shown in the dialog.""" self.preftemplate = msg.data self.dlgPreferences.LoadPreferences(self.preftemplate, self.values) def LoadPreferenceValues(self): """Load the saved preference values from disk.""" if os.path.isfile(self.filename): with open(self.filename, mode='r') as f: try: self.values = json.load(f) except ValueError: self.values = {} else: self.values = {} def SavePreferenceValues(self, msg): """Save the preference values to disk after the dialog is closed.""" self.values = msg.data with open(self.filename, mode='w') as f: json.dump(self.values, f, sort_keys=True, indent=4) def GetPreferenceValue(self, msg): """Publish the requested value for a single preference setting.""" query = msg.data.split('.') v = self.values if v.has_key(query[0]): if v[query[0]].has_key(query[1]): if v[query[0]][query[1]].has_key(query[2]): pub.sendMessage(msg.data, v[query[0]][query[1]][query[2]]) def GetPreferenceValues(self, msg): """Publish the requested values for preference setting group.""" query = msg.data.split('.') v = self.values if v.has_key(query[0]): if v[query[0]].has_key(query[1]): for setting, value in v[query[0]][query[1]].iteritems(): message = msg.data + '.' + setting pub.sendMessage(message, value) def SetPreferenceValue(self, msg): """Set the preference value for the given preference setting.""" SetValue(self.values, msg.data.keys()[0], msg.data.values()[0]) pub.sendMessage('preferences.updated.values', self.values) ############################## Preferences Dialog ############################## class PreferencesDialog(wx.Dialog): """Dialog to display and change preferences.""" def __init__(self): pre = wx.PreDialog() # the Create step is done by XRC. self.PostCreate(pre) def Init(self, name = None, appname = ""): """Method called after the panel has been initialized.""" # Hide the close button on Mac if guiutil.IsMac(): XRCCTRL(self, 'wxID_OK').Hide() # Set window icon else: self.SetIcon(guiutil.get_icon()) # Set the dialog title if name: self.SetTitle(name) # Initialize controls self.notebook = XRCCTRL(self, 'notebook') # Modify the control and font size on Mac for child in self.GetChildren(): guiutil.adjust_control(child) # Bind ui events to the proper methods self.Bind(wx.EVT_CLOSE, self.OnClose) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnClose) wx.EVT_BUTTON(self, XRCID('wxID_OK'), self.OnClose) # Initialize variables self.preftemplate = [] self.values = {} self.appname = appname def LoadPreferences(self, preftemplate, values): """Update and load the data for the preferences notebook control.""" self.preftemplate = preftemplate self.values = values # Delete and reset all the previous preference panels self.notebook.DeleteAllPages() self.callbackdict = {} # Add each preference panel to the notebook for template in self.preftemplate: panel = self.CreatePreferencePanel(template.values()[0]) self.notebook.AddPage(panel, template.keys()[0]) def CreatePreferencePanel(self, prefpaneldata): """Create a preference panel for the given data.""" panel = wx.Panel(self.notebook, -1) border = wx.BoxSizer(wx.VERTICAL) show_restart = False for group in prefpaneldata: # Create a header for each group of settings bsizer = wx.BoxSizer(wx.VERTICAL) bsizer.Add((0,5)) hsizer = wx.BoxSizer(wx.HORIZONTAL) hsizer.Add((12, 0)) h = wx.StaticText(panel, -1, group.keys()[0]) font = h.GetFont() font.SetWeight(wx.FONTWEIGHT_BOLD) h.SetFont(font) hsizer.Add(h) bsizer.Add(hsizer) bsizer.Add((0,7)) # Create a FlexGridSizer to contain the group of settings fgsizer = wx.FlexGridSizer(len(group.values()[0]), 4, 10, 4) fgsizer.AddGrowableCol(2, 1) # Create controls for each setting for setting in group.values()[0]: fgsizer.Add((24, 0)) # Show the restart asterisk for this setting if required restart = str('*' if 'restart' in setting else '') if ('restart' in setting): if (setting['restart'] == True): show_restart = True t = wx.StaticText(panel, -1, setting['name']+restart+':', style=wx.ALIGN_RIGHT) fgsizer.Add(t, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT) sizer = wx.BoxSizer(wx.HORIZONTAL) # Get the setting value value = GetValue(self.values, setting) # Save the setting value in case it hasn't been saved previously SetValue(self.values, setting['callback'], value) # If this is a choice setting if (setting['type'] == 'choice'): c = wx.Choice(panel, -1, choices=setting['values']) c.SetStringSelection(value) sizer.Add(c, 0, wx.ALIGN_CENTER) # Add control to the callback dict self.callbackdict[c] = setting['callback'] self.Bind(wx.EVT_CHOICE, self.OnUpdateChoice, c) # If this is a checkbox setting elif (setting['type'] == 'checkbox'): c = wx.CheckBox(panel, -1, setting['name']+restart) c.SetValue(value) sizer.Add(c, 0, wx.ALIGN_CENTER) # Remove the label preceding the checkbox t = self.FindWindowById(c.PrevControlId(c.GetId())) t.SetLabel('') # Adjust the sizer preceding the label fgsizer.GetItem(0).SetSpacer((20,0)) # Add control to the callback dict self.callbackdict[c] = setting['callback'] self.Bind(wx.EVT_CHECKBOX, self.OnUpdateCheckbox, c) # If this is a range setting elif (setting['type'] == 'range'): s = wx.Slider(panel, -1, value, setting['values'][0], setting['values'][1], size=(120, -1), style=wx.SL_HORIZONTAL) sizer.Add(s, 0, wx.ALIGN_CENTER) t = wx.StaticText(panel, -1, str(value)) sizer.Add((3, 0)) sizer.Add(t, 0, wx.ALIGN_CENTER) sizer.Add((6, 0)) t = wx.StaticText(panel, -1, setting['units']) sizer.Add(t, 0, wx.ALIGN_CENTER) # Add control to the callback dict self.callbackdict[s] = setting['callback'] self.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.OnUpdateSlider, s) self.Bind(wx.EVT_COMMAND_SCROLL_CHANGED, self.OnUpdateSlider, s) # If this is a directory location setting elif (setting['type'] == 'directory'): # Check if the value is a valid directory, # otherwise set it to the default directory if not os.path.isdir(value): value = setting['default'] SetValue(self.values, setting['callback'], value) t = wx.TextCtrl(panel, -1, value, style=wx.TE_READONLY) sizer.Add(t, 1, wx.ALIGN_CENTER) sizer.Add((5, 0)) b = wx.Button(panel, -1, "Browse...") sizer.Add(b, 0, wx.ALIGN_CENTER) # Add control to the callback dict self.callbackdict[b] = setting['callback'] self.Bind(wx.EVT_BUTTON, self.OnUpdateDirectory, b) # Modify the control and font size on Mac for child in panel.GetChildren(): guiutil.adjust_control(child) fgsizer.Add(sizer, 1, wx.EXPAND|wx.ALL) fgsizer.Add((12, 0)) bsizer.Add(fgsizer, 0, wx.EXPAND|wx.ALL) border.Add(bsizer, 0, wx.EXPAND|wx.ALL, 2) border.Add((60, 20), 0, wx.EXPAND|wx.ALL) # Show the restart text for this group if required for >= 1 setting if show_restart: r = wx.StaticText(panel, -1, '* Restart ' + self.appname + \ ' for this setting to take effect.', style=wx.ALIGN_CENTER) font = r.GetFont() font.SetWeight(wx.FONTWEIGHT_BOLD) r.SetFont(font) border.Add((0,0), 1, wx.EXPAND|wx.ALL) rhsizer = wx.BoxSizer(wx.HORIZONTAL) rhsizer.Add((0,0), 1, wx.EXPAND|wx.ALL) rhsizer.Add(r) rhsizer.Add((0,0), 1, wx.EXPAND|wx.ALL) border.Add(rhsizer, 0, wx.EXPAND|wx.ALL) border.Add((0,5)) panel.SetSizer(border) return panel def OnUpdateChoice(self, evt): """Publish the updated choice when the value changes.""" c = evt.GetEventObject() pub.sendMessage(self.callbackdict[c], evt.GetString()) SetValue(self.values, self.callbackdict[c], evt.GetString()) def OnUpdateCheckbox(self, evt): """Publish the updated checkbox when the value changes.""" c = evt.GetEventObject() pub.sendMessage(self.callbackdict[c], evt.IsChecked()) SetValue(self.values, self.callbackdict[c], evt.IsChecked()) def OnUpdateSlider(self, evt): """Publish the updated number when the slider value changes.""" s = evt.GetEventObject() # Update the associated label with the new number t = self.FindWindowById(s.NextControlId(s.GetId())) t.SetLabel(str(s.GetValue())) pub.sendMessage(self.callbackdict[s], s.GetValue()) SetValue(self.values, self.callbackdict[s], s.GetValue()) def OnUpdateDirectory(self, evt): """Publish the updated directory when the value changes.""" b = evt.GetEventObject() # Get the the label associated with the browse button t = self.FindWindowById(b.PrevControlId(b.GetId())) dlg = wx.DirDialog(self, defaultPath = t.GetValue()) if dlg.ShowModal() == wx.ID_OK: # Update the associated label with the new directory d = unicode(dlg.GetPath()) t.SetValue(d) pub.sendMessage(self.callbackdict[b], d) SetValue(self.values, self.callbackdict[b], d) dlg.Destroy() def OnClose(self, evt): """Publish the updated preference values when closing the dialog.""" pub.sendMessage('preferences.updated.values', self.values) self.Hide() ############################ Get/Set Value Functions ########################### def GetValue(values, setting): """Get the saved setting value.""" # Look for the saved value and return it if it exists query = setting['callback'].split('.') value = setting['default'] if values.has_key(query[0]): if values[query[0]].has_key(query[1]): if values[query[0]][query[1]].has_key(query[2]): value = values[query[0]][query[1]][query[2]] # Otherwise return the default value return value def SetValue(values, setting, value): """Save the new setting value.""" # Look if a prior value exists and replace it query = setting.split('.') if values.has_key(query[0]): if values[query[0]].has_key(query[1]): values[query[0]][query[1]][query[2]] = value else: values[query[0]].update({query[1]:{query[2]:value}}) else: values[query[0]] = {query[1]:{query[2]:value}} ############################### Test Preferences ############################### def main(): import tempfile, os import wx from wx.lib.pubsub import Publisher as pub app = wx.App(False) t = tempfile.NamedTemporaryFile(delete=False) sp = wx.StandardPaths.Get() # Create a frame as a parent for the preferences dialog frame = wx.Frame(None, wx.ID_ANY, "Preferences Test") frame.Centre() frame.Show(True) app.SetTopWindow(frame) filename = t.name frame.prefmgr = PreferencesManager(parent = frame, appname = 'preftest', filename=filename) # Set up the preferences template grp1template = [ {'Panel 1 Preference Group 1': [{'name':'Choice Setting', 'type':'choice', 'values':['Choice 1', 'Choice 2', 'Choice 3'], 'default':'Choice 2', 'callback':'panel1.prefgrp1.choice_setting'}, {'name':'Directory setting', 'type':'directory', 'default':unicode(sp.GetDocumentsDir()), 'callback':'panel1.prefgrp1.directory_setting'}] }, {'Panel 1 Preference Group 2': [{'name':'Range Setting', 'type':'range', 'values':[0, 100], 'default':50, 'units':'%', 'callback':'panel1.prefgrp2.range_setting'}] }] grp2template = [ {'Panel 2 Preference Group 1': [{'name':'Range Setting', 'type':'range', 'values':[0, 100], 'default':50, 'units':'%', 'callback':'panel2.prefgrp1.range_setting', 'restart':True}] }, {'Panel 2 Preference Group 2': [{'name':'Directory setting', 'type':'directory', 'default':unicode(sp.GetUserDataDir()), 'callback':'panel2.prefgrp2.directory_setting'}, {'name':'Choice Setting', 'type':'choice', 'values':['Choice 1', 'Choice 2', 'Choice 3'], 'default':'Choice 2', 'callback':'panel2.prefgrp2.choice_setting'}] }] preftemplate = [{'Panel 1':grp1template}, {'Panel 2':grp2template}] def print_template_value(msg): """Print the received template message.""" print msg.topic, msg.data # Subscribe the template value printer to each set of preferences pub.subscribe(print_template_value, 'panel1') pub.subscribe(print_template_value, 'panel2') # Notify the preferences manager that a pref template is available pub.sendMessage('preferences.updated.template', preftemplate) frame.prefmgr.Show() app.MainLoop() # Print the results of the preferences with open(filename, mode='r') as f: for line in f: print line, try: os.remove(filename) except WindowsError: print '\nCould not delete: '+filename+'. Please delete it manually.' if __name__ == '__main__': main() dicompyler-0.4.1-1/dicompyler/resources/0000755000076500000240000000000011700133245021070 5ustar apanchalstaff00000000000000dicompyler-0.4.1-1/dicompyler/resources/accept.png0000644000076500000240000000141511677672407023063 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ŸIDAT8Ë¥“ëKSaÇý;vÎvl dD!‚„P{$;š ż,KÓݽÒ6cØL‹2r^ÆH)-³ÔjsNmêÔæÖ”2qÙQB̽éBµat±oçìÅL#zà Ïçó{®qâþ' ø‰r§ê³=)LÆãýaéˆ8,u%2Rg¢>ݾW´« Ï›JË<É!G†›A ú–»¢é\lF‰ë$½THÒCÑ; تtæxRäêœÎÕ~Ø^^ƒi®2®ïXíCãLØø‘dŸÞ&Èñ¤3ÝIëÌM¬ ”¡Â_ÍÌèØ”³©ö…ýõ=\œª€ØB®‹¯“˜@æN2¨Æ²¢•9˜UÞSPLB1UõT!Ükƒ0x•p°’Üž#H¸Bb‚Œ1Ól†5Ø„ _1”Oä,˜å$O>Æß¸ÀµPd mÏa›kD|=ÉÄGí Vn£6 Ö[Ä®d‹桚(ÄØÚPþ±ùmÏ.Á0QŒ¾`'Fb#&ܧ6ú—»aô«Pë×âÓ×Qèý—·1Ø2[µ+z÷iô; ¨ù]ÐC17æ›Ð¾pºI9̾jD¾}ŽÂ›?7ayzeÎ,hXAK í^3¨*bk ©·ù@ì+wQ=!‡Ú}uÓåXz·€¶Ù”‡Âq:g쯺‘n= ª’Ø:Äd+_¸½³Gƒ‡ÌTŽæA;œÕ JÎÆ£¥.‡Š!PóÖ)5!Üö›H:¾ˆ˜Üep°’Ö€úÑ"œ–£•Ý‚…õÎ"ðKy¦w|Ê{Hš2!i‡í~3z_XÑ;o…ÅkBZK* ^ˆRô®Ÿ‰:OŠ(¡§jF å…*^˜­È°ÑS¥„诿ñ_ó gЬåyºÔcIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/book.png0000644000076500000240000000112111677672407022550 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ãIDAT8Ëu“¿jUAÆçäÞ›E,’ (HâM§‚‚à (>… v>€£ˆ•Ú(ˆ‚…±´ Q´ŒEþ€ÉÙÝ™ù,öDc¼waXv~ó}»3$®ß{°"é©`qY ¨Avx¡¤­oïÝ? 0 ^¸#87^^lÖn!í%A… ØÜúÅn¡Á)úUpu¼¼Ð\[]`ñäU¡¨U£V?~xžù¹WnÜåÒ‘ñʯ>~accE{*$bs{‡.eö¯ÞÍ`4àìù%Μ8Š;HA„p…Øí Ož­M÷àûú6£vH×u””±¨çÆ ƒáˆc‡f iÌ )%n^V8 ˜PLSàAI…T¼Úq‘{ˆ¹h°ìS±·PrXˆl{PB”âÌÐRæ5ÙBHds,©%7QÂ)¥ï9ÅbÚ/P/e¯"0 Ì„Yí…V nBˆˆøñŸÚ– —VQÓà.ÌEôQ}7ælHº´vðæõË÷·ëäíë&ñâë‡ç·ö¿|úÕ–»:Ç IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/chart_bar.png0000644000076500000240000000103511677672407023547 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<¯IDAT8Ë¥“»jVQF׉'j$Ê`“BðWb­v¦±ðìÒ –¢vv>‚¶‚`í¤´SAÄwRåòŸ½çnq‚DH$)fÍúf¨*.SãYÍWŸö+2Q‡ˆD=O><Û.M.nà™dGñùëáÅ š%ÉþcQl¬]á¸åÅËæ¨'ÝBÆŽšŸ ¨×/æ+fRæ”9Óí—ˆÝ óäújpØã?v “!‚!‚£‰xÑ$°€µÕâð¼eÎàß¿ܽÇqSº%“ÎúÕov@tHs0ç@ ±¤IbQ¬_Kr™<ßÛ-SGÕ°nˆcvaꀍr¼LºMùÙ’Ç[Oñ"Kãã—wŒÙN6‹€*¸ãÍfƒ¿€¤šcnüüý 5aûæÚ²3æÔçÁÖf3r™tý-0WšNˆ bB_vÆšœŽàN¶¤2P+J@\hÒÔ•6 cN'›ï?€p§š£^ÜÚXÅÄŠE]Ylnãa˜+Ò„áÍÎÛJsJœ² ,x¿þ„ @“ §*ŠÍÅnIxžd$Ãeßù´Š¤=ž¸OIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/chart_bar_error.png0000644000076500000240000000123711677672407024764 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<1IDAT8Ë¥“KˆÎaÆß†>Ã0#”…q©1#Ô¹—2 %Jv“+ÛÉmÅN²·±‘…R”Rˆ ÅŒk(Sî¹ ’ñ}ÿ÷vÞs,>iʤ)§N§ÎâwžçÔS13þ§šÇZ8÷Ų*Q g%ŠD9µgNe\€•õÝ-ˆ*j Ù¸882~.)1+_~)Ó&5Qs:~@Ý QŸÉFh.øédl€ÜÛø¢*–KB9?!>I”ê„̈ÏÿP°©T©äL%g~>R‚.dR†®É8ºê*/¯½Ÿµ ÷Ìðh@aI@`àˆPsŸ”2*YJ7_fuÏJ4¥}[‘ŠI ?‚’₲ºõ&sæ­`êìe¼{z®ÿÐå¾þw¢$Ÿ!Q¨ >€÷#µºâ£1Å>²¬zƒÖmä‘Kt,ÝÅÚÙ¶ ­‹vâëžBÝïËá7@q‰˜2+«WèX²ü}îž>Ë”6ÇÄáç|zq(W÷Z6®â\ZWfä'¬éˆ´¶—hx¦äÚó7öÓ9üŒäk VºQvŠèÑó´uô åcLËwt¡ñÕêÝÛ˜övW -¤k×Áú ÂæÙ·èî\LKëWL¾A¥‰{†Eý3Î¥ýͦÇHåpïÓ$X,e,eÖõÝgãîã4¯1ùŒNlAÓ¤.>?}È™“'¨ŒçÛÇ–µ¬-¦†©Žjû3QÅÌîýE‚«ßipIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/chart_curve.png0000644000076500000240000000130611677672407024130 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<XIDAT8Ëcøÿÿ?%L˜”û=ýnEØ×KóŸtw9²äûˆ¨. ~ð><ò2Çá6 ôè‹öÆö÷þ:öÜã‰}ô 8üÉ?èŸú|ˆñ# VÂ4 äȵ†GÌ:õ~4>ØöÕαý‹ƒó¿ÏÎn60…ŸÝ<•>zx|çðÍ{òƒŒ‹ÿWro•.{Ø•ž°þ×W#‹ïßÌ,§"Ûd\ÿ ×¼ñþ_˦û¿mÚîÿ´ë¸ÿn€Qñ¡ÿ0…¯µ,ÿ=4tý‰¬Y!ÿÁ •¢{¿Õ‹ïÍÿndªúÍØìÿ²èŽéŠ€ ø-&µô§¤Ìo[¿]ß¹LâMzÈ,t‡`â½¢Iwë`þÔÒ=ð][ï܃Â}ÿ« ôÿò þûÃ'ÔÆñ …!üÁ¦ð™Ãüc »Žú?U~! Èßûÿ?#«ÿ&¶;(!p§Ük‘Ãtï%6g<—Xu[l7è·¸ô¸úy»ÿÿd` ùÍÀ"Sà5ÓÒËc¦å YæÜf[¬uk¶Ïe¾é§Æñ y k¥àèåîü?GTæٱÃhšc§ÑG‡.ÃE=†<0q‡~ûIúï1Ònööÿ–%:’¥Ú3ÍKué·e:èþ¶¨×æ°hÒ¹gÖ¢=Õ€¬mÿ24ý 3ÔÝÔ,]485 kxÈ ©:ÙFùêRQ:¦Å<ò2Çá6 ôè‹öÆö÷þ:öÜã‰}ô 8üÉ?èŸú|ˆñ# VÂ4 äȵ†GÌ:õ~4>ØöÕαý‹ƒó¿ÏÎn60…ŸÝ<•>zx|çðÍ{òƒŒ‹ÿWro•.{Ø•ž°þ×W#‹ïßÌ,§"Ûd\ÿ ×¼ñþ_˦û¿mÚîÿ´ë¸ÿn€Qñ¡ÿ0…¯µ,ÿ=4tý‰¬¹hÒŠ£Û×Vü³¯=»ü»‘©ê7c³ÿË¢;¦# (:6à·˜ÔÒŸ’2¿mýv}çJ|0‰7é!³|æÕ={×äý{{{õÿÛÛÂz@ê~jéø®­÷n€Aá¾ÿÕúyÿýájcˆxÐÂþà?Søƒ¿ymsþ½¾¾äÿï/wþ__øþÚZS°!Š*¿äïýÿŸ‘Õÿ?Ûd§;æn.àÇ×[þÿxÖ÷ÿÝÝ­ÿ//óÜti±;ëoqé pôóvÿÿÉÀò›E¦Ùo–•÷Ò%nßßÜ\óÿÇã¦ÿ‡:Œÿÿz»æÿùyÎÿÎÍv ºV n€^îÎÿsD5à6wMË™dùõþÞêÿ?_-úÿíNâÿCí†ÿ¿?,ÿÿáÞ¶ÿ§¦X];9Ñ‚n€nööÿ–%:’¥Ú3ÍKuÙ”é¼=8Ëñá{`À}Pòÿë­¾ ôÊ„ÿNú´Ë¨a@Ö¶ÿFšþ†ênꖮǦÛ<<Ô÷ÿç‹é`Û¿ÞŽzÁL»—õÿÛóÿ6ë¾:ب­6@;sËíŒÍÿµ26ñÆÿ;z\ÿ{s÷ÿÏw{ÿÿx ƒW«‘ðÚÿ¿?_ùÿäÄ’ÿûªÕVcÍa@ç½9Ònðíp«þ·Cͺ߀6};P¯ùm­Æ7 ¦o{+U¿í-Wþ¶§Lé0ä_ -¿“IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/contrast_high.png0000644000076500000240000000066311677672407024464 0ustar apanchalstaff00000000000000‰PNG  IHDRµú7êgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<EIDAT(Ï…‘¿+ÄqÆ_w.NœA †Œ$#%uYt%Õìö+ÿ‚E©LçN±(Ö+e‘dÀâGWŠîÂD¹‹/÷Ã÷ýy ÷u#ïgy×ëéé©'$þ¾Èï³ÝOÚÍ1¬Ý«@fù5Bˆ|jÏ{TS’ÔPY»ÞæB‹´pâT59ÕUUEjêKkšI ¹ÞXq>áÃá0Ä û<”Üø‰Ò“ñ(ÖVžb kÂà’CÔñÛÚ&Ë7·ô¡$„A|bøFŽ,ß8®ˆâ&  ‡#ÏÂ0„!Z çuº0|rdÛI#xpa°Ãgºñɳ…=ŒQªØa+aãì©A‘l€ ŸE*¼”´!™ÄÛq™®¹#Bœ1<^ñf/ŽL§´3Ø££Á;/ž–. m€©~¥™³QÕ(QPæ2+ôßÜ?i¾8eVIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/dicomgui.xrc0000644000076500000240000002170511677672407023440 0ustar apanchalstaff00000000000000 wxVERTICAL wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL 5,0 wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL 0,5 1 wxALL|wxEXPAND|wxALIGN_CENTRE 3 2,0 wxALL|wxEXPAND|wxALIGN_CENTRE accept.png wxALL|wxEXPAND|wxALIGN_CENTRE 16,16 2,0 wxALL|wxEXPAND|wxALIGN_CENTRE wxALIGN_LEFT wxALIGN_LEFT wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE 0,5 455,300 wxALL|wxEXPAND|wxALIGN_CENTRE 0,5 wxALIGN_CENTRE wxALIGN_CENTRE 5,0 100,15 100 75 wxALIGN_CENTRE 5,0 wxALIGN_CENTRE wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE 0,5 error.png wxALIGN_CENTRE 5,0 wxALIGN_CENTRE 5,0 80,22 1 1 99999 wxALIGN_CENTRE 5,0 wxALIGN_CENTRE wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE 0,9 wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 0,5 1 0 wxALL|wxEXPAND|wxALIGN_CENTRE 3 0,5 Open Patient 1 dicompyler-0.4.1-1/dicompyler/resources/dicompyler.icns0000644000076500000240000050514111677672407024150 0ustar apanchalstaff00000000000000icnsŠais329„ÿ$'‹ÿ@‰ÿA<‰ÿN <‰ÿ\ 4ƒÿ=¡´¼¦Aÿg -ÿÿx´²—¥úúùÐߢŒÖÅòÿÿ¶>q±÷ú¿ÆêÜ´Æÿÿ؉Sÿÿ@hw¤ØöïÉ¡•pDƒÿ3–Éåà¼v=…ÿ~b¶²”£U„ÿ Aj`^Z¿¿p@ƒÿ }``C3ÿ²¿»Z‚ÿ _[`FDÿÿ+Ÿ¿–;ÿP_L=ÿs˜µ]‚ÿH@ƒÿzdÿÿ„ÿEE‹ÿEE@‰ÿAFE<‰ÿaGG@‰ÿsGG?ƒÿ`iŠ€Aÿ}GG=ÿÿhœya€|)oYC>Q¾¶¿¿>Nd{|U=~6?…¿¿¦pLÿÿ@JLD8#/=…ÿ¥°L?@3 1„ÿ A¦ÉÁKW @ƒÿ §ÉÆ^3ÿi,‚ÿ ²ÈfDÿÿ+?;ÿ~Ãw=ÿ:3‚ÿc@ƒÿC/ÿÿ„ÿ‹ƒ‹ÿ­¤@‰ÿA¼°<‰ÿ‹ÊÄG‰ÿ¢ËËZƒÿ= XZAÿ¯ÇÊ`ÿÿ;V*#aT?BCy%>+=~6> $;ÿÿ@-"38#<)(=ƒÿ3H<.2>D=…ÿZ 4>?I¢U„ÿ A4-†¾¾p@ƒÿ V*3ÿ±¾ºZ‚ÿ A (Dÿÿ+ž¾–;ÿ'$=ÿs—´]‚ÿ0@ƒÿzdÿÿs8mkuzðó üù";ÿþLpÿÿƒV±ŒD›ÿÿ¬nt»ÿÿÿ÷éûÿÿøßîýÿÿà¯ïÿÿÿÿÿÿÿÿÿÿöÆ? ™ðÿÿÿÿÿÿö¾&ùÿÿÿÿønÿÿÿÿÿûeèÿþÛÖÿÿé”ÿþÓ ÕÿþñÿÚÙÿìtþÜ Ýüytpil32MŽÿ™ÿ"(™ÿ! 'I˜ÿ ">—ÿ B—ÿ5 @—ÿ9 @—ÿS 8—ÿ_ 2—ÿj -‹ÿ’†A†ÿu *U†ÿ AqÿÿÁõͳ±ºAÿ€ (Fÿ A¼²»ÖÑ@tè‚úâ¾Îɺ€ƒ¢—S?“¼ÌÊÃì‚ÿw€È…ú ï·šÂËÍÆ¹Ÿƒ×Ûù„ÿó„@Bp«ù‚ú ô˜ÆàêâÜ͸–“„ÿ ç•\?ÿÿ€?dˆÕ€ú±µÕ€ñéÚħ{ûÿ¹}Q@„ÿB\sÜ¢¾ØéöøïàÉ­ÕÿÑŒnIDˆÿ@Uk}»ÕæòôìÝǪƒniBGŒÿ@m±ÉÛåçàÒ¼¡v\A9ÿ …“¸ÈÒÕÎÀ¬kXÿ n”kœ¯¹ºµ¨“uœ\FŽÿ€ž^_j†˜š•ˆr’¿˜\3ÿzj€`^anx„³€¿vMŒÿN‚`CCBÿ¯¿bD‹ÿ„^€`IEAÿÿ¤®€¿œRŠÿ_`RD>ÿ3«·€¿qD‰ÿl€`[C@ƒÿN¥€¿¹Yˆÿe]€`F?…ÿcš€¿?‡ÿU€`H@‡ÿd–€¿e†ÿM\`LA‰ÿoœ¿žZM‡ÿGNA‹ÿu~K@‰ÿCŒÿU„ÿŽÿ™ÿFEEA™ÿ€GAI˜ÿHGGA>—ÿHGGAB—ÿFIGGB@—ÿPIGGB@—ÿkHGGB@—ÿxHGGC>—ÿ„€GC>‹ÿ]cA†ÿŒFGGC?U¸¬»„¿·m@ANbƒ| zJ=n~16<>>[„¿ ¯wR?ÿÿ€?JTn€| Z?H¤X!+6>>A½¿gK@„ÿBHLYqN>8, #3<>>ž¿Ÿq]EDˆÿ@FI>>9-'4=>;Z[ZAGŒÿ@A><5.-29>>;QA9ÿ ŽC><99;>><1.ÿ”Ãbƒ><6"CŽÿ€ÄÀÄ^@<=<:4 )3ÿ£´€É‰>79C€<Œÿb²ÉlUBÿa<&D‹ÿ­Ã€ÉYAÿÿe€ 6Šÿt¨€ÉœZ>ÿ3k€!D‰ÿž€É¸\@ƒÿ;]€1ˆÿ¾ÉÉÅc?…ÿ;A€?‡ÿšÉÉÈi@‡ÿ:&.†ÿt¶ÉuA‰ÿ<4M‡ÿbuA‹ÿ;"<@‰ÿCŒÿU„ÿŽÿ™ÿ¨¨t™ÿËË{I˜ÿ¡ËË…>—ÿ§ËËŽB—ÿa·ËË–@—ÿ…¿Ë˦B—ÿ¢ÀË˯Q—ÿ®ÃË˲[—ÿ·ÄË˳`‹ÿ*BA†ÿ¿ÅË˵aU†ÿ A7ÿÿ#NgOAÿÇÄ­¹¶aFÿ AD`A@/‚FwvhN^GDGHI?Fbqi8 ‚.)… /G=;;=>?K] „*@?-‚ :=n~16<>>&„ !6?ÿÿ€?2 € ?H¤X!+6>>9'9@„ÿB4%)>8, #3<>> *9-'4=>;$"-?GŒÿ@:><5.-29>>;3A9ÿ ^@><99;>><;XÿJj(ƒ><;\FŽÿ€|(<<=<:<{¾—\3ÿT&€-4A`®€¾uMŒÿ;W4Bÿ¯¾`D‹ÿ^€2Aÿÿ£­€¾›RŠÿ:€0>ÿ3ª¶€¾qD‰ÿ?€-@ƒÿN¤€¾¸Yˆÿ@,?…ÿc˜€¾?‡ÿ)@‡ÿd•€¾e†ÿ( %A‰ÿo›¾ZM‡ÿ2'A‹ÿu}K@‰ÿCŒÿU„ÿl8mkœáá¤Óÿÿæçÿÿí%öÿÿó:ÿÿÿùPKÿÿÿþswÿÿÿÿž ÿÿÿÿÁÀÿÿÿÿÙ9@Úÿÿÿÿê-ÑÿóÚ¾DìÿÿÿÿñJ’¿Ýöö#$ûÿÿÿÿÿÿ÷çÆŽ^þÿÿÿÿû5J”Êéùÿÿÿÿÿÿ”SöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿþÌWÒôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüé°9BÆóþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûéš,2²òþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúå$ïþÿÿÿÿÿÿÿÿÿÿÿùÜiÛÿÿÿÿÿÿÿÿÿÿÜT ÞÿÿÿÿÿÿÿÿÿÿÈWþÿÿÿÿÿÿÿÿÿÿúGåÿÿÿÿÿÿÿÿÿÿÿÿÜ~ÿÿÿÿÿþõòÿÿÿÿÿûi ðÿÿÿÿýÉÉÿÿÿÿÿè£ÿÿÿÿýÒ'×ÿÿÿÿüŽ#öÿÿÿýÚ1âÿÿÿÿí¹ÿÿÿþà< êÿÿÿþ¯CüÿÿÿåIíÿÿÿñ1ÈÿÿÿçW+ðÿÿÿŵüÿèf;óÿö¸ {âuNâxit32;±ÿÿÿÿÿÿÿÿÿÿÿÿ«ÿIˆ@ñÿ=‡1=f[€íÿF6‡ 6adkìÿC2‡ 2_chfìÿ@,‡ &Zdeqìÿ?(‡ Ucfbìÿ;%‡ Ldciìÿ:!‡ Eddfìÿ> ‡  @de`ìÿC‡  >dedëÿUH‡  9ddbëÿDG‡  4aedUêÿ@E‡  ,]demêÿ>C‡  #Yeb]êÿ;D‡  Qdcfêÿ:E‡  Icbcêÿ@E‡  CdchêÿJB‡  ?dcbêÿT@‡  ;ddcéÿG[?‡  8ddjéÿ@]=‡  1`ddfÿàßà¿f[àÿAa<‡  )[deUðò€õ׃_]Þÿ>f9‡  "VddÀôí±‡ÃóÔccfÝÿ:m8‡  Mddåõw€-©õˆddfÝÿ;t8‡  Fddéô7--0kõ¢dfk€ÜÿIr6‡  @ebéó-[õ©€cmÜÿXo5ˆ =ddçó-[õ©edc]ÛÿIeo3ˆ 9ccæó-[õ©ddejÛÿ@pk1ˆ 4adäó-[õ©€djÛÿAyi0ˆ .^dáó-[õ©€djÛÿ>…g/ˆ 'YdÞó-[õ©€djÛÿ:”e-ˆ $SdÜó-[õ©€djÛÿ9¤c,ˆ "JdÙó-[õ©€djÛÿB­a*ˆ !Cd×ó-[õ©€djÛÿR­_) ˆ !>dÔó-[õ©€djÚÿ_«]( ˆ !9dÒó-[õ©€djÚÿ@k¨[' ˆ  5dÐó-[õ©€dj­ÿTJAU¦ÿAu¥Y% ˆ  0`Ðó-[õ©€djÿ‹ÿ Ua»‹ib]K?U¡ÿA¡W% ˆ  +\Ïó-[õ©€dj˜ÿDK\_ZLU‰ÿFž€ú ÷×®‹phbL=Uœÿ=Œ T$ ˆ  'WÏó-[õ©€dj“ÿ BL^cnŒ·àþØ^eªˆÿ[àƒú ÷ì߯¬‘yohOA@—ÿ:S# ˆ  'NÏó-[õ©€djŽÿ fDN^hw‘°Ììøþÿ`cb‡ÿb‡ú øïáØÔŲ‡ypP@@’ÿ<«šQ" €   € (GÎó-[õ©€dj‰ÿ TBN`o›³ÈÙßîù…ÿŠ[b‡ÿA™ŠúøðåÚÖÔÓʼ«–†tQ@@ÿJ­—P!/ˆÄØèôõãÆ¤~R&@Îó-[õ©€djƒÿmDPdx‹¥¼ÊÖØÛâðûˆÿÔ\dU†ÿYÜúùòçÝØÕÔÓÖÑɽ¤‹tQ?3ˆÿY­–¥ÙôõóâϽµÉÝñõõå†+'=Îó-[õ©€djÿUDPc|š¶ÈÓØ×רÞäóü‹ÿþ_dU†ÿ`’úöêàÙÕ€Ô ÙßßÞɦ‹wV=3‚ÿ@fÉëõë²s6ƒ- .[›ØõÞx@Íó-[õ©dcWSg~›ÂÙßÞÚ×Ö×Ûàèõýÿƒ\f…ÿC’•úøîäÜ×ÕÓÔ×ÛâëìáɧyZAUŒÞõï™7-0…-/--gÖõÙáó-[õ¨m–ÃàîêâÝØÖ×ÙÝãë÷“ÿÎ\c€„ÿXØ™úòèàÙÕÔÓÕÚßæñòìâʳãõ¶E-.Š-//€ëõó-[õµ•âæßÚ×רÚßåðü–ÿý^g]‚ÿUÿ_ú÷ëãÜ×ÔÓÔ×Üãëðóõ‘-0ƒ-1/-//ƒ-.Páç- Mõ¸Ï×ÖÙÜâêô›ÿ}\`ƒÿUV¦ôŸú ðçßÙÕÓÓÖðòz‚- .-.Z{˜„n;-/-/@Â-Lõ»ŠÐßæîùžÿ´Ye€ƒÿ@R\â úöêâÛëõ~ƒ-?¢åƒõóÄm-0€-14-Lõ»åþŸÿÌm]`e`ƒÿ€cd\[|ÎîŸúùóõ¤-/.‘íõÜÁ¥ˆ}˜´ÏíõÄI.€-0-Lõ¾“ñÿù³e]beddfƒÿ lced]\mºãðœúöõÇ0.--03¹õë‘hlr|†€rkpÇõé_…-Lõ¾“ñšÿ úå_]bddedd€„ÿ ffdedd`\c¥Ôãñ˜úùñõS/--/2ÉõÒuo”±ÀÇÆÄ»°—p¡ðñj.ƒ-Lõ¾“ñ—ÿúëÓ‹\\€d€cb€†ÿ€qdbdcdaZ]“ÄÕãò•úôõµ‚-¯õ¶jz°ÕÛÙÚ×ÒÍż­|êëK0‚-Lõ¾“ñ”ÿûìÝÀy]\dcdcfc]€Šÿmjdfcdc\X‚²ÇÕäò‘úøðóM0--.{õØkŒÈåéæâáßÛÕÎÆ»¯‰–õÓ.‚-Lõ¾“ñ‘ÿüíÞÏ®k_^ddedfb`ÿffcdedd\Yt¢¹ÈÖæóŽúôóÈ-3áòÎïóïëéèåàÛÕÌù¬|Äõ~.-Lõ¾“ñŽÿýîßЛb\`€dcbdU“ÿ€iadedd]Yg’¬»È׿ô‹úòõ.€-‰õ¬o¸éõóððïíêæáÙÑǼ²Ÿvéß.-Lõ¾“ñ‹ÿþîßѱŠ\\bddeff^€—ÿU[gg€e ^[_„ž¬¼ÈØåõ‡úùðõU/€-Ïìj”ÚìðñòôôòîêãÝÕËÁ¶¨†»õ^0€-Lõ¾“ñˆÿ ýïàÒô¡{[]€deebfUœÿ€]`c€d a[Yuž­¼ÉØæõ„úöìï.€-?ôÓpÄâêïòõööõñìæß×Íø«œ˜õŸ-Lõ¾“ñ…ÿ þðáÓĵ¦’oZ]dcbj€¡ÿUdgdcdc[Tk€’Ÿ¯½ÊÙçöúóêÝ€-/iõ»{ÎàçíóöùøõòíçáØÏŬzôÂ-Lõ¾“ñƒÿ ðâÓŶ§˜„d[\‚dff€¤ÿmdcddec[Scr„’ ¯½ËÚè÷úñîË€-1{õ¢‹Òáçíòö÷÷öòíçàØÎù«xÜá-Lõ¾“ñ€ÿ ñãÔÆ¶§™Šw\[_dbfm¨ÿfYb€ce\T[eu…’¡¯¾ËÛßó¹€-.õ‹ÒÞåëðóõõóðëåÞÖÌ·ªœ}Öæ-Lõ¾“ñòãÕÅ·§š‹|lW]a€dccaf­ÿU`dbded]€Xhv…“¡°¸ð¿€-.ˆõ“¬ÏÛáçìïññïìçáÛÒÉ¿´§™€Öæ-Lõ½‹ÌƸ©›Œ}mbU]cdeefai€²ÿUbcc€d _YTW[iw†ãЀ-0võª¢ËÖÝâæéëëêçâÝÖÎź°¢•zãÜ- Lõ¹~¤›~o^_U]cegmU¶ÿ€fcgcdca[RWV[jÕâ€-.`õÃŽÄÐ×ÝàãååãàÝØÐÉ¿¶ªƒõ¼- Lõ¶q|oaU[W]c€dc`]€»ÿ `bfeedc\PYVÃó2€-5ñÚ¼ÉÐÕÙÜÞÞÝÙÕÐɺ¯¤–‰¢õ—-Lõ²dXWXZ\eecdg[U¿ÿ Uhbdedc\S¥õc/€-Ãñ|´ÀÇÌÐÓÕÕÔÑÍÈÁº±§œÃõQ.€-Lõ°aW[_dedded€Âÿ €^ffddX–õœ€-.xõÄ™·½ÃÇÉËËÊÇþ¹±¨ž’†…ðÒ‚-Lõ°bbcbYfÇÿUf`b^´åÕ-.Õõ™©³¹»¾ÀÀ¿¼º´®§“ˆ{Öõm/- Lõ±ddca``UÌÿ €E¦½Âõ^0--/iõ懦¬°³´µ´±­¨¢š’ˆ~«õŃ-Lõ°€dX€ÎÿAmÅÀŒðÈ‚-™õÏ’ž¤¦€§ ¤ ›•„|ñã@/‚-Lõ°bddhÏÿG²Å£u¶õf0€-.µõ蛓—™™—•’‡€|ÄõêY/ƒ-Lõ¯_dd^€Íÿ A~Å´‡ioïÙ6/--..žõôºŠˆ‰ˆ†ƒ|“äõÛK/„-Lõ¯`\dcjÍÿ N»Å“p_a›õ¼.‚-qÞõõèÍ´«ÂÛòõó¥:0€-0-Rõ§]RccbUËÿ BŽÅ¢zc^`b¼õ”‚-/2Âó‚õߣO-.€-/?-[õ©`UZdc`ÊÿAUÁ³…i^``agØõ–./- /-7Qem[E--/-.MÚ- [õ«eQObdd€ÉÿCœÅ’p_‚`kÑõ±2/„-/0„-.-cêó- [õ­lcQXeefÇÿAaÅ¡xc^‚`ae¾õÒa-/‹-: òõó- [õ­ss[Q_df€ÇÿEª±„h]ƒ` aac¤îõ¼P--/„-/-6ˆéóÄÊõP.-.ƒõ z…nTTcebÅÿAq‘o_…` a`co¹óõØš]A0€-8K|»ïôŇtšõ¯C-QÐ€eT\ddqÅÿHµžwb]‡`abdx®Û€õôäÛî€õðʉtw~‘Éõõäõó¦w‘¨“x^Recd€ÃÿA‚¯‚g]‰`'a_]^]p™¬¼Ëλ¯›„rqv–®·£¡ÇÓÀ‹t}¦¼¥ŠpZ[ddjÃÿN¹Žn_Š`_WPNOT[`€dbq€ƒ||Œ«º½¾·‘{wvw~˜¹¾·œ‚hPccfUÁÿC‘wb^Š`YPG?:CUec€d]‘ÂÆ²©­¿¸£“Ž–¨¹€¿°•|`YdeZÀÿAXªg^Š`[RIA8>NddcdeddX²àз¬°€¿¾¼»¹º½¾¿§ŽsQadef¿ÿE›m_Š`]ULC:=Lcdcdfcc\eW¾áʳ¬´Š¿»¡‡hWdd]½ÿAg›ub_Š` WNE=3CÎó-[õ©€djÿUDL\t‘¦²·¶±­«­¯¸½‹¿¾WdU†ÿK’|{wvw{ˆ‘œª·Â±–}iK=3‚ÿ@qÒìõë²s6ƒ- .[›ØõÞ{DÍó-[õ©dcWM_v’³ÆÉº³¯¬¬­±¹¾¿n\f…ÿC[•|{xvwy~„˜£²ÂÌÆ²—kPAUŒàõï™7-0…-/--gÖõÙáó-[õ¨kŽ´ÏÝÔÈ¿¶±®¬¬®²º“¿ŸXc€„ÿIq™|ywwx|‚ˆ’ž«ºÌÍÌdz¥áõ¶E-.Š-//€ëõó-[õ´ÐÎû´°­«¬¯µ½–¿¾Vg]‚ÿUÿK|{wvwz~…Ž˜¤³ÃÒïõ‘-0ƒ-1/-//ƒ-.Páç- Mõµƒ±²­¬«®²¸›¿j\`ƒÿUGazŸ| ywwx|‰›ëòz‚- .-.Z{˜„n;-/-/@Â-Lõ¸|¦­°´»ž¿ŽVe€ƒÿ@IMZs |{wvzÖõ~ƒ-?¢åƒõóÄm-0€-14-Lõ·|¯¾Ÿ¿`X`e`ƒÿ€cd[NUlw |Áõ¤-/.‘íõÚ¾¡…y•±ÍìõÄI.€-0-Lõ¹€·¿¼ŒZYbeddfƒÿ lced]PPesxœ|”õÇ0.--03¹õêŽb`_[WY^akÆõé_…-Lõ¹€·š¿ ¼­~VZbddedd€„ÿ ffdedd`RL^msy™|ÐõS/--/2ÉõÏq_SF@>>?@EM^žïñj.ƒ-Lõ¹€·—¿¼±¡qVZ€d€cb€†ÿ€qdbdcdaSLXgnsy•|•õµ‚-¯õ³d`e`J5689;>?BWzéëK0‚-Lõ¹€·”¿¼²§”gV[dcdcfc]€ŠÿmjdfcdcWJRahnsy‘|{ÊóM0--.{õÖde~ŽyR02469;=>@P‘õÓ.‚-Lõ¹€·‘¿½³¨ž‡^X^ddedfb`ÿffcdeddYKO[cintzŽ|}îÈ-3áñ{d…·ªyF*,/259;>>@TÁõ~.-Lõ¹€·Ž¿¾³©ž•|WV`€dcbdU“ÿ€iadedd\MKT^diotz‹|šõ.€-‰õ©ao¶[&#%).36:<>>Cgèß.-Lõ¹€·Œ¿³©Ÿ•‰pRYbddeff^€—ÿU[gg€e ^PIQY^dhot{ˆ|¼õU/€-ÏëdZn„}\+$*/49<€>J¸õ^0€-Lõ¹€·ˆ¿ ¾´© •‹~fTZ€deebfUœÿ€]`c€d aRGJSY^diotz„|{Ðï.€-?ôÑ_HUWF#'.38;€>?ŽõŸ-Lõ¹€·…¿ ¾µª¡–Œt]R[dcbj€¡ÿUdgdcdcSEGMTY_eiou{|{ÚÝ€-/iõ¹Z:2,% %-37;€>=aôÂ-Lõ¹€·ƒ¿ µ«¡—Œ‚wjVT\‚dff€¤ÿmdcddecWGEHOTZ_ejpu{|zæË€-1{õŸT;3-&&-38;=>>=PÛá-Lõ¹€·€¿ µ¬¡˜Œ‚xnbRV_dbfm¨ÿfYb€ceYHDCIOSZ_fjptñ¹€-.õ‡N:4/)##)/48;€>=HÔæ-Lõ¹€·¶¬¢—‚yndZNXa€dccaf­ÿU`dbded\MD>CIOTZ_eí¿€-.ˆõŽH:61-(#!!#(,169<€>=EÔæ-Lõ¸z—ŽƒzoeZSMYcdeefai€²ÿUbcc€d _PC??DJOUßЀ-0võ¦K<841-*((*-047;=>>==?FÏâ€-.`õÁR>:8520//0258:=€>=¾ó2€-5ñÙY@=;86544568:<><:”õ—-Lõ±_LJNS\eecdg[U¿ÿ UhbdedcXHõc/€-Ãñi@>=;:9989:;=>=>>=;:¾õQ.€-Lõ¯]PV_dedded€Âÿ €^ffdd[¢õœ€-.xõÂH€>=<==>=;:cïÒ‚-Lõ¯abcbYfÇÿUf`bjÍæÕ-.ÕõŠA‰>=<:?Ôõm/- Lõ±ddca``UÌÿ€OÆÙÈõ^0--/iõåZ?€>=‚>=<<:8žõŃ-Lõ¯€dX€ÎÿA}äݤñÈ‚-™õÌW=ƒ> ==<;98‡ñã@/‚-Lõ­bddhÏÿSÓäÌ¥Àõf0€-.µõç~=<==€<;:8E¼õêY/ƒ-Lõ¬Ydd^€ÍÿA“䨽¬™ðÙ6/--..žõô°T;€:98>tâõÛK/„-Lõ«RWdcjÍÿ [Ûäó´´®õ¼.‚-qÞõõæÆ¢“¶×òõó¥:0€-0-Rõ¡Q>ccbUËÿ F©äÌ·³Á›Åõ”‚-/2Âó‚õߣO-.€-/?-[õ¡N,Pdc`ÊÿAbà×¼³ºÉȼ’Üõ–./- /-7Qem[E--/-.MÚ- [õ K'6bdd€ÉÿL¼äô¶€Édz‹Öõ±2/„-/0„-.-cêó- [õŸI#$IeefÇÿApã̶³ÁÉŪ‚ÅõÒa-/‹-: òõó- [õG1_df€ÇÿQÍÕ»³»‚É ÈĪ…¯ïõ¼P--/„-/-6ˆéó¸¿õP.-.ƒõ‰E"AcebÅÿA…á´¶„É Èű‹„ÁóõØš]A0€-8K|»ïô½lR~õ¯C-QÐí`?,ZddqÅÿUØÊ¶³Â†Éƹ—€ˆµÝ€õôäÛî€õïÁoROF4®õõäõó’P3 >ecd€ÃÿB›Õº²¼ˆÉ(Ȫ€nftœ¯¼Ë骕x_WPF. »Ë´qSI'TddjÃÿ\ÛÁ³·ŠÉÆ«r^[ba€daitueU8 3JOPOI+ :ccfUÁÿJ²É¶´ÃŠÉ³™|bN\[ec€dXu™’]1€ /5- "MdeZÀÿAfÑ»³¼ŠÉ»Ÿ„hNZWddcdeddMƒª‹Q& ‚5adef¿ÿPÀÁ³¸ŠÉ¦‹pTYVcdcdfcc\eG‰ª~FŠ Fdd]½ÿAxȵ´Å‰É È­’w\WScddcf€€ÿ`cHŽªp;Š 1^cem½ÿR¹³¾ŠÉ´š~cUSbe€d€ÿ q_I“£b2Š@€d»ÿA‹¿³¸ŠÉ¼ …iTRb€dbUƒÿj`L˜“V* Š *Ycgf»ÿXÁµµÇ‰É§ŒqXR`€df@…ÿb[N„K"Š >defU¹ÿH™¹²ÀŠÉ ®“v]R_ecebfˆÿ`[T vAŠ $Redb¸ÿA^¼²¸ŠÉ µšcS^ded`f‰ÿ€kXX¤j6Š9cca@·ÿM¤´µÇ‰É¼¡†iU]€daU‹ÿ€hV\˜].Š KdbiµÿAg¹³Â‰É èqX]deddIÿUbRa‹Q& Š 6`cafµÿQ«³»ŠÉ ¯“w[\eded`ÿ@fNf~FŠEdcd³ÿAtµµÈ‰É ¶›`[ddedqÿfbJlp;‰ /]cfm³ÿR¯³Å‰É¼¡…fZ€dc]’ÿfeGpc2Š@dda±ÿD‚³¿‰É ĨŽkXddccb”ÿUeDmV* Š(Wdc]°ÿAU°·ŠÉ¯”qV€def—ÿIeDhK"Š =ddcU¯ÿK³Ç‰É·œwUc€di€˜ÿUcDbA‰Qecm®ÿA[±Ã‰É ½¢}Tcddef€™ÿf_D[7‰:bdc@­ÿP›»‰É Å©†Sbdecd€›ÿj\FT.ŠJdc^«ÿAe´Ç‰É°ŽTaddc^Užÿ[[FK&‰5_e`U«ÿR¥Å‰É·—U`ddcf@Ÿÿ€iYHC‰ Ddd`©ÿArÀ‰É¾žW_ecdbf¡ÿ€fXG;‰.\de€©ÿS±‰Éŧ[^dedef£ÿ€`TF1‰?ddc€§ÿI‚ȉɯ^]ddcaU¥ÿUhOC*‰&Vcft¦ÿAVÁ‰É¶e]ddcbI§ÿ@cLA"ˆ=ceeU¥ÿ=TªˆÉ¿l\eded`©ÿffH=‡4^ddj¦ÿISƒÈ…ÉÆv[dedbqªÿU`E8† :W€e^¥ÿ€`Ua¼„ÉYddea]¬ÿUcC2„B`ddccU§ÿfRU ‚É‹W€deb®ÿmbB* 2Mdecdbb©ÿfVUxÆÉÉ™Wdf±ÿUcA!€ =[deece`«ÿ €fS\¶¥Vbdcec€±ÿ]_@%Eb‚d€®ÿ `UTVcdegf€³ÿ b^>8Rdcdeaj€±ÿ€bedde`€¶ÿ f^]ddcedU²ÿ€jdgiU·ÿ€cced`^U·ÿ€¹ÿ€bhdUùÿÿÿÿÿÿÿ”ÿÿÿÿÿÿÿÿÿÿÿÿÿ«ÿIˆ@ñÿQ‡jOf[€íÿFh‡ËdadkìÿCy‡Ëv_chfìÿ@‹‡Ë‡ZdeqìÿMš‡Ë›YcfbìÿU¦‡Ë¨ZdciìÿT±‡Ë·YddfìÿZ·‡ËÀWde`ìÿc·‡ËÁ[dedëÿUm·‡ËÂ[ddbëÿD|¸‡ËÂeaedUêÿ@·‡ËÃt]demêÿCœ·‡ËÃYeb]êÿQ§·‡ËÄZdcfêÿU¯·‡ËÅšZcbcêÿZ·¸‡ËÅ¡Xdchêÿg··‡ËÆ£Xdcbêÿs¶¸‡ËǤWddcéÿG„¶¹‡ËÇ¥[ddjéÿ@—¶¸‡ËÇ¥e`ddfÿàßà¿f[àÿA¨¶º‡ËȤo[deUðò€õ׃_]ÞÿOµµ¹‡ËÉ¥yYddÀôí±‡ÃóÔccfÝÿT¿µº‡Ëʦ~Zddåõw€-©õˆddfÝÿUǵº‡Ëʧ€Yddéô7--0kõ¢dfk€Üÿdȵ¼‡Ëʧ„Vebéó-[õ©€cmÜÿuƵ¼‡Ëɨ…Vddçó-[õ©edc]ÛÿI‡Æ´½‡Ëʨ†Tccæó-[õ©ddejÛÿ@ Ä´¾ˆË©‡Xadäó-[õ©€djÛÿA´Ä´½ˆËª†_^dáó-[õ©€djÛÿGÆÂµ¿ˆËª†dYdÞó-[õ©€djÛÿTÔ´¿ˆË«‡gZdÜó-[õ©€djÛÿUß´ÀˆË«ˆdZdÙó-[õ©€djÛÿ[äÀ³ÂˆË«‰dVd×ó-[õ©€djÛÿoäÀ´ÁˆË¬‰gSdÔó-[õ©€djÚÿ~㿴Èˬ‰gQdÒó-[õ©€djÚÿ@–⾴ÈË­‰gPdÐó-[õ©€dj­ÿ;?AU¦ÿA¬à¾´ÃˆË®ŠhS`Ðó-[õ©€djÿ‹ÿ U6*9B=>?U¡ÿD¿Þ½´ÅˆË®‹hV\Ïó-[õ©€dj˜ÿDB;;:LU‰ÿB!€ ';FHB==UœÿPÎݼµÆˆË®ŒiWXÏó-[õ©€dj“ÿ B@;A@5 Deªˆÿ9 ƒ '8HQQIAA@—ÿTÛܼµÈˆË®ŒjQZÏó-[õ©€djŽÿ fD@=GJ@1 ‚@NUO@3" …,[b‡ÿA#‹ "/=RfprkUA@@ÿeäØº¶ÐÞçëðõõìáØÏÊ«ŠjLRÎó-[õ©€djƒÿmDACYb_VJ7*ˆHdU†ÿ8 Ž (7FXnƒŠ„oW??3ˆÿwä×ÔéõõóâϽµÉÝñõõé§gKQÎó-[õ©€djÿUD?C]tvpfQ@1#Œ=dU†ÿ6’#0?Oav§™†oYB=3‚ÿ@‰éðõë²s6ƒ- .[›Øõ߃PÍó-[õ©dcWAG`u‡ˆn[I9* /\f…ÿC$– *7GZl‚𬭛‡r\DAUŒæõï™7-0…-/--gÖõÚáó-[õ¦cu‰œ¬’zfRA2%“Kc€„ÿ: ™ $2@Qcxލª¬­œ–ßõ¶E-.Š-//€ëõó-[õ¯|™‡q]J:, –>g]‚ÿUÿ7 +9I[n„œµìõ‘-0ƒ-1/-//ƒ-.Páç- Mõ¯eUC3& ›0\`ƒÿU:Ÿ  %2Bbæòz‚- .-.Z{˜„n;-/-/@Â-Lõ®R' žNe€ƒÿ@C='  Ãõ~ƒ-?¢åƒõóÄm-0€-14-Lõ«I  6G`e`ƒÿ€cdZ@.  õ¤-/.‘íõÚ¾¡…y•±ÍìõÄI.€-0-Lõ©F :Lbeddfƒÿ lced]D3œ3õÇ0.--03¹õêŽb`_[WY^akÆõé_…-Lõ©F š !>?@EM^žïñj.ƒ-Lõ©F — (?U€d€cb€†ÿ€qdbdcdaL; •8õµ‚-¯õ³d`e`J5689;>?BWzéëK0‚-Lõ©F ”.AZdcdcfc]€ŠÿmjdfcdcR<% ’¥óM0--.{õÖde~ŽyR02469;=>@P‘õÓ.‚-Lõ©F ‘ 3E^ddedfb`ÿffcdeddW>* Ž éÈ-3áñ{d…·ªyF*,/259;>>@TÁõ~.-Lõ©F Ž 6G`€dcbdU“ÿ€iadedd[A. ‹Cõ.€-‰õ©ao¶[&#%).36:<>>Cgèß.-Lõ©F ‹  9Mbddeff^€—ÿU[gg€e ^E3 ˆŠõU/€-ÏëdZn„}\+$*/49<€>J¸õ^0€-Lõ©F ‰ %=S€deebfUœÿ€]`c€d aI6! „¶ï.€-?ôÑ_HUWF#'.38;€>?ŽõŸ-Lõ©F †  *=Xdcbj€¡ÿUdgdcdcM8% ËÝ€-/iõ¹Z:2,% %-37;€>=aôÂ-Lõ©F ƒ  .A[‚dff€¤ÿmdcddecS;)  ÞË€-1{õŸT;3-&&-38;=>>=PÛá-Lõ©F €  !2D_dbfm¨ÿfYb€ceX<,"  ï¹€-.õ‡N:4/)##)/48;€>=HÔæ-Lõ©F  #5Ka€dccaf­ÿU`dbded\A0% ë¿€-.ˆõŽH:61-(#!!#(,169<€>=EÔæ-Lõ©H  &8Ocdeefai€²ÿUbcc€d _F2'# ÛЀ-0võ¦K<841-*((*-047;=>>=:8520//0258:=€>=<:”õ—-Lõ¬P*%0A\eecdg[U¿ÿ UhbdedcU=–õc/€-Ãñi@>=;:9989:;=>=>>=;:¾õQ.€-Lõ¬Q8F_dedded€Âÿ €^ffddU‹õœ€-.xõÂH€>=<==>=;:cïÒ‚-Lõ­]bcbYfÇÿUf`bTäÕ-.ÕõŠA‰>=<:?Ôõm/- Lõ±ddca``UÌÿ€<‰¤¼õ^0--/iõåZ?€>=‚>=<<:8žõŃ-Lõ°€dX€ÎÿA_ª¦uðÈ‚-™õÌW=ƒ> ==<;98‡ñã@/‚-Lõ°bddhÏÿ<”ª~J®õf0€-.µõç~=<==€<;:8E¼õêY/ƒ-Lõ¯_dd^€ÍÿAkª”U*IïÙ6/--..žõô°T;€:98>tâõÛK/„-Lõ¯`\dcjÍÿ Cžªg3Šõ¼.‚-qÞõõæÆ¢“¶×òõó¥:0€-0-Rõ§\RccbUËÿ >tª{B-´õ”‚-/2Âó‚õߣO-.€-/?-[õ©`UZdc`ÊÿAJ¥’S&  @Ôõ–./- /-7Qem[E--/-.MÚ- [õ«ePObdd€Éÿ;ªe2€NÌõ±2/„-/0„-.-cêó- [õ­lbPXeefÇÿAT©y@J·õÒa-/‹-: òõó- [õ­rr[Q_df€Çÿ:‹Q$ƒ D™íõ¼P--/„-/-6ˆéóÄÊõP.-.ƒõ y„mTTcebÅÿA_¦c1… ?]²òõØš]A0€-8K|»ïôŇtšõ¯C-QÐeT\ddqÅÿ<–v>†2Jj©Ú€õôäÛî€õðʉtw~Éõõäõó¦w§’x^Recd€Ãÿ?jŒN"ˆ(‰ 'L€dc]’ÿfeU£½­­½‰¾´‘Tdda±ÿ?B'‰  $Iddccb”ÿUeU¨·¬¯Š¾­l[dc]°ÿA<1Š E€def—ÿIeU«³«²Š¾¡SddcU¯ÿ<;‰Dc€di€˜ÿUcV­°«¸‰¾·zZecm®ÿA=%‰  Acddef€™ÿf_W®­«½‰¾®Ubdc@­ÿ:/ ‰  ?bdecd€›ÿj\Z¯¬®Š¾ŽYdc^«ÿA<Š=addc^Užÿ€[¬«³‰¾»]_e`U«ÿ9$‰:`ddcf@Ÿÿ€iY_¬«¸‰¾ Wdd`©ÿA5‰9_ecdbf¡ÿ€fXc¬«½‰¾h]de€©ÿ:‰ 7\dedef£ÿ€`Wh«®‰¾²Tddc€§ÿ=*‰3ZddcaU¥ÿUhVlª³‰¾{[cft¦ÿA9‰0VddcbI§ÿ@cWs¬ºˆ¾ºTceeU¥ÿ=;ˆ,Seded`©ÿffVy­ˆ¾»b^ddj¦ÿI<#…(OdedbqªÿU`U°†¾¬Z\€e^¥ÿ€`E5„#Kddea]¬ÿUcTˆµ„¾ŒU`ddccU§ÿfR;‚G€deb®ÿmbTŽ»¾ ¸hYdecdbb©ÿfV?)Edf±ÿUcT–½¾¾£V]deece`«ÿ €fH7 Cbdcec€±ÿ]_TŸ¾~Vb‚d€®ÿ `U<@cdegf€³ÿ b^T]Zdcdeaj€±ÿ€bedde`€¶ÿ f^]ddcedU²ÿ€jdgiU·ÿ€cced`^U·ÿ€¹ÿ€bhdUùÿÿÿÿÿÿÿ”ÿt8mk@Üæçççççççççâ çÿÿÿÿÿÿÿÿÿÿô˜n*îÿÿÿÿÿÿÿÿÿÿþôÕ;HøÿÿÿÿÿÿÿÿÿÿÿÿèV pÿÿÿÿÿÿÿÿÿÿÿÿÿìl œÿÿÿÿÿÿÿÿÿÿÿÿÿïƒÁÿÿÿÿÿÿÿÿÿÿÿÿÿó™×ÿÿÿÿÿÿÿÿÿÿÿÿÿ÷¯áÿÿÿÿÿÿÿÿÿÿÿÿÿûÅéÿÿÿÿÿÿÿÿÿÿÿÿÿþØ"íÿÿÿÿÿÿÿÿÿÿÿÿÿÿá3<õÿÿÿÿÿÿÿÿÿÿÿÿÿÿæI_þÿÿÿÿÿÿÿÿÿÿÿÿÿÿé` Œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿív³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñŒÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ¢Þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø¹èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÎíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ)0óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿä=OüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçS =Ýÿÿÿç†0 yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëi"ðÿÿÿÿÿúÌY¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿÿÿÿÿÿÿôÇT Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó–ºÿÿÿÿÿÿÿùå£Úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¬Éÿÿÿÿÿÿÿúì¿CåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÂÌÿÿÿÿÿÿÿúíÄ] ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÖÍÿÿÿÿÿÿÿúîÅe $ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÑÿÿÿÿÿÿÿúîÅf CùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÖÿÿÿÿÿÿÿúîÅf hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÛÿÿÿÿÿÿÿúîÅf •ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíàÿÿÿÿÿÿÿúîÅf ºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðåÿÿÿÿÿÿÿúîÅf ÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôêÿÿÿÿÿÿÿúîÅf àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøïÿÿÿÿÿÿÿúîÅf êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüôÿÿÿÿÿÿÿúîÅf ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿÿÿÿÿÿÿúîÅf Ra+7öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿÿÿÿÿÿúîÅf âüëæÙ°e-VýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿÿÿÿÿÿúîÅf /i¯ØçCBñÿÿÿÿÿýñéÛ²g.ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿÿÿúîÅf 1l²ÚéñýÿÿÿÔ&«ÿÿÿÿÿÿÿÿÿÿÿþòëÝ´i/¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿÿÿúîÅf 3o´ÜêòþÿÿÿÿÿÿÿÿúZ Þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþóí߸k0ÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿúîÅf 5r·Þìôþÿÿÿÿÿÿÿÿÿÿÿÿÿþ£;îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþõîá¹k0ÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿúîÅf 7t»àíõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿä.£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ðâ¼n1çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿúîÅf  :w½áïöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúW Üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøðã½p2 íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿúîÇŠ€Àâïøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý5ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøðã¿s3Aýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà,šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùR Ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý–×õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ+vòþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÛG,ÄûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüñÆJ †ÏìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûîÖ¢YJŽËèúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúëÏ•P$  A‚ÃåøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøçljF 8vºàõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöã¾}<1k¯ÛòýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýóÞµq4*^¤ÖïýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüðÙ©d-%R—ÐëûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûíÓX' HŒÉçùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùêÌ‘M"  >Áãöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷åÅ…B 6s·ßôþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýõá»x9/g¬Úñýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýòܰl1)[¡Õîüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüïפ_* $P”ÎêûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúëИS% EˆÇçùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùèÊŒH <|¾âöþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöãÁ€> 4p´Þóþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýôß·s5-d©Ùöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷ܬg/(]õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøËd(‰üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþð•ªþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù¹,)ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÝV Éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò—'JôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÐZÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð .qúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÕh æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô®7™ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÝwìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ºB »ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã†!:ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÆO Ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿë•(_÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüôëéöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÏ]áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýòÍœ‹”¤äþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð£0‡üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþïµV:Ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ×kèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþòºU5âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ±:¬þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþóÀ\ =êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞz,îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþõÅc Gðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø¾E Êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Ëk Rôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå‰#NôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÐr"^öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÈR ÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÔ{%møÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì˜*uùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùØ‚*{úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÑ`åÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÜŠ.‹ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ§2þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûá’3œüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÙo!êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüäš8 "­ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö´<¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýç¡> &¾þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà}=ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýë¨C +ÎþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÀH Õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýí®I0ÚþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçŒ$cõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþðµO 7äþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÊU áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþò»V @ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí›,‹úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþôÀ] KñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÓdæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöÆdVõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóª5¯þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÌldøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÛs/ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÐs"qùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö·?ËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÕ{&ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ‚RñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÙ„*’ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÃK ÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÝ‹/£üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè&ßýÿÿÿÿÿÿÿÿÿÿÿÿÿÿûá“3#´üÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÊO Éúÿÿÿÿÿÿÿÿÿÿÿÿüä›9 (ÅýÿÿÿÿÿÿÿÿÿÿÿÿÿûÕe‘õÿÿÿÿÿÿÿÿÿÿýè¢? ,ÓþÿÿÿÿÿÿÿÿÿÿþùàŸ;  Kéþÿÿÿÿÿÿÿýë©D 2ßþÿÿÿÿÿÿÿÿýóЇ9 "Âùÿÿÿÿÿýî°J :çþÿÿÿÿÿÿûé¸g&{òÿÿÿþð¶Q DîÿÿÿÿþøÛœJ=àïéì»W OòýþýñÉ|2 I’ XT¸æá¯]! !%K‚@  ic08˜¯ jP ‡ ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cÿOÿQ2ÿR ÿ\ PXX`XX`XX`XXXPPXÿdKakadu-v5.2.1ÿ —Âÿ“ÏÁ0 À ìÂSòy•œÂHê4/u Š Ÿå]uFäc‘^h•€y6?úJ±jœ´\Ýúà¡ U†Atf½ :OI¤Úñç.)R‚(yÿ=؆ÏÀ쀥yfµ„ÎäüXöÌ/‚›ãv‹0n?-X¬fiõêºyÝf=+“ ÞAÓ¦yl#ElØR7âëxÏÀÜÜ‚a±ÙužUêzÞÀÄ}Ñ´­0Ú3ÿ31ˆås‚ÞöJtèWoB$'Uó?+ÐÁ¬qÏÁ$ÌØy/{ç¤È1U„c&J½¼ñ0P»è„à¶àl&àØoC|T¯¿2 ‰Ãã.õCŽJŒ{˜ÄÈÈ6aÈ¢×¶qŽ„_¹ÓÝhß™6lÆ5™±¯Ë.ÿX9c ’йó«C®¼/C&¿< æï^·…D ž&§6j¡#ýL–±ˆBx®Ê\“½íÝãº`ÿzMKž)PýëÍñ–WÄM¸ÊßÇàmÌ?€FÑ' ^ÔÁ3܈ü@ZB¡Z Xä{ü’ ôßaÄ#ˆ[ר¡‰—PßFn`Å@̓÷Y-ÁîÙ4)?Å,éäükˆC?aBðÄ)Úׂœ‚íV‘b eÁÆm¼rþrt 8O®-ÍFãçüS×,„BCv*‘ P ÎG‹¦):ZA@©-ï\# … šÕꯎCt «bxn¦=Ôéð3TD‘œ6Çàc¬?PVâO£J1¬TÆéH°-LiÎj¯/$õÔl§Œ]º±âí„Wš*•ã‹9&¯]K ÿQß3dè*È`í&ٯ‚|Ö¹ Ô☈jVCTn8ßl®~N‰e£gy¹Çb™OÝØ*Å0 };áï÷ì)`•¢} r‡Ñ} ;’ùím¥‚—_¢bqƒic¸±Q.¨øpÇày‚ ~h`A²Òûä)ìé¦V4ž·Æ2±Y„ÂÎï’К2íQÒ—[I)Ìû¥Zh ›ëDê…Ù•"=:À7ŸA¨p㫈¦ek·¿ÏÁˆQ·Pz¦t'I0óXƒ ïÑ¥$${GDƒ~wJÑç¬Ú.ÇÇÙ´4ÝYH´M6WòåW¶¶æ9ÂqYtÇ"2¯»¸H÷L£sqŒw2+ç)y®÷¿¬Wš’ü­Òީߥ(iUP.L™\9RÇáWGáWGç\€„ùß/k]´¡i«ÛÂÝ#Õ#}a$æ*þG<ÜîŠ}¼J/Æ‹ZÐ ¥y²’çRLK¯×3o6äd>'?T‚Br**Ù(j&1f¿‰×ýçÁ æ[AíñrnZÏ™{úHï¯ (XY#«3¦ç_–€{…>y~Sí’æè¨XRªËóŠHèw ÓëðaàÆOÌñ“ è¸DîϦmˇïv©öB!…~9„>%|ÉÈãå7SûngoÒ6{qhS!Ì`C,­ý8.‘‹¨;êÂï±§cëÁ_¨zçû¸@åYÈpÀM]¤ÜÙV`Ù¬øŽ »þsjÎIgÕãNdyù¸Y[:âX¯ejß2‚§Ý»áþd2‰8ˆ×ȃ=1áÝK^#$¬÷¨ü£Eâ^I1VݸœOÉQFH^ëØÜyâNx­ž®ŒVÓÓ¦5²’_²R©P¾NØ÷3%G!ªŸ~ÙÔ®G„ØqÒû–*ºÝ]22DŽJ`,:6žŠ‚çg%_*úKàYÕ¾²HWÅs²õFîµiºÒþñÍ$eTÀn<¾9fzØM– “Zu†^2áÛ3¨¿ðD›,œòDÞá É61˜a ú µÂ|8ræ{oIcV¯©¡,UTîSW³Ýr,÷Jµ™ö~²~~™ ¯ Å%:é©A¢eûºÇHÉhKÁÇáIÃí¤!øQÀ‹ÃÝ«ïx?©E•Pƒ¿.úayé*v#g"p´á@CdŒƒª,f?ëˆç‘´+­Œ¡ Ä]îsI™#à”op9^êbárô:fdÕ¯8»·,ò÷ß&8=¥/À2•F(©‡%TQ[e»Œð‡wõ¶ylÀ #{^im™ˆŽcÂP±)æá¾™wê@E¿Ê¨;:ÊŽ\;ÏÇ&U±å?DfÐεSrÿƒz:(d/-*êÝraHß$]§åËœzTsÉp¹â­FÈ!äiɬ. fäëLIƒ¸Šã;ÆsÅv£¿¿µ¶åxXÐÞ÷Hùäh1,œ¤ö¬íðô0ÿI+ì^³µ4. O31¬z÷·ûë£|r /„ go!'£^(Ž{Ÿc½dX9C=÷81@Q$ÇÕÉ%üL™8DCJÙky£—ËC§+%U§¥Ã‚Bí/­0QOš5MÀ^zöˆ;”5ÜVÕœçÁ€%Ó©ÒÚ‡¨ämkÝ­ÌãhLBÞ<# 5ïÆ^§v&ÝL7MÙJ¾Þ“ê'”92Ø«_"ŒÚ2þY–숬v¹àó¨p‰¹þ˜´Úµ™cì²5¥‹‚‚(IñœÇªõ‚-JçAŸb¶ø×eý)(ØâÚF¼‹àá’ž‘O–*aÆþP¯‡5cÈ‚GryP Rë í´I‡¯Y²‚›œŠï9ïJ`A¢øHbI¶@DPª/ }@Bƒ¯ù+ϯÂ>§>vŽ’‹=æ@*¾ýË ÇO˜M+—º°£BJÏΤŸS§‹µjÞÁ'(nÖÈà%ægo©euˆ†P#©|Ya2wƒë<2óŒÁÌÉH÷DùœÈÔ±ãšRy¸Ñ]mk~.‹Rh¾*%V›ˆì°]&Wàé‹qýÏôõke;c7£ø‡}[a4ô’Ì3‹½å8Nž /âë¨H &}¹ãª‚ûÁÅÜvnZì L²¾%SÕX÷ 6LÁ_ÖÅ–J,!5âûÓÅ£¡ÙÈpÏŒ²Y÷£ˆaˆO§Û*ä~oDû̳ 7þpû•Lç8Ê|ë>8Ñe’˜Yañ]|貑 »( _^}ï;ë¾ì©Ÿ¡äç$s*¸òzœªm--Å &‚OCÇá¾ñøoŒ~}À­ôØHôÃÿ&ƒ‹~=Ç·Ù>ncSV\`ä÷]8#N‹ûVwM´æ2e1“ôðŠ%*žEÌÝâsÞº4Û©÷H™ùôOÓ,V]âå5L £/ÌŽw «‡Ê×Oµß€¼#äPzæ¨ØZ_f¦n'Ø0 («I’œÈ7û¶6)FŸ[Š®»!AÄ‚’1X!yAÅÈ¥õuî,;q¾òqT¾|ÖšLŸSp¤þñ# ûSÇNã›Sƒ:‚FP_Å»S°-.âxBXÄã9\|©Éµ‰gÿô« Öß“[…h>Í£…%kf0Teˆ–MÎîvŒ§]"=m| ðþý@‡bEµÆâàr6Ü(«ËÑú57+ ôï+’Äa\Tbù¾£ñCKà¢;}YM±}íw"Üb"ìþ𙢯=6r*¥*ç ÷PFwÄÖæ”)Ò7¾šã×NI¥–Nâ•ÂÙ!)™éaI”| ÚR6zÆ—8Ð.ÏzÃóâºrì¨ü=Ñ”z8(Ê÷È€''eLi"Nã¹)Bò³fþÊ쥟 ÒŠ5¸|vñÀ¯_cU4É¿ÅEÌ·;Ù×%÷Ãn ÿeFã^#¡Cµ¤PŽ=wÈo'…òÝ¿ê~šåÂ/oõé|FQižÇ…|üuº»ÁþëÇæàó Ð=_Ëóýñ.…øØ^÷¦Ö.qc7ðŒÀ™†Î”—Cè`Ý‚Ð2¹òkáhdþ¹ÞÞ”»`åòH*nPgüá ŸúfOòjÙ@ÅÝô?™0aTñù9Â| .Çáª1øjT? DÌ ¦ê/–û˜sP[‚5l W‰Á  ALœ€‰VésÑÅ=~JMf4‡ã^fzѸ¶»[,€š0h5»ýÉÛ,ŠžRö—¡Ã¥¢¿¬jÈwlQ‚ßì«5ú^3%ÌhlèèSö)áìv’­E½ïDÍ¥”¼Ïƒñ‹¿µ¤ÿBá»gZ·,| aˆL&J‹ÿRÞt½I¼¾cZø¹þ‘Iì%V |0ʾs_ÍyÇ•7(.`º×µÑ¤ÕxA{H«V¦ÔË,TŒÖƒªmTˆ7›Ý;ŠHߊóªâ ÍA:ú4)CõéWåoàÁ—ê¤òpœL95ÍØ ƲãKY´AR Íñ¹9çnæ I²þCÉÌWΩö÷PÒ—”#$cqŠ”úzÑþæ• MÃ^½á(ÑÌðfè/_î’s‰UùõØû««¨ap•…\c ©áé.~0eÀõ´•˜å×,Œw9K&ÃÖȦ,9DìJOÒñïÙsÇp…©;UI, a=qƒùs|NÏ™ŒRlqÓ4ƒÔ)»í¥¡Û¾ K(ÒO0î4p ÞyªåR1ÞBelÁôÀˆKK÷Ž+_Ç:oMìQ¾CíIMv”/,õ?(?¦` ~ þ[EÅOß>7^: ÏB«¤ Ø’_pγݲê. ôö“gÝlf.5p'MºoÖ’c&›=$Báºð‡•O>ðâœÅ¸#LWSöó•¾ åŒ8zVú’ÒŸÉx%½¯ä¨—ÿ,^àáòáhIì}JkžÞ·ÜÕÃÚÊöò¢’35ä1a¾ŽMDë˜XJ1Y1!a„öù·ˆ6PZ·MäX ð(û ˆÇá©ûu†¡€ã*y7¹˜KÎ+SJ6ê+ˆ,, );ô›ùnVåçä=!8OÿU™ÔÝò„òãÌ¢ŽtÎ $br„a£J»­l€ÞÞšœB ïßη3ƒÕýä¸x˜„@sñpâUêÑÚÜ ˜C±ìüa4õ®"ªÏ8Ïáµ`Hû‡TíXr““7±‡¹¢ëx¾í޵Š0½‘æè=[mê|£yí8P¿îÓÔÌxE¬JPÃÞ…‹sa+F¨¿­ëfœªx›»ŒgËOdÇ@ÍY¯,¹†ñ}ÛåÖìÀ¿q$l㋉KòWÝ€ lG޶ÑP,[@·b ?˜‰>RH×¼ò* –¼„}MPÊ…l¢F@KÚ|—oæÉ/¥)†Àf¡à˜Ð"ÈÆ·æ$¹¬‘újÍ9@,Ø»=eÐlð‘;¿â<ž¶ß”±éHÑÌž¼[˜…Ï/ÝÙ¿^>jgH¦Î¿5/ÝmÏ}}ó ®ínйÓÎh6¡NÚô$ï=¼']Uüa.ÏÈ´±#µ×Z·p¡7)œ$`>fØÈؼ= zpq9PN´«W3 Ú¶bà ºf{@ÿyB·ð5ê+霓ű3) Œ§.Ù Ñ,˜ÿJ—•Ì 2èyÄ£ª[ŽyÐúB¯Aó¿)yQ¥ƒ'|yA›NiÂ.^D§À&{À8þØP°ð&ªƒŽ*ƒé!hJ|õE(r¢ÍAôEq Ö“)‚>‹P„þ´Ëçgž…%õì•Ô§žcç Î$÷î_ûG;+0Ô8½>yăáVñü6ÀR'ÒÏkÚK›N\¢g•GkŒD„ŸŽqs:N/: ’qiìt{U¹Ýy…ïÂy¼ŠÎKè¨o•ÿafSÛTDjèM‰šŽ„”àgeœqý½§ÍP»âí‹v¿‡ yë°”M°²§'µ©äTðªklÏh˜Nó¡ª?”6°Û-5‰kKÇ‚ŽªX«¦Ròö°ã\óäóã®0xk)»C›ù4vókF ‰øš~vxKX@Önƒ.}"ÛøcM'„KîNBh DCƒjóµã‡ºÃÑ«Wýšu¾ze“n±çžIAî*ÀIŠ ¡¸®ÃVéçÜk«Œ,fìuû¶'Í¡D»ü1œÚ,3É )² ¼Ù¹ jã§‹Çá¬ñøk? U\’FÃ[c>ñaÎD‘2!>:{¾,{¦¢0CŒ­Uc§!(5‹‚l…|v0¢á®Œl铎Q9ì!•t·)·t…›),EJ¥„,÷Å’€mœ}¶b½ðÊn®Ò:SnÁBêËVÉ_M—Ò<: N+¼¿-naÿ9‚rîÄŒ¥ÿRa:PÔ[BEÝä,ÔÓ7´@Cß *ƒâ;`™w¿é¿b ËÜÂû¼CÕDGAŸø bš eVp80{íÆÛìþAŠÌ–8úb&‘€ßRÛ¹)ÝJ;ßHÖãõ—–[Â<­P+W²ó¬±KÒœ²ÌéªQ„ r>v*Ú|C–Pæ¦êÐÑx&èG:_ÙJ§Î†‡·ª}á6ËFð¥eqJÒ~—Ÿõ©(HAÒ ÎP”Ò;å$‚V“¸g¬v7yì9Ýd¡«Ìû-ÕÒŽö_,5Ôé]½@¹0LºV–ß¶ñ ªn“ô>8kdî?.ÑA_ì:V»óì­ùÎXØRÌuÉBãdSPâÉ š«££¨[ùŒÔu„C§³/hœñ”FÓ¿‚ôÍÓGìŒ RÌ„À¡®å ÈI˜ØôDû$9ŠÔè1/Àž¢€y*ÝÏЃ±Z8q—§I»Xnò}«„pÚ8 Û‚A”àe" ŠÑùþPÞÒ™|X9š´£¾þ3W¥¹ñ5Ž€ÇV¶éÜ^b9+(ÊÙ¹úÆï° ?/ü eœR—¸xňÑHý¦åãÊD" ò~ÀeâùŒ"!Ø›°UÓç…¸¨vž„Dô)vÇŒ”©‹«ÓÜ}˜·Ö%B>·ƒœÖêFQyW9l.bQé34¾¼öpY«MM¶dø´´ ,€Œåüã?ß3Êq3øáÖ®¼óÕ CV'¿½jvÎfGq/Ø9áŸàËÄZ°Ë^™Å à ß5ö¬bXzÛXLjÂ_KCãÖZ1¨ºw}Ñ*²³Ö~™h.ѹ–ÉÖ3GÊR“š¹ŽŠÄÛqž’øm" jP²I7™<–…x1*ŠS£¦Ècfû‹÷“Õ©²‰=ª{áÇ)5Ïòõ‘Ö´G´X;.A§ÁqŒÉ™¶(–u7é~Œ Æ·éLbNû&§{=twÁ >R˜ó,dx¡¤÷å»7É{DN« *8^(†1@?ý–ìlYÀM'9î+{ùÑEø;oGMánUƒÑ mâÃÎ!¢ÒÕö@æ$öÝ„É"ïÀ'04õ#ENLù䋊O2ýév‘Oýè)5ÂÂ!j‘úÐ-0ƒ3%¥ùQÝ•e?{Òƒ3iV;ĪD-‘¢¬$‰ØôÆ,Þ6šFÇhð+#.!TÉÒcú°Çáꯇ§\~ŸÀÅŒ_^– ñße+ñ‰¼n+¹í½‡’b9‡ÜzZ’tôªœ•Q+»uwbÒƒÙa^Cp/[øÌð û-—žpœÄ™R¾–¢®Q çV~Ÿ‘'¡€föW%ï‚!¶kCÛÿÀWäsÚÇbˆ68ÇÙ{å¤Û÷P ¦—?¬ßVð]Åö0y3c5ºýµ¶èvFÉÙJöø™?’,Y…ÐyÚŽ7¯æ[κ" *6ÓQŸ=ØJ‚@£~º°3ý ¿ƒ»d/¨G.¡þÂU¹S…jn,=kDüƨíEš¥3xFNVº˜‡~ˆwO’E Ṇٽ5ZÆaHtmÈ”Io\û|B0ƒ/Arnæ½êÎ7ÅŽ-6ã\›={H* XcݺãŸ>}ïw™áàsÓvVA¬{Éyb% äM]ÀQŒ§A¾¼÷AI”l…„ƒ¸½µ¡¨¦Ö¶íæ{“ˆsÜæÆ±f ž?çF9ÅtðÑà*ì]QýÊW(©5êÄ4c&unNðÏ y¥O6Ž(ލB8‚‰³Ô‚éßbA“Æ 5_ÊmQÃÒ³yôæ* -hX§!Û z±? Ž¡˜ÿnm“ÃÁÎã£s„üãbà<Æô¢î˜à´í3柑¶kê]žPª¤‰\ÖNkÒ’Ôq$؉Ð[ypÿo­¢‹;xA/¯²rM1ÂÕdîOt«G:€n‚á—B<Š|ëú“qÈ(¸–ý€.× -|à@Œ½ÇbÕ↻ìM–;lúÔì{1×XÄæßS+»ý³Ê:Œ“¬eL"hHçì´©gtŽf¤[¹°¹ˆì¥éHcÂ%&äÉçRõ˜3¸ ¨ÅÂ÷¦üy"~· `4ͱ$‰^g„, -ÇsIýg l#¨Ê‡U¼œP‹Y“ óù[Ñý¬m(õ¬' 8%&míìþî#áôÊ“¸!ñ‘Œ(ü¸‰¥~ÓrŇÕÎÜÜ!¿³Ž,^w bz%$Ÿ“«; ø·(ø­NÙÀs/Ùv[eò]ã ÛYLº±(ë7žI®¶äž ý·{HMZ…ß X„{ßÕøŠ•ñ«‚iÀ Å…Š”ÑßçœÊ]–#ñ=Бuõ.q1ü©$ÜiòŒŽð ‚UV8ILç¿ÀpÁȰ,@žë&€öÅùÞçþ÷0V –@Ü/‘ttkXÉÅž+Æ•mØ;~õcHb/ gªx|¡î¨í0ø!*«¼(G†\è"…ðãéë·ìÛÍ„”ˆ³57p²È÷¢Ú¬]#™,`ªÏñ?jE™7i„V ÷«¬¦ZáS ²f¯Ip-û×jJ3v*T¤°ä$ÛöÐßÇ$®6È´ÊVZfþ(àÉo •áIå¨5PÔ/i.D¡f•4¿heè -"ÄdœÏ¨T¬M "þÕHI#ÿP¦ëÍÕ˜ïZäcNwа¹k¶Õ¶ŽcqrÍ"HFœéÅÏåL´†‡7ðîs ÃhŸ®¿´ç2YÔN'|›—šJÿ€û ùI(î©èYÛæwåf8ù‚9í‚èÑ %ÜÀJÄÔ|Oõ=ÒoGÐÉe CîaƒÕLÂš× ¿Dÿj¹ÊØos¸V ö1žÁ°:tÔ­nŸéKàfOóà(µ¬+Z”'¥M?å ȃ~£c—M¼·=ñú,@2@¸<œº«r¶<  "óŸÈ,me¼7®½¬,ó¶±½‚/Ëô!< ˜2ãì{&]q[§`nOúº\â ¹ìZ·&ÀäžGæžS´î}¥Zo~óƒêKâ /~Á7À?þµÃ-üÅôÁ/èÜð ;ì •¼ ËÖA‘U“Z¬õÔ­&د.°ù? |ãD;ÁeœbÇ ¼·¶{rW‚œ—ô©O“ WM~Túz{Û#HEùÞ»cÌ¿‡ýLå7^êݼ“Ô@‹Á!´bO CgÊŒ zmæ$zÈñwt/$¾Q] ÝÃÚêL mµzÀZTË–on¼þ;h´Í\æs{äXÿ^f@i_cš‘Ymêã*ÔáþÏþž«=XѦŒ´å}phÄ‚úMrÎCÙ½¨~›c(\ÄKÅß?É5u pIaV}…O(M!~÷”ýÎ…óÝÒ£b^| ë²h­ÌÙD£›-ñ`•m…x[ûm3òƒéð™…>ž·Kœæ¬„e£wŧ2ít ylv‚¾U€óp)Þ¢ãoçRœÐ™Ì t+‘>W?Ž]·ÔØÈvF=zGLn¸úZi4žbþm6¸Ä£~/õù=m€±|‹\¡‡Ok¿qè^ÍBâ×{àUàûwÑ,~õªYÂÄæ52oìv`*O3ðè|q¼œ8"TÞAvÃN•×û:è:î 4Ó÷ßIIJÙN}9•óý³qM0àn%€¶ÞQÏÿ1'SÈ®ÀÉýXáž Buò·No= fÖk9Øð„4J5•F8 ïM”^²". /dh{†´ˆu#6“•üs‡žÅ¤çEÑ L%k”Vó (]OQ åý[$ ömÈPÉ“³þ•GÒc­|eªæ¦Rj_¯VxÔ×ß~©´›’¥¥<1Ar–DÇy}`3Ð!éËÕìY{~å/§±g9êǦÿ3ú‚ ­lJFLŽÍ¶ÿƒíßo}_i aa`¹NÿXî”o5¡ç ZQsŸUÂ?ëˆà-¶€)F 79ÃÊ385ÈNùô)+q¶%µ–rJª{¶¤yjÖX\Z²$ÅZ›¼ySóßG ÁkÕd+›õVÏЇ÷4Óô[ÊÓ‹ìÝðìJ†á:Ãt|ÊÏÃ¥å‘ëL.ÛB®L@‹ûœï[[g.ð†Û’,ôq»vþ¾øl(!Õ²ŠO ck.ÔW|3Üî_̆rÑGÕèt.Ÿ9´p¶| n+ý1»ß3Ï\^Hãz¿ ç®ä ‚*²YlÙUzÂÇcª[a$|µ ®ú™¤8:nõµr( QK>Å_ÌÉ7®ÀYˆKÊK¡™½Ï§ï:·Ó%†:ÙJí }·¶ÕÉèˆB3DžÇ=ÝS¡es`>~<•…WPµ_¯LO3¡ƒØê:3B MíÔUQ¨ìp—Ì…¼'¢Ntѧ’Ý9_ëœIw’¢RƒdrpÕ)å Þ€UdaŠí^¯ü};A5Ä´<¨q?BÞ‘.âGÜA]Gfèc²[‹!´ þß-÷²zìIÄUé èÈôóÍd̓Þx <> ³”ˆúqÌô¨{î“_Žì‡2ØÛ¬Õš6J%'dž­ï²1>zãi-I¨þi]:ø§3zXëJ´õÀâU冿ƒ«jÝ(ÐWÔ3B¶Õ5SÇÏEŠWŒeÛÅjŒpSo’BN]OûdS ;×)ËÛȳžÓ˜&'æ‚«Ecx—“7.f¼nÌȳÕw8Öa¨ÞF" ,·“¾âx:@•~öP‡eÚ¾çì]Øà…YÛbðÚGfÎݨCº— û&Ëóæ T@ÝObîš«RÿEí\òöý¶.GµÞ:5ês´ÂÍð$ÐjÙêoð„±<Å8T³ÔHÔj¶dúäVAùK±õ =ÞÿoËïô‰¢lG6ÌIP› ‹¤OóÞT¦àäåú<Õ†pOYæ|ôÿ M`„Ñ@déÞ]Ê©ÿAiøÂÌNò.‡™Åí[5&3â"¾bÁîyþ C\0U0ÃÞWÿnä‚ð÷_§«ÙKƒPcÐôLš2VE+bX1çc4öÀâŽoEy¸õ¹ê|[U3"²ßšråùÄ Ôw¸æ`* |¡j;±2‘2¹ú––'DÞöÛcó6UÃŽÍ^vwñ¼³uc0-S½:ivßí/"­Ìvý5ù&Å7³ÙüEàV{p”î&ãAü–Y• š(AêõÕ=…1wb÷éV5æÜ—ôeC¶d<*Æõ‹Ê¸Ê.èÌNT=™î“–ö²“Ψ+Qo#ˆ]–B³ÎM_Çi_yý{f]2NÜ;JóçöL&*5L»Àƒ{n.ƒÉ .ÇMÔ›-ÃíêÒoVÑøu2ÃI–£èÃñ7Ew®o@µºïÞcÄÃAe }¤&žÊ‡ÇÞy˜êà[äÇÜêã ì=.´ÈÕÍ&ÞF¼²Ý´o J€w|,¨tr¾DêÈT !@· a:†(õí}æÔßô¾ä+ÓòÉì$*ØgH'S­kÉ/^@68Ærkv… ¿ý½©ÃÜ\Ýó«/‘á‚‚O˜DxŒ±D÷Ìð¶âX( /á©=L‡¹Äw—ßi™G@A¿G:LÒp<¤R´\‡²jnÚÐ@…¬à;Ä8 °ÙH┃ѬiïŸû\æúò¯Ó\ ¾ÿgº·IÔo)áv4¥y“º'.¸ûN¼»îvwG¤ÕU3¯L"6ß —Æ<£ElB;ý€t ÿwÆŠ£B/9¨*Öj%Wó±.„ˆQ M<~$ ÷n÷º]´á„‹fú‚—¬û€_ö§ç‚ãè\Õòãj,rË¡/Ò–GOK¿X_ƒÔÿ —’? ߢícé¼”úÚ|êq;§ÈÞEN'ŠiX^»×$`4öô¬vñ4.f"l;1#•j×Kdõ1’QüS('“¦ÞüÈ›©cØ\ µ^’áežªõëœQ†€¤q’a,~ dE?ÛŠDÏ#™ãÒpAðeŽ/mOe¥Ügtφ…ìñD"èÕ‹!wý„Íqvb¹lšŸIA=–åñ ÂÝæ¢°.¶AÔñ}” ‘E±ÿ\M€€xëéi~ŽOã¢ÍÉ%féï ¢Ò¡7¹üâFx= ò×9AñÓì5ÌÎ’LùOÈϹz‡‚.àÇ3ÃŨgšé*~¥wš¸¯]ÖØ—Ë DôVöók›'Þ¦?ô9†|ÿ Sü¶üÓÑtó‘yr2.ŽœCãï—mõMG®m†3‘ œD™ì n ž~WÖ ŒÜzÁWJFw6—j÷8wº?cʳÈéÚ¹”³$9Î5Ñ _>-}¬šq éKBTÕVl+ÚX“5I_ÿ`T})u‚·ÙøÒFQe°rŽd"®¹R Éc€ãÀÄuò蕲\kýï÷UJ –­õr!BÆN{øv>‡€B¯°\)Jc¸$‚ïŽ.\a•«a‹kI @«SʯchY#¾{1ñÉô«@Zò‰F”ß³&uò·À¦ƒòù­"oÁëaŽSI0a¹d˜¿4ÐkyÚ›;œQ¾lìX¯öÑS'YæÌPYƀРÔ1öJ¤R#>’Wáʨ[(ã…LöýLüøq%³·¶Þm1­å´ù«TQÁI¬Y Êæ`(ód!pÒÚ¨AãbôÁIA2[ª’Åõ°©Š#zb ¯–Ö?Do˪î@Á-ž Òe éÞ×§ï‘jM%+ò^ÐáTê>1 †ºÓ´ÊJ£~ÉcÇ£E­¾·yb17ø]+‹.p\Ò¶?ÝZÉEÏL!áë¹x Ä®éô7e,\e~+Uã¿‚GIJne4ÞÌÒÀÓû,3-†å…B‚ÂÁçÐ¥ã |aþ¬LvLzUÊÙ[”’Îot°ÇO×Õs'þmÖF<EB§Â 7‹P2Bwüni… Õ¬œãä®Ùßm”&#`8·xk9Ïs[ýgm‚Y¤'gðzx¬Ðõ áS€xvf·‹ÚjÁ÷–¿K-:Æ•‰Ì©ÒÓ´½èQUêÂ÷§x° Òä¡ê ckr?`Ü<ým)o«8e¸ŽéÞãÒÊãÌ,»äÅzKÀ]Ûq….ÚBÈÞ°°HŸ ûⲋå-mtk¸‘U’"”5<¡MÔ´*¸Èà?P¶@%ÈüøÓïÞèìi¥ ,f³ïJqš(å#åI«ñ4f¾šà”ÍK xÏzS,5gu AiÖS¶ƒ‹Ÿjõ+™á#êq²¢¸åB2öØQ|9lU$(ÃíêÊoWpûz”ì8ÕYGZ?ËZTªþ£÷©",Ń74Ù)Á—wÇšèY'©¹¢)È^´š®ks6æÚn«ñ”_Ve8ÌÖIáí‚ù¾SKiÊg¯X-…8ó¤@ë Ù‹AÛg~ásÇ6W=ŒÜ“šê'¨xÊçs×&û*þn¬4õ–;„:¤w‚–œ2ì• ?–ÝN`Ž6Á ¿K¶2½ž2ˆYfŒógw/j'Z VëÑÖÛÇ“ìiD¥J‰Y6Î~³Rïv86™‹Üþ_·í½>jãA‹VjOÒÀZ`h 5¸÷W–̾Z¼4,Æ}¿³f&1Íw®Κy‘€h ¥É®Í5BOõ(îàìÂÞ•n½ÿ+`@׊Ç`lŸ‚}ßy°§âHê>K\¢É®D ~/9¶Ëö¤T’ –ª<÷¢ÖûÂöý)ºXäôý‹C³Å¨%6 Ít:{P¸ ÊJÅûˆÝ>žö…«\Dú;š§´Ïìôl¼,ÚŸs°+Ä®]žzwÑå¥~l÷cÔažZ`ún<] JÚ5-uGG -%:ñYŒáÓ„µiâ¥=ôèÖ¦±Þ¾æß“Ý‘¼»nl# '[´µ–¹÷ëî¶BÕk j=©ÏA=ëW.&Ó[àël@ý•éxsCdžúPN’ˆ þ’®}9‚ýKêﯣ¿ùu}ÛlM¦Áö¹4üŒ.Ö8Åänv_µ2Úì=H×óD(KbîºÅ †9쀷dQŸ?ʉˆÀ¼¨W À¸Ý˜4_j†ßîoà-MÙÿþQžÄâÀ%}ü=›Ž1ós½Ø-r#¶ q˜¢Â¥x mR¸7MŒXG)(Y ûTÅ æ*X6‚r ÇëPóßĤýûgÄDȆ+<÷¦ÑÂl}([M…è „9 Á¿;:Å¢Å×§.—²0WE…òݶҒ‚iõqÇ»Žªa›zViŒ®ÂZFµºÄ×ÀOôD7·²¹~ºƯ@x«WL)•,h"$õ)8Ýû~}õnÈ[ÔñCÝ‚–ز°qHº &´_`~e0éпf8áBØ_ÑÌgÏp‰£úØíКŒAcì¾dô´ÏEÏ4Ó»s×ÿdÿBþöMçÏv.I&Lã\Œ£é´kê:Œxi<íP*÷1é¾ÝÀ¹Å-DQÛ±Rl·½ù×Çç[ÚÁÍA/[g÷€œÌqLé Ö´7Ò(ÞÅ¥Þî¢Æ¹*"ÙX¼ÓÐù€³\ï¡’,§:ë˜kGuŒas¶Ë½º™"Q\?ÒøQö˳Q4'¹ôâ¢V_´\­ëñþcÞ .] C\p~ÅÄã Ÿå„eÉ?KüÛŠ¦×€^ªq°Þsn…­7>„¸b¯U€:™VsD¬s¡ñ¢ûӱǮ þ;Ó^5âf“­‰´Yþ´œÉ_zB‘6ó×°±Ó<} Œƒ°oìZh4zLÙ·x³ !Y‚â¾ç“xrè/‘w™Iç}K[¯«9Læ/s^:ši†~nQ0jŽ­ ‘üJZ½©ÆuKFûmÄÂÌvш—Yý|€'yYÕÚ¸F÷¨ží)ðÿ{<“Іòk ÓIÿ4X`×[µÉp…£y)9y #a| ”¿5Ë!!øÙÂß¾6KjÁWmž´Cß$†¸ê©lºí¯Bi;ò–½%–^8ëGKˆ‚Á"i”ÔV¨f fG?Š^ÉXúœ\Õ™LäYAqiˆ]æÖv.REÓ…¦cùµ¯å­uò­J˜ÖÔNaF]I6ù³Ðs|ìsrº¢ñ×—,sžÉÞ ¼í)ãey‰rT%ÌÈùÇù ƒÞ|KžÁ| ¤'€Ol§>^åNÁ3'e¯cÓ¸ÔÅÆV”‹øÎQÚ©OM ½l¢×‰h!c7D%T?•3¥‰RñHˆ0ïyV'IïIr zR ×<ˆSް– G·š?i×b¥Á6ɶ€»ø Š&Š4l–ñ‘;&9êåàÒPåk-*`×Jßt´šÑºYR~…ý˜cÎÞ¨«Œ2Š£TöBJ%Þç!âpðY;ªÞËÿ\tI] ä@Z±5›KÙá óíGÙ¬ ’ê>i- ʸ^¼•™Ö?ÈJ]ŠTqQÏPýpU½('`Že‡9ÕUÖÊM»<–îgÁ?øV·~{°&pÎTjzk¿ä~¾:‚"ygj¸9Ãz’#y¶£Ñ~‰<ŸÉqO»w‰ûXuƒ£œþ0œÃ¬ ÍË\Œ¿Ž÷On"þ…rJ]Aº?#¿÷ú;Ž2ý/Ï¡qŠG? @Ò\×. h’ïxûV³¦úx°ªøÞ¬úÇQ\=bYáÖ­õ’YœXˆ±øi§¿9t¤¼ìJ«”þCö¡\Q!†ŽJÇd3¯tŠƒ¥‚_ÖcjÉÔœºë‚ÖU?ë‡(:E«×ëm ±: ßâ¡A‘B˜8Ÿ*i­Ëg9©¢›¿¼î~Ú(NÜü.¾g¼ ûºLåQ[†]=±ï›C³bÓ+[3 oXµÍØ¥³BÅ|b«/©X'µ®’ YN9ˆÂe„cf ;VPp8º;e¤m®§æª™<ÌyÕó% Ø1ÿ\gù(tn+û/uôR‰Ôñß9ºFÑ{n¶"g< 5G~Ûšhñe'­ª‡z3X2úNË:#û ”0»†|“¢,£%ËBé5|¯DT‘N·ð14õ.‘ÊhøË•éÕ¬6oY¼7*SÖU^i="?æÎÆeš’l†Ëv}´gˆ±H_-m‚Ïúr‰“˜¢Ô€¾µ J£ïNDUÁ®´ä;šÿ{@ÎŽ…ÅÊ¡ª‡ 6"™ gæ;tÛRÙçmáfw@õ¸êB»L‚öaÃ'òµ+ºo:`ú›uàMçJpUÂ(zi Ùv먅ÑÏ‹mpÁ&6€RÜqªf› HÉ4 ðe} ¦T$Ëo* dX¨ŠÖ/;Z-@$¥dL»{#ñ‹Ê¥ô>™÷Õ'ï†@’j5ÚúTå£Dp&$¨ÍgâfÙºŒ¾µÆ‘hÎèg'hPÒƒ‚¶Ÿƒ¢Ê‘&¹f6ÑìÕ˜Èô¢dR\µ¬þ V0¿L3\ïþ±¢Â,YÛBLéÐOæÓKùÊRiÞWÈ6–.2¥ Û:‹FbðâhT4îÏ¥ŸÎütøñ ê5íýÖ#—寋RLÃhBXЭpl³Ðò)l: öU‹æ7V}Þ]Õ¬¼{‰×>ŸãÕš\ß4¹Rü£+H‡/¿buÚteqÈ«m]´0lÊ+Þ²yURK ªEO3q¥î, áýܹ¼Œ¾‘þ!­ùÕzÖ=›˜Â®‚„´ñfXŽ• ¨NqŽQçÒÝ´œ‘r™w’~FEÁÜàÉðØf§Œ’ º$´‹æ·_n3Ʉר¶fOQÔ$ÇûRr"pÄ|B”Ô%íÜ™ þõ^}Ç‚°³Wîƒm½šWŸKËDòë-„w ­é½ ìMüºÛß4tŠ)‚î¥Ì]ë­B>'Í'î–µóüN®fT§Í~×<'ÅÍþóª-Á»Â`{ÚI{³3¿Ùg&Ñ‚À*€+ü…IoQ:Ôm&¨çæÖ””¤;8=›dh¸óFƒsª[/Í~ìႱ¯&X»g¯–¿ó¶®*%çðékøvqü;ˆþñçðékøuü;gþ×eøt+üúlþ}®>âÀïy')KubÄÔ«’`èÇü”gŽ W‡Òà`ãt'Ì8Ð/1GDxW²1Ãåd§€È6{xÙaºQ\æ£Hê¨Ç[ð;§¥>rv&ÚcËÕì!^ÖŠ€Eà‘´øèwÒYûÃ×G/ȸߧÈóï:løû«ã„bÃóÏ©EøŸ¨ýìð‘g"І.a²ñÕôpݳ¿ÀžâU)¬ïér¤[€4Éí›.yø ¨, åæm:D9pá\TÖšL¾nU>p'ãµ’ÓLƒï•†lJäz^g±‚ÞIî ÈM< ÓƒRö¿ ¨!Yâz´Êõ —­©Šd¥!õÕVNvß’t|%º%!àIÐ|Ñò•ñ ™)sI1‡*-a ª+²€¦ô¼ äOt`XæVbEí¯)Z'2E½Œ:T# 83?aã±þ(Ü{ËúêÃiviÃÀÉgáÜØˆºˆéÇm%.o„™}µŸåØfÖ]Øû:1ä“J[ÛŒ ÓQ}쮞—‘HE}> ]×þãý ¯7ñå^ >Õõ½EŠÝìJüpl2ã[Jº¬ðΈša–+mïÓ.:X5¶"8 ?ž±œU%pK}Ãtã[CJöv•1*þs/ÅðÆë¶ Ñ)¨;«.d˜Ißl4­<Üòz\»D÷ÄcÒmyÐo*n¾-’ò!ÓõŠËnÓ; 5ð‚}åuKývšWÌ|²1-Ÿ}¼¼ÂZ»srÔðòp <½¸U훲¸æC §îãÝ·&Šñ9©Ù,|Œ©cDÌèè@å—+=fx<죜§…›`$÷^àŠ Þh_F-yq3!(Œª¡þ®5—è… ‡8 GÒ,) ÎDP¨}”3æ³×/š Ž\¿†³èxT8)±‹.âVpÓÉ>y˜ öGØpñ¾geÎ]©¼Fñgo½WzÖû*”5nÂøéº’¡hÆk rUd?Àc(ñ÷ù¶Êð?·êj¾ƒ;ƒ]‰Ñü$Qü™ÌQ¾bžFx³àsröÞ%éÃmñªìýç·8K1¬x8ÈÀuà TÍš“YJ[ÿfßã’ Å=0b ·>›ˆÃœÄæÐŒ-¬›ìªd4­˜_I¾ÙZØg^õjî5ÇïpM°Oøß½Sµ—;–Bž¤4‚`÷¸ßÛ$|ékW"¼•J`.Ú”£4º³ÙT/4Þ­búý\Oo©6ÝH“UÁ“‰ŽßUè‘~J€]—ö_ ‹´Ú ½¹Qs0VKT™ÿÙL¯­Üºåu‰<ý31ಖ‘X &ްÈ€X«5ÄÓòšdœ¬ÄÉç_î«nê¸ÿáø®ëÜ3)íÈy$ £#¹Y¦>O@äÓ´ììÏ&@YfÌŸÏ3ä)þ3¡td€^ŒCëAÒD 6}µäNR>eþH¥-Þ´ÿE ¥l²Z˜nñã¬o <^C™Â’ËKÑØ/ó‡ïCx›¾‡D¡O…*ULr‡IÓ§Þ’ÚŸÕ>ß-¾cóÿFýW€özA!þ‰/&äe;?'±ŒW27ª?£ƒÄåtüof’T^?íŽ:aT˜õø( À\Ð.½Œ’cQ§G}yAÙw§?½çÜžCã\2÷Ösa„Ñ–g6ôÙ€µQ¶|×}¤( ïìduøðˆÍI\/£Ç¦ 8س綯ÉwßÑ&(<.J5¹å¯•¨l¦p¤f¼Ï»ôš’,¹á.a1Ö=MꜭM¹–Ô«p¾å]„A|`ßó’aìÍë^uAò¢²V™d¢J¼G" &1:ð¶Þ*À÷þéI"Ò¼¦.ïËfÌ×ç˜í W~0%‘E1SFŽ.OŽÕ̳¦™sI;›Ú? ¾FÕ˜ °ôçª^ÈÆ 2:Ÿ“[µÐL!ÓdÅ- 0µ¨ð"Jr ÐqÎ2 pÖø©–¤G–«Y¦ÎsQñr6»­6lpÞ7´T«!µ’Ïú2|ûpbñÍD:VANaŠLçg¬…í`˜gXP]˳¾LžqïØ^1^ÍŠ×Ëvi‡úܦkÄçñoÍBš <:¶ ãIÁŸwt”Bê²Ùàâe1@'܈ÅÙôüTTÓEmç7’üÃØžÐ(t…0h,§cv¶Ð!É¡Ãô³ /8Lˆ¸1Ö™ëÊȆ`õjFÇô ×õCY!oVòèâõ2ïFÏEIÿTû©à¥Ô"¦mpiË3ŸžºÓO«S2¿cp+R§ÑÆÖ¥Š«Ú«H:;˜áhœk_|e°E{ym4±²;ýMŽxµ8|7h|èXøª>—?è U¨…R™héJ¥äsE¶YE.4n1É£ÚèìDÔŒt­ujåm$\¤u3cG½ñ–G"Ü‘>yœk9h@´S°–ä<üë¹·[PÁÊ æ)¹·v-—–¢ƒþU—ïnè¾»LHFîZÝ™ ˆc@÷Êç`ÀÖà5ˆ¤ÖñHØæüµ„d.#3Þ§ÏEã{#6qz±÷)‡Q$·TQpQŒÝ¾Ô¾ýçx{éÎh•Ï®R¿Ècô_mÃaÙåI÷*Ž]>åõÔíRÃp²Éà—î6–DV?Ð!?Š°ã“¶/±óf²à^Q&5NÍ?.)õ¹Oó ÙBUiªá£Œ=@ºUµ6EqÒ¦*[̦@.L·XáÒDÛÔŽ–+°p¼ |¼;î?å膦Ýü˜Xæ‰Áù~KªÆmá’æ?kÖýy+¡ëO£Îƒëèy­¨¥þåA;¡_2ñTza6–N‡¤÷Ì‘‡Ñϰ»ÝŽáDˆ ß%rUÊð½˜)L³ŠÀO¢½Ɔî¢ÅqþfÉ0±½E—užÜ—¬d¤þ9i¶Å0R´þ̓ =û?yäѺ4²à'Û¸!:Ýf:Y";·ÌÕ#]z.tK©†nªãj)syö=³à|ÊÏ‚:ï-:Ú™ª$õ²g  É®1§Q:XòV+×cÝ%¯yëëm²âàb)¨óŸ{SÁƒ:ω1ö—L12!ÒrÓ\ŽW´8û gëոɚ2?^ű­µ©Ñš¶4@eL!Ym¡f×U~"²ßp¡d–\˜Ð‚ð¾Zÿ6ZõÁÞƒWc¤JW·Þú´}e.½ù…RaHå¾vüUe Þ„ÑÃßv!H¬=ÔÕñOR@ œ«»ó|ÉüÇT•‹züSË“ÅüðuÉ ©µ%hä`Åò<ûwÄÄl­çÞü¹^xbè«¢ƒå3Rh?m\ÒÆñ/–k-t(Øë¾ŠþùÒü¬Ó#w=" Ðfà…’ÛE£¤Úv :ÏZ]4–Ø.;Dä”·99´®Xk%F¬â»v{œâƒ§r‘Š'"ãJPÙh›3ñÚþ”ªÎË'¹‹s{.ŠÉÃ(@—¸¿9@ÝÓÕ|5埾Ëy–ÛÒAÝù»wšé8ñÓ'¶>l4}h¢Ø[ÏqËTVzgsˆXY7‹á( ü›û“£ºx*O£2ã×õ&hVAuKî7¯“ (™²í¯Ö¶È‰‚%GBñ~8±"LŒøÆmÀäa±E®˜øªÉœ»Ä$ µê_•8’Ù¨mÔ—@]óù á©ò|HÄRÔ-„ ¸Ñ@µ5UïOq¨õDÇAå‹;({˜Õ©„G÷ohpLˆy´½“zíÚÙ76—}÷÷YÔÿ4³‡zµ7VmùjµfhNV29¡%Äi˽d'•hzØs¥þc-Îk þS]É|NÆÂ1„÷Á$Iý÷P¾ÝœÎÚhàFsO£‘Y/ÕWþUÿ Ì],+GÔ«˜óýÞs‹’nùÉ6ò¦ºšíºúGyv*²ÂØr¼ŸËìNqPcòàâ`­Jwrk;¾ ]H[#›ŸÆÞ*çñŒðD¯ãŠIaYÊ7«$'x)®Šð’#o“¤AÚˆÅÄ’]74´¹ìrdˆmš^$Œ ÄœÖ^;ve¤¹ëâí÷?‘p0Uì®Ù‘~yc]Â6S8¯N¤—÷‹ŸŽ<²ò ›€{Þ€ë>ƒfѲ´AAá©Hg¿îºva¤Í‡Imž“ ‘)…PU××2L±“{ÛÃÝz¨Ôæ x5ZÖºÝVÀl‚bÄÇ_—«Š”¹¬ÑÍŽžêì›Z™ÿa †¡lqƲ8,ã¾åÓÙi+Ú2*r¯Î‚M|:¿+çN¥àÐz°©ÌJ zþD’§‹»W³øú8ÈÛ ãlP õŒ½†’9ûåÚõîÔÐ0b¡l<¥+´{õï—~ìÖ•õQÓ=Y類kt’^­¼*ýsíìGV8š=YÛE5æf<¼KeÝ€ú5ÒöWF=;Âv+Côö“d#çQòÌE ôì eI6.(°­yÝÔL }Bæ@:å76Ñ©íŠrê¥`ejF€Á§£K²…‡~lCŸ I€&yâa€3û³-Éâ\uF¬ëç¼'…U`«8öo¢ýö…WSД®ÇøÖÇ$Qšx)´§(éÀ=¸—340‡u8†4t}‡çiž,­ý5´Ð¥I¥ÆæßW°¯ ¼nY¡c.w”åõ°‡Lÿ’©ÍŸ%x_wÉ{ž46¨¦¼Ö/ eå ãÃ~^H pcµ¶ŸèÂ%ƒêôüwø±»V\2šÚ;ó·ë"ïŠçO ×KùÚÔþ³pGaõSOµ¥U`|ENÙí]ŽÙjL_¥ñÎÆ–æ Æ¯oŒÉç“kq&>é:‘Kâpæß‹uÑôIÒnÊrdî¯6ž"LrY&¸ÌÇŽ+°Ž8ìc¡¦çñÍ%È]{wgh@?TN½UgÔ»ÕWÍ—Ï}öP+÷œ•NC¾Lvâ‚¶g–ZâtQ•K†Ë§·;ä,¹(Q/ÿ=á­2Ê}¾Ø´¬;v¦"ùýÒ©”¡åür*Ò\ÏÏÁ‡.ÝNŸ4­‰S_R‚uŒ¦°—NçUÕÄܽöä“.:¬¯EÒ«Wb#šVJmßìÌ-Áµzï›p¦›éˆŒEú8ê€v#ÝpÆrÞ“ý¹¯8@g&ØØÏ%¸L˜žÎwù'çèé~爈.w«H-µx]û¢h}ÔZÌ#\¡EWš`öâS‰¶·3hT!$ñÒk¸0Zçφ.‰»dfPiJÍ냂(xebÜV+Äõ™ Õ0çm~0S½S}gº²˜±w?&cÔÀ-)îêgCªH°’ !.pÃÅ )3þóˆkTIAu˜ªÉ–ºhm»ˆ_ë\Í™!ßkg0rsÓë_ùp­GöùóÎBèÓY”‘ƒÊîÆö!kï‰ËÎñ5Ü&QÆ m„mª÷®Ö^1UàäÐYš:òŠõs¥©¶ª¥òSZÿe/£*ÿ`‡ròÒZf™5Œ“ ‹é‰NJ°OSìͶO†[%óº`/$o\ÛO GôZúLÈf‚Û¶3åÑyøÅ·F(Ž]4°a³æÖ"(AãM8…TùäUWííÑP1ЗÀ“ì­¤‡Ö¥éì·£äô뜆»Ló²}2„?}èØïà’ÛÕxtL•ÏE ÇŒ6!f”híýÝ×´ÝYS!‹,„.vˇMb0îet7•‰›êͺfQ³7·¡½Øè6R•}5iÕ[#A’î¾60AÎ4Vs®åO»‰tÃé¿xé@#¬ÊØÙéìØÔ9˜ùj×bâ7àY2- ,‚…„‰ÉhgŽJåW‚™^˜7€?;Ü¢,b¦Ì{çx! åcU®íùLƒÍžüBåR E–YAmmZõŽþŽ‚“W¶“ÞvþðR"¿nÒ°ÙÅÜ&‰m|EõÊò±}37 %.ÞݧÄÏ}GŸ¢tÜÔ'X‘‹) ¬ UÖ*á[¢Ç…­¦êy\ìXï4h¤d÷&'¾>ôñù²>äez‡¨Õ¿ö‰­–£­`^×l©:+yÆëš`FÚˆÿkKOLcS Cd JWÿ ”ËklKçŒ[½ÙJ(^e¶«ÝàÀù—±‹;¼çvÒñ>…2â@3ßw—#U¯£óHÆbIäOo±Oü^~áœ\ÐÙà&£hEYiää²Cn1Ïmò¨a‘÷°q‡£,튱`ˆQw{]k2ªD\ê(²Ü;‹†]³7ŒóL­qV]~P¦?VÏZ¸î¼fMÅš[¾P¿r˜ŸB­Òqf¶¶3œNò ˆ ù‡_¶Âx gæH—ÄéË©œfUàæ…„íÕ‘„|u638oNp«9Õw“îxJ^™RÀé^Z²UŒó›‹óÁ<ׯ*Ã$§gŒ¯L¨w‹îܰ‰‹I±P÷ Cã•x[$. ð½Ó«l¯Ï  nK[³%J-åWSOà@Hg}ÖÂÏkHúγµ»ó‰°„>s ŒÙ&kÌï§Š1Á&´º &mJ:‡ú.œKnšR†«ž-'}Œ¯³®þ 䵫žW,RÅ@\Æè0‚2±Ï3\•‹—Ãꄟ3¢–øôqÀN Z©Gr˜Â³¼Ôt ?ÕßË÷X(*Ջש°œ3GÞiÇ;2|8lÿžý{´£íÈ{y¼AŸ­kr+. „"-Ð:±TFÀd(GªZÖ¬ ŒÇDVãS²$H_´bÓÌ]3˜cPÁ@¨?l4OÕÍCv;ð´ýN„Œ÷)µ3[µ;a¥ÈxXƦ~iÞ²…IæSô SÏZásýÅkª¤*±aN&¿°×OdÜ0ôp ×À 6ª`I]ú×Ó.d}×ǃUwÝG¬£ N„¢6¦UQèÉXï  ټ͡éÁ¥RÿvŒíÄâ°ž=u&ü_Ìäò€Æ÷6/jÑ>m5úuh”š›3R· ­k¢»ë.ŒRVå ÛT¼‡nLUrîÚ¿¥kcû§»aäy{“vÀ²®Ô¢!!–@fN4@ðÚZ2ñÃU˜=ð§‚ÙtnÓT;ƒè«ºvìÀß½¿¹<¿BaKZ´xmÐÚNþ×ÏŠ Àé¿—ÜÀ`\nCm_7kc† \,Õ^„¥nyMÅFogR°Åäñ›@ßýZm÷ sß>qƒ=J ºQ+úLKÜÀ"‹øÀzëYõ”˜óìUqT|xh`AŽ«ºCâ->†davw°nß8ŒR[ñ8Èi·LÛšðÿ9â+£w€4*m´×çï ý,,ú½™q§q't‹Ë]©ŠdœzBõquïç#f?{ÌÄèÈ>“`3§ù²añ´³ácã¬Öå%=I«‰^œN»÷„gN>?_ëç÷ÊËÁÄ!ϦLeË_þx¡]ècÔ>\ž;X'Zòç²=-¿¼ ‰Jä!ÔÝ “ ô…â†ÈÁ¦<ß´å!£FÊÐoõh¢H=Øöˆ¼°:Û‰Øå±Me¢Ä•9›´w*oïúÊW’öÓBJ6ż,MuP3NM–BNÖ]רŒe¹Ð'™¾ÁØ ¦©²õ3°—ÊÂÔÏð_Õÿ{á=tX`g¸”™hï-BÇ6òg6 MÂQ FØ_þ1|s13_æë’þÍñNÍ2Xˆ(´¦ªÐ½Ä¡ÙAíi¯Ï’´suhf"‡ª°(`Î]F’v8)êgƘ‹/…’T¸°·˜‹G)úüýnh8Æ{è] B;éêSýU¥bvëü÷Ðvn_“:ãØ ÂDsu9J5`ÇáÔ8Éuˆ¡n¿">3ÉÈûw‡Ž}ÔÆ(Þ¢î†+Ì®ÚA…W,Ë8âÎJv³Kgçü†&[‡´<.ñQF]ê~ŒÚò¼Á¿w•Ù†¹r]µ½+»7°¶ñgÔ«ÔGfxÿSvO²±!Gƒ« Çç$4ž !x\:V+G„}o5ž¿`(vç’ ÆÊÏccþh57T‡qî XÖ ÁiÂÌyø¤½X®²?ÿÑÉ׉&¦*ú L9ÃDŠÆñ÷£±“á~z"G¼Ùw .¼êæhAV4 õÜj¢TPôú¶µnGéZ •þQ©\h®rC|;‹Í(iÕЉY¤LÛÙŒ7äküçûý$¢õÚÖºÍè ä[Gg³lt”†#–¦sz«É=îWõ8Um|OIE D‰l‹ùýìÖGHüÄèôS¦­Å$© µjvg_+t•ß^®!:¬Š2ÁÓ¹M¸5 \ñä,³7©ŽN4&¼\>ÙqE{v®ä€ø^øQê2Ú³á—ÊÿcÜâ[ "éÇ3Œõa”.Ts@!BÆûÎQàö2ÕlŽ„µÒÛ¡¸Nq;P$ËfèÀ¯:7 fn”{â&õÖGÇÝþ5YäÖJ¿=ì½?LJ;!4b72žþ‰óa"¨ö]Ú"Úiƒ!Ý=—r‚ÒPäŠÓu%ך² ¥"˜Xç*SmDÂ~ðn!hl^Ç`þ>êž’t3ž×QBGibù°ÕÃóÙyä!åeÙÈžåÀ©ý§³,}ƒ ¶!Öå+¤Z›ièf·ˆˆ3r3ª–E#åïÌSÚŒ1ÞB¬Kœ€¶gÎõ¨(ˆììKi©$} ob¥}óaŽ€°ì~‰£a-OÆ­½ï>Ë¢¡-âбÅn_íî~##câú´ŸöégíÚÿmÙÌ_Vž~Ý&ý»/ûv/ êÐË꽿nÌ~݃ÜÌæ#'ßzk¯˜£¸â§u|šf€Ó>Å=¾gJ(Q7òý¢¶Va$2dLÈíÂ¥ƒùüÙ$Pæ̪ *ª¼aõGÚH²2ö\>§ŠH*Æ•‡ñÌ>ºE [ä™!qLZµþA˜\˜?TegMMÎ9…#µE}I^å}¸é¼S7È1àåÀ· ±cÄ)l1•‰]øêŒÂ'-¹Û(ï<ø[‰ÜÌPO­Ž×¾ð‘™l#>L¡-GÕ`žn ±¶µbFž(ÌuÎÀXÐÕ ã¸&êB|Ò0Ñ,ßÝ>Õ·¦[M5}O !LÚœo†ðð©™ÓÁ…ÌqJ^ , °ÌªcpµÇ:Îߊ‡© éY½Ê lìõ Ž.«†Ç9MUÐFÃÙÞ£KÌeŠe¨ Þ\¶ë]ŽÖ7R9Eq_'É›ÞAÔ7\ÒÓ {¿á`T6äŠíOi%W7çz‰ÑèB¸á·æ{¯´“2÷WºÊï‹ù?Gò—y&V¤|”G³ŸÑ(÷ .Ès7˜TÌErÓŠÙôZ Š}ý"¿•ÉÕj«“H…ú4é$ÊçÕ!DǪ Ä*»6LŠë¦®0Æ!±}1g*µ„QE‡¾ô‰ó0A šÜ2œÝ uûË>„ïõM”Ç£› ŠA ÑŠL¨î?r÷8ßq*ÊáÉ-å–áû‡Hƒ^4wYe#²Ir /öÝUþ]ƒo~„“ÏÅC{—=A¨#Ón%7÷‰(æ›ÏvÓKä×:¬z}jUö&o-.âá¢bMÿ@ƒõ|3ìL–-„7ÇJïùÜ’(¼ŽP%—µÐéB¯Î¤wÐh{½¦‹=F ÙUm$éXH>1ËÜz;Ä­§\ÄløêÊp–-Ð 8{ÀBáüËñjÍ’8yŽ›ª Îx÷Ÿ"‰ÒpdÔx=[ÂF9¾0–͵‰@0»‡b÷ëÇ¿d‹ö ÛéóÊ'©à­[AŒ˜O±àW¨¡ú8 †eÛÂëtŒÖaÙ]×qà„¥Ú´¶½Xê+'i¤¿ù¶®48‘`‹âi±no¿½tÔ6r³ƒa޲´P.6Od1ÖèJø¦B1”žVÚ4ïi}PÈdz¢Y³Ï‘¸š>Œ#›E i_;¶Ï¡.¯„ë¬(J5{‚8pª…d3oÙz#þxÄhÆÄ– 1¹{GöÓÕ 'ðš+ð^¨Ðw ¤p¥Ã×Lh¸ýˆ ¨‘ GÀœÆãÑÆ¬nÛÓm$§k4¡Bí®v=Ô‘‹»ËôT–¿˜­VÎJZÕp0@·†%pSEJ#Œ,RWõOÐ!_³<ëß|;Û(žÎÇÇKÃAncæŽÂ<²03#$kk8Ç©âYÍ(dWÚúÖV¹+‘¼ð`±; ‡ÞXn¹+Éòs–äxÁ¨ó<†S‹3pa¦ÇiàçGöœ‰Ó\¢Ü;š€­óZ6¦=B±522tRäX ¬&¸C¢[ #g…@…«Iw·î,~*uäÉ`Ë­J6’ûïò+ìŒ|ZÐI7­¾‚>ì0eµHá±FrÈŽõƒïÕÀKHüÆqÃβ¯ `ɵe¦ë¥e¼Ô¸`ÝOe ˜k ÖJô32"LÈ“#³ÛæïþTÛ˜ï“XhÜ@ÝQz»+AfBÌ¢”iÕ=l­ã¡ZÒoVUû,nàâ–¥ í_Ï(£îm&ŽÀƒx‘??ò „Èxp}Ͷ¾UüJ^³¤#°G+M…¹‰›vÙëQ2¡™ƒ î΢V©\3|Ùú¡Wˆâ±àŸ“99wÉêfo¨dâ ¿iÖ$Ò Þ ›Ü}~zY”ì‡%€R¡b£¢-`wô Æ‹9™ìíÑTÑå¥y÷ôÍ¿RKfôXçNã.axÊMYå‹®U«9àñÀ¸‘,“â¦EêÅbïÜz"îõ‚ ÷çyÀà•MUרÿ` |k¼äBòKé³² T#üv¿µc† ¦å«Z8²¿VH˜CS²^àÝ îY¡hö À#µA(|ùì[\øßr˜P„äÔŒÀw(ã³%?2\kÔóÙ¿*44°4I‚.håz-*M®ãùET£. ·p±vr0)úóò]yû˜ç"´XZÌÏâŸW—ë*iW&òºöô‹ØšÊ¾&D™.kJâ}¨"»¢‘•Õ =±ƒB7PBìÕ_s Ï(I•<¤)¡b7þ8YתH¶¯"FzPã„unË—Ãq$%j†š¶;•d µì³ŽðH¢nª¸“× Ñ épÙ¥tàFÚö1p„€eéY @Jì8:‡<$%Û14Û®.K÷÷Mˆƒ Û gØlç°´ÙÈ §Dª1 mN aÎ{b¶ ³ï ™Œj!ÏŽáŽa³\¾ÅÁb â!u3" ´jâ—Oú±6_jfd8:²ó[ÚwȪnò=‹yæÔa!MÅý&¡r0Ú=×Ååo'Å~óè«›Lü ¿gºy· m5/Ø&Xy«¹©¥“ôESt>{S¯ÔqŦƒ6?Lí æ`ƒ1¯8–L*ÏTUPg«q´lî¥ûš°“ëÇÕUÅS¯—}ª†ZGîtö?×r—þé©+h‡½ÐZÅÕÌÏí„q‘ýª—ä:Z ë`ÓXƒ¡qì"OP(ôÛÜ¥†”I ºSA*¶¬E+…RºCHÇm’‚G¶¹™+òcm‰\Q<“/˜‰[^t7¾ã èsä‰Û ×wjñ:Q6ÀXññ¬w´½ÒU¤¨jÝZDì GE5º8kŽ¥j`öu­BöHbO"S Ou“ô9ÍU½š!úÝÄþ‡"q.*ø0A`tÿ)„ÂDñ¡¸â%ÉyÜ}Ü%ö²¹@,¸9¤zX»µö‰# ¼-eýSë{Ìgr%;ÕòKèÝ @Îs/2 Ó¾PöéETí=a³ã:ÿ½ÿR0­ºF¥×†u †ußÐ6íP(ðwœ©SÝ­‡Þ˜[<Õ+c.Ý;Ó0‹ÙŒEs¾byS˜Ñ(®ÉQ7𪠦Ìy}Üœ¸ÓÿXzª.å÷`M1|-áVà vÊ¢¶ÐMcy2xÖÓ¼Î_÷ѳûf€ºÖ e¯o;±â¯‚5#&m*XÅK/ý»B»ÆoÖÉ—hµ*Jö¬9 ;¡¤þò> ŒF¾À)‘\¨Á7£ø)äKy>ò›¸«NóÏbé̃™„8X7c¸Š·7Ê Í\‰‡LDŒÖÆûŸWrhrA1 û3© ¯ŠG‰rŒMº÷_†<«ÍÒ[}nw’•¹µsHV“ÊËíÑÚ\ÚÆF¦(™ÎªsÐ~+Ƶëœw=s:s!wÐï‘v¤idÃ9žü¦RM4tÉÇ-¦g@¼ûi¶”j<à.æñ¢õN%Þ}T»@Nå­›q€½°oY¹‘gG·¡ÉVôüaÈî0·_ÏCN\²Yzõû þü' ,X8;1íI2Œçb˜‰_¾{ê¤w²X_ÉÍŸ‰y”+U5ˆ £½ß¸:ßI Çkaó$f}› 3¶ÂºsÒMßHÊõ¤ ¼Á›ô†¹òþïè>?"O1—ïÈ^GÔÆAvdãDAðùÌu? $ëðj«L&3 1°¥š‚¹>ÐK'ï:If:#8/íx_®@ijå3Ä\9·Ÿc¿$³l“£AE¤å©RbÏÌ®_YZûG–i„š^>o o3 ñÇG¤èŠp 'Ô›G]p­ž°P탠ŸCV%¥41¯$ëFqýÒ&Zò'¸O°’«õ%©»êðñ  ÝÀ†î†›+*š÷¥ö-õóSŒÉksªmȽ Þ ¦mžý)ö™¹†´­+ÔÃ<Úé“td[§#Ø)Á/‹àÉòOƒ]JG¹€ ˜?­ŠíYoô-ݵÁ=˜?s›`) ´kÊMÍÙy°Ù¥ ëɘ_Ò´kF´ÚÚ.·G>¨cŸ2œxpùªóØ™À&f‘#Ûéªù—?Ы¼4^Øaäv– ÓrWHΰU„LW)@âÏÃÖ¦J°n›Í]è°jm×t}$¶…ãNOµ¬3ˆ ¾Qí¾ÊMÒt-H±%¥jgìç†FöÉ,ÍOÁ³×B© µt‰+{Dw á€-^ã¯x4WEùœ´òSºòs—ÅÖDä—² â55×Ü*µÇ64TÏwš×¼ÓDš–òr…³‰Ú#ª…„‹©Ÿ)#¯ö€Ä@Î`ÌAÊHXÑõhò-ÖöKA°­§²×µN½ ¶%|I üaÙ¦£m‚C¬Ÿ~ÞùDÛºbhu´66Ѹ‹ôSÕ¸hSðFò«OB$™‚ýæêrvøä÷sņ3övŸ¬1g˜“ˆ@~Þ©CX–¡§€úæ¶êá?vqÖ1 0_OˆfÚ†Hï&ðŒ«í¥KÕáSNZ2%–¥Å;•|ÉOþwpG`ÂE*˜³&ÿ$5-~åg ­¹¶“Òh>—u¤;8ŽRéÂ|i.`F´íLàxBm»d‚±-yÇ|ŽÍÒÇ«Öim~XÁž£œ[ 22Jf*ÓM„y"ŸÞ f{—¬õïÿzc0ÙÙú Á®ÇÖRJáŽwƒöxZòÀ{ØÈ× 5^ÿsJs-p3Þµ@âîNÞ/¿+ìÄ= ÷ÇvÖBxM/\:Êæ™ƒž;d‚€Z¿¼H†”a¾ðŸv–¢£sœ‚ûXߺ&{íïáþá¡[Ø]-ì°õUëÆ[ׯA© ›/³ø·(Øî”uˆ'V xÓü!òÐÒmŒ†¶gÁµüYŠœËIæ/ÿgb`\+‚.!ÿ\±ÓH`ú ʦê> VLâú´æ|ûæú¶÷öîn/«V'Ï¿o«bÿ7qð¾­ “cßVË~ݰôÁFlxÝBæÕ×ʈM«ûké‚qL¾fœ9­ï_2J{QÛN™hïÉ92aãò¬±š$βΉè"ÎT­) ˜Pÿî6ÝÅòëÓç+GÄ„w27þ`.@ ¯¾èŒ]:ÙA²:›†ÛwÇœÚ^oN§¿1(øñ¸MÆô±øK/§X-,¯7†¡t -T Qšjo‘P¼õt0Ä`ÁÁ“CRò*É;'¤/Rqäí›1£·ió R–¶ðÝ%±KS\?žÎ¸ÞD¨¦MÀh—*°ìáùÏ7Ì(ÿ#:æÌóác‚KÉèòOá…ûI7ã9±à²#‚U0º6üGiµ^Çä)£H':˜ùàdÀˆ1Ú2Â<­é;ËH¥2QL÷gHÎÒÚ¬=ª¬Ù9` µóþdéö>®Ó8Œ {r\´\”?M ÅaÙ{(Ê H Œ'QмõõÒ·žÐŒjÄàü߃ÔYó‡WÂv.¡Á½Ú$¾5Ÿ3¢‰XȹϜúÑ ½ßå8/_›CÿgÍ›f¾äfþŸ¤`w$”ݪÐ:p§2’§ØR½n›ºm`A²‡ù(i¶%QM@aáib¢¦Ê°ÑGغi³…L}TÀhÙCn‘ÉE¿Ý˜=‚Ÿ”¼»í5ƒY KÛŒD2J©ß±H¼ý½°ÝQÃqÞב:p£ÕÙ…îÀL?ìÃmÛKÆ^1˜Ý3ÿEÏÙ>óâ0J0÷;…LÅó­@÷aÇ;…„®’;H­"ƒ^+Ý¢®Èè®Þ„f?7³ ÿÔAdz+‰Ý •Ûþá1»Å®¹dEØÚlú´zH < Æ2M@Ü[h¯Ä³eѪ˜uéN’)Rþ‰?ó§øýUG¤†«".8›KCNt3œ*uûÒÚ©7vHr:£ ›ã¯¬Üsûxÿ~²é¡rÞ³þ¡~ËUq4´ à½ø9ú6åÁé~Z­uˆ}º…±´Ãù¾FÞÓÚc.ôûÇŸACú¶  ÁÒ©’Ç Ëã;ðC%¯¥®]ðE)å̶r"gp€ÉƒXí½!ú¾û„õ¾.‘ýtÌ÷º6¯üh`Ïx!J¡8#9£’>ÑT#p‚ŒuèÊN[†R›ß|ã=ROžn— Ø#¡µÁøE&¦_|¹[ Ï\f ‚N¿1£IEAë9™Í£ºlþlÓo¿#¨¾ˆºîŸÐžìî¢ë…w«Ì|ÌnT¿éLàèwxò¡MI®ÜËO„aŸÂlT´ëüŒ³Mƾ®Ðzöt0Z Bƒ<é… \å¨Ì·ÌÌ{Ê+¸@ó„Od".M1¤ ^?EšÁ³}-Ž#V:’|Ì|¹i50"ÕþB/º Ù(ZˆáöÓI¡Yæ=$JÜ™µ²©ÖHž ’VYŸ· _É ì“eGs=©Ç9xÚ¸L1ƒ^²J¬*ûªJ&ïè2JÕðÖ¤§–ön=‡¹ÞÚP)ը迴2w8X …0´„´_ü2Ö e¢íÂŽ™2‡•M¶¶ˆ»w z>“ー+*…ltnà‚Ï`7¥VE û—6Fý Cqw1Kj~Y“à6ÇS'˜ÍÍ^4·(é›û¶O¬À+‘¿Äg\Í^ÚOøêD9>G´WG 3£ñ·h®ž†X˜l½ˆC¶÷«±ƒ;ÁÃv'O #yW{Óle¾ÿMg>£ ½t·Ï•ŽÛÑüZI‡ Š>ZHi”ýVsÀ… ükÌô²ÉEt·:YH( Ÿ#{M¾ÀÛ7æ¤xWq•ë·e«>ÆGXc,¹pxO¡Ù1·­|Üyº©~éãTjw¼úáèwpÝïX¦‹©%Àý5pë+bÔsßG ÛØ´T@«;\¦¹ôÈ‚ºµPQï&é0Ç?Ä{è‡jÑʲ$|鴟º ˜’çGÛ‰^*Õ§=“¡²;2,Ù‹ö\fa¶—™9ÁSvpÐI›¸›|á# ^c`³/¢¯¥aWµæå¥‡cóÌfš náµÄ…Ù-2Î@»·h´ë³,È¥®ËÏ•ºIž¡‰#Ê[2µ,{3ænœX/fÞ§ØïY€ßõ–¤=FTJáˆt*‘Š aW¦ÃÂÃú„ÓQãä:¡Ñ)tô®”t¶ð-m²«ìJ¸G8×(÷t^q#õ½rªðCÌãþ9üîWä) d³8F{q`•®F€ÀXg2jƒ¨¦„jï2?I]úa¡¦bB‰}ªöž-Ïð‘qQxÑs–…¬ÐIøòɲæ€Ç²r:PÆœL¡™æ*~áÁ}A4òñ‡EÙ¬D®DŽz… ¢h]3šÎmôŸ© ÒÏ\4q®Ú]Qo;Lþø%4ÑÔ“ÑÂÞ´1]o^·™Å¡ 8p—%®š&u7bÙ–<‡€MsvO®:¶±4&GfÓÒnT¥»k—Öû_È%Ÿ¬qëIü®!§”+¿¢N— P¢[/Gˆ¡»½˜Òç·µisOŠåÞ÷Xí'z¥šËØôSwc sæ 7/z ^j¥CöZôUŠ7ÃÒú[Ä_ì§Ü÷æå‘Œ%sÖ'\ÂëÔ‚q!K¨ß ÚZcãàv©( r´co5¿¯ÙjO£³Ó‡õ1Ý ßAeK9 œ[¢‰ð8Ьè^èÓHgª«gö1pj¾ÞË£u^¿$’²ÍTÒÃâ#;”§U‘û Ti/À¥PU ì8d}¹‚âØRð‡0.ÀÑ["ôÂ5oŸÐkÇ­ÃEQ….†ô°`Uû"ŸJ z„„3VVCô# Wì—½Ùh‘',rO¸ç£‹VΑK@üëm81[¶½/WÌôѼ®aÕ¡¼Léâ¯-:n-®Þ¨hꙪw¥×üW£ôŽqcÜ]hrQ°1ôP4-Ó•o3U“t£øe…MNyk»w Ý_HG˜¬±7u€Ý?ÃëãOù¶m½­Ý\Ë衚׮ԗȣ6Å!´±ÀOó5ïºöqPt‡…Ô~ÿqâµJî.w€;7Ì–ÚY¤ä¢Q‚…¬þr¬ÂMk§¯›ƒµ]õ bÿæC[Eµ°iúšª`a$un÷•ôÞãAKÙME Ä^+—oÓå_•¹)ï™ðªå#½·î99Ðè7õ ‹Ñ¡0úÅYÕ1“GÌÙÌœÙ}w;p¡ãHqúYGõ按Äïº0áK1pÉ ¹T/VÀ‰PÌâÕÃ)êtãØ¿f;ØöáÕÖ%ÝËs=pY …x«ÐÀå·¦6OeÁE[zè7C..KÁ ƒž™òçq]–žT?>î@¾¬yö0<Ý{r òW~Áv`gFË €ð1_ ¥é|¦bvݺ6fC…ÊPý`Xk%iA¶½„‹ªÁ´—¸E¥ÎÎúkÿ€¶`Ñ”)‚t3&5vãMwb+åæsM’j½†A2¶Õ<ôFjXXs(|8 WŒšIDà ÐÍúRÿRçž!G¡(àtIÎ.¢À6ŠjóZ—°apA#¸«µš} °KN.›V…ÿ{>ëøŸ"74ïÿSLò4 !t’QVëC*÷ÃܸRnáNh²‰ŽÐÙ¦„1Ä<ÑvÍ–; ªÜ0¡$mÎ:ÒÕñöžá“z”OŒ\øL—1cƒ¿ÈùNGT¡ïÅë,©rÔ\Ùçq¯°× ñö;\¦^ÑÆ|y†à!ÿr6ˆ¥¯àUÇ:#%{ŸÇ§¬”°¶©"R (i_ÔÖ…ØbµÃø„ηç&ÑE‘¥z3Þ@Ý¿b5Ÿ„èB,"¹; T vlbð«º¬Š·ú¸N!äš³1G–S/@uÔŸ©ÕÄ ñ+*mÀâùÒžKЫ*a‰¹bqñ7W¤ç#€Ä»‰Uî±ê‚½£fáAûòÆ“žXh9¾ÕՀ߮M çsãê+PqZk‘Fv®Ð€¶} õ£»ê#ONú”²«1eØgn[‰ÍöÈÝs vZsM“ÉyLÇ£¨Æ-Ðg{ rÁC²»¸ùޤ6aAèå0]šu äj‡f)<7cr¼ó5¼ôþÓdM1 =v£ÝÖÎÜÌž}绳[‡utκû3h·¹êýº8ÈN]“Y>-Œ^ŒO‰ þ}ŠÂ½Ð9OC –”EØ‚Yð‡°jø´äSæØk¨C6èri’9¦-¾à»pü%‡~½à³ç þz¥4M fö-ïɰ‘qp¹òîºû’4ñ-hkq·ú§9ÇÓZ¨ïœz˜ÛQÇP‚áÇðXíÀ÷¿t›f:•[¡—|ÛdXßöF@Šâ%àd¤›~­sÿ3[C8nÿE±<l+©Oø@ÔÈ÷òÈ’to*Ó^+±jäÔðƒŒú Heœ÷W¦sH}Tð±ãº üp9:—J0fžÝålû'gèžÄöUÙ8ì™DËEÜÕ«jD9Á™±9fƒê˜°„=Ý´B©¤ŠZh)ÚyH1 Ç),Þ7Cbÿkú- Û_‹#ÿTýèAÞžèiqØöÛƒÎÃ,pªñ[3[Ê àHÚ«fsG:5 9pv Û°²nRÏ–Ý÷2ýQï°¿ï£T´¶wÌ *ñ²XÏ“ëñ?­ç -O¹Í ð1.€¨j=ÁÓ C˜aPãöÞÿ[¨ÿ[«÷Õ«˜ýºût#öë=õjV¶Ìý¶GíÑßÛ @ÊÜe?UØ—ž^†¸h×ð>─K”ÓäìÂé°“hÇ$sz’¡ØOðͺdÚêg÷—áLQ—[Wå(jT÷kÆ‚˜ž?JË™¯% ½îgzàŽ‹²,ïˆ˜Ž§´Ró1¬ûL»¾^ƒ¼ã[˜ö™H+z±™ãs`XÈ0÷‹øöYóSSvÀl_Ið÷Šy«ys·f+¥ÖºõEiç@9úÞ¶ÅÍŠyüŸ)H'™^Ô"¢SSg™”D¿¯Ðá¹ ~£êuðË+ñµÊžrÅД⭸dGÒ ¦-ÿP‰¦Ø´Z±¢°ª23É!ܧٛo #v%³Óv³ËM-Îv³ìÜ¢9Ó^ׯádìÒ8(†·õ[þW (9íh‰@v¶æõÖ7Æ^)µ³l_‹nƒ=>#¹´îgÒ%ÕÏñQ^RM]Å •†O¬F ¯2ޏ7Ë£Y5F¸‹‰T¼¨&E¿>P~×ÿp÷­š=²ËfÍ'¸‚ñdÕiyùPIf(HÙn0¶·(:¿bÀLª6_«º·2öÈù‘¬Óg½POÐ}(Þç‰_”ívTÒ;1ç®fÔá‘çħUlÚW:¸¡{&]®Ð¿ˆv“Ym÷r_ÉôA¤¬s«H±—÷Cp7‰‹ÍåÇ ~/Ü¡zOû¥Èyź‡ 1ð#@^¯gÈΠbrž¨ IN^í<ë§.ǦÎ+Zdö!’>Ð&¯<ÞÄ9ÄDã»#ÃNf›º†cDò¾ÆÝ`†ùMJÿvE5Ž-ªŸ>@~ÕêPíu4Å¢"lƒËós\Õ™„­Ë(e8i$µŠ¾c\ÿA‰°{µaLØýþ’ÁòE/Jã—ÑG«ÆÖÏœIÚÙ¾–Ôøõõæ×„/ŠÐÁSø0…ù¯»‚úÒ1ÁBvNîã GXußn“"_.0‚o²F»· Cîz€BgM²,Ó?…‘N4 Ñœ&^¿æ‹Up .ÈXáJ”³h-%\/gx½xîE.ûPµ{ÌëC45…®T¹n‹`‰¿Eí×të:ñjÒŽóåì|1{t@Õ͉CùèMG'À×™Õî”(O¸sËLÖ|"&¼ €Ekg)óÿXÏ­ÞÕ¸PΚ9Œ¢LüyóF4h2ÆK.=ŸÅ†šŽê⌴q9ÞNÔ…%zÞ;ˆ §£©í[‘ØÃUÒ¢^Ô»Û*'õò\t:@èõyô_Ó¾>3Üäîó‚_cf¨.ÏóÝ’~Qof;»Û´ÜÞ„c+–&;¿-xôC[ëË‹wõñ±æÔˆÂø:®1f”#€õ‘ʰ\5`›‚4VjäÎÕJßê:BëÒ¼®|)ñù…h?Áb a‘m«òQ~¹è®¶¯³±íœØù¦Ã5 G‚®¨ý¸uºVûªó"vk&$ ËEWZJ \#õ8»îr(Òˤ! £•Úqâ Ñ%ìiVùª6Ðö­V_ó ’c•¢ 3 Ÿ@ PýMºKìw±-‚lÜUåM‚€ ¾A[ØdpŽ8þÈnøW ‚ó¼A$^4FˆÝEè};R šWATsçl©”!c:ƪ ÒúS:ƒ½oz˜¶¬Ý&aãÛ ¯‡ZDKÙ70xÚˆå ½XÕPt®¦3¹=InÛ»Ïâ7-¼pæÛôá‹D!\)•œXPíSpº*¸“èL‘›dª&…Ù‚j  hº@)LE_ÿdbN4¡ú`y(Q+ì¹5ì.Nª4ѾÌ×ã”KõÏ71ÂR¯øM)m—–‹ï>™ö””³yV‚×ÁŽÅ~fÓ¨ø“Ë6Wk}uge‰/i0Vû ¿~ A†r ‘!㛼oPó„únÂs­{o›ÇQH"@?j«ù—Öi<.ò[(ø˜qlѨ½©5Lnt¬ ½`li˧gœû[ã¥lJ‰p@ä÷Ï -—ó4Þ–YK¤yÿ˜sè.¤›XvhŠRó#~A Ø9d~› Š4¡RM2¸‚Á¾Gô2Brú,±Be½|MaÉqµ˜_ð8 ·Z@wãz&¥O„§&kµM T+}Õ±tMxjì†xÍÛ¢ƒÄ¤z¬‹òAB >^†©h²e]g «ݧ Ò4彈º*P›vý0AÍÖkÙ³Aˆº=Q˜—O•+~¿b-Ú¨ 1 GnëxÙDï´Ýu'f*„Û Àÿ3qas ¨°]ÚÞEú“Z$d0ÁÊorúÃQ *!Žû ìLgYnº—CPSÒ¤FŠQEñš WÃдúÚáX+(Öæ*ªÅ&þr³ÓÀü»£eÄæ¸0a%ü#è7¢G^W™‰h;QB=}ì¾ð&2‰IË6͆v¥éXÒu\½-ã‰{|ÿ|9KÕCµ;V[&¡Ó;h_ñBV¼YP¾­OOC²4K°+‹¦EaÅ·C×{L\ïí@/9Ì_7ÖÀs\HwD³¶ìjï¤RÉ`NdºQ££±›™D—?ê¸PùP2¸‚Ðptƒ%q:ïé<ñ³ŠÎî’l‰g"ýÿàAf¦H|èaä¿A§ßµþBGJ«Lò¥³mšÃ-'¼#kR×Ê7n$ç† J=¸mËðy¶4¬.1Á¬_äfß’Æ#ò¬KºJi$›ÿÙic09hO jP ‡ ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cÿOÿQ2ÿR ÿ\ PXX`XX`XX`XXXPPXÿdKakadu-v5.2.1ÿ gbÿ“ÏÂû1‚ÿY(ò&`™§tHѬô6ZÇ2¯%û2M'ÿÛ Ú7àÉ÷„R^ñEËÓX°†ÈOq)DÑaùˆÙja’Âa^j "•a—NÓöü@žØóCf¹ àl‡„xaÅqßY6t߯Y»'ÚHÚ&Ÿ‡V´ B+A. +-ýÞMR1v pF`uÜ·•®é:ød¹¿è㔿‚Ô*ÁV-Œ£óÜ¿NŠ ðu-s9¢!­³SÛÖ_^+ýe(’‚›¢aÁ‘CÌBÂè39 «ÃŠ«( ×»ËwŒ1ÙI/z§N¢“Ù”ê¼ïeUÕITÝ ßH¢ ØL­Vj°9Y­¡þ–ZÄYŠy”ú¥5Êú8©¨¸;º'´rloÐ.æ´Icžf>³Õ³Õ8“8å:[s¨TÁ°,¾Ð§“ÀÁ‚åØŒŸÿþß´šiÁ&öÒ‹€ô_gKKšåSóÙÐ9V ZPü«eÄx/1U¨ö*ÃP‘Å]â~…ã=áÙìæªÚ‹äjl=Ô)R®HçÔ¤ß8”ÿ3¥ÅŽúÂÛÚWSuù†ê"3? ñÅ¿Ãt±ÈX/¾,Ç¢…·åoÇdcgº9Þ)?þªµÉ¹ýCbæõ#nûKÐmâ!„ø…ȺJ­Ô$&R&žÔòw³ÙÏBòÐ,áh³ÏµÆ² žÉ•7éxàza|¥ºé†Z½{X;áØulªÎ£ÏÂéPQ9Žôd/Ù·)­ I¡<L]…ÈñÔÌ:•“xIl×P]¶fYyËO>((„†Êú àûZ™@¢rP)Ÿ Ø7OKâÏ6˰tçÙcùÃq÷v—ƒª ëÜñU<,Özœé̉lbÍÖ˜O¿¡¬iw 1ÔŠ¤:‹Öhj€b^3!y6ìTuÿ&eG+Êæ¶ªûø‡61oÆýà=–\ú8N§A9ªˆTs¯¬¹¹(Ös¤wúUúÜÉÁå S³d#Z;l¹Û£¶±!’_úk…c§gÙp”Qµ÷ˆ¶h/<Ò˜fù× ˜!¢.3rÝH†îÊ"+24ë­óϲ‹ì6}·x^Ï$Šuq¢ÍÚPM7?Ø\VZô]u‚„>%|ÉÈ¿½"ɳÎjT6 ‚G[S‘ä35¼‡I÷WβÊËhÐS´ÙSþ_-ôÐÄBڭТÈaØŠpÆÅJÇ‹'àFòDî¾pl¨_sü‡ÆðÄv†?äÐ6¿mìd?V¡óŠ:]²Æ>pý!D¦æ×¦Ì¨=ÆÅnÝ1^+ÎQ±þÞçœÃ‘nCwTúSZÆ’CMa–ý”£’_²Q¥8~¶  ¯üB=)ªg4úoÇîè]‡Á Ç£¿¡_¢á)`}Óÿ…kiÝ” Ñcöò Árþ¸€ù¿%X] €/*ï)$Ææú2Ïs2Q~xŽ4$ÈEaÎŽ"Á>CS¤¼üJá·ÀXñ¸O ›%*ô!Ӌꆣ‹¾aà8!p¸³ý7„‹>PÎüèJN&tB !‚NöÜãoà¿ÿTS°¯7Vyýˆ†Ì¿–4x•á m1•«ÇáHÃí£¡øQ ‹ÃÝ]Ñ.šöÃÌ,iFHÊôŒÑ“Ÿo+dsÁ#ÞÙR‚i©rjÙþSŒ– E’-óöN†vå Îé z·RlpLd¬‘Žå÷Im€ŽÃİk YçœåI?êðN_Å~>VÐâƒs¡&½B`…QJíöë®ÒÁîÌz¢BÈ9^mï[‡=)#‹Sü;:ÊŽ®-9 âïEêº[3R[½ø X Ü땬Šônç ›^-]Zùh½c¯È`R£ÕA”gð³Zàpê™pðÍŠJ_ЬôF2¿InÔÉ!WHquFRQž6UQcny”’®Ìүϙ•½[/ÊlÚW“ùŒƒÎ™!ë4³¨gô‚ÔíJ;B`õô5Y /ƒÿ¹¿Þa¼$Ã/âo'ùòb¬œ­ô½ë´yßÄmÙ”c•oåµ®Sy{fË0Юµë#Jø¢LI&Ôì~&æ8‹ÜÞ$Ý|¸ ‡¥¹Å+ôÁ°›LšQ·L:¤6&f-næ¾’2<Í ²¢5¨ô¶fÇcîlÿTN¨ç`ºée¥jy²d=Š­žÂFiÉóùë¡9£û3Y’Ib<2Öø "ˆú‘ýg/³ë3Ê(·¦JèyÍvÛ"=3Є¯±{Ãö¸O¯–9â?‚rz‰¨ñWÎeÌŸ¯rébaßôqa{ù< üµ^¨M[ ÈveÅD”»3EÔå’_ÁºmtЍ_É𻶆˛‡RªZ!áÜ ÌiCîÖådƒ+MQéòesö7¿D.rͽ˜„åC'"&C½éÄ äÎDÔŠúôMǯÏ΢ŸW«‹µlÆ h"±ß‘¸¾h¤•yÔ¨HüÚ=FÉòQÁï‰uÑ–<”ãã¨2ÌÉ~ã“(eÎn™‘8›,Ú~”Éi,jÕ0pš2”5ÈÙõDÔ,ySß!Cú²½ÆƒèN‹¼Rúz¾‡€|%ô9¢´{¯íL=VI˜ÎjÌ5¤äÎ7¥­ç·ºx2)ɬ­üÑ|,…`&1׺¡ÙÈpÏŒ²Y÷£ˆaˆSøyùIaKŒ$‡U†º6¡3ÀÇò$Ò‘Çûà€HÑkòÕ]ÛÐ4Øq |¡¶NÉ@Ü™ù–ðcÙ‚£§ÑYFš¡d_Õ׺¼¨…Û k¨CvFž¹ÆÈ=ð/눺뭀[èzMðþ¹ÿ)PX ¤Ó×Ô¹ºÀ<;E–:ü¬Ù*aè|=[˜»WÓŸS`˜ g,Ɖ öô×riùÝÕ¯‹žê½zʰ®ôtbìgâ¨Î.;1+vѼØâ*¬Ë?3«má™CAX2¤¬¶È]þgeyê!‘TRî pë “¶ÿM‚{Ÿ°°¹¬©]ªlеo¶NÔyÖM¡b0@ÛÞÿ i®¹˜VáÕ¾ïÆD5ŸÚåÀ“ýur:ªi¤€aqZä ù ÜëêƒY°¿‰SS›‰Õ}ߪÇá¾øo<~{ôÀ­ô¹¶•³p_]Ó&Ò~]}§¶@|:ô¦X7¾Ó/ÿ0> »ç, P­înoÐUzš‚­Ô&âˆ;&ѹ‡ïü…Ï{ô™jxÕz ß úzó­t¶ùΦh\–mw"!F|Föä Ð~wN½@Bß+iÿF¢§Æ—¤Ìµ yÔ{Àƒ:˜q¯1Ñ‹eùêüf]ZŒŒýv*±} ‚#q´…Bvªý=þw,”£NÇXè›ÿgA(¦/Ê >×$fáLØ›µ’﹇«–r/Sßùû$¡‘}¤èõ6¤iythǹ#”Ú¤qš˜l¨hµ?GIÍU³€ôSJy!Z²£•gôî8RµÙC ÁÎYSl3„*y”•y¸Ðïø6V¥¶èÁÝ6•!9»Ù¦¼{X±‰§õ´•×|{;Ø VUï*žFÞÎ5+óXàH9…–Ö=É>ÈVC,)¨‰‚Ý}xGPõ…ù.…V†Öãu.û•ºF¾/5hUI àiQë¥EJ’ô\;ѧÅ|ûÜŸÎhùèYæ+¦X°Gãß"¹$xíÛb"²5³˜óã>- 5l!*vÆèŠXú¶!DêhÁ~ä1!'ð>`ãçý:èÁ µJúõK 5lM™…¢ù.Û¤¿ ¶U§Ä•£¿q4”úaJúmúÄ÷S+{zÌä)õV)›!³HOŽáàfVvQhQåyybãaýÛbTûĽÞZ6$ àóqéä¢W ~ÐõVe×4…†`V¹‘K~… Ü_ô„ï†þ©ì€xÐþ©¼SÔÑÞDGYd”—bq»¦ @á^vR&þ5/øzXÎ)Ø©®x¥±Q¤þ-…ÔßQ²õCÄKìD›ÛˆàßyQ ¯»¥¿lϟ¸c 6¼Ü¼Éž ¹µÛâVÛ7l½˜Y Acfuø5œ… j›U6Ô!½Õì»ç~ŠzãÇç4“º¹3 _!Þ¼g¨Šrú¿lÆòË•(&ûæžlã›-a‘±<ú€ù½Ñ‰wÚ»¥'0hpžÆ‘…±]äç•Nln kIìɯï}nãŽ)d×&ôQñ Êà ç|#³e3ŘſëRLSmš“’š…&ŽöCOœP¢IšcÈLìáçÁîˆÑûù¬C@*‘:\%¨o˜užèèƒù.§¸èl9ó_VOÿ Û‘½›]ô£­;ßZæ ‰"yðJe·@iÁENúäN§±ÑÕŸùÀib;*ZA·ëiuKÇ‚\]é8šW«¾'*èeÃ&›,ÛS„ø¨×~º«àüx›2`bˆô~¶EpåÔ‹!€y&Ç [M©$ÜÜ|4r%AëNp·Þ';sjñ‡¼€mîË(Ñ©FL½akè:e]Ãjñ†"‹ø»$0bd6Ýãzšñs,¯ôRù²!}{#7®iµ”µ,¶Óç¼ÈÕ6"‘MmÏÕÎÙåxì²ÃÉ/ žO.Ì«DpD…1 ÝÌtÀ‚.2׫±—ňŸ¯'cTÄåý`üc¿®›è“àéHŠØ}”ÿb,ùw„ÒÁÚ!7œ”[ 6[}L» ©±ÂãS‚k7\` {ÿ3-Z®mßÊ‚T`mþåf¿ùÅÖŒ¨ÓwÔ_–}1¸¤EÓË ¤à(6›k«ÖÍÙMf«³*•9Hj#9/•ÙŸi£=7ù"ƒVE5gºÒ Síþ²øôV„d 6ÿ5/ÖïàLôo˜×ИÕ+¥¹0±ÌøbúGhi‹€ (€Ìá|eB§Þ–øôŒó»í0޲’?]œöMQ$­šÁÁ3;¢æ»¿c€ÉÈÊ#¸á@zsYQÛYeE:¶ÂÝE½/üyö¸·Þ¼„„ž…ëÅÛ‡Q¨“ÃØ.5UÚbé²ö}*/&˜ê¹ßCkbùtÇÇᩱøjD? BÌ ¦ê/–û˜sPXDuÈÜuåhwIß¹øèƒÈWι-€­}nŸSœàÀBüÛÜÜ\¶ ôvŠn€sƈÏýû‚Ÿ Ýù‘\Sr4]VS»•!Û#s+)¾Ì•æ> QЇô'’ëu2.<³·¿9—à2ê'*J}+Hçàó@ï~ªºX7À tÅÞßšŸ˜ôYnrñ˜Ãòv'n ¤š|€6ý¥Óào1þïéoFså?ÐùÆPéÀŸáQä‹•Fß¶†f' ÃDôß‡à ²Œw§(0KK.8»ªl»¯"Ð;ѽLEr¬S©¯‹émQòE'. Ü?Ý=(;'ZSA¥×—dYø®Eî@ „§ÖÆÈzR¾U´–„(ñKöGœBF÷ßîVΊ°¢„v½cá_õ©påÌyõ]áëÓ:ò¦n·Òø€|T1K˜­r?5ÛžvÝSÿt¶Š?ZkSI6‰YÄW\èsiøÿ\‹ñV·Ÿ¥ÇÚ—,FVû3¤s¿” [„Z±|×xŠXx"yv¶ð®‰å§;{&ªÒQj4Â#A½Ö_‰Œ†€{¶Ûê—ˆ1ošº‹4Óƒò }þw,°Š€C"¦'sC¦cOÆRÚ³ë+hÖ/#•O§„ˆvB‹u·˜ Ø1”LiùpXÿ\‡MŸ9ÂÎ't+»#˜JyðHr«Ï»‡pòs<ŒÇ ,Â…©M|Go @ ¨ÀTؼ~¦ì¥½ògf4Ýš‚ã`ËÜ€t"'ΜŽÃÊ}Æ„g] küñ(¸…¾`×çªÌ„PÇ…‚ Ïõ„Œ7 nßbŸ].LÛrv7ã,p táéö{å´=õƒë’Ü£3<±Þ¿À®ëÜÜ}÷wk*cOÔkHˆý}¹d<çäÉ*M D=¾«pMV¨ê0^¹¡¡|¾vëPè_}ì•Ý^(œÅ…œÈ° Óþþ¢±‡AFG 1\EÁÛ¯Ž—ôaÃc·Ž=bð&×ѰÓTî÷)B 'À …„ÔEÞ‹úÈ|^jƒ&)w fKôÑ ¦/~=@‰!í•fWÛ]ºœÒ¾äJÇé¶KŠRõ”¨cŠæ=Gu7|>!‘ÿ[ËälÝÝ©’qó—T[O`áh} š_­S]V¾»Óá“4íí}Îç«›Úä žûÖÝ<“‡xHbÞ8)`:»l¾ÜÙÁ¡R Ñ÷"[¢Ptˆ•Kž€ À½D¥øˆ°IBþ{|0$›N Çáªqøj$? Bã*y7¹˜7LUa¯5Œu“¥Ï# “cß¶ºfä* «©Jô„êQ|íø»xöCwçMÅÛhR…øéß«¡÷ê§»:÷ÎþÐÒ‚þv¥ ¶Ë 6TÞ¥ý–Ú+¢rW*¼~|8Þm× ^ä½·o‰çåê«Ü¨ý¡û7Td¥u÷gQ¥˜5ùÏ*Y QGÕGÕíÑ–f…ú¾lÛ\Æ6sà ¯ÉK0SA9"ã;4¯wÂà ÒZþF1¡pz—Öl›²½ˆ oy× '§ pÍßèCßÚh„©/¢#þî*˜¬|Y¶d¿h_9€qL‰WYmG¨P†C[pš¸&¯JUã<$fÞ\2(¢_ø??¸ùÓ!ŠcL_a#J¤Ü²*¸J:C‰ÄõOÎÜÙe'Ë',¢îLêñtƒ1WÏ;ýÓ3ø8(óË´臷gç32„ÇÀ+$:˜ç…羡A„—*–¢¦íšºsníñomˆ>qÄÙ zƒ™z™hgEʃßrá¢Õ&íFǯôÁc¤üÜy×I´Û÷«…ì Y¯HlƒhR›`›Ö§ €.r7væ8èc¬ÄbIï(‰ÉÆ]ãÿH+Ÿ/ÙÙ çÿ=bÑF(n8ß kÉšÓéÐãl·«¨psKpŸà½Ý4Ù¬fÏŸ25:ÄR0dh/£”P¥=Nß6?$^ÃèðfHÌnø²•v(ukÂ" D8”ªÛ¥¡ä­q[;Ñ ‡í4>^°×aB g¼V¡‡GYý5`¬¿®°ªQí¾›¿ÂþèKž"™\¸*¦‘¨×ßåݶ=ÿP—RóÔs_à£×‰VGš£ËòeW徤ú—1‚#°ÄÝ…³Ûþ9 Ì=l9´É`üµòÚBSá._ŠÖU’ãÖ{‡/ú†OºªiC˹lï°dÈO2 ^GÁ’×ëâeé¿ÚJZå{¦E$ƒ~¶¤YR¿zDÞm¦ƒNŽ¿áÙÅìÓ~j8z×{ß|áÙ«×=}rðʲ|(¾¾©BíótîßOVjÝÙSìþ CÇá­øjì? V\’FÃ[‡%àÎÜ`“ÿrÛV î*ûʨÝ»f –k'¥7hà6ü½•m·Õ ¢ôvN1~ˆ…Ž‘¦ÆeÁ¼*<… uÁ2ŸDŸž‰12`(ãSÂÙÅw_˜Ð(èCM0M•SÙB0°R$5ï« 3¦C`ÛqÐj†IâêèÙ ÔtÔ‰Às ³ÌÐñpÚRõr‚M)õ¦¯® M‹F_Fz?&Îk‡.FÆ¿¤v Ǽ™lMD‹¿½rGpkÐ á ÛŽ"CÑ35fLC·Vàâ3ŸÅåm•ú-pü¢çj\„ Û¸-d`Σp%”+!kàáác­«ÚõkO„:5W¢–oÇ>ÌÀŠ“ì)„Qcå’‹tœÀ¾³hÂñÑ~µ'ºÊ$Ž0‹®;û) Fª×j*Ü®*@úÔÒü¢l¾cžä´ºž(wÊ!¨õ9ÒEjB–Äï-°í1P„ó¥¿ð+*d]½:Äó†Ò?+†9X Z@‰V¸“Ë›DÓ\5…CIWå ’6ú_Žùûn‰ãÆelÌܧpO)¶ímyÝ“ˆ¢·Ë›çÍ jˆ}Yj3ÞM!_™5‚„¤zßqƒ©B$€§ÝèÖx!Ç/Àžs<â`6AS†¡1 –Xó€°Šfz2ŠNƒz¯6Jþî¹”J×73»KÕ„¾ÂµNó[{ óx5–¯6ç|öMáyOXt ˆÕJÌë·´~1ã:E!Ç­xÁþ³IùEùqGþÑp•ð_¡ÔUá'í[ÉW!Fªf¬ ”@·OAÛIÙ®bsCvªw'S8*’ÜœÉç®d…ÍÄZ°Ë^ËgÇJLÏ l( ü…ç þP^ŸR^=™ž ã$ó-ªÞf}x À=5D’`0×üàÚÐ0n_Ïü„Hämë¥/§ør°Cbo”%Œ!í _-´su¾6Æn/¡¡MÛ}_ßhþ²¼Nº1jΠ²1‹ˆwÕ†"Ââíº_‡û;È–/Õ}µçæ»B"-‘œÂnJ+†h†ç•4¤¿0ç › ݽx«|ÛÑï;:o2Õ©’åå]@©Ö¦›7ã{úQ–¤¨W8•6J™œã’t÷`æj~}ÝCrºù HRCàò “Ž×ÈÊ‹ùKÿ+9.GèG>4³aŽ<íQÁsç)DÖ®—¯’sÊ>ŒªÂœB¦MfJwb!jU¾ œe„óà5€&´ËF ƒ8`dåŠI5¥ÞÚ`nÚ¹þµEò%!©à "j¥[Àé•L»XóÇáꉇ¨4?QpÔ )ra'Þ´À’€Ë°hù®!Ã;zf…gÆ+óZ'—º ‡¥£Í;a‰Ö/&Š ÒgÑÚâàÆB´7ã`MÝp亱&±8²óÕcÖÙ¹®7gùîbd÷ƒU1À^-t5~c Õ]É8 [ÒØX ŒIªºkêÒJ@œ:t»/ºö[°-£ ZxÏ=Y 7Œ–¿RGÛm¡÷ÊÞʈ/êUŸ[‘‡mz t±_¨Ïuc[~1. • @éT†~ÄkAúÝÿ:Q°ØØ¿¼´5S®@ö2.Ob,;ıy)ÙqÑÂL…æšë¡Ü –8 U„ÞCo•-µp­íÂ?õÔ3alŒ6¡¤{½•KÓ^ô!’ èÛÎóåÄ¡#¼ˆ²lr—ÕäTò˜´ñÉkB‘R1÷Ѐ=Ý/×äÙšN¾àEÍyqù¦ÄùDÆäé ³omscÏ×h˜l}i[ $º¤•X:ÔÜ”ý¼¯á±•œ;ý„VÞ^žîޝ$æEŽbjë½á—c<ßãkfMYhòx¨’9&ÚÙ¢ar\<¹Üw3cp÷&Ø<‚(‘Ò£8¬¥™÷tCmDã°aGÕRkþé`† iJæøÖRÍV0a;jøìXOå àJqðŽ´çÝ!l§øœnŠôö(µ +"ÿ`2•ª$»³¼½ï°%¹‹#5Ç¥·¤RШUNU©*Èß2séÀ%ƒ {ôXó¼¯íŸbû€†÷z¯U<¿ÎŽËu7Žk¥¿’Ïèpè\ýó¹wÔ½2ªÕû˜µð•9yÔ{j‡ƒhçÄÊ;±"”9ñ—ƒ;²¥Ø˜Œç1´‡Ns1š•ºj›}R; çn¹Š‡¾» Zú€ÊµÎCýz©+¢Æ9¯´?ɰó(Ä–%’KÛ L|$i8­ŠnNIZmô¯w$wéû4rò*b(í'€GÛ¾]uà¹HsLÕGLm\°£82—¶6tx’!¦>U»G¢v>8MðoùÅÏ3°i=yž¼!°±3åÓÔBìuŽ0< 8!v›dÄÓÈpÿhN‰ÅðL ŒœN¦ÒKáüБ<ëÇ£ÿ)S~¾`/à 'jÉÑÕ*ÕÂlοõ-‚¿c¤þvŽexŠ]¾{˜õNg4* úáuZ,q‰å˜£Oœ¡û|¬^è»ÐìùéÁ ã¬i÷Á¬«¬4¼!ë¹<7­¯; ÛxTLÉT “k¾` °ðo*T$~ÒÖàÈ#/Iýp—w1Æ8¹1”º…ñoëßžÖÏžÞE¦®¦àuÜvùE^B­ÎŒ4¶€ß^°<Œc9½Þš—LºøÆ®aEdt”ÆÓm(s)1Wf=Å›-WôVüN{&’0ðÍ‚¶.«X#+Ç­ä†Ýp2çÆøVAÛ{BJ$IØØ¢¸:\Q‹·»SBÀ:‡º]°ë¡ÊÓëó2iQߟPüaPÝ-.qÖò{NüôÔÅù0°Ü@à:[I,wã“Ü­Mr>7s³+5¤ÿ&ÚD›¹¯Ä®òkiÂÓèÕj·A’ ˜(’p }KD´Ûíe£bpµ&MB•cÁö˜Å¡gØh·'ø"AgäÞý7bñü™U™TàU÷ËâK—ƒ{SïkÄ]¸.Mˆ¤¦v•Ü©yzjsx‘ŠûÛ.бŠKºY:öû#mwºu)°ÏxT2Ź:ª²Ý*OVz¬˜`G-›¨8dEhÓ^Þ`Á8“-OœC;­}é5ûK!4ÀÙµ èÁ•D>aéžÂªIoõJnŒznó[ gÖF¬Sk óÇî–­yi3´7•™Π«wÁiNBѤ´3øÃˆSä¦$c|i/Þ°HÏfå²PIÂ<"5HÎ}×ì¨Ê«Â=SÆøRbÕ«Ü,&Å~D™—Ë]X{@Më»ÖMÌœt.Ç´÷Z=~`²œö8¼–'Ýp홊÷+N£«ç¨7“G纪UáXas_5𠃲»;²jTúMò‚Ä»– ´¸Å¢Ó w£’V¬`'¨÷ýã€í``p[Jõj9: ²"ãqí%SŒ i¡ßû{nN©ŸÿK{Ìê‹ÉA–¿ñÝ—‘1CJ0é¼V¤â|émö®yXÀHm•êúcƒ-`䓆ã'ßì ŠÔï÷Ó ’È˜Š¾¢Vá—¿Å,Uv¾2•Çñš7e”swUúÄáÃnLæƒ?Fhi À6žº6`”UlAФ'.tEZÊÂêhõxVÞ˜,FVEu`¼ÁFKª—³Láûû’ÿÉð´hÔήb7t3’ZnO‘õ¶´ÐËŠ`¶^øâe}A$Pkj¾oä°ìî.]ØçP¼ÂFn"Ú:Ôä¶Ö«ÿq­ÛÆÞ b'É%àÈe—£E¼sZ[‹]ê©:=“¾O=ìÍ¡ ¢wô'Ù&ÕBÅ’-ß.7KTzpñ!åê, jÕÞÏyMŠs–籫>2ÏD™÷"Û¨ÿ&úˆ3"Küsãï\m4­@Æ~ÉH>4}8+{•#žUŒ®HXW 8¹U]ú™EÈ€®îݤÿ#£ ÓñI•æŠ(i ¯ën´¢ÛnòÇc¸ZqËçÌÌÒãÄPã=Š«[®8\ÃD‰²¡Ý!Jr•*(ˆ­Ï%n§£Ë 4Ö œ©2 Žõ Ha9KäùÓ%5iPªî~$ƒcaáuL[”¸á)ïMäþ¦'„¼…:†ÄŸéKøYçþ[·J‹mý„;«ê!r蹌û®á‘°h˜¬¸¶µ,ÎpN7³¹øGú Ü„Q~½¦Äð‹ÞÆñ ¸žíÑlnJ›'S25Ž3ø½~ Ö/AöÝ=Ä€ þ…Á {Ér„æ1Œ¶Çu‹sf+î L5 E6~qSw“qZŸó¢PKÙàGW«×Ï(&4ôåØþ.š¤XÞÿŒd–dÁ—8H¼q{d7Þun°æ Ù –‚Ì;ê3¾•ß9ØÒù‘¥ÛHÆúÖ{þx—FÈs»¶F« ælþ> ¦â/NQ„:æ_XkÕÛPÍ»ƒQu÷:äý î.] —¿J™c@ý™¿y_›4™@ƒ¦"›=;Ð1¸‡ëyžÁe/Èl²v *ŠØ´.´Px%Ñ­‘,ާ¹ Ò¿)ltÆ©ô‰ õÝÉúM’Þ«ZþÌŒl¼î¡™ëiÞ¬¾”±;™=õ°¼?kó¹gøjO¨Ùµ Ž3×A7Jѧ4v w+ZYB•è±Nø5uÛ*Æ=?"u0é¿Ò¾BÔ¯PÅ\½ÙqñbIÍŸɽl¯ZèúŒ Òw²VÐI5Þ÷FÜ ÉÚ×[—Šv5w³¶~+è['‡LZ”€9aÛFÉÀ‡‘¾f‘NÛ!{ÁÎé²£‚½Ô„c˜O°ê[×ü©—<ÑôŽá$ù^ú~ëg—5YÎý·_ã?,j‰,éDµŒà­ïœ/Bˆ‡ðÐÙFõk­Ò<¯H9IéÓˆÖb3ƒ…LLgænþDþ}Ä“ý®ÏŒÃ“böwUt£­‹•ÇáÖ\>Þ°áøuhïÑ× ;hu‰fáx‹ «@„&åqäX#xd“òßTE»¢Ìådè/õ\•fâ×Gþ+Y ±pAC9"@ŠªÌ➉\^eÓPŠ}E„)iÌçbæ f£Ã' ½wЫãõJþ̬QFÖY™‰ðR*D~o˜’ƒ+Ái#ó<å|½rЃx Ð/eôI£>Ø)[YuwyµYüIÃÅf7Ü8}ð}ØŽTé“Ö…5Åk ë¿ðªl èàìGÉf‡t í­7(°-«±”g­Èu‘ 0ƒ®)ÚšÕQ¢ˆà¡VW7¦|›JõÈ6·qËH¦ gðŸaû8D8loÍÆ¡„¯ƒ°Û„ÿ^Ñ×î «¼Æ &¸« `¤5Ç×1KukÛ¯ìCè @çQ·ø˜Ý™÷4ÄŽ`—‚È vbîwö9÷³ÿVSx:½½º‡yåì™^)<ãpýóŒqø 9v‘nƒÜ[œçŽÁèàsâQ®º¼T®qqßbqÜzÊØÍ» T¹ ¬¬?’R-ùó-}7tÕíGökÎS?¹¸ÇÀbÊdG;•Giý¤d½d·Q~ÅZÍÜñCà§o@Ë3<¸é°ÓF%E0/óÓ½cPÚ(k#bBŸ“!š-Eå r^.ÌÒƒ]ùáð®|róðì1pÃm¯×Aö±šZ®FnÔ—¸ÅÔ×7Pø‘“h1´Ã/ðÕ‚ŠbŸíAAzýR1ûí¹S·o£m¥5Þ%û¡#(`=ï#òÏÁbµ°?½Ä5®LÝ«Y‘~«‰ÀÙ´á¢I܄˺ñ%*Ÿw@*2gT¢Ió›Ë'öiè–ƒ·Hª aË`%Ç‹•ºÊݯ³ )Šm€4‡¤bï¬ëp†µv“ÕȰ¼&áH—nÇžâû r?ʆÅEô‡á_KÄëqf"ü­N#ƒÌá÷›ÜÕyá‘R†¥#ù÷©lÁW^ð3£žl0—b>ÐWÁ#çNÝ7VfûGï\\ð%LË·’јúlX|ŒO·½dQ=R¶¾9ŠLqW8}—BŽêà£öðå»þ0 i`?j`HHAíøúzàï଺)c*Xy—ØjÁOÖÅo”%¬jqþÍ/&wϦÆz“\gx WN*_8ßÊø ±6äå—Ì3xtË€0/ >ÄoÀšŸ{X”ÑŠbœóÆ}ˆƒÔl¾5+ßçTÏìBÔÿ`š9ÊgI­Û¯‹ëìùØw†¤ rk„"Kfõw²TN‘áÛ8®¦yKk1).‡š/EŒïëñÿÉedà1SŒŠŸÅ¿y¦‘ å5©ð ¦ÕÒÓ¼½쇸A Ž"=íÒuRð³•ÈK†>y¦*ãÕä’¹Ámq*HÍ"ïÚeo;CÀõdüÆøAôý×]òO—Å’G§†Ÿw]þÔ³qóR?D•/ïfbl³•ì.ó6d¥ºëò.­Rƒ€M'à•þuý®¿V þwã2À˃pþ!E´q±ö¶O|ájù†gžÛ$EñE ·XGdC»°Zr|øs)‡q¦'yï¢1•˜Ëmt%‘]O¦Àû‘ݪÑp©mÓºH0w\´UàmA'å}zò[p¿éÜû‰ãnDLZC›¹næ½²ºÙôX¬_Œò ß_ÖâÊ%âyÿ6ÝMÜ4`Oÿz•×9Û{½ÒÇ.‹¯âÇ ówäKB¹_e9^’Ócºm „ðwzQÎøv1v’$mz)@ÙøhPiÙ½@ÎúN$pÔE„2"Ù›R+ëDÁu«^NÊfê(|¿ˆ’ÊÊ¢$oMp+0{7 "ý}Ñ&̨8DZÍ6î0.åàgg¸^£öÝzEû ÑNƒÅÂâM,¼¤AÀX3¤ ny«i%»Ò?«Z}¬ÍtyÄ#^](MÏÚƒµÁÛ$ª‚&‚€«½‡¨I ±ckÂâãä€âÀ»y~@µ„øâÊ¡HvÉ5jsÞíÅ$H> Eå¦,gÓϦÖNÈÑN†/a­ZŒæ’ ÕŸB¨›tiˆanDŒxB½ œ«M†Ov@E¥Ð‚ðÀ;KðÑ+ګΟDlcü|&ÀëJÅÌs_G«’¢»WJ_@ª ˜½„ÅÃ?Eío »V)‚˜Yî>œU UŸ`„ý•ˆVvð¥Âkúì.OqpWâ6YžÖAZrd±9hu²T¨cÀ½ì£¯1¡$_nrŠƒ=°y *M—[†:(a›àè62ݧ}s^»83Ö ŽÛöº&§ ÚMºê{¶|yŠÛã4›ü|¨t5Èðy=ËÏ÷ôý Ö~±wô‘XZ³ÏØClÕZAûnaÝÓò¥ëð"&ïÕãx®þZƒCÎ{Ëž¦(™ëîM~?‹wª‡FvWve±<íLžÂ.—t¯çAES¤…#ÚPCJèÚGö› Ò",ʺë3_¡_£‹í\ñ0ôú°ZÍž]=í9! ­õ½ðñ6hó C÷Ǩ:…(f‚lT[ì5«üâzzëò]ÌË g™GÅŒAL¤CÅ:Þ¤ãMF! ŽÙ™ºúeëTzÕÚ}çPÅé5x\Ãíë^oYpü:Åè ÿf¢'¤DÒDê}­øNæOa°‚÷”ºÆ€žŽŽ÷|œCê…ûêàЩN „äYa0Ë5c‡%ÁZÕÀU’šŸ¢ÞƒFçá“ë"(ì\Lë]–NÛó†â…7J½P…ñ›ÍÍ~"îg$`ÆxÚ٠ܢP/%Ê‹Ö$.Ý„}„¯èyÒ'½’%6c²7½"".ú –”3‰rXüH%—祯îúö¨–‚Cn¶zöõ%QD75,‚qeɧAzkÄTmÚÝÏ}×ÿf=лhÇžúuÏYáR™’C’X<'S œ¿X°x âegÑ™À\ðæÇï )0,mÝ÷>“4Ð6iÏb€3ÈH–åeÏ*(˜Ç!l¦±}7ÞÛ ŒSà^£M–ð·„m}¡“Mü8³cCRdwæ“ èpBCžhYYŒ{ìÙ³kÄßjÒþn±]å0áP‰¦FÎ @D£lë—agŸü50‘Ñ ÚÍ~ülz䜽C7ýZÁ|käó4â²È HÃ’&!8…9DéÌ»J~õIði–Ý1 >|µ<ДÝzØ·äØò’æ¾”X2!ƒsº¬S£XVÖèîò*‚yØkn”wÕs{Å]ÿäP7TŒ0ÉÁ.fv ÇÈÚ%Šur‹þ4Qå÷IôU%ô$ ãp°%D‚ÃänöY5œèÀ5Ÿæ«]ŽÌT£ƒ’vM…”…êvçc8CìOÒd# V|`-¿||*Ãú»wØLV Ñu¡äùÿ L¸§3ò¼6‚U‚Ø,X#?LÓ4ÜóP~⩹¶M¨GE. þO-#A¿ñ®ûEWò¸/rjÔwE¡Âáu©Éâ|1aá1ÖÖ?Qû‡3jã>*²„õRzÀdäO>Z9Ü×6q'y^Ã0ð+ðCUÏŸ‘n¯|É'€êÎBµ2-Š%‰G³Âг­6ÃqJÛZÑéaòäëË4•êeå·ôD¦'‰†FÛ¨jYå†+73Å…9­YìÄ «¿÷ <¥Gþ%L¹ÜŒZf‚e”Ðè©Ø¡P(ÐÁo˜kõCìÒˆvñ•M B0,> ¹¿vVBµ$ö~™ûL°ð¯ =Ónk(íG†ŠA„îÜsCuùf ”xViSJi=FBÿ?_è2‡Žn* £Ó‘ü€º:+éêç­¥¾Ëlæ²ðk“6ÓËÆ°LžÌÆ<Ò¹a©¦#€Ã/éŒ3ç\°¡Úö€6݈XùYNÛýJ9«I«<âÊwÀÓ«aoðzZÞ\>aЭªÜÌàèU@½"ÌKøó4pEo1=š¦nÞiåoàíÈ)b÷<ÐQˆ4ù6±3oË誢¬Î=ÚÌ-Z:Fo¡mwÀò°9Ic~¦åêí˜dmé¾°æÞååìä<¿Ódµbòó9ŒL«Þˆ dzÛãP¹H“Õà! R3›ÊÇñá¤îË8Úôà>Â,m<ÿT-ûƒ÷üjDäa@è•EÏ ™ †3™u÷Fø=À0/âU9 ÁsìªØð:ºO.£>k·Õ‹„µÕ¬ˆ!‹‹tðެ>ÙÇÞpݧÙfÅØÚŸ„q³h(,”_\ÿcëc^…ð¥yG¼ñtßœO©ÿtXÚä¢2ôAÅ»Eè?ÁîI›"yé˜ÙJ³ŽF|1¤}†›7ÃòÙO‹Ç ã0Ö’#eÝèöžI³f%F¯A…yhýY¶“vT‰%‡tHÏÝ…˜ÚÒ«ùOwÕK”–2 ºÐ©²¼â¬› ¸Ò¯Œwð@É,48No^Ýðâ”ÚÆ“k7úsZG߆7ÞSé—hœuQe&{ªKã"¸IBGm‘<üã"ѹ3© :‘ÇáÕô~MCðéÜà ҵQݺ½ÛŸeö>{'­q‘½*K)âwW6Õ7Ç>ä ö„CQ{Ú•,ÿ¯+nMÆàQMExîÕ@ñÓkê qÿ$ ›ž:©\÷=1Â…¥¿:U‡ôDÿDˆ`ý4ìÖ(CRÄ an[ðWw­´®“dmk·®þõ6ØQn®gxDñÈYܨ}ßÓP;u´›¶¯Nâƒß…É-ä"k¨5·µ‹ZB©ýØhP¹ŸO6b×È0Zj¦8*°ªx*“£Š^&¸ãU®Ä ¦~¥_M¨* ¼M•{þ&—C^îâ•£ãO@v[èË%³&Ï ‰4jf” ¯]—W&þtÔR­²îo°0}Ž$9I¹hÄt~ª<àŠ IÿKðžÌ:Í­õŸQ­Ö|V=EGx²eQ6Dß[þ¨Ôø¼êR&ßM^cΨ''mjœ€ˆ­o/'X˜äsžãD&`ó׃%nb@ŠHbÂ#)à²eº MB¯o:²e–TóïÍgOÄ糺Ž/*÷¨”ó0¶#©3AWUÐÉi=E‹’R³ä3yáÅ÷Šìmý{f±áT2N*+ ÷Cï/:Ðî·¯꬞5:>Ú,È¢=ùå¶E-c¼8i¹ªO5tù¬ß¬Þ—Âp¢HPS) £F”Í\¯°+ÊUç²8‰Æa!Áwò­Þ-{ziÝ\„oÊWrj…¡Äªƒ·Ð9åÜÂY–JJGR“_9%<`µ?«ªAŸÁø0^<ô~='ç·ÆYã@ë¼ÿ<ùØŠ¹«=…c‡±†˜¾Å™S’Ž^‚ôÂX ¬¿äB(8•O•ûûÁ–(…­¤Ã@³"ôUເ…qWˆèô£²r\CõgáX€fèYµyp^|º7Ì—â 9°^žÍ‘ç@L|èÙ?ÐWoÐßf!|K&É5{¸?E÷aa‰CE´†|Žñ㔑ŵ\¥_¦UÕœˆ’‚†¡HšÂŠÌ9±þò ï¬qŒ*»n$îih±ivXF¦EÃåœð>äðLÔ°ÐoÕ1Fç­ÛˆýÓ )µm‚s9o’}¤¿µ…îhþy!œFô|œ…Á9ýÚ>Ι­Ë7×ôE“!pnŒ\÷VAÅ~Ú<O Û©#ÑÛc™¥o|ãòö(˜¹žœØÁo¬/¹[Å'ËßÀ·iÕ5¸:Ð7‡kÄYP¸p[l‘ƒ ¿krþFš½]Ï€QcvˬNd+qU¶ORE”z!{8-c‡Ý­üÔéÎ%^Ÿ­@T“X‚«U±˜Ý¥s³Ug¦Àó¡! p°5¯ÆDE.0)_¡˜â r˜ŽEFfù¨ÑÎ »”DJ}®ðcÑx䘀¢õ+L$dYî‚ÂÎÿÚBCn÷wz·XÌ0ŒÉ–©Ç! ›ùþÇÁ~Ó»¢Àâ Ì$Õ2L@$òýÝßÎÀŠd“††ø?2#M,Bvy”aé|K „5ÜIÌH+ÏV½‡§"ÜS·‹£³déï›·A¬®šx€0¢«É£Ä†‡T¼&Ùa“Ù¯+†œ÷<ƒ”C>Kÿoô†Z»1k¶¤œãÿ(%gß)ß ílq &ɵ¯5— YÖÑßü/´ ¤ºm¿„¦ ~# f¡iXL™‚œ9bâ߬añ”Pù±›WR©Ùl4t:qMÊñÔì]@¹­8 Þ%ïcѱ ØK±¦ƒv:¥îz½ %YñY"¡øQ±±O_6rh)ì•GH4ê“ål”ŸBgN“­¸ð¼Œ¹Å©÷®´Îñ¿¾Í¡#”²Žq¿²–·p"h¬¡[3ùüé6æ‰ÊfcŒGFw,r{ÍC¶ìƒî“A/ÓËÁÛ-]˜éÛ˜m¼jk²|+Çï]ðnTðÅu§Ô;1pì¾qôAÇÇÅS? ¸‘‘Ùñ4Î9~+ÝvvŸ¡“‚tv¤ºåÀ%ýFtOžºgç€ÄZ‹~uw?ƒ ºYÄ $á—‚_!Óø9Bo%)’yÓKʈ„¹Ó!LÓE#¬Þé+‹³2ÖÐ=‚•Ìüøø‚è^¥xgÀ!Å€E›£XÜýw×XÑD±QŽDpó*Bq«ðÝ–ž$!ƘiCŸÃ5jR°> -î¿”Bõ0CÌÙ,TIµLÆÃý?>aô”R¸vÎg…8ÒÕ’Á»›5ì3ï¶:C4¬m :²˜¡`4Êvª{MÃðèj-4¨è ؤU¥°»±ï5µÑö÷D& $Kwý½så¥è>CZþ&ߌ¤Øö†9P{-dá}gÑ Ônó½Œòû£‹BЀ^•]­“î¥pjÛ܃Ð$•C|$Ï¿FÛæ¹äÉ·‘ÍýäÒ8ºÒôéXÇrž&0óžºiÇŒ)¦5-ÕÜ.:l·=2âôŽ'VPÈúÑ`ä@LÑkâ2 ô¤Ùf,ÆYÙÂxMMìŸu`_™.ø;+~º#AMü5kÊUE1fÚ:ú…?ñOékÌÒ¡Ë – žˆø•´ºjÈ9¡n<ì‹ ë1–4ª÷2×ì͸_¦š‡êW.x>‘샣%Q@4ô¸ãøu1ü;†þæAü«ÿC¯OáÜçðï_/é¯çÖóïFü=€ã"sÁØê‰V¿È\’KJ—Þg ¬ÿjý«ÈË,YŒl‡ûÄ€öøï4,ñ€Þ6+886¶C_ Yë~\M]ÐØ_†ä”òà‡o•ⵃ¤ÿBY‘IåG[´î‰9^ÄÀ¢§Kžêoê×Ûê9½YKþ˃ö¦«Lž„Á§×¥7È(Å‹#Óp}YT'ðýw*†2õÕÛ}^ß'ÎØñjh¿WNM&CUÁ VJöY„9‡®ŒP™tÿX•¶öÄÜZXoGVÒ¯fœã®$t¯SžžÈNõ,/p€uÑP…q«tnÛO˜Š4s¡êè‘­.)uÿi¼ƒÑÇ5ÎÉÔ´c7z"·mAv£ÜŽ2SŸ•oiLQ|AX×¢ì¦Õ§,lÞrß8 þY2dPŠhùU¦úeiêUƒ¡° |•É« …w-à £E±™0Ãe”/öŠX`-÷ÓOI|g`A/›C÷Yð+’ŸXZT%™¡Ëv\GÚ^a”üØ…Å<$¾åEÎçF…`ú·Ïø3ó”OÉžš?H$FÊpc™3Ú2 Œ]®ÖE“ xd{ø$ŽøÈ0#·gåºÛXªnû{aôU‡Ñ€~,!ül^s9÷B6O™šaâQ9*ÿ,xc#@¹CD1¦h"]i‡@\#ÁLÝPüÛf¸&pÚ¹hØÍDøH4QÖ£1`Ü)?‹Ã¿W [SY5Î’b~{ ÷umE8ú,Ù¥°K0Ö¼‰š”"¦Oz_( ÙAJ’Á¹ Œƒ*øŠÀR7§{j è´”*¢P–˜ákŒ­CŸúá¢%-S0#w¢ÖUm4ן³HUùüƒz0„»oŸ—Ü+zêÂi„ë­‰ŸÐ± ôUÂk\EY_=ô,`ªÕ—Ã1ë‹z,VÁs[Õ^ *2¿/9ÿÕ³S4ü‰€ÄäiÈÒ™ê)U²´-"¶€ cgb¹“Kø©‚#pŒ„5œèI.è=X‡ ?ûiÈáï…¤úïìTkà D H=gñÍ­Ò¸šO»NZÈF½.ÐAvCÇM»¶›»‚RÑ ºLñÅu~íY󯞼KB hÛ›/?xìúVß¼ç_ò:€8ÏÈÃÚÛ:ìt§ÿ!L@ýU¸…^Á?wƒ}¥§%H’©¥PÑþCE]m1Z¤'X =@äP¦^{ð÷ÈÙïOñu³ââ'Ç&·U@X @^,›l®¾ÏØÓ:³™±N ³Ë[´M—D ?‡»Hœ 9²w¨7#÷8‘» °é ž€;²Vò%ðt±1‚VB¸Nñµ~4š»&… a”Ô¬rKZQo’}å¼'ŸqÂD² `œcUN’|â¼ø‹å'GĶfZMp·“a%Zóæ©x\ȵQ-—©»‚RÓòt`þG6 è 8ÂXÜ}IŽ ÜVÛÄ‘ ƒ¸)ëQ RЕðq«•! Išõëâ©?ܪYìU^Nc¯ä½&»šóÔ+Î7þÔä®®RûÕ†²oÂÁb²¹€¶-&+þ©fd¸æùW¼_æ Ô•ºP‚¿ðv8zÝ IŸ:¿C-”Šª÷OˆÅÃxcpTä˜xS'®‡Ô1Î*lq\oÈÍ~;>\›Ñ6Ñá;ÎLùúvò»Gßsž©>ïÙ%ˆ|Y#$³3×èNA{˜l«è!ƒ³ä-ÐUƒM\©zÁB,ä}. ¿íÖ’Î…ÁnŽÖëô*ôû‹ïµzZEû,O¢¦c‚AuM¤<y,þ ý0oæOß Ó7R»å¤­üÌÚÓéß ø¤ _x˜k]ršÄ£é ¥Xí*RQ¢ªƒðv½Ua>ÃÌŽE~rò×rHLSI@ÌÄ¿òô“’.úI@™Ô)HNŸ};¯x4Št!EêØ"™Y¦lÍW¯GCâ¡ó§?Áh²Èvfóg“ÌØÎ?wtyq¶?µEKédµç÷Ê‹8Æ?¢¤/«—¼Z+”wؽVJ…Ç$êj˜ç÷£(÷Xå‚„lùÂènó±s°KGþ×aâq%ŒŸgl:†Úz¦ÎJ<Δ¸ wtƒ·Ë²Ö¨ äD]¡ÃxÖ£¤šuÄ^¦~«¤«Þ0ÜF[“ŸCc‡;c±÷aX ßj<º> X…Q¦ !†Úq}5WB­œŠÈ'2œ ™‹7oÜàäAç<3Ýf)ç;«Ýç–S¡úhû /6uƒ¬è qÚ{¥6ÿC D™^­€ñ袨¯›1úŽ<¤É:ÿ4Âûíš÷‘|9bõ¦i¹3û~6R¶3÷’²!^˜ ¿:Ž×ŸÉûíqY þI/טC6cÎì› ¿fÁ¨KYù&­Ö3éØû…§¢¡Ð²@4NAÌé¾dP7uŠjZ‰ìO@AÍ]¬@›.ÀcA’(GEº#Í[ æÆm K“ÒAz8íl9‚÷\OäuÉF†:ÞÁ´Lûƒ±aç ëè‚ _%±¹çÆe©SÄQ¢/+²v:4¶ÉÒt’ÞÌ©rŠåQÔÿtÔ’,t»›bdê8\®…Òè‚+ÿo}9M1mx™íJiÌä9Žu&}•(ùs6sCèoÓ*š˜ûX[ÈâØ ƒý*A²cÎc"Ð52¼B ÑÙ‹â)yíîþqŠì¼ãí‰"7PÓ:loãýÓìœÑÛÒ2t\_uiY·Ês£Šù(:zgŠY'øÎ¼ÈvÝ€äCi C]°™ 6^´7£Í!®=[^žØaòà_ûH¿ŽÓ?ÉJ^muû–,œ ˜s œ6L[Dg‚Š :¨Dl¶Û˜#æ7ÖÇ)þOÆQëlâ%„{£ OÍ" †2Æ%«÷ÒNV}FXèc2dêÕ‹qPgoJ.šÌ ¤yë3)îï"ØC(=ÌØe—"áïÏ먠ñjë)ðÝZú5½Éuœ0IåFhõŽåP`¯‰_8Šãô1,êpÎûƒ‚rxü§5º(å#ú†·wš1±{(óņ\Ñ·\¢í„cü‘¾uìðìÚê}/‰¤ ³¢oƒÐÙF@±þzWã{ Ö{•ÐWY+m•c€bܹQ2i.MÍŠ€Ž°¾ —¦–‚Þí¾ÄöÇY«Ú°’/3â@¶î1ÉÈ)m ÂѸmI6(›ßg²üB­Šù œÓ ¯)ur¬€«À½‡ ÇËçFæ3bL o¶O*L|'ú†×ý¯·ßÈÒ9ÚøËÏïdâžJ—ú°EÇ}¬å…y-UÐ¥ç¾3eZ½:é(×zP<;O©?ý³Ó£ƒK¼¸£…±Xô ²M¦Ü^ƒ:RDχ¸PÝŽ[!ñƒF*ï¸LìE[>W;!X¼ÎÀM.:À«]*”啦çòk÷ΕâÊ"†þâ(T 8Í¢Yü_óûíí'Ko‘³ä™ŽÁM쵿þ¾Ë×Ü«ú»i†s‹¡­&^¦ÝUæÀ’€.N8O¿  ÚyRËîûœ/”T‹ÊôuJ–7/”Ľmåîéæêº¬‡8Ïû†®H!Û»6·>Š›è¨êðZ&ã»k(F£ùœ‰ÆÞOâ…¦fó¤»t¹­¸¦–]Æ€ø|/»›ÓØnÔ!>½à ÿKR$Ý3SÈ>ÀË¡Êʹ c¶q!0©‹jƽh-‚ ª‹0ÜÚÕÁ Í÷ch¶ŠÕ†.÷2ÅUÎèZÜpìå;(©‡xtP`ĈÁàv¯Águàè¤Ãr $ïB½VqŽ\Þ)E—¼/¸‰ã̴À§©O¼}|Dމd¼èæ7'㌣é¡#rí(/‰tÈØ¿l¥e'á¿l:Š½Ú¤LÖhšœ`œIà™l¢ãvQèY”^×ÏðûÇË\éTâ4Þ)"ƒü8ìj-Åè`å?ÌïÕ™|M˜È·#Ý=ÇE¢>£÷c›ÏBfT½Nïûˆl‡’)šxNò²Ô#FéÆ)ÇÈÝ…BJ¹ÆˆMm§¥Ä*è®bîÀÜ>ðÈvÐÊHEåâ`jq{`ršNÏoá>k¿€Žšêy`5"1$;¶5A´Ï0Í5MfEã²L½vÊõÐem‚Òê ò#§,šÎÀ©!ë¥;‰€¹Ywßò‚#¦Dˆ-’>[]æÿj€Â^í´Ý¥3÷ fUèA8v±Bô~)[ ä¹VôôÕ33”i½;jÚ%°r’æî1æY>)huïôFiì+»u¡‡Œ“Þƒóæ$õ™2ŽòL9ð™®Ý½;* ï¡B·ý¤ lž —¥Q¿ü3Ù*˜É#è4õ$Yp϶Ü`‹­0Šb7Oú[ Ò¸2~…’IXè®æ ó_ƒÞÏDˆÁŽž“ïuñ»mi|t/&ªv ­ÖöIœ£ö¸EëðR¥X±)t‹ÊÃ]"ãCŠíÓ\U)ª3ÝZ7e[x>ìi3û9®k¿ j¡$˜j%I‰&¥kÝd¼:[š€9ÓµÜjKšl/†X§Žì¸HމI¿„¨N¬Â¤”0Ÿ¿“Þ7/êÅÌ7•pˆZGsrÙ+UÊŒHüÉÓ‚@¥!/¼y›×ASÞC­4’)¥aizK¤Ç¾9¤Ö@‘ç R¨ãóE¨§¢cZ¨‚ùsÞÅŠ‹xóž^÷íyº?[Ɇ×]¹ Ùþ6ãíl õÊÿZTà¹Îaݧ刊˜\–ÙÝGmÓ£Û¶ŒG»(tX”š‹¡Ãkdù|iÓi ÜòÁÍÝÄ|8#Ø’,[Šx›e(4Ç‹‰ZhºƒV_ "íuH+ô”‰¡òñHÑ4’‡º¯_—Ѽ½e@ûö/5ì£@)sCÔ•âáë§Mì”(¾ñÏGwo}¸ï¶¦:-ƒá¼HÍÖÎtqDÁ³+ ó¡OgÆ\pñ)/uÔ1>ë#:xñ&ö§“y›jÊ®d7PŸ.Þ™Ú{¨zì)?_™¦C¶2µE’5Qå9æÍcVwõ˜ä(ŸŒI©wô)SØ!·Fø¾ñÃqFGÒ6?«Ìz3mº¨SÏ °â4|,¢~&f%‚Dܳh¿ÛíëÌþpçÁüJ³] 3ü®ºä"ûñ/¸ªß4í/*I Åä…Ñá†É4wŸkЮ_ÊÐS;À`[ƒµXi Ò˜"ŒczìÂ[ cpXd¨–HLë,:ï›y{·ÏÃ8Ûøç~fk0JÛ„Ú”)qcUü ·RLž >›Ê1da²¾ÎõùUM=¢˜`„7f®ŽÓ¨µ- ˜ûº-aÓ§™¹—Dã ]Þù{ò{dqjz’!þx¼ ’ÆB“Å* Óê96›MÔl"}’ºðTh$`ôK™WŠ ÑRð_/l(`å}M b‘•È…ëMÐV¸ì5t½97Üà;¦›© j“K£[ûBSGï16ÕÞ?Aw¬î†d FõC² Eh‡‘ªƒ,v³i•Zîb©¹cÐ,zÊP6«0¨Ñq•Ê>uÕ¿²íðXrYð‘øEÍÉ´4p©¯V@þÏõBà?Ë£ lã¼Zxo½PL|mžA»¡Vblh]ï7ý« ’œû¥=|ls礪'‡%A/PɬýnÕb>ßã¹—™û)Ö¦ˆ÷k$Èo[~C{’ÚÇO¸ª<û4˜§¬6¾µD%k°ˆuQ¥ù,a+Þç—[‚é"MWÉ… 4úÌv u‚Ñ.µg=×bΛÏwúi’ã®ä°M¼× )xYt6H½˜ð"Ù ®ê{óÇl±BDØÛÙBµ²F“IÌ’$»Ÿ"+øïôE÷UÁimê×3FqØôÓz³²“'më°¥J¸ü{ƒwÔ0   S[U‡OºÊŸÛ2ùQ°VÏCØv¸r€£WKúk³tq†ÕI‚n£¶ºK¼ܲIñÅT!íîŒÝªqI©`á’½U`¹nlgñª^b•ñ·´“>iµ5›¼zÛ)» Øš¹‘4l(½’N}„ŒNM§ ¿x6h‰“é¼Qü6ÏSæ’[3–ap\âäq–ü»¦öjê’ã>±i*jëÔpVO>«&›™<I9à .òa8n'òpþ7t\&ûT¬æŠ6ýw94 “Wj\îuÊÖz,«öµ°½ŸÒY£©· u"CÀÆ×9뇭~ Fc®Õ\hö¤¸ñS;–~Ôk¿äZ$†ÿO ø:kÓ*Ø-2ŒpÕ*Šÿ/m0ƃñ"õÌíÙ¦æctϹ‰ðÿC½;ÈEpGçp‹ ¶‹ƒ`«¯JÑþøõ3µYKjrx*á=¥»‘É&ÛÕê ‘SëÝÜÿ>ý1­A¹˜Õ²GõËhï¹h© R3SãlC@¥Æƒóæ[|"$O {oÉIm õ¬wxcZt½~ïæ?¤¥Šû<÷¤(Û‹† vÄæn¬g1 2…ïæH\ßÏDí²¾¯®¿oüÓ :»ýÕBJ/Æü¤…VF/„Z/y€ "¬o žy¢UZB߯(ÕüÐÃÿ:næ•t9»Å§hŸT” ¨fîQÈùñÈ£ÇÎ46¾·Bû4ŠÇ¬þ¾œ¾P¶õPFÔî0s$&Á-³e1|`ÏûL†]“M¥ß~‰H ½p0’!š°Jy .C&ö:§FUT¥Ê®4AËo¸ƒÎK0öD¤|Q"ýÑd‹4~§7RØÅhˆ˜7çB©„·ÿƒ–~¯ŽÑ–/6°Y#£Ý›ºR|D0 â-YÕ žü]Û…«|P~A‡„ã\ ùé$d¯ƒÓoNù€‹ ¥‰¸Ô 䝿RðB϶\ým1IŒ½ŽÐ³}3×wø*¤sï£?d©HØ[BlØš¤q|™.–(X‚eGmšƒÐk¶¶ç†ý;ŽJ¹|O»B½2jø4ÏmO5÷>ס•´¯=›ÜF-^Ê[ÙÈ­y‚+Åå%êõÛž˜*•>e.Ñî¨Î ïßãB‡z¥dð2x¾)yñìß´éëÉFŒÃnù¼ýÑö»à£s’‚`ð¸äGv#ÅÝ\zïQ9ü ¥±%eàônŽ!ï”M ½Xˆºv~ˆs0ñDÝæñ›M;ÙÜ£žÒ¾ßú|mú_0hÍ3‡äߢ8y³½ÜÅ–v¾ä?ËÄÌ5è)íDLs‰ðO@âQž!åð´µ¼w`É%Ýù‹é#ÈY[4Sx°!™?2kÀsu®¦aÜ7RíüDzLÈ'ø®È_%ÔJ}ªA=ºL€ §|iœù×#í=ZÈÖC±ÀŹچÂ\ æ²2Éoø&hŽprÜ•^âÑoWǧ/Üe3¡Êl? *§‹èÊ»B„òö¡6>ÙÝ‹óÔHU>•ÙŸßGg¬ò^spMHsPOêVÖâgCÏ#\ÍPnb¶Œ«<Îÿ9ƒ~H¸ŠLà0HÆíydÓmú(½ž5µ¶-4wãn4Ð7w1b,æÊÅIŽ£fý±'h¤‚މjÅ}»qÍ¢8ŒºsoÒýŬúÒR1™"çÏô›N±?•8ý8§KcÉZ‘yê·2ÁÆdRúü_šðÂy´£aÑ `$Ôs£7Qœr²0oíN|öħýn=ÂÐálïÎ&5SÈ݈„»‚ž4åäxCØD¼¹|Z‰¸y© o9_\3¬s©H0-Š˜h’Ô{*TŽ£ C——‘½>d~ÑG7î¿6ò¡àˆ„{åÿJîO¸ñn " ¦c½ü…ùã+ÜÞÁË6 môíkM_!ñ3ÄbzœÜu¦bf‹-ùN¦6ðÚAVM±&œ®¼,¤‡³ÖØjñœQ9“‚h([Ä©è®1´Ü¡ðY, gôm}îç}X"xê/ŸuÞâ&Ô÷ƒX›¼­àÀíÑýbnz¸Œ s‰ÀÇøï„onA{-EÍÔ|zóRGg¼d¡¸¥2%¢³ô¼‡e,B°jeÜîh©9$:—îŸwB‰qêâÒ—tL½g†üñJDŒ ´i«Ô 4[…„¤D)‰BŠ_³Pj wE2ä¿Í>ùzo)yÄ™ƒ©ð¥¦ÎýƒnYœ‰­ÿOs¾¦Òhõ¿ï(Rµ»J!x¤IÜñµžŒ$ÿ^Iìùðž:,ØQØêpÊëbV&V%§îœ7K®4.±¿Ñe2Õˆ¸ÂOزup:,MÚcYÆW›µN²ƒn½¹ÆÊ:™Ï„k"û6 =ù€•€ð%€w}Ê| ¦y&}Ÿ£,Ï‚Æ|¸ñ䣸Íxe¶vÐWÌè[ßdÝ@)Ÿ¸Ír¤d½G£Áå„rß<²˜ðO¼{½Å ÆÜŽ6L‘щIÿ4³´W€Z;^Z&{g9…ã,]¸WQ`ªÀ÷™"·Â”DšŒÉÒ°óGó ©^¸bƒy*ÖÔÎîGqb¢¶ŠtBYï ¦âün4I“;o½}Z´¦]§ƒÑhr¸$:àâV¦WóýwLXÝãIã Ïz²•ŽLa`¡ N¬46mZ7ƒƒñ›Ç«Óÿz*Ç·šNsó`¤CG¢ûtà œ×·‡ê÷’n$®·šfgÁÌ'l]Ch¡?Ì“ªùqE²!× §ÿpŸ Í÷wL›…ÔìvÞ Çr´T*½æ©ó.8R–SCT>ž–SÒ¿œíÕ[:ËýؼŠ}l;fúwyÈZлÍ+e„>™;7ÕöüD켄Jô·á ±z3ésZÅÍ:þ&ôxVŒº º›^åP¤$BîªQ®0Ð&Ó©—tú‹ÎW¿ç¤Ô]Q¡Tq™ñ\‚œt4¨©Â@mm.?¢1!“,oê°íb|n-s`—ïÜ`z[SǵGÒ~:÷×c¯‰Ì¼q¸)$‘qöC²?LFί5ã:jÁÀ¢ÀVY»M( e^èà1sq”‚@g[Øñ­ZâW+ÈÎ&¶Š&A,r…¸‡“ån0Ç"‘6bî „&{C hf™Ã¤ÒcdV»ÂŽ29¬ã·—Ú’}oÆ¥ÉmOç#Ô€îT6öê¢X-†Žfë™,ðåZ7mp/Ï0º­ÈþtéZ,5òÖŸQÙq€M¨›âŽiŒR#Í—Ó©ê5O÷ÈØŠ>\¿^[þ^æl§®±¬¸½j1ÜÂ6×""½}Űaò¹XµÉïŒ*ȇò4Æ!ßJ‹»¾ÈbRÜwv=ÂYTòk˜Àì?J;h¤ç¥µ±È›€­ù7Ó¢*  —™éc*Ó¢†ÍL.-_Ha·±ƒt½+äÜÐØXsÕz–= ŽÃ±…´KÚð<¡ÏÄ®ô±ÿ ¼/i“4±Œ"Ø-4ÕÇ,xcÄzak ˜-XªdÚÒq]ç£Î| ×U¬iÆÀõöïÃmÊ  ‘…ŒÃÇïÇ™^a‚'š:uüÕã¸*w_Ë]}Œ¦#ó~F†åµÂQÎ-³ÒU“³E®«Àr¯Ó{ç¬ XæÆ,ɶ‘ öORŒ@È6ëþßTD³ƒ‰è Ô£é õ>ñ­9@d“0¨gqGĘ›LMÄEv6ÛA®»èÇ‚:{Ô§€ò†£‚ `l¾ËÚ,UÌ¿¿Õ[IìeMiƒ›{ÑEöñrõƒ§h¾ÙM§SåúdÇZ(JI¶ù›‘qOhÊÙÿEJ 6,õsЦ݃¼ÄÒm…ϸ$g×É•]‡^Ã:Þ¾"^Ÿ«CTŸÊ?Ö‹ÂwÖ£Aß ÄVbf§<Ñh~ÚYWBªUñšµf§$o* PJd–¾ Kòÿ"T³Žö<̟ߣ*î-=§Fј”jUJÈAV¤5z_玫³’ƒÿSl^î‰ý…?ýÂ5š›ÿQReɵ7AÏ0 L„›½üéßœf1~œ$Ú‹e~£h¡,qçsS9´(¸P+4ü/›ót·KÒ•<;^  ‘Û¢†»nhJKjþ¡»–˜U¬@`Çþ-•ªImØ¥š«¥¯75gy¼ÜφA4] Àib¼HH´cãþé.Ä¿Í6XÿI5Ðì=6ÐWi›Ø6”íxcÆû+×r@,ù“ì¾þ‹h"j,6Eà¥íÚzÓB‰ÄMbg>´ð&ë«I Ü.7’VM2AУfæÔ £¾—Ä%÷;‚Û3­h¦8hN#Òƒ…è —\m~D#Å'WÂód\í;–½»ÔSÒ’ŒLê[oÍ)Ì—W±Uan8ÞVÿ"^ORV!e¸im#Ò)‚ʈð6[ ^fQõÊÒ%fëÛó¨xËÄnAµcÇv¯ ©6D`w‚ôÇzÄOf¹z{‚-6S總;VÐ#"\Jmˆ÷cýþÂÏïáÃÛvø˜f‡L¢7¥ÃÑÉåÜíoY¢ÖÇ5¾å‹¥Õÿw^'e*`5f&à@îßèxó5ò§õ”ìÅ sµ¨2ûÀ¼¡†Ý;5iUŠ^Zú.hh-Ë’ :!Ûឪ-’{ÃÖÖ–®k€RÜ/ÛF‘‘®rþ?Òét§,݈Ì@°¾‡£–k~Š”ƒ¹²Cqª³´Ê Ø8ðlu$(#³M¶âLF·ñír.Ñ¿À±ÕPó˕îÑäx¥5œ‰n$«çÆ-l6ÐS_éïõÙ¡™Dޱ.c÷r|'swv(9„©IrÍ:ßíé¼Fˆ©ñ‚Û2Éè’¯"Zùâ‹l¬±ÐHß^%j‘6¥>Åã")ø…äÝ9ª [_.Æ `¤­4% ÔöSp|e®‹§Du¯ý›9~}^#mê bSúã@$T©’ª&jÈîSC›¥ t"¶7±6·½g£xÕn.˜Ù¼˜™†ã×ê¿Èà ŽÕ‡†üûýÆÎ‰ £r澦`Ë»Kƒ|“ê2T®§q 2Ë«Â>ôüØÖéû­}0rNõ¨½£B«±a¹ÿdiSvb` àØÇ†þc Ó‡¹‹v}í¶Ò`ÙúžbåÅU8ÇÞ„·.EÆv@ìúåô¸ÎuåŒCã‘ÉumGƼïóK¨QÌPÌjNŽ~lè2kÃ3ü"×¼°Qv†XBü™Xj6yQîH”N£[åçä«›Çwá`89Ïlê€ANàú%r.í¼d¾—†³W€ªNøº”ï¡D÷†õïj>ª¼bˆEcç@D•”F#õ÷ÊçŠÑÌŽ½*QU‰°ý?u˜£du BŸŽNÜ~°¥ŸjóÝ2Ò›,”JFÔú=¦BH‹ƒT\ðŒ;rîõ©Ä#¶\a²¦Ê5èÖ*6\~<Ó$I5db]2a;h–¢J °Ítjºsóó€òR ¥BãÏr—¾V ¯i¥ÍïÁc¬2†!s5R)|Y²cñ5/IfPrm»xž(×ôSÿ?tZ©­ã¥Ïªõ~ý6¨…ãߘ]Ùã4'Òïáp[I¹aK·EÜùvGvåñ³NÔ{¯›º RW¢Pû”E_,L·ÚfšÀ¿PáûtÛöéïíÜÛ¸x~Ý`ýºûv³öí~/·Mo·E†ä_n×ôÀ “wøò è°wí øäE¿“éÛÖӌ҈qŒGtLšQúýZ+é~špnIÒÀ@]áI‡Š­G¥˜6à½à¼†ÌÀ¯³Ð×óêÇÇlEëžmòÆ×fu˜wdo¤‚ÞmüåmnÐûÉä/ÁZ„ôSj5– ÇQœx©ØM(~u€•­h/ÜÞ9//Øžf4»•4r¦Û³é¼8RW%’$µ¿hDè6ˆ²êÕ1õã•)1zÍMCõFaÃ*œ:‘Á€xÁb'ÜÇ’¡lm àú´Mü}຃”"% ^NêpDÅWˆ1±œoÖF].,žý¨Rz+³Æ-ÊH‰Jj¸³Œù/)T`Ö÷žPc”àÙŠÐáÌ>gòÚ‹|IÛ÷Ó‡­>Z8÷À^’ü+ïÍú|å?œ`§n5øùA…-43Oêë?È^¡ïyÑÝ눔Xòy ÿfïmÝݘÐ:¯f:u1gK`¸žI[‡OS˜©ŽB2ž¶„$BŠM`ŽO4Žô/‘­(jAs~c.`þMœ¿.MRþL“`PF7öIêÒàO:Ã#q”4´RxD?ެÆ<ïqnôæSÏÏ\Ö §Uø&,#ÃfoùžºWâ¯ä/o6›ª/‘h´ÂÞpH€Zˆù¨¿9¤ö·€šùaŸÒöF mÚÏñqûc[ì­})sfá²çÂ#³»4qÁE®ó诪ÞYþ`?¯½¢ÿ;çǦy  CüÖþXÉl:ÉNò†ÿ 65©Áp„fA­îCÌ£„ÉÜÉ Ç@ò¬>Jf­ò *÷D;ÞRªšTú`WiÀîÝ0FM1·5”p^tÖñ²«ž>ðœy ãÂÒ ´ã|Zk8iÙå5N7}!ð_.H#7S]B¸’²ˆZGÔµa­=޼~4s¬†LgLZäälÑ–”'#ߨ5ÿ9`,#ˆ àHš±P{P¨èp¹íGjhÕ(`Û¾ ìOŒ ïp«ÉÆÌž{†,ÅL¬Þ<‘ZvBѳl:îêÃuÅþ™_ËÒ#–â–6°”ßÐÚFèq­ÍS¹6KÛ¿ƒ…GƒwˆÌí~E&™O‰Rò™qû&'Yn?×ÖnãÄSeB{]vûãW4¤e¥=ÆXЇo,LTNê ¼‘±2 8èm"‹0k%„È ¨m8Ão¾JÞ@kÛŒ£2ÛÓ-×–;4Q‚ÛY‰Œ¹¡/ø¹d=›:Î è ÿ~óÑù`-Ñ›ÿp+ügÎÖ ˆÒôHŧpöL§!kÛ#½-»i.ŠËkOä]sÕ³6Ø®…öÝXµ…ÅFl’a¯IÎÍ3­mï{û–…6­á/Q,Ô:u ÚE'{ÿS#l™yŒÛÐÊÒŽV¬×˜Ô8ÐýºòòÕj3}ðo1ùÒ£ä×u}X\ÇsVVõñyù">ГÑ~q¡‘Yz·Î(ÓGYм©ôŠ*áÅZdïˆå¬X„›gá“D­Rý*‘9¤‹X…U)MQ;Äs* ' u"E¦ò€pÙåÄM”F—I¶bû¶‚º¸G]9!‚¬5Ó¢E-~s<[àì-²ö|Æ£óþ‡u¨éÒ¨‘ª,øUà1EEà­ÇÂyG} ˜¥GÊå³z:~oý2ýf¿¾òcÓŸ78r¾É¥~p‰z ŽIE…$Š("+1´ïÉÊ%Héÿ h¦Þ¼åŸŒ'¥P{Ý$Ç¥èéŽh{”?¥HXq9¾3ý»+ ÝqÎå¼yÒÝ ãŽG÷\ÿ-× œ[Èië¿Ø¯¼„?©°W]Ú©±±¶[À* ŽA{XÑ¥#J¥è»›¿#l.óás‚×Nñ¨ÆÛUäRøü¶ÅôºxdOÔè“þ½I ù6yÁš[?»ÕhaÝk«Áð4……uÖ‡WQâÀ¸$”(ä@ÁàŒíél7ZúO¯BÌ©g«ÓóòeréáóÂk#OÿJ`šõ=¶Ê?—‡H¥éôÖ¿¤ÿ €88Ó¸û‰P úêy“ô”jÞ¥l½éQZUx{“Dr*¥sÝ2âW]5C{{¼8Ä×e‚ ½áÛ=.ÛÚ î´,°2÷4ÒifnI !.±'êÓ·=Á\œ8÷³ëqQ)e˜l(%¶àñ-ŒÈ›ìÑÞÓ?ÔŠw³XÌï¤z„âÎ¥õ<è"¦Šß$—3o{UÏkÂt‡Ó@R[º]&‚‘ŽtôÒå‡b—¡ÔmEq.±Aqíœ/(åB*G“ñy‰ƒˆ`ÒŠA÷LkÝš{è)[Á0ä%˜¿8$Ý‹ k•ÔjFØÖüä{Ç5Ö(û£¿wÇæ÷HÎ{tXN³½þî=åT@jao¯¨—xsžp&ß$ö[MoKÖa×tÜb–ò¿ð\;Ͻ«?"ËPï™ ŸÿV!Pyk[p¥QPduð¶oû wšÊʳ6‘S×¥”MΗ£j²§Ï¹æôŸú)ïôLÊ+n0«Ûêv…W™¹…ZííÜ/lȆ¯ÌqÃ×ç»Øú߸Âç3ªPXÛ¯?†ÈÍ®¡@çf¥ø£;˱Y_æ¯=­ºF|n:nÜz‡K¼_Dé–Ñ0HU"?mfxÚàl‰h<¦I÷­ÑªãOgìbª©|T#*aÊ/å/Xó;TN ظ¤âûhe¥æq*†˜¿²pФy;`¡µõÊ^¡ˆê‚úƒ^”\¿¬²FúpdÊR_õ„*Ÿóôíir´´ü‰%X²¤ÆÃºDT§è6E §DQ¦T‘äÚ›­ÙŽT<èzÊiz6+»ûß‹j‹=¹êjÄä˜4‘­=ó^±ämú3Ë™*?Ç0/λE‰€<7æcÌ( Ü‹Èͼèv¦ù^gÁh×(÷÷‡+m¡;zŒõ6ä\ÖÇ£gö1piÑÑÈŸ*ÙH‹‡ (¯˜éŒˆÀÇnØ ù¬ÁýG7˜RøÊƒÛlC~Oì§AdGjÊ(†“¹ÇÊI¯™%è:ÕŸ›ê‚Ö£@…–{ƒGÞG Æ»ã³Îš2•xÑÜ7xtÑ6»YLß÷™iiëÇPyðK„35G½•Ε`˜ìC.—dŒvU¢:áÇ|5kp-ÇŽ<Âfî í ƒìJ¨?D…ÇsäKF÷]l0×þjG¿y^ÿ Ç/¡dŠ_ùß;øX–·l\JÁ !*Ò—U›B,åÍi¤èªåÅJïúYÆQóµÙ£-Þù˜Æp]§Î ÿõ¡1ûÌÈ 6$Ú"ê4<³¥œÔÎéÝ-AÐB¢)0».“ï|…¸ZL)eÝSÀ«³ìm 7»|y žÜzú@3ÀLàìä"†óÎq“ˆåŸæ×;qÖ”š’Š¬…Ó}–‘0h§F+«fÃŽ…á˜0àòÈÓú(õí …c&@÷æ«ý¾sj¹òU5™uœ 5˜–ÇwgÝôp©cpÚbWªB5™͎G£ÔWðq&¤t‚“öqfÆø@6eQK3Îo¿ñvu’ÔâÅBïVÕé%…—zŸ½H–BS™ø®FíD «¤©ÈùžÜ„ÊŸ\>núÅÅUuñ}{úq»„ ìM>v•©yðÛHêçÛåܨSÏ^p´XæuúÒÑy[l_Q_ 1•ŒÚ¬âçYñž?"s F²&ù¬ìQ¿žMD"ÒòçC®ÀïþΡ)uÒÁY¦UŒê<¯T¡>¹c!]“H0«ègûL¸Ë?¯2ÒýIäZ>Y bÈu< ÎÅlËÛW¬?'{c¶{.y ɺ„sî íEdBMºvŸòž¨+2ÇQþ­‡arèÎ;§ &® èãŠ[Ú„ @YnaNÝ‘À޼èÏ-Ô_ˆÌeÙÉñÎòs×gÃTX*þÒyýo#÷žT^Ôè…W¹LoE§TØ¢ÅÔvŸ”“±? —–xËqIìGñ$$‘e _ÈCk{©ù´aÔC]xš9²ºÚ°›,Á[ÃRiÞE~ïâ8atNÉx" G¹zeJfÃûËéü@ Ùø= E ^0d•hrÆtÞƒÞ *oº¹p…HZ(9×Üa‡‚«2à–Î÷s‡ (\QhÒ£¦˜IÕpiö&,€ÓÿKFŒƒf»4+¼wTÓ7ÓN—¥àÎVR%¨z.Jb—8Xg­—`P«F•ø’ÅîπĽU)Ìó^phCûžžzÈ~¼°SÒG9—DæH²" @yE±£¡ éÇaìrA7>ß¼ÚJTsbTÿø¦hÃ=Ÿi©‰ÕÞÆ²:22Ëâôì=£µŒ±‚5²û@<û=RÒ·@àˆò  ˆƒ/” ÅK’˜13~“,•ÀBr.É ðûÿ.'Q±ª–t$kvÌÙ Éz† ‹ÝÄ{Z…ß²/¦nˆÐ‘Oä1 ÞÚT*göݧ÷ö<ç ©>îýfæ0 xŒ²™§¢s\^K7… ÂÉo:‰éñû½õÅÌRŘ¥±Ã@Ò½¼ò¤€=ýŒÆ$#Õi›*ˆ.P´I/ôÌ,ðÄå2 ê.ª€´´‹Ôö€ÛÙ=R]ï§!]–a”%ƒÇþi1#.¦ùE<Ù+»ÀÆØ(UCÖmûÏTÌ-Ó˜2Ñ9j'Jià0Œ£"ÉröSûífkå›QîWvrr€Î~µ}º«>"ô—ÞÑŽºK\­£Å ͨ§H4­: Ì8ü:J) ®ãµ‰L€ÅÔ‹vL`³ßL4ŒK~ß:ÿ€L~¦óõû{õ׃ó7ÓŽª5>2½'O¯†v ‘¼rö-õóS˜ÀíÌæé† oü@zªH¸ÌKÉx¡¯âšŒŸµ,bó8'þ8úàÄYЬü:}TQÝVrYö&RÑ!Š•ðH½QMR‚kZRî¹µé¹8m¬uƒPÿFC“‹ ª È©|½ ÄgQ.¿¤Öjû²Ï&‘×ÍŠU«Ü,÷ÝÎÍ>j©Ë$ƒüÔŸdÌ.:´ãÿ%¡ôÐøeÌ&ü¾>MõYà›ø¨¢H;{`Mö¦ºC¶s?#¯Š$Vò4Í£ ¡VQOHä0[Í€q*cm÷³'\b]Åb·ýýI„|#²( `¾Ò¯Úp-è€5û+JoÙñ´TbH­S(S! h2˜Ä¬ÙÊæ =Œ):ä3¹ á.¼«ˆÅci½IÔ ì#½_Ãtút™¯Ý©»I8ÆÓã/41Ï6Ö¿’(ÐßBÇ'¼ã\™Iø– B2óx¤+#е¥³ iÙXluê7³1?«Ìºt®ó-)d˜v´o±¬†šÏ« t/¶Åkð3ÓZE½ÆSs³U rÙ-ù´¶pÆ|m½ènJ·ÝAú(þ›`´ ¤.É•£Ê6©Œ²Æ ÕÏð6"ô FmšÙi€A ‘k˜] afB±®[%ÌúíÏ› ¥ñ¿DPön>yÕ% xÁbÙ}ÁbÄÇé·I§TI…¸ ’ĺ £‹ã JJ“+ºŽÜ¡ÙA½#[täà™I6ǼÒÞñ¶rØ¡<%…®Baù{ÿ™ )¶o÷ÕkwólüG£Yž)`güˆ‹Ó5ìÇ®?È S‘ð–¢*` ¢ ("½$¶›Ñ×É Håb@gØ7ΫáÝŸ=v šá_ ÝOûÉÜGÏ¿àÀ_02žê Í_ ­ô€€/[³}æh_?ÞOf¥ñk­aË9˜Ï6³zER깊ÔÜ\õµô<÷EÓ+)ò{£Ò ;£Xúpw*ÃÙ_Ñk| €ùÌðõþª¤_g ¹uÛl72€là[uåׄ€üıÌÜ9ÿ{á=¾.cOxYBÁŸ¦ôºe¤{ÓçªçÙJB6Ø‹³ÉòÞ‰>\IsïdRÌùÿ5ø4}ay¸#hv\“Ž?Ä+bÛ,%Ò>cÔa.Ì?+gëž[«À t}4®!KrÔüòϧ  G[Tš*måaØh9‰û2¸>˜ã͘.1?  ¦3îZœ ±R_I‘°ÛÅ<-òÝćê£NÛcRaøô‹}M,)ôé… º‹B˜ü¼°„Oûæçø¶*´ý_$$‚5Íël¿öôE=Æ(¤Â-·1C–¿ã‰â†"Wå…aËt•ÔÂ"<.˲ÚA—ÿ ÜWµPAþAë¾2áÞtg ñ¸PüÄ#x~ºDñK»˜¤TZ›ÏÛÄ?×\‚ã+žwŸ6¦:ÍñF"É9Á|sÈÙã<ê˜yòòÕj3}ðo1ùÒ£äÖЋ¨œÉªµÀ¿£­÷a£ˆ’Ó£‡°E;®Û!SLœ“FóF1ÒX‘™¾•Aå‘Ñ,ÖßL.üCÿfÈ_PÐÏ~z|/F8,ôqû«kÃX\60ñÓ×±¾¾&@Sjò–T*j`çqãK¦5 ‘ÿT´ÇõI”¿[ÏÊÍðB®.Ôu<¼åö Þ´wáb½ÈQdLw~“_¼œ.±Û õd¯‰ñS‰ ¬|Bb’ªaòê‘Mœb¼Â¥‚½¸Õh=J°ÕOÚ,ƒ”v'^ä֗뙥 ²å,e ’Ë þÓWìàÌmííe#Ù÷6\üqßwaÇ…»ËEM––·é…Ƹd²ÛŸš _õ⦒_Z%s¶è$€&zøÀe4z¿~È®|“†ñt²h²š{äRh’¼ÇàÉ;ÕLÒDà="+mãÅÉù§ÑÆÄƒÒ«š?ýe¬j6’B”*Än6L(pój†K Áy—ï}sŠx¾ÇyK"uH¤rkW)Óݦ’ªu•ñ¡ ŒèÄèǧM| o9FSò]»4ö]臘«Z¥XûÐ]a,¹ â¿(«Q íÒÃh>À£­œŽ>,üÖDÞ}HqAôÈf HoYZcxhMUæê¹êÍ}þÒj·ñCØáŽDW7šÏváÑý¯81†§âSRqã ŽûŸ§‚.çŒøGÖÄ®'ôîΠ¸=ôýºTá–¶Ö'©ˆ=;ÄGWƒ”©gÔ¬uŒìüM›ãæÏýªã×?r2[‘ê>a5³–c{ÌDÅzTö•µžkÿg+3†ˆZ0ÝxÿqÄú)‚5ãcåw°R»’V™‰ó^™ÏÕƒV¨ðË%çq]­²u˜BÝZ¬ÊÀ„à[†¡5w| Y”Ýy߀!‹ÖÒÒñ»Œ¿¤ïaB"Û•Sk†007P•¶Î“®ú0åëÔÜ!‰£¡twEó6£k]¦EdùÞàå‡È/³ÝT¼b'Ö.©‚z*Îmuu'Z>ÓqRCÇå.d[„¶ã’–“ ®§MMõ(ðthpñÐ ¼mÅf+n öá…8n+¾FôÅSàþÐt3#tñi‰ï¢ÔäûÐ}S§³Iïz§GuŸ_ŸÛ…ßІís'³H¹×Ç|/6¬8Û )•´‘’š°Ö¤ø>”ëEâp$ßW;¤×¤å°FwJö1piÑÑÈŸ%.çQŒ³r°ÄÊ¥Ü&# :{@„X,ÑÝ-R•”¹L1¨л !ËôgŸnÜ·šZÓY{wÒëíYcÂeI[>ÕZ‹ÂÆ)Þ–æiÍð îh‘ðJ†î²§ ß-`âÑ$´pèìR [C.ƒUq[~Ü9!Ï“Oš¤Ó´¤v69úŽX=yèS­çzDsEõãÊxú(Ù¯£íOÕÜTÃr½g]ÑyØ:á—óÀãñ½nð¤ÚܱŽ\[ŽÖ“L/!æãO#³Ž,Ó÷÷öÒzgh;È£Õß*†e{ó~õÆégmîÇüØ«ow‹!”™=14–8™ìŠçKþü8 û¢–ó~D·6¶­!¥ëOÑš,O>l|T›Uq¼43Âᑦã‡ö±æÍgg+Ó³—™ÒPsEeÛ$–*2±ÞÇÉ39—ªl¥ºxñòžmŠ“ ÇQðϾîB×ë\ôPÀt[—˜•p²¸»G–ß^1QÅ-"ËU}„‡ˆÙ.îF±Q êðŸa²`ÔBýô¨öqfÆø@6eQœwÆ“&AÓgþ[°©«Ë¬NåèfQ;õóF½ÊÕÂzت[ÿ…Ñ´ð á}›-Ï â×ÃVgÂŠË \³Ãä£éµ>v÷c}ZX0;’´Ïl\¾Ü¡âöÃë… «&JœaG‘(­p¢ý!®~`÷N¶¤´n¤n“)cœœuëN"]éϧ‹C ¹îEÚáHi#ý¿‰Š“Lœ‚!¿ž„°œµ}têqÄw®ªD)u¹tÿ<Êå}…ÎvÍ­0Óò­£† Î:…\¶Ù²¶Hh§ü¾Ó^Om"æúšo~äk{ ¯¸ uµ\ÔÓåÐWQ}âaK³PO ¸ùðØã*ïRíl˜ý­õðäî”ébÔö HÙùh½`2UãhØËÞ~ùûÑ!pF›Ó¨-<Ó«l*R•ObÏâZW÷'Ó¡ óãe_Á]0Ruiþòã¯ÜÙ`–<%®z󩹯ØYQŸ‘×; ß¾Ø?Æ\5éy d”EÄáZà¢ÄÉÀƒH¢MdSŽß|Ùžæ£Þ6ååF®ËýB÷}p@ëÅ2~Šà±vÆI³è·É¬Þ»«Z=C ùXj?ú{ÇKÙ劄`d¯fÃø=uE ^0d•hrÆtèÄ'‡ÁE#ÃÇtæAoxŽqAÑ•Çc LÀŒ–ÕmEƒeêK“y™6† f‹ãÕ†£EP;°ëlx16Wá5rßdŃv€lJª‹šR«¸¯®IžS½Tª“ø5RË–õ“LÕ. dl0Dºä!{#¤pN7#À_Q·{9ãæø‹7£[#jŸ‹0’Ug!ϰíXeXÊ*vÛßu¥¶&#´…„n–¿¨Þ1ÞZ;¶»¿â2„aR§Ïk½…‚dF}I½$Æ )¬%t¬¹ïôZðe1XÐȧ©Û˜!3wÀ;3^¹* éoj`SˆHï‘@7—P>/‚F1µ¢b±ƒ£ª§¹C'N@ÕÂE)/LPÖÎyE·š{[G×_YŸ9a¯¥Û‚Á‘ÜÈ)û(4™âÚáñãÛ$÷õß C+îRÉŒ‚nÝÐ ×Ô„¸Gî8à ÷×kÿ]¢ÌR½ç9×GÅ„²…õÊóÂ!`¦ÏØ—¶°M‡…xK»ƒó‰&ÕÞ±FåÐT¥E²Mq¶×1hJ,ƒ…|¥Ø*GÓa¼UÚÃnyk£fº¦O· /Rj(Öz¢ è{•vq8Y»â\h/Žß=ÕÁ§Öß¾zÍ€ÖØ©žò‹ØWAÿHGRÂ*“Q'i¥Ø?]|Yb4á„¢;8&Åä©güD"rç©N¹OpêWˆ¥AÆžA¯*£ˆa NÆx0)ö-TŸÓôŠh$–;$5ükŠQ(Se™ó«ïh¬ …š_ør¥«Q4|ª0¼•º¸  ÙÞ©ƒ‡º “z„JL¬ÄÌElðÚêÌœ|I›‰ëÍ溚Õ?ŽÛÉXéHÓ<3’½±cÿXÿQû:>uêK¼‘ì@„N¶õ¬¹Œ–Cç¾ëß~Š@P$x„¼næ)üÌ:ýÛÛPå“ lI¶¼©e ~ýŒ?Ÿó½—fFñ3M¤Ǩ¿±ûÍã¶çuý?zÌÝÞHE6*ú••`§oYØJŽ“DštÊ îR" ½OÍ~š¡œ}±Ó;glÖœ0õì»?Œ­K\wF¤VöˆfÛw`6«ŽÄPæ÷* B!t§óBú÷Vó|n•¯Ï˜ñ”qt²4¶ÃÝ ¸MEµ ã´&WÙ…ÌPUGõ•õò/ß¹Vä¿ÕÙ š“Íÿ ¬ÝÊâÉÿ^.cø¤,*‘Õ2 «’ÚH¢š }ã¤zAmtðß2§r·¨­v}95{E`BPF¸'*ÌpŽ[ˆ4t%Ãè!‡U©°é¨¿Ã_>õÈê{þšf,¤Œãοƨ÷ôWÇÎ÷¥Íô FmšÙi€A ‘k˜] afB±®X>ôí¸üFi|²(ƒõS´sü£Ì+0ì=ä‚iÓñÀ(4ÏÉ’K ¿‹SVî•ÈA{ ž2UwIYoò°ït÷a˜ˆ™–ÒAvö†ºk{cÒ:º‰°ìódD›•ÆÌ+d=˶ÕÁd¹q6oÁaæÿ}o•úç%žše+gûvà§¹GÁúŠœekÑ$”®¦‹‘®¼`aÁL$f‡Ë1 *&„öÐü†¶ñÅ•.WÛY½õ§C~Á†Þ³ÏÇ›}é*‘²A±ûWáÎ6 ¾KÐ+wåÄ‘ï·ÑY9H˜€dë‰W½áéÔf¤]’ÛÜQ®Ö^ z1»ÅìÜe-^ …[-Plø~µ<ŸMYÉG='~%"ºyåÖtÎ>ŠmªºD,Û ŠÕñÁ¼Ó4Üä§x÷LbpôÎÍçzdaÙN M’Ïo"×1tz[ƒ“þáB^pmSþk3 ¶‡÷:æ§5Ê â†­Šž;K¶þ6gëé ‹nåÓþ¨ _#—'†[±¡ž·÷ã*ÐéA¥­OÈœ;Pn (pPÏK(×µt/j¯ë ±rx5ÒºÎ}úêL×vX¤ëkÚÍwrãøi-öê—ðÖùõkF?†” $¾ÝsŸV°aûo?Ût~Ý:ýº@Û×fkÌ>ÎÛÛóñS•£šÒ²E®æ‡ÌHñ¯—¯°¦¼î3ÖaŠ2}A&];U!røÉ} “ƒ½Éì²Uþ$î¢hÙZ ò…“¾çgÖ»]‘Rb%‚‹ö`¿ZT4FÔþü¾Â³¿ÍípÁŸW놛( ‚‰ÃV¶×ñ5ëòñŸÿÁ>ŸJ‘ 1ì@¦Û1‡÷q—nÍê‚UÃZQÜÎÕœ–”añY™³î3{·´þ`’VýPÍCL×·È$\nzEÁ6oŸoÂfÆ Aȵ„žµO‚ˆ´ LIÓ:à±GD÷é2aì9ÕM÷WQ0Z²/GÇ«¿æ0¥2…–F‘=«*ÊùºÍMˆ„h°¦H ut¹º±üä‚Ò#)} {®ŒÿNo¶ëÌv¤†kse%1bÅ+Ã/›âËOÕ§$Çõ÷K’Å{ªÑ“ç{^SŽO)G.øQWffðò[ØêRŸašFϨ e+eäÊ6©jíX ³»ìÐúÌ?DÄ éŒr0ñÀ£E½^ 7*Ÿ jð†¼³´XÖdðJE2=ÒúÖÞ“$ê2‡cÿ % ²õãq•ôŒ7˜BÑÑ€ØÝWgUýÆ0§”)QH\;-„‰"áB¹Ø™ ô@$mâ©ö6~§ˆ%Î1C­+"k3QÖ*«ß.qP8{Ö†Î2 "ž CœíQX÷æžmk¡³äx‘ÅØå&âA±!,bof 5Ý¢ž-ä§ ËRy€=.„Ÿ0Bºž›³¯3x¡>8c^Üžj•êø! ì¹ß…AÞ· &Ù­œ]q˜WMt ý¥ÙðúšKlK˜*Q1ïÇ8¢ió"禛¶ju©)ŒWÊdõŸDËp0ðkèHM ‹ÍbšóO]-‹*ÓuÙ#æa¤V›ßÉÐïÒÖíYðX…˜O 0 ÷Ù5td•}±¼±×ÕÔÇ —cÏÂhJ#YÌ«ÝïùÜ¼È 7»v ɨï3’„‘070¨Nf5 >¶x{0êïxb’ç^ˆ‘ããw?ó=½\µ#’[W•^ÿãÖéÜÖé*gº©-YrpXÕ[T`Y“sÝÐ_#<’xiì\Qõve]›Q¢b¢'FˆÆõÊ΢ÁVê‹×  E“°=¿$·vQî`™J£W±rÀËÃÜÔ%¹~‡/±CÞ?'#ˆË¹kY«ŒÇþõŒ)™OüS ÒÍô¶ß.0¦qôŽn»ç‘ÑýR©)®Ð¿ˆ‚î6Ù1B™\ÕPIƒÊJúÔVWÓN¦-H2rÞ_´L%îñäõ‘•ÑsP»/g›GQ)£4õ#ìôgE7ܹgÌV$‚’Žˆ~Æîíþ]ù ]æöˆ‘*ˆ7j;8a_ï²kÝISE‹`7o£×)<ŒéôÍýpÚµ’IxÅĺw¥¯Ç8oƒq–9üó‘èËÊ}c)uÚ?$"[-:I‹‰¯a Qiq`Xßnå¯p &äc\VÀíwuÔÈ«}B´²"»¨ÅØŠ%ÓçŠJÜ5OÒåÚ S[f$fhÖÀ¾tÿׄ…µ¡9†ˆ; >ÂIqÍV¹ðÇh€g¡ öù™ŽÊŒU\ns[ìh]ÿ*|ö,Š3EZ ƒÎ‚»LÓÛëÆˆ:W}5íßã¿ã(½¶ºÆ„g­»:¤U±øFZ&mÀ€Ž„Bgò."lNB95Ôy·kà¡DÂÕŠ¸ƒÖ± GBpZÂÛX Š&Ùómw’Éapj  Œ} Ü„o¼^LÌ=¥¯.Ö\éä ¤)ð’íÆà|ˆm®pÈfÿ)ÞÊß;œCÝì:_ähu’9&‹W=Ž•Â´Ã½Z£ËM—‘þ±RÎ¥‹Ÿ5zÅžÁàpDÖ R¿`»‡ˆÚ”dÏ*¡lúe.ßìy#®ýÜï5ãa‡rŸÔ—P×Ñg&²9ùAŒØÏý=Fv)‡úô›A­!جw€§ýù§¦­ñXô"¹®=þøï]”Zê޲Ûu|6á6"èE¡ÃqxéS!7Ò°þÃ÷·Œ:¼ e~Ú×í¹òf6µ†íá‚ÿOÇÕe7^€D¨%žóò¤§ÌuÙCèÄ¡ŒðŽì7C ó¢;³xȾë§v 2¨6q€å×íB“]!šfYÍLZ$åÿplN#šÍ?K^!ËÀ³ÚüôÑÚ¡ï™:€îâ>sª·]Ä\vr²` ô°å2ۨ¦ŒáX-ýšg”Å©ã¹1T}_Í¿¯hõahcܲ#s_K—:³Zï *±-b˜Šµ¿ºwBV4…ÂG ò{ìž|ò±µ´hR=(B_޼kœzª¡É Íò.*ãE0'Ú4Ö¸÷è*9™“HÅŒ>èÒ$Âó\T»îA+' óÆÀ8Z—àï=Ôù;Uœ¼ t“ìQà–µ(ïÁ ¯Ê?0A~3¥{èl,S…Ç”-r´“´ œvYU’eeî°f.‰4$ú}˜1H6ÜÞlITkÐ2—(1ЫŒ%~d݃y0kÆcÞÖŠëé¦]LñD!Ë,Ö˜ÛzÈÅ“òþ‘Ûúį¥b­ Ž÷9Ö1«Ta»ºŽZ³QɆòµ0ÃnرK¹wO²ÊnUKõÍÀ\ÍtØ…‰)(`+©Ð0"zU’’·–®žuézðC(üS¤{=§¼'£½…Ñ%¤²Ñõí>F:â§sQVLlùp[«AØ:êMçý%j'ýò"ºF (8f H^<;d~MyB¢iìåö¦.`âè^+Í8Rc©i xÙ¯)%±n¡ûQ@½Â7L:“T‡[ê2–AÑDhœÇ7hf4~Nœ‹RZb³E\!¥‹~–ÍI¸HB…®p@#p­ êY¾9¥^˜ðrÝìÃdÞÁký—–z ÃNÁ¿ÂwÓï@ÿ/‰‚æeYUôeåæ4£¼ôòu­‰ƒF©Y¢þÖæF2QF‰†Æî'£“|#;ÌðQ"c¨.¦ó`âš^s!J*ÈW£¿þø2zieÝÝÌH™€N:‘=@ìŸh¡Eò0´2Vtzˆì4#dK™é§_û«|ÞðC)AfÓt£¤$qVF¬¥81*+Ÿ÷|êîúX»Í}ä*8KïclmtDÝÙ¸Œ’5ÿ¼v7¥$ˆàYÿ_˜Sî0)T¦GÙ ŒBe|øèñ׆ê„!·yEA;$Ë£á±á„vöèáüVˆ.Éàë j|'w¥±XUU[X8%'WqmŠòø‘¡ãΊЬ<…+°ýþy°à$X) 9¼LTAE¸±z¬>À-kÏŠÖýä ¢‹iŽÃ¿ƒeÝnÚ¶>Êjù¦ö“C3× 9dZ¦¡ûð·Pòs>k…ZH¼³‚Ô“3Æ mJº>ŽõÉž^?Žz£làYÛ>´îÊnp&Üéþ/ü:…?‡bóoáÔÏü+áé‘ÿ§ßðÐ?á¬_Ã¥ßáÑðèMöÒ?ðÛGðé³øv)ü:"þ€¬þ•ßðô¤þ%ü5{øt¥ü: þ÷¦üÿsÛ7óèzü:¬ŸnýÿU=øU_ÏÒßþ~˜—á ÏY¿ŸN¿Ï£¯çÐXë–Þ35ÄÔø@ ª¾JE­Ä/Rªyñ¨LKé'zzHHìaÇ­Á‘¿ `´/{ƒÄZ€|$âÙÆ1$dîÙ¾­ùÌ?«Ãt.çþ; ôiFª]«ÝýšÁÊ?e)Gð*1oºù†^}ÔZ©kin×ù!#”ËL}H‚Bº5JÁrl\ ´b².98øŠå%Z²o—ûYôî³¥"kÞÆ=ÝÉéGVÈâö(˜3îqZ}"á÷ë‚ðÁ¿]š8ŽVäÈXk,~¹K¤\s+t&êäæ1ZV ª;Ùpa©y ¡Ždëüu똨c!`zîíšÚ)~]0ë*WŠ¿ó˜±Î¢Ò7¾ÿG>§™KÛBŽ!xÖ¼/ 2X²õ·„ûýP˜ÿ ¼F9&ã|öÍ5fýøÔ:òlW¹òTÂKÚO²þÂªÖæm>aSM´;ÞbÀ56g&€Y$çü¶eܧè*ª`lÝG-Ž®Z³íÏBíƒ à½9ãµ”Æ( –…ïú¨‡+YJÐ}ëôf$6w` †Cy!ÇF±º¤PN=¤xŸ°³À\2Á‹r¹4dYÁXÂðÞúϦÔ\³ü•¯ ÔÎI{÷¨¾¯Œß¥ÝñzoÿÿVèØ 8•qv|Š4ÓãO=ÒÂhôQÅ-Û-ïŃ3öA u¤ú®â³5–çaÑ™ 7ÎSËO¥~vº+°êéçÄ,wROŽ2¨=Óuãßxç¿ÆM.X-ÿ_{Ÿ=7þ0­Í7 ";æ0¦X̶`Ðû–aŽˆv*€1©òN³ŠJ¡GESé×]ˆnËhÆX¢¾çó6 f™ØàrÀV>’kR Är|ÏÔô–+º£ñp’Ž®ß..tìè1Å‹ÔOhN 2&þ—óžîP6š@¤9Þ0ç N”Ÿ7k»¬°”¡µ¢˜TÁ¬|œYsi1«×ÚIa§ŒB³ùo8T7ÚñƒËÿ"B³T›3”p0ž"œ… Ì‘ÃL«“”9« 1‰Š .Ð ç·˜;Î) Æôö-A™ÿ}·æ'ÔŠ³Ó«cs½WÇ~Äå]5ä¶ Uj']»Ó4Ç<—G ‘QÑw%\‡il W}$ߊ//œ]À;iâ¥Ft7Æ…âÛË(¨  >ìE¬•yCWKé’Í´7Rã~ìeï…AéúɧÔÌS3aÚD´’^渡S4òJb/L‚ê6â±@ñšEH\Ÿ{ÆÀú—©òÈ;ôfó0¿Éà#¤RléM¡Æ–ôB¤O:ðáy'\…_3sœ+‹nNKN”éùrs…3J²­Ît~ߪ–Lº“”¢ç‰„x›ƒ21^m˜yGœ^XÁ+¾#LàEƒt4lò5Ðp<âµaüú1Ë.K½zú0ÊVpãñÅù7ì’a¨~ðMþZU(b£µÍ˜:ׯm¾j9cª™ ÄÍØjBê¹Ú}V]5³7Xå ¿wûN—þ"ÅÖˆ[éoÍ·KE‚Ï¡èê¤!ÝãGF‰]”B(=c¤³ä`KiÂü,ÍO« /÷0I­q¡ÉgîºB“š™Âò¦~»…7qOªÅ™lÀNÝ¡¼}ðÙFï¥wÃXµ“æ£Gø’ªEé¸lßýûsCÊ%Œx^ø˜ÛxÃZ !ššè$|÷´ò@sfÕœc}©³N'šåœ ‹Ž Ï[—{Äÿâ;#µ¡ÆPâd­9ÖKˆtmpI›Þ”Ñ)ðpïÝØ©J‘·/Á=îgr™XŸNf´è^ðk[~¨æA˹°3ÎЩö1ôÜ(µšsüùÌÐ~k`Nw³¤DÐIì" »á‘|ÏÇ1ªœGkè^-(¢«®†x#Äá dß,5@ùúÓÞI×c…rÏcz".…Ì“‘/…Ã5&f“|ï5m28h3&q[§D«¯w)Tð({Kûÿ{ÔmBb)BðBá+qq¼‚Qãh"×µeåQcmS˜Æ°• ï&—ie8ñÈ?³_ùxF„¿—…£ ¢ÆŽJKZ ™UòÑŒ8½%–qZÚbU>4ýV·}&ÿ_þNûªLÖ*|,íJœÿCbölùß/CÐjá¢J΂BEsÎý6¾q§ò$ƒº®'Mù¤¦e•*4Yu­Œ¨‡y >„3Ö˜NYÎo@è ]R0U€õù4ùŒ.{hAК7D :·JÑz8vF„  ‰ˆÚ=°Åš$‘"èRÍ¥ Se#…åR_(ýwæH‹ ©y¡æ`!B´Î®œ©ÑU–‡R×¶K†Yƒ5„5ž±á¨Q=ˆ¯Ôj¹ù¥ÁòxÙfæÖ,Aˆ¡é“[%«€âµ%êˆCÞ’$þÏÒ”µ¹A_/›8[תÒÑŸí’Ž/ÔWzϱ®K‹[¼Hd®†7u9 Ì?šÊÐ8”ÙIÁ÷Q„üp¥¦ä.Úgù—T¿w´ % {üîîØR0~ƒ:á}¤!¹ÎÆtìW˜|ÎÆÝÊl.Kƒx_fïƒ dÝæJ6Y¦œ>1OœeJª“Txº*ÄRBP*(£û p¤Á=ãïµã€…ÐÖ¸C¸—^¡_Kk¾ Sßà÷Mg‚5ù’Ó v2¡g 9­…­S@¼å ›?¡½élw½7ÇØ¬ùXÚ'ø­  «&_°rñ¥fv2ØUŸŒŒå6[®r%SÞÒZ’SÀÇÃắiñ eöúñÛ4qÖÝÑ`ÔÙ!Ç=lÎ4¥þU ôƘAê\ZRˆ LÒQÌpUc²šŸ$xbJµÛV_xò{Ë®Ó2Œø‰®™Vûת2”øªÃg;ª…a˜ŽÊ_@b™2š!¯zí-ܶ ÏŸ‘9†ÛTgàÞ¿½2q'¨/N¶S(Fü$ú¾sñy1nŒi+üwS=ÎçmÓÜ0½{‹}ð’ý ý¿ªg¸$ǨTRZ>0>Û­"wíª0[Í{t‡€}âöY- :Óaù[6îÔ¢ÇV¶¬ïºzš©äÞ¢åžäŠA·ã`Ûò3ÀeñLäå!á&|ü|%Ý•ÉV=©ðegô²Á…ïË¢ƒA\ãb¬ÈÛP±f#÷‡Š.L`v^‘èCfÔȶáÅÚÞT:|ã‚“eÕ™‹ôiµÓAWþ%úÿkˆ¹tñÅ[€*çU ›`Åož\¶O~wßðý“VhL³^‚üýÈÝ"çòaV|¬"Þ‘BD%Ž?_;{KHÎÆEéÏÆº»P``?CªØE|äÖ<+‡¿ˆ•v‡g)È¥[LÙVpÆa'ë C1©°ïx!@ZÈJ*¤JW&õËþ‹ò+W„&ÚÎ^`.µ] Ý\ÝØþdÂLæßQ*#}§|c›OÕ¿,³K7E•›Øz‘ézß¹¦ŠQbóé ×(ç·aŠ<‰ÍˆådQ6;Ìþ’j•/€¬X£ GDà™ù.¼]tŸî_™æ¨báQèyÆ#î{bvÐ {rÝÙÜnû/rÝV„ŒR…뎣„SvÅ/=^qzÀŽ›á¯ö’ß^@eøC9¿Söþô¶oY¸Pa­üŠ–’ Ù²äèÎÀÅIsÉG4£Mr¦ÈXžêµ…*i)WiÚæj¬€húêfùAÍÔ@îí£Öî´·®d‹rÉ*µŸôé8Œ¶Æ-•VÎɶW}¸@²­ª~´Ü eæ8@Œ&Kë)·ðÔHVÊic8ZEŒzH@U&7#ðÍwÜÎsÅ粔ؔ™VC¢ÛsäÆ˜Šª.Þ%:¡¼”“à Y£¼Ò¯ñnù¿Ú*柖.‹NÇû“/›öi [3\n+5å/´ ½ºG&01HÕZ®{|Ã'FÌÿ}Š˜¸+ûì§§È亨 ¥°ýlÔîÐ:÷b´ÆSK³ˆ,ü+d,“ 8.=+LŽÌ)¼g²½Ô3%öfR—©ÝðäÃÜièËÖ2㊓OLþöm @DÈkõžX઄—Æ"oÈ–>sOþCåÒ;pùIJt¸›¦IÁ–c C¢'Yñ ÝëŠÚh'æ@ ­KþÍ)¸ôÀ½zóXf{Åq•”8mÇ;¦”|OÞÛ!Ýæ`?ƒÆ°hÝ„eö۔떂MÑOòuÕyIý툈;Ôz¶~3t3$!5ÙpùÔÕ-‡ê#=O_«ì®.1B?Bjg*MªÉ ˜™à?d-c[vk50e0†8/ ²;DàÜT4W™…q}ˆxvGÉ¡*4#¡~{*¼ ma=œƒd±¹)ç»_Î0·Ñ(@aªå V‰tƒ„l½$>¢HøIøGâL¼éáTÓz¥_ô;¨dJs ñük€úJÑÏy†ù}©ña÷î÷צѾ˜Í¨MÌÄ=%3Õ,£DhmÏ”ŽŽnû`ú]07kLäǦ‰t§óÄð'²¼½ÈÐ/Ä?T"63×›¼À ^èÁ#uãS9—T„sE##|¤[î'¬…aw› ´Þwë`fŠž¼èJ⦧„)ÓÙÈ —+s6ÌÞf­ãAŠåÜ­LÞ– Ôm&Z"õù´iSÇhÔnê]¥Û¬'K†)5oý°¬ýLufãiÒDD–%58‡À O7cÚ¢ýï˜øÂª´z.D0\ú¦¼¶á¾TÉlYMSz§šë~VäGí0‰×Dì®À¤?t¼Iœ¤=Eff ¶™½ióZkC/©Üá>ªfì@øFö³Ëغ´ÇT÷v, ×Hq÷ ¯cdl¹(z5<”`.ŽåƒÆÕ£±>Do wéóÝ^ááù³ÿ,¢¶§I¡½‡U!‹††jÊ’¡Ð9ê‘°ØÎ=;Ç-ùaÐü ž«†'ÿÝji¯Xñ!^Ìì ‚ÜÞAô!gƒr  —ëTí‚,Ñ‚¿}%Ù¸Ó¦¸—æ3}JÆNlV_K»ü“8De½Õk¥¤: îÃ|õq0gP×Õh:óõVo8Ve©Å¿8Ë qÓk5„N«*û=W«U{Vƒ”f{ï>wøÆë½å+î)¨(·üœz“éHwü.U¶—‡°Æ:\P PTZo—dyø\ =Æ3TpÊá6*¬½Eï+¢ýïYó7h¬þŠ,›<§UTP_Óa…Y}úÈó»Õý7(q’Û“º²PÅpËÄøÀûdÒiîëEÛK#««8(ÞáS²gci¸øæÚð9Í›f?²CÎHi®Á*ô÷Uéf"~ÈrpIMZ+µÀ·Œr]Àã¥Æ­EuVÀL¡Ä{{©ð6u—…w{Ë”aÉ“çvN™ØÓ‰ãýß|z¥­ ñJÅ·;H´+̃ñ²˜ßYzTcøÓzó©!üU¡fËF_çz*aÆ‚qMn$s%ûGQaJ°€x‰:äçÓýE¸2þkž R ºŠ}f=‡qj‹uç/æÍ ÛU©‚XÂVvþ>5ôÌ­±š“9èž©{ï½ šÖ©ÊžÑËœç%‚YJÀß+ß«õš¹D’tùõuõaM²UVv\y,`݈þž“ø¹î¹ŸÉª楋ŠÑ¼­­‹”Å—XÚ² %‹"'<Ÿn|Ëó%€—¿á^rá*ŒÝ(Cõ'píßÄx™ <_PêuÜዺa¾³M¼”6|FýåϪn>vVHxRÍËÀ!­ë©¡@Ÿó›caÂü}5äîlþdXÆ%»1Æêã./jÂöbÕ«.^š5!Óã¹eýWìIW=6àý… —Úö5ß’5qÏc=Áÿ¸MJÆø„û‰W –F§òÝÐк-{Ru—Âs¬^‹¯ý¥’ÝS9µ3,›Ž-iø8 3ã~3}CrG–uj…­Ä_HA×RÒFvdîyþÊ5…ƒ`ô]gÝìD8ukö|‰ùèフdW¼±ûL…ÆÃŠá4_Â_ÚWcV+(Ô3Ld,Ð6±‹& f"“¿`‚Ä>³A(GU£`ès½3tÀª¼J…@¤èö[ÿu¹ Ï9µ×Û厙mœN=à8N‚‹_…³LDÅìÈðÌ#ý¾±©×¡U*ÄŒ¼µUól äëÐÝ^ÂBð*ÕîÌ€Œ"ÙØW; 59€5¬óÚu.ô`.e_.>¦'¿­p"Ž ÁçS­[mÿ6tÔ_Äa`¡Fœ½Ï²\¿äÇ÷а±H mõÕ/ ;ËÀzìò²Ç¹¬É Âæß\có“º1º¶5üMšÈ™Oãû" R0‰ê4ü¹X#÷“PÜ<ˆ i]ÃíÔ]á ¯EJ0ŠÛ ºxöîE^²¸P¯„Æõü B6þ÷ïŸØg>pܦ®^*×Ý6 Hßë@–(Êc“•êv_„”y»…zE³ÊþÈ¢/íŒ5»ŽH\ƒ®Œ³ÓP\…ä/ö²"m™áDÙíê7–£©2‚Ù­ö‘1…P^ñƒ$«ðïñRPcQrÎ.0…K±µèð‹‡—†O2tª°ßw<‘ÅËYyòÌ௰&(OºɆUgS÷É'í8ï¶Üš=ÌI8Ò–~n®Ÿ3Êa÷f , ÆJËRtü÷·’†y$0³Q¾1‹K;Q$í© öÁH‰@7)oB]Qô††²2¶?]¥Ì ,2:R­Gæþ8‰N;ÔôæVˆ½å(4nV󔾬§´ žÂ‹ß[\ÂpšgWÖh![’hyxºötÏ~ËãK˜cŽA¬öcæ²@óÌ~q,܇Q 1P{šd8m]£ª>~ªÐý¾eÖxJÏQu88ý¼ávOcvìßTyŒ³«\ý…œŠ nÖHÿe ©õŽKP5  dÖ—V¦•ô¤šuÇë%£àÈ4`TǬfºk”AOTÞųn4"!±éœŠEEN€Û¼b×´-}ùÀbdŸ,šè j|ù”á‰Ñ‚Øç+Ä…Ò»Ö·u«/Õí¨¬!z2F=]}š ôAèÅKŨþY=sJù'^Ô ³Ü£u8>ÃdÓ9ÝMÕ¦ôù/hÒí"ßžl³œ%/¶íÊ ôS/¸1h íq±UÚ)DV×f÷-¬µ †½©Eþ€t4åðhßg ¿áò‡ëŒá™óœ¦ Vb-{”†¼ŸÛÊÝÂ佲юˣ NóœïÁëŠt&‘ÒãÚÎ…ÈôXi„eŸäÈJ¹+ù´vŠ38ŠüÄ„ÊIP)¦L3$KH½ ØB¥\•gòîè2ûM¼ ku¡·l ëB¬¡;Œ+Áp¬‡¼iøXY x÷1*¥©„‘Íþ€&háóù…¿€È/÷,èi§éCóë´Jà7®ã­ ðƒŒ•5sîefôvÕ4Š(׳ÂMöúÃlqŽöçø‚ÞuqÁSB$Ò|pÆÑ»Ç…ÇAòÌ(Ä ËØ»Öù˜­ÀÝÞs®ll:(]&Ãý¦øgÅFöjsƒên•I¶Ï~ì ­i#ŒLÕ@‰Ç­¶~‚zõ–¢üO6„tMg6§zâËtb:½ÈBQ‡ KØ¢b«míÇ*xKÙa Ç$¾cT…yßAöÆ»x„ÑЉޜJiìÉ®®ADgØ£VóȺ?ºÈ, _?H¤Z¹kŠVg€êm#@4³þ®Õ [L5èºÁõ$ë,ÿ=¹û8A×>ìÒêtNˆ+®¶8BLoß×`tô `>Ÿ0Ì-¨Žöè17Ûy>Ù¤ŒÕô#cæÙ•Ò´}í„[…qУ Gù÷Ň/”+„fq‘éÚŒëé¶vêÃÂm8* åU/Ô./˜½¦kQ™J<—Å8Ò6¸o| w¦Ï*9ƒË\ušô總T8É(8ËÝudç 'PïÂî¼fï“ #m‡¥ô¹‚¯éèbǶ¦aÜ[dã;º¿ W˜Ö}kƒ/_„a4U¹…ó`¹©Œ·Xu‘Óƒ¾x‹}È^Õ4QvÍÖ Ð“b}ù׃gÍ^MÜã $–h~hjÎqπǠãùËyøXàß8q¿fוR[ˆ3@‹ËY–Uî*霳¡Ëã2CøÙÎÛµ0Ýo˜¢Q‘ýè+»±“Sƒ_[SC| ÿOV@L÷¿Œ¤.ýˆLü[³GàÐüÈ6þy^µ¢*à÷%a†¯™{>¿RÒ# Çê· ¼ºã¯ËH-.ð0„b¨¯ê;bë;¨f|KŸI'°N¡­ÛI~ÜÃj&ïH­³WâáEàWn„/z@Á¶’° ñˆðŸçDߣöÜV…›ü¤ šˆYÞ¤ ÖT¨^Æ^uÎECK*”_Âõ!ïR´÷Ç$aÏ#¸ae»oÁ#ko:G&õ -M!p?·ŒxÔ-@Їƒ¾q|çžGó‡‘J:ª1¥ù„$U åì¨" žóö·Ó[i4F“—¹;scøžþ¯¡Ï¥~|ã?¼í\ *ÕÖíéflØÇÊ \Gj¥ó½ûf]9‡r­Ô¸:-b²9,¶÷8n,‚F™®MhÍC°ÖiÇIì1Òór¨?83vd‹8ŒMÑ4ËÀñ¢z¬ Oõ—dhS ×]S5Œ„ìoÒHß"vôNO÷Ë*rHÎNRÛš[”èàé?«‹sœ ;mÕñc·*x}‡Ö,ÃõºógO ¹;˜¿fé{™÷¸’ A¤ýù¶Û¦Ç+^ü†úâG>ý š/GÇ jøªÑ¸sƒõÜÞÁs¢êÀá­\ÛL2¾·ÜKïÉ‹X¯_ú½'ŒÈt¨ýwTú¨*âøÅ°9Ð$°]*ñÇ~UtTë ÷…:OϱËÌŽé¨ÿlþˆ^ü²³¦n¬â>ÙÂΛΓtßÅ?b”µQ:~)F!a`14u)êëé{úΕÃ!Ï…ëë'/&¤¬Ç^[ãšf“ÚØIãªÐÊ„`ç÷!ÀSÙ0lÁÕjÆ–Îò½K&{Õ§JT¯Ô·þnNV6úð/«\fuÒÙD ÊiNÒcC{,¬J{2þ‡Xb¦žgÀBò\…-·#^ö©v»k „vi­Cü­Ý°ˆV¹Í¬O‰‹ãEýßÏ~ÓÑTu¥Ïv{aWrÿPãÔ|…§ô¶—˜ÂГyi>K¬àøÉÑp2žŸ|…ÉÁƒZDòã¹uoœ-ÚtígàýK.­í!ÿ4lâºÐC"ü÷½ òA&Mæ¶?6ÚcÖ-½¤zõ3”I½Û|4íÖʃwü o&»3®sºàìX;ö7=ÕèYÖq:tKG]Q*Óñ}éô¨ZízžñǼþbùhb fžþÖº6B1a>¸ ­"É3·è6ÖO¿FƶºdÈP8½Â(ù¡¨yºÐÑa±‰ :Éý³ ­¯^ö`„Oò,bÇA ¸úƱçXwhét]~ûË2¢þa…Æ)±é)ï-h…TÆ7Åõ-Ÿ!hE€¬+Ê=é0ÓYA£nÅraÌáø(J¤Z)l¤­MV'áöeçzqÙzŠŒŸš Fdn¥Ò¹ãxWâ߸3ß_e?•/'wú÷]sènÍ ÛŸ+ˆ,v1”^ÍYÂ'þ£þá5÷³¦Yîæ†×±³ƒ``ð Äð]Ï}ùcÔ• Ñ¡÷cN•Â×× @îÝç# ÑD Ø:ÒÚ[Àª.ê#®æÄ¥qü«­×Ô¤ñK.MÌšVkÓ,æ•bø±p:o ·Õ‘,…Тò2à64_š„Áqcžrñ~Rp}ÙWf7®:¸Ãõ/=KGÎVÂÞ䥒±-ÅqiõÍdo´~GMœß –XùÍòbék­KQ"&ð_;èîlÝNÁ£’+:L²_Oä&ô¨%ð^pPZI¿w·ú ^ãÌX„ŸÏ*þy„0‘7­›b~ʺÖwû\R°™:¾¡ŒÕÛþ½¹ÊÝ¡ðQ,XåƒÌSóZ73w¹’㤤÷BôQèý>Shʩژw±ø9ðBëÖÛWžÀð6Ù–´ëÉw ¼:s£×3 à'°ýá.Ù™¶ã3½;ïFÚå`dØî+¸ä‹6š嵐@s;*aB®Ç7B$ès,¢€,€–~5U@8òO°»ÙVGÉj"Ò«§ØÊÞ[zmw\LÊ\Ö £ýL B»H„¹€ãÞ‹¶!ƈªDI¡Q§8 +XW÷º•‹ "LÏ_Áǘ—‡ À<;°ÈÕf“ËÒGµbO¢jï#¤‚‘fp)è œ¯“½lâÑ"ô¼kl\—NU3_\Oð·YåÌÖd3Õù\’%~z‰øõˆî!Z‰éFkÌÛ@P¥-¨ ¤*ÃÒf£ß»]=ÄžO<Òð¦sw[g{R¶a!;ö‚y ÍÞAfÑx¿‡mÿLÄ8Þ(AßcH’ù V˜z¯TÒËÜl튥PL3!Z„‹AThº®þÒ ¨ª˜…¡¡F½a>ú ’vŒq¿@ì»652cwxa„î¢Lif­Ÿ¥“ÎÉÀ˜Í5•„”¥c£7çÛ•3ÌH(ârï¿¿ó/ÝÎMÿ :ŒƼÎÒ.°"gƒô¸X&,>í!›Ç“MPÜCM©0X¦6Áà©>/CYÿ:ò½y{M€MÈæT8bˆÖkçí¦€¿½Ã´,¼°ÞÊäÇÔ5Ö‚dÉ݃|‚%#›ª_öžó–è 8mÇÍÇtÔØôPß,_¿Þ € >Í8úÎÄ–W#=Q°4Pœ¾R>´À,9᣷ʌ©¨Sɵ»ÝŒ×YèûaÐÎ§× Æ‘&ÿ2Ù«-Z&½Pò Us¤x]‚ßè¢ð:ùSLŽ„%0ay¹K™W¬Œ•ál‡y£:uaÇBegTü)å“áa„ˆ<ÖÝkÅ‚k^R¬Ág<Ö4'´]`x(±  .zæe$ƒ‰"§”"¬¥ÔŒ7Ü7ªòÌ ¬&ÑL®ÔÛ»ÞKú¦y«ÄT€C¾dFuì‘l ×mëvˆ€F‚×ÔÈ«ŽšFš@A‘êsúmÛÏzôlž±Ç$$ð» ¿í´€òÂ{½$®çyíÇ+EŸ“ ´7•Yt·‘[bðòÐv÷ùF×@¡Xþ— Ôr Ša“GÑA-w<¡ ðªÀAê:z†÷ƒ„n‚"ˆr¥ …4æs*ÒØ5v¢þºm®@;»Šý¶ôGbcèæ"Óxé &«‰`@š!ü‹ù*Á…Œ˜Ñ¢UQ¥Q'i(¥¼Yðà=‚}lbÆ<º»(7#u"ç‡<\dAsЩcþÿSÈGĬ’Z½Ûƒ>rÈþ`}ç½D(é…'Àðøùºàò ;y!òªÅ4ëÔD‚'Å\£Ù‰™YÏLÊÖ|z <ðòÎÔÛhr$Ieÿ”â­……±¾z&ƒÃãvø»pS¡3bòzð䋾À_á¦vs¹–%ÔËiÙpG·›¸fÜbPeÖS®ºp/ã>cþ Ù<ìú‰ĩ߫º¥î®?:PÚ„J»‹²ˆKM™“¨­iWRæ¡!âZP°1+¿ãBWŽÔé³î†××Û&±inm-Ó2uææ6ã!”€J¿»¨Á»æ5Vw²Ë÷<=§ÈXfHr Þ U#J™/’·,êGMüéHÁKèBQÔWÚMàvr§SºWÙïº%M§”{Úº"QÖOîªE)`À•Çqò–ˆý9Îß—B«áan€ý£¯½ê¬Eä3ÔKо6f·æa£hˆ:mK*ûÀ›Å…°SÌú/s±‰§Ä¦-~º µ1«¿M«¼‹|48ݨDÄ•WÀU—|þªþ*¶ˆnà#Ÿ¹RrÒà4Q6ÐH`TôPxQZ.Ô€‡íúªŸ":æÕÛ uÓ!RŒÒ{3‰ïM!›/6³u'Q9¼ÊŠÍvݲç¬@ÿ4 ™p¿\&æ[<úbÚ…$ãO‚ˆ ŽåØÁîfƒÛº Æhö²1¾á. }%À<~•áyÅ?ÏÛÃ>ÝÒ–0ÒP¸ŒüÁõÈÂ1JnÅ…Úú…1áR7†د÷@ˆ‡Â3 mGnꃣ-4Ý2ñóÓV,Á=>Þ©òêjÔõÿaým;¢˜§ÒïïmŸ«#»ú&®ò™6è»·±ƒ-Žè;íËq²Ç”©X”îdh·1ßÿXÙú3à *rÍ´?•Ó¶©oT¢gE²·UÊK `®2²!`‰gYUýÐ×F¼˜ý®´fº|ôbUÍ ³ãmº(G:A5¹¬.;Òp^F)3':@'Ên7·©a³¤Cd똪5:ÝÌõ…–|ënË€²–ðZOdåðr༱œÂÞ—™OÜ@çÍÇüL+òN Ù^R¨[xr’‡Ó ô„Yðð*v¡i{¸ÿZâëÎ|"›¼lñ–/u %,Ý‘¡±ô9¹Œ¦2uÛ>k ã;"вÝot"!9j’Ì!½v' }Ä0gŠ9y–ÜB‰`H½²œk]NþXSÃ5{™™î8SO ª%3J†šþ¿«yNkìŒS¿åšQÁVH]À“2,`,Ü]¨‡<šÕhEYndÙ€Ë~—½þëdÞ€d0‘¸s£x›d¤æ,€#p‰Ç/6dœ”i¸ä±°äT îs?Kp½NÌËŽ(ïˆ 2¼@Kr†àWµ.ŽÿTÉB4(̦Sý‹®°Ï¶g`а$ûHtñìÞŒ¿HÙ:ž¤ÚY®ÂÁP O¡¼Þç)Ž%i2s)tñ6"‡Ý£)>š'ÿ,Ÿ1‚·†®´}ßV@ì*‹”QP:ǹ'ÔñþG ½™öXÕíËÐ;^”ÐÞO6š¡¹^á.ÞÙÌýªWÁh@…Ô¬W‰¤ü Ršþå“"8LPbõ°cðÔ¥óZiZ=‹²I›JÜGjnüž]½v2i@Lnë¨=1;·Èž{@t[–ä/¦e¡x* ðÎ*5ž}[ƽê1ObõìƒÅÿ!ÜÁÈ3¥)žqÞ'à-/?&±¢Ì)àëW—²SlñŸæ>¤Òä¤ÜÕ ´K$QádG}†+q]ÇZùâßLÈ8*gO°qC®TSˆVÓb àø^’~ðÓï |ÝŠcÿ:q­íýô-\„ñ7†dý”…ç–zã)º‹@AH+V‹Ò™~›bÜâó°J0Êv&›Iq"Rù¿ßžbŠ‚.Å×wÏ _įgÉ^ªÚ1ÿ`ÓQÃÛÍÿ>éæÞ¯žR˜—¥\n¼ËÇ*,¢ µª-sø™âcÇ Ûú ÆÌèGPoi£¶Ï K‚Ð^Û.cD'P¿£Þp«SÄ”’ˆ`s1@.ÊŠÄ¥,KÕ8Jú|ÿQÐq[îõ@e ¸àW Ë­x—QPð)•aÇŠâi-qOZ@ï'"×3µyÚY2•ôÎ ûSÒDQb"4n@û„gÄá¼ñO¹:­üc‹ iä!z›DʃëÄÊŽ kgs•D¸Å‚DÓ(–ùþ‘¸_+d1vGZ­rnP!<ž.*T²| O6iÐÁÝ.Ȇ#§N.ØÖ²hJÓéP¬¦÷wÍÖ/nëÒ–æÓr~UðFüº[àT‰x¬^a±¾œIwoyÈKÝM©ÏóÝÿj+=‰…þ-ä=Ä“`Þ쨸NiORÌζuùˆþK?PlXmL•èûϺmþÂ7).Ç.ᄨ‘¶xûµú ÓºÐ1@é4·™sy݃•qþ?ã* Û}0´•j˜194ø'Ÿö´îÊ“p‹&nëQî •­yúh,tÑôVtª.iü-…a¸xNÆ’2šá¦E˜ƒ¶ F(&²màæ\6cý߬1þ>æÔλß;r«½Ü!ÔI1®e`Öðxà ÀwPþ 86u¡þìÈá*-kÚÓ^²Ýýãõ7¾Á™§äŽ¿:Ž"È‘ ûᯰQ ^(Ûž£CLA?ôåórOàuxl[»¿œºj ïþ’ˆ§îÔGŒFˆx @£(oç%w-0™i4ŸlÜP7¯±´«DùE ÚzJTqRLî-^ðÆKa÷/ÐóÄU¶&3ÿ|mß<)£#Ñd°Ç5$]ÙF^HµÂéÀÁÇÉ•ØáÂfÚp×<çrZ³¥BO·VDu¦Oçî}-±]£iT|týŸït+íp:rºMêFòØ­Œà¹!ÅÄäž`Š ÙÓ0µ{öÜ·La¢á*u9ýÕ¦^ˆJ¶íÐGM6ý,äK<-PÒPYý–/5Rïïe¦Š¼Z‘¯ÜÔÍò4áÃ/½îèðéÓ‹µZ[Ê(æó.³)i¦ eí¬¥öçC B¸+´’+[*SH¤ÔFë>¡€¾Ú,„H;bÝhV`9//Ækxu/JãÞR'zaë¹RLN&«?Ñ” Joû+ê¶`~ô¢œ• |Sê;\o'‘M(—<¼|ÔBÀžð€!ÛnK`žDZôÝ<´Ô>;Èj/SÝ”9¿Õ:ç]'“÷,Š;[~."ë¡IV®~u¢I> Ù©z¬–_w/XO\´ž“òTl a˲0‡r%6ùíŽÝžáw›JMö"\˜®ÕZù06Ôú®œî8ÿ;´ÂÙÍC±Jõb^i}ð_\PåäXöV3%¥ö+r©!ª–øÈ'šì·™^#¢4âׂ^ï4ø«¬-^ýàÕF“"Õ¸Žú«[s”Ï¢v®ÔìÏÇ ,§±ºÔõÇེÅRÓòt¬fͨ Xš>æwÏõ‡ Þò¾TŠ®D¿MηÙÀ—1s4òivì×ELP ]Ò¬í¯æEÓy–ÆQ¸Ë1]î>{•_5*M@3ËñuS"8Læzü¾ÏúžBP‘.@2S ^…¥­¶Ë`Wf0ó'aÈ[ä±&¸‰G±¶M=ÐFEϦ‹Ìù¼«…_@ÓQcQXIeJâõà^-k5v‹0€ñUSc©|:]­½¢‘•pÄð~¼{69áòÿzíFfâT>üÍr´\R¹Jrn7&ú–™6³Æã%BmFèe4¾ÉŒ›“Gxh ÑW¤h(ïH ¿Ü¸c®µAåÜEhO¨ã’>ßÚ‰1|ŒC†<Ç6›¡:²¾ì^u…ø×j8õxs|WℤŸŽ'ìŽÓcåÜœQ:žc=Ð?“YïäHv]¡~Ûu!÷€ª#tàŠCYý]ÔôèÖ¼¼5«RÑu×e‚…çŒðŸ±ô‹§as‚ÆGÌÛb®ÞLfÈHÐY$Wlˆ¥²;uÓþ…Á}uÊm·EÓÄÛF鋦O•x"Ö…‰†:.©uÿRc8ª——— m©`ûôó`ßÏÙo…Ó d·Ò+oÑBdΩ—aÈ£P)G`­À€É{¬ .DeB/{æëK{ñlÕÝ•Az‡ïENËs#Ãò`æ‘qï—Bà7˜á‚TÔ'*ŒC"xZ˜lplYÛh† ÒrÜÉšpí#pmÞ­öÀ~Efd…W' (û¸D¨UœÉ¬¹>4mÒˆëÏŸq0ºÇH ±`JâµPDØÔ‹ì§“Y)žúˆéQ-:ÏVÌüF¢ã‹Uj“HOêí«}ÔxÊ>¾¯³íKÃwÄžþ˜g(_Ð>aŒë³9=¯7„Õì›Pñ”u¿z©W3~_3«¨]ÞS4Š"èÎ;‹£Á6«."©ašô’=‚ÎŒ4¸Lõ¯ä„>Àž/É WÌÔN—ˆñÅ[]gþfŒQ4¥~âLA‰ÊMq¥6rîy“¨w}$RæEÚ¨°%þ’ƒO¨Ê÷k‰àÈLˆrxãUÁ å¾V]r$ç)º•`?|mŠ1ÝP•Ô’ÇQNÈ ´ƒLç–ÁT×"ÊÏr€¯ž×†}¤jƒ(ëé)2­ ͧÅ,Ür¤9¼‡Þj”«‹íêŠtí¥ ñm”[¸@;?:ÆC£_[^%Z"y”ü¤•c‘–€;jø‡~Ž‹ãôë%"LÿJÜ ú_Vø¤ÜxÖÞÙS–OUÔŒˆÔÇ뉨‚ dõKS'ô|ô«:>`ûøË2{樿.RÞí–C’„«À›’úã©xI]þˆž4Ц®²U†¾ÎâÖiîaÑ î²ØÙˆ-ᅢء<èâî+Ë© 9\º†VOæÄfö±¥MgDÇ'|>ÝM:ê…ÙnÓš4mXðüA5tá•TGÙPã½fKÚGû6—µiêÔŸ©Á¢­ÓÙî'Cˆxž FìÓ,E,Ì`¯_ôOb;Zó<’{ ™6‘LO‰5–‘µŽ0ÉÝð»ëUM ”Tº“?Í…~F¯¡{ß' ïŸRöÒG±Ru£­=™!`YtÎk"ŽÊ¥TL´Ø»;›í7¶Å¨ÊvÖó¡î4xD,¸B<żë#Ê2æ°@D¾7ìå“ &7¾árcåýÜÌ Rˆ«Åé†Ó"‘²7Gp ¼Z†Ë:8ŒÓŸŽd†ÊÀy¡§Í$ÂtC¶@#6ä'â·c+?*lú¡ŸÕù%؃ÃɆñ× šÃcó‰ÑÈùâXìÈa7ØäY°‘¾w;ÉAì#±’~ºäuáÊ,}2‰Bx÷ M.]fÑ¿…R{܆Ã,_~¼%)øäOn}! ~°è1L˜, ¥ùy&Uñ¦^ RŽl¸ :Ô1ñÜAÄ@Ip'px©8d:ŽÛö0Õèm© ½u~ââär$GoScK6ìVÆý;Ó‡¥£\ŸRÇ 2àÑñ´ˆ$íp¾mϳŒùªÐR_¿€Êi]iìe€ hðÿ1ï h}Æ¿rõ1·„º9S§'O' w@@«Õ!kì:Šï^q{lV•ê¼…8_›É»~5ÆõßßÇš®#ÒkCÄ‚FŒ·ûúÞL”éæ§sûz;/×qä›´cu»›<¹þuƒòDÝ?ä<ƒ}„û»ñÉ—92û¨âÄ•̤Ðßás~¥©[\jÔnå.ro3æYûרIoÚÌÆÿZu½ÀõGz!©¢c«â­½4fcR–åËsùv™°FNCΉÜà‹—,a?’r?"¿õ„RŒ»çÈf9QF¥¤%ª_˜ÿ­kùäÑ/¾ö6\8]̆ïV`d@áUÏŽGä!ýbªzsu–ßâ‚åÆ)‡—»œ/ˆOÞ–Õð®ÅÆ6q-Õ5ªL*£'¿Çña¹$hì`˜Ð;]ÑšÿrÂËk @^k雉‚‘î¹9xƒ¼TmISuÒ³±y9xÙPðÄ«ˆ€„ø‡?[ëpmúA#§½SÓ˜¶iÙè¥oyr¶3þÐb`>â(®‡DŒ%n¤ÂNºUôœ„Ón„%ˆì¾iô|¢9ÕJ»ÕFø7Oô+`¢Øg›[.é״݈µˆ|¸ŽvÊs'AÑ;Õsà<«ð-¾ÇQ«¯ ؼ¢ÎÇ÷ƒÒÎÆí ¶ˆ!zh‡U¶,6”qàs]¢Yâo{WM¼"1Të-'ëÅþÅ¥½$#c†[·®ròއ:qõ$:ÈØsAcÝA'OvDe%`zÕø¦æÁîöàYa.@w}ɸže­Ym'3'i-|[‘¾Dɳé;Ì®µiòKO…§p ~Ì;Q¶ìHAqÆN™Â·/Gœ¢Br_ùšPœÅŒ ›÷\”î†>"|»É6ùqÎ#¡O{!3ò“jä…#=T‹HwÂIš*ã‹RLŒÀ;+C&>…v`CcQ»6?ðï¢ö8y¦|•í~UgŠëskUß2ý¢BžMޏ4 g¥èºe³è2ãIjù›‡¢l9ˆÞ4²°øÛ¾ žD]A•r›‹këðØ4¶ûõlÛlyË!e@w?Í£>çeåµ:ûT­&~XBäÕé}à£}ÎSi Äà…$a^¶p}›_ò‚{óg1ÒX±ŠJ9Ú:‰¹žsoH)v0‡?HM$˜aÅ/Qó o@á[#‚ˆ:ž†J¬&tŸæO,3•ÙUé C“v?`ÙáúòÎk““6Gh*$çë„ËAªÒ×IÞðÉÎx‚_ûó³>Qøg~‡ZªÙ|¡«s•ÄÃ5­É&ɯ›úgT%Ê•ôWR‡‘q*ÄWÁµ‚î4¹‘ûÐÉŒ=?¯ÉbÁL™ñKýòØE*I„¢‘…?(ÿ=…÷ÿm}„ô[0Æžž­QÒ(­h”HYñl58:ò›n×µ;!F»•öèf¾Ã•o§Mi¡ ’»ì᫽&xÊæõyÑå¹’Cí°†CªþG ëúú¸#€n_uzœ|{´Ÿ:âh$×úâû8•Q'ò [Ð[c (F L—v{ÖR(ùÛKð.LÜV—%¯Ë ¤HP¬FÜɸÊ]Còj½Ž‰Ê]œ7^%Nv¡vvi2˜q ’‡(µíÄ=zfÃ@•Nä pFv$Ai¼ªÝõ,nÍ{E…#÷·Eôtn ;q¡gàæA›ÃNåÀ;A•@¦ã[Öd0ˆu‹‰ªÝ¤5³^s ~Ô¸¢½9Q`Ƹ´¶Ou% >+> _w8ª”ÍÁ]ß:Ï;Ñ2s&!ë°žf0•r,‚\mnÌã]9ûtNa°{RWÔN…C¡lGÅhȤU´Ê6Ñ4hbÆ/Ïì¶Ö£è‹M·‚ö5H\Oߍ_TìÅChåz Aº? ϲ³ÿ~/LsöÅ;‹ómlw ÆÓ¼áYâT—‡ÌËL¢4ÒΫj7VÛ“ëFÃAº?!!¤oÑ4ƒ!²z~ØJ†¦tzõÚg~H€ÃÊf^ =‚Èi^T°ÿ)I)hˆïw™¶Ã¤¸~þÓ{½[}Ùå¸ô ÈԌҔñLÊ»ï^€†;UïÀ‡®Ø:u•¤H3z^‡!7ó$~'Õ¹²sÊMñmYËò­“õùß•³t+>¼—}îZtψ·µ=VœþËT:`YóÎXôÇ–~gÎØC¤{¾í¡òƒ^lj%ºÆüËU¹¬(GlJ$@?5‹ñÒ2±óB²H—×KøÒ¸ ‘´AзRT.Š•”îtúˆ¸ÁàûJݽªzãe¼ê[é4„îá ü{Û¤mµ$ÖDÎE&ÓêôN2+jÑÿyø6q— ž¸ŽfWK±qZšatz` Å}ÔÌ`m »€îúl‰0ÿ,·$8â OiÅš¬6Ê'‡î³ÛQ£¸ D]ÜŽhU®‹B’±UpüTÏý@}µz÷‹%Åp¬¨ò}£ã@"øœÏhÓ³)#å(ãêä&¹|e‘Iä>¥Z;üOiï“¶NáÄ•‰R}¬¨A•u¦^E î õ°Î%Kðøk®oV¿eNÑû‰šg•¡ßá<¸]¹è£é´³îé^AHT†NŸq} T`9Ljb¦M{¹õÎò;0-šÞ™ã‡4e’h3"8ÆÄ[ÀžË‚4cãåãÙOÑ4ò+þ]ŒFbPpˆ"`P#õXXÙ…ó6 ¥¾øxt½~ð 4!š ÚDVÆùq&Ùä€uÏ´A‘ÛÈ=oê±8'-¿….CïSzLs¾Y!Ëß{vª€sw²1 9ô”KZÂÖ™ü3W‘åÙì´N¤äJÌ™N?. ÕzÝ?Æû!x[Eá ø —d²°ûñýÅ v’SD`‡ÆÒpØH¨h§×R” aÖÚ«zýYzÑz‡ÆÝ\9¹õ&l‘±^oŠá¹Ö“¾`:o8Z^É4 VÄñ³q¢'~‹¿Ë«tT]ªx‘ÔϨÜŽr6£ ‹:óoàœojhB0hâÍó“  ŠBªªðhû\ˆsvƒç‘g8V}iᮽ­?ÉsfÕ¹·­•Ó’#jÈÙÕæóÜ3†jSuî KØ£ŒÇ-Ža4hXIOml<'ÖOJäšäˆ²©Æ|ÉM¢¸  81€@úÎ?¥JO5E„^sÀR‹{&—Ðü´Ž÷ ÃÇí?o¯ˆñ=ÃPA» `p4’*7ØØÒFIqf‡Åÿs¢7­eœ °é6þÌò‰•ç0Ú ôMe):Õ; Éïú`l)¹llûe‹a4ƒä@ƒ]{¿0ûkØ÷ŸÒÔÕÌ»ÅÆÍþ6/±RԳƃz ±ÎznX¢ é>qv†RC!®^Pa`Ðâ¹àúa•bÎ^Æbm°3G|ðYŠ>Ù€i„½£˜íDÓmøá4UÝ[¸Ù LN‰KßB†ÎÖÜ3* ±;=›—|\þþ6W2V…̱“ƒìà¬é¼Ç|ºÈôŽÝ(dÕ&oÌú d0. ®=ƒúBD­ W}G[˜ít¼Ìƒá¥ùľrOæpMÏÓU›­†´ZÅo¢­Øìd«ÉÄ1Æ`ÓùP6_Íœ¨jÏ/ЛËðØwŸ2šž…•)ƒ íZ$ºß‡Ocô|`KTºt»ü(Ó35*ÿòÏËÁ¡çúåej|»6uªP\¬úL±S>ò`°#n=?˜[©0ð—J8l¬W,íëØïÆÃGv«/·›*—ÀHE$`Ž“.¡g`<¼$vŦöƒNñÔG XÖ§ýbmÞ—¸Ô¸Û‘™Wð0 mh œƒýÿaX¯˜ Õ%›7ˆÚ ˆxÜ4NÄm}§ « ~ñ• ãYÉ6òî5Ü |àˆù¾ÂûÔ¦ªø¬ÿ²fZ B!¼¶ÿil©bŽÝžaÖéOÇóWÌÈb¿å(¼CÌË.D¥~ºxI éÆ?·Ïëâ´Okå ß1–IÚtJ?™(|Áª-×Üs[{€Uxwü`Ê«#ú¾+¿€Ðþÿy;åÿ(¢«ï¥ö}yÿ!¿ðÕ‡_8¹òv5üü(H; nÌÚv1³ö }°”Fb¨?}"Ö¦| ø ÍcJ@vkd0»]{73Û»µ-k°Z*mÕ◆݂€›™í"„xvUîS50.¿L©»Æ›ð‰†ïÑßÝË^ÊG0~¨òWœÜì°ç„iÓb6÷¿ZZ‰Ÿð‘…PBà€•0¥Þ«b„of=y¾›Çõ¦ÑcÕAcÜÙÎ×dƉŒcþGqWЛqüL/£\쮎a²Ò„×d\¸:-·>k]K+¿ù¥m¥>ö²É NǯECWxZd«Â¢ÊkƒÄÖfH1©Ê7Ž»(4ÖâG±†··BõïJ¢‡^ °$j¯Ä¾=ÔCÑ7ÆÙ¿Q_…pV±…":Ö…$ë­—›{*E¼Óœü[ÇHnÍÖSä[dßÿ/›¢{ž‘ú%‘|FóI)åÎÇ)Ú«Fiõ£Ò4Ù˜Ø-!c¨¹)_høøŸ2ÅŠfê=²”Οð{u¾Hˆ0Ľ_/Œhá’ýÜ…âøaÖ#`ñ:H0<)âvk, -ólk8A½&c»=74é)`̶—I!é|k³?F¨´?õc׫{‡™³ÃPè[8Ó.º BnդᡜÅ!).€äÞ-Y£uؼ Uáq!P0›Ö§X¾6>x³†b”½èâêïTj|g1gx¡Â—ooüœÃõ‘ ®ZÃÑÃoXìÃn•ä2÷RŠíbÎkœI³PŸËÞGy§N63\@©ê–'y·G¸è:÷"\¤§˜&÷’iÞg¯aÂV;þR€Ñx‰ àbz$  #}ÅÂf$ÌÖâ aÔfÛïœS%"Ò{Ÿ!BŒ+£Œ&1™1²¨E˜ØÕ È™ûNéÜI° Éa;úÁ“à³3Õµ‹xÃ+ÔÛÅÏÝÄŽÝÌe»2š){:hKî½±µ†‡ïH–<½ñíå‰Ð½µŸH ,"-uºâÒA|ýuF?r¨Q+ý*h2‡Ìaoë¦í„ÿ;Q¨œ·jŽgvQ°Ó0Wïƒx Ÿ:Ì¥²9YUqˆeyhÚ _ÀDÆè]7hâø%>vQöq`cºÄð«/rÜ ¬›8uÖûB3€1ÅÈÀrûV׬¸½ñãèø;óÁ| Þy|Iäì‡)ùtØïÄJÔFªsÉÜá}c0Òl芺Óyxæ,˜ÑÒ“›lf_Pqå‰h*>ÊlÓÏÝzîÃ"'í¤í´TbˆXÇ®œ·9ƒÉÙßâçâ±I”'öÉòÏŽ3‰bjæ&ônzþ²˜ý:J$‚¸@&Ǩþtô¯-s¯î~ÄO^4Šr©¤Z›…iõÆ®M $Å aöT&wùå `Úºw“à;ýö_bþ“ú‹mäRŽ¡”îË~„…x šÏO¢Ã —¡Ð½ˆr~I$Ž×k’Jî÷÷ÿ ªÆ3 õV’즸`áQJy³5Û4Àr ¤Øÿ\@å×FÓ¢›QJ^Æâ()>ßiü¦ ¯Ç´ÁM[ß([õÚ™µ£ÎÍçúƤ ÿaá \åJԱݵ8€Â”7¼XàÈÍ¿”梃OÅ—v<'#Z»"´þõ€Î)ŽŽš¼7îôø éhžI Ö@ äó@i=ÈN¥ÜîN TXQ©+Á]kÆ¡ÕonÇCLÝ´®¤èSʇ‹´ç&*¼ÙÅ6&›÷ÎÕ´ë¦ #~ÉO€þ,2ãˆÅXÎä»Lê^+mõ^”¾üÇá4é&TNØ]ômGïmuE;ó}X*—ëníÜ;ñŽ“ û¬»^¹Œ 7vù0`Å\Qùj£j*J5©™ ¶üé±»¦ @°œÂµ¨ó+ ×ä¡¡µ D[`Õ+¨g·{È ©u=‰¯ï‡ÂB𼑣ö˜½ÖvV‹„Þ"‚ÓûêÆ.,ŸE¨ÎûQ|`°1׊ëy™(™‰Þ»ˆÉŒ˜­9òT næçtK™%3a²õºªQ—”BFg5Þٚś.´êÄùî›ë$¡ŒŒÕŸhk²¦0k44ÝJ#‚ôÝo¹lcˆHêYû6*D dòT&a‚)"§š[·š#·ˆò~ª n¥LV‘ر)R2èKµuSq¦ÉŸ®Êüà쵞ŠZT² ó^Êî"fQ£]Aá{S.Mó(¨2lÖÈëÓ˜–Õ¯óÎ!Z>SZŽ÷œ×¦š …’ô·[Y ÿC$É¥ð°§wø8þŸÜ dgO>uíØQo=YíÚ⸡Ä_‘‡Ÿ‘½ɧš0xž¥Õl:¸š%íêÁ|ļ[¼—К®¼Ä˜ÛL)Qä=$ SHïÏ»Q™ŸÃ\¯égÐÙÔ½4ĶœÂ>$õˆ áý*‡¨]ÑÑ9ZýZá´Ã)Ò‘ÚÌAÈV9XÇMÆïˆ@å&b›gj¬&h q_Åäm¢[Z¼ã4QœÚ>¶ [ìÆ™‹Q·+È‘ßÙ5T^0ª•C´,”™î`¨àÞE=|Ô’dÜ0ˆ%jgv̸çaЂð‹6€[œ;¨1ƒ±QÅU¢N´:Ž•ÏE)M€ûœ©tׯ`Ä‹k7ȇuL¯ÆSâ~!ÅF™®—æïvÚtt|æ(€eŽ©°º……g^Ì”Ð˼Œ*—9Ü."ìŒ Xà¶*ó£)ö•sê0àÂ|pÊñÉwÚü*!ÈX‚EÇ`+p¥auWáƒAG1ÊÚó>Xj C¢«ø©C’¢âø¸ï¦75 $Ëg¶ú6Dxs¸}t“OòÉDŽª¦÷ÁI@Òb÷QVÞÁ¦uVÇ×À¯À®£9ʲìŒÀÐ¥N>®lŸ|aeFɈlˆG »Ëý™ò…6AÖDý kŸÀáÆ·!‹R­vEç{Èdú×2#w¢KŒÍ.arªü§Â†h; WPZÄáÚ¸(É ;Ìëè¿S·}NÕuþ­/Ÿ>æýºÃáVú·×¾­Ó‘7í±¯ªð¾­ý´ö‹õ)wÔ–ýZú´Þú¬?Û®ùÌÏŸ½|ùû¡}F¶·¾«¦|ûãöÓù(úùß¿Uÿ}Z|ú‡õiG!kêÞÞ|ý³Ÿ:¯Ûc~Ýýº'ûi`ç&ËëWLbÈá5~³…,ïti†k=½öäÈào~bâ6vØ4•úKVaÕ.%”©§¿oÈV\Qæ*lÁIË-kØ*¥âB°d'ºÖøÌG¬¹™™Þ’!ÆÀ&™R!•Ý|4³Q'Њ ÀèìÅi”yãàŽTö¼­bŒšIñYwɳ3’”o+¸WÓØö¯x(’ù;T¡zPF$¾ýÎHÆžÎ9‘óWh”9ì’§ X·¦€ÅL±=‹†o4²ª/Q¯ã¼Nl\›UÄS°Ë˜„ºã€‚þËË~†•Q»0 ï‰ø.rî­¤h`„7€CÛé±eH~qX÷¢±ÖP2ÞqºUŠ÷’Q™d¯¼äˆ{]—‡ßÄù>9Y¿ÀòÉ+1™ºnÁÊO¨ïL v%Œì“…ƒë¥-íï—P! Þ_Ý¡œ’¥ïÐ:kùKÔõe˜Æ].í²Ô_öRµ²¢iZ¸w ?µžøØË“‚®-äïèN¸¿y äéµâžåg™ÍÇR–çó´X=Ÿ IŸižÆèXS>S›¸%ñó*6µÙV;÷IÝJ–¦n¯vÉrs~¤t$4ü« OÜöÑê²qE¶ŽC“×;xÙ©Š(Àcº)Î… jûÞ:¾kÙ¥R—ÊŒlUÓs¨7[ž§U;Åü?gNá•ue•ß2ñ24ؽ=à ¯âå=þPA\58;°å‰ûµÝ'-â—‘©+ï’¬ª0:4,÷d S>ÆB•&%í&`n„ý’Z;ªíP Ö|nwÁk˜d6 Ðuî™D ô…-zv)5Œ}¤ðYFÇ঑ëÆW„u¾´N]ï D Nˆ³PßTnä{àbpÿAšÍΤ²XT …[›§ÃJÏÛ(ÊÊwæŠçY² 9Zi›¹ÚGTa¡']MÅH†L&;E¨²sa, ÷«ÿ~¡³»%Ž´­¹ÑlXC®T/$ \R¢^Gsi! •îŒJ}Á0‡$‘'¿%KÀHÐM¢Àa0á¨@Z>–p‚Ú­†›/ÚJMï#úbv::Ôú/ûñŸRfÈ[¢6¼§ÀV5¯”æ†#.Ó´©eaÑM |QulE{ BDù= |{© Ø 3ˆrƲ§ßí’–-ëŸÁá1ŒÐDÏœ¢ v‰Ž>Àå4JÐûðuËýL[›­Äe—ôyœ ùÍ?S‚¤¡Ú޽q=Bè%e\› qö]FK%ÄA' Rk°VH„¬Mc¥a»ŒS@|‘QĺgÌ|D Òù+H|òü2ï\%Ìžâ̪mªøHø­^¿;à@€ã K ÕÒ÷PrÀ8H>Ïw)”ûýzË=7S†\1w¬I»Å'ùňa=3àY…XKŒMy)סÇ@¦ ¢Ù£jõÞ@Ë Ÿf.èž(ï‡L±† WI¶[«/»cùø‡Ô[l˜8;±g¥ ,aU¸ÚٻŎAýàH©Ï  7&¤ßp¤÷&â­äÓ×V¼Ù½æ2Zg‹è;Š@$¢dÒ­s!WÌ@ T膾u0E\Gš‹)"|fŵó2)Ïlê-tôYÁ'4ú¹OoûÀŒÊ÷D voû)V w!µ¢EN!“ƒ'Æ)Ö5˜ËÖ,¥WŽ5.—á¼p(b0ð.¶Ê“Çøq"Ë3.‘Ðo©.ùQQêž²@rĶ` Ðp¶eª0ôê>‰¦–¤.úŒZÈû„sngc¹Ú‚‹KøëGK§(Ï‘d$_‹O¿=Öˆ²ð)O·)û~8-@g^Ÿ¹œ\v.` <.Ä£W¢®—Õpt£ŒéÅ_Ds6×ò€KjQ¥fL®ú¢òºÂøh}Î\f>ø}k@&Œ:uFö¤Þä|æ'Øã­ó­ñÛYOxˆbý¤ÑÉÏĽš¬‘&¿SºX¼#6Îl„ÍbIîQ\ú ¾G\‡V–gtPš©×Œ:'\õûrÖÊÐ{ÿ8š3†[mùW”û ÿ)^ûàQíO××çõ J%VÕŒ¦S†käM§æ¸ðø'§Â>ôï@=BÑpÀ¤ÆÔøHj*N)2Î:™Ø#ï'¥ÉšŠB>¤ÚVV W€rÑvÊZ¤.σ(œóÀšù¢M@«ÆÄ¡ò Á‡ùßN.¼,1õp§ƒßKh“ýw›¾àÏ…U.zTà63¦ £èƒÑ™¡cJ4gž´I,gùyw¦$²wÆðU}ËI¥} XŒ2Ü\îQäÞ}ÉüñK{<"FúÕÀ2ê†)q³)Ì,á†nöL(•p)TÓ%nÒpœºkO³û ý_Dwô~‚xþd½MÜl(Ò5­ß‘Ïù0gÅ Ög×aa]Óü· žB-î3Åî!`õY)kIG;Ró˜@Éã~4Rx/å¦U ÚÅÒ2¹Ê$ØÆh7ÑßIFï‚€ÆÁn_Ki%©‰ÒL<­VNN/ãl¿…ƒPq.ôi+ì(·:ÑŠŽ‡Ýú<: JìÚfÜꢊº¾·ˆgÓ’íJfUq幆¤­'òA¨ÒiZ…wBŽÆÌ´P.H^L_ì+âÞh¶cêÀKiß²OÄàM ›o5ƒÖæèýÉúl[å›°mËñ‡‰Óri°W’Ò.ÑOp-”µþ]u ãÅ‚·ž=ªöÁÖ•{ “qæ%»ÊBV'ü"IýÕ#ñ¸”jG|Ê‚Ÿñ)}Ž;) ^£ÜÖgoù£A‘ói§ŽR?ò/­ŒuXXäÌg{ƒŽ‘Fyå_µê–½X ¹;ÜéÖ :k,1‡Áç³u|”±Vß1;«§Mª×·øê*q14hg®k×'¨ Ën›ê7UMÏÃ×vä‘1NiBaÃ…ê°å˜j£‰/(‹Ðóµ;‰ …]Ë43‚tM¡Ý0»tÑÐ%õKá«B¾CoÇûæ¡ê¡sc=Š× ˆDÂt÷ù†+Û/Šn¶&ËhJÌAúÀIÑÒé’È~8"XæEV1wWF`Œ‡ H)¸¦…ø°öG­å°'æÂ²|-Àž± A~óáÀ0|iÙҾȮŠYj'´d"È¡&b‹úÚjΑ|ïÒ·’0×Ãåa,gæaýÔUQ¿/¹*A\…rçE{0šš¦ Æ°ó¨‰OŒ±ˆLá’E4Ö•°ðEiyDxHu/P™ï^¶£ˆJâo PÒÒ–Ãܪ þ YlAé~ ~·xm¥Õm ëÈ»ì§À:PͨNnEZ¨Nö•®6œ¯B¦þdHÌ%å îùÉÓìÊ]f'ÊL°öúµKvQï•YRd!ƃ†RÑÛ •e„v Ÿœê ¿ËÚ!¤xæó4>@©ŠÇ âü%+Êæ&¨¼ztøX쇬€MÇšLBFÖA0k —YJdk¡OÂŽp ]wüì©­ x‰“ÅígïIâ]µßqõœ÷={³¾äï•ÊÙS‰ÝÕpïv=9CàFÞz¾a0 ïÃÈÊ!²`Z&PìUçµ[ 6 ÷‡CóàÌ@lØŽâøÿN”·ÈºlRž;ëä–VàÜqÀíGë‘kä¹ð妟*œ­nm[;ò2Á_Í册ÈVŸòÅÿ[œÂ×õwý—ŽÕX‡çôÕ̾Šâ±ŒH »]¦¬÷`‚@¼"äå= ³\z’q´)M歹÷üв”v“BÓC”ñŠ Q6ßòQEPºÍÉœŠZä®Ì8@ëz¼‡m”ÆEãZE„{+f%þG,M«‹>öÈùYc`XÌ^ß¡ä4˜§±?“‰óËâÎY]éFМQ¢–͘h%A™¤‚¤¦Þóf>uÿ2Ýq÷ñ‡¸”¼\c e|¥ËÙ8—v ÛãÅjOÏ_¥r³Fjêy‡99kòÉQ–gæcšWêŽÔ‡~¼ \÷ŒF"e€j´:jÖ>z–+õ:˜£œ:)Øæ~a“¼ Cxjpæ:g/ˆ3ôÚ%ñ/½:1ÆÃb/ïW-ïÃq­øä‰gè­©ý9Ói–‘‹-ÅŒ ÄÀX¾Ú(cÜ+À·Ã  ÿ;ÖÓ‚ˆékÄC#ƒï¶ÎÈïR†k2CŒ?HF`ÃÖ‚zEëÖê³È±ÃÚ?AÃdó̬.Bä½ ©ô¯Ði†ܨMåPTžS¸ÅMdÄ&–œëûzJå|0v;¼Éˆšpx´/vê¢AC}fÞ(}&;o»)^e‹¶UBµ›‹{lѪ?y}ƒô?^e('Zïë${5ÀDõq|1 ÙÁ"† í†Ü í·˜\Ø®c{hpQû¶LŦš’MÜ3€§aÙ]wuÉxd*”±ýlÜ WPÔ¾û¹<aïòXµü@rúµ°„³žbŒ.ÇK nðô$>¡z [þ2ÏVã[÷{×UŸÂ.r~.‰YÂo‚Aú`Ö¥Cߨ—üóÍÁpL¬^D[F„±ðmJô¦w›6î4Å¡ñ|À[àèdì Zèqî÷”ŠÁI€û‰t`þ³ižu°XæsÏÉ™–Ö¼¡[2 XéŸhbYYOY¯ñÖ¯LÎÇVÆ=×5AŠj=¯?¿’QtkÕ­Š'%3xPÓbŽ€×¥œ+Ю´JÞ|ï·ÇêîGåhBk…[¼"²fŽÝ/–¶xú0T#Šb~aEÌÓNýÒ!ªþ¹~ó7ž+N/êÜ6j¤¨N#Zf·ç€B%»ÅŒPÌ3wn…¼JÑ%Ÿ‡ÙøQØ=š·ÏF/õêÁdð†Q1ËÎ÷Ü]W–%Ö¶*qÜÅ4M× ;M½¸Øp«xdQ‚vqq)n³l|$Q²ÿ{p´öÈT-ƒwÒØN^œ¼Ê'®øŽ žíìK)`빫Œƒ2sFñ·eÓÜ´¤ßо*üºæ2ßj€Î`Ùþ£p+´Û{ø M%ÙpO#ùù¼Ò.m.! WŠ6ø»¸¤†\›.9/#ˆ ʼnHÁ,*ÈÙ-úL©ÔB×ï.Ž+ [š”WÖe­^6Á¢ÃS°»dÀ\ž ŸË9¼è•tZ*Ø1T¶”̤.zš(öQY5È:J©‚Ðïû•ܢïN 0ƒpû ï•p6•¥T«æzý°XŽo$dîÔ=Týìg²^w–÷ß>áî¾;C1èÇÀ“£, ÃØôàÊÄò¡«×kot©%tS€¾›8¢6!ÖÿJ>Úm õ`‚ “‡`h’µI…>]ññÖL7ÇV¢YdžÁÛ(ÆÆ¼?b £òòÆòKGý×ÛB¾:/É|ä¨Ð 2…w»Ùë#0M©«Ö©qUéþ¡#*m§Y)E¥\mÕÓÌ8gFd`y –sCv^cô뺿qñ5ùù +t>,![®S‚Ëù¥®ˆ¼•«vÔòœzJÚ=xKÁ½Í:Ý…z¬SÊL Ú¨#T“:ðëþê2îIÎ<ÝErº¡ˆÍq?Ue$ûÅ4ô}AûŸK–ÕÒÍȆútØ‘ ¾sþÌÆKn½:Ì·MâÐk¾5å¼8c-`¦ÃOÑÐ%"kûF )ãªÙ»–q)çzš¼0÷á>Ôãßxé†*cÀÇ÷_áÚVý[´¶zm Ư&Üvû(,¿Líâ»ø×e­QÕ}Aýp”…Ï ŠïZˆ²|IíàŽ‰BªnuF+[Ù9k7\ƒËé‹ó_hQøÓ£¶âɾóÈêÏ:ÜóJ¾^¸$µ«Ð$^÷ZXx§gG‚¡ÙÜeäôöó7¸ÌgAí˜Ùõ!'%&]ó…Í.JÓËIUÚÆÌzpƒÑ|#C:pÿ$!l)ŒRŽŸ¹›Iký¬Ö[“›ôRœf½¯1òjd3“±Qh‹wãA zr®i-ªY8S„28f-,‘ªÊ‹¨[úWMÎ*ß™V Êv\%:æ¨rݰKðùfVÃË'B•¢Pøæ@ÃÓ‰|D’¶Ýd®ùkâºçViÚé:ñ¿Õy˜BF9³º#€f¥éÛÕ˜¾œ%íñ±™:y}ÞÝ…³ôZî§ôÔdÜwœÀNM€ô»§ ç%}bë«~ óÖ@ƒ}MM¨ýõŠà4 :=ú ýwnÏ(tÖb¶c ÄN€ÉDç ÿ ü]T“ñ{SÑZááŸ>¹mÅC{Mõ“œy¶ÄB¸ó¶) ÉAÉŸZäqe›^Ï5qRRLzmÀb³7S'JÈ¥Ã8Ôÿm½cö1¿Áy& áßh.ãß k2e_Z%ÂÓ±Zf¹Þ9ÄPVÝ¥¯&Nا¦úªþ©Hȼ”vœÝÚU\wûê~ÆÕ"BñB GÄ÷¥$ØÙÞðüä VO'âVçüë)t~r E‹9ï×´¸ Ê[K#òZ,ßY'\ªxoÌè-c‰Q6$)T,4/«y‡>%3üȘÜÈÍJžé%ÜŒH 9æ:*l´èz̼ICWy@‹?­ÿ[ÕÚÝSË )2€ë§öùÉÒ‚¬rðø1êI×Àïá/Õ}5—߈ëw…EvƒùÄ2ÃhÒ¼¹âX®OCe?{µ6ùð‡§5ŒÒ‡Áƒ±iTmâ°àÒˆÚ.Gøðr<.ôwãáQôÑ&Žd–=$‰¥Ê øÂôŽIÔÈ$iR,¤ý1úbçô ¼AFN7J2®NV¯_ùÛ OûE­…3.çÖÚ=ÆÒ¼©š¨\y¯Ç˜ÕΑSEâ¥rig»çK=<³šÁ†ÂúÜÆ5›±¢-ïì‰}ÀJù²dÞ^Àº LžÙÖ»¤·eôŠúÐ\n‰áGlé*òrþ¸•³w®\àu—ypÄ{:ØÅó­¹»ú¨qþ†…<tjfq×C½X‚ÿdÝ ×Iô¡‡9ÿgÙQÃÍd0„‡¦k™³oÂg»I÷%²q£RLüFšZ@ZãÄñIðù1î*Ábž½ª)Èàœ9Èn [%Y¤p*ãõÅ 3ìô=õCx‹W­¦²&_ÏÕœ•Tɳ¯ÿ8æËÌ‘»²Ðâë˜È™qcAè†HýnÒÖjÌÈW Ì7ÿ;[âºj6„³ÿa Ž”è9ßÅl³n—iQWöaáÄ0È3€&™jëc3ƒq|à"Ïf*³š¸¹5$ötø-ö0øò_2>Öv«¾,FìÍOõ£´]OÛœýå;x…nµg¦+Â+§0JJÓëôѶ¶”üDîÅB®.u¹yV8áŠÇÑÏ¡D¹1Èç ÖˆYgÓ@xq$y¿ $öMg?“eÿ(,H²ß/Òº nÚçg´“€R-³§9=Q‹ŒòÒ‡cÕ Z}ÒŒBQèÙ´‹MuM¢ŸæYÐa¶œŸÀ“ó9 YQ÷øªÿ/Ÿ¸a­¸Ò¤õe=3…–9°*ðây¦—ò+5Å#âÀ~ÉÔM…NUàî:jOøÁ¤äj«ëøT?äL,îŸïø‰;<Æ«7ÌÌ`™ëGG°ÂmKæ3ó$ˆO&ÆRq ÊÉS5Z_"Y{P·MÕ)á!ª¦/þóqÿ\õ;ÏÑ4*†f¶'ôCñ&Wi€~ÔRUîaŽý.sÅ*&› ø= qCNÐ~y<ê…¿ž7Ôtˆó‹¦f¾´®” Y hDäóö BçNMöº‚QÄb-Ë¿"9ñ˜C˜VÌ% ŸÅ¢²Ý§[%BÎ\×ࣶö…Ä¥q«¡ºÚ_b¶¹ûÄ–ñùèu¼€Ø DîÉ@ì=ŠfÉá2yÌ!šNF²ñl ÏÝ»ö8Q¢Â– ¯°?!”¯éàV¢É½å*rÃfz+Dß³ó&JßÉ‚Z¬O˜N6Sîe †Wªº%pˆ-Žg¹;ÛLwaѶoà°hu[±ä†ñÅÚPœë8#}`Fø%wìñhËV3¬_UpœFWû‰ÄÔvýüEÒý7"Â4iÒ³+e%2:rêÚÖFmsTGH€SÏÅU[ÀÖÁ©aÏËŽ„0fÉê¨YmÇ÷U^,{«Þb]ab7îaœk"‘^¼!~þèÎï_ û„3Zé\rj)Å™moXG"'S‹üe¶ˆG… œ ½~Rü•™ýr/§6‹\Æ‚ L[˜fïz±}Ø7Fù] ñWw£z_Ž1ÔìÍ_cì[ìNéœ#u+锆2øG}þd´å€Kœæ fÄ®oÁ‰"ßçó±l*e:°æ(ZÒu›´C¡—ÿ~Ýv|ìðGä&“ÄÃñ•}›Œ¤™Q»ù®ö r‘86VÐI!ç²í˜ÎÐ¥ãqqF3èE=.™¾ËýË(Æ™éÑŠéÙÝTt»*³ +×нçÈŸm(Z¡z‘òšQ=;¤öÊqÙATfqÜÄ;}<Ží^ÒÎõºtgÎâ+»ÒZŒaø'jÕÑpL!oM®§µöåZ±Æ=a`HD¹9Rþ­•µQÚçgt 16üL'°ÝhÜuüû„ì|¤{vZH ®a{e&PÏOpÈ«~¦2ÜBÏIÖ È€±ý(×AQôÍ\JD ×%ÿG~nñ&ÊíwDPÜm¸É„ã·q0Éݳ 9!QòP4¿Ðã! ·ÂŒš5KeÙ‹ÔÑxªð›ÙÁLþ6ÅW4#3'´öËÌ-´2Õ‘géÛùÔÖð8R§—Ò«¢2£ò<Ñžø>,¼îxAcÈyèkúYä¸Õo' k…»ŽåfŠž“dÑ·{=ãžÌ4 ‡O,ÌtÇ-ãßm†Ÿ°÷7â3{?Áu+Éw¼À ýao—‘œ“Ö)òBÓ: „2fgc‰±ÿ3ªåh’F}Ž‹úÖ=@ø%L‡»É·>s"WI¡‘Hîáî2"o¶Hˆ—Ë+3¶•Vd>/džÖL‚}4ü%v¡Œ¨Õr®¯¢ù>:èn›Ö©Ð™M]dÏd0•Õ¼#ñ¶ðÿ‚»¶õʘf‘±U÷ «U™!ëYÔqÈõ 6Wßô‚u ‘±X¦Žëß„M{ÿé@o¬âî — C°rë¾c¶Ì¦.ORöÅIã‰h‰¬N•þ}#«ÔóL¶®úVP8èÀ¿0§q˜ïbƒ8`Ò6È"¥•«dI_¤yaÉ–EvÝæXZläy±3„ÔhÃù(½ÆËBü8v÷b«ð·*'Dü%eÅ<4+yúÉI¼-àS25êâlë0QÖB?ì­«a<8®Yvÿ:õ­žMcÀ VIÏÿajobxú^«ú‘ý5WJ¼KtÅ•!ã±ÁP DÝujÌ®,–»†f,yÐߘ™§hpG.“„'yŸ*äÛßH?‘ú.z¨l ½¶ÁÒl!»Vå÷¢Ý–OõqƒÀÂ1V$Ȳ Ú‹–‘¤5&AWd”o±[ƒt[«+¨á€Û=”{Z[Ó?M‘(çMßÙWfŒÿ~ ÀŒ©Èb2t‡´0¨œnU|¼æ!V™[ós³Bsøõ!I—A­tÈE&ܺÛÀ[Éï¿P`“‡gÊ^¥òí»ÙV·)Y€†iô/>ÈokaªJå™È»Bo ‚4™>##“Dç…DÁç”àNílãàÖØŒ—d1ò!‚Jò3ø 0Ê ÿBÉWk½èµBFëM‰ïOOÒe|!nõ~BCе+ÎjˆÆáß<(äÔítæV¯•Í»W…—ëüP¸€Ö[51~M·¥òýTËôšðL{ꣻäÁäH%f|BMRrŠ^ÉãšC±ëÍ2Ì ›[˼%¬àöåmæB7âqmMá]{›8O˜êaŽ•;ý9Êeð Í× w¤²ñ©Fòž‹ï`ZcÁÍ~÷¿påõa8èºÙ¬up{óŠØ`”ñ0 †ÒN,I’±y¦ ÊŸ-×´ÌÔ^æ°‰±C‹ÚÍZ—÷SéÕÊ.òæx¶.*¯÷Z¶ƒÁ:Ó· eKWº®õy4„3S—󔜿Œ©–³Ž÷¼to%;Ý‘l¾=Ï’Ø`mk´`ˆ™òÌ~áêU”ãFÇK€R Ýe1´CαÔÄžÐur.´þ¯má,ŠŒ/Ž©­Š›$¶_D¤Þ…vlGôPÐívDÑK¸ö…JP1ÙF¬ÿ|tÉpGÞÒOèÞ'`üvEq ®oµžËðͤG¨Ê—˜¥p#.>¥¡L5›û¯'2!€$Xý8?Ÿ—%òä/15/a^EzP1î›Ï†f‚j\ÞïO`.E½Ð˜7‘eþ·ÃßëXXqš·‚ô2§«Ñ³Ýe«VV…ˆÅ›†KÌØl•ƒj=gô÷Gºi_½#ÐýžëÔ“dz¶Dá÷ú ¼Õ±K­óÎZšÒ­DuûäFËýê_¿q èŸÿÜZ½cÈùú u$Hi“ y{Y (ÂmÜ•ý¹ü^êçQ[ÕCVjWeôͤ.¶ï-žL¹X¹+]°VˆÔs¢>}tfOðÏšQûMkä ¢ž¬.ÕਫS<È?çö0€ŠpÍÇ¥mv¢ÈÕ6¦f#6@¦Ð ÐŒôìa\u5]¿ AúÚ¦òŽÀÆG; «î ”JjÈ‘ýËÆfG_ÒÛ V kðµÈ@Y¹d«²Ào›^i"†éÖ³äùŸæIå«Å‡jÿ=T|°XX äÝM`]HkljpóÁ˜9"ŒÂg'³u¿WMÝõtÃXýUeóì»êÑoÛNh¯žŸsõjUóõÃóï?ÏÒ=|æ/ŸÒŸ>JÑò&ýT½óìóïOÛMèQðÜåò+~­ýZ>Ùüû[OÅóúfçÏèÞ ߪ©ýVÏíÑ?ÛKç~ÏëWLbÈá5~³…,þoºÍj@8~ž¶ÌÔ=—Èø‘Ëú& ]þî·Fú•Ž @Öag¨öìÏëí©ì&r¤s‹ýM3aÉ[)ÈUÙ¼4¯jHÎ*?Þ~È’öÅMØ.±Ëò`"fðLLL³©-3»ß 9}ÛJî½¥‹ýѪU좉JÝÂâ(0¥æ«dÏp—Ž9Ò’tã Ú•õJwCý¼•ÝØÁ7ÿ+×ãK£(èôš¹ú¶ºk—ê%ºñ„%nõqºC:ögV*âW¨kÔ ç*ΫÏ]¿=8Ø«Q§æjDË bᜪ2‹«¥þ'Üà‰|þÕìæ¨“Ô«©“¾šýX«lä ’ï!ã%ÑŽõ˜2üÅ ÕŸF ?IÔ•‡Hƒe;ÙCh‘7 {ƒ¦Gbrì 7œI_jþHÜ“O;Ic1P° -­<ŸôÙ‘·5]:ÕVDòä±hþÑî«ôГsш‚1tœøê†—¦ ñToÓ˜qÒ.è©ø€ !V€~hQHŽŸ\3bܪ –ûôXÞê}Eÿy¦{¶}宆‘Er$¾ÝŽäz{(wÌ©Ð?K”×[žÖ×‰ÊØbë`¾®C:z.Io¢¬Ó€¼I¾Î$JÖO¼òwDëïê…*O¿%Z¯f±Ï_ä ¹fÊñm4•;6I˜ $åð 2EËú²6ù‰HaÒœ_Ì‹X¡–˜ÍüP';±“ã°^Yþºîh9Ÿ9‡ö®Ï~|Qv=Y S,t¸_®Š«Y¬méŸjÌË-b%¡‹á™ôœCOKùfJ7£±©yíî§n†½z<œ„Ü»УæÏ‡ÓñiKéÀ†¸ëWw/Lœ‰Åaìé„ñ„¸BtèÚL¯qM½|ŸÌW=¡°PágpÀÜ=3ˆ[Úc!÷h0g¨bÐÏ ¿ó±B! ,"°ƒkðÝ9 ) £´6œ npV€¶QÍ4îÓ‡c0eˆ™#⦼'.û÷Á!ö˜ XTÚk…Z/—‘ÖÄãs>¸“ãÛ]‰’+K€Z¶H'êÏç#Å1 š}z¥#/’°¹Eà œ\G¿ºÝ!-Ùþ? úÍ o—çZ‘½‡~Wzµ¸}šWl™;e½˜¬d–7èø|Îï‹0µ ;'Ç©·ù›iÒ†PïÚdz1äÇÛwpo 3Äü^·ïf™¿&ÙöaÁýN³j„Ë9k~ à°ç‚¡ãGÅ‘]“Û!½dÅJ çøOðã2]|‚i‚6dÒ:®,Ñv¥C¨û¬YQÉ~7©jKض š"ᚪٞ¶ÿ}ì±×ul.Â<1«¿.¢ýŸ‘¹ª–n1™U: Ðîõkœ‘˜=þƒ €ºÙTüŸÓUªŒYÂGûÚ·þuÞ°ÖàÊbZ¯š;Øz˜©4UCI.{{hº_[‘½â#¼¨ôk¸Ÿ5-‹öøHj*N)2Î:™Ø#ï'¥ÉšŠB4ËU#A’o—Š¡¢"øá³÷»¤œ3D”úly¹y$çÿl¶¯RÄÂgßg.žj&sÇ$ ì¨3û«7€´rË@Áù†Ï¨pL¡Bs*o£»ÔeÁËÓà€¾.ÕŠR7:ó*úÚ[•çß6 E\±³Ï2ÒØ&‚w_æÔÉjÏEGãY¹¸òiLŠ´§ƒ hõ’¨ØD>&±Ãb…Û-¼¥ì»hoåfºžÃ’"Kw¥#yœ‚–’«ÃÔ¥/Y62*‚ %yMjKhl ¹ QeÇNá­Sz˜ÔÙ)gÆxŠC–wó5qöB£¸4è¹{¯‘74¨eɔߣÿq’êþÍ‹„XÀª§‰²#ßãpAy¥ñ•®È~n…Û£Üvijæ½+ÙÍŠ+U_ÙÀ/Ò¼Dø2Aåëæ;$ï‚ÈÈÔˆþBI¿;(F3ÊdígÄ/‰Ã>f C´R#_>%Ù©Ìz~—dû/æI_øá9 .‡&˜¸ë(Åúº`] ˆ4%©\v“öào–H8?g‘nꤓ‘­ÔèC˜sGN(È 6ðnj›A^{„^‹çì'žìŒ?³†Äë0ÿkË¢ÿTo9F[Æ:u¹¼ËÅ„ñt JÛ¡~¤­‡ÓaG °ë¯Œg¾3ç2øæ¿#âÔ¸ÃܧqÑsSà¹#•¨aò”e%" -õ HRb»«Ëa?í%MÒ(’§ºD~™}•ó`­æ¦¿õõ*¤ÿ^lU_õíU3’öúóA Ò²õÊ‹Z%øÉ1¹{×ÐcQ£’eHO§ôÏܬ§žì¨_!+ |N("5×è+@º±4š‰ÿ`[N>n@˾SÓRó´¼7­ì€‚PL¹¶?‰áÞ¯fëÂùKãNˆcÈSÚ3rCŠ[Ì­<5gWèè›O9½7Ü^¶Ä[+«=ö@}(š7×öÒïSx€¬Ï©|* ~“ŒæÃÓl-¤›í‚ª×·øê*q14hg®k×'¨ Ën›ê7UMÏÃ×vä‘1cí/jî”F1νEuj#.~Á:òZ…ãÓ£)ò ¸wðV\:œýCÜ}wm¸§]c4•ø3W²‚¸Ã<# ó¯}Æ®ãã@?cnÆ4ˆ{]Ê™„”XFä^-וÖèÕ÷.º5ô€½×)žÉ­c?ŸEÓár–x†ÈåTØù”}}ú56K1÷E·xÚµÅÕô±Ÿ¬ž¢u¡±è{¬¯Öa›n yœ¤oøÆ2Æs!c ŒIhíóý¬Z'{œ? ğЉ(ÏœrÝ›;ü]ȧ/§»?ðŒo¤ÂÓÇžò3sy-©ë“ ÈMµÝ-•/;NxQ‘eC†[ðEiyDxHu/P™ï^¶£ˆJâo PÒÒ–Ãܪ þ YlAé~ ~·xm¥ÕmaÅÑÔ;¤øZ‚`ÝË4’6"佑¨{9ëO‚z»<‰¼e?TWÇ„v2lÂvD2¶æ‡iŒN¶ØAZç¹-ÌcŠÈYpàÚ*÷¾´ÙI6ίôgEÞÞ¥¨§ÿ%ÇŸ½íâý}/Êæ&¨¼z‘÷ö;">&šAÿg~¾8¡Òsó$¼|À);”•€E^¬ÊY:ð—JGqèse!‘ë;9TÝØá>ìzr‡À¼õ|ÌNCÄVe{6ߺ SùteFl|üjƒ¶õ£4G)†9í÷ó‘kä¹ð妟*œÇᔿ/Å÷8𨨽ÞZ+X~Ï×ÅüÄ=Ï,ì¢SDž¯[D\ȧ f”T3‘o‘4uWu×Q™7о¨U$¼ãKIãûA®ÚÞtöc*uðòqχÔÌw¿†µ&o$^…Kƒ3PX‰h Œd#xÒ.ÅÔ¡uA9Z}B› ›º|I• J@Ån·º aùDË'(-†{é@ñÙ §bÊEœ¢¹ŽXu÷´‹·pß"òþR7±¸¼––'\4ÜÉYo¾#áÎ (í”a0-.üû}™M0$y P«®ÓËDAÕî@ZÒ^@* ýxG¿¦AHÃì´a 'DÈ_W@ê%Õwôˆ@aLU‘Mz¬^iÀSgŸÒÈ­*~QnDNÚœ£äp£JdþZBa@ì„q[Õ°Uj·½ôÕÄEeitç/©ÚBÛ2p|Οþ0ÝgiÜ*~Ïõ$!Xá`1Ä{{iH Z‹bhÈoU¸œ»„Þ)­"ayÁl¬ÂQçé¸o{>„ß¡™œ>" }ÙåEÁ N“ÎãJ ÷„±=°ÿA bÏ{){Û‹±æÖ‡ˆï'/8»Y³Û‚ÝDàÔbÍÊ«µ­Õ^ù·üt¬å3´µùw§¥·8@fbÿTY› nuÐir §ƒ»óÏhl-‚6%„.èð9ó$>¡z¨E›Cê¾Öÿf£.³u™ÁŽ´9äŒð•œ(èG#O¹±ŽÆ‡ç†ç j•¬Åß·Ö{åÇãÖÈ H)+ŒªÛ᜹â¦ß©õ$Å=;¢³¦>¨ïŠÐü¾Áð,OßÄè ãí «møKíX?h𗤛îF‹ÊYFD¾‰e$‚Ö<°"Ö­äùfÓåÆçY×yJƒ4P“¦ §ñD…«AѳºycôF„Lª IÖ ÝO»ê~ý{]ù#î"dSÄ}'±¥I³sIÙñŸ‚‰˜ GA*$òN, …õžºÚº(/«b é8{2ôÞm–ï{T'®Y¼@ ŽžßÑ[¯[ýÔý’-áÆx¯îýÿn©Zð{ˆ Ç5<ç4dú¸¼K_Qes,³aײ9äÍÅbB´: Ç ”!V¹8áÄ™2a²¬½µ'|ÝÂfˆf15ù\ŒÇeùXZ2µ`äÔòF£ÈéèkÔ9è9MP4ZN†ÅÐêpù«#à¤aQýYa#»3Öù²S(þ…¤Öa 5VËæÓðáP\›òhé‚þ™¤ñ6»ÊR8q4V³Ëñ@I"I+rZ#«¸d-o¨G•³¢s,£Knåº>“  HÀî¬?2r«ng¡ ÈF&‚º¹É6ת•¸ƒ„9I€0ÿm&B¿N±YN5ìjÈDƒõ"ˆføƒìVqwÅÂíÇOzÆóNWæVkÕ>¼OÜ£±Ó©Árµxãܪr¹sáºì”¸3•߉FÄ Wžl8óKWÿ_ýšdwŸLiÂê™\òÑ0º“¤€íž) ì oz¤“~£Gµ½¡òŽ,M¡=¥Vü‰øolrã$ÆžF$J‡Mq5”"½:‡*ŒbäzD׆S‘¨ßv›Y…ºÊ Ët)¹öE_2Ì$¼ˆäs%B³¨æñ¨ÌÂíQ-›ƒ~»'Ð`J9§^I€žÍç…á§þ£¿:޾‚Ñ'wúïèì"º;ëɪ/·Æ"GlGoD¨œ,:÷úDŠK47àbÝ+ZÊâ9ôïèÂfGÕï6yOA„A¸6ÍÁ†¦9Û±ïºÏc‰Ø€)h%•n?Ö{ÊMŠîšj·ðî MËѵzc¬t²·¢Üîèq á«„ÓK0{.®Ôc: â‘ãGGìOò Š5ê?au¾Á9ÔçT$Z`“7[ï6 ­û)GÀû»j^z‡ê÷ªV €ð°«ÎŠ}B®™úJ¶AúÒcxöPÔ3â™®8¨È×µàj‘4ò\Ûª¬ì–4³Eå¬+Õ¿¼5­õÙ{Ç´‘ åÉbËÜDaÿ‡‰Ëžùä >lO.v+$J:æI0tI£9Œ­gÄdÀ“ï)Fuó'*‘JÙr¨44¨À;¢Íœòw=J×ðËûedò§ù6¢²4\†Pîv¿$Ž6Èf|J«ì˜s6ȬŸ,ÊÄ´ß&x„`‹%©ó¬Ÿ¡ûK#=àOlÄ€KT–n^#(aâìâÆYÐ ^ú’Æ*޾ bW»== &ñb€Û£·ÊÑ™BRö"ê¦Û{`DjðêP½ÖœÒoÙQ ‰XýÄÞìÿÎA %¸_/˜ÌÈöã+#•Z§ žkzþFëƒ{§rþf¦Xoâ¢éR èw­Ï1® a :uŒ—éÈçR¤0Ñn„rQfgö:qUWB Ý‹—;|sk`ãÙch–ˇAAø¤Ž*$fÐr» –Ï0Ó Eúгþ*[뀦³‚c¡!oÇþªPåœnå_ž4¢JJ£…{Íuƒ1ÔÆóËf+óÖ¼•¬9ë”À¶¡9{«£{T2Wh’¤²^òƒ‹ež.‡ûbúÙÎ}[š^À:Ñ+N ƒí†µã‹$㽓 ˜Æï;è¿SÉáóÁmDåç1ÌÀÞ¢ÛÊ‹N‡rK†­Ar«kŸ?Õ@àV]¶°ÍM˜Í‹Íž6Ÿ‡í£ì–¶‚§ýk4ê‡ÆÀ3heÍ*'ݹ“¹ÇxÑ¥Ì{ÅÔçK–Øí Y$ké"€ 0—ó(Š'£¶LP AÙ˜þt'Æa þ¢H¬7ó#˜— ›€ì-Ÿ¤ŠT(ŸS L’¢Jí—i‘@ìF Ž*LãŽ~Nž$3׳rÏŸ(cïge•Lc´eÄïÃSðœ45Äöè[ ÜÛð 3ÿGêâV˜ÅÃ’µMîÐ…I¥´’÷¼lqýI¶еÛ3ÕÞpwì@6Å:ñg €Q`]_K¿¢ÇÍÞ†cÓBë«û¬"DÙÎàÃ#&ÇÏÔcûÛ“Œ,-¢›¥Õ ÎÖ›NøD䤲Sµ[ª¥¯ÖÂá§vvþ½kÌò¥u <,Ö’ƒª€VÊèÙ£SyË&¤‚)`ø={h6¨Ž'ªÀ!›˜-¯ï„fâ¤Zþ!gâ8µßQaö@jW¾@tÍ®ÿ¬ÿ5Ö¬…®× zÀ³`ç plÕ19§c«QÿTÛNƪó”m«Ûd!'¯†™&™×ÝN¦Ã4b6á“^9+Ž{7âìðP¬z°d`còÅÿ8€/Q(Ð ÂF‹€DÖð››Û¾XNrzíö€MrJ¸^ÐÆîb­àöĉ®¡èn iùOpwÏõg̃–þ—ÒðñÌZí[н $‡ôÑ ´”üDîÅB®.u¹yV8áŠÇÑçØú Ç…´+/€žr¹* n¯=O1yéb“/ŠH!t+ ²ðª[ïÀ(#žr—þ[Òæ¯–š0¢«“š­Äòêh…þ¬ì–ÝýiêÓ_¡h+Öþej‰zðÚ²ÄGò­dŽÖ‚2&´! ”¿MWQ³<Özò0Ljrn¢¸ùèmâð¦û<7KRÎVnÕxÿ@í{ؼ­0e àoí^ëµÿC·†Ÿë-(ý^-;ãWPÓÜ ï‰ìÙZ4‘Öb-Q§’êТÔlL<•ÞÓR¿O6 s K®|á#Á™>ÈÃbÆþ»õ<%ÃÛPUd $x[„/Õ§c|jW«Ž|áq (:åD< ¶LµY)Ù¦_G’c ÚOœT~3—*TĪõ5 Iæx 4 F^i.mñLCj‚ÐVZÁ…0;ù–áÚ%ù~/|Èn,¡WÆÍ£3›wýíƒQsš}Q}ВꪽíZj¤75!?´²«ÓX5m(ÝòW'Zi>Jò%ä†àX…8|eê?wx<ŸJBe`>ÈìG!VNsáåÉ}3Ùî™S3ú›˜+ EÌéÒ çì3-Õ{kðWAÐR˜Zjcàûu#HŽ™AsîðX¹¤áݲ?´GYæ°Ë£‡,è<ö‰éÁæWYSmÞ†ò=<ì ²4!G~÷Z~GšôôÃç”^ʵpŠTPÓºoXèó>ƒ°ˆÿ)|Œ¥ØŠEUWõ†~êA2“ä€û'r‰ ’5­3(rñ¸ÿA&j¤Hû¡!ò.GcÁrT+Ú>HDvJ滇1Ù67Ö)-X‘ž&=Ê`´ÑöØ‹¥.¯–èÃUµt€•<°IO¿g¶ct›ÍdÄÀÀìlÎP˜„òžÒI¾ã áò0ÖÕ³úl±©!r¨]¬—ÜzµiSRÌ‚XËØž€jñמE—óµOæ[ÔAÜìØûÈeí‹uÅD%—§ÅüÓήjRW…ÒMö¯=w §j$ ç#Œ¤„\Ž;8˜„¦Š+E’î•1Ïþ­KáøÖZ9!ç‰o|꣣˜ß\Uã K6þöêÔÅÕ µ»Ø©hß¾vñpÕP¯9Ì™ÜfȤ–!wcó /Ó¡«m g•1oú¼¾VUL8`9f*9^È–n-ëÏid…­ õA`oÕzNθ4#©V'ç¶õÛ,Ûßµ…Q´lžôí~‘‡¼ŠuwörÉ@ÿFâ5kXÜ „„P¡úMÇç¨îÏ`Æ”)1‰3 ±iéufwþ÷ƒj¿9Ë €^TF's^Kþ`5åô^‡œöžfã³ ÷55‘ÇÞW*Ã?¯‹Ü3Mðª(šÞ4 ¨3­Î|*þ}WWÂà‹²5„ì I³BäP[Iëþm;þ(’…{Õ RKä“}“ãP,Žð}"<ëW©IëàÆWø_Ðù™¡šñ–dZK2sA´Iå`P&¶jw—ÃÜY`ˆã™YPùA0¡^}CŒî˜ï8\’ò8â}ÅU|À]_³ú¤À¥àr6ñXa=!iàvd¼0¾r,³ 0ôDOeR¥—•HÇsë±Åè3fáy7=+É %OÄF2{ݼ/·"®4-ÿ•SÈé•ntŽ#Bfì¿ Çýuµ¹þÁÉ…Û‰Ú}§!E¡œfã@Ÿ®sþ‡ÎÇÊï~w m!Côï]ù—ñ…¬DÊ]YR²†»Ac›Á0æçL«¬"õ 6Wßô‚u ‘±X¦Žëß„M{þù¡R”'µ’ãl&2ñX‘êl”ÚǬ¬R ˆÜâªÞ`p2³<„$k¼pS½'<`Ú’Œ¯NÄ€4‚÷.·rŒ[©¡hL¢â&dK‡÷™ëhŠ:™•È dC,ò,À"·ŽÎÞoéOœcy ð1©~]Ô˜pò´fw>!`¡ù%äÚî{3ï2èQçc€Þz]raëb÷'ÙG* Õ$4“ƨLRcÏÕ¾{¾)£åÛµi8—úLŒÝ³õÉ [|ëF\ËÖ\œ«“<²Ñ4!X¯ ¤7W$1³»ðªëÉ•Òçpn¢ÑöùŒþÔ/R¦Áçª#’Ÿÿt6ÞÝ—”a‚@êMïq€CÝ«q@g3ð‰LŒiÑùy³9…7onÊêå´™ÕG&¡Î»4l tsz›Úá¶”Z:×8›!¾þ^Úq¼õH÷¸ÅÓ6ôW›NÛrþÇM§šH*Û?øí&M×Eø˜Aõd‰”4¯$Ä?Õ#¬<óŽ2¶Ã5žZ}8±´ö˜)ü;aéêØHyíîJxãñ…·½–}+¦´IËðûOè†D04eZõ/¢“·vÕ‡ªÓY 9¨Ê5¬Òí,úÉõY!`¨ ûoÐ{Œù@xÈ瓟ҕ·‘Ê,Üv³™Éë³ÕkA…VâôéGt© á$ÄýÀlæÜîø•áY½ÏçÜÌ9éïR}œ¯c°›zO—ú‹cÐôÔmiçÙ_ˆzò3ø 0Ê ÿBÉWk½èµ@-ª½ÖwZz„ʆUØOH±Aivê¯.ãs'±fžÜÜpÏú½ã& fœ[^Yà1Ö—s(ÝÐK ÀQâö¿/Gí¯;Ved™ŒúØ¢#ø{׼Pº GpâeÓ–Æ:Øä»娞©MÀUø‰“Èß§YWºËÅh!ú@§NÞc$`qý'%ïî”ÂúAg¼bñ\ÃÝèxs¹÷a~DœHÌ4"¹ŽŸÊô`ËÏg¹CÍPÔáeÏZ>ìäjí³fºn_Á+zÎ7mv^ÍmÝ; 2Gxq>Ï­_Hþ¦°ê !•\èeíÙhS¬ÜzcÞ‚²}ùýÁ©Ö^§‹l`­Q³›*<ñ*âf:›bòÒ­5œ Ý%¸‰G¬*„–ù5Å~,ȨäÝ0—MJýjk^ê­s}L<³ôÍd©VßÍ+È“¤¸éé< ÜcðYyYµüÁgïº U©šñ`@ [D}òä/15/a^EzP1î›Ï†f‚j\ÞïO`.E½Ð˜7‘eþ·ÃßëXXqš·‚ô2§«Ñ³Ýe«VV…ˆÅ›“lØðÌÖÐ÷£"Ö@„ø1oÔ¯Þ¦FÅKÛÍâÒYˆ-Ðýü|„K’é…~Á´ŠÊ-$ÁBéÊéWšÏ`ñÜú¶ÀÚü¬ûê—…ggC!S7å#òã $LÒA'jÀ_Ük‹Vi0ç²P©Ô-'`;êáqõ‰q»Ý¥ ꈀé._Þè°°®¶ä‚ü§ º¶8yÈRµ÷¤µA"{6†Äü!æiíš“ëÛèiœF09° ß½z9ójï¡!U“·OÖ¿»Ó¡Ë×éZòŽÀÆG; «î ”JjÈ‘ýËÆfG_ÒÛ V kð­ï½çá—O·qÊèÓÚAH*·ØcÔyÄ_ºJj¯Ú@|Î["]Ü……ކr¯É4îç«Ô­/bœvKo[FXYÿ¥±Å[8: 4ìûGä^±]ô©2Ù:}K ý§Áè0Ø€–_v!û±4¯èþÖOöÜoÛr~ÚVú©¯Ûwóçmûo]ú®XwFýµÔ>¹ýVÔ|=o£ûP?ÛY?n~Úö¨~Ýãä}>~†kçÛÑðëß¶ºŸ>¿ýV¯ê¨´?´ëý´ƒöÚ7ÔÓ>zMõZ½õ~Ûc~ªÊ¼ýµ¬é/ÕgGÃÐ@âÈ3³uFšÛ_1ó· áÑqðÊP¯Lì{ûÑÜ@ÑE#î®3Þ•ä…„hÆ™àÓWȾ}Åš ÁøäKSìneR)xvÍ-±»9æù>1µ}nMŒ³tY‘)Uáâ,<јeà2#rRë<-°Ýe²—~‘[ͽ¦w ÑÔŸÆ!¡gšÊ¶s Eh5?xðò[O¿¨˜ƒ‡F‡Ó8TË, ·ÿV­–s+sÎÆ­¥Á…t0—¢Åâ!Á wâKB~wÃ9F*ÓøH¬µJ…mƒ[a*d]º¡]¼[æ˜];ÑÞ©/°|›ÑRa‚B]¨„P¨H3xì^´TÞÁ jçí>èq'Ä'Þ\-¡[Y«+í{uO„y7E\À‚º]‹LEI ­fŸÛ3¬0 qCeõ¾÷Ÿ$ÏØ¶÷ìߘ¢sê@˜컓8Ì|¼PÝX¢ë¾åf˜)‡QÂl|à$¼ÌÁÕÉ}wýYð¥9Ak‡MD¨J`D”RÏ$Å(ÃsZ2æfO‰{Trçep Ô,Ç®7«åSÔe:¾ævˆªZ[ú»ÂB[ß™Ë ÏÀ£¤–hˆ+=òX•ýÕt#÷qyÕkT!AjŠQªþhßÝ›¹ŒŒÉ»È…6¾ 䬡âÊôÜ”"lj¥¡¸Ò4Ð-Ñ S›ûV=7l ¯§få~*A×èËzÑu›mù‹v–Ó³?І…k’_É´O«Pg”øO#®™Â³-qÕ+ü»Ö¦Õ™4x#ëú³sº°@DñÐÍ:Vdåe›AiˆÊl‹ÍO1m¯\?øQhIÀÉmR˜ßTK9JIjÈÏéBІ µÁ‹4yÒ¼ ȵú;$|ŒÕyœ4‚šªŒêïÔ®ƒ¥±ë^8¥#†¾ÝË,¼·¯%»5¶T~,2˜¨WYåBË·Ì£oö ”ÏùPïZdF`‰,ø KËB*çdßÿ}ΪÛÌÞ×`ATëg30*6ñÍsû‰J®-®Ôã¹@ ä“™ µ¡~áS€ŸÈK1Ä‹ŒÝzÄ%«üUDçQÅË6ÄTQ,¾b¥Ç€rð‘&¥•+ Å£3_õ'‘rÝz™÷veóì/Ÿ`êθPï&+‡f²h–¢½##w)ås„ç?.ç²ëÒ̉_`äaKAËITCÓ’a)1rn³޶î!‰3<ü Ù{nئއQ³Öð’êxoÃǯ<ñÅZ`¶þH·¡h Úû2§†€Ûÿ™’dO­ºöÿÙÀþ â¹ïeKì)¹%¥>]‰š $¬Õ=‰ˆ^ÿ óv6·Çì×0s'‡€¬ òsôvò…í%GORæ…”5¸ñ/¼ÃæÝÙõVÃ?Êôý`e¢:UºˆP9-9+~¸k©çuz; Ó…ø™ÄkÒ² $M{˜[‚]éŒJ Ê¿îÿ§jf¢ìbÕ×vG\–íÐ,5Àµ5é69î3âÆC²ó›%'ìÃá›OüL¢¾V|!(v(‘žgPÌ>èù"Qƒ=?ÕÌ{í  BŠZûʉ‡µŒ(ºYG•-ˆµN²ˆMÜcuZZÿhJå†N…2еáÈ5ÍÅ•±§ÉÚÙ1¿N‚ý’‡ ÔµÁ)DžÒÏZGŒ u!!Dò;!ö ÍÐÌ “KñKæ/?ÇÜcaärÐ+âÿeWŸù&á³æèbÞIå4æšÃæª\‹KC«ey5„ÖÞÈ pd3örãP¸ÅÃ"lý ,$ªtw–rUQ%o>&*øaÃTÇYòÀ±x¤›¤`Øe³ °fT±y™®¸’&ov~õN•Ë9bÝme·Š¹^ÐW&`(Â-±˜0œjW4æì9ž3ç\1A_×öõ7(Ò¾á%’kü–ÙŠHÉh@ÐÎð¦ÿLª°ØéOðŽ <a“€ÓyuW°É$¼ ®ð0Ëœy“¤„î»Ï0„ëýP¹j¼ÌSÂ=t!ùIᦛ b( ¸9sZQ˱–r Ž£v%M?æÁŒAVš-Äû7+äÍH£|ЉûôŠkɱ–-·å\Œ¤f¨î1í#Éü»;L³¿Ia@Þâ#V‰zæ5¡Õ‹vUåuÞí6ÀÇ&fàó çÏéz[‚ŽÿF(¹ zô+a©xuó«m-[’$-UéñÀg„B‹f*w=hïœÄ¨¬(9¦C8äÅ«èûÀäSøg½œMaäñ¿æE·’žMU*„P°Næa¡*†þN¨Ý#8Žç2FÉ™»³• †•WՈГѶÜPô{ÿ@ÚJ¬Ìg<¿^ÕAQäÊ¡Þ%ò¢“ð¹R(ŒùÝäéäƒý¼3©KõÈ‹k˜£+”Ÿê”Ô¡ï=Ý$‘ÉéYÐhÊ·LÅ­»Z¿‘™iƒg¡î'lŽ¥Š%¿‘ÓÈ)‰ÃPâTLà =Í^5¥Ÿ·FÒ^ ‚Ûðe9ß³`ìþådÿF>nاÙq·beøŠ´°TIä”– /ÙäàÀî²Þ6Ž’¯õ„Cg³  œêšçÖq|^†“ͤ‘? gqTéÈi#¹m%%RWîpÖ0¿Q »@LRÆiüÖ£žvì´Êȧ·祹̺ضk‹Ý#Pç!£'úA<Û‘‡‚0 ~Bûi™SRvåeqLÛT —È.¨üxšÃÜ \èí¼ü6ýlÀËö\|jm‡ƒ–Áv(ÙŸ—Ä\èQ<ªE^Šâà J§2ÓwÞ§Y4y%I÷b˜× s¤[Rv‹ÁÊ+õ.bŒGûßçÊJ–3«uUÇÅQó…³TÄÓ•Üê!à%È„_õìˆ1µSÓ"VÖ „ì’%¼V’ ;iˆ¢˜î^…s=a2¸‹$8ÞŠÉ¡Êâ…{xi­mµM‚ oz:·×­õ†Î”„#E4S„º4R ¸þ_gºïôh©4ƒkWP®1êÙ/ýOL­–œ43Ä›™™÷ò…MOl§ çÑ(ãHž2û›‡ZSƒÉ<Áѧ|a«Ñ É8Öh~E{®‰øg2¤™Ò»èåÁ *+HÊ¿Oܦ+\é‹2јø˜ß]"”Ubñø‹Õ['FW[õ:fdgÜvÛDœÓñ¶±þá¶ÙŠAjƒ¢$ˆ…Ó&!jpku®A´÷»Úð`’¨•=%NóiÁžÈ®á)‡g%[uë#Ã'û<¢’o ”¢!…­¼±-5ÈÔ¶;Hƒ7& iÜ.àÕ!I¶°èɬI<7αDÔŠ²Rý>Db/ÞBñ¶´îÈÌpèVú¶Z«×wûAóuÑ ¯¡à]‹½c!<¹²e^Z'º¥`,T’̉˜»7õÍvÃÔöè®O4=®ž*5çY’]§®™Ê\–j J9]×~ö†è²Pr;·‘5ñ3rº‘üšò~ÅM?&O rhëãuÇϸëðbl1°@1FšLÁøC·ŒûÀÜΈ5P‡ë­õ'ÇñÄPݼÈþ,{ÊÝuE:wXD5òøŠ©¹ñÜb+ˆUŒÃDïx¢x£]ÔCá9×qz¾{Ø3:ÒÞ¿v³[ÒÌ—’J—cò ãλWCò’œ%ElTce\¸4†éò—‹çè!Ši²z×)FÃÁ`ŒMÏ { Ô‰ÎW‹œ‡²ªt?<‰¥ã%:/gC©SèDÙ5ŸêåðÙŠLÕ‘½q‰CWÊ*&š|0Û¹?þ!ÇÁlëÇ<›ù 5y¯T©.k¨ndq¾ì`ϰdR´Å¶˜ 'RÙý«à9(ê!“¡9;µôļ3ãÃko2¯PæÌ¼¾†±œÎ´Òïéü´‹<Þp?Åô ËÚ—]“+=ELšK?žÅ[Œâ³•¤VѣϼŎSês%]õû jÉC_ŒJþ<h†.þJ`ej iÕÔPÏxüÍþÑðȰ³ˆ½J1˜j§Ë/¸ ”÷d…‰ðôµ7ìˆg ±·Ä6« 1û¡IGÝ$ÕMiPí´ÓBA|jÏÚ` ôÐãÜŽÒ†WOâö¦zØ·p¡ß[ ‡klBÂõI‹9…†úÂzNécÞV¡×å;SY¾¡M$ÿJÄŒ$ç ±„ugg¢¨(¾w>! šV œ«Š«Šì&–D”ÃÚ}ËÊßÏœ´ôä£î”³'X<é”îÌÉ™ãpVre…è÷•u-\dGMðPÈð±÷!šÌÒsV}=Xlxá¹ÁFᑽ#ÌÚ¹ƒ|a},ÎÍ: ¤ê~rŠ˜-gR’¦2Žù£Íãu¾ríÊ̺¢LEÆÙ†Wcé~8° Ô6®à´äºÖ|øç¨vy†*W¤O‘"caïrfÝ 7îž¼óþT©—ì)F@ »CØY¥° 9õfb*ÌРΚ8ÈÊ›ÔÒõZxÖêé.Œ •íø`ZÅÿ^1…&†¯wéûoqBï&Ø]¼Ÿñîü±"Ƈ`o; É/C„aE|µúkZóßå‘ËCNÊ~kÒÊU«·kA#ÕÚöôÆ:›¸Â2d‰ fœÞÄD Â/èúO¥/*òÌ[»-ô›òmDЊðû{cÛG ë혠èÁØDgs<ó+2“¡ã´®Q˜–1†>OÊ äFó· ¦ýÜÊ®Z íÍWn±5uá>ň½¸ˆ.£„‰ßŽŠ+»â;þ¿‚2"À·¶6‚.´bÍ»5¤Îˆ¦3k4$è•ô7Æ•2GßÙQíÐ7êY–5¥Ä´ fl›itâU%ÃYyüÔŒúõŽB%jZõýêqW-@¯É]Ó÷ÖÇ€h)­i*¤=ÂO’s´Q‘ÿ eŽöáƒù&I Sš%­èÇןT¨`8ü ‘ÆEÈ"’ äº|­0õÎUÊgJ‰~/E-Hó÷›rÎO¿–˜¥tY$¡òˆiÉ.(kQl,a†õû¡Šôç×gR3ör$ %y[»wHû(îÇs§f’À/ðW‘&íúÒøNĺY:ÊÅPžAj%`×q@MÝÅäF>8ìò²UxÛ"ziDÏ6RªX§O\ÓÑ[Ç{©ÖUËé Ê"³”&B½¿Ä˜º¢*ÉbÔõÅ_7¥´ˈÄóÉoÐÄuÑ62¥½âXbû9V\(¿Qä‚¢Öér‘ÊÙ ¡¯cY8 ³?×#Ý‚n•W]ºNã ۪Ȱgî°_ tP>F®=¥›=Ÿ6‰Q“Šêª&®‡îHÅØh@ĤšÍ°À#õm&(%ûV°*×láÝÉñ±~9ËØ ×+`v¾Å#âÏn+/s ˆÌ‰9)‘‘›WaŽ#þÔ ¸jû¤Ñî£!y¬ÜVáì2È›šc}Fv™>ø|¡F÷[Ô6Ê ðÉmöÀ+iEM)LÀÃíî~Ã*ÏuhÈzR`«?J2V\`ð±_£P&´Nó'ƇÆl’p«!ÙB­ƒ¹°þ‘ˆWNKQãÃð&usÓdÏXè²oU›ç£&åÙQé\0ˆ¥*·ˆl¶ò…éàéÂX‚¶8Ú*/üB>H¿gV4ø¬¼±èÓ¸MËmÒ¶±`¬²îÛ`§†¾þ¶8D j ’ioˆÏ¯°ïÅÑ3c¦CY§î| ¨ÓJKX,ÈH—Ò…Ž=`ôß-å–,wÈÉ®<×,³~TJ…˜Ç’®š³O’zCIê)$ƒÐú qÍbl-î 7Æ0ê³jŒšùU;ÉZ Gø,ŸuÁNû±ÊErÞ«!NeÓ6ú¸,\ ¹Ø¼¥z£6õbgÁ¸Ž÷6ãRz¤þì‰h¿ÖB^«WR¢\öT3QŽoäþSY>ó>È訑GÚÈ)Óñ†ôT3mþ ήNBÇfÜñŒ—LýDfÏúxÁ4]/€7B0 !¼ÁÏP"hP&1fís ¶ íŠó¥U³W/îñF},Ž`¯BLSqU‹U[×ò êáÌüÉ­Þ“­ÇU. A¡8æ~ù{K¿qÎWz€)¨R«B¯÷²µ¾t6V6”¿&Ïæˆ/M»âÕ𚆹¥]ß¿_ï=Ô³7M|Ò·î½V . U&×v'€Âäf»2–}4MÞot¨3‹"Ry¯ÄŒY˜% tø Y¯ÿUXÃï‡|`¶:öÓgÄšR2ÐÛVþe.‡ŠLܯژ”úËbFæÐU¡”¸r¸c"­V² …ÍéÁÑNf ö1ò¯ºî½ V@ wµâ&ëWšD_iÂrÙ¸ôÃñuCˆ+æõp(`2ÎT&O®7‘›þˆ|ß>ŸTµ‹×³î+Ø–"/Rš}ü„õ›¸"–ø™%¦!¹R±Õ-µD¶ñÏ’›`¢DÚ¤‚—ßéžá?&£ '¦í?åJ÷èb€6íÅV"·æùQÑièk…bÜ!_ÆÅ´dÖ”Â+¦íÇóÉr±îÉèË”w{b$PÒ¦-…xš»Ã=ýCÙ38GâMÛêW>“¡5xnDjôÌYXé¹9Ýgý¼ÜÇ27¢53ó›"-qê2ËAáVÜÙÝlø¦ÌkÌ-\÷ %£,eÄàe) ›Ôׂ}X¦¡³,üèDQAJìð…첌˜œæKCæäÍ€Â×̼_PêÇ®âc¶¹íÁÿ9Ûñ+9…pøT??ùË]Hàüž$QFÃ+¤Ðói”yÉzé Æ_ÆÁVŽØÑäd¦`ÓÜtE.ºˆPŸ|:fƒå%î˜O¼1€7/a)B Y¬2P×Àû…°§é›Zç25½u:?·Ø*»ä‡–¡F‡lhÁ‘ý×·“ŽÞ…¶ò-¥éîî…Gоƒ9–ËϤZr;ï;sü¹Zì’³¬áň!ÎJæ˜ æÄäPv«ge<”ü‡¤±HâJ:d" .4O[:Ýp¬‹{$àY®bà•Í9”hqr=Gè­øeØÐt#£½@¨V­S°}ñG&ñù#ÞäÀnRíàÓá• âµ¹åàõ˜Œ!÷xN J&Äv1[B’KZB/FˆW/“æ Á(=¬¿ü·tà –üVa¿É MiÀB9‘t!\KÙÕ­“kÚà£Ó!fؘԣ‰fÈ¿*f.¶g+öbûÉm×›ùqÅM!=ª¹vœ¤ÃÄlGövvãÐÇ%³“Dµ\ö©ääÀÀC¹¿ˆ£K{žR b¾„ªšøÔÿTR8‘ñí¢*“Yü[Âf3¤ƒÝs \¼¨áyn, 1PØ!è—‚@h™”гÈKo4±1¿= Ýõ7Aç$¡(®õcÚ÷Ôÿruš&2°‹ÔÁëž[œ5™Æ‚–³81.r3½}|²Õ¦a'ª¹³|ƒÂñ/›³}Ó›ù‘¦ÏUlNˆ¿ÉÝ ¡eeAí¨ç{r T\gGƒriñ}~Rº7ê —MA"ÙŒ¨€ÍQuÒˆ %oµóì KRÅcv¬e ÀŠa ›òŽWqíh^’Ä“f}G£»ÐŸæ®š› S&êć`lÔðÑÄ7pt²Æ:kñ¿¯å<°ù‡¨*ŽáÀ/£ôÔ(ƒ…ÉþŸ‡õÅíýÔ ÿR‚ù:qÆR`˜GþÍŽ·Ó dZ®T俈ž(Ubˆ fÀ¤_8rãFõÖ}t—¥µ–ØÚ´kL–€Gr† Q@’úf¾œzî Í8z¬ŽXt“]l~‹@K16ð^€H:˜§,¼á>7¶àMw$m¯ÝMîä¸*}G#ÿÙicnV Bþdicompyler-0.4.1-1/dicompyler/resources/dicompyler.ico0000644000076500000240000014330511677672407023766 0ustar apanchalstaff00000000000000 è–(~00¨¦ ¨Nhö ¯j^!00 ¨% Œ  ¨µ± h]Â( @@€ÿ®€€€€€€€€€€ÀÀÀÿÿááÿÿÿÿÿÿÿÿp" Õp"']Ýp""pÝÕr"'}Ý×""pÝÝÐ""'ÝÝ€r""pÝÝ""p}ÝÝÐ"'——Ýp‚y™™—Ýw™™™™×y™™™™ssY™™™™“7sq9™™™™s»—ps9™™™™;»»³€s™™™™{»»»¹p™™™—s»»»»·xˆWwPˆ;»»¸8€lÌ`ˆ{0ÌÌp|ÌplÌpÌÆpÌÇlÇlÇlÇ|Æwgÿÿÿÿþÿÿøþø?üüø?üð?þàþÀÿ€ÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿÿüðÀ€€øÁÿøÿÿøÿÿøÿÿüÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿÿÿÿ( @€ÿ®€€€€€€€€€€ÀÀÀÿÿááÿÿÿÿÿÿÿÿ Ð"ÝÐ" ÝÐ"'}Ý)—Й™°™™»°™™»»°Éœ»»»Ḭ̀ Ì À À ÀÿÿÿÿççÿÿãÇÿÿá‡ÿÿðÿÿøÿÿøÿÿàÿÿ€ÿÿÿÿœ=ÿÿþ?ÿÿþÿÿþÿÿþÿÿÿÿÿÿ(0`(+(.0.575W9+W?2[E9|N7\>\z{` ag/gz z~.*_D2TB7GU9QY|EzI!jD>oV/Ib1Ol'Zj'Oy<^~GIG[KANPN_SMZKZKXQWYWjSHtRBgZTLeXFkXUd\G\oS^ikIknRnuAuQaf[bdFfpcedfhgnanfikjkjxoxkppsss|vsptxxsx{{|˜?¦? ¡?@©@‡G(Ÿ\;¼[-¼]0¹b9ÌD™eL½iA£v` K‰MšQ¡Q ªW°W·[¼]8hª` ¿a¸dºgºi0¢h?¡n2²o=°t?¼{_ C¾~Å` À`Éaª‡w=>Œ8:•8;¨8;¹L€X–?A˜nŠ}ž&j€Ž–*—–6–§ §¬¬¨ ¨´´´ ´·¸»»³´°°®*¯²#³²;³‰U‰“I“‘\‘ƒƒy”n”™q™˜~˜¥\¥³V³±h±¹m¹¾¾ ˆ²“¿b…•œ_®†X³„t¥Œ|£|µ—M”¬J›¶k²c°{™¸z¡®x«¼kɘ,›À4ˆÞ¢×£Ô+ªÕ…û%à ­ã±í³é»ü ³å@‚Ä_Ý}°Á~¦Ñg»×C¸à………Œ†ŒŠ‹Š€™ŸŸŒ”“”—˜—›œœ¸šŒµ›€¨“‘¨œ±ž¸¢˜††¦˜—§•–»¦œ¦¿¿™¦¤ž³©ƒ¤º’®¶¢¢¢¨§¨«««¼­¦°¡°°©°§³¹¶¶·Ë¨˜Ø¬˜Æ´¬Ñ®††ß—«Âª¸Ç¼¼À†µæ‚»÷ºÖÇÆÆÆÒÏÒÒÑÒÿÿÿé;êÜQÖ”—+ÑYaa_&Ø¢››š 7´aaaaT%飛›››š:SaaaaN'ê§™››››‘Þ_aaaaL'››››—ÑRaaaaaK.Õž››››š,Uaaaa_0ã ›››››×]`aaaaO1››››–+ZaaaaaN4¬œ››››› éÜWaaaa_J7­Ÿ›››››2³Taaaa_:ä ›››››– æ^aaaaaO 47¥©™›““™˜ ;¶WaaaaXÝðëëðí¡«ð𒎾Ta_²ð!êý7§ ÑÞ^_³é!ÓÖ066¥ô[ððâutàø4ÒÖÞûðzeeedsø4×6ø1â{{{{fduÑ4Ö4ëê3Ùéw|€|{fcð1±1éé.gp†×ëv‚‚|fdð4½Á®j.Ø×-qƒ‰‹ÄÙéw~}‚|{ið4½ÉÉÉÁ®l-ÖÑ(hƒ‰‹‹‹‹‹÷6áõ‚|fzÓ4ÍÉÉÉÉÉÇÁk/¤;r‰‹‹‹‹‹‹‹‹úðÔŒ|{tø4½ÉÉÉÉÉÉÉÉɯm;9n…‹‹‹‹‹‹‹‹‹‹‹Äïðáuuàø4ÍÉÉÉÉÉÉÉÉÉÉÉÆo6ëx‰‹‹‹‹‹‹‹‹‹‹‹‹ŠÅùÙ0ØÙ704çÈÇÉÉÉÉÉÉÉÉÉÉÉǰ‡‹‹‹‹‹‹‹‹‹‰ŠÀÌÎö÷üðéþ6åÍÏÐÊÇÉÉÉÉÉÉÉÉÉåç‹‹‹‹‹‹‰Å˹öñòóìîóñ"ð7×XÃÈÉÉÉÉÉ‹‹‰„y»ÚDFFFF@ð6×躿ÆÉ·»ÛDFFFF@ø4ØßEFFFF@ø6×ìEFFFF@ø6×HFFFF>ü6×HFFFF@$ü6ØHFFFF>4ý7ØEFFFF<;øüBFFFF?ÓGFFFFAÙIFFFFAëbFFFFÛFFFF#FFFF$FFFF8ÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿÿóÿÿÃÿÿáÿÿÿÿÀÿÿÿÿ€ÿÿ€ÿÿÿ€?þÿÿÀ?üÿÿàøÿÿàðÿÿððÿÿðàÿÿøÀÿÿøÿÿü?ÿÿþ?ÿÿþÿÿÿÿÿÿÿÿÿÿ?ÿÿøÿÿàÿÿ€ÿþ?øàÀÀÀðààðÿïÿðÿÿÿÿðÿÿÿÿðÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿø ÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿüÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @iD3nL;->a;1k<4r;6u;>s;;v6>}_&_K=Uwxn9p{0{mD=wY(^j9A{'^q4Tt0Uz(bk?m|wZLFoYArXNq_IA~rFr|[|Vve^yjOhrIp}µC¶D¦B¯B­F²C¹F·I¿I˜F “I(N1‡S<ªN$©Y4²Z3ÄF ËG ÃHÀH­fG‚lcJ‰^œS8\ ¶]¸[¾^?„b&«e&´jI¯yÃ^À^Ä_Å`È`¥„t»w;;ƒ>>::ˆ<<‘==•<<™>>>>¡>>§>>©>>­>>±>>µ>>¹>>½{’Y<@†$Z*\:J˜&[“ sŽ9tˆZ±)N¢#h®/k·GHKUƒ^TƒU[@C“GD—GKš`C„Rm‰_r†qDC¢B¾==Á==Å;;Ë<<È;;Í<<Í99Ò99Õ55Ú55Ü88Ø øöòô--æ##ï''ì++é,,é22à!!ñqÉsÏ nÕqÜyï yèxâzô|ùHHÕXXñnnà~~ê— ˜œœ‹;Œ“%“›2œŸ  ¡­®®³¶·¸¹¾¿¯<¯ˆ]ˆ‡l‡qŸN¡«j¬±}²³{´4‚žºTŠœt‡Žfœ€rœ”`¶‰s¸”jÔ ŸÑžÕ?„Ì£Ò¦×<¡Ä ¬Û ¯ç ³ì·ó»ù½û¿ÿK„À{ŸÄ]¸×p¯Åu¸Î“ˆƒƒ–žž‡’•±‚±œ ¢…¥°Éœ‰È¢ŽÌªš‰Ê§˜Ê°ƒªÒ”µÕ¤¤ñŽÄÖªÁØ£ÊØò1ÿôQÿöqÿ÷‘ÿù±ÿûÑÿÿÿÿ/-P?pRc°vψð™ÿ¦1ÿ´QÿÂqÿÏ‘ÿܱÿëÑÿÿÿÿ/Pp!°&Ï,ð>ÿX1ÿqQÿŒqÿ¦‘ÿ¿±ÿÚÑÿÿÿÿ@š°=H<£ž¦CHHHœ£££¥?HHG¨£££›³HHH?©£££¢§AHHH;«¡£££ÒGHHH9ªŸ£££˜É²HHHH ˤ££££ BHHH:n £££ ÑEF\PQOMZ£—´QVXXWTO™ ±kXw{{ytUNjVv}ƒƒˆ{YR!È X{ƒ‚…}uTK]"q[dt~‡€„ˆyUL¶ÁµaÌp^Œ’’’cW“Õ”‰†~uSÀÁÁÁÁ­bÊoe’’’’’’‘_u•–ˆ}yXO`ÁÁÁÁÁÁÁ¼¬¯Š’’’’’’’’ŽfmtvxuXQhÄ»¿ÁÁÁÁÁÁÁ¾®’’’’’ÂÓÔ×igrl ØÖƺ½ÁÁÁÁÁÍ·‘‹sÃ×Î3')$ØÅº¹¸Î344#Ï344(Ð544(8644&Ç+44%*44,044-144.744/J22Iÿÿÿÿþÿÿøþø?üüø?üð?þàþÀÿ€ÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿÿüðÀ€€øÁÿøÿÿøÿÿøÿÿüÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿü?ÿÿÿÿÿ( I4y|3|/VG|`NycJdq¼H±I¨M#±O#ÅG ÊG “|q °b¶dB‰dF®wÃ_Á^Æ`È`>?µ>>¼I3”B>4L#z˜U¿3D¤?C¢)T¡?@²'mµHULa˜V[ ==Æ<<É66Ü88Øö..å##ï22àsÑ{÷|ù~~ê&£¤´µº»¾¿¤6¥¬[­™r¬4œ¾^‡”m…T£Z‹½…Æ©Ù3£È¶ò¿ÿb–Ên™È{ºÏªœ–’¤¢ˆ¢½¾Î¯¢œË³£¾Ú‰ÆÛÑÿßÿÿÿ/Pp ° Ïð ÿ=ÿ1[ÿQyÿq˜ÿ‘µÿ±ÔÿÑÿÿÿ/"P0p=L°YÏgðxÿŠÿ1œÿQ®ÿqÀÿ‘Òÿ±äÿÑÿÿÿ&/@PZptް©ÏÂðÑÿØÿ1ÞÿQãÿqéÿ‘ïÿ±öÿÑÿÿÿ/&PAp[t°ŽÏ©ðÃÿÒÿØ1ÿÝQÿäqÿê‘ÿð±ÿöÑÿÿÿ/P"p0>°MÏ[ðiÿyÿŠ1ÿQÿ¯qÿÁ‘ÿÒ±ÿåÑÿÿÿ/Pp ° Ï ðÿ ÿ>1ÿ\Qÿzqÿ—‘ÿ¶±ÿÔÑÿÿÿ/Pp!+°6Ï@ðIÿZÿ1pÿQ†ÿqœÿ‘²ÿ±ÈÿÑßÿÿÿ/ P6pLb°xÏŽð¤ÿ³ÿ1¾ÿQÇÿqÑÿ‘Üÿ±åÿÑðÿÿÿ,/KPip‡¥°ÄÏáððÿò1ÿôQÿöqÿ÷‘ÿù±ÿûÑÿÿÿÿ/-P?pRc°vψð™ÿ¦1ÿ´QÿÂqÿÏ‘ÿܱÿëÑÿÿÿÿ/Pp!°&Ï,ð>ÿX1ÿqQÿŒqÿ¦‘ÿ¿±ÿÚÑÿÿÿÿ63751KI54H855 2#&*,G()+&:;!./%0'>BB?<=///-D$"M@ABB9CLJ E F  ÿÿççãÇá‡ðøøà€œ=þ?þþþÿÿ‰PNG  IHDR\r¨fjvIDATxÚì½ |åy/ú¬¼‚X“у'1­Wµ[¯bZVÇ>½ÚÔíµ|œ©¤ÅªsQáW!—Èäw.·¥†žKlÚ_Š“ž7÷„ZˆEbцD|[@L¼Æ¯À‚Yã…ðâðÚ¾Ïó¾3³³«]iõ±+­43Ìîh?gçù¿Ï÷>|Ì[fúøðácæà€ó>øð1á€ó>øð1á€ó>øð1á€ó>øð1á€ó>øð1á€ó>ÌAÄVÇ>+Ö‹uÞcæIsª¿u`àé7q·33ý}L|˜c@áߎÂÿ ½ü=RƒÔM7šW4œÞrë"Ÿj>Ì1ÄZãïYÁèù´LëëÊÙ½ ÁÖ´T]Y8AøŸÐ Ýì}°÷$ø$P“ð `!Þpg&…vвÓK ì5Û{Z]¢Þ€w?ÁÍ@x ÉàM=£¿ï“@mÂ'€9Tÿ Õÿ×’B@dZ_[‚4ewB¸1< âG(øi$€—qÿ$þy¿ªª¢9pz¦Ï‰Á'€9$€µH?Û/tƒ‘U§õµР9»à#1(žDá×ñð‹¸ý;’Àx?µó;OÎô9ð11ø0‡€&ÀŸãîûC·€aÉÓüê´d·€Ô ‘Còiø þ«¸ïÅ?öáíá?ÜI~ß ¨!ø0‡€ð×&7'C=Áé'€(€Ò À|®ü;Þ 5:æ@ Á'€9$€ÿmÕIac…`(’²$“ÓjŸDÁ߃°[Ïè¯ö>ØûøPSð `!Ö„èšd°­&@$»”Pµ…„œìý$€Ÿáþ_µmpOßžã@À'€‚OsHG¬P¼QËÆÀÈN?¨ÙÝ ‡@]¢ÐÝ,nGqëÇmÞ{ÎÔ|˜;¨Càc]j jV´" dû@EyW—ª`'½‡‡ŸÂÛ÷jií™–K[Þ÷  ¶àÀAì²ØE¢(¾­KA³ÔŠ€”Ý MЋ@N£àS2Ðîw™Yóq˲Þí}°÷ÔLŸ åÃ'€9‚ØêXL¬÷ê ]Ì„Áªä’@ Š ø…ÿüÓý¸ýï¿ãçÔ|˜#@õÿOp÷/ZC7h©" d“Ð ; FDröep{·QøÑF´7ûé£4aßX#ð `Ž à›îФîZF¨èÐ’½’x.À ܸ=„ð°nè‡Ð°À'€šOsHwƒ¾F uÖW’¢Ù HÀs‚@Â~·‡q{È´ÌÄ}÷ÞG¤à@À'€9$€Ÿš¡H›!´*I<Àà¹AVø&dá3k>hÆ+}÷‘Yà@À'€9‚Xk|?HñˆŒB% @Íö‚*$Aid¹'QøßA"ø9îïÇý ÷|ÿžýd ÚOsHBCÛB"%%Ûª“ ÄsÞÅ?Äýs¶ÐÀQÔ²3}N|”Ÿæb«cëÅzñ§æ’nH¡ðë%€$À}Þd ã(üƒ¸ÿWÜÿ I`ÄoV;ð `5€kq·Ã\ÒH(¦Vå@Ȧ væ’‚@%À¯RI°]œôƒÔ|˜@øŸ „¿a†;ë+OäfÜŠ“ DA²ðk òï¸?è7©ø0€pH‘?1¥¶ºêÀ®\25 ÂaÜÓêßë7©-ø0k?'†c¿m -P P¡”–k „#dÿ5IiC~cÚOs¨¤„ƶ õ`¤* @?(BÔF·1H rA~é7©øP㈮ŒÖ˲l K7ÖéY¥* à ¨Á Ì5y’ƒ˜Yó™¦KštŸj>Ô8b—Å–ˆ¢˜–u¡àKU"€OÊo ²—rðAëý˜Ÿ Tð  Æ[û¯b½ø„´¼´ T… \€¾üÆ ÏÛõ?Çۚߤ6à@ `À¤U·@2e‘©œÔ ©¾·°1ÈË(ü ü‡–ÒÞòƒÔ|¨qÄ[ã·€ ý•´¼;X-€“:4×ß—ß$ûQøÂÛ£ ð†ß¤6à@ àûBƒúU¡qã‚dºzA @‘„\c,¼Ô$ȃðƒÔ|¨qÄZã?“ck…†80 @¶ò Ö÷ç7ÉÂ0õDáÿ±ß¤và@5€CÒÒ¶Ï[¡(hŽ@Å@PYPê@´üÆ À’þIà…]÷íò“j>Ô6¨œP–o¬ Ê  J€R?J0™ß$ á~—™1š"Mø¹³>Ô0¢«¢È åtxU7Y‰€†òiUX.’@Ac§q»W7ô§è¾Ÿ 0ûá@ #¶:Ö,Ö‹/ª«{PðNhXVåMpõ¯Èo ð¬­ô£ðnA ¯?ßd¨2|¨a ýÿG¸{(²öHjPU MõýŃàg–e½]¬1È™3g_î¨Ã› ä\‡¯ÓR¬°è´žÑOiãtÿcý§À'ƒªÀ'€À×AÙR—ÔÌêíOÚàm ’…}¸ß ¼4x¸ 1H`bÿ…²$Çðöo Aáב$Nhiíþ{£÷áÞ—ñ9¦™53H !òN!‰œŸ *Ÿjhl—•¦ÔhW°ú`  Зßà 15ù xƒ Qw÷î¾…~+þí¼b¯ŒZÀ0 û‰DâU»¼ømÔ¨È(Ó¼¢9ë;+Ÿj(X?––Ä.W.Y_] €™D{ ƒ¼´úôJ’ôêöoo?¡iÚg¥ô¯ø·ß÷å³@êÿÁÞ‡zŸ9Ƴ ßá]ÔL_˜~øPȵƇÔeí+E¥èHT„,$Í%M~E6rAÞB¡ýOÚP`‡öüdÏGVÆú)[—÷*ÕˆFÄÁÒ‰ ±ãvŒ &@]†~…Ï}_oÈ´Ì‘¦HÓqÔ|ÿÀ4Â'€À{M«ºÎ‡…ꌅ•…šÓ„’¨1È/PhïÃÛÏo»sÛZ´åpžNÂö»¶ÃÞ½î+¢v]_í‚Î+;)šÀ€æÀéÛï¸ý¾ÆQ éÃAx ày1(&ñ52¾“púà@B]¢ ªªžˆ´öHÝŸ )(&òƒdÔ÷{ñöÀÝwÝýî/¦g’§¿ãжúCû†vÀÕݽ¿ëþ]<5@‡¾Æ+öë>§gôáæÍ'|ŸÀôÀ'€Eluì±^<Ýp EM-M…@U Û (‚Æ“ ƒ Ä[ãÛ/oržIÂÝ÷Hߘ¯¾ûþÝNbi YVPDáE€#¸½ˆ¯ÿ A^@óâ¨?{`zà@ì÷p÷hìŠmJ[@Â/Tà] o«¢M$ŒžÆ ]×tYÑ•Ñ]Î3×i=hš6æ«oùæh¿¢ÝFUÿôö;·S6ŠÂךHµ‰×Zb-_ ˜:|¨Q ðUÔî‰oÚHŽx€L€luðv€› DA‚ð éƒ=ßè‘q5ÿkö´,@s¬yÜWïúJt­›¿SÆ:³ùæÍ$üãó?À×¥7ÄÛâí_ª‹Õcþ²©Ã'€j%Há[Z6l &“$ÓÕ"÷šM:À€·3õ ÏýC=7ôœp{Z™ÐùåN蹩‡¿“…ðMFä\$3à¾no÷›Yó #mñ»M>Ô(~^ûJÓšÎ:F)U›¨;°¢¸É@äá;ˆBúÓîºC‘e‘¯³§M…²¬í8õHÛH¨ÐèQÝÐù]‡¦ŸjH«+ÛW]Ñû“:¤RÖŒ€L€"[£ƒt}µëìèÊèµìi“%€ol&'gÀûøº#øZxûÔöû]‡¦Ÿj±Öø[Í­Ý‹•° ûx @@¡²ÀƒÈÔ`¡ÁƒdTrj òh×5]4³àjö´é € ÐÅ×z÷?Õ3ú+þ²©Ã'€ÚDjVlÃ-õbHfÀ‚Τ@5Þ„O ¢žÇPø$º*úßÁ>: å¼ eÂO´”ö²ßvlêð  [SÄzñø¦m`¢p%ÑpûV™DH*&yüž'é¸ ƒðgìiÓEAF.ÏãíŸhií%Ÿ¦ŸjH¿ƒðlûµw³ ¤­¤œ~€3AA6nÚøAK¬åOØÓ|˜µð  ðe$€íºéØ?läÀ Ty$ÁCž\ Ù n¼r£Þ²¦årö4Ÿf-|¨A ý¿Y¤ÿÙyãŽûi3K4.|abTcö+ÚßÇϹž=Í'€Y Ÿj(Xÿ …#ѱ©'8ÓÝV)¨ä7i¿¼ý=üœkÙÓ|˜µð  ‚õ“ð²¶ÿ¶~C{`ÿ!Gtæ ˜ @H‚²È3%àpû†öt|müwÙÓ|˜µð  kÿªù²M-kâ0s óº$…„ä'A à@Œ=Í'€Y ŸjH¶¬ëYؼ,ƒ¶ 0cÀƒè Ê†wJPªmCÛѶum«ØÓ|˜µð  ÆY )Šr<~ÅVæyO8à„gˆ”…FnJ@…ÿ]$ìi>ÌZøPcˆ]û Q_m¿únB$РRàüÑà•#Šý  RQÐÂTnJP $€£Hö4Ÿf-|¨1ÄVÇÖ‰õbåPàÌeçj9p’Ž·­eÀ%ìi>ÌZøPcˆ·Æÿá»ÝßÚÐ M€š&Ož1ÐXA·1ji$ÞßË'€Y Ÿj Hw êM]×n Ž"€ŠN&”ÖÂBÔŪÛ$¾6þ~ûåí‹ÙÓ|˜µð  Æ€ðÏá%±+;6]_§!$‘GøPœPMЙRyÉ@ø9?@`aŸf/|¨1 `=«®lÿ¶ (ô6ØS ' X‘ŽÀ„Ò ¨N2ÀǨGXÄžæÀ¬…O5†Xk\kníÇW·0`&€3 åÑš!@Z R2P˜O B¢ú¨ýŠvþA|˜µð  ¶d@.¿eAóò&zÝÎàCAªBÞù€ ,´ÜÆ ñ5ñH|¨O³>Ôb«cŸëÅ7Û6mc‚–G J(FÜ weÉ@vc"*$€sÙÓ|˜µð  †€° àÉ_»mm‘˘ `ØÏ@ àc$‘=Í'€Y Ÿj¸ªvâ¿µ‹ ÒÔR¶0€Š‚dË͈­‰}Üye'OFð `ÖbÊ€å˜ÿ¨o;ˆÔ²ùEó¤ùb*•z%y(ùñLÁ¹<×7 !鯻nÜÁæèÒ,€dµ  D€ªÃ!ÝÍÀÏú jg³§ù0`2»õ¶­l?¸oЕáæÍg¶ÜºåÌDÎÉ4@ìgª`}1"YuÉŒMZrþjuø§S5Ú¤0ˆ¤@c^xvÀæ0I Pý£ÔýóΫ6/ û^ D ÊN&”">&Œ@Q– €Ÿõ$ÀYìi>L£„Ý0 ÏÁoâÝ/àÖ A“*.?B9û 4¿^GþI ìñéS"ü‘%Ü¥»—ì_ÐÞ˜â)?/ÂDF¤%á&ŸJÒE™…VÖ"Rx?ìs¤% =û¢®ë/ ½4d–ûç3ð|ÿGxyÛt\ÞÉî'G<&@ÅGƒÆ&I°@¥d ‰@  ž=Í'€r0ZØÓùP~ 7ò( ûoCV¤úŠ þ;£JæéH£± ÿ€tÊÊŠªê­¦e¦Å˜ÙùƒÙrÎÍT €Ú>ß»{U?þøÅceY)’AV‘‚xJ³„€e¡¦µN[Yó’³@# ñ»kÇ´¡Ä« êKb­ñ×›/ëZ_g÷‹€eÉì|W ¶Ã¯8(‹€5Ákã@?Ç'€Œvü¾çXk%ð•ý ø]/Åïº÷ v¥…½ÁZ ,2 Ö!Úh0?aÇ#*ô¾¤G €±¼jfÌ#¸Ø~Ôû`全>Èdêÿ¿GBÖú»W-(çñÐfâ–²D$ IˆA`¤`X’ )X‡í1Pƒ¸½¨iH óyL ࣖu=bË*.LÕ×ZÎ |<˜C¾µŠoMAâkðëb~@15>„ßÃ#ì& »øyü~u‚ œV¬3jƒ‰ÂnAX2 ©Ag™–B ^OŒHpý›©1ë}¢ >€¯3¤hGËž:iˆ,œ«,’ÞïZ’°$ziˆ&ÖÔ,)ÄVÇþµž£F ‚ÀókHI/Tt2°ƒ1€’˜€°fNÀha 8})¯ƒn1 »$ ®°Ëø(%DÂήlff@öèýeüNzÿcý%Í€ÉÀ?…}Ó®iVÿ'ÇL SÎM:ZZ;`Ò´MŠ …Ê$0ƒÌ|8“4¬3Ú1½Oh) IÁby ø"/à‰|qàÙººg”ðœ?^ï踲+§ÒØhðTŠ ,õÑ<ظ6 ëc2D—ŒòIk<Ƀ4HÐ?€ï9L™xðd¾€Åƒ´­k›mPÌfÿ´k¯3UÞ\…×J˜„] Ë(ìB@ 9Dlð•¿·•åW;nÐÕ>f <;.Ø‚_%):„fÀýÍ”ŽÝ'Å]fÖÏ ˜0à…H_'ªÿ§º–TNýŸ88?…À~†BGbŽ è˜Ìoã/$Øä`1mAf{H!M¤ Õ¥FR §5 WÅBóá=¨")ÄZã/6­ÚØÜ¶n½{Ì!Ðø¥ Ä£ ¨•´ÿ9†^M®Ÿ AÿÓûÁ2má·…<×4%¨ýòö™$€h⛋‹Øw[¢œnV倂+»´P`+»ˆfŒÉ4®º[L£)²Êg=«¼­Þ3_гÊϘ>œÃíE`ïÅB3€ Í€WÆ2&L±Õ±Š×ì¾l`|ݱaÙ„YÇS äù¿0J; _©Öì¶ÈMº$ Ý:³D;“Ju$\¨)¼kå)¼9óáL‘¸ÓU5c—o 4/WY$$på*®â“P ÂÐ C|¿ohRïK„Y\A ùÒfˆ.–|¿?Ø»èeϱ ø¦‹è\ü·—iqòPò3¸ÿ‚­Ê£ o~AÊç+a$0U:Y*ש‹Ã Wvt°WvÓrL˜‚UÞ¶åðÚötÍØª}ÐÖ3ƒVÉÄœ™Äš[¸ð0š÷#±$‡“Z)3`¢ˆ·ÆÞmoL5tÏõ¿\äÈ gµöäÂŒ\C(¢ÐJiß!È6¡pç¤qIaXGRHÕišF>…c†a£‘Hò^DÁ<  …Øe±¥¸:¿ÞvÕ6TõU¦æS1¯&`÷OvCÿ£¸"gŒi?‡Ê"…l{hÛÐÆëþ ßÿ@¶Ýµ¬ŒÎ3@ Ú´âq$Yã8¾æE(ì¿…Â.;Â]ªÔ)‹¥åIðóƒoŒ ˜Ð3ç¥-Ôx;ß–Ûyg :ª½cÃ;6ý´ŸÖiÇú¿E3 ¤bf@úQ x5£ÅÌ€ ª¡ÿwOß½Õi–©ÿ„Cy¦¡¤ï@ôh9rɾ-@*¥ {^?G É7—Þó‚c>P:eQR@­ë‹ÍÑæŸo½uþ°£ÿ¾÷©½¸ï€D"QµóGZA÷uÝ”šÿü¾}ö1¡#-À96 $€OÌ,3UÎX«ž²ç¸°‡OG—Êu±%ÁL.躻gj=Sí=λlÇ>ë Ó¬òœY{~²`fÀ!ÅD3€²ûpÛ‡š’QÌ ˜à…¸ÕÿjAýŸF› P$ïÀÖß3!çTm-Am-!(¼O)Øš‚žÒÞO¥ ¯ù0ˆæÃ;ô¼=}{~Ôkù3¡@ç$Ûw¶UUð Ÿ‹Á²ÑDàš “%€onfZUF—“àS©±G ™ó×^åI­ÂUÞñØë£Wù ×ÇmyGµ‡z/ ð 3þ MסLY\tRÅÌ€‰j#máägz–Ö–ú?”4ãiކ ˆ¶ß€…@Z‚˜ÓòÞ¸B¤€×/$F´Óަ §5½eMüîM]ŸÅ‹Òý­(igÛ]Û ÷áÞ™>].:7vB÷µÝ 3Œ'IÛîÜ òB@“G†È%aPÂ"'Zå™ÀsÁf+½i'߸+}n•§cÞ”Û¼UÞVñg£=?ià9úâ·ã …¤ƒ²„f@ÍCO3Ê&\ý›©gÛòAˆJÓo_ÎV”ÔJ¤(ç‚d“A¡và9^ì=wö¤Æ|[›ÔýÛÿövÐŽi3}jFšl»c[Q³`J@ÏF‘¥¶åU¾H2ÌÝU~7¾&Î4v®lO—Ú@$,Ÿ^°Ž Wùb)·yvY š£Í¬R:öàʯgùû‘°&“I–@D› œ(Aç¦Îq[”,@S˜7ƒžØ|†éª»íá RΨKÕ{ñ÷}?‡¿ž0±(ÀÚø§ð…®Ç›7âvATÒ ˜ˆÝØÀÿ!Šc,íÀIQGAÙ´ËJZ‘»®éš´ð“€SïÀŽ/uŒvÊMjÜÓ·ÇíO‘K"x}üï1š®gžþ°¬AscÉ`i·þ*_É”÷ ¨ÐÿªÂÓ”%éC9$?Úƒ÷Ãý›÷|ÿ3Lœìoçã ®Ò ýF+cüW¼¸ÏŽ 3·…“Ð,ÙDþT ®v`—43‘Zˆ+òÕ¿ÈÙýø·ë¿qý¤l~| ÉQžš¾Øi»î¥tß]1E ‹š&@l î¥Ô¼qâMYÞd×ó R¸ JlT;©úà9û>æ?̬ù3#mD­,ãø&B­·m]<’\„/öÛxÿð…טs1^gÑEtX߀?VC dÇŸ Æ#‚¯ ð/iqm½c+ô>4ñÛÚ6æ,¤rÖbHâê=7uObBM¿›„D!„è‚Ù…CfB±T_•LÓçëªÚÏE!ìø6€‰‚¿Hƒ(jªdøàÀ®€%oïó*®ü2s'IR˜ f¡¶á@iÔYVDµ÷âýŸ›–ùZS¤iro/À ƒ&\ŠÛÜVãÖˆo ",02F€œVRЄ8’@É@.°Ó|2ȇպ ÖãÞ§<þÍ7ožÐkЪä -kZòÿ@3^‚Á}û!±oˆ9Çü,Þ×D" B æÍEMˆ¾GúL§6àÀ8°…ÞÀSÞÿjv©Hâ"Óôè`‚O¡û K—¢©\T3A #ûðØ“¸·7ÕŪ5pL€O ‚Á7kŧڀ¥¸O$€ûzd˜:Ë0ꨫ ¹½Zl" Â5|2ðÂ’q•½z¿{QS¼œœe)ä!ûzÛÛ˜cÏ *ÌÙýÀn0R£;6 N…[6×<ÌòüVPÌÝÊÄ Cû—;ùä_H£ ƒ*!§>Óë"KŸèÛ§@ßKa$^[ð‘¨i#ðœ“x~>ÆçPÿË4Þ~÷oàF&À>4£ÜêÞ¡!­¬£yoh[(øâMÀÛÓòp1nÔpáS¸”—…º¡×±‹/¸þx- ¹È— xáÅLŸéêÃúÒ=Ëssäô›H¸/º" ;¾³#ÏÖ'}÷ƒ»pÅÏwºIøˆYÚS2 °øºh¿/CÐÉc ÀÀûzÐÎgò\ºè¥Q踼#ÏĘ®HÁ'¡'K3¸à÷I­öø›ŸqW|^1I?õ¸¤’éܨ[öaÜ'ñœ½… óQ$ŠѼûdÂy¨Gÿ9x4à .Æc*¾ÁçðÍ>‡·? DAñþ¹¸?‹AF V / iÐ"i¬™'§gÚü"KFÞ¼vнO6?©Ôå"µ…ß“ÄN«þÎïïäW É2A±xÕ›L+¾C ¼¦“bãœtšBŒïav¨÷]×uçidZt]7ùˆ…ƒùNô³9‚oâídZ‚~üC¶G?ÄÇ®Ùö= ñ'(_&Þ7pÿ.îàžžÿMÜÞAÓü=\„ãïö1šsÙI×xŸC$€û³ðÜ‹oH9¬DM‹!d™F ØD@¬4%–ˆ Î"?~9rÆ‘š(rPœ æòݾ `O—%j»¼­ì˜;©ý$èÞ•h`/ìºÿ>wE§5ZÁU_&'“QLÓþ¼ÏBÿ znƒ­ Ôó•Ÿi(ø^€t›–:¿ÚÑ•¹„%úì}˜JÂ|$€B¡§Â°Äˆý ÷ܱÇ´®àŸÂßøj†À?e þa&øYx ÷îu”7j¤ò‰-øE§Ov.@Àž~BpÎBõBDm€ÔÿO9sZÁ¼}Þ¾÷D4Ù4H~ƒ4‹; [¤$+0=Qƒ¹LÖ"œ«s!¾í·vÝ·«¬çRÁÍ®ÚRCN '‡\ÿÃ}ì6?ò(A‡ðXFÇ ¯.=WË>‡sÃ[²Ìîçei)¼µ×Ú®hgÙ~¨!I×Õ]vqÎÄ1_ Oè³öHwÜ&%¿O}¾ÚSH{á9xßvî½e =­øGðöQ4»‘«…ø¼OÔÅê©ñÆ…Ou<8#‚þÇúëÔ%jßXD¡>ÏÌš‹>ƒ§HŸî# bhÀKÓSšoŠD°€ã0”RH¼Õò˜d@ï\ÂվWÿ6v›VNêžSŽ7Ò…wþ`gžWž¢}vÈ ?ÕÏãFÀ,Fz]~…WgÓ‰œ?.ø‚çv Ðz‘õå"Ð$‰9 ۯܘ}èC"ÚrÛ–I—¹L¥„žu\NrûÞÈqì‘G?‹‚d}jDûÞ~Ó±ïQÖFð±Gqÿ ½IãÁéyã ¾ƒ©€‹3gÎ:¾ÜA“y‚J£" í±P ‰2¼ˆ²@×'jÇü)'r€·ë˜Ã©À&DC)hF"ÛY_sI3°¡ð^³ýI Ë­ð£”Ú®«ºÜûÌæÿîîÍÇó@‚¯â ­,tuGñM§ÀJ#hZž€`Áx$€0È/ÀH $B÷ =y> IN¦WÁ\#GÀ …ž~/-#ÀPR†½(øV¶„GŸNyÐöègám¼MŽ=òê£À¿=†‹.µI3“ÃÉì/úq*`"]v»¹i#ïkÚ>‚øÁΖBÒBd& *Ì7d>ò‘†¤©z#Ê' È¥G œN¬…dwí2À~ü5·´ò’V}Zý˱ýÉîßõ£\ª°‘ÒPX¶3ÿ#ü^&‚"ÉÜ`¯ú@¡@|<@ž¦áQýͼ0àØ$à˜Ž&ø~›oÞâF&¢Õx1 ˜Ð{½úäѧŸšw’UıwÒvì‘Gÿ]&øäÉfãwì¥qq=®hGv÷»O;©½E%À}m»U3ýdÜa¶Ã0 Ÿõ8 é6i«çAÎÂ}Ð9P-GAÈ'O“‡ÙNŒ¾Š¶#wžM$é‡4×é†WØÖ;·±?}_¶ò7Hl"å°ÊB"z‹Vý‘$'MË ûúLû8{  89n˜rD´I@4A÷¹„&Ö»à»ë]P«P(ô¬¬Ð£L‚¡#y©º9Ç_ñO°Ì=jK6}Òvî‘ʯ¡Éü¾ãѧçô>ØK„1¥ŽÓ•$÷=ˆ’G’”DD}sÎA“i* ª06Tm2à‘"Oä€; S,r uhF2p‰ÀCBžv0;ÉÀªGž»)—4CI4ýŸR[(^»_ÅïF+?þ%*¨Àq\ùGìUÿH¬¾ú“)ÀàíDÌ·ÿMÀ9VR °7“Hßß@"èºa³ëŸ Õ¿£½cBQZ"€r„ž<úIM ’\H¦‹xôɺÙì'p{ßöè¿à >ݦMG͘¼þczô'ƒj€û^Näµ"™Ì éÁgìÈÍV9 iŽy ¡(ä² g‘ \¾“ݦïAÕzå¨ÉÞÕŸ¿õÖ-Lõ'…» /²ÕþÆ0H <^ì¬þLèG¸ `Q¶ž7èœg¥÷Ü7ÇÑrspø¦‹œ$ -·nu_{¢¹ ³Êz:–8&Ãà0c†·gö}¡G?Èæ¼…í†\ÇÞÜÅÇ“6@äp²þdPMpAÃ/Æ¿Hu,ªlÏU_„BþüÒK˜Àqf¡ý= baä€9 …(‚ÉFRåG ¬rÞöy†È€]$í»@XÎcÿÔÛ*þÆC$bÛþ6ÈËÞÿHû‘ €áE2Ú‘O ¢ “²/IøS¶ý÷=÷¢vª¯W轚€c ÓF™Àµ€”m ´oêr£ô1’+3,8 À+ôÀ_(éÑ×M`E9CÃ%{ =KÕeíßXâ©ú¦£ŒåèÓŠÏ…~½·ÙqþwËãѯøìÉ™&÷säE$)Ä"Yæ ?C<ÕìÈ“jìDðâWQk¢,CÅÓ1f6¡Ä@¸*']_-/ïÏÃ{€†a/ ñ @~¼ð¨#,ˆ R9/=€ÂV.æi› W`Çàäù!ßðješt,µPfZ@ªýÎgžˆ£s& ÀfpUü±…Þ‘'U—×ä¿ ©Éã©ÆäØÛݻ۽ß{ÿ.4X~3ªùt2§§ ë’ Lý(ã„ßʽeÿ{HÀ Šî±QPÔ (F” ‡!~y ²¿OÀ×QmpÔzË+ô%b÷ŽÐóâ™å鳌½bý¬ëØ£ZüÞ'›ž >WùÉ3ú¾–Ö>šNþd0ÛÀý\£"–)!|O(/@â~9ÈÂŒ(rP˜j ¤ œ úÂW‹ ˜\z k¹j\n›lÖY÷Öœ:MÎ?ªöSqU¢š¯œ4A®·U¼ vÖŸ`9=óÁ…9‚pVu·Kqq ˜ñØ{m‘"™žÏ]®¶Sð¨÷%…Þ³ú›vÇ4Cd‚Ï{Í7~–9öt;Ÿ{ôs9ú)“ÍS‡ÊMÕ­4f+¸Ÿ¯0r 4(çáJÿi<”Q¸ÀSs®ÃBˆ¬7Ó¤„" y`æLðÁ8©Ç¢Ó‹&Nì[{71žÅG­¾®¿a| ÙûN}JüÙzÛí\ýGáoÂU>ŒW©è9}.û6ß瀂ý8GèÁ½í5JùÊ%ƒÌ€ ¶}w§+¬åv8ª”׎'ô‘w<úÔ|CË à§<޽bÍ7Hðƒvª®ãØãá¼ÊÑ^ª{b¢©º•Æl'Nª1ªZD$‡"\„'÷"FA×Oðvœ÷& ÇÕÛA‹àJ®7X§"*D* #Nˆ ÊlyÆ#»Y×_B¹á±ßÛ ÑU<üG…6”öKuýͤ Pm9–•WÈ…*B–mxý¨ÞÂÖ;Çÿ¾ÓJ“z÷ï¸\Ъ+~"ÍkðK¦ê:}Þ|éÁÆmÕûc¨æ“c¯*ýÉ fÀû™ÈÞæÃH9NÍÁçìtc*O¦šÉ®98ËP˜ÌäD@I­’msBd <®¤>ÂWö€`÷ý+7ÿß뤰aïý÷’áê¿zÜdj½è¶§ÑWù‹˜6Ê1& ôÜÖ:nØì’×ÐKC¬JpþPaÛIø9»$y±j,å5)1ôõ&Б”z>pBšÁ.9 ”*J*ÕòŒÀuƒ Ø£¾Ê ’Ð; ?(öO9*jŠiºv=ïìS@옇Ø}ÈOòtû™' K‹ˆoê„ø:®ñLv2PY0¡g-"ôf–¯ö:>&¥Ó¨6Ê+šªËUù£®c‡òÞ´Ësiì;ù>ž þdPËà~Çaˆ?EÎi¾V–ñÈAÎaHuTs@Ñ…¼&%:®¨2ëÒæA= o12°òîJ%9dàôþ—¾™ËØþíí¬½öx|ÞÓ1Õè½õ£ý¯3€KôyÕŸîjÎ1€œÀ“ó/æk“ʬ*”Rï'(ô,bš!Á—ÐÖ§ùF–9ö¸Gß®Á7³æ[x­¥Êm¾11Àý.…MJð‡üþ0²ÖeAÈ¥gY·"'ÕxTä@8©3"ê³Àôöɸ©wärâË)—¥•Ÿ4Ô“h`W š( Ø4]¡/j°ûM Jä&’4ŠdâWuM+DÂ(!^Ëra”лé¹c=9óH\ |+[fª.0þa»åÖ[¸?Š×–1‘æ³s‰ÜïäA2é4)!‡ 9ÉAèt+¢ÒäÏàžŽóšƒ‚È0"à¥È\+ —Ÿ óL¢–;’îúr¼âTÑç âh¶à¬úŽÀžë]ñ=½ðªýì~1çŸ}<û €"\==`È:¨ 4“ҵ҃יwr´Ÿz`tI[Heè;Ëc¥ê’à§Áñè;Í5y"Ï»Ô|¯©³Õ±7ÌEp‘9²•žjÜÈ×hO³ y›”°öæ‹§Ÿ4AbUš¶ O€ Pˆâ·¡=ßÀzå¶tUuÖöë¡Ý܈ ›<ÁIð¬ö%I Š ?Ûƒ}¼|õß¹OgIŸ&à£Á4/ÔA‘’,rcÚ¤æ®þf ¡÷¬ö¹™`"„⾓ª nó rì½í)ΡŒ=ücHÊÇ šoÔ¤Ð{1§ Àû=GEñ|dò‹pO¾ÇO°˜ÕŒnRÂ"&9 éÏær2`¢S‚ @@øænP–òRÙßÛ;ÿi|' ¥Ñ:M6œ(iŒNæ|ŽKc ¯ &Vì<Î=–寧£ Ðvu7´¬åý'M·]«L©Ý Sx>užªë]éíÕß+ô”Ï=úü;ÇmÇÞXÍ7²žT]§«nÞÆ…à½éj¾11_Àý¾yMJBrˆ:ãf½ ‚žšJ5²&Dg{Û›Sä@¶‰Àµÿ!Ÿ  h{ÚÛn¸"«xìùíßÙ>ÇE–qÒ PÚ®ïït€D9ßÊ©ù$àÀuzÜA áwn—l¹ã.à^Wèºu ¨ög/·ò±¶"Xš.EE^)v^)}z,¡7¸>~~ _ÅÒ©ºAOó §Ÿ?h§ê¦´ÌL§êVóÜïíМ¼@(_àB»C‘mE›”F8äD* l"è¸v+D[yV_¹2ÛîØÆòè ´Šî¸s;Kv@ðÚ÷^cÐþÛ(xÛäC} ½ÿìX–WH[=mËËÍ{E·P“XÊ× nFzi¡gß“{ltSy¡X*U—ºêÒÇ$!?b =mä䣿šï“}5èÑŸ æ+¸ßß›jŒlNÑöæœxªq‘öæúq‡ý ˆ€À" (Pm›º¡í ž CY}WuŽûắéÎ%Ðà…¾WQÙ´{_(vq 3À+ø„‰ ¿÷¸³ú3-"Ò[¾s·ûº“í|´õæëÁÈðˆ M &"ËÏä³ÈüÌU;:U7×N›2ö~.U…þÝj4ߘ˜ïà pæÌpš”°ÈAÖâ©ÆY;rn“'ÕxaÑ&%9($‚z$€Ë;¡ójÞ3‹ÎÊ>Z.m»¿›¤ßÛÚóƒÐœN±0 ó5Ð =ýÚ@9°<A1áw޳ýÙ}GýG•[]×]_ëv_oý—Öƒ¦ß¬¶°rfªlÄÕß2rBϾ'RC–;öÊl¾1lÛ÷Iü͘c¯šÍ7f#|(@Ñöæ‚x :©ÆN“’`éöææIî'`d мº ºoÎåïo[?n¿<– ðØ^w•¦0`ßý» BµTt’‹¦kßÃĈÀ»â»÷Ù±âÂïìG…³B@Õ»ãÖ­n0U/Æ×Ov„QðMN¦MD¼ªí{…],’ª;ºù ú0äjðÉÃOž~"k¶§êV>”F^“%¬êï4)¹¸ «1¯9(Ò¤Ä4¹Ÿ@Z$ÃÎíq…™*ýúí÷CܪtKŒ;µ”Ûo»Ô´Ê8iæÍ÷+Iã ß!8¾ð;s„ßù;¦ªÌþw¾ç.$¬íwïð$ŒEä `ýô ®à—l¾A޽ 'GŸ;öÞÁßä= å!A×Lªn¥áÀøÈ‹Ø~‚óY}Ar]öæYÖ¤äܼ&%Þ¤äî»îvh”[!GåÀ[<-¶vüÝv°†AE-Crüôé@á1F¹ƒùÇŠ ¿³/¹úãkD¾L¦N®ð§Ü^„b ‘ €Ë~ʰë,B%=úäØ#þ[î€Ì,¼‰ª~ µƒYÑ|c6Â'€òQ´I î/t§$CA{s;ÕDm ®çÆw5OJBÇ•ãÇÆi¥£$ §(ˆž·‰ƒÌå8ïõWHòÍ‚? BN¸½Oø ¬/ ,Cj,4›€Pnã…@ÎCõ3ä5 (Èô=œäîØ 2Ç^Ên¸ASs’öí£³­ùÆl„OG©öæ‹ì&%Þâ#–jœI*ñ ‘ºÈ²(´­Éyÿ©o~òHrÜ7ô6%ìü‡` ìåZ€×`C÷Gc,Áwn—~ÊΣ‘¸ú{F—•[õè 0Ç*/rV}¢2š“G«ýˆ=-‡åèS;mÔ(GRsòæ#|˜ Û›ãx^x ,rÀ#ŸÃìP<–ußÚ04 Úbîj^nF ûÚÓ›óh”pÇVP‹hÅÔýb„P¬<È*r»h$Àk÷“ð“#?ã–ïî`I7ì3¦4踼cB“‚GG¶ëÝÀá8ø>°‡fìÃýk§íÌÉSU•>¡/øeÀ'€éA^{sÝÐC²$_`fÌßH¥Rÿ_û±³"‘&0Žè .jˆLÃ=;7Ÿ@蹡'ï±Ô`ðÁ]L  ºB1áÏhq\ág÷ùêßöÍnî?z’é2”$¾úgYOØy·§q# Oÿ‡s5U·Òð `z‘9@Bø^x‰ô§×ßÖ¹€BrzšwÊ_•”rdä Ç¡Ô ¹ÇȰžê)c7'9™[×'€1¢Å„Wÿ(š2ž¸¹þBŒ¡ÓŽúÿ+Ü~ŠBÿìÃ0NøýÉÃ'€Ê [[!Ö‹C]·µÕ¡‰Àê†Ú먯édÞleÈ‘­[â­( wæ„zì¸m+¨IH…yD±€÷xžÍàªöò¥-ÐCŸÉÉ(ÄÇu]‡¤6Tžç?ïûóðÀÿiû£PBuI¡^éO ­Ÿx|ÁŸ$|¨â­±_DV©¿ÛyCÇ瘉+u"±¡ Ú캘Ž/—ç $x;¨[ðÎ;na8É›„@i' kN2Æk—|ï}fóÏ”T®¿k»[±H(·ÛQÑsVÊ ÈücÖu7 /áíãö êгó;ONí—šßð  ÀÕ=®þ?íùN;È’â×ÒIH&“ è°ùŠœà­ìíÿ?¨Q9ÝQáÀ³ív‘à¼:Ä…y^!)Œë,|úŸ#ü¸)ÑfèBíÃ+üä—Ør[yߣFÀ76®òNÝ~ÖÎîÛÛn3k>‚ÇßÚùÃô5€IÂ'€i f½¼HØ[×ü¹õWÄëœã¦¥CÒHBJK‚>b@$Üë&—0Cþ¼ðÁ1-¤* ϸšaÜTàÂcÙÑsV}vœÚ›¡)Ò…«³Í P.MÄë_ˆ¢`è<þOI?<Å—2ûFø7$¾ƒ-±– ’çé©þnó>L3ÐNïF©û‡[¾³1 ²{\3¸áê¯k¸bë`¥eØzåÝ|¤7”_!è@Y¤°B!/ 9AãÃö?Ô rAx°¬"•‚…‚ï=FÞþ–/wB;Åú=U„ÍèþZ7ðA­S8w…p'‚(‰N/~š¸Óï¿ÿFþ€÷}?ÀäáÀ4WI–…dÛÕ-’3"› g’LøSÇ5Ð-{¶ÓDÃе¡Ç}ÜDígR¿ixˆ“^ì€2ðúþi'/ °ù¥:[«]Ý¡pÕµ@û5] ,É/jTBŽžxý¤QŠH«@m㌩´×@x àßÌŒù4þ‚&’À©)€yŸ¦¸úo“„¯o¾³3èvÙAÕ?E+ÿqTý­$TØ‚´™ÁU-†[®ØŠ­ßù•Næ'(¤†“c°Íƒwxaöþd7h¸»ƒPI_@ájOÿ#Á¿´â¸êGVEG½>56¥¨ÄTÔþ¼óWJ ¾øO +§m3à þù?q{·ýZJÓ÷ôíùÄÿO>Lb—Å>/Šâߊ›"ÍöQ‹Ùý´úë& ?­f5¶âÂh(ζÀæ+·1çÔ鮫&nKÓ Q²™.<^'=14ûŸ€Ô¾A0ÓHBf7 Ý>K]&ÆH´¹è둪O‚_N5ãDà%"ÃÍ×mF3ĤþÝ(þ+û•dêãGýÁQ½aðÀ[ø·ü\€‰Ã'€iB¼5Ö^¦|éú›;ݰŸf«þ­þ(ø­þALg )ŸQF™‰ xA~RÇËi6BfQ©-å„x'¯S¯(ÈÇðp/ó=LÕÞ/oå##€klÈPá>’ÀBƒ|¤êÓX®îÇíI4~% bZUÕO|‡àÄàÀ4 ¶:¶F¬Ÿì¾³ ÕTn#s¯…_Ë©þLøQpD{î u«M)lkuçe–ÛG¯"—D˜A‰CS§MÈrR¢ÈC¹ù “A'š=7q"t€ÞŒcxþê« –I}¡?/‘@ž3-s¸)Òt €Â×Ê„OSG ¾66]Óô›WµÙa?TýÓI¦ä©þ$üx‹ ígÒè*k€æ@Ï[AmŒ¸/\Ρ±@NÂõ¿¿žË˜ÈHsÀA4(S‘š˜NÅÉG‚]Žs³$HºÁ[‚+„çpÅ×ño¯ãCizʸ½¢gôw›W4ìkåÃ'€)…k Öz¾³‘U°c…ª?­þà¬þ íVáòΛHiÒ Y*l¾j;‹ó;§¦!ãM*äcˆ¬ˆ°°¡ºX¥n:\í§Þÿdথ5ÐŽh¬q"‘˜ç^×Wº ûÚnhŽ5ûØR@=Aõc:O ¦S³¨ç€ÉráeÜ?‰¦ÁH ¢öDÏòµ€2àÀ€«ê9²,Ž_ùt|]œK ó9¯Y¶Ýש‰+?Sý ÍlVÝN¦’G: j¨z®Üš·Z—;Ih¶¡ûºnfŠÐwœ l¹f _õÑþgZ€Åµk1jáp–„ÏÙ$Ïâ{<Ž$@iÂZÓ%M¦¯”Ÿ¦\ýoÅUôÖžït.ƒ<ÓÞUý“êúhÕ¿¬w­í@m ŽC×åÝnd€@½õv|gÇ´…Ü* šüæhÛ`û4¦HÔnÈJ# XHì\빤,QÈp _ÿU$‚'ñö3ø˜ƒø\ÃO*>L±Ëb‰"¼Ñþµ¡ùR»ig)Õ_0J ¿ÇÀH@†p(×_±%/מ²©@f¼nÂ3 j^B!Bg¢Ãd à«[¸†DÉ€i~LÇVM–°’"F³‹ê(è—HãþE-¥½MS{ýä ñáÀ$«ÿ¥FáÏ6ßÑɆVýS¹_1Õ¿Ž?ÀÈ‘€T¡ûÊ7²@ ðM.gÐFµAÉH”‹à%-ç»M–„¬ÀÇujTäT—¨T ôn$-ài j#Úû~rÐøð `ˆ­ŽEÅzñÅ·ÅMK",g>eì·UÿTùª!˜?@`H¡2'%èjß ÔOÐ š·wû·o/kàF¥ANEŠßG‹d :ß«,ØØÉò–aÁÖë¶ò¡'D)Œ@3ˆ¦ ³°àY’i (õÿçaÁ <û·tCÏøÉAcÃ'€I Þ{R]©´t}ƒ'ý°"ŸLqÕŸyý'òâ^HKL Í }mÄcù龬ðþ]ÌAX‰Äœñ@‰G7m„Î+:óCŒ7W"«rf%'÷½ÍO“’°ó¶ìµ]1%r墠ãùõhd￟R„Ÿ¦¨ðÎAÇüä ±áÀ«ÿâêÿoÝwµÒ ºªŠåú¨þ ­\Èo"(4 N‘ÅQ$‚Î<“€@D@µøD•LÔq@‰F4î›Ò s /% ïG},ygË÷sÙŒdô?5¶Ù²ë‡» ²Üž†üèìþánT Pø‰ Ø€‰š•ájXjÂ"áŒÒ Ð\¿wðó áù{÷Ï A!©(ŠŸ4|˜"Ë"g)‹¥ѵ꒎+;%UÁ(Ïî ^ -€4Ò P bÑXßÚ9ÚÖ¾òRÖù¦ÓYHÎ=J(";?ÏÁgƒV÷=÷îÄ@Â=Ö}g·;#€r hu/Å(lwÖû½^|j€¬! 6D5 ôÏ~Ý1³ä ¬=àóP’€™5_ÁcGÑD±|- 8|˜ð"ý¿qe¹ë–ïòZ®úÛé¾¶êoâÅ åxýËC;YˆÈ@—ÙPLâ–4 ÚÖt”Ìá'¡z~÷ ²Û”äSNF%")‹Ö½¸iEÄ.¹‚\ʽïuú÷¹ »¿ýk¹öeT:LBžx=GꤖhÝWw»…GÚ! vܶƒÛÿøÝM΀»ú[,$ÈnK…ϰä >è¦!|à'‡Oe"vYì|Q„düªæóhµªˆê_ tÙ’'œV¦ d¸I@Ú ?‘@óŠ–¢A!h¥f5övöÍ.¤Y{ô:l¬vH,ëu´a €ÁÁQÈs/ZlŠ´ÿe;¨±ü¦%”q˜I1WÕüŠCüûŽ[w°×'Èhþ„µ0÷dM¦à3G`Ðöh,6(9è~~ Òt g©s0î‡ðyïøÉAÅá@™@¡ÿ{Aº·ÜÕ‰—žÉkü+¡úCÖÞhi†C¢Kô÷ðbš—µ@tE”jç§ý°²â} ØûÈ^¶J‚…ì äã2[¹ÙýŨ©ÜÖêJuü×'_ƽ}0øØ ûzò1”•*þÚ£V[#0$ƒ'5*DG7 ' <ˆ¤§ûÉA£á@ˆ­ŽýšX/îo¿)º€VZÖÞ‹’~Žk¬ÈgB ?ScÀ“€j±™&í@…Å´rS$µ‚(«éWÃjѺþñ@Ú zUöDÉ.¥çÈr{&èÔßà8j‰­ÚDtÌ’tÐÂ(_R íª6@-ú^ä;軿Ϟh¿.©ÿÇFž»úƒmûã9×=d`4<9H’iô÷[¬s ¾ ¥´;9È zà@ˆ·Æ/•ÿÏëoí ê[õ7+¬ú—‚-t&3 ìÝÖ²69°Çå }.A™z/˲3^›©þ¸òž¡)ÆŽ*O+½qÜ`ž|æ´³ 'øž«úôa¸à“À2á'ÓÄþ´2ka$ÉEøz &ÈKdæP”Âܦ'RIi)N.0矎MácÓüµÙ뾿œ±U~Gð==)VO$ ‡uN‚É„u,°Ð¾v8 ŽÈÅûš^õŸûr¡ÁÔ’T.9(Èf¾H…Bxû—45XUÕŒÌÁ'€±Q_{9r™ÚÔyMGÛÙ×DÁ§æžTåGs¥Uÿ˜¤à¢ð[‡È ™ÀÊ8«¯YJ÷%ÞHð¤$å¦ír•˜“@’@P )nd‘Ááª9­Ì¢a¯þ‚ï…²I@ⶺ¹ÐÄÏc7å“eÙ>F,”ñh5ÎëÑ?' èÑ1„¸C%A·û>c ½ªú1¿g@>Œxkü«¸ÛI>èB%HÙ!¿ª«þ6˜ð“à“°SDö:ß›dPR,­ ´êÓÅõî»È‚c“è²7"„:?,Èá@ D–EBÊbépìò¦†øºX •v:ûΠêÏʆ¹ðS“ Ë^‰io22´uöqê9¨r[¿ÁOprc¶©ˆ†n~ àBÑö?ÏÈ Û/m‡¦p{"+MFrÑ   ð¦,Nì}¹Ú.dÇ9A›ìByŽI"fE×¼p_+;öË®þNh‚)5Å“ƒ‚â@#ă°×N~™FŠùÉA>”®þ·¡|sÏw6.0PèÕß Çß ¨þÌî?n«ü ÷†?V` Gà  =ÏÂ\ݧ‹ýÜhÄöxì=¶'± 2R ºdzðÿçâJy±•±Â=k{XÁT~ã Ù0fkTóÄH`üüz-¯ @`NEöéÇ|ÜÈ_ý N:aAêô.PrM¶Ì½–eB-À𵟊"¶:¶X¬_o»6rvd…šê1Óª¿k÷Wýíc4c€$PLø3(üAÙÉ á?…BÀ§ëYíüëx?‰·ßÅ=‘BÞ®ÇÛîÏ*aÊÂZZû"î}ˆ- 6„Á)k¢$=ik ؾñLéFÎh晚 q-ÀIÊÂ0~·çì:A$8j*:ïŠøP¸úÿ³þ¤ë¶¶ ahùª¿·³o5U=_õ%üY®ú‹ ¿è 9ÂÏ;çá5{ºîKxû0Þ~ßÌš¤&3ç¯ÇÛÌuˆÛù¸©ZJ»»b놭,·€¶aƒ“@Ê&"%r @UD~h0çÔ4V-È’ƒ‚Œ¦(9è);4øšžÑçýX1Ÿ €«ÿ¥¸ú?·ñ/£q‘ :SýÇèì[a¸!¿bv¿#ü¶Ý/fI—¹Ê¶‡?h ?ÀA¼ð_ª™Bmú£¨ ›Œ €«C!@ùx<¨4(çâaZö›þÿë[/ßpŸR¾T2æˆÉ5c(Ç0­°òÔ71M´ä’¤“”¡0 >˜'4ÏŠøP€xklox¹üÛ×Åïì[EÕ?ËUÿ<á·í~Ú›»_¦v¹–ÌrëÜðž£ö“ÊOùµÔ6k¿ÝM÷£Ø{-ÚÖµÕK RHÄÏàã âo$Iú,šÙzJÉ;FÒà$ á*lûȬÒüJ Th´€”’r:Q¼ä„§Ð Øoe¬ô|Nò À\ý;põÿñÆ;bÌIUr¨GµTÿã{¿„ÝOƒ¼@ˆ•P…wVþ™£ªá²0d'Âü Wx²}3㩾[oÛZ7¸o0ˆ+çyø¼Åxè É‘äVµAýô-n 8ùä dNÁ‘B§àÌù ³K<,V>Âïò6ùÀ(‚û7çsrO6Ô¥êÙª>iU[Ö5×;Ô£Â(×î—YxN)~rìQ*ìaÜèb'µwÚîo£z¼\»—H`ïÀ^Ò>…w?‹Ûe©‘ÔßD#ŸêÙÐWè¹þÇpH Œü€é_ýs5äLJIH-faÁ“xŽˆJÚaArî›ÏÉA>؈·Æ7ãÅðÿvÝ«ÓqÅ/6Ôƒ Tÿ²í~A1m ?Oô¡ ˜T]ºÈ©5uÇù%î÷áç>‚jüñ‰ª»DÉdò,¼)¡°_Œ¯÷»©TêÖØ’Ø9]ëº8 Ð认­xI S}§ 74è-N4&h i'(€9CŸÀÏNeÇçë@Ÿ€ øhe!»R ©Q…©þªþãõ¨Ʋû3ä|ãvQáç!=þ7íì·_RW¼ÿ–‘6>˜¬­k›gË”˜5‰µHߊ/‹Ÿµ±u##ªâsýÞÈ@¹IBÓˆ¢5¡$Õ¤3Pä; 2`缌Ǵù˜ä"ÖÛ!‡ÄkÚo)égBC=¦<Þo«ülµ/Pý)Ý7È…UsVÑ|å'µþæé¦qY€ÂOvP|“ž=Åæ˜ö+ÚiîáYBP¸…EÕ3ú$•Û—·/èXÓÁ§"èSOš&Ë$-ÀZdñ"”„ê¿ø ~š3øÁ| Î{ˆ­ŽEÄzq_üÚði±<=}'‰<»ß.ôåô£DŸ´:Zø³ldöÜï`j?Åú“hßÓdß2@ÍCCòù(0K-Ëúc$¿Ø¸jcÝúK׳Q’•´ò‚3’$4:4¨…4H\’ sw ¿ÃqQhþÆ|Hšw»,v±(ÂÁè•òYòRaæT§µ—QÌî§•Ÿa÷x+ûx³ËðbÝ*™'fêb=sæL ãËõ‚ ,ÄÕ´E‘¶âý‹ˆ¨$)/IÈKUt æ7áZÀÐ%CNr 9LmÃl LAm®™w€«ÿ¿ п6Ô³3¤ú;­½Œ"v?} šXª²TÕ þçqÕ'á§He_ÕáÔ !áêm—+îyµÜʾjÁ[<„Ÿýb ÿ’J¥þ:ÒYØsyOÀ1fE’·}% DÈ_qMJ¦z“ÕOð±b/jiím{ ÈœLšgû¥´TŒ6_!Gµ÷’­ŠgûµûmágÝ|í)À%+ûre½¯¢Š:B•}»Ø}r6%­x‹‡p[‚$Њ$ð—ÑpTè^ט-IB^‡àÞÆ½ÞžïÙ=XϼÍ™÷çjrм!\ýÿw÷Ǿ&°Üþ¼Î¾3e÷{Zz3áOQÙÇ[yQ¢y¨G¬Œõálmdá AAFs@Õ }’ÀMñÆøYíkÚsþ€Lò6¡B¡þeý4P„’ƒ>²Sª)¬ú8Øóø{¼…ß!3“ƒæ ³ ªª _f*j«\—7Ôc¡1ãvQáw*ûH%æ¦Ê>*ò™Re_•À²Q˜Î63æù¢ ~Iëñsßж´mA[´ Œ×‰¸)àM‚j’€'4˜' ±8ÁŠðä ƒÎX1àY–Çæbrм \ýÿþ ·ÇnêhÕwUVågMý ÆAžÝŸñ©þiøµteogMkˆ¦£²¯ZÈÕ âøÙ©nàO‘þ|ã²µeV$ åzhЇZ ò"ä\Þ?ð9ª­Påø\Kšó]½P^(V×Á9áåbžêÏ„¿ª)»Ÿ„¸¨ðG9$ü•©ì«œ2by€ê.AÓåÿ2 cc×ò.ˆ(‘Oò† ldaAž”åÉAøù_±²Ö»s­gÀœ'\ýÿæWc_ƒ¬½—£úËÆÌÚý¤ö*QÙ¬o/ëÍÂ^|‡+PÙW-°º!$ˆJXù4Þ§º¿@"ØÐ½²Ô:ãIBÞÙ}Ëú(,È“ƒx¶å+vë0"ŠÂ|0Ó!×éÄœ&€ØêØ ±^|)r¥Y'4‚Gõ¯Rȯ”ÝŸÆûšŠWö]Üd+ÿ`…+ûª·xH ‰a6‚„v#’@kÏʃòŒ' 9¦@RÚý‘>P„wÆÏKmÃÈ!H&ÁœJšÓ€ÝÏ…°ù»ÑMd}¯5B~…£¼œý1Ü¿ª¯ìãƒ,ʾ«TÙW-xë(Ç9¢hßBµúÒ[–÷àï!çü”×XõNB¹"ýKûÁP >P„TyȘeS†"ë³%÷bª˜³€«®þ{"W¡&×`å:ûÊU ùsú9Â_XÙpÂÎEO8Âog¡U«²¯Z`$ … þEvñÐÿƒ·#·,¿% ZâŒ& 9¡Á¤¤Aߊ>ž$ɤ³‘3–4²Çq{‘ áÉA5ÿ»ÌUX_KHKásêk›íW­Ÿg„·[çOÂÿ’ZllW®²Oí¡âž×f ²¯ZÈI²;tï_ܳ¼'ÀÎ× & 9þ€þ%ýT’Nç ò¿$€{ É9aÆ{³<[æ$ êîîŽ\«€Í¡÷xý+Œ¢v¿Î…ŒÊ>*î¡ÂžÐî<`fÌÔ UöUyÅC‚Hu_`ÅC \ؽ¬›“À & ±þ‚½+{)9è ëD]—xåå¶æMu±úQ­ÿFsŽ¢+£’, ‡Ã«-YŽWýIøå*ÅûG ?{)ºú”¨ì£n´”uFùý Û˜ñâžJ£ x賸ªþN*•ú›p0,w-é @š´‹Dø°–µ€9E±Õ±Ï‰õp ¼Áª——:ªªz!?½ ¾Ÿ„ß(9³íâ•g´šPcÏYSÙW-xH€2¡– ùó{¨ Ü"g·‡Ûy¡Á*' ±¹‚ èô{“ƒ(`Àî@EŽÖrrМ"\ý ’ù‡‘«X@§zª1»HÁ‹ÖSÙdBMÂOFÁa;Í”}öÍÖʾ*!€$ â!\eÏ·²ý7#môD…è‚¶†¶À¨ü€ª9yh´Ö3 Q9a¡ M',H­Äj69hÎ ÿjÜ=¥^‰†cØb^Êö«tªoQ» ÿp‘ânGOíå•}†~d6WöU nñžŸ ðÜ|ÏK’Àu1!V—⣒„¤*úH  ° K ‰²ß×gP+ñA=£kh œ¨Åßo®@ ¾66(4ÂrõŠ_ý«òË~žâ[¤²ï»²ØÅó ®üoÖ@e_µà:Ò€÷/AÁÚˆçç¿Ç…x Šæüéê& ‘? wY/hŠÆ“ƒœ"|¬µ­V“ƒæàêÿg¸»W½Wÿ ¨Š!?Ýo%–èS´²Xq›Ù‡퇡v*ûª·xHÉŸFÁºDOë]†a\Ñ&´A$™±$¡d( ÷]z_.9ˆkQä†R„_02Æ‘ZLªyˆ®Œž#ËÂÒJëB¹Õ€ ±l¿Jc”ÝÂo½T ü”à„ÙÅÂQþ’5›ÌBm]½ÖCH/„sð\~ZØÐ‘n4“Öµ í â¿™H"S oiŸÓ3ÀI:à(R‹ÉA5Ox±ü%Í¿R¯MÕ0CvÿëXÏ6]Ù—e«Å®‡Ñ4x¿Æ‹{* wènâ¹£¡#=–e­Ù(lÅRò’„ªå f¢;Ví(L´ÇŠñä (RÓ[SÄzxCj5Dy%ÅûSÕKõuÔ~þ§ *ûxq=êÖ_ŽêDµöÚµïÏ‘âžJ#oèž¿&#mÜ‚§.ÚìÉ”f$I¨¿±.àEQ·s9žaþ$úZKªiÀÕÿŸ@07©×&ƒâ½ý* ož¿5Œá£cVö‘ð?o÷—{}UöU …ÅC+Pøk<…— ¢Rå$!r’2ÐX±l<ï×H#ÆŸÅÏv˜|µ¬Y@á_‰»ÁðåÉ€¸UÿEFÅßÓµû™ðãöhdìÊ>`“{HøÒ¨)˜[•}Õ+²ë¨x(ÊŠ‡²ÐØ y$ eª3tt(<}‘>g È,©+ ¶)ð2‘B­$Õ0Äž€0´¨›põGÕ¿šv¿•Âí~(RÙd•}¯Ùóú^@Õõ5T]ßÅ•áÄ¬ì« œº¥QYˆçu1Z•IÞ.˜BCtxKuàùFuL«v‚!| OÚÇR„ö"ÙS}ǵ¬I@ÛÿKb½øïáMI/IVe¨Œk÷£ð(ü-RÙÇ›G¼f¯úT×O%¾D36¶k® oèˆ RñPKj$u›tR:4æLÛ¦@¦ò»/Ê‚§ä|œu¢üž"<ˆ„O©Ýæl(RsY9KQ¤„°ÌºX¹"± *v¿ïO£ð?aÅ=E*ûÒ¶CˆJFiÛ?_*ûª…¼¡#A¸ØÌ˜ÿG*•º5|2,vd;r$`T'4Ø»¼J‚®…=ÉA4PäiÜ ‡ïlOï®9@Ûÿë(’¾6Õ*ØýNKoGø•}¼ƒ,í¢âžýBPxÇÊZ󦲯ZðÁó¬â9ÿ}$ÿ¡žTëÛ¬¶€LWÞ`aÁØ'9ˆ ‘#¶é÷8Þ~^ÑŽ!á£Ùì÷©)ˆ,œ«,RÞ–.Ó>%)Qù–ÞÙ\ª/þT‘Ê>>¶‹*ĆìʾWçke_•P8täsöБ¯GÌÈ‚¸ÏåTÁ@ƒÔJrF¸‘ÙGÉAOâu°ßÊXéÙ™Êöd{Aì×!a0Ãé0N%R½·ÌÊ`þ×5Å=µ•}Œð%Æv‰I1ª²¯P)M *z B`Ïhq”eÝl` Übª0yýÇw3{ƒ]=Ê,<ùÍ0ðü(G‰QxÞqEåXãuW„€‘ÍÀö{’»üC°ïfý%+‘y̾©_Jv¿Ú`üˆ•}æ!æ§)1äðûÖsUe_¡R<!°BàA÷üE÷Xi,‘á™À(ßÜ/Ÿ2&Ùƒ_1-¥1ÝЫÎàà`ðe«7ê@Q2 ¿ãŸÏ¤3›Y3Œ«k¯´‘bl˜ñ>›ÛÇ@Šç´GŠLZUe Ùýþ¤ÎøçÃ++ûjgöÉÔ í¢*îi*T‡Ž¤3[p}¨X(¾ï{þ¶1»F¬äè£>Ù)¦§+a˜BíƒDä²p“óöÈOØsÔNî¦<ýÅ ‹z·º~j(I)¢æ€y$î=œóWñÎßfioqp²#Õc&ó˜ÿ3YÜÓ¨²ˆ›5ý4³o*༕·ó7ꇎ€‡lÛ>¦yZö@á@‚|¼Øi[¨ü<Å«ŽàªÊï Pp0Ð׳ø[9üÍŸeèB4ø "ú¡Ã‡’xá”!¦Ã&† ø˜•½íú 0 žøo“À™Ñÿtˆi,Suê„•}A.XÙwëœU°æãØþ©]P7t¤_ )ØG!º…€ðþ€‰­ìÄŽË}¡Êì Jú¡™Ór +s؃%ßõ¯ÈhÐkòýÑ |ÙOŽrÅI4`¾màe™3h”3à]ИýÉPãÊ>ƨ„w:ÌïÇF¢PàDQ•}MŽCGöÑÐÝÓÓûóû†m4üµ±ý:?Pèå_”ÕçÓ*?UÿýZ,9„…oö›×›Õæ¯G+ ñ<²Ç|’?E-¦A”ç!Ž2ÍK”8´£š3à̓ù-¯ì l;b~ÛE£ ÂÊ>"¶ªì‹j‡Ž@ `?< áý®±hh$êýËbû[ElŸ~Lû²%¹l6K)ß4$TØþIö;eâó«qÓ [MTž‹¢¹™Üzq£çz;í’ýˆ5,ÒˆŸµ˜çÁÆ;þ8H𠲏’’ص•}ÄüÉÊ̾¢ªì‹*CGpßCÁÄ^xÓtÌäèìh"L¦†Ÿ“ý“¸§Får?®Èj?RùÃSšÒ¾±o.:®ó§Lýnz•Å Šz·óÙD” Y畽»!z‹‘3pXÛê$ü2ž½”Yó_Ágd6ù÷ÌJO?eùU•}±F£¡#/ò?²ËÙÕ¹7¿—Ž3¾7Ží‰_äD"ç^N:ûèžöÄßSÐ/'êÜîç£Àº`u‚ùƒœî<Å]þ>ë2zŒ\*êÆ}·ˆæRH§Ì~‘ ÏÈÊ>Šý«Ê¾xcùБ$tJÎ+œó×è3øëº¡S.‡ø×al?8 ˆþd †Ž¾<#ó°Ì\œü×â¦ò¯x1Q/àN='¥‹šƒfT@´rµ›¦ÔQ\ïeÔW(Ý5-çö‘šW€XP•}-ÊÐÈ^ìûiüè½§>ù úpŸÂ5ð•…(Ϫ*? BÑ.^‚™àÇUå_ñR¢^À|VÚ¾ë'ÜŠƒÐ&ˆNI#ä4ä0,œ3øü7UÙ×r¨AìÃ÷$üý‡qÝŽï7ÉÜb~àš«í[³ÖÕVе“¨ËØâ’ @îà.0?5x,ÑÔ؈®bþ–Dà žÊ‰¡#0ÀõaÐ}'#S0ßN)½tâOcOÌù¾«Øþª^FÔ ˆê¹CmïÂi°’¾„&'ßU¨„Å)–«°jT‹‡\¯B`÷&Ü„«¦/B œÃž(r—_Ž[lU/"êD ™6Ú{Ž… ¨wKPÿn5)¯ÐBHçð&™+@NB1ÌBà2µrg1Œí¯ê%D½€&AøZ’È ÿŠÀ9<`&Áü]Ðü:ý²Nt¾&Oý–> ”Phw„ÉB^€F¸ÔêŒ_yø¨   ”PPhc(  ÐÆP@A¡¡€‚BC …6† mŒ@øÆ*¼õ'ÂIEND®B`‚(0` JKJ010ZZZOOO=>=WZXU`a`“_`_*,*POP=_Y_Ï`a`RSRVXW6sS¦¹^þ>oVþbbb·MNM[Z[3Œ"Œç¨ ¨ÿjGjüabaÄXXX9OOOALF4 ’WÛÉbÿÊbÿ¿_ÿEjWýbbb«ZZZLKL'ƒ.ƒÝ¼¼ÿ¼¼ÿ·¸ÿ~.ÿ`^`âZZZ3#rJ¡ÊbÿÊbÿÊbÿÊbÿ·[ÿJdWüabaPPPSTS{;{Ô¹¹ÿ¼¼ÿ¼¼ÿ¼¼ÿ³³ÿ_U_×bbb=D@!¨[÷ÊbÿÊbÿÊbÿÊbÿ ªWÿPaXú```‘EEE MNMvFvǵ ¶ÿ¼¼ÿ¼¼ÿ¼¼ÿ¼¼ÿ~-~úaaahDED,lKˆÄbÿÊbÿÊbÿÊbÿÊbÿšQÿS`Yøbbb„fffIJIrNr¹°°ÿ¼¼ÿ¼¼ÿ¼¼ÿ¼¼ÿ§ §ÿ_Z_ÅRSR2?9$“YçÉaÿÊbÿÊbÿÊbÿÆ`ÿ‰MÿW^[ö___u;<;_`_ pXpª®*¯ÿ»»ÿ¼¼ÿ¼¼ÿ¼¼ÿººÿp:põ]^]R9bNg¸dÿÊbÿÊbÿÊbÿÊbÿ¾\ÿzIÿZ^\ó___iPQP:;:m\mš°>°ÿ¹¹ÿ¼¼ÿ¼¼ÿ¼¼ÿ¼¼ÿ’“ÿ`_`®WWW 656<ŒcÑÁ`ÿÊbÿÊbÿÊbÿÊbÿ±Wÿ#lFÿ\^]î```\RSRiiik_kˆ³V³þ¶ ¶ÿ¼¼ÿ¼¼ÿ¼¼ÿ¼¼ÿ¯¯ÿcEcí___<Žÿ ÿY:Yù```cAAAc€qvkɘÿ ¿aÿÊbÿÄbÿ_®†ÿ²·´ÿXZXÿ+-+ÿ(+(ÿ(+(ÿ),)ÿKMKÿ¨§¨ÿÒÏÒÿ+.+ÿuvuÿ™q™ÿ` aÿ][]¿aba=ded^^^xX]\ß¹½»ÿ(+(ÿefeÿ“”ºÿ9;¾ÿ8:Éÿ8:Ëÿ8;Ãÿ8;²ÿ8:˜ÿll™ÿ…†…ÿ(+(ÿhjhÿŒÿ[\\çddd‡^^^'?@?IIIjjj+aaa‹TWYã7GUþ1Olÿ›ÿ(+(ÿ ¡ ÿWX«ÿ56Öÿ/0âÿ./äÿ34Ûÿ8:Éÿ8;®ÿ=>Œÿ±±´ÿ(+(ÿhjhÿ•œÿ Ykÿ9QYþVZ[ëbbb›eee6MNMSTSggg8`_`œOUZë/IbþL€ÿ[§ÿ%xÍÿ’”“ÿ(+(ÿ¬­¬ÿKL¯ÿ-.åÿ !ñÿóÿ)*éÿ56×ÿ8;»ÿ:<—ÿ´´ºÿ(+(ÿhjhÿz¨¸ÿ¡Öÿ ‰´ÿnŠÿ/[jÿQZ]ðaaa¬jjjFRSR ddd [[[F_``®JUañ'OyÿZ£ÿhËÿtéÿ{úÿ„úÿ™œžÿ(+(ÿ£££ÿXY¥ÿ23ëÿ÷ÿùÿ#$ïÿ33Ûÿ8;Áÿ?A˜ÿ²²´ÿ(+(ÿhjhÿx¬½ÿ»üÿ¼ýÿ´ñÿ¢×ÿ‡°ÿ&j€ÿK\aö```¼fffV]]]DEDaaaZ[ZY^_`ÀBWk÷X–ÿhÊÿtéÿzúÿ|þÿ|þÿ|þÿ}þÿ¦·Èÿ(+(ÿkmkÿ˜—©ÿ††ßÿSTöÿóÿ)+êÿ56Öÿ9;ºÿno ÿ‰Š‰ÿ(+(ÿhjhÿx¬½ÿ»üÿ½þÿ½þÿ½þÿ¼ýÿ³ðÿ¡Õÿ}žÿCcnú^``Í\]\hfffEEEABAbbb]^]k[]_Í9[|úd·ÿsçÿzùÿ|þÿ}ÿÿ}ÿÿ}ÿÿ}ÿÿ}ÿÿ}ÿÿ‚»÷ÿHJHÿ,/,ÿ¶¶·ÿ€™ÿooÕÿ23âÿ45Ùÿ:;ÅÿSTœÿ¼¼Àÿ585ÿ(+(ÿhjhÿx¬½ÿ»üÿ½þÿ½þÿ½þÿ½þÿ½þÿ½þÿ¼üÿ²îÿ“¿ÿ:nü\^`Ù]]]|jkj!MNMSSSÿËEÿÍDÿÍDÿÍDÿ˜?ÿ__^Õžžž`¼¼¼êÀÀÀ×fff`GGG786Ÿ[:ýËEÿÍDÿÍDÿÍDÿ@ÿaba½[\[’’’HIHŽT8åËEÿÍDÿÍDÿÍDÿŠE$ÿaaa¢/0/ŠP4ÇËEÿÍDÿÍDÿÍDÿ„J-ÿaaa…€K2©ËEÿÍDÿÍDÿÍDÿ|N7ÿ___h{I1ŠËEÿÍDÿÍDÿÍDÿsQAýVWVKkB.lËE ÿÍDÿÍDÿÍDÿiULùeee1]B6NÉE ÿÍDÿÍDÿÊD ÿ`YUÑQQQG?9aA4fb@0fb@0f_@1fOMKÿÿÿÿÿÿÿ÷ÿÿãÿÿÃÿÿÁÿÿÿÿÀÿÿÿ€ÿÿÿ?þÿ€üÿÿ€øÿÿÀðÿÿÀàÿÿàÀÿÿð€ÿÿðÿÿøÿÿøÿÿüÿÿü?ÿÿþ?ÿÿþ?ÿÿøÿÿàÿÿ€ÿþ?ðÀÀ€€À ÀàxÁÿðÃÿÿðÿÿÿðÿÿÿðÿÿÿðÿÿÿðÿÿÿðÿÿÿðÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @ CCCUUU2bG{'uNâAAAuu;uN}"~âK>>1333ªk«â¶·ÿ¾¿ÿ¾¿ÿ¾¿ÿq!qíDDD^­„£Ã^ÿÉ`ÿÉ`ÿÉ`ÿIý2YEÒAAA'£e¤×­®ÿ¾¿ÿ¾¿ÿ¾¿ÿ› œüR6RŽ;bN W²‚ðÉ`ÿÉ`ÿÉ`ÿÉ`ÿlCý4UCÉBBBaɯ<¯ÿ¾¿ÿ¾¿ÿ¾¿ÿ¾¿ÿ`&bèDDDT£z~&´jÿÉ`ÿÉ`ÿÉ`ÿ‰^ÿ->aþ47nõA9xò`C„ÿ®³ÿ¾¿ÿ¾¿ÿ¾¿ÿuvûM>œÿ>>¯ÿ>>¹ÿ>>ºÿ>>µÿ>>¨ÿ<<“ÿ;6uÿœÿ\"\úFCFG^Ž…Þ@C“ÿ>>¸ÿ<<Èÿ99Òÿ99Õÿ;;Îÿ>>Àÿ>>¬ÿ<<ÿ;1kÿX.XÈ@@@:AmÛ>>±ÿ<<Éÿ55Ûÿ..åÿ--çÿ22àÿ99Òÿ>>¼ÿ>>¡ÿ;;vÿ3Q\ÜAAAT999 @@@$7FU'Ikï6>}þ>>»ÿ99Õÿ--æÿòÿôÿ''ìÿ44Ýÿ==Çÿ>>ªÿ;;ƒÿ$Zÿ"[nù-ZiÜ?ABiGGGBBB24H\²%LsòYþqÜÿ)N¢ÿ>>¾ÿ88Øÿ,,éÿöÿ øÿ##ïÿ33àÿ<<Éÿ>>­ÿ>>ÿžÕÿ¿ÿÿ ŸÑÿqŒú*]nå>Äÿ>>§ÿ9A{ÿ½ûÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¹û'g}é9KQš@@@,@@@?ABW-NpÒb«ô|ùÿ|úÿ|úÿ|úÿ|úÿ|úÿzôÿ:J˜ÿ==Æÿnnàÿ~~êÿ11âÿ66Üÿ<<Íÿ>>¸ÿ>>–ÿ&[“ÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¯çü!w•é6R\°???9)T€SlÈö|úÿ|úÿ|úÿ|úÿ|úÿ|úÿ|úÿ|úÿyïÿ/k·ÿGKšÿ==Âÿ;;Ëÿ;;Íÿ==Æÿ>>¹ÿ??ŸÿKUƒþ]¸×ÿ ¬Ûÿ»ùÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ·óþ*m„Ì/Qt$wèû|úÿ|úÿ|úÿ|úÿ|úÿxâÿF¾÷w¢Îçv ÉÆhºŽNb€^^TƒþGHÿDC¢ÿGD—ÿH@ÿI:Sû???5F“Jb¥¼”qµÌÊi²Êé8 Ãù ³ìÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ¿ÿÿ.ew”jÁÑzõÿmÍó#j³ÚN±¾gºOnDAAAÇ–€ìÄF ÿ­Fÿ¹Fÿ¶Dÿa:(ñFFFAAAD|J`¥¼’A•²¿“»Ý£Öö Ñö@@@#*]’9Bc†@AAA¿ŒuÚÅF ÿËG ÿËG ÿµCÿa<*êUUUAAA7cq-·„jÀÄG ÿËG ÿËG ÿ³Cÿ`>-Ù®x_ ÃHÿËG ÿËG ÿ²Cÿ[>2Á¢kSwÀHÿËG ÿËG ÿ¯BÿQ@8ž…P9K¿IÿËG ÿËG ÿ¦BþB@@saF5·IÿËG ÿËG ÿ–Bù@@@P§HöËG ÿËG ÿŽAóBBB:¡HçËG ÿËG ÿ…A"í>>>%G!ÓËG ÿËG ÿ{A'æIIIF"œ¨Eá¨EátA(¤þÿüþø?üøøøðüà?üÀ?þ€þ€ÿÿÿÿÿ€ÿÿÀÿÿ€ÿþ?øÀ€xøùÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿüÿÿüÿÿü?ÿÿÿ¿ÿ(  0cHt@@@zCzd/dp'~PtÃ_þ$wLÜ===s:s —˜Ý´µü]3]yA_ ²[ñÈ`ÿ(fFÚDDD+++ž?ŸÙ¾¿ÿ––ì;;;V§}”É`ÿÆ`þ*^CÓ333 ±i²Õ¾¿ÿº»þZ,ZAAA4¦jèÉ`ÿÁ^þ-KZÛ†WÖ¾¿ÿ¾¿ÿp pé@@@Z¥~n °bÿ4Lÿ>?¶ÿ?@²ÿI3”ÿ¢ £ûU1Ue333H^–ù<<Éÿ..åÿ22àÿ>>¼ÿD/vø===@@@ -Jh™"Lwð3D¤ÿ88Øÿöÿ##ïÿ<<Éÿ)T¡ÿv•ö(^p¾=BD&>>>+Nq¯d±ï{÷ÿ|úÿU¿ÿ==Æÿ~~êÿ66Üÿ>?´ÿ…Æÿ¿ÿÿ¿ÿÿ ¦Øö$p‰Æ;LS?a¥»|úÿ|úÿ|ùÿoÐ÷aÃéTYŸû?C¢ÿB>ÿCQŒøy¾Öß%Åî¶òý¿ÿÿ¿ÿÿ¶à `¡Vi´±XмŒZ€¦DAAA¯}g›ÇG ÿÊG ÿ`=-¬;hxVœ´n*²#y—t¢s\pËG ÿËG ÿZ?4ƒ‹aN;ÊG ÿÄG þG@jߊ ½Šê"ŒL‰¤‚ˆ¼Øê¢‹è¦ºè:Šˆ‚.‚nŠÂÑÀ]t€‰Óœ'Ì©sb;è6wøw5‘j‚/¼7ïû>?^xŠ‚RµóÄ£^i¡¿ïîƒR7‚’jq˜îÄa´ó<Ïn`³Ù$«”‚Íår¹-›ȶ·î_¥ätZP%#­ß4@^©9PÈCL¥‰êP©;æe§ñœJR¸Pfl¬>=íŸu¹\®‚´\Ö @42Ñž¢ÀÞÃé9‹þL!é5ÏÌg|ÔëNƒó¬~ú8Pð%”鄱=úž¨¤# L6KcIEeZo4Ï.M¼U4ê&2IV‡tStTnvÇØÚ¡µ&ér™Fœb¦#RMX$h>b…Î?ä %îS¶4MKĘ•¥ž» ž÷[¯SÅð<Ïî._|¬,ŸoLW(,I! Ȱ,‚:üÚE$MñÔèœì›÷—úªó³;ÔßA²X,®uòÀxRT#Ë0±,úb¢ÌWyÆàv»#ºÐv«®Çsº_ôE™Æ %~Êeð/KÑ8v‘Ö&µíÚh³Ùè(tt) ö‘WìRhAsÒ×0Ћšd £_Òßçyž) நŸ¬ìðh§È̳]Ã9‘ùò{}¢'1mÓ­êêhõÃÿ.ݳ©Æ¥:@€ð‹º®æ<Ï ØÝަɣ“A&ÏPÜ<×Áqœü@Œ„;™Š´,ú®v,åSv:±âÎëõæ½Ùá}m`|ÛoƒÒ”6][{²Ö§ºöÞ¶Z­†õ³õí8æ¨ê6wß,ºGÁòç6qZiIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/dicompyler_logo.png0000644000076500000240000004163311677672407025021 0ustar apanchalstaff00000000000000‰PNG  IHDRÀˆpY;ÆtEXtSoftwareAdobe ImageReadyqÉe<fiTXtXML:com.adobe.xmp OëG?ËIDATxÚì½ |Õ•.~jé®ê}Qkß,É;¶Á0v x‘Í2†äoý_BÌÈyCH&L^€™¼`gf3¿$’ì–’`3!`l06‹w[–­}ínõ¾TÕ»·Ô-·e˪’Z­ÅçƒëjuWW×½uë~õ{ιŒ¢(€@ Äù›@ H€@ "ˆ@  @ H€@ "ˆ@  @  üT®Üu×]'D"“ÆÝ£;vìˆb—@ ˆóÌTN…öÙUŸ?TŠÍ´žÃík…–ÄœšÂ‹C¡P|ÆŒÁûî»OÆî@ ¨'®¾újKŒ­¶bn/Ò²ÿÁ˜X–uQ"Å.‚@ H€“Éd²Bæ\¢¾D. DÁA¿Kh t°¨¨Í 8y 3\Ì;LzøŠÅˆÜO€éPŸÏ‡ˆ@ H€“cŸÏðFï ŒÆ™`x“É„ˆ@ H€“ ÃÏb8ôe²f@3Dj !O Æ"ÄÆ”T8FÞP„5¬‚tŠñ¤LM j(„Á`@ˆ@ ¨'ÌV›]’U%§Kʤ9’’úU~… @ H€“‹-V³,+ èQ€ ô¿¤4-Ÿv@œ¸æškÜ‚Ùe 4¦(úL ”%…O+@§ šV“G ÄäÔ3ñÅb± VÌ3ÊB€:Ü`ÒÖÒ˜l†d2<Ïãñ¸gíÚµüÆ'Nœ q…—UX~üøñ–ßýîw‡C¡PxúôéÁuëÖÉØ• p\Aª’\&% Sà2jøD3$Ý”9 áõzy¿ß¹Ûí~’ã¸jºk~~>µ?÷ÜsO=ztóW¾ò•#O?ýtXÑ7éˆ@ ˆqÄ”3JŒ}®Ádã$º¸­¬Ï F$ 0½* …¸ãŽ;Š ¾˜H$ª·mÛ?ü0|øá‡PVVVtÓM7}‡|ö˜Ñhü\}}½æPÃ.…@ ¨ÇI0Ì£Sw „õ9ÁÐ}é Ô…qí©U!f̘qá5Çÿ÷ÃÖ­[Õ=ûÛßÂ÷¾÷=¸þúë äóÙD~S–å!AºC»@ Ì9–©‘N‚~{$u‚a!–PÒ…Dáͧ–Í7Þxc`/Žã`çÎêë’’ê)ZÁ²ìƒ!ýúõ,v+@Ì9ì6Gž¬0ÐoýÔ_=Yá!žÔ| DÕ·”ÃáSÂN’$ðù|êkAò¹@Þ+‹F£Ó{zz0t@ s‡Óe‘dYõêÔ£ûýWRfÐþ zž¨ºB‚æC#û1©˜ÁL @ H€9Çu×]'Øìn2Ÿœ&4­ð_¡Áðr:¨GÑL˜Ú‡!D(Úl6t„A $ÀÜ"‰”[œÅJ`²¬7žI‘  ™'ß—©ª3“­fE§è޼G `@cM6¨’è „O“ 5…Æ%Aç#0-Îé!ÄÄ”š¯’ã «ÝmìíLªêO!¿Çd$!0üT%À1 Y|”Zm’[jSeQêïm¤l!¥o_âœÐ¢èý´ê¼k˜TÎ’)E€I°ÍwØí ·÷¦æuÓB¿ŒÄA<É€…´,˨ÇI©? !n e-)›°‰ÄH1¥L  03œ‹¡z꜒SR$(ËDâêš@Ç wŸ…ü“c-6@$0‰ÆR“` ,@—Cf$ÕcTòŒÅ“ªBF8®8ê±™ Óí±Ð­ªS-#!?`8Hô¯ŒË£PeçÒ¸@œßHcl6‡™Îýõ/† #T€ýH'ÄF8¡áÂ&@ ç=.[¶¬Ðê,2ÐU TTN­ñ§GÒÿé÷’IõËœ–L0ˆ¬cw–÷C ˆ3pV/Ð5×^ñùp’wD$öoçàæÍ›¥I +Íö¦A£&PYÑA ½,RBá &'åCUÓ|mB/ô{x7LJ^ "{XWWÇÙøÄOÖN;V²·ÏímG_ûÂÊ@"™8Ðe^ Ƙw öO8Rä¸J³-OH&åþF?wõSF¿ L$û p¢gwY·nÿöÛo_ ä¥.Kl5ÇÁœ/Ý´Ôqà 7|Ùd2í"×)>Iûæ=Ðû·è䇈@ ²G€þžž«®-ër\åéàHñÐ÷$`‹#Ž™Âù7‹{‚-QôñÚ¾x<~ Ó—xÉŸ€wŠŠŠö')&ë|›ÍÁÒÙ¿þÅpÓjnd*0I¾ŸèW€†)ÙíÚµk¾Y?ã¶&V ÆÙ“â¸æâxAi§uNI/[‘€Ãm6¸ÿ…Å öâÏoÙ²åØš5k¤IØ7© \•RkàÌ@x$?‘]Ì"ÿ{E~‡ý4q2Ô˜¼´°’rÄ’ öX Ú$ÏôVÉu}KÂl&cÿ|ûçûb±è6oô¥¾ü•¨§};vìHæ¢"ðs\[?ù¥W‚± á<9‘˜iÜ.ÌÚµk ̆äN›´Êl6ÏvX8ûKe±Òã·–¹¼Ì´¼n° 0rd&v™UV‰Î”eù’W^y¥``’öOJ‚¤ @ŒÒ¹£¯^Ù¢SdÈ/pŒv>vè…bÞ s$žé3˜l«Í–ä,?›WÝÃä]ו´…:£Bü_¿ñ•¾H0p¨±Ýû¢/ƼMñéX"ËrÓìV¼¨j=ÛrH49š``€çˆD%5Uš ™J‘IíEþUâKˆ91‚2ööö^h“Wºì*‡Ý>«Ø!Ø/­µˆE¿9ßf\b Ù 8Ä XŒQ0òçžäœ[æã?êˆ^+ÂäÏvu8._¾|éb×I§fÒ!Taáj‘•>ˆJ¸­ff:o±Ê6(»;bÌŸÖ·pÖj¯dŽtEØØÝû@oOçÁc-Ý/xÃJVHÑn·;Y–Q“X+jÓKDøâ ;\1ÏN›¬&ƒª–zýhêACK7GàÓã øàÓ$H‰~k!]T7dÑJ0î¼óNã.²ŠÒ•'{µ'¯`Æâyv[‰Ç"Ú#&»d¬|°r8%»‹žÕîÙsÝÂ&æã—Ê–³l™§®®®c282!ĸ`‘ùúÊüöÅVî3'‘!&±ŠŽ˜ÀÀšÁÂ[˜"Áaž-:ÌœÛíJÌ*®ˆ¬ÊB´ÍýðûÿÜ×ÑÞ~øXsû ½aØ ?ýàƒZßáp«AðÔùqÍÊx`n>”ZUö¢™a¼^/´57QZFÁãñ@!)TÏ÷7ƒÏ_ OmNÂ38¦†APsêH ’Qv"ùÝ…V1qež“_ý½ïÜ2½¢\°_»äbÁm›,†0”.PmI?0rQya02apX#`2$€cGæÌ9·ÔO4DW´Ÿý‰£ƒ#8L5ôϿզ^W§Þ§¦ÉÝ©²&8BuF ªk»SõÞ–jÏm©¿G$ú3~Óg:úlËhãñJüí‚Ss¯éöp¥Î©!ãܼ:úÑ¢Œúº2Ú’+×sº‹àT¢utNÞŒsòBk[.ʸ‡÷g½}&[÷õšŒsKsÛªÔ9N€'/©0‡G¯tH9™”äA’Š‚qÂ1b$y pFQ5°˜œb™Å-çç;`é’rÅR¸²ËÛ;ÖÒ%¤èîî<|üdÇ‹m}‰ápø“³‘"Q®Ö™ ¯5–y ðÙÅ`&*ì Û_Þ|óMصk]+p(¢‚ùóçÓ8B¸òÊ+á[ÿø9ø_Ÿ»6ýê7ðî{»5g“!û±„´§'Ãßùúm×,ûþwo«./˷ͪºØT˜' F.J¬’Ñ6b½ $}ÀÄ I^2ÂùØÄˆ†¤ú01zµ 0½°;Ô]ír¹vê$@ÚY6ÀЙV\ƒÇÝ©ÎtO–‰0™ìëSe‘Æ6Amr‰Ž:/Êø½áP›A”2ã‘=ÖknÙÌk»áç™Yºß¹æh¥úÇǺGgµÔm`°ËøÎ†s\óÁm¿)u^à ÚZWAÑûP½´¥Üê¹ÄšT?©ÕØŸÓõÙ¢±Mõ\g÷ ãÝ*çtLz€¯­­½ôÆ¢[¿^uÌ3–-FcôÂ!CY€(#B‚¨CÙ`™"crgÉÞâÁQ&g0f7„â ´w÷ÆZº£­í]”žhiÿs›_zƒ(¹O/^ü™Ïÿ_ÚV˜ï`ãñ8lÞ¼üq•9ŽK¯í7´zeYU%R,]º¾õ­oAee%47·€( àv»Õ÷3±`ÁØ´i¼øâŸ”·w½®Ì©qËógz ¼4·ÛŒ´B4؉H·Zä„ɯš6y©JOä£`ã„ôä1ië=.øñ_–ž,E×———ï¿ï¾ûäLâw§:©^xSz dg9¤±$ÀôàV=Ê&vk¸‰‡#= OÑk èÙ<ÒD¾(µ¯^+ЦÔy ~¸Ø8‚:žíX£©[zÕWê|ÖŒ°o× Óîzê[—"-ø@ãÚ=gy«{¨FŸŒÞ«ãGïƒÎÆaî·U„÷NW€ùÆÈ?®.èSòK›J­|¬@§üB•¼D! $êÐk˜ÑQRf¬n0Øò¡ÜQ ̾´J`˜G\aË fY{·7ÖÑO^tŵ&·ÇÁRµ÷àƒB{{ûÀ?ùAÊDšÆ»ï¾«–›nº î¸ã0›Í§}>6«‰™=½˜¹~y9Ë˽¤û »»›¨¼ªò¤>•ô¢òŒD[LqBz#7mêÁ…•>úÛÅ<_6óرcÇÕ?7†ë8Zú‰l2Í x62®®kR¿—­”mÕ©àÐîëÕØ.#%¿ôàŸÐFÛê3ˆ+[×Ü•ªÛ¢Qã58÷\›Rý_kÛ-:®‹Ö‡\©¾lôiWÆq†ëËzøtõ=ž¾S •®ª¶s>"õ›J£äU$¥‚ ¢q#Ĩ:ì³@œ·BL°Cˆ¨C?QƒkÞ€:œYY,Ì)_,°F<öØcj¡Š/e’<³²<ŸZ)BVUß`òËüûøìÝ»zè!º0îÇêëëS·áÙh°zN‘é%áSW JP%<ѳ1!÷>(,£@¥§?Þª%uÿë08šAkð`3QÉo4ƒàÙLNà äǨ.iõº6 ç™iª͵K›Âe¡ÕéxÏьټîáÔüïP$¤Å\¹FãµÓª°r5¯6}:ýМ _£·ïñ”ü( 2IN~®¥<ü¹â³‘•a<@C,†8 ¡ŽÕaÉ‘”:Œ§æ#DD'pîiPò÷ÿ g„ÿüÏÿ„_|ñ ÅGU %Bº7oTUUA~~>ˆ¢¨ª´ÎÎNؽ{·Jf™fÐ4?~n½õVøÁ~p¦}.E°§¨j íQð‚@IÂKª ,žÞÉ}·o¥ÉTøóõë×wfšA ^Syi¡l“ßpIJf É/s0j€ìÄHnÈÒ9m„Ñ›•3ÏiKŽsw–Û}3)5CŽVL+»áN´ö×m9¸‡e±Ÿœ­ßì†ÑùŒèÁK5R\¾|ùüçÚgܵµ»ò{‹lÖ/Ÿ`ŠL±qµú½J©ó u¤ÉP‡18oX³xàò;£—¹\ðµ¯} V®\ NçÙ£;(é}ôÑGðì³ÏÂo¼qÚœ!ýÌï÷Ã]wÝ5ôI*päüòM­Pì3AòÆìnpÃS»¦ƒ/êR³›ÊÝ²ÆÆÆcªÔÎ]Çž(Øœeò;Vç€ü2IbL/Üê,kM–H0ÛS†Ppiu¨E¹­ÑpÝô(À±®óf‚Ýç“ö$®ÖØ—Gãжf$ýo`pÙ²erSSÓV¢–>·;R¾øƒ#Å|•Ð Ÿ/<Ó,a°ÆWÍðŒ NCˆC|Õ¿[y1<ÿüójÉUrTQâûò—¿¬ª½¶–8zè475ASs3„‚A(-+ƒ2RèvÎìÙ°aÃسgª&[Èþi…—Ê:´j%jÙÈK`“ãN~q‰…W÷À VCTqƒÁT ‰6.Dο‹Îh4²Yxúß §\Å]#íx9ÄÝ ²Þ{bI‡ÔóÔ­u2\¦ K«g]æÓóXxÿ5¤Ê"½Y;}¬Ú jc5¸§ÃwÒÍzêS×­aˆãj©çpûhÿËE¨F½†{|(­{Rßî¾H{•TÍŽèw€×­[§|ãßèD";Mf‹E1M/?‹9Ü\À¹HûÞæØC`3$Á<ަ=©èb`/ùGøøãá‡?üမS%#¢Þh¹ÿþûáŠ+®€p(mÜ>ý”.ìF²Ÿ™-ípØ`€Ùß`6ÃÿóͰpáBxòÉ'á»ßý.|øá‡§~Sš¸1ä ‰.¿^ù¨v,™ÏƒèJ ,K³¿´C¿ÙËôf/))9›úÓ:(yS|ËY:¸&—ãqzZ×cKÒpóçrë×ê`3”·ã–TÑ:'›6ýdË bwêšn4nÁõݪãîk,<*¬¡ô™pïâúiu†IÇÌ5Œ²îcmþ¬ÖðœÎÛÛpŽ6 –‘zÈ9÷L¤Ëç¬]»¶‹È£²,¿Eˆ¥V„+A˜VH–¹Ý“oÚza…» .tøÀa”ÁnL‚‰ËRšK\ñÕ“¦ä§¨)Ï”åGË#<¢Î÷;|žþõ¯!ÞÛ óˆâ+Àƒ@ˆ~C&Ä!%DH°¨Äg}/[7Þt<üðÃpï½÷Â[o½qU!Jzþí>^Ù[µ’+Y ˆvu­õ“ÒLΛ:½¼F¶Ÿ’ÒQQQ:ËüŸV›yºƒe²y Õq?˜`MU¯cÐÞª£–§é-:‰q(’9Ö¹WÃ\ÏlàPm°)õÙY:(_Óp ×0$¡çòžÝ–RÒZçŠÏåÈ¢ÇæQ(/Œ½ùSKŸ^«ám‚áC¤Ö¤®y¶í¦ÔuÍLZQ›9Ž1ƒøõë׳~¿_y„gRYNöYN±š(¡¼X_—Àżp™µ:zÀ%H`7H„%Õ£sLM|Å‹¹íuu®îž{Î?¨³ÊêÕ«áÍíÛáÏú8‰Ò»ŒÜŒDŒ wu©ŠQ"D™ ÛÙF‰¤Dè'Û=ùùÀÕÔÀßú0ä³Ûn»M5‡žMÒ8A:oøÞ[†}ïü ¾²xØÅȘÕ=II/Ì©Ä×Ôc‚¿|RÍÞ

šzj[JÚZÑæž6wááP©å@g[ÊwÃkŠqU RUè$[)BÂ*’¬ÜôZØ·o466žö™¨ãÚÚ AH9~ؽ{% Æ)ÈTýÑïÒ¹@RxZÈß´! ©1%àlj‚É÷–^y%\DÔ õ:ͦ7(5éö ¨<~@õõ p¸Ý ½EÀ @t»h›Ó´3­¤Þ£ŽœÇžçÛ‰à ¦—9"×f¬ìñX‰ ¨£Áq´¿§·=²|=ÚÛ}¸k:œBÒâ s6g-Ž'¹ ÀñðêM?Þ­÷œɯ¤Ý>¢ƒÇŽk%jðCRfSÏQBŒW‚§²B’ÊÜüíÆ}½=PÆuA©)L ¬*Aª) ¦M¥tk8GnLIa!é™J<‡:üh¢ê3fÀñcÇÀHˆë¢Þ^ÉVîè…(9–¼ÏòcÈw)A%E„Q‚,9Ö@P %Ú‚PŽí߯àòåËÕÁQ+=Jz‘3In»F8Öá‚®Pp¦RÅä´RÕM=:ORÞO™:÷‰¢ØYZZ"£ËXtîÉJ€¹&øñ&@ rM€ZÔÈÝ®[fü[µ†ßmÖ­¨d×L·õ‰ͯ¥aš_2L]u8Ÿ"|(”å,Ë®0¹K«WI^«¯Ót¼¯ ÙÈ7†Á-PÓ¨¬ªBjµñ)24ö“!ÏœN†]¹Ýœ~Bn™äGÃæÎ«*BšÛBˆÏÜ×rg'H„ð8¢yòC”}M¿©ÎÿQó§,«JÞÀ6³ÏO~ò‰ú{555£º*ý¦MJvü€ê;E|4v» "ç#–(f·š~‡z6’ò)¯“zÒù×nÇ—âC ¹#Q-Î0™¨…t¶`Óf|–Ž£U8à9‰DAðõ•TšÝE³EgA¾/è3·ûÚg´\„a\U€iehWáéÊæ¥Ž!¼5z[zNûQJ€TR´µ¶‚“5iª.þ©¢ôï²ÁЯilZRi[ú—¼íßOI™FÓ+ÙÞÖ¦†<è%º~µÇ«ÛÁs|=AZ|.Px7ð¦RÙd0Ð õnRSÒ#jïMžçAèýéOW&b@"ÐBVzÖË«ÖxLÄ"À~29å9ÚY__ßL&Ó6ò÷åäýU¢Õ¹ÀlwF‚ëIo3ÛòY %˜è÷¥JP0ö“ +¤s†Tòx,.èí=rº2$Ê.M€Dº©™“œÛ£Î.ÔÌI‰OÊ\ýz}ÒB?£ûÐ}éûôüS¯•T1D£'ÊÑl±hª  ¨=_èÔ6aîô…è ºÁh.ƒ£€œGt')Ÿ¦Ö¨z›_³Á`ðmÚ´)IÛ”æ#²ˆ<¡%)±Vó‹3¤Þ@ß©nªž¨¨ÎR›kq†ÉÌ 3Yîžâ×z|2ÁŒªž£uuu^‹År‚¨™d`¿˜|´Údµ]"ZfÇ¢Q{ÀßÂ"^h …ÀiH ˜A) ÚUŠF\ÉäY—%J$úˆAÍôB''i {2ííI³ÅP˜R€”ô$ò:Iî£î›rˆQ¿›Q8 N/´¦Ñ8 GZEˆÇ¹³Îñùˆ ÅŒˆ»A´‚5ßC~Ž BS•í¦¤ï‹¢ØVXXH9ÁÆǺ£iÃj˜¤7’ÖúeƒwÃØÄµMug¥‰W 0M‚Ãͦ×Ôü>‘î!ÝŽ'Ž7¹ 0)‡!€Íf£)ºÞ#ežšjM— bMü]qßèo8|8™2ö‡O˜Œ,\ÐçSWwÈUu½½ýÉ7 ‹Š œ"5šá…¥s})%GÃØÔRGé8ÀÝ—n ñ%S$H©”2O‚°‘^'j³ÛÕ•å‡ÍÐB•ßÁf"1Ãis|4”a–)ñ•(v§üK[ZHy—”×Èßbïðz½¡Ç|"&Õ˜:Q¼µÞ”k²4¨èñÖ Ü ˆÑ¨¹Ád”Ík®…µ>hN4óç0‰M²|®~(E„õëׇZ R®ü3“ÉäJƒÁpŸW>Mv•ºãþNcÄß É¸zb²êêXèóùÀWxÚ1ià:]Ó¢¸´Þ'*0FÈÌ@ CÈK { …faSShr*Zšã)ÂŒS"$ŸÇSÒ•SYùÍJ˜±”ú‹¥0För­¼ðBõ;GŽZªÈB[Šœ ÄkƒÙŒ©T±›LÔ±¥‡5íØ4yÀö`0HS•õ‚(èËo7[˼•+ƒ‡Ãݨoë!´ô’B£\´¦[¤ƒpõ,Ó³ Ãõ͵¯Oý´ùpX­áþÙ6ï¡t"öIùÆŽão+O<ñDôñÇoóz½#ä°‰(¢ï’÷Lˆp»h÷4æU^ä ç)’Ù'UMžtÉ¢´C E4UƒÔ)JN§ºÚ+Mn!ûDh¢k¢ÕBˆ3ý:J ùL-4!6!<šÆš~—¾¦H_WÍ«þÆ«¯¾šN}¨e•ª@…·o­Áu¡lvÕ„L&Ó BÐo’òr¾ß!»þ¢¸¸øÝgžy¦…zÌŽ3ù齡6hêu¨•\)@­Oütðy F÷Õ cкÖ¼ª¹Xn²£^CvöÅŒõ:£lѰÿ¹ú„r;ÿ—®£–~¥u $À3X€BüÑGíª¨¨ØKÔÑÓ„lî%¤q?Ùn­Ž#†¼¢ˆßR÷_²d‰júLƒÓK/½+XÜsÑVN¸•?Nö}…œãRî …BOÑÔe=öX'Q¿-œAébã7%Í»UÐõ@©1½ Àpë¦qõD¤õ·6hhï\§s›êØx•µHç@>2Ú”£{5×÷+Õv#Y´Z‡âÎ:ø‰Ò3SÄ@§ázêêê|.—ëd2™|‹á¥F‡òhÅnðù½*>ôÐC™ßƒ­[·Â-·Ü -‚}+V@ßË/ƒY’Ôƒ±ÔJJšéÕ`xè{ˆ§ 5}†)ù¥ ]#ðæo F#üò—¿Tg&éžF,,'З”Ü>%ûm$Û·Èßí„üéTecìÑ9š›JOÆ‹ô·é'CLì'¿´óÖú¹RÄ´NeÛhÈ «3ê{¶@Ó«h]ïξ"üÝ:ÉÍŸÚ‘¶f ns=ý¸a„„4šÕR6ó=äÒqïl¡³Õ¤=lÓ÷Ò¢Œ6Íy?æ'bM‘†ýúõ·÷l»ýö»oLVŽ<‹æ/QÍ ™¹9© üÙÏ~¦.’{ÃÍ7ÃS{÷BeK‹š ›VP]öˆ¼fRAîRŠiý¬¥š=©¤ê–‹n½*«ª`/9]{ðœÄ-+i^í#Ç;‰¼2{öìÖI”±åž”‚ÓƒÉdîIý†_Œv(oε }=¹ê”*éÓ‘´Ík!Îl÷ÑÌWku Š8׌à{ã5ÇæMÕWq¯A«Ç£rìDî¥o¾ù¦{ö‚šÛ++ª8ŽgáoGÿ‰dþéŸþé´ÄÔTÑÕÛó›ß€Ãé„kîº º‹Š ]>!@ !¿¾ôkZÈg™¥/H_n¼Vá jxÅ¿ýÛ¿6ßxVÈV‡4HÑàt:ã“,]Ù–10¯x'XýƒãuÃîÎ!)ÕÎýGʵ)sÓ8×9¡H€ƒat'YUw)]Þš¼¡ƒÙݳæÏŸ¯šBixC&~þ󟫊mæœ9P÷“Ÿ€´|9x ¹ù AQ2¤ÅG^üM·¤ùE­V¸æ?þþ×7¿ ±X ¾ûÝï‚×ëÒô9 i晈D=dL¤L‡ÃD¹²“ì¦^›Å'Ì`ây„Ý3ƒHõ0Ö¦\34}æÙx¸Ñâ 3øarÓ#†²¨ ¦±bÅŠ9‹—]¸*ÏUÀxƒÐì?A¡þrè—OÄá_þå_ÔÌ0ƒÚ½÷Þ Ï>û¬ÀþEBb³È~¢½„È|„èü)ô“×Tõ…xò®¸¾þ«_ÁBBª­­­ê€û÷ï–üú€‰1 Diª™éƒangg§y’ÝØiSÚh³‰lK‘–ãTà |OW«á÷֎ѵZ5AÅÉ‚†,(åÝ©vφâÖsírû7^ýn\Vžà'jµ•°O^yÍ"'}}¼ëè7@Xl‡hi'<·ý—ð¥«ï€ûï¿îºë®ÓÔŃ>Ÿ|ò Üyçð™U«Ôðû¡¥±ÚŽ…y]XSÅUUPXZª*IJvtá[ª"iæ—Lò£Ÿ^ 0–-Ž‚¤$!Üf«OÞZF?ª««;šv€™DO·4nó;㦠‚ÑJ€¹Nßõ@Ф7d醮鸍lý^ºÑ쩟i»QOE×ûÍYlw=Î0L vôfX6ŒÁCl¶ÒNn\yõU×]ÿµË.0›¬p¬ý#h ´bë…aáCOCÕÞ¹°dÉr¨¯¯?«‡åöíÛé"Üxãêz~Ôqfö…ª%4“ ?|î¹ç ¡¡áŒÅv©Ât8)×Ò0™L) Í'ñÀõré‰XÌóòö.‹ÅÒA¶¾I¨éS^=hUkHݤ“E‘¤Ÿä×ÀÈ&ë3£ç÷Òq’µ#¸&éyLÌ÷9ú¼ :®ù¶Œ‡¦lßgZ"^ó´çqÚc¹z”m‘>^ΧN˜‰¶ÊΊ+8O•éÐ7¾sM_¤>nyš#Ÿ@HhÖ¡©\€ ZÁ¸|ûºŸAyq<õÔSð裪ä5Øl™a0›ÍPRRDQT ’]G0s¿Ó‡.…ô£ý¾ð…/œöÙ‚ `Ó¦M°uËVxuË«Ð}´ d‡yóòüäX$»ü¸¬¬l:±õDÃPýƒ°{D¦oÌm0ùcÐ\g©c:¥›wÐM™®÷h2òW§Ú5ÓÜ5è"ý»cü†ƒ–l[ê!dð5¨Ôþ ýz,ä»5*ÀU09æy÷é¡õÝ–ñ0â/ÒS;MŠ÷&œd-ño^ý¥ÏVð¼Žw]Ñãæ:ûɯŸ²A±„ Võ)üôù…ú¿û>Üzë­PZZ ëׯWwÉ$²ôkjÖ¤íi•7ؤ98¸ž6Ð¥—^ ÿõ_ÿ5 öÎz¾ ‚,€;éVI5ܶÛÊl—’–Òœ§äX=“x-¿±ðˆª7—õl˜`f­óék0×ah ÁØ“ÇÉiÒöé åCÔŸ­bzñÿ™3k®áhûh €ÒF/0è¬ Y¹}®Ü ?{îûðÞÞP[[ O>ù$\|ñÅŠîl D' ³Ì‘ÕjUlè\¢e¸µÉ©²œq'˜ˆµÇ˜x<^I—}"ŸÖ¬[·Î€ã €S©Ö´L-àCÒùF€¼3öÃkoY\ØÝת†=ø¤fˆ‹^`ų—(i‡äìOá™7~Ï¿ú(+/Uâ)qUUUªä0±|éϩһùæ›áùçŸWMžÿõcØ÷·}êç555fCº={¶ú:ÐP€y 7p~B'CfB€Òß'OžôýYìjÄytª=-îþ“IýMjL(Q•ÕÎøB‘§”ÝsâuèŠ5B„ëÎ=û)|˜Ò6„ì8œ€÷÷½×]¹–.^K–<MMM°sçNصk477«q}™êª»¼¼<ÕA檫®‚K.¹D]mâ£÷>‚ÏÖ˜9o&\pÙª™•’+7¤dxà 7@<‡Æ ÄEpÆœ`a¢àíò2±âX‰hWò|ÿÛßþ65³E°»!ç¥ê£PzòdbvŸaÂ8Á\[wåkwþû—j;úáP÷»Ð%†„£8AC,žÄ°ÓPlGxÄipÙü«`þì…PRT:°­«ÏçƒH$¢Ÿ Ÿ%â 8üñaØñ‡ÐÕDˆ—Sˆ×ë†Ëÿu Ì»zžº%Qº0/5¥¾øÄ‹ðÑö ¤© BÛ-b+´šZ!X„¼YyÍd?šÜû—eee'&R†N0ÄDÇHœ`r´cÈP‰Ó‡ ×@óçXwš‰ä³b岥˿¸à²¸†fßAð%›!aöj#? NÅá¸`?$òÚ¡eüñ/­ðüŸ¶@ž½æÌž«žÓæVCh\_cC#ýAèîꆎ¦èmë%ª€!fKØ…í…PÔ\¦ˆ ßuÞzf^1 ¬N+Ý{Þý}è8Ù¦˜ùíà‰x@2Ha#e£ìH@¸ \hrš–‘3|(Ç.² b×C ¦<6ÂÈV8Øä—[L´•ð›/»È¾¯õ¯Ðk„¨¡8[L¯–™B”ñAÌvä>?0Ñ<ˆ„bÐÕÛÅ "„F(ùÅRÄG å°ÅË¡ÐV©²¡}Ÿ”' ùm/))iyì±Çziyúé§Û‰Ä‡Ö2ë—7íØÔÔÚÜ …ó Á9×\9WT%1ÝÄ.óàŽ»ÕR-è_AƒãEQœ~çw±ë!çµ ¤Ÿ_‚äwž Á{dáu¥Íþƒà—[ iöÃë7}ª¤×sJýQ5hK–A‘£Z!„G•Þ‡¤üšáNB~½ƒCž|òɘÝnÿT(nþék?ëêê膒EÅ`Ÿe®”…°# £_¬Y“V•óH)k/UƒãÉ9̧Áñ‘H$ƒãˆó’Rªo`róó“‰ú›7IõŠ€ÔÅôÄO@Œš>-qÝÇIø~“gWIøy°I%P쨡ä!dó1ÙíiY–·ûýþî³ÅáQSäƒ>µX,{Œ…Æ[þËÃ~:X¸°ÌÓ- )ÐgíQ=©)4/‘…ÑB°vZ Š–Ÿ«å8nN}}½ˆÝ5 eÕ8œW:ás[RÄW¸–ãùM€æBùñŠËÌ®Žð1±ý¦O™(½X¦éÓk‹\Åöé4uµ[î'å·äõk ç ? $øÈ#„Ⱦ•ÒW²õ¡kf!Ä*’ùIˆý%Â1m ­ì¬„HKD Êo!d:7P\WWÇaD ¦,ÒJÏMJßÄCÎ`V^ûÙÏ]vSùÜžhø¥ì>BXúMŒiâ‹uˆ 4€)^%¶™4•YŒÌ!Bf¿“$ée²mÛ±cǰK¥œR‚·ß~ûö˜-öO¿üð£ß¾þÛ&×1èw«N1†.ˆ }ÂÍ$™TŒNîníŸÇ_`ɳ\EÎñ=›ÍFØãqáÑsàü*ÀS ký9 „;j$KOìÄŒ=ÀšôG¤çûÔÒ#€&ägUÉÚQ•µ%‘H¼Èó|“ÎeÚ´i}N§ó%ï»û¯ü"V¸ œsÀ•qr„@bõDZ».•«º«!Úå ùM§1¤”/_¾œÇnˆ@ SœYkì®9«å4ä!Äuãé>÷ËT|ŸŠÌ3Àh(ù'¤ò¢üž…B'FoGç óó󽄟m“Ûþý±WKä/ÈÛ,0% l#lt.ÐσéM5n»I/!å²9sæØL̉@ S—‰úsxJìÿÌå}J+È6èõƒ”ãì)å×eÖ[…â s‚pHÙåRžkoo?¶yóæøHÏ•’`Åjyâx¸ñ¡çÞ{.éYàS¤B üf¿îcdä¬òUƒÜ%³Édr9ç«£ÑhU}}=ª@˜ªÈ;c?žVË÷ÆOB\ìVÐoJ¤Äí2@”(?¥3òùj°ˆv:¿×FÊVRž%ÄrxëÖ­±Ñž/uš¡Î3ã§ûzö?õú‘íRÞü<¦ ô$!l ë>¦-iwÜóçC¸%L•ßÅ,Ë^)˲U @LA$ꯪ°ÆvCÒÜÄØ`lúÉCÍó©ª?”Ž(èäwÅ]pÙÉK!Ú¥¦ÏYDýÕF ŽG ˆ©@€¬9ñÕÒÏ(ÓÂ\€S¿RJô!A”_¬É†æéP`« $GãÞ%å)žçß-))¡ç$ª›:×%xÈVi¿eóÑ-­ñ¸b¨$퉩«?èM”=ûÄ,÷„=„?KÞZXTTdÁn‰@ “˜‰ú3‰Næû†ªN³äìFg¶—dSÕ_䄘c3 Ð~je‡Ô²FoúÏ–ßs,±eË–(QŸ˜JM_~öäæ^¹D¶Œ…€+ ;i¶ pIÛ%èHð’$Õz­‡Ã@ “˜yGì…ËúŠ£æ6£>oI%ɪޞÑf (ûgB±½Š’_˜”Ss~ožme‡\€šZ7mÚ±X,Š ·néz®)gÕ y¿M| 3á„%Ç–@¤=â"^NKEE…Ã"b QEæ|öf(n3(VýócÔã3ÒbéÃY„üÔe¢äí©·g"‘re‡\’àÏþóÙìbò™üŸ¾çÂP Iõg·YÐ9ø.ž!u›F꺚”ê5kÖ°{"Ä$#@Þý…{Y{‘äêÖí…ÎûEÛÌg&Yk€ãÔi÷Ó8?òúÕ“'Ovœke‡\òàŒ3‚v»ý5)OþÖ Ò‹1¦œhAT÷| §pP{p%D[¢RÏ…<Ï–(L7†E Ä$"@¢þ.´OK^¡”63zC’!"m"DÞ˜­æ÷¤+;B8D>úÙÒL/šVvȨ ¥ó6›í1gìÞ­üËq $ð蟬T€«ÓÅÄ¢±Ròg-©û¬uëÖap<@L4¸cY¯jð€)ªON%ˆuˆ~}5镎ѕHÉÊ9#A:)Šâï‚¶à†m–mI¥Ào×?øw¯ƒhS„Æ^@eSSSáúõëQ"ÄD'À•×}öóö }3™Â^Ýßu xy6ä±5 EJt²,?OWv Aè‘ü2I0 u¹<®M½¦Þ_¼é~3™,Mêž´Æ­0«mDú"E„—s7/ bp<@Ld\±bk°%ÿÛrÕ;è4ÿÅ{ àß:\J ˜3%º&B/²e´+;ä t^òàÁƒ6íG]¦®ß¿[øž+‰A’Óg±½êèUo‹Q˜¤þ«ü~)Ç#Ä&Àd2iUd–‹w˜uMüIaŽß,°Ghrk‡š|ša˜­¤üž®ìž•r:?IT[«£Ä±®Yhzewén)PЕû”!ÿÙ"vH$nB€KI™“ŸŸ*@ &*îܹ³ Ù/ï}vþþWk  ?ê+¾—g€Ù[6ÑE=;;HyU’¤¬¯ì+PS­×ëm2™¿sD<úÞ§ŸÊ}mYpzM½ðøåO@¼&‚ ÐP@ )Á`PÀ¸@˜ H±xñâ®bÛô«“Wÿ¢sã%a)pn'FÿkÕ`hª‡)ŸªÆnR¶ò£±~Åʹ5Ù†B¡{…}íÇ–Oö¬>¨D„È9¿óaÙ‡ðû%Ï‚q¦QmbœT½…(à¿‘²?‰„'k[ ÄD“íq•*•úúzSww÷ò×ñ¸óï囿tŸ¡^ü;Ë@þàÈ·•«+;Pò#_ý%!À÷üñàTð¿ùÍo „/èkìûŸÏ´/­œp>ÃɧOçјÁç/ü#øKý`)´È4ã !=êýú2ÙÒî?*í@ SšÓX»v­Áç󕔎'5‹Ÿ;hd¸þß í-„ØöP`«¤äGãv’²‰æ÷ܸq#µN™Á¾®®Îd·Ûúüÿ³¢qEáìã³&5)Øâh¼B…‚IHÒ£{¨÷+i‹D Ÿ¤f`$?È>Æ,ƌ΅Í;·±Ü5ëùHå:¹,ì1Aô¨"¯Ï¥\]ÙŒíïÒäÖt›Ë•rš<;d©°Ü²½r»ïDÁIõý3Þ€.{¬3¬ !?:×ÙHÚàER&íñÇÒÒÒ†'žx"Šä‡@ “LüÁm·ÝFM—”¶§9–wÙjâämŽ”=Dé>HƒýÉç[M&Óá‚‚‚ðTm 8o0õë×ó'Ož,à8îBò›×’·ìdÐ%‘H¼1mÚ´Îó`ÀgÖ®]kF£«HýÿA„ä½yý!yøÙ¾å÷û[ˆbL êC ˆ)D€4±óºuë ³³ÓÇó òdàï&è› É­sñÀ6559ÉË%¤î”ûˆêÛ.Ëò'‘HÄw¾´@œW8ˆ ¼BÏ7µCI(a‹ÑhÌ#ä—$ª¯‡Î¢êC ˆó€§ø @ ˆœ—ÚA  @ H€@ "ˆ@  @ “ÿO€\®rJpIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/error.png0000644000076500000240000000123211677672407022752 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<,IDAT8Ë¥SKH”þþ‡«®ëkËTC[RLÊÈ5 o= êÔc©kÑ)¤c‡.$B‹ìD…P‡Š,,‰ˆNY(FFf®»²˜Šþ™¯ƒkÌÌaf>¾o˜1Hb=f¯U˜~±Ï¢ð=USªl.ÞÿZÒõ™kPØ(9X(>ÐHÑ3kR ¹Êýáx_ÃOqg覆8ñt÷ôøã]át½i¨èÝì²X®aš°s–ǽü_ âÏj'{ë…âЉ_§›è¢ºI~}°ÃëÞ^ûOT½ªj5ÕŸÃ羇}vêM¢ æ‚¥¢]b¼7Ë(ŒVçl9Š…o­ XoCn¤™%M£·+ciâ½Ñª¶+ŽCýi@—§I¨zÐÅäW€Š^¾¹5´ @E»‚eÇ6dVÀK>@dW„Á™¼ì¢2äUžÜì/zWæ ’øÑ³'BÕO¥‡lYxoêT3# óÉYdæ(,‹°òšaåÁPG+Ž_½óRrÔ\:ím;gSç ³o*âËà0¦>N @…Ÿê‡aΣ¨¾5à;Þ50¾?©k±òkz65µÃŸ} /qoI’¤0-  R`Z‘UÞ†1ÌŒ ¶U¤³ rjé1B °CÑeqÆŠüåÀßû€@éÞ³H \µ):xuþ4E¡¢ø3‹'ǃïxǃ8>Äõ!ž@}úòÖXï;ÿ”LcÙ§¹S/IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/folder_user.png0000644000076500000240000000133211677672407024133 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<lIDAT}ÁKlLa€á÷?sÎÜ:™ªÐRmC5‘Õ)±¶”¸,$6MacciÁ±³×° EDA$U· .±ª¦w‹V´ãÌœËÿ}Ú˜P‹ñ~=Œ àäÙÛQšºš)³,‚µüá*LCyqî^ðæíëM.‘A%ĆIT”ŸCèο‚ø8<¤ªã›®¤>ßsÉŨ‘¸ÄèÐ ÑÈ'$*S·`=?Úwñ/eBÎƒÑ“Ëø’œIó¢I—ÈÁÍÔ3cÕ~@@‡$¾ßä«_ƪ¥dø1Ä6¢¹a ˆŸ½(xý±K¤¨-~; (¿ )±dÉ1&IŒ<¬D¸X ÑÑÞ4ge߈K*1@G#ÜÔ5EÔ)¢ “δó8³’ƹ„ŠÁ‚*¨ Q @ʨ–P룶ˆÆ£¨õa2WC!ö‡‘Øu@¨z )Tj@#TC0!81¶\f2WËÁç¡#P †ÿQÀÜÑa*~=]4/ˆþIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/group.png0000644000076500000240000000136111677672407022760 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ƒIDAT8ËÑMLÒqpëЖ:uèí€dÒÖ‹Î9¶ˆðÐXºæ@†¨ÈBLÉ¡"d‚¥[ŠþÁ?Z)”èßR Íë¢m¦â{XkƒC Ýš9kËð‰Ø2›Ì<<§ß¾Ÿ={~Q¢vŠš{X¬-8&P{ѲâÄÍâK+D d{½Ÿ0ITÈ~ì |ê¿‹À€#-7p:ÿ”3Ió bg7‹SƒB¶¦Ý ‡±U:“w(ÑÿΆš¡j$hOZÏ(NDoþ¹‘‡…WÑWu ž.5:<9l´¤Â2£†yª îRÔºËÐó¾ %Žb¤&‘›€‰Oïù8DmŒªdÁ0u un%t%¸=QÕÛ"ˆÉ¥´ªÊkÃÇ{Ý’ …{uø3p﹚v JŠÇãß¡4-aJ·5†Ùé«£hœO>¸6°„Þä¹Ã@æ À§bj<ˆc×9}âÏÉ^#/¹‘cBj wxVERTICAL wxALIGN_CENTRE wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE 5,0 150,15 100 0 wxLEFT|wxRIGHT|wxEXPAND|wxALIGN_CENTRE 5,0 wxALIGN_CENTRE wxALIGN_CENTRE wxHORIZONTAL 2 2 10 6 1 wxALL|wxEXPAND|wxALIGN_CENTRE 10 Loading... 1 dicompyler-0.4.1-1/dicompyler/resources/magnifier_zoom_in.png0000644000076500000240000000125011677672407025314 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<:IDAT8Ë¥’[h’qƇ×]D]»Y¬h­ˆXtºÑUÕXZ³¹jHcË!$D «LkXk®¡[+Ûš³}æn²$—sjú³t²ðøIòô}ä¢ÕÅÿ›ßïÿ¼/o€¢ÿɯ‡Áû3<“!t.šp&³/ñlÏÛoT·9J< sþ( øµ;CZ4¼‘ Q9Ó_ÒИÇCr–” Qœzw†°Ò ¢8gS°’0û“°SIx¾ÒêàkkP«®J¼šJQÞ0ƒ- 8ù)“7QO"':ñü0:'/ J¹ì¼~¦2 Oøgà‘™8uÔõVC¤­‡Ì(‚ ³»oìÄ®ËÑ<Æ6ŸõEh˜xŒùÕÈÀ†é8[ò‘VÜnµÁÓ¸ø’qï1l—#O 2E¨©Ÿõ߸ã Xÿ1žªûd{Àkßó}Çq@Z‰Í¢2” KSy¥1DôÛçà ¥s³³°ÎËÉÞSle»bSóúÂ;¸¯rîøIÍDVŽÏ)¼cbòÄ ôa‡d;Ê›Öakci¶ç\EIÁCºÙçá´i\Ä¥.'%épdÅíöl‹Ül“*½e3BænØ·@%Ü~tjMÉo‚…Ò/©ä>kݦ%ê4(0~¯ þêð_ Ø<=»‘Ûqf­zèú!x´WAÔ®À¢l4se¼UjÅÉbHk–]Y´€Í£Ë—Ü:²teÁ%þk~€óðIœÂû¬IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/magnifier_zoom_out.png0000644000076500000240000000122111677672407025513 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<#IDAT8Ë¥’oHaÇeÁ^'ô", ZD½(z½­^XÙ‹þ‘QFT’(¡ËŽÔõge­d³¢¥µ­n™n¶?Ά³9w×î^Þ6fÛív‡ãÛÝŠ`µEÖ‹Ÿç÷{xªTý?Nj^ófZ&la‰ å σÙBÿØÛçIFxͤ"¿ŽÈ¤‘@%d0) LRÂÔ—<iÜ&gÈ.;«©pDdÂËäÁ¦d„fDø™<±lÑY Ö±ô/h¢bàå¤ÈR¼T”½Šøþ“7%`8*|1ZŸDØŠuߘ2²*Ò\Šün: éÂyÌm€| âÎÝÈnÛŽ¹ ›Zµ6U°øÓ:!Á­È#Ê­CŠìœÊå¯;‘¾ÛŽdwx}3¸Ö‹HÔ¬DIÀäN°“?ÆÉ‚TdÇÇ 2ÇOþzsQž­^.–ŒC1H"Ìå‹»«²-”)Æ>ÄEl4:®Dû¥º²oÐíˆkn ÆHË(_L@ð³ˆqw4ƒžW4ÚM.LX®Àzn ßz£®ìGºö,ªi±„‰ËBlSo°pöN ÐØã‹·è”×x œ§þû0[Çß;²Z÷[ M[µOÏl6wàºYáü_TŸX¯ímXc¶·íBÔzÄþ¥XP@åV}­¶«n™Ùp¸ú½ÕÍ ¨tî[²¨cÏâš²ø¯|m»ýá²EµIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/main.xrc0000644000076500000240000004332511677672407022566 0ustar apanchalstaff00000000000000 wxHORIZONTAL wxHORIZONTAL wxRIGHT|wxALIGN_CENTRE_VERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALIGN_CENTRE 3 3 5 5 1 wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE - 1 0 wxALL|wxEXPAND|wxALIGN_CENTRE 0 0, 5 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL wxALIGN_RIGHT 3 1 5 5 1 wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxLEFT|wxRIGHT|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE 4 2 5 5 1,3,5,7 wxALL|wxEXPAND wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 0,2 wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL 448,136 dicompyler_logo.png wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE error.png wxALIGN_BOTTOM 16,16 5,0 wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxHORIZONTAL wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL Ctrl-O Alt+F4 dicompyler-0.4.1-1/dicompyler/resources/pencil.png0000644000076500000240000000070211677672407023074 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<TIDAT8Ë­“?Hq†][ ƒ–p©–@›\ähˆ†Zr0Œ+’† ­¥¦ÊÀàܲÔH‚lH¨H‹î¼à*$:Ôâ2,ÂìÄS’·¼á *ûA ïö=Ïß Í_B\xÓ7¥Ã–§¤×Zc<¸ôëooÖzÚˆBЊëÀÊ" 9Ç!¹B!>gˆv‡”Ú€,ž ’;SRNïƒõXjD°üœ€|D62 ‰(© 8ï`™~“vPÆðÊOáÜoCvsì² ‡ø Î8 ]õ¢ú²ˆ|ÔaÉ„†[ …¿zºˆá/‚½…Nóİ*ðêtuXä¶Á„œÄ°*ðºÚQT.°;kDbՀ̱ <ÝÓy#~=庠òq]…ð}fÄZ„­ˆÎtƒèlT3R´%~>„#w¬ †Ì-¦´ Ç;0æ6mýû7þ”wU\÷&IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/pencil_error.png0000644000076500000240000000132311677672407024305 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿasRGB®ÎégAMA± üa pHYsÃÃÇo¨dtEXtSoftwarePaint.NET v3.5.4>ÌvCIDAT8O½’]H“a†=é ƒNŠ‚NB íŸ5K;±%EMÃˬ “¢ƒB ±È³ÛZ‘‘vPÓÔM©Ü‚~6ó[ê~¢¯lis~*›ÎMÃmï¾iv÷î‹FBåŽzáá=º¯û¹ŸçIKû_oü¥h¹³]°ª ÌŠChy=êìµ)û;Z ð¥¹1Î~Ìë ˜qgJꎈý1x®ñ±wBÅœXÒ…% 12m…»ó "l³P €MUû'à—ø[¤ QGÂì9|h’Á­9‹RДšŒ2‡›¬ƒO'‡ãæîÔÅáጋrjàÕË–w+6 ™…¶]Çàï;ˆ©!5æ#. i¥ìfÿµõu™˜õ“âv]Eà§tà}õ˜vëñ©åÀ³Gû—-‚4œHOOˆ9Û˜ÛN ÎáÏù4÷IZº÷0×wb.¤û`ï÷÷÷÷.¨Î®Ã,gÆœÿ#ôÕ»`º'†ãÕqx Uˆ‚¸JÁÔŠéUâ«ç9¬·s-ÊœIH§×l/ÅDC˜²5èWæb†.6rÑáâŸå”Ó( p=·Ð{#ëR “¬„½QŽ(Û‚ Û†>E>8¦ñ‰»‚{Ôy„FÈ~â)ñ?suûdÏ•­[HqÞ*•¬Fãé è*¶¡_‘CHÐø´|€Î  ù­:0¶cܬ†±*SóÇÐö‚okwÓ5¡N„:‘7—7“îêM„Šˆáb1Tn ¯+Ö›~WæÁ²¼ùøLIEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/plugin.xrc0000644000076500000240000000254311677672407023135 0ustar apanchalstaff00000000000000 wxHORIZONTAL 750,200 wxALL|wxEXPAND|wxALIGN_CENTRE wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 5,5 wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 3 Plugin Manager 1 dicompyler-0.4.1-1/dicompyler/resources/preferences.xrc0000644000076500000240000000172411677672407024140 0ustar apanchalstaff00000000000000 550,350 wxALL|wxEXPAND|wxALIGN_CENTRE 7 wxVERTICAL wxALL|wxEXPAND|wxALIGN_CENTRE 7 Preferences 1 dicompyler-0.4.1-1/dicompyler/resources/table_multiple.png0000644000076500000240000000114411677672407024625 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<öIDAT8Ë¥’AK”Q†ŸoæŽ3:¢¨˜´KÁ ¤…ˆAû ¢QH Ò_ˆV¢ —þˆ"\ E "A¢‚Am)hæ03ßç½çœN_š#¸ÜÍå9Ïyï‰ÌŒÿ)ðäÕþ}3¦Ä´_U©'Ä A´y †¨~^š¾w  f·§'ººÿÖmauëbKQíxúi}@“¡ž .'ª4Ž™´e  9æW¶wDõÃÒÌðõc€£:¬Ô{m¼èZu7ƒZ, YV·J' DøQ .ñÆË÷/dDòQ‹L;ÛÛ˜_Ù6Q]w¡ 5ò¹çG²7Æ;ù‡LÆ€EÍUoÔàÙ—G ÄUê¾—@Ð@ÀÃ+Kx/ùtkÞù\”fr·4wf÷ZðAH L!¨{M3)¿Þ;ps¼‡Ã_€ïÕ:¢Jd3Qš À«ýTÖv™ž8øvÿ6±‹åÍ‘Þ.j I3qY¨¼Ýƒ*k»¸LfäŒup蛀åÙÑ9€7v m":h€ËFLMôµ4ØÚ9 åÄÒˆØúbyóÒ`ß9LÁeàÅ»¯À‘€‹@ÒùÆ€åÙÑ[MU&‡»bÔ>ý™ãÕî"m¹¶"öf±¼yùP´èƒP­% §ÞíWãç?èCGÆ|…IEND®B`‚dicompyler-0.4.1-1/dicompyler/resources/user.png0000644000076500000240000000134511677672407022604 0ustar apanchalstaff00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<wIDAT8Ë¥“mHÓQ‡ *éÅÖ¶VÂj)¤¢_ÔOÕÐ¥²9]¬•m*æŒYŠáë–©e-¬¨h–¶¹µ-ÍÐQšJišLËFŸ¤2Á…ä¦L½¿þJŒ™E8\Îï9÷r¸~üþ‡s£·)kùŒé­PóiPòè.I$«ú¯§"EwDtRŸÆÀ]Š[)tÔ$ÒÏÝB„Ì « 4BtTPM…j„‹Ðq1‰…s± ¤M­*¨ánF‡¦ãíj 4ä#/†…‚¸8ÏAö>ºÇPœìïSPŸÎžœùþ S}|뺆ëyŒ=¹çØÌ9{¦Þës×ûÜ”„ö:`ÂÞ÷;3…e©v™q/#Üîó ;„¦uÁûs fEfí ˜q<^ÂýÖ„–Â(„ss±âë7¬(`%šý™|£<ß Ç—iXËD°”%/±X¿ý‰À„ç`ðîŸaòê6.0Ö5 V);©£ãnÇ d¾N>*^¯?ºÁôaW+§ÅjÖza)Zq\ªMó¨ý9=YÏÒ[ ŽZRõ (m§DVžø2Â’®J½‚áò½ó¶bÜÖÚ ¥š2©‰²6‚ÍTØD¢]€HCp°¨¼ÞÉú C<^Áˆ2„8;+‘–­Cu— ’fà5õˆ 7nÄ: ¸e‰éFüè®Â`Q(ñ >%C%Ás2Ù¥¹hidÔÕ3þ@ö› A+Y¥®aUÄ,•©Z¶…1‹<@UZÍÜ%¶Õ±cô_9ñ]œ„f'®É³;ÖìfÐMjë<›W±ý³Uàó7þ ¿Fß©õ²ÚÜ,IEND®B`‚dicompyler-0.4.1-1/dicompyler/util.py0000755000076500000240000000622611677672407020443 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: ISO-8859-1 -*- # util.py """Several utility functions that don't really belong anywhere.""" # Copyright (c) 2009-2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ from __future__ import with_statement import imp, os, sys import subprocess def platform(): if sys.platform.startswith('win'): return 'windows' elif sys.platform.startswith('darwin'): return 'mac' return 'linux' def GetResourcePath(resource): """Return the specified item from the resources folder.""" if main_is_frozen(): if (platform() == 'mac'): return os.path.join((os.path.join(get_main_dir(), '../Resources')), resource) return os.path.join((os.path.join(get_main_dir(), 'resources')), resource) def GetBasePluginsPath(resource): """Return the specified item from the base plugins folder.""" if main_is_frozen(): if (platform() == 'mac'): return os.path.join((os.path.join(get_main_dir(), '../PlugIns')), resource) return os.path.join((os.path.join(get_main_dir(), 'baseplugins')), resource) # from http://www.py2exe.org/index.cgi/HowToDetermineIfRunningFromExe def main_is_frozen(): return (hasattr(sys, "frozen") or # new py2exe hasattr(sys, "importers") # old py2exe or imp.is_frozen("__main__")) # tools/freeze def get_main_dir(): if main_is_frozen(): return os.path.dirname(sys.executable) return os.path.dirname(__file__) def get_text_resources(resource): """Return the resources that are located in the root folder of the distribution, except for the Mac py2app version, which is located in the Resources folder in the app bundle.""" if (main_is_frozen() and (platform() == 'mac')): resource = GetResourcePath(resource) else: resource = (os.path.join(get_main_dir(), resource)) return resource def open_path(path): """Open the specified path in the system default folder viewer.""" if sys.platform == 'darwin': subprocess.check_call(["open", path]) elif sys.platform == 'linux2': subprocess.check_call(["gnome-open", path]) elif sys.platform == 'win32': subprocess.Popen("explorer " + path) def get_credits(): """Read the credits file and return the data from it.""" developers = [] artists = [] with open(get_text_resources('credits.txt'), 'rU') as cf: credits = cf.readlines() for i, v in enumerate(credits): if (v == "Lead Developer\n"): developers.append(credits[i+1].strip()) if (v == "Developers\n"): for d in credits[i+1:len(credits)]: if (d.strip() == ""): break developers.append(d.strip()) if (v == "Artists\n"): for a in credits[i+1:len(credits)]: if (a.strip() == ""): break artists.append(a.strip()) return {'developers':developers, 'artists':artists} dicompyler-0.4.1-1/dicompyler/wxmpl.py0000755000076500000240000017140611677672407020640 0ustar apanchalstaff00000000000000# Purpose: painless matplotlib embedding for wxPython # Author: Ken McIvor # Portions modified by Aditya Panchal # # Copyright 2005-2009 Illinois Institute of Technology # # See the file "license.txt" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. """ Embedding matplotlib in wxPython applications is straightforward, but the default plotting widget lacks the capabilities necessary for interactive use. WxMpl (wxPython+matplotlib) is a library of components that provide these missing features in the form of a better matplolib FigureCanvas. """ import wx import os.path import weakref import matplotlib matplotlib.use('WXAgg') import numpy as np from matplotlib.axes import _process_plot_var_args from matplotlib.backends.backend_agg import RendererAgg from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure from matplotlib.font_manager import FontProperties from matplotlib.transforms import Bbox __version__ = '1.3.1' __all__ = ['PlotPanel', 'PlotFrame', 'PlotApp', 'StripCharter', 'Channel', 'FigurePrinter', 'PointEvent', 'EVT_POINT', 'SelectionEvent', 'EVT_SELECTION'] # If you are using wxGtk without libgnomeprint and want to use something other # than `lpr' to print you will have to specify that command here. POSTSCRIPT_PRINTING_COMMAND = 'lpr' # Between 0.98.1 and 0.98.3rc there were some significant API changes: # * FigureCanvasWx.draw(repaint=True) became draw(drawDC=None) # * The following events were added: # - figure_enter_event # - figure_leave_event # - axes_enter_event # - axes_leave_event MATPLOTLIB_0_98_3 = '0.98.3' <= matplotlib.__version__ # # Utility functions and classes # def invert_point(x, y, transform): """ Returns a coordinate inverted by the specificed C{Transform}. """ return transform.inverted().transform_point((x, y)) def find_axes(canvas, x, y): """ Finds the C{Axes} within a matplotlib C{FigureCanvas} contains the canvas coordinates C{(x, y)} and returns that axes and the corresponding data coordinates C{xdata, ydata} as a 3-tuple. If no axes contains the specified point a 3-tuple of C{None} is returned. """ evt = matplotlib.backend_bases.MouseEvent('', canvas, x, y) axes = None for a in canvas.get_figure().get_axes(): if a.in_axes(evt): if axes is None: axes = a else: return None, None, None if axes is None: return None, None, None xdata, ydata = invert_point(x, y, axes.transData) return axes, xdata, ydata def get_bbox_lims(bbox): """ Returns the boundaries of the X and Y intervals of a C{Bbox}. """ p0 = bbox.min p1 = bbox.max return (p0[0], p1[0]), (p0[1], p1[1]) def find_selected_axes(canvas, x1, y1, x2, y2): """ Finds the C{Axes} within a matplotlib C{FigureCanvas} that overlaps with a canvas area from C{(x1, y1)} to C{(x1, y1)}. That axes and the corresponding X and Y axes ranges are returned as a 3-tuple. If no axes overlaps with the specified area, or more than one axes overlaps, a 3-tuple of C{None}s is returned. """ axes = None bbox = Bbox.from_extents(x1, y1, x2, y2) for a in canvas.get_figure().get_axes(): if bbox.overlaps(a.bbox): if axes is None: axes = a else: return None, None, None if axes is None: return None, None, None x1, y1, x2, y2 = limit_selection(bbox, axes) xrange, yrange = get_bbox_lims( Bbox.from_extents(x1, y1, x2, y2).inverse_transformed(axes.transData)) return axes, xrange, yrange def limit_selection(bbox, axes): """ Finds the region of a selection C{bbox} which overlaps with the supplied C{axes} and returns it as the 4-tuple C{(xmin, ymin, xmax, ymax)}. """ bxr, byr = get_bbox_lims(bbox) axr, ayr = get_bbox_lims(axes.bbox) xmin = max(bxr[0], axr[0]) xmax = min(bxr[1], axr[1]) ymin = max(byr[0], ayr[0]) ymax = min(byr[1], ayr[1]) return xmin, ymin, xmax, ymax def format_coord(axes, xdata, ydata): """ A C{None}-safe version of {Axes.format_coord()}. """ if xdata is None or ydata is None: return '' return axes.format_coord(xdata, ydata) def toplevel_parent_of_window(window): """ Returns the first top-level parent of a wx.Window """ topwin = window while not isinstance(topwin, wx.TopLevelWindow): topwin = topwin.GetParent() return topwin class AxesLimits: """ Alters the X and Y limits of C{Axes} objects while maintaining a history of the changes. """ def __init__(self, autoscaleUnzoom): self.autoscaleUnzoom = autoscaleUnzoom self.history = weakref.WeakKeyDictionary() def setAutoscaleUnzoom(self, state): """ Enable or disable autoscaling the axes as a result of zooming all the way back out. """ self.limits.setAutoscaleUnzoom(state) def _get_history(self, axes): """ Returns the history list of X and Y limits associated with C{axes}. """ return self.history.setdefault(axes, []) def zoomed(self, axes): """ Returns a boolean indicating whether C{axes} has had its limits altered. """ return not (not self._get_history(axes)) def set(self, axes, xrange, yrange): """ Changes the X and Y limits of C{axes} to C{xrange} and {yrange} respectively. A boolean indicating whether or not the axes should be redraw is returned, because polar axes cannot have their limits changed sensibly. """ if not axes.can_zoom(): return False # The axes limits must be converted to tuples because MPL 0.98.1 # returns the underlying array objects oldRange = tuple(axes.get_xlim()), tuple(axes.get_ylim()) history = self._get_history(axes) history.append(oldRange) axes.set_xlim(xrange) axes.set_ylim(yrange) return True def restore(self, axes): """ Changes the X and Y limits of C{axes} to their previous values. A boolean indicating whether or not the axes should be redraw is returned. """ history = self._get_history(axes) if not history: return False xrange, yrange = history.pop() if self.autoscaleUnzoom and not len(history): axes.autoscale_view() else: axes.set_xlim(xrange) axes.set_ylim(yrange) return True # # Director of the matplotlib canvas # class PlotPanelDirector: """ Encapsulates all of the user-interaction logic required by the C{PlotPanel}, following the Humble Dialog Box pattern proposed by Michael Feathers: U{http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf} """ # TODO: add a programmatic interface to zooming and user interactions # TODO: full support for MPL events def __init__(self, view, zoom=True, selection=True, rightClickUnzoom=True, autoscaleUnzoom=True): """ Create a new director for the C{PlotPanel} C{view}. The keyword arguments C{zoom} and C{selection} have the same meanings as for C{PlotPanel}. """ self.view = view self.zoomEnabled = zoom self.selectionEnabled = selection self.rightClickUnzoom = rightClickUnzoom self.limits = AxesLimits(autoscaleUnzoom) self.leftButtonPoint = None def setSelection(self, state): """ Enable or disable left-click area selection. """ self.selectionEnabled = state def setZoomEnabled(self, state): """ Enable or disable zooming as a result of left-click area selection. """ self.zoomEnabled = state def setAutoscaleUnzoom(self, state): """ Enable or disable autoscaling the axes as a result of zooming all the way back out. """ self.limits.setAutoscaleUnzoom(state) def setRightClickUnzoom(self, state): """ Enable or disable unzooming as a result of right-clicking. """ self.rightClickUnzoom = state def canDraw(self): """ Indicates if plot may be not redrawn due to the presence of a selection box. """ return self.leftButtonPoint is None def zoomed(self, axes): """ Returns a boolean indicating whether or not the plot has been zoomed in as a result of a left-click area selection. """ return self.limits.zoomed(axes) def keyDown(self, evt): """ Handles wxPython key-press events. These events are currently skipped. """ evt.Skip() def keyUp(self, evt): """ Handles wxPython key-release events. These events are currently skipped. """ evt.Skip() def leftButtonDown(self, evt, x, y): """ Handles wxPython left-click events. """ self.leftButtonPoint = (x, y) view = self.view axes, xdata, ydata = find_axes(view, x, y) if axes is not None and self.selectionEnabled and axes.can_zoom(): view.cursor.setCross() view.crosshairs.clear() def leftButtonUp(self, evt, x, y): """ Handles wxPython left-click-release events. """ if self.leftButtonPoint is None: return view = self.view axes, xdata, ydata = find_axes(view, x, y) x0, y0 = self.leftButtonPoint self.leftButtonPoint = None view.rubberband.clear() if x0 == x: if y0 == y and axes is not None: view.notify_point(axes, x, y) view.crosshairs.set(x, y) return elif y0 == y: return xdata = ydata = None axes, xrange, yrange = find_selected_axes(view, x0, y0, x, y) if axes is not None: xdata, ydata = invert_point(x, y, axes.transData) if self.zoomEnabled: if self.limits.set(axes, xrange, yrange): self.view.draw() else: bbox = Bbox.from_extents(x0, y0, x, y) x1, y1, x2, y2 = limit_selection(bbox, axes) self.view.notify_selection(axes, x1, y1, x2, y2) if axes is None: view.cursor.setNormal() elif not axes.can_zoom(): view.cursor.setNormal() view.location.set(format_coord(axes, xdata, ydata)) else: view.crosshairs.set(x, y) view.location.set(format_coord(axes, xdata, ydata)) def rightButtonDown(self, evt, x, y): """ Handles wxPython right-click events. These events are currently skipped. """ evt.Skip() def rightButtonUp(self, evt, x, y): """ Handles wxPython right-click-release events. """ view = self.view axes, xdata, ydata = find_axes(view, x, y) if (axes is not None and self.zoomEnabled and self.rightClickUnzoom and self.limits.restore(axes)): view.crosshairs.clear() view.draw() view.crosshairs.set(x, y) def mouseMotion(self, evt, x, y): """ Handles wxPython mouse motion events, dispatching them based on whether or not a selection is in process and what the cursor is over. """ view = self.view axes, xdata, ydata = find_axes(view, x, y) if self.leftButtonPoint is not None: self.selectionMouseMotion(evt, x, y, axes, xdata, ydata) else: if axes is None: self.canvasMouseMotion(evt, x, y) elif not axes.can_zoom(): self.unzoomableAxesMouseMotion(evt, x, y, axes, xdata, ydata) else: self.axesMouseMotion(evt, x, y, axes, xdata, ydata) def selectionMouseMotion(self, evt, x, y, axes, xdata, ydata): """ Handles wxPython mouse motion events that occur during a left-click area selection. """ view = self.view x0, y0 = self.leftButtonPoint view.rubberband.set(x0, y0, x, y) if axes is None: view.location.clear() else: view.location.set(format_coord(axes, xdata, ydata)) def canvasMouseMotion(self, evt, x, y): """ Handles wxPython mouse motion events that occur over the canvas. """ view = self.view view.cursor.setNormal() view.crosshairs.clear() view.location.clear() def axesMouseMotion(self, evt, x, y, axes, xdata, ydata): """ Handles wxPython mouse motion events that occur over an axes. """ view = self.view view.cursor.setCross() view.crosshairs.set(x, y) view.location.set(format_coord(axes, xdata, ydata)) def unzoomableAxesMouseMotion(self, evt, x, y, axes, xdata, ydata): """ Handles wxPython mouse motion events that occur over an axes that does not support zooming. """ view = self.view view.cursor.setNormal() view.location.set(format_coord(axes, xdata, ydata)) # # Components used by the PlotPanel # class Painter: """ Painters encapsulate the mechanics of drawing some value in a wxPython window and erasing it. Subclasses override template methods to process values and draw them. @cvar PEN: C{wx.Pen} to use (defaults to C{wx.BLACK_PEN}) @cvar BRUSH: C{wx.Brush} to use (defaults to C{wx.TRANSPARENT_BRUSH}) @cvar FUNCTION: Logical function to use (defaults to C{wx.COPY}) @cvar FONT: C{wx.Font} to use (defaults to C{wx.NORMAL_FONT}) @cvar TEXT_FOREGROUND: C{wx.Colour} to use (defaults to C{wx.BLACK}) @cvar TEXT_BACKGROUND: C{wx.Colour} to use (defaults to C{wx.WHITE}) """ PEN = wx.BLACK_PEN BRUSH = wx.TRANSPARENT_BRUSH FUNCTION = wx.COPY FONT = wx.NORMAL_FONT TEXT_FOREGROUND = wx.BLACK TEXT_BACKGROUND = wx.WHITE def __init__(self, view, enabled=True): """ Create a new painter attached to the wxPython window C{view}. The keyword argument C{enabled} has the same meaning as the argument to the C{setEnabled()} method. """ self.view = view self.lastValue = None self.enabled = enabled def setEnabled(self, state): """ Enable or disable this painter. Disabled painters do not draw their values and calls to C{set()} have no effect on them. """ oldState, self.enabled = self.enabled, state if oldState and not self.enabled: self.clear() def set(self, *value): """ Update this painter's value and then draw it. Values may not be C{None}, which is used internally to represent the absence of a current value. """ if self.enabled: value = self.formatValue(value) self._paint(value, None) def redraw(self, dc=None): """ Redraw this painter's current value. """ value = self.lastValue self.lastValue = None self._paint(value, dc) def clear(self, dc=None): """ Clear the painter's current value from the screen and the painter itself. """ if self.lastValue is not None: self._paint(None, dc) def _paint(self, value, dc): """ Draws a previously processed C{value} on this painter's window. """ if dc is None: dc = wx.ClientDC(self.view) dc.SetPen(self.PEN) dc.SetBrush(self.BRUSH) dc.SetFont(self.FONT) dc.SetTextForeground(self.TEXT_FOREGROUND) dc.SetTextBackground(self.TEXT_BACKGROUND) dc.SetLogicalFunction(self.FUNCTION) dc.BeginDrawing() if self.lastValue is not None: self.clearValue(dc, self.lastValue) self.lastValue = None if value is not None: self.drawValue(dc, value) self.lastValue = value dc.EndDrawing() def formatValue(self, value): """ Template method that processes the C{value} tuple passed to the C{set()} method, returning the processed version. """ return value def drawValue(self, dc, value): """ Template method that draws a previously processed C{value} using the wxPython device context C{dc}. This DC has already been configured, so calls to C{BeginDrawing()} and C{EndDrawing()} may not be made. """ pass def clearValue(self, dc, value): """ Template method that clears a previously processed C{value} that was previously drawn, using the wxPython device context C{dc}. This DC has already been configured, so calls to C{BeginDrawing()} and C{EndDrawing()} may not be made. """ pass class LocationPainter(Painter): """ Draws a text message containing the current position of the mouse in the lower left corner of the plot. """ PADDING = 2 PEN = wx.WHITE_PEN BRUSH = wx.WHITE_BRUSH def formatValue(self, value): """ Extracts a string from the 1-tuple C{value}. """ return value[0] def get_XYWH(self, dc, value): """ Returns the upper-left coordinates C{(X, Y)} for the string C{value} its width and height C{(W, H)}. """ height = dc.GetSize()[1] w, h = dc.GetTextExtent(value) x = self.PADDING y = int(height - (h + self.PADDING)) return x, y, w, h def drawValue(self, dc, value): """ Draws the string C{value} in the lower left corner of the plot. """ x, y, w, h = self.get_XYWH(dc, value) dc.DrawText(value, x, y) def clearValue(self, dc, value): """ Clears the string C{value} from the lower left corner of the plot by painting a white rectangle over it. """ x, y, w, h = self.get_XYWH(dc, value) dc.DrawRectangle(x, y, w, h) class CrosshairPainter(Painter): """ Draws crosshairs through the current position of the mouse. """ PEN = wx.WHITE_PEN FUNCTION = wx.XOR def formatValue(self, value): """ Converts the C{(X, Y)} mouse coordinates from matplotlib to wxPython. """ x, y = value return int(x), int(self.view.get_figure().bbox.height - y) def drawValue(self, dc, value): """ Draws crosshairs through the C{(X, Y)} coordinates. """ dc.CrossHair(*value) def clearValue(self, dc, value): """ Clears the crosshairs drawn through the C{(X, Y)} coordinates. """ dc.CrossHair(*value) class RubberbandPainter(Painter): """ Draws a selection rubberband from one point to another. """ PEN = wx.WHITE_PEN FUNCTION = wx.XOR def formatValue(self, value): """ Converts the C{(x1, y1, x2, y2)} mouse coordinates from matplotlib to wxPython. """ x1, y1, x2, y2 = value height = self.view.get_figure().bbox.height y1 = height - y1 y2 = height - y2 if x2 < x1: x1, x2 = x2, x1 if y2 < y1: y1, y2 = y2, y1 return [int(z) for z in (x1, y1, x2-x1, y2-y1)] def drawValue(self, dc, value): """ Draws the selection rubberband around the rectangle C{(x1, y1, x2, y2)}. """ dc.DrawRectangle(*value) def clearValue(self, dc, value): """ Clears the selection rubberband around the rectangle C{(x1, y1, x2, y2)}. """ dc.DrawRectangle(*value) class CursorChanger: """ Manages the current cursor of a wxPython window, allowing it to be switched between a normal arrow and a square cross. """ def __init__(self, view, enabled=True): """ Create a CursorChanger attached to the wxPython window C{view}. The keyword argument C{enabled} has the same meaning as the argument to the C{setEnabled()} method. """ self.view = view self.cursor = wx.CURSOR_DEFAULT self.enabled = enabled def setEnabled(self, state): """ Enable or disable this cursor changer. When disabled, the cursor is reset to the normal arrow and calls to the C{set()} methods have no effect. """ oldState, self.enabled = self.enabled, state if oldState and not self.enabled and self.cursor != wx.CURSOR_DEFAULT: self.cursor = wx.CURSOR_DEFAULT self.view.SetCursor(wx.STANDARD_CURSOR) def setNormal(self): """ Change the cursor of the associated window to a normal arrow. """ if self.cursor != wx.CURSOR_DEFAULT and self.enabled: self.cursor = wx.CURSOR_DEFAULT self.view.SetCursor(wx.STANDARD_CURSOR) def setCross(self): """ Change the cursor of the associated window to a square cross. """ if self.cursor != wx.CURSOR_CROSS and self.enabled: self.cursor = wx.CURSOR_CROSS self.view.SetCursor(wx.CROSS_CURSOR) # # Printing Framework # # PostScript resolutions for the various WX print qualities PS_DPI_HIGH_QUALITY = 600 PS_DPI_MEDIUM_QUALITY = 300 PS_DPI_LOW_QUALITY = 150 PS_DPI_DRAFT_QUALITY = 72 def update_postscript_resolution(printData): """ Sets the default wx.PostScriptDC resolution from a wx.PrintData's quality setting. This is a workaround for WX ignoring the quality setting and defaulting to 72 DPI. Unfortunately wx.Printout.GetDC() returns a wx.DC object instead of the actual class, so it's impossible to set the resolution on the DC itself. Even more unforuntately, printing with libgnomeprint appears to always be stuck at 72 DPI. """ if not callable(getattr(wx, 'PostScriptDC_SetResolution', None)): return quality = printData.GetQuality() if quality > 0: dpi = quality elif quality == wx.PRINT_QUALITY_HIGH: dpi = PS_DPI_HIGH_QUALITY elif quality == wx.PRINT_QUALITY_MEDIUM: dpi = PS_DPI_MEDIUM_QUALITY elif quality == wx.PRINT_QUALITY_LOW: dpi = PS_DPI_LOW_QUALITY elif quality == wx.PRINT_QUALITY_DRAFT: dpi = PS_DPI_DRAFT_QUALITY else: dpi = PS_DPI_HIGH_QUALITY wx.PostScriptDC_SetResolution(dpi) class FigurePrinter: """ Provides a simplified interface to the wxPython printing framework that's designed for printing matplotlib figures. """ def __init__(self, view, printData=None): """ Create a new C{FigurePrinter} associated with the wxPython widget C{view}. The keyword argument C{printData} supplies a C{wx.PrintData} object containing the default printer settings. """ self.view = view if printData is None: printData = wx.PrintData() self.setPrintData(printData) def getPrintData(self): """ Return the current printer settings in their C{wx.PrintData} object. """ return self.pData def setPrintData(self, printData): """ Use the printer settings in C{printData}. """ self.pData = printData update_postscript_resolution(self.pData) def pageSetup(self): dlg = wx.PrintDialog(self.view) pdData = dlg.GetPrintDialogData() pdData.SetPrintData(self.pData) if dlg.ShowModal() == wx.ID_OK: self.setPrintData(pdData.GetPrintData()) dlg.Destroy() def previewFigure(self, figure, title=None): """ Open a "Print Preview" window for the matplotlib chart C{figure}. The keyword argument C{title} provides the printing framework with a title for the print job. """ topwin = toplevel_parent_of_window(self.view) fpo = FigurePrintout(figure, title) fpo4p = FigurePrintout(figure, title) preview = wx.PrintPreview(fpo, fpo4p, self.pData) frame = wx.PreviewFrame(preview, topwin, 'Print Preview') if self.pData.GetOrientation() == wx.PORTRAIT: frame.SetSize(wx.Size(450, 625)) else: frame.SetSize(wx.Size(600, 500)) frame.Initialize() frame.Show(True) def printFigure(self, figure, title=None): """ Open a "Print" dialog to print the matplotlib chart C{figure}. The keyword argument C{title} provides the printing framework with a title for the print job. """ pdData = wx.PrintDialogData() pdData.SetPrintData(self.pData) printer = wx.Printer(pdData) fpo = FigurePrintout(figure, title) if printer.Print(self.view, fpo, True): self.setPrintData(pdData.GetPrintData()) class FigurePrintout(wx.Printout): """ Render a matplotlib C{Figure} to a page or file using wxPython's printing framework. """ ASPECT_RECTANGULAR = 1 ASPECT_SQUARE = 2 def __init__(self, figure, title=None, size=None, aspectRatio=None): """ Create a printout for the matplotlib chart C{figure}. The keyword argument C{title} provides the printing framework with a title for the print job. The keyword argument C{size} specifies how to scale the figure, from 1 to 100 percent. The keyword argument C{aspectRatio} determines whether the printed figure will be rectangular or square. """ self.figure = figure figTitle = figure.gca().title.get_text() if not figTitle: figTitle = title or 'Matplotlib Figure' if size is None: size = 100 elif size < 1 or size > 100: raise ValueError('invalid figure size') self.size = size if aspectRatio is None: aspectRatio = self.ASPECT_RECTANGULAR elif (aspectRatio != self.ASPECT_RECTANGULAR and aspectRatio != self.ASPECT_SQUARE): raise ValueError('invalid aspect ratio') self.aspectRatio = aspectRatio wx.Printout.__init__(self, figTitle) def GetPageInfo(self): """ Overrides wx.Printout.GetPageInfo() to provide the printing framework with the number of pages in this print job. """ return (1, 1, 1, 1) def HasPage(self, pageNumber): """ Overrides wx.Printout.GetPageInfo() to tell the printing framework of the specified page exists. """ return pageNumber == 1 def OnPrintPage(self, pageNumber): """ Overrides wx.Printout.OnPrintPage() to render the matplotlib figure to a printing device context. """ # % of printable area to use imgPercent = max(1, min(100, self.size)) / 100.0 # ratio of the figure's width to its height if self.aspectRatio == self.ASPECT_RECTANGULAR: aspectRatio = 1.61803399 elif self.aspectRatio == self.ASPECT_SQUARE: aspectRatio = 1.0 else: raise ValueError('invalid aspect ratio') # Device context to draw the page dc = self.GetDC() # PPI_P: Pixels Per Inch of the Printer wPPI_P, hPPI_P = [float(x) for x in self.GetPPIPrinter()] PPI_P = (wPPI_P + hPPI_P)/2.0 # PPI: Pixels Per Inch of the DC if self.IsPreview(): wPPI, hPPI = [float(x) for x in self.GetPPIScreen()] else: wPPI, hPPI = wPPI_P, hPPI_P PPI = (wPPI + hPPI)/2.0 # Pg_Px: Size of the page (pixels) wPg_Px, hPg_Px = [float(x) for x in self.GetPageSizePixels()] # Dev_Px: Size of the DC (pixels) wDev_Px, hDev_Px = [float(x) for x in self.GetDC().GetSize()] # Pg: Size of the page (inches) wPg = wPg_Px / PPI_P hPg = hPg_Px / PPI_P # minimum margins (inches) wM = 0.75 hM = 0.75 # Area: printable area within the margins (inches) wArea = wPg - 2*wM hArea = hPg - 2*hM # Fig: printing size of the figure # hFig is at a maximum when wFig == wArea max_hFig = wArea / aspectRatio hFig = min(imgPercent * hArea, max_hFig) wFig = aspectRatio * hFig # scale factor = device size / page size (equals 1.0 for real printing) S = ((wDev_Px/PPI)/wPg + (hDev_Px/PPI)/hPg)/2.0 # Fig_S: scaled printing size of the figure (inches) # M_S: scaled minimum margins (inches) wFig_S = S * wFig hFig_S = S * hFig wM_S = S * wM hM_S = S * hM # Fig_Dx: scaled printing size of the figure (device pixels) # M_Dx: scaled minimum margins (device pixels) wFig_Dx = int(S * PPI * wFig) hFig_Dx = int(S * PPI * hFig) wM_Dx = int(S * PPI * wM) hM_Dx = int(S * PPI * hM) image = self.render_figure_as_image(wFig, hFig, PPI) if self.IsPreview(): image = image.Scale(wFig_Dx, hFig_Dx) self.GetDC().DrawBitmap(image.ConvertToBitmap(), wM_Dx, hM_Dx, False) return True def render_figure_as_image(self, wFig, hFig, dpi): """ Renders a matplotlib figure using the Agg backend and stores the result in a C{wx.Image}. The arguments C{wFig} and {hFig} are the width and height of the figure, and C{dpi} is the dots-per-inch to render at. """ figure = self.figure old_dpi = figure.dpi figure.dpi = dpi old_width = figure.get_figwidth() figure.set_figwidth(wFig) old_height = figure.get_figheight() figure.set_figheight(hFig) old_frameon = figure.frameon figure.frameon = False wFig_Px = int(figure.bbox.width) hFig_Px = int(figure.bbox.height) agg = RendererAgg(wFig_Px, hFig_Px, dpi) figure.draw(agg) figure.dpi = old_dpi figure.set_figwidth(old_width) figure.set_figheight(old_height) figure.frameon = old_frameon image = wx.EmptyImage(wFig_Px, hFig_Px) image.SetData(agg.tostring_rgb()) return image # # wxPython event interface for the PlotPanel and PlotFrame # EVT_POINT_ID = wx.NewId() def EVT_POINT(win, id, func): """ Register to receive wxPython C{PointEvent}s from a C{PlotPanel} or C{PlotFrame}. """ win.Connect(id, -1, EVT_POINT_ID, func) class PointEvent(wx.PyCommandEvent): """ wxPython event emitted when a left-click-release occurs in a matplotlib axes of a window without an area selection. @cvar axes: matplotlib C{Axes} which was left-clicked @cvar x: matplotlib X coordinate @cvar y: matplotlib Y coordinate @cvar xdata: axes X coordinate @cvar ydata: axes Y coordinate """ def __init__(self, id, axes, x, y): """ Create a new C{PointEvent} for the matplotlib coordinates C{(x, y)} of an C{axes}. """ wx.PyCommandEvent.__init__(self, EVT_POINT_ID, id) self.axes = axes self.x = x self.y = y self.xdata, self.ydata = invert_point(x, y, axes.transData) def Clone(self): return PointEvent(self.GetId(), self.axes, self.x, self.y) EVT_SELECTION_ID = wx.NewId() def EVT_SELECTION(win, id, func): """ Register to receive wxPython C{SelectionEvent}s from a C{PlotPanel} or C{PlotFrame}. """ win.Connect(id, -1, EVT_SELECTION_ID, func) class SelectionEvent(wx.PyCommandEvent): """ wxPython event emitted when an area selection occurs in a matplotlib axes of a window for which zooming has been disabled. The selection is described by a rectangle from C{(x1, y1)} to C{(x2, y2)}, of which only one point is required to be inside the axes. @cvar axes: matplotlib C{Axes} which was left-clicked @cvar x1: matplotlib x1 coordinate @cvar y1: matplotlib y1 coordinate @cvar x2: matplotlib x2 coordinate @cvar y2: matplotlib y2 coordinate @cvar x1data: axes x1 coordinate @cvar y1data: axes y1 coordinate @cvar x2data: axes x2 coordinate @cvar y2data: axes y2 coordinate """ def __init__(self, id, axes, x1, y1, x2, y2): """ Create a new C{SelectionEvent} for the area described by the rectangle from C{(x1, y1)} to C{(x2, y2)} in an C{axes}. """ wx.PyCommandEvent.__init__(self, EVT_SELECTION_ID, id) self.axes = axes self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.x1data, self.y1data = invert_point(x1, y1, axes.transData) self.x2data, self.y2data = invert_point(x2, y2, axes.transData) def Clone(self): return SelectionEvent(self.GetId(), self.axes, self.x1, self.y1, self.x2, self.y2) # # Matplotlib canvas in a wxPython window # class PlotPanel(FigureCanvasWxAgg): """ A matplotlib canvas suitable for embedding in wxPython applications. """ def __init__(self, parent, id, size=(6.0, 3.70), dpi=96, cursor=True, location=True, crosshairs=True, selection=True, zoom=True, autoscaleUnzoom=True): """ Creates a new PlotPanel window that is the child of the wxPython window C{parent} with the wxPython identifier C{id}. The keyword arguments C{size} and {dpi} are used to create the matplotlib C{Figure} associated with this canvas. C{size} is the desired width and height of the figure, in inches, as the 2-tuple C{(width, height)}. C{dpi} is the dots-per-inch of the figure. The keyword arguments C{cursor}, C{location}, C{crosshairs}, C{selection}, C{zoom}, and C{autoscaleUnzoom} enable or disable various user interaction features that are descibed in their associated C{set()} methods. """ FigureCanvasWxAgg.__init__(self, parent, id, Figure(size, dpi)) self.insideOnPaint = False self.cursor = CursorChanger(self, cursor) self.location = LocationPainter(self, location) self.crosshairs = CrosshairPainter(self, crosshairs) self.rubberband = RubberbandPainter(self, selection) rightClickUnzoom = True # for now this is default behavior self.director = PlotPanelDirector(self, zoom, selection, rightClickUnzoom, autoscaleUnzoom) self.figure.set_edgecolor('black') self.figure.set_facecolor('white') self.SetBackgroundColour(wx.WHITE) # find the toplevel parent window and register an activation event # handler that is keyed to the id of this PlotPanel topwin = toplevel_parent_of_window(self) topwin.Connect(-1, self.GetId(), wx.wxEVT_ACTIVATE, self.OnActivate) wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) def OnActivate(self, evt): """ Handles the wxPython window activation event. """ if not isinstance(self, FigureCanvasWxAgg): return if not evt.GetActive(): self.cursor.setNormal() self.location.clear() self.crosshairs.clear() self.rubberband.clear() evt.Skip() def OnEraseBackground(self, evt): """ Overrides the wxPython backround repainting event to reduce flicker. """ pass def OnDestroy(self, evt): """ Handles the wxPython window destruction event. """ if self.GetId() == evt.GetEventObject().GetId(): # unregister the activation event handler for this PlotPanel topwin = toplevel_parent_of_window(self) topwin.Disconnect(-1, self.GetId(), wx.wxEVT_ACTIVATE) def _onPaint(self, evt): """ Overrides the C{FigureCanvasWxAgg} paint event to redraw the crosshairs, etc. """ # avoid wxPyDeadObject errors if not isinstance(self, FigureCanvasWxAgg): return self.insideOnPaint = True FigureCanvasWxAgg._onPaint(self, evt) self.insideOnPaint = False dc = wx.PaintDC(self) self.location.redraw(dc) self.crosshairs.redraw(dc) self.rubberband.redraw(dc) def get_figure(self): """ Returns the figure associated with this canvas. """ return self.figure def set_cursor(self, state): """ Enable or disable the changing mouse cursor. When enabled, the cursor changes from the normal arrow to a square cross when the mouse enters a matplotlib axes on this canvas. """ self.cursor.setEnabled(state) def set_location(self, state): """ Enable or disable the display of the matplotlib axes coordinates of the mouse in the lower left corner of the canvas. """ self.location.setEnabled(state) def set_crosshairs(self, state): """ Enable or disable drawing crosshairs through the mouse cursor when it is inside a matplotlib axes. """ self.crosshairs.setEnabled(state) def set_selection(self, state): """ Enable or disable area selections, where user selects a rectangular area of the canvas by left-clicking and dragging the mouse. """ self.rubberband.setEnabled(state) self.director.setSelection(state) def set_zoom(self, state): """ Enable or disable zooming in when the user makes an area selection and zooming out again when the user right-clicks. """ self.director.setZoomEnabled(state) def set_autoscale_unzoom(self, state): """ Enable or disable automatic view rescaling when the user zooms out to the initial figure. """ self.director.setAutoscaleUnzoom(state) def zoomed(self, axes): """ Returns a boolean indicating whether or not the C{axes} is zoomed in. """ return self.director.zoomed(axes) def draw(self, **kwds): """ Draw the associated C{Figure} onto the screen. """ # don't redraw if the left mouse button is down and avoid # wxPyDeadObject errors if (not self.director.canDraw() or not isinstance(self, FigureCanvasWxAgg)): return if MATPLOTLIB_0_98_3: FigureCanvasWxAgg.draw(self, kwds.get('drawDC', None)) else: FigureCanvasWxAgg.draw(self, kwds.get('repaint', True)) # Don't redraw the decorations when called by _onPaint() if not self.insideOnPaint: self.location.redraw() self.crosshairs.redraw() self.rubberband.redraw() def notify_point(self, axes, x, y): """ Called by the associated C{PlotPanelDirector} to emit a C{PointEvent}. """ wx.PostEvent(self, PointEvent(self.GetId(), axes, x, y)) def notify_selection(self, axes, x1, y1, x2, y2): """ Called by the associated C{PlotPanelDirector} to emit a C{SelectionEvent}. """ wx.PostEvent(self, SelectionEvent(self.GetId(), axes, x1, y1, x2, y2)) def _get_canvas_xy(self, evt): """ Returns the X and Y coordinates of a wxPython event object converted to matplotlib canavas coordinates. """ return evt.GetX(), int(self.figure.bbox.height - evt.GetY()) def _onKeyDown(self, evt): """ Overrides the C{FigureCanvasWxAgg} key-press event handler, dispatching the event to the associated C{PlotPanelDirector}. """ self.director.keyDown(evt) def _onKeyUp(self, evt): """ Overrides the C{FigureCanvasWxAgg} key-release event handler, dispatching the event to the associated C{PlotPanelDirector}. """ self.director.keyUp(evt) def _onLeftButtonDown(self, evt): """ Overrides the C{FigureCanvasWxAgg} left-click event handler, dispatching the event to the associated C{PlotPanelDirector}. """ x, y = self._get_canvas_xy(evt) self.director.leftButtonDown(evt, x, y) def _onLeftButtonUp(self, evt): """ Overrides the C{FigureCanvasWxAgg} left-click-release event handler, dispatching the event to the associated C{PlotPanelDirector}. """ x, y = self._get_canvas_xy(evt) self.director.leftButtonUp(evt, x, y) def _onRightButtonDown(self, evt): """ Overrides the C{FigureCanvasWxAgg} right-click event handler, dispatching the event to the associated C{PlotPanelDirector}. """ x, y = self._get_canvas_xy(evt) self.director.rightButtonDown(evt, x, y) def _onRightButtonUp(self, evt): """ Overrides the C{FigureCanvasWxAgg} right-click-release event handler, dispatching the event to the associated C{PlotPanelDirector}. """ x, y = self._get_canvas_xy(evt) self.director.rightButtonUp(evt, x, y) def _onMotion(self, evt): """ Overrides the C{FigureCanvasWxAgg} mouse motion event handler, dispatching the event to the associated C{PlotPanelDirector}. """ x, y = self._get_canvas_xy(evt) self.director.mouseMotion(evt, x, y) # # Matplotlib canvas in a top-level wxPython window # class PlotFrame(wx.Frame): """ A matplotlib canvas embedded in a wxPython top-level window. @cvar ABOUT_TITLE: Title of the "About" dialog. @cvar ABOUT_MESSAGE: Contents of the "About" dialog. """ ABOUT_TITLE = 'About wxmpl.PlotFrame' ABOUT_MESSAGE = ('wxmpl.PlotFrame %s\n' % __version__ + 'Written by Ken McIvor \n' + 'Copyright 2005-2009 Illinois Institute of Technology') def __init__(self, parent, id, title, size=(6.0, 3.7), dpi=96, cursor=True, location=True, crosshairs=True, selection=True, zoom=True, autoscaleUnzoom=True, **kwds): """ Creates a new PlotFrame top-level window that is the child of the wxPython window C{parent} with the wxPython identifier C{id} and the title of C{title}. All of the named keyword arguments to this constructor have the same meaning as those arguments to the constructor of C{PlotPanel}. Any additional keyword arguments are passed to the constructor of C{wx.Frame}. """ wx.Frame.__init__(self, parent, id, title, **kwds) self.panel = PlotPanel(self, -1, size, dpi, cursor, location, crosshairs, selection, zoom) pData = wx.PrintData() pData.SetPaperId(wx.PAPER_LETTER) if callable(getattr(pData, 'SetPrinterCommand', None)): pData.SetPrinterCommand(POSTSCRIPT_PRINTING_COMMAND) self.printer = FigurePrinter(self, pData) self.create_menus() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.panel, 1, wx.ALL|wx.EXPAND, 5) self.SetSizer(sizer) self.Fit() def create_menus(self): mainMenu = wx.MenuBar() menu = wx.Menu() id = wx.NewId() menu.Append(id, '&Save As...\tCtrl+S', 'Save a copy of the current plot') wx.EVT_MENU(self, id, self.OnMenuFileSave) menu.AppendSeparator() if wx.Platform != '__WXMAC__': id = wx.NewId() menu.Append(id, 'Page Set&up...', 'Set the size and margins of the printed figure') wx.EVT_MENU(self, id, self.OnMenuFilePageSetup) id = wx.NewId() menu.Append(id, 'Print Pre&view...', 'Preview the print version of the current plot') wx.EVT_MENU(self, id, self.OnMenuFilePrintPreview) id = wx.NewId() menu.Append(id, '&Print...\tCtrl+P', 'Print the current plot') wx.EVT_MENU(self, id, self.OnMenuFilePrint) menu.AppendSeparator() id = wx.NewId() menu.Append(id, '&Close Window\tCtrl+W', 'Close the current plot window') wx.EVT_MENU(self, id, self.OnMenuFileClose) mainMenu.Append(menu, '&File') menu = wx.Menu() id = wx.NewId() menu.Append(id, '&About...', 'Display version information') wx.EVT_MENU(self, id, self.OnMenuHelpAbout) mainMenu.Append(menu, '&Help') self.SetMenuBar(mainMenu) def OnMenuFileSave(self, evt): """ Handles File->Save menu events. """ fileName = wx.FileSelector('Save Plot', default_extension='png', wildcard=('Portable Network Graphics (*.png)|*.png|' + 'Encapsulated Postscript (*.eps)|*.eps|All files (*.*)|*.*'), parent=self, flags=wx.SAVE|wx.OVERWRITE_PROMPT) if not fileName: return path, ext = os.path.splitext(fileName) ext = ext[1:].lower() if ext != 'png' and ext != 'eps': error_message = ( 'Only the PNG and EPS image formats are supported.\n' 'A file extension of `png\' or `eps\' must be used.') wx.MessageBox(error_message, 'Error - plotit', parent=self, style=wx.OK|wx.ICON_ERROR) return try: self.panel.print_figure(fileName) except IOError, e: if e.strerror: err = e.strerror else: err = e wx.MessageBox('Could not save file: %s' % err, 'Error - plotit', parent=self, style=wx.OK|wx.ICON_ERROR) def OnMenuFilePageSetup(self, evt): """ Handles File->Page Setup menu events """ self.printer.pageSetup() def OnMenuFilePrintPreview(self, evt): """ Handles File->Print Preview menu events """ self.printer.previewFigure(self.get_figure()) def OnMenuFilePrint(self, evt): """ Handles File->Print menu events """ self.printer.printFigure(self.get_figure()) def OnMenuFileClose(self, evt): """ Handles File->Close menu events. """ self.Close() def OnMenuHelpAbout(self, evt): """ Handles Help->About menu events. """ wx.MessageBox(self.ABOUT_MESSAGE, self.ABOUT_TITLE, parent=self, style=wx.OK) def get_figure(self): """ Returns the figure associated with this canvas. """ return self.panel.figure def set_cursor(self, state): """ Enable or disable the changing mouse cursor. When enabled, the cursor changes from the normal arrow to a square cross when the mouse enters a matplotlib axes on this canvas. """ self.panel.set_cursor(state) def set_location(self, state): """ Enable or disable the display of the matplotlib axes coordinates of the mouse in the lower left corner of the canvas. """ self.panel.set_location(state) def set_crosshairs(self, state): """ Enable or disable drawing crosshairs through the mouse cursor when it is inside a matplotlib axes. """ self.panel.set_crosshairs(state) def set_selection(self, state): """ Enable or disable area selections, where user selects a rectangular area of the canvas by left-clicking and dragging the mouse. """ self.panel.set_selection(state) def set_zoom(self, state): """ Enable or disable zooming in when the user makes an area selection and zooming out again when the user right-clicks. """ self.panel.set_zoom(state) def set_autoscale_unzoom(self, state): """ Enable or disable automatic view rescaling when the user zooms out to the initial figure. """ self.panel.set_autoscale_unzoom(state) def draw(self): """ Draw the associated C{Figure} onto the screen. """ self.panel.draw() # # wxApp providing a matplotlib canvas in a top-level wxPython window # class PlotApp(wx.App): """ A wxApp that provides a matplotlib canvas embedded in a wxPython top-level window, encapsulating wxPython's nuts and bolts. @cvar ABOUT_TITLE: Title of the "About" dialog. @cvar ABOUT_MESSAGE: Contents of the "About" dialog. """ ABOUT_TITLE = None ABOUT_MESSAGE = None def __init__(self, title="WxMpl", size=(6.0, 3.7), dpi=96, cursor=True, location=True, crosshairs=True, selection=True, zoom=True, **kwds): """ Creates a new PlotApp, which creates a PlotFrame top-level window. The keyword argument C{title} specifies the title of this top-level window. All of other the named keyword arguments to this constructor have the same meaning as those arguments to the constructor of C{PlotPanel}. Any additional keyword arguments are passed to the constructor of C{wx.App}. """ self.title = title self.size = size self.dpi = dpi self.cursor = cursor self.location = location self.crosshairs = crosshairs self.selection = selection self.zoom = zoom wx.App.__init__(self, **kwds) def OnInit(self): self.frame = panel = PlotFrame(None, -1, self.title, self.size, self.dpi, self.cursor, self.location, self.crosshairs, self.selection, self.zoom) if self.ABOUT_TITLE is not None: panel.ABOUT_TITLE = self.ABOUT_TITLE if self.ABOUT_MESSAGE is not None: panel.ABOUT_MESSAGE = self.ABOUT_MESSAGE panel.Show(True) return True def get_figure(self): """ Returns the figure associated with this canvas. """ return self.frame.get_figure() def set_cursor(self, state): """ Enable or disable the changing mouse cursor. When enabled, the cursor changes from the normal arrow to a square cross when the mouse enters a matplotlib axes on this canvas. """ self.frame.set_cursor(state) def set_location(self, state): """ Enable or disable the display of the matplotlib axes coordinates of the mouse in the lower left corner of the canvas. """ self.frame.set_location(state) def set_crosshairs(self, state): """ Enable or disable drawing crosshairs through the mouse cursor when it is inside a matplotlib axes. """ self.frame.set_crosshairs(state) def set_selection(self, state): """ Enable or disable area selections, where user selects a rectangular area of the canvas by left-clicking and dragging the mouse. """ self.frame.set_selection(state) def set_zoom(self, state): """ Enable or disable zooming in when the user makes an area selection and zooming out again when the user right-clicks. """ self.frame.set_zoom(state) def draw(self): """ Draw the associated C{Figure} onto the screen. """ self.frame.draw() # # Automatically resizing vectors and matrices # class VectorBuffer: """ Manages a Numerical Python vector, automatically growing it as necessary to accomodate new entries. """ def __init__(self): self.data = np.zeros((16,), np.Float) self.nextRow = 0 def clear(self): """ Zero and reset this buffer without releasing the underlying array. """ self.data[:] = 0.0 self.nextRow = 0 def reset(self): """ Zero and reset this buffer, releasing the underlying array. """ self.data = np.zeros((16,), np.Float) self.nextRow = 0 def append(self, point): """ Append a new entry to the end of this buffer's vector. """ nextRow = self.nextRow data = self.data resize = False if nextRow == data.shape[0]: nR = int(np.ceil(self.data.shape[0]*1.5)) resize = True if resize: self.data = np.zeros((nR,), np.Float) self.data[0:data.shape[0]] = data self.data[nextRow] = point self.nextRow += 1 def getData(self): """ Returns the current vector or C{None} if the buffer contains no data. """ if self.nextRow == 0: return None else: return self.data[0:self.nextRow] class MatrixBuffer: """ Manages a Numerical Python matrix, automatically growing it as necessary to accomodate new rows of entries. """ def __init__(self): self.data = np.zeros((16, 1), np.Float) self.nextRow = 0 def clear(self): """ Zero and reset this buffer without releasing the underlying array. """ self.data[:, :] = 0.0 self.nextRow = 0 def reset(self): """ Zero and reset this buffer, releasing the underlying array. """ self.data = np.zeros((16, 1), np.Float) self.nextRow = 0 def append(self, row): """ Append a new row of entries to the end of this buffer's matrix. """ row = np.asarray(row, np.Float) nextRow = self.nextRow data = self.data nPts = row.shape[0] if nPts == 0: return resize = True if nextRow == data.shape[0]: nC = data.shape[1] nR = int(np.ceil(self.data.shape[0]*1.5)) if nC < nPts: nC = nPts elif data.shape[1] < nPts: nR = data.shape[0] nC = nPts else: resize = False if resize: self.data = np.zeros((nR, nC), np.Float) rowEnd, colEnd = data.shape self.data[0:rowEnd, 0:colEnd] = data self.data[nextRow, 0:nPts] = row self.nextRow += 1 def getData(self): """ Returns the current matrix or C{None} if the buffer contains no data. """ if self.nextRow == 0: return None else: return self.data[0:self.nextRow, :] # # Utility functions used by the StripCharter # def make_delta_bbox(X1, Y1, X2, Y2): """ Returns a C{Bbox} describing the range of difference between two sets of X and Y coordinates. """ return make_bbox(get_delta(X1, X2), get_delta(Y1, Y2)) def get_delta(X1, X2): """ Returns the vector of contiguous, different points between two vectors. """ n1 = X1.shape[0] n2 = X2.shape[0] if n1 < n2: return X2[n1:] elif n1 == n2: # shape is no longer a reliable indicator of change, so assume things # are different return X2 else: return X2 def make_bbox(X, Y): """ Returns a C{Bbox} that contains the supplied sets of X and Y coordinates. """ if X is None or X.shape[0] == 0: x1 = x2 = 0.0 else: x1 = min(X) x2 = max(X) if Y is None or Y.shape[0] == 0: y1 = y2 = 0.0 else: y1 = min(Y) y2 = max(Y) return Bbox.from_extents(x1, y1, x2, y2) # # Strip-charts lines using a matplotlib axes # class StripCharter: """ Plots and updates lines on a matplotlib C{Axes}. """ def __init__(self, axes): """ Create a new C{StripCharter} associated with a matplotlib C{axes}. """ self.axes = axes self.channels = [] self.lines = {} def setChannels(self, channels): """ Specify the data-providers of the lines to be plotted and updated. """ self.lines = None self.channels = channels[:] # minimal Axes.cla() self.axes.legend_ = None self.axes.lines = [] def update(self): """ Redraw the associated axes with updated lines if any of the channels' data has changed. """ axes = self.axes figureCanvas = axes.figure.canvas zoomed = figureCanvas.zoomed(axes) redraw = False if self.lines is None: self._create_plot() redraw = True else: for channel in self.channels: redraw = self._update_channel(channel, zoomed) or redraw if redraw: if not zoomed: axes.autoscale_view() figureCanvas.draw() def _create_plot(self): """ Initially plot the lines corresponding to the data-providers. """ self.lines = {} axes = self.axes styleGen = _process_plot_var_args(axes) for channel in self.channels: self._plot_channel(channel, styleGen) if self.channels: lines = [self.lines[x] for x in self.channels] labels = [x.get_label() for x in lines] self.axes.legend(lines, labels, numpoints=2, prop=FontProperties(size='x-small')) def _plot_channel(self, channel, styleGen): """ Initially plot a line corresponding to one of the data-providers. """ empty = False x = channel.getX() y = channel.getY() if x is None or y is None: x = y = [] empty = True line = styleGen(x, y).next() line._wxmpl_empty_line = empty if channel.getColor() is not None: line.set_color(channel.getColor()) if channel.getStyle() is not None: line.set_linestyle(channel.getStyle()) if channel.getMarker() is not None: line.set_marker(channel.getMarker()) line.set_markeredgecolor(line.get_color()) line.set_markerfacecolor(line.get_color()) line.set_label(channel.getLabel()) self.lines[channel] = line if not empty: self.axes.add_line(line) def _update_channel(self, channel, zoomed): """ Replot a line corresponding to one of the data-providers if the data has changed. """ if channel.hasChanged(): channel.setChanged(False) else: return False axes = self.axes line = self.lines[channel] newX = channel.getX() newY = channel.getY() if newX is None or newY is None: return False oldX = line._x oldY = line._y x, y = newX, newY line.set_data(x, y) if line._wxmpl_empty_line: axes.add_line(line) line._wxmpl_empty_line = False else: if line.get_transform() != axes.transData: xys = axes._get_verts_in_data_coords( line.get_transform(), zip(x, y)) else: xys = np.zeros((x.shape[0], 2), np.Float) xys[:,0] = x xys[:,1] = y axes.update_datalim(xys) if zoomed: return axes.viewLim.overlaps( make_delta_bbox(oldX, oldY, newX, newY)) else: return True # # Data-providing interface to the StripCharter # class Channel: """ Provides data for a C{StripCharter} to plot. Subclasses of C{Channel} override the template methods C{getX()} and C{getY()} to provide plot data and call C{setChanged(True)} when that data has changed. """ def __init__(self, name, color=None, style=None, marker=None): """ Creates a new C{Channel} with the matplotlib label C{name}. The keyword arguments specify the strings for the line color, style, and marker to use when the line is plotted. """ self.name = name self.color = color self.style = style self.marker = marker self.changed = False def getLabel(self): """ Returns the matplotlib label for this channel of data. """ return self.name def getColor(self): """ Returns the line color string to use when the line is plotted, or C{None} to use an automatically generated color. """ return self.color def getStyle(self): """ Returns the line style string to use when the line is plotted, or C{None} to use the default line style. """ return self.style def getMarker(self): """ Returns the line marker string to use when the line is plotted, or C{None} to use the default line marker. """ return self.marker def hasChanged(self): """ Returns a boolean indicating if the line data has changed. """ return self.changed def setChanged(self, changed): """ Sets the change indicator to the boolean value C{changed}. @note: C{StripCharter} instances call this method after detecting a change, so a C{Channel} cannot be shared among multiple charts. """ self.changed = changed def getX(self): """ Template method that returns the vector of X axis data or C{None} if there is no data available. """ return None def getY(self): """ Template method that returns the vector of Y axis data or C{None} if there is no data available. """ return None dicompyler-0.4.1-1/dicompyler.egg-info/0000755000076500000240000000000011700133245020550 5ustar apanchalstaff00000000000000dicompyler-0.4.1-1/dicompyler.egg-info/dependency_links.txt0000644000076500000240000000000111700133245024616 0ustar apanchalstaff00000000000000 dicompyler-0.4.1-1/dicompyler.egg-info/entry_points.txt0000644000076500000240000000006611700133245024050 0ustar apanchalstaff00000000000000[console_scripts] dicompyler = dicompyler.main:start dicompyler-0.4.1-1/dicompyler.egg-info/not-zip-safe0000644000076500000240000000000111700106157023000 0ustar apanchalstaff00000000000000 dicompyler-0.4.1-1/dicompyler.egg-info/PKG-INFO0000644000076500000240000000460211700133245021647 0ustar apanchalstaff00000000000000Metadata-Version: 1.0 Name: dicompyler Version: 0.4.1-1 Summary: Extensible radiation therapy research platform and viewer for DICOM and DICOM RT. Home-page: http://code.google.com/p/dicompyler/ Author: Aditya Panchal Author-email: apanchal@bastula.org License: BSD License Description: dicompyler ========== dicompyler is an extensible open source radiation therapy research platform based on the DICOM standard. It also functions as a cross-platform DICOM RT viewer. dicompyler runs on Windows, Mac and Linux systems and is available in source and binary versions. Since dicompyler is based on modular architecture, it is easy to extend it with 3rd party plugins. Visit the dicompyler _`home page`: http://code.google.com/p/dicompyler/ for how-to information and guides. Getting Help ============ To get help with dicompyler, visit the _`mailing list`: http://groups.google.com/group/dicompyler/ or follow us on _`twitter`: http://twitter.com/dicompyler Requirements ============ dicompyler requires the following packages to run from source: - Python 2.5 or higher (not tested on Python 3) - wxPython 2.8.8.1 or higher - matplotlib 0.99 or higher - numpy 1.2.1 or higher - PIL 1.1.7 or higher - pydicom 0.9.5 or higher - simplejson (only for Python 2.5, Python 2.6+ includes JSON support) Keywords: radiation therapy research python dicom dicom-rt Platform: UNKNOWN Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Healthcare Industry Classifier: Intended Audience :: Science/Research Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Operating System :: OS Independent Classifier: Topic :: Scientific/Engineering :: Medical Science Apps. Classifier: Topic :: Scientific/Engineering :: Physics Classifier: Topic :: Scientific/Engineering :: Visualization dicompyler-0.4.1-1/dicompyler.egg-info/requires.txt0000644000076500000240000000006711700133245023153 0ustar apanchalstaff00000000000000matplotlib>=0.99 numpy>=1.2.1 pil>=1.1.7 pydicom>=0.9.5dicompyler-0.4.1-1/dicompyler.egg-info/SOURCES.txt0000644000076500000240000000345711700133245022445 0ustar apanchalstaff00000000000000MANIFEST.in README.txt distribute_setup.py setup.py dicompyler/__init__.py dicompyler/credits.txt dicompyler/dicomgui.py dicompyler/dicomparser.py dicompyler/dvhcalc.py dicompyler/dvhdata.py dicompyler/dvhdoses.py dicompyler/guidvh.py dicompyler/guiutil.py dicompyler/license.txt dicompyler/main.py dicompyler/plugin.py dicompyler/preferences.py dicompyler/util.py dicompyler/wxmpl.py dicompyler.egg-info/PKG-INFO dicompyler.egg-info/SOURCES.txt dicompyler.egg-info/dependency_links.txt dicompyler.egg-info/entry_points.txt dicompyler.egg-info/not-zip-safe dicompyler.egg-info/requires.txt dicompyler.egg-info/top_level.txt dicompyler/baseplugins/2dview.py dicompyler/baseplugins/2dview.xrc dicompyler/baseplugins/__init__.py dicompyler/baseplugins/anonymize.py dicompyler/baseplugins/anonymize.xrc dicompyler/baseplugins/dvh.py dicompyler/baseplugins/dvh.xrc dicompyler/baseplugins/treeview.py dicompyler/baseplugins/treeview.xrc dicompyler/resources/accept.png dicompyler/resources/book.png dicompyler/resources/chart_bar.png dicompyler/resources/chart_bar_error.png dicompyler/resources/chart_curve.png dicompyler/resources/chart_curve_error.png dicompyler/resources/contrast_high.png dicompyler/resources/dicomgui.xrc dicompyler/resources/dicompyler.icns dicompyler/resources/dicompyler.ico dicompyler/resources/dicompyler_icon11_16.png dicompyler/resources/dicompyler_logo.png dicompyler/resources/error.png dicompyler/resources/folder_user.png dicompyler/resources/group.png dicompyler/resources/guiutil.xrc dicompyler/resources/magnifier_zoom_in.png dicompyler/resources/magnifier_zoom_out.png dicompyler/resources/main.xrc dicompyler/resources/pencil.png dicompyler/resources/pencil_error.png dicompyler/resources/plugin.xrc dicompyler/resources/preferences.xrc dicompyler/resources/table_multiple.png dicompyler/resources/user.pngdicompyler-0.4.1-1/dicompyler.egg-info/top_level.txt0000644000076500000240000000001311700133245023274 0ustar apanchalstaff00000000000000dicompyler dicompyler-0.4.1-1/distribute_setup.py0000755000076500000240000003661511677672407020722 0ustar apanchalstaff00000000000000#!python """Bootstrap distribute installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from distribute_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import os import sys import time import fnmatch import tempfile import tarfile from distutils import log try: from site import USER_SITE except ImportError: USER_SITE = None try: import subprocess def _python_cmd(*args): args = (sys.executable,) + args return subprocess.call(args) == 0 except ImportError: # will be used for python 2.3 def _python_cmd(*args): args = (sys.executable,) + args # quoting arguments if windows if sys.platform == 'win32': def quote(arg): if ' ' in arg: return '"%s"' % arg return arg args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.24" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ Metadata-Version: 1.0 Name: setuptools Version: %s Summary: xxxx Home-page: xxx Author: xxx Author-email: xxx License: xxx Description: xxx """ % SETUPTOOLS_FAKED_VERSION def _install(tarball): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # installing log.warn('Installing Distribute') if not _python_cmd('setup.py', 'install'): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') finally: os.chdir(old_wd) def _build_egg(egg, tarball, to_dir): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # building an egg log.warn('Building a Distribute egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) finally: os.chdir(old_wd) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): tarball = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, tarball, to_dir) sys.path.insert(0, egg) import setuptools setuptools.bootstrap_install_from = egg def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15, no_fake=True): # making sure we use the absolute path to_dir = os.path.abspath(to_dir) was_imported = 'pkg_resources' in sys.modules or \ 'setuptools' in sys.modules try: try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): if not no_fake: _fake_setuptools() raise ImportError except ImportError: return _do_download(version, download_base, to_dir, download_delay) try: pkg_resources.require("distribute>="+version) return except pkg_resources.VersionConflict: e = sys.exc_info()[1] if was_imported: sys.stderr.write( "The required version of distribute (>=%s) is not available,\n" "and can't be installed while this script is running. Please\n" "install a more recent version first, using\n" "'easy_install -U distribute'." "\n\n(Currently using %r)\n" % (version, e.args[0])) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return _do_download(version, download_base, to_dir, download_delay) except pkg_resources.DistributionNotFound: return _do_download(version, download_base, to_dir, download_delay) finally: if not no_fake: _create_fake_setuptools_pkg_info(to_dir) def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay=15): """Download distribute from a specified location and return its filename `version` should be a valid distribute version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen tgz_name = "distribute-%s.tar.gz" % version url = download_base + tgz_name saveto = os.path.join(to_dir, tgz_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: log.warn("Downloading %s", url) src = urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto, "wb") dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def _no_sandbox(function): def __no_sandbox(*args, **kw): try: from setuptools.sandbox import DirectorySandbox if not hasattr(DirectorySandbox, '_old'): def violation(*args): pass DirectorySandbox._old = DirectorySandbox._violation DirectorySandbox._violation = violation patched = True else: patched = False except ImportError: patched = False try: return function(*args, **kw) finally: if patched: DirectorySandbox._violation = DirectorySandbox._old del DirectorySandbox._old return __no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() if existing_content == content: # already patched log.warn('Already patched.') return False log.warn('Patching...') _rename_path(path) f = open(path, 'w') try: f.write(content) finally: f.close() return True _patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content def _rename_path(path): new_name = path + '.OLD.%s' % time.time() log.warn('Renaming %s into %s', path, new_name) os.rename(path, new_name) return new_name def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unkown installation at %s', placeholder) return False found = False for file in os.listdir(placeholder): if fnmatch.fnmatch(file, 'setuptools*.egg-info'): found = True break if not found: log.warn('Could not locate setuptools*.egg-info') return log.warn('Removing elements out of the way...') pkg_info = os.path.join(placeholder, file) if os.path.isdir(pkg_info): patched = _patch_egg_dir(pkg_info) else: patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) if not patched: log.warn('%s already patched.', pkg_info) return False # now let's move the files out of the way for element in ('setuptools', 'pkg_resources.py', 'site.py'): element = os.path.join(placeholder, element) if os.path.exists(element): _rename_path(element) else: log.warn('Could not find the %s element of the ' 'Setuptools distribution', element) return True _remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') return pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) setuptools_file = 'setuptools-%s-py%s.egg-info' % \ (SETUPTOOLS_FAKED_VERSION, pyver) pkg_info = os.path.join(placeholder, setuptools_file) if os.path.exists(pkg_info): log.warn('%s already exists', pkg_info) return log.warn('Creating %s', pkg_info) f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() pth_file = os.path.join(placeholder, 'setuptools.pth') log.warn('Creating %s', pth_file) f = open(pth_file, 'w') try: f.write(os.path.join(os.curdir, setuptools_file)) finally: f.close() _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') if os.path.exists(pkg_info): if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): log.warn('%s already patched.', pkg_info) return False _rename_path(path) os.mkdir(path) os.mkdir(os.path.join(path, 'EGG-INFO')) pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() return True _patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') _fake_setuptools() def _under_prefix(location): if 'install' not in sys.argv: return True args = sys.argv[sys.argv.index('install')+1:] for index, arg in enumerate(args): for option in ('--root', '--prefix'): if arg.startswith('%s=' % option): top_dir = arg.split('root=')[-1] return location.startswith(top_dir) elif arg == option: if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) if arg == '--user' and USER_SITE is not None: return location.startswith(USER_SITE) return True def _fake_setuptools(): log.warn('Scanning installed packages') try: import pkg_resources except ImportError: # we're cool log.warn('Setuptools or Distribute does not seem to be installed.') return ws = pkg_resources.working_set try: setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', replacement=False)) except TypeError: # old distribute API setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) if setuptools_dist is None: log.warn('No setuptools distribution found') return # detecting if it was already faked setuptools_location = setuptools_dist.location log.warn('Setuptools installation detected at %s', setuptools_location) # if --root or --preix was provided, and if # setuptools is not located in them, we don't patch it if not _under_prefix(setuptools_location): log.warn('Not patching, --root or --prefix is installing Distribute' ' in another location') return # let's see if its an egg if not setuptools_location.endswith('.egg'): log.warn('Non-egg installation') res = _remove_flat_installation(setuptools_location) if not res: return else: log.warn('Egg installation') pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') if (os.path.exists(pkg_info) and _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): log.warn('Already patched.') return log.warn('Patching...') # let's create a fake egg replacing setuptools one res = _patch_egg_dir(setuptools_location) if not res: return log.warn('Patched done.') _relaunch() def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process # pip marker to avoid a relaunch bug if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) def _extractall(self, path=".", members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ import copy import operator from tarfile import ExtractError directories = [] if members is None: members = self for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 448 # decimal for oct 0700 self.extract(tarinfo, path) # Reverse sort directories. if sys.version_info < (2, 4): def sorter(dir1, dir2): return cmp(dir1.name, dir2.name) directories.sort(sorter) directories.reverse() else: directories.sort(key=operator.attrgetter('name'), reverse=True) # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except ExtractError: e = sys.exc_info()[1] if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" tarball = download_setuptools() _install(tarball) if __name__ == '__main__': main(sys.argv[1:]) dicompyler-0.4.1-1/MANIFEST.in0000644000076500000240000000017111700132756016452 0ustar apanchalstaff00000000000000include distribute_setup.py include dicompyler/*.txt include dicompyler/baseplugins/*.xrc include dicompyler/resources/* dicompyler-0.4.1-1/PKG-INFO0000644000076500000240000000460211700133245016006 0ustar apanchalstaff00000000000000Metadata-Version: 1.0 Name: dicompyler Version: 0.4.1-1 Summary: Extensible radiation therapy research platform and viewer for DICOM and DICOM RT. Home-page: http://code.google.com/p/dicompyler/ Author: Aditya Panchal Author-email: apanchal@bastula.org License: BSD License Description: dicompyler ========== dicompyler is an extensible open source radiation therapy research platform based on the DICOM standard. It also functions as a cross-platform DICOM RT viewer. dicompyler runs on Windows, Mac and Linux systems and is available in source and binary versions. Since dicompyler is based on modular architecture, it is easy to extend it with 3rd party plugins. Visit the dicompyler _`home page`: http://code.google.com/p/dicompyler/ for how-to information and guides. Getting Help ============ To get help with dicompyler, visit the _`mailing list`: http://groups.google.com/group/dicompyler/ or follow us on _`twitter`: http://twitter.com/dicompyler Requirements ============ dicompyler requires the following packages to run from source: - Python 2.5 or higher (not tested on Python 3) - wxPython 2.8.8.1 or higher - matplotlib 0.99 or higher - numpy 1.2.1 or higher - PIL 1.1.7 or higher - pydicom 0.9.5 or higher - simplejson (only for Python 2.5, Python 2.6+ includes JSON support) Keywords: radiation therapy research python dicom dicom-rt Platform: UNKNOWN Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Healthcare Industry Classifier: Intended Audience :: Science/Research Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Operating System :: OS Independent Classifier: Topic :: Scientific/Engineering :: Medical Science Apps. Classifier: Topic :: Scientific/Engineering :: Physics Classifier: Topic :: Scientific/Engineering :: Visualization dicompyler-0.4.1-1/README.txt0000644000076500000240000001567711700106032016417 0ustar apanchalstaff00000000000000dicompyler Readme ================= dicompyler is an extensible radiation therapy research platform and viewer for DICOM and DICOM RT. dicompyler is released under a BSD license. See the included license.txt file, or available online at: http://code.google.com/p/dicompyler/ Full documentation and source code for dicompyler is available online at: http://code.google.com/p/dicompyler/ Features ======== - Import CT/PET/MRI Images, DICOM RT structure set, RT dose and RT plan files - Extensible plugin system with included plugins: - 2D image viewer with dose and structure overlay - Dose volume histogram viewer with the ability to analyze DVH parameters - DICOM data tree viewer - Patient anonymizer Release Notes for dicompyler 0.4.1-1 - January 1st, 2012 ======================================================== - Fixed a critical bug where the logging folder could not be created and dicompyler would not run. Release Notes for dicompyler 0.4.1 - December 31st, 2011 ======================================================== - General - Added an error reporter for Windows and Mac versions allowing easy error submission - Added support for volumetric image series (i.e. CT, MR, PET) - Added support for RT Ion Plan - Improved image sorting significantly which now allows for non-axial orientations and non-parallel image series - Implemented console and file logging, along with a menu item to display log files - Implemented an preference to enable detailed logging for debugging - Implemented searching of DICOM files within subfolders - Implemented the ability to specify the user plugins folder in the preferences - Added support for RT Dose files that don't reference RT Plans - Added support for unrelated RT Dose and RT Structure Sets that share the same frame of reference - DVH calculation and viewer - Implemented a preference to force recalculation of DVH data regardless of the presence in RT Dose - Limited the number of bins in the DVH calculation to 500 Gy due to high doses in brachytherapy plans - Automatically calculate DVHs for a structure only if they are not present within the RT Dose file - 2D image viewer - Improved support for very low/high doses in the main interface and 2D View - Anonymization - Added support for more tags that can should be de-identified - Bug fixes - Fixed a bug where multiple series were loaded when a single series was selected due to the same Frame of Reference - Fixed a bug if multiple Target Prescriptions exist in a RT Plan file. - Fixed overestimation of volume calculation when using the DVH to calculate volumes - Fixed a bug when calculating DVHs for structures that are empty - Fixed a bug if DVH data has less bins than actually expected - Fixed a bug if the structure data was located slice position with a negative value close to zero - Fixed a bug regarding dose display if the current image slice is outside of the dose grid range - Fixed a bug where the displayed image width does not correspond to the actual image width - Fixed a bug with isodose rendering by moving the origin by 1 px right and 1 px down - Fixed a bug if the dose grid boundaries were located outside of the image grid This release also includes all the new features introduced in dicompyler 0.4a2: ------------------------------------------------------------------------------- - General - Automatic conversion of Differential DVHs to Cumulative DVHs - Automatic import of TomoTherapy Prescription Dose - Preferences dialog for each plugin with data stored in JSON format - Support structures that don't have color information - Added scrollbars to isodose and structure lists - Added a status bar to the program to show additional information - Added Export plugin capability - Added independent DVH calculation from RT Dose / Structure Set data - 2D image viewer - Support CT data that has multiple window / level presets - Support for non-coincident dose planes - New isodose generation using matplotlib backend - Holes in contours are now properly displayed - Support CT data that is missing the SliceLocation tag - More accurate panning when zoomed during mouse movement - Support RescaleIntercept & RescaleSlope tags for more accurate window / level values - Added DICOM / pixel coordinate display of the mouse cursor in the status bar - Added image / dose value display of the mouse cursor in the status bar - Anonymization - Now an export plugin, found under the File->Export menu Getting Started =============== dicompyler will read properly formatted DICOM and DICOM-RT files. To get started, run dicompyler and click "Open Patient" to bring up a dialog box that will show the DICOM files in the last selected directory. You may click "Browse..." to navigate to other folders that contain DICOM data. In the current version of dicompyler, you can import any DICOM CT, PET, or MRI image series, DICOM RT structure set, RT dose and RT plan files. dicompyler will automatically highlight the deepest item for the patient. All related items (up the tree) will be automatically imported as well. Alternatively, you can selectively import data. For example, If you only want to import CT images and an RT structure set just highlight the RT structure set. If you are importing an RT dose file and the corresponding plan does not contain a prescription dose, enter one in the box first. To import the data, click "Select" and dicompyler will process the information. Once the DICOM data has been loaded, the main window will show the patient and plan information. Additionally it will show a list of structures and isodoses that are associated with the plan. Example DICOM-RT data is provided in the downloads tab on the project homepage: http://code.google.com/p/dicompyler/downloads/list Included Plugins ================ You can use the 2D View tab to navigate CT, PET or MRI data with corresponding structures and isodoses. The DVH tab can be used to inspect and analyze DVH curves. The DICOM tree view tab can be used to delve into the raw DICOM data of the imported files. You can anonymize the loaded DICOM files via the anonymizer found in the File --> Export menu. Custom Plugins ============== Since dicompyler is based on a plugin architecture, users can write their own plugins with ease. For more information see the Plugin Development Guide on the dicompyler wiki: http://code.google.com/p/dicompyler/wiki/PluginDevelopmentGuide 3rd-party plugins can be found at http://code.google.com/p/dicompyler-plugins/ Bugs and Feedback ================= Please report any bugs or feedback on the dicompyler discussion group found at: http://groups.google.com/group/dicompyler/ Credits ======= A number of individuals have contributed to dicompyler and can be found in the included credits.txt. dicompyler-0.4.1-1/setup.cfg0000644000076500000240000000007311700133245016530 0ustar apanchalstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 dicompyler-0.4.1-1/setup.py0000755000076500000240000000627211700106032016425 0ustar apanchalstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # dicompyler.py """Setup script for dicompyler.""" # Copyright (c) 2011 Aditya Panchal # This file is part of dicompyler, relased under a BSD license. # See the file license.txt included with this distribution, also # available at http://code.google.com/p/dicompyler/ from distribute_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages import sys requires = [ 'matplotlib>=0.99', 'numpy>=1.2.1', 'pil>=1.1.7', 'pydicom>=0.9.5',] if sys.version_info[0] == 2 and sys.version_info[1] < 6: requires.append('simplejson') setup( name="dicompyler", version="0.4.1-1", include_package_data = True, packages = find_packages(), package_data = {'dicompyler': ['*.txt', 'resources/*.png', 'resources/*.xrc', 'resources/*.ico', 'baseplugins/*.py', 'baseplugins/*.xrc']}, zip_safe = False, install_requires = requires, entry_points={'console_scripts':['dicompyler = dicompyler.main:start']}, # metadata for upload to PyPI author = "Aditya Panchal", author_email = "apanchal@bastula.org", description = "Extensible radiation therapy research platform and " + \ "viewer for DICOM and DICOM RT.", license = "BSD License", keywords = "radiation therapy research python dicom dicom-rt", url = "http://code.google.com/p/dicompyler/", classifiers = [ "License :: OSI Approved :: BSD License", "Intended Audience :: Developers", "Intended Audience :: Healthcare Industry", "Intended Audience :: Science/Research", "Development Status :: 4 - Beta", "Programming Language :: Python", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Operating System :: OS Independent", "Topic :: Scientific/Engineering :: Medical Science Apps.", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Visualization"], long_description = """ dicompyler ========== dicompyler is an extensible open source radiation therapy research platform based on the DICOM standard. It also functions as a cross-platform DICOM RT viewer. dicompyler runs on Windows, Mac and Linux systems and is available in source and binary versions. Since dicompyler is based on modular architecture, it is easy to extend it with 3rd party plugins. Visit the dicompyler _`home page`: http://code.google.com/p/dicompyler/ for how-to information and guides. Getting Help ============ To get help with dicompyler, visit the _`mailing list`: http://groups.google.com/group/dicompyler/ or follow us on _`twitter`: http://twitter.com/dicompyler Requirements ============ dicompyler requires the following packages to run from source: - Python 2.5 or higher (not tested on Python 3) - wxPython 2.8.8.1 or higher - matplotlib 0.99 or higher - numpy 1.2.1 or higher - PIL 1.1.7 or higher - pydicom 0.9.5 or higher - simplejson (only for Python 2.5, Python 2.6+ includes JSON support)""", )